publish to fdroid through CI
This commit is contained in:
parent
3c9ec72b88
commit
c22ed56806
3 changed files with 112 additions and 4 deletions
|
|
@ -142,8 +142,9 @@ jobs:
|
||||||
- name: Unit test and build the app
|
- name: Unit test and build the app
|
||||||
working-directory: android
|
working-directory: android
|
||||||
# The release APK is unsigned here; signing happens on the HSM runner. Lint's
|
# The release APK is unsigned here; signing happens on the HSM runner. Lint's
|
||||||
# release checks run as part of assembleRelease.
|
# release checks run as part of assembleRelease. The run number is the
|
||||||
run: gradle --no-daemon testDebugUnitTest assembleDebug assembleRelease
|
# versionCode, so each published build is an update to the one before.
|
||||||
|
run: gradle --no-daemon -PtallyVersionCode=${{ github.run_number }} testDebugUnitTest assembleDebug assembleRelease
|
||||||
- name: Upload the unsigned release APK
|
- name: Upload the unsigned release APK
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
|
|
@ -270,3 +271,69 @@ jobs:
|
||||||
status: ${{ job.status }}
|
status: ${{ job.status }}
|
||||||
user: ${{ secrets.NTFY_USER }}
|
user: ${{ secrets.NTFY_USER }}
|
||||||
password: ${{ secrets.NTFY_PASSWORD }}
|
password: ${{ secrets.NTFY_PASSWORD }}
|
||||||
|
|
||||||
|
fdroid:
|
||||||
|
name: F-Droid repo
|
||||||
|
# The signed APK into https://fdroid.lerch.org/repo, which is /data/fdroid on the HSM
|
||||||
|
# runner's host: this label is what puts the job on that host, as it does the
|
||||||
|
# signing. The repo itself (config.yml, metadata, the index signing keystore) lives
|
||||||
|
# there, not here; README, "Install with F-Droid".
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
runs-on: ubuntu-latest-with-hsm
|
||||||
|
needs: sign
|
||||||
|
env:
|
||||||
|
# fdroidserver, pinned: `fdroid update` regenerates and signs the repo's index.
|
||||||
|
FDROID_IMAGE: registry.gitlab.com/fdroid/docker-executable-fdroidserver@sha256:75f6b88ef13a63fc5c49aa4c4dde35607c4a364d22cf57d534aa75c54effa56d
|
||||||
|
FDROID_REPO: /data/fdroid
|
||||||
|
# How many tally builds the repo offers; older ones are deleted.
|
||||||
|
FDROID_KEEP: 3
|
||||||
|
steps:
|
||||||
|
- name: Download the signed release
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: tally-release
|
||||||
|
path: dist
|
||||||
|
- name: Publish to the F-Droid repo
|
||||||
|
# In fdroidserver's container on the host's daemon, as the repo's owner (uid
|
||||||
|
# 1000). The repo is a bind mount of a host path, so the APK cannot be copied in
|
||||||
|
# from this job's workspace by path: it goes over stdin. `--mount` rather than
|
||||||
|
# `-v`, so that on a host without the repo the run fails instead of creating an
|
||||||
|
# empty one. The versionCode is the run number (the android job), and so is the
|
||||||
|
# file name, which is what orders the builds for pruning.
|
||||||
|
#
|
||||||
|
# The index signing keystore's password (the store's and the key's are the same)
|
||||||
|
# is a secret, and only that: it is not on the host beside the keystore, and a
|
||||||
|
# pull request from a fork gets no secrets. It reaches fdroid as environment
|
||||||
|
# variables, passed to docker by name so the value is never on a command line;
|
||||||
|
# the repo's config.yml reads them with {env: ...}.
|
||||||
|
env:
|
||||||
|
APK: dev.lerch.tally_${{ github.run_number }}.apk
|
||||||
|
FDROID_KEY_STORE_PASS: ${{ secrets.FDROID_KEYSTORE_PASS }}
|
||||||
|
FDROID_KEY_PASS: ${{ secrets.FDROID_KEYSTORE_PASS }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
[ -n "$FDROID_KEY_STORE_PASS" ] || { echo "the FDROID_KEYSTORE_PASS secret is not set" >&2; exit 1; }
|
||||||
|
docker run --rm -i -u 1000:1000 -e HOME=/tmp -e APK -e FDROID_KEEP \
|
||||||
|
-e FDROID_KEY_STORE_PASS -e FDROID_KEY_PASS \
|
||||||
|
-e GIT_CONFIG_COUNT=1 -e GIT_CONFIG_KEY_0=safe.directory \
|
||||||
|
-e GIT_CONFIG_VALUE_0=/home/vagrant/fdroidserver \
|
||||||
|
--mount "type=bind,source=${FDROID_REPO},target=/repo" \
|
||||||
|
--entrypoint sh "$FDROID_IMAGE" -euc '
|
||||||
|
[ -f config.yml ] || { echo "no F-Droid repo at /repo (no config.yml)" >&2; exit 1; }
|
||||||
|
cat > "repo/${APK}.part"
|
||||||
|
mv "repo/${APK}.part" "repo/${APK}"
|
||||||
|
ls repo/dev.lerch.tally_*.apk | sort -t _ -k 2 -n | head -n "-${FDROID_KEEP}" |
|
||||||
|
while read -r old; do echo "removing ${old}"; rm -f "${old}"; done
|
||||||
|
. /etc/profile.d/bsenv.sh
|
||||||
|
"${fdroidserver}/fdroid" update
|
||||||
|
ls repo/dev.lerch.tally_*.apk
|
||||||
|
' < dist/tally.apk
|
||||||
|
- name: Notify
|
||||||
|
uses: https://git.lerch.org/lobo/action-notify-ntfy@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.NTFY_HOST }}
|
||||||
|
topic: ${{ secrets.NTFY_TOPIC }}
|
||||||
|
status: ${{ job.status }}
|
||||||
|
user: ${{ secrets.NTFY_USER }}
|
||||||
|
password: ${{ secrets.NTFY_PASSWORD }}
|
||||||
|
|
|
||||||
36
README.md
36
README.md
|
|
@ -104,6 +104,41 @@ Android ties an app to its signing key permanently, so a published APK cannot up
|
||||||
copy you built yourself (which is signed with your debug key), and the reverse. Uninstall
|
copy you built yourself (which is signed with your debug key), and the reverse. Uninstall
|
||||||
one before installing the other; the app's saved state goes with it.
|
one before installing the other; the app's saved state goes with it.
|
||||||
|
|
||||||
|
### Install with F-Droid
|
||||||
|
|
||||||
|
Every master build is also published to an F-Droid repository, so the phone keeps itself
|
||||||
|
up to date. In F-Droid (or Droid-ify, or Obtainium's "F-Droid third-party repo" source),
|
||||||
|
add this repository; the fingerprint in the URL is what lets the client check the index
|
||||||
|
it downloads:
|
||||||
|
|
||||||
|
```
|
||||||
|
https://fdroid.lerch.org/repo?fingerprint=99387004A55002CF54F291D7ECA6A83AD7C7945E8E633160CC76790AD12E2D6E
|
||||||
|
```
|
||||||
|
|
||||||
|
Then install Tally from it. Each build's versionCode is the CI run number (its version
|
||||||
|
name is `0.1.0+<run>`), so every master build is an update to the one before, and the
|
||||||
|
repo keeps the newest three. The APK is the same one the registry publishes, signed by
|
||||||
|
the HSM key with the certificate in
|
||||||
|
[android/signing-cert.pem](android/signing-cert.pem).
|
||||||
|
|
||||||
|
The repository itself is not in this repo. It is `/data/fdroid` on the HSM runner's host,
|
||||||
|
served by nginx (only its `repo/`), with its `config.yml`, the app metadata, and the
|
||||||
|
keystore that signs the index. The keystore's password is kept off that host: CI has it
|
||||||
|
as the `FDROID_KEYSTORE_PASS` repository secret. The `fdroid` job in the workflow adds
|
||||||
|
each build and runs `fdroid update` there, in fdroidserver's container (pinned in the
|
||||||
|
workflow). To regenerate the index by hand on that host, after editing the metadata say:
|
||||||
|
|
||||||
|
```
|
||||||
|
read -rs FDROID_KEY_STORE_PASS && export FDROID_KEY_STORE_PASS FDROID_KEY_PASS="$FDROID_KEY_STORE_PASS"
|
||||||
|
docker run --rm -u 1000:1000 -e HOME=/tmp -e FDROID_KEY_STORE_PASS -e FDROID_KEY_PASS \
|
||||||
|
-e GIT_CONFIG_COUNT=1 -e GIT_CONFIG_KEY_0=safe.directory \
|
||||||
|
-e GIT_CONFIG_VALUE_0=/home/vagrant/fdroidserver \
|
||||||
|
--mount type=bind,source=/data/fdroid,target=/repo \
|
||||||
|
--entrypoint sh "<FDROID_IMAGE from the workflow>" -euc '
|
||||||
|
. /etc/profile.d/bsenv.sh
|
||||||
|
"$fdroidserver/fdroid" update'
|
||||||
|
```
|
||||||
|
|
||||||
### With nix
|
### With nix
|
||||||
|
|
||||||
This repository is a flake, for the CLI and TUI (the Android app stays with gradle). To
|
This repository is a flake, for the CLI and TUI (the Android app stays with gradle). To
|
||||||
|
|
@ -242,6 +277,7 @@ punctuation: the source is ASCII.
|
||||||
| Android | `ubuntu-latest` | `zig build android`, the library audit, the JVM unit tests, debug and unsigned release APKs (with lint's release checks) |
|
| Android | `ubuntu-latest` | `zig build android`, the library audit, the JVM unit tests, debug and unsigned release APKs (with lint's release checks) |
|
||||||
| Sign | `ubuntu-latest-with-hsm` | master only: signs the APK with the HSM, then every release file with a detached signature logged to sigstore |
|
| Sign | `ubuntu-latest-with-hsm` | master only: signs the APK with the HSM, then every release file with a detached signature logged to sigstore |
|
||||||
| Publish | `ubuntu-latest` | master only: uploads the signed files to the generic package registry under the short SHA and `latest` |
|
| Publish | `ubuntu-latest` | master only: uploads the signed files to the generic package registry under the short SHA and `latest` |
|
||||||
|
| F-Droid repo | `ubuntu-latest-with-hsm` | master only: adds the signed APK to the F-Droid repository on the HSM runner's host and regenerates its index |
|
||||||
|
|
||||||
Every job reports to ntfy. Zlint and Gradle are downloaded at pinned versions and checked
|
Every job reports to ntfy. Zlint and Gradle are downloaded at pinned versions and checked
|
||||||
against pinned SHA-256 digests; Zig's version comes from `build.zig.zon`.
|
against pinned SHA-256 digests; Zig's version comes from `build.zig.zon`.
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,13 @@ android {
|
||||||
applicationId = "dev.lerch.tally"
|
applicationId = "dev.lerch.tally"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 1
|
// Every build an updater should offer needs a larger versionCode than the one
|
||||||
versionName = "0.1.0"
|
// installed: Android, F-Droid and Obtainium all compare it, not the name. CI
|
||||||
|
// passes its run number (`-PtallyVersionCode=N`); a local build is 1, below
|
||||||
|
// anything CI publishes. The name carries the same number, so it is visible.
|
||||||
|
val tallyVersionCode = (findProperty("tallyVersionCode") as String?)?.toInt()
|
||||||
|
versionCode = tallyVersionCode ?: 1
|
||||||
|
versionName = if (tallyVersionCode != null) "0.1.0+$tallyVersionCode" else "0.1.0"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
// Only the ABIs `zig build android` produces. Anything else would package an
|
// Only the ABIs `zig build android` produces. Anything else would package an
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue