Add f-droid to the release pipeline

This commit is contained in:
Jon Staab 2026-09-18 11:57:25 -07:00
parent 1cbb862b33
commit 5bc499cf3a
2 changed files with 55 additions and 12 deletions

View file

@ -178,17 +178,23 @@ run and prints the command to pick up from there.
| --- | --- | | --- | --- |
| `web` | `scripts/build.sh`: web bundle, `cap sync`, generated icons and splash screens | | `web` | `scripts/build.sh`: web bundle, `cap sync`, generated icons and splash screens |
| `apk` | `assembleRelease` signed with the distribution key, renamed to the path in `zapstore.yaml` | | `apk` | `assembleRelease` signed with the distribution key, renamed to the path in `zapstore.yaml` |
| `fdroid` | reruns F-Droid's own preparation and build against the tag in a throwaway worktree |
| `play` | `bundleRelease` signed with the upload key, uploaded to a Play track as a draft | | `play` | `bundleRelease` signed with the upload key, uploaded to a Play track as a draft |
| `ios` | `cap build ios` to an archive and IPA, uploaded with `altool` | | `ios` | `cap build ios` to an archive and IPA, uploaded with `altool` |
| `desktop` | `package:desktop:*` for this OS | | `desktop` | `package:desktop:*` for this OS |
| `gitea` | creates the release for the tag from the changelog, attaches the APK and any desktop packages | | `gitea` | creates the release for the tag from the changelog, attaches the APK and any desktop packages |
| `zapstore` | `zsp publish zapstore.yaml` | | `zapstore` | `zsp publish zapstore.yaml` |
| `fdroid` | nothing to upload; F-Droid builds from the tag, see [fdroid/README.md](fdroid/README.md) |
Release notes come from the `CHANGELOG.md` section matching `package.json`'s version, so every Release notes come from the `CHANGELOG.md` section matching `package.json`'s version, so every
store shows the same text. The APK and zapstore share one artifact, whose path lives in store shows the same text. The APK and zapstore share one artifact, whose path lives in
`zapstore.yaml`. `zapstore.yaml`.
F-Droid has nothing to upload — their servers build from the tag themselves — so the `fdroid` step
is a gate instead: it runs [their preparation and build](fdroid/README.md) against the tag in a
throwaway git worktree, and fails the release before anything is published if that build no longer
works. Preparation patches source with exact-match replacements, so it breaks quietly when the
files it rewrites change. Expect it to take a while; it installs and builds from scratch.
### Credentials ### Credentials
These go in `.env.local`, which is gitignored. `pnpm release --check` lists whichever are missing These go in `.env.local`, which is gitignored. `pnpm release --check` lists whichever are missing

View file

@ -1,6 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
import {existsSync} from "node:fs" import {existsSync} from "node:fs"
import {readFile, readdir, rename} from "node:fs/promises" import {mkdtemp, readFile, readdir, rename, rm} from "node:fs/promises"
import {tmpdir} from "node:os"
import {dirname, join, resolve} from "node:path" import {dirname, join, resolve} from "node:path"
import {fileURLToPath} from "node:url" import {fileURLToPath} from "node:url"
import {parseArgs} from "node:util" import {parseArgs} from "node:util"
@ -143,6 +144,52 @@ const steps = [
await rename(join(dirname(apk), outputFile), apk) await rename(join(dirname(apk), outputFile), apk)
}, },
}, },
{
name: "fdroid",
title: "Rebuild the tag the way F-Droid will",
missing: () =>
git("cat-file", "-e", `${version}:scripts/prepare-fdroid-source.sh`) === undefined
? [`F-Droid support in the ${version} tag`]
: [],
setup: [
`The ${version} tag is older than fdroid/, so there is nothing for F-Droid to build from it.`,
"Name the steps you do want, or release a tag that has it.",
],
run: async () => {
const parent = await mkdtemp(join(tmpdir(), "flotilla-fdroid-"))
const checkout = join(parent, "flotilla")
// Preparation rewrites source and dependencies in place, so it only runs against a checkout
// that can be thrown away
try {
await run("git", ["worktree", "add", "--detach", checkout, version], {cwd: root})
await run("./scripts/prepare-fdroid-source.sh", [], {cwd: checkout})
await run("./scripts/build-fdroid-assets.sh", [], {cwd: checkout})
// F-Droid signs its own builds, so keep the distribution key out of gradle's environment
const env = {...process.env}
delete env.ANDROID_KEYSTORE_PATH
await run("./gradlew", ["--no-daemon", "assembleFdroidRelease"], {
cwd: join(checkout, "android"),
env,
})
const built = join(
checkout,
"android/app/build/outputs/apk/fdroid/release/app-fdroid-release-unsigned.apk",
)
if (!existsSync(built)) {
throw new Error(`the F-Droid build produced no apk at ${built}`)
}
} finally {
await rm(parent, {recursive: true, force: true})
await run("git", ["worktree", "prune"], {cwd: root})
}
},
},
{ {
name: "play", name: "play",
title: "Build the AAB and upload it to Google Play", title: "Build the AAB and upload it to Google Play",
@ -289,16 +336,6 @@ const steps = [
], ],
run: () => run("zsp", ["publish", "zapstore.yaml"], {cwd: root}), run: () => run("zsp", ["publish", "zapstore.yaml"], {cwd: root}),
}, },
{
name: "fdroid",
title: "F-Droid",
optional: true,
manual: [
"F-Droid builds from source on their own servers, so a release has nothing to upload: the",
"metadata tracks version tags, which makes pushing the tag the whole story. The fdroiddata",
"submission is still open — see fdroid/README.md.",
],
},
] ]
const unknownStep = chosen.find(step => !steps.some(({name}) => name === step)) const unknownStep = chosen.find(step => !steps.some(({name}) => name === step))