Repository navigation
follower apk #1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Build the FOLLOWER apk, and only ever the follower. | |
| # | |
| # WHAT THIS MUST NEVER BUILD: the `full` flavour. That is the app that drives an | |
| # insulin pump, and this repository does not distribute it — see the README. | |
| # Every gradle invocation here names `Aapsclient` explicitly for that reason, | |
| # and the check job fails the run if a `full` artifact appears anyway. | |
| # | |
| # The follower is a different proposition: a separate `applicationId`, no pump | |
| # drivers compiled in, and structurally unable to dose (decision D22). It is a | |
| # viewer for somebody else's glucose. Handing that to a person as a file they | |
| # can install is reasonable in a way that handing them a loop is not. | |
| # | |
| # The APK contains AndroidAPS, which is AGPL-3.0. Distributing a binary | |
| # therefore obliges us to publish the corresponding source: the fork and branch | |
| # it was built from are recorded in the release notes, at the exact commit. | |
| name: follower apk | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| aaps_ref: | |
| description: "Branch or commit of the AAPS fork to build against" | |
| default: diaswarm-addon | |
| push: | |
| tags: ["follower-v*"] | |
| permissions: | |
| contents: write # only to attach the apk to a release | |
| env: | |
| AAPS_REPO: alex-poor/AndroidAPS | |
| NDK_VERSION: "28.2.13676358" | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| # Whether a signing key is configured, decided once. A step's own `env:` | |
| # block is NOT visible to that step's `if:`, so testing the secret there | |
| # silently never fires and every build comes out unsigned. | |
| env: | |
| HAS_KEY: ${{ secrets.FOLLOWER_KEYSTORE_B64 != '' }} | |
| steps: | |
| - name: Check out diaswarm | |
| uses: actions/checkout@v4 | |
| with: | |
| path: diaswarm | |
| # The follower is a build of this fork of AAPS, not of upstream: the | |
| # screen it draws comes from the fork's own overview. AGPL means whatever | |
| # is built here has to be publicly readable, so this checkout failing is | |
| # a licence problem, not just a build problem. | |
| - name: Check out the AAPS fork | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ env.AAPS_REPO }} | |
| ref: ${{ github.event.inputs.aaps_ref || 'diaswarm-addon' }} | |
| path: aaps | |
| - name: JDK 21 | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: "21" | |
| - name: NDK ${{ env.NDK_VERSION }} | |
| run: | | |
| set -euo pipefail | |
| yes | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --licenses >/dev/null | |
| "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" "ndk;${NDK_VERSION}" >/dev/null | |
| echo "ANDROID_NDK_HOME=$ANDROID_HOME/ndk/${NDK_VERSION}" >> "$GITHUB_ENV" | |
| # Pinned by rust-toolchain.toml in the diaswarm repo; the targets and | |
| # cargo-ndk are what turn the record core into the .so the plugin packages. | |
| - name: Rust for Android | |
| working-directory: diaswarm | |
| run: | | |
| set -euo pipefail | |
| rustup show active-toolchain | |
| rustup target add aarch64-linux-android armv7-linux-androideabi | |
| cargo install cargo-ndk --locked | |
| - name: Cache gradle and cargo | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| diaswarm/crates/*/target | |
| key: follower-${{ runner.os }}-${{ hashFiles('diaswarm/**/Cargo.lock', 'aaps/**/*.gradle*') }} | |
| restore-keys: follower-${{ runner.os }}- | |
| - name: Test the record core | |
| working-directory: diaswarm/crates/diaswarm-core | |
| run: cargo test | |
| # Unsigned on purpose: the `release` build type carries no signingConfig, | |
| # so nothing here can accidentally reuse the debug keystore that the loop | |
| # build depends on. | |
| - name: Build the follower | |
| working-directory: aaps | |
| env: | |
| DIASWARM_PLUGIN_DIR: ${{ github.workspace }}/diaswarm/plugin | |
| run: ./gradlew :app:assembleAapsclientRelease -PappVersionSuffix=follower --console=plain | |
| - name: Refuse to ship a pump build | |
| run: | | |
| set -euo pipefail | |
| if compgen -G "aaps/app/build/outputs/apk/full/**/*.apk" > /dev/null; then | |
| echo "::error::a 'full' (pump) APK was produced — this workflow must build the follower only" | |
| exit 1 | |
| fi | |
| ls -la aaps/app/build/outputs/apk/aapsclient/release/ | |
| # Signed only when a key is configured. Without one the run still produces | |
| # an unsigned artifact, which is installable by nobody and reviewable by | |
| # anybody — the right outcome for a fork or a pull request. | |
| - name: Sign | |
| id: sign | |
| if: env.HAS_KEY == 'true' | |
| env: | |
| KEYSTORE_B64: ${{ secrets.FOLLOWER_KEYSTORE_B64 }} | |
| KS_PASS: ${{ secrets.FOLLOWER_KEYSTORE_PASSWORD }} | |
| KEY_ALIAS: ${{ secrets.FOLLOWER_KEY_ALIAS }} | |
| KEY_PASS: ${{ secrets.FOLLOWER_KEY_PASSWORD }} | |
| run: | | |
| set -euo pipefail | |
| BT=$(ls -1d "$ANDROID_HOME"/build-tools/*/ | sort -V | tail -1) | |
| echo "$KEYSTORE_B64" | base64 -d > /tmp/follower.jks | |
| IN=aaps/app/build/outputs/apk/aapsclient/release/app-aapsclient-release-unsigned.apk | |
| [ -f "$IN" ] || IN=$(ls aaps/app/build/outputs/apk/aapsclient/release/*.apk | head -1) | |
| "${BT}zipalign" -p 4 "$IN" /tmp/follower-aligned.apk | |
| "${BT}apksigner" sign \ | |
| --ks /tmp/follower.jks --ks-pass "env:KS_PASS" \ | |
| --ks-key-alias "$KEY_ALIAS" --key-pass "env:KEY_PASS" \ | |
| --out diaswarm-follower.apk /tmp/follower-aligned.apk | |
| shred -u /tmp/follower.jks | |
| "${BT}apksigner" verify --print-certs diaswarm-follower.apk | tee signer.txt | |
| - name: Name the unsigned build for what it is | |
| if: env.HAS_KEY != 'true' | |
| run: cp $(ls aaps/app/build/outputs/apk/aapsclient/release/*.apk | head -1) diaswarm-follower-UNSIGNED.apk | |
| - name: Upload | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: diaswarm-follower | |
| path: | | |
| diaswarm-follower*.apk | |
| signer.txt | |
| - name: Attach to the release | |
| if: startsWith(github.ref, 'refs/tags/') | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| AAPS_SHA=$(git -C aaps rev-parse HEAD) | |
| cat > notes.md <<EOF | |
| Follower app — shows somebody else's glucose. **It cannot dose.** | |
| Separate \`applicationId\` (\`info.nightscout.aapsclient\`), no pump drivers | |
| compiled in, installs alongside AAPS rather than over it. | |
| **This is not the loop app, and this repository does not distribute one.** | |
| Built from: | |
| * diaswarm \`${GITHUB_SHA}\` | |
| * AndroidAPS \`${AAPS_REPO}@${AAPS_SHA}\` | |
| AndroidAPS is AGPL-3.0. The corresponding source for this binary is the | |
| two commits above; both repositories are public. | |
| EOF | |
| gh release create "${GITHUB_REF_NAME}" diaswarm-follower*.apk \ | |
| --repo "${GITHUB_REPOSITORY}" --title "${GITHUB_REF_NAME}" --notes-file notes.md |