Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions .github/workflows/android-dev-client.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Android dev client

# Builds the debug Expo dev client of apps/community-mobile for arm64 Android. The agent box (CLAUDE.md
# §11) installs it on its containerized Android device and runs the branch under test's JavaScript in
# it through Metro, so the native app is built here (Google's Android build tools do not run on that
# arm64 Linux box) and only when its native side changes.
#
# The artifact is named after the app's native fingerprint (scripts/native-fingerprint.mjs): one build
# serves every branch whose native side matches, and a run whose fingerprint already has a live
# artifact stops before building. Debug-signed, no secrets, nothing leaves this repo's artifacts.
#
# push to main / pull request touching the app or its dependencies -> build when the fingerprint is new
# manual run -> same, for any branch

on:
push:
branches: [main]
paths: &app_paths
- "apps/community-mobile/**"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- ".npmrc"
- "patches/**"
- ".github/workflows/android-dev-client.yml"
pull_request:
branches: [main]
paths: *app_paths
workflow_dispatch:

permissions:
contents: read
actions: read

concurrency:
group: android-dev-client-${{ github.ref }}
cancel-in-progress: true

jobs:
dev-client:
name: Android dev client (arm64)
runs-on: ubuntu-24.04
timeout-minutes: 60
env:
CI: "true"
EXPO_NO_DOTENV: "1"
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 9.12.0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile

- name: Fingerprint the native side
id: fingerprint
working-directory: apps/community-mobile
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
hash="$(node scripts/native-fingerprint.mjs)"
name="android-dev-client-$hash"
live="$(gh api "repos/$GITHUB_REPOSITORY/actions/artifacts?name=$name" --jq '[.artifacts[] | select(.expired | not)] | length')"
echo "name=$name" >> "$GITHUB_OUTPUT"
echo "live=$live" >> "$GITHUB_OUTPUT"
echo "fingerprint $hash, live artifacts: $live"

- if: steps.fingerprint.outputs.live == '0'
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"
- if: steps.fingerprint.outputs.live == '0'
uses: gradle/actions/setup-gradle@v4

- name: Build the debug dev client
if: steps.fingerprint.outputs.live == '0'
working-directory: apps/community-mobile
run: |
set -euo pipefail
npx expo prebuild --platform android --clean --no-install
(cd android && ./gradlew --no-daemon :app:assembleDebug -PreactNativeArchitectures=arm64-v8a)

- if: steps.fingerprint.outputs.live == '0'
uses: actions/upload-artifact@v4
with:
name: ${{ steps.fingerprint.outputs.name }}
path: apps/community-mobile/android/app/build/outputs/apk/debug/app-debug.apk
if-no-files-found: error
retention-days: 90
2 changes: 1 addition & 1 deletion apps/community-mobile/app.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ module.exports = ({ config }) => ({
name: "civfix",
slug: "civfix-community",
scheme: "civfix",
version: "1.3.1",
version: "1.4.0",
orientation: "portrait",
userInterfaceStyle: "automatic",
icon: "./assets/icon.png",
Expand Down
3 changes: 2 additions & 1 deletion apps/community-mobile/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "community-mobile",
"version": "1.3.1",
"version": "1.4.0",
"private": true,
"license": "AGPL-3.0-only",
"main": "expo-router/entry",
Expand Down Expand Up @@ -80,6 +80,7 @@
"zustand": "^5.0.1"
},
"devDependencies": {
"@expo/fingerprint": "~0.15.5",
"@types/react": "~19.1.0",
"eslint": "^9.16.0",
"eslint-config-expo": "~10.0.0",
Expand Down
20 changes: 20 additions & 0 deletions apps/community-mobile/scripts/native-fingerprint.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env node
// Prints the Android native fingerprint of the app: a hash of everything that ends up in the native
// build (dependencies with native code, config plugins, the native parts of app.config.js) and nothing
// that only changes JavaScript. A dev-client APK runs the JavaScript of any branch with the same
// fingerprint, so .github/workflows/android-dev-client.yml names its artifact after this hash and the
// agent box's Android QA installs the artifact matching the branch it tests. Both must compute it the
// same way, which is why it lives here.
//
// The `extra` section and the version fields are skipped: they carry the commit and the Play version
// code, which change on every build without changing any native code.
import { createFingerprintAsync, SourceSkips } from "@expo/fingerprint"
import { dirname, join } from "node:path"
import { fileURLToPath } from "node:url"

const appDir = join(dirname(fileURLToPath(import.meta.url)), "..")
const { hash } = await createFingerprintAsync(appDir, {
platforms: ["android"],
sourceSkips: SourceSkips.ExpoConfigExtraSection | SourceSkips.ExpoConfigVersions,
})
process.stdout.write(`${hash}\n`)
2 changes: 1 addition & 1 deletion apps/community-web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "community-web",
"version": "1.3.1",
"version": "1.4.0",
"private": true,
"license": "AGPL-3.0-only",
"description": "civfix public user web app: static-exported Next.js SPA over the civfix API.",
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading