Skip to content

Commit 75e3168

Browse files
jamesarichCopilot
andcommitted
feat: gradle-flatpak-sources v0.1.0
Gradle settings plugin that captures all external resource URLs during a build and emits a Flathub-compliant flatpak-sources.json for offline Flatpak builds. Features: - BuildService listener pattern (coexists with Develocity) - Zero-network settings plugin - Post-build cache scan for bootstrap artifacts - Cross-platform variant resolution (linux-x64/arm64) - Maven mirror generation - Download-and-hash fallback for uncached URLs - Repo-aware dest path computation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0 parents  commit 75e3168

26 files changed

Lines changed: 1863 additions & 0 deletions

.github/FUNDING.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github: meshtastic
2+
open_collective: meshtastic

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## Description
2+
3+
<!-- Describe your changes and the problem they solve. -->
4+
5+
## Related Issue
6+
7+
<!-- Reference the issue this PR addresses, e.g., Fixes #123 -->
8+
9+
## Testing
10+
11+
<!-- How did you test these changes? -->
12+
13+
- [ ] `./gradlew :plugin:build :plugin:functionalTest` passes
14+
- [ ] Manual verification (describe below if applicable)
15+
16+
## Checklist
17+
18+
- [ ] I have read the [CONTRIBUTING.md](../CONTRIBUTING.md) guidelines
19+
- [ ] My code follows the project's code style (detekt passes)
20+
- [ ] I have enabled "Allow edits by maintainers"

.github/workflows/ci.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
name: Test (Gradle ${{ matrix.gradle }})
12+
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
gradle: ['9.5.1']
16+
steps:
17+
- uses: actions/checkout@v5
18+
19+
- uses: actions/setup-java@v5
20+
with:
21+
distribution: 'temurin'
22+
java-version: '17'
23+
24+
- uses: gradle/actions/setup-gradle@v6
25+
with:
26+
gradle-version: ${{ matrix.gradle }}
27+
28+
- name: Build & Test
29+
run: ./gradlew :plugin:build :plugin:functionalTest --stacktrace
30+
31+
- name: Upload test reports
32+
if: failure()
33+
uses: actions/upload-artifact@v4
34+
with:
35+
name: test-reports-gradle-${{ matrix.gradle }}
36+
path: plugin/build/reports/

.github/workflows/publish.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Publish
2+
3+
on:
4+
push:
5+
tags: ['v*']
6+
7+
jobs:
8+
publish:
9+
name: Publish to Gradle Plugin Portal & Maven Central
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
steps:
14+
- uses: actions/checkout@v5
15+
16+
- uses: actions/setup-java@v5
17+
with:
18+
distribution: 'temurin'
19+
java-version: '17'
20+
21+
- uses: gradle/actions/setup-gradle@v6
22+
23+
- name: Build & Test
24+
run: ./gradlew :plugin:build :plugin:functionalTest --stacktrace
25+
26+
- name: Publish to Gradle Plugin Portal
27+
env:
28+
GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
29+
GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
30+
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
31+
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
32+
run: ./gradlew :plugin:publishPlugins
33+
34+
- name: Publish to Maven Central
35+
env:
36+
CENTRAL_PORTAL_USERNAME: ${{ secrets.CENTRAL_PORTAL_USERNAME }}
37+
CENTRAL_PORTAL_PASSWORD: ${{ secrets.CENTRAL_PORTAL_PASSWORD }}
38+
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
39+
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
40+
run: ./gradlew :plugin:publishAllPublicationsToCentralPortal

.github/workflows/snapshot.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Snapshot
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
jobs:
8+
snapshot:
9+
name: Publish SNAPSHOT to Maven Central
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
steps:
14+
- uses: actions/checkout@v5
15+
16+
- uses: actions/setup-java@v5
17+
with:
18+
distribution: 'temurin'
19+
java-version: '17'
20+
21+
- uses: gradle/actions/setup-gradle@v6
22+
23+
- name: Build & Test
24+
run: ./gradlew :plugin:build :plugin:functionalTest --stacktrace
25+
26+
- name: Publish SNAPSHOT
27+
env:
28+
CENTRAL_PORTAL_USERNAME: ${{ secrets.CENTRAL_PORTAL_USERNAME }}
29+
CENTRAL_PORTAL_PASSWORD: ${{ secrets.CENTRAL_PORTAL_PASSWORD }}
30+
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.SIGNING_KEY }}
31+
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.SIGNING_PASSWORD }}
32+
run: ./gradlew :plugin:publishAllPublicationsToCentralSnapshots

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Gradle
2+
.gradle/
3+
build/
4+
!gradle/wrapper/gradle-wrapper.jar
5+
6+
# IDE
7+
.idea/
8+
*.iml
9+
*.ipr
10+
*.iws
11+
12+
# OS
13+
.DS_Store
14+
Thumbs.db
15+
16+
# Signing
17+
*.gpg
18+
.kotlin/

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.1.0] - Unreleased
9+
10+
### Added
11+
12+
- Settings plugin (`org.meshtastic.flatpak.sources.settings`) that captures all dependency downloads from build start via `BuildOperationListener` + `BuildService`
13+
- Project plugin (`org.meshtastic.flatpak.sources`) with fallback listener for use without the settings plugin
14+
- `captureFlatpakSources` task that emits a Flathub-compliant `flatpak-sources.json` manifest
15+
- SHA-256 computation from local Gradle cache with remote download fallback
16+
- Maven Central mirror URL generation for redundancy
17+
- Cross-platform artifact resolution via `targetPlatforms` + `platformDependencies` configuration
18+
- Cache scan for artifacts resolved before listener attachment (included build plugins, settings bootstrap)
19+
- URL suffix exclusion (sources/javadoc jars excluded by default)
20+
- Configurable output file, destination prefix, and task ordering
21+
- Functional test suite using Gradle TestKit
22+
- CI workflow (GitHub Actions) with Gradle 9.x matrix
23+
- Publish workflow for Gradle Plugin Portal and Maven Central

CONTRIBUTING.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Contributing to gradle-flatpak-sources
2+
3+
Thank you for your interest in contributing! We welcome contributions from everyone.
4+
5+
## Getting Started
6+
7+
1. **Fork the repository** and clone your fork.
8+
2. Ensure you have **JDK 17+** installed.
9+
3. Build and test locally:
10+
11+
```bash
12+
./gradlew :plugin:build :plugin:functionalTest --stacktrace
13+
```
14+
15+
## Code Style
16+
17+
- Follow the [Kotlin Coding Conventions](https://kotlinlang.org/docs/coding-conventions.html).
18+
- We use [detekt](https://detekt.dev/) for static analysis. Run it before submitting:
19+
20+
```bash
21+
./gradlew :plugin:detekt
22+
```
23+
24+
- Fix any warnings or errors reported. Suppress sparingly and only with justification.
25+
26+
## Making Changes
27+
28+
1. Create a branch from `main` with a descriptive prefix:
29+
- `bugfix/` — bug fixes
30+
- `enhancement/` — new features or improvements
31+
- `docs/` — documentation-only changes
32+
2. Make your changes in logical, atomic commits.
33+
3. Test thoroughly — both unit tests and functional tests should pass.
34+
4. Submit a pull request with a clear description.
35+
36+
## Pull Requests
37+
38+
- Ensure your branch is up to date with `main`.
39+
- Reference related issues (e.g., `Fixes #123`).
40+
- Enable **"Allow edits by maintainers"**.
41+
- Be responsive to review feedback.
42+
43+
## Versioning & Releases
44+
45+
The source carries a `-SNAPSHOT` version. Releases are triggered by pushing a `v*` tag (e.g., `v0.1.0`). The CI workflow builds, tests, and publishes to the Gradle Plugin Portal and Maven Central.
46+
47+
## CLA
48+
49+
Before contributing, you must sign the Meshtastic Contributor License Agreement via [CLA Assistant](https://cla-assistant.io/meshtastic/gradle-flatpak-sources).
50+
51+
## Community
52+
53+
- **Discord:** [discord.gg/meshtastic](https://discord.gg/meshtastic)
54+
- **Discussions:** [GitHub Discussions](https://github.com/meshtastic/gradle-flatpak-sources/discussions)
55+
- **Code of Conduct:** [meshtastic.org/docs/legal/conduct](https://meshtastic.org/docs/legal/conduct/)
56+
57+
## Reporting Issues
58+
59+
- Search existing issues before opening a new one.
60+
- Provide a clear title, steps to reproduce, expected vs. actual behavior.
61+
- Include Gradle version, JDK version, and OS.
62+
63+
Thank you for helping improve gradle-flatpak-sources!

COPYING

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
GNU GENERAL PUBLIC LICENSE
2+
Version 3, 29 June 2007
3+
4+
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5+
Everyone is permitted to copy and distribute verbatim copies
6+
of this license document, but changing it is not allowed.
7+
8+
Preamble
9+
10+
The GNU General Public License is a free, copyleft license for
11+
software and other kinds of works.
12+
13+
The licenses for most software and other practical works are designed
14+
to take away your freedom to share and change the works. By contrast,
15+
the GNU General Public License is intended to guarantee your freedom to
16+
share and change all versions of a program--to make sure it remains free
17+
software for all its users. We, the Free Software Foundation, use the
18+
GNU General Public License for most of our software; it applies also to
19+
any other work released this way by its authors. You can apply it to
20+
your programs, too.
21+
22+
For the complete license text, see <https://www.gnu.org/licenses/gpl-3.0.txt>.

README.md

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# gradle-flatpak-sources
2+
3+
[![CI](https://github.com/meshtastic/gradle-flatpak-sources/actions/workflows/ci.yml/badge.svg)](https://github.com/meshtastic/gradle-flatpak-sources/actions/workflows/ci.yml)
4+
[![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/org.meshtastic.flatpak.sources.settings)](https://plugins.gradle.org/plugin/org.meshtastic.flatpak.sources.settings)
5+
[![License: GPL-3.0-or-later](https://img.shields.io/badge/License-GPL--3.0--or--later-blue.svg)](COPYING)
6+
[![CLA assistant](https://cla-assistant.io/readme/badge/meshtastic/gradle-flatpak-sources)](https://cla-assistant.io/meshtastic/gradle-flatpak-sources)
7+
8+
A Gradle plugin that generates [Flathub-compliant](https://docs.flathub.org/docs/for-app-authors/requirements#no-network-access) offline dependency manifests (`flatpak-sources.json`) for Flatpak builds.
9+
10+
## Quick Start
11+
12+
Apply the settings plugin in `settings.gradle.kts` — it captures every dependency download from the very start of the build, with zero additional configuration:
13+
14+
```kotlin
15+
// settings.gradle.kts
16+
plugins {
17+
id("org.meshtastic.flatpak.sources.settings") version "0.1.0"
18+
}
19+
```
20+
21+
Then generate the manifest using a fresh cache to force all artifacts to re-download:
22+
23+
```bash
24+
./gradlew --no-build-cache --no-configuration-cache \
25+
-Dgradle.user.home=/tmp/flatpak-gradle-home \
26+
:app:assemble :captureFlatpakSources
27+
```
28+
29+
The output is written to `build/flatpak-sources.json` by default.
30+
31+
## How It Works
32+
33+
1. **Settings plugin** attaches a `BuildService` listener via [`BuildEventListenerRegistryInternal.onOperationCompletion`](https://github.com/gradle/gradle/blob/master/subprojects/build-events/src/main/java/org/gradle/internal/build/event/BuildEventListenerRegistryInternal.java) before any dependency resolution occurs — the same pattern as `gradle/github-dependency-graph-gradle-plugin`. It also back-fills URLs for artifacts resolved during `pluginManagement {}` (before the listener attached) by inspecting the already-resolved settings classpath.
34+
35+
2. **`captureFlatpakSources` task** reads the captured URL set, locates each artifact in `caches/modules-2/files-2.1`, computes its SHA-256, and emits the manifest. If an artifact is not in the local cache it falls back to downloading and hashing it.
36+
37+
3. **Output** follows the [Flatpak external data format](https://docs.flatpak.org/en/latest/flatpak-builder-command-reference.html#flatpak-manifest): `type: file`, `url`, `sha256`, `dest`, `dest-filename`, plus Maven Central mirror URLs for redundancy.
38+
39+
## Included Builds (`build-logic/`)
40+
41+
If your project uses an included build for convention plugins, apply the settings plugin there too — the plugin detects a pre-registered URL set and reuses it without creating a duplicate listener:
42+
43+
```kotlin
44+
// build-logic/settings.gradle.kts
45+
plugins {
46+
id("org.meshtastic.flatpak.sources.settings") version "0.1.0"
47+
}
48+
```
49+
50+
## Use in Your Flatpak Manifest
51+
52+
```yaml
53+
# org.example.myapp.yaml
54+
modules:
55+
- name: dependencies
56+
buildsystem: simple
57+
build-commands: ['true']
58+
sources:
59+
- flatpak-sources.json
60+
```
61+
62+
## Configuration
63+
64+
```kotlin
65+
flatpakSources {
66+
// Output file (default: build/flatpak-sources.json)
67+
outputFile.set(layout.buildDirectory.file("flatpak-sources.json"))
68+
69+
// Destination prefix in Flatpak sandbox (default: "offline-repository")
70+
destPrefix.set("offline-repository")
71+
72+
// Task paths that must complete before capture runs
73+
mustRunAfterTasks.set(listOf(":app:assemble"))
74+
75+
// Generate Maven Central mirror URLs (default: true)
76+
generateMirrors.set(true)
77+
78+
// URL suffixes to exclude (default: sources + javadoc jars)
79+
excludeSuffixes.set(setOf("-sources.jar", "-javadoc.jar"))
80+
81+
// Force-resolve platform-specific native artifacts not resolved on the generation host.
82+
// Use when generating a Linux Flatpak manifest on macOS or Windows.
83+
targetPlatforms.set(setOf("linux-x64", "linux-arm64"))
84+
85+
// Maven coordinate templates for each platform target ({platform} is substituted).
86+
platformDependencies.set(setOf(
87+
"org.jetbrains.skiko:skiko-awt-runtime-{platform}:0.144.6",
88+
"org.jetbrains.compose.desktop:desktop-jvm-{platform}:1.11.0",
89+
))
90+
}
91+
```
92+
93+
### Cross-Platform Artifact Resolution
94+
95+
When building on macOS but targeting Linux Flatpak, platform-specific natives (Skiko renderers, Compose Desktop JARs, etc.) won't be resolved by Gradle's normal variant selection. `targetPlatforms` + `platformDependencies` forces them to download into the Gradle cache so they appear in the manifest:
96+
97+
```kotlin
98+
flatpakSources {
99+
targetPlatforms.set(setOf("linux-x64", "linux-arm64"))
100+
platformDependencies.set(setOf(
101+
"org.jetbrains.skiko:skiko-awt-runtime-{platform}:0.144.6",
102+
))
103+
}
104+
```
105+
106+
## Requirements
107+
108+
- **Gradle 9.0+**
109+
- **JDK 17+**
110+
- **`--no-configuration-cache`** — the capture mechanism uses runtime state that is not configuration-cache-safe
111+
112+
## Internal APIs
113+
114+
This plugin uses Gradle internal APIs (same trade-off as [flatpak-gradle-generator](https://github.com/flatpak/flatpak-gradle-generator)):
115+
116+
- `org.gradle.internal.operations.BuildOperationListener`
117+
- `org.gradle.internal.resource.ExternalResourceReadBuildOperationType`
118+
- `org.gradle.internal.build.event.BuildEventListenerRegistryInternal`
119+
120+
These APIs have been stable across Gradle 7–9.
121+
122+
## License
123+
124+
Copyright (c) 2026 Meshtastic LLC. Licensed under [GPL-3.0-or-later](COPYING).
125+
126+
## Contributing
127+
128+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, code style, and PR guidelines.

0 commit comments

Comments
 (0)