Straighten skills/workflows and profile tab underlines #15
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 a SIGNED debug APK and upload it as an artifact, so teammates can install a | |
| # team-signed build (Google Drive works) WITHOUT having the shared keystore locally. | |
| # | |
| # Why a separate workflow from android-assets: the heavy part (the ~1 GB aarch64 rootfs, | |
| # built under QEMU, ~15 min) rarely changes — only when Ubuntu/node/claude/codex change. | |
| # A UI/server/Kotlin update does NOT need a new rootfs. So this workflow REUSES the latest | |
| # android-assets artifact's rootfs + jniLibs and only rebuilds the lightweight server | |
| # bundle (webview + localhost dist) before signing. No QEMU here -> fast (~5 min). | |
| # | |
| # Run android-assets when the rootfs deps change; run THIS for every UI/app change. | |
| # | |
| # Requires one repo secret: ANDROID_DEBUG_KEYSTORE_B64 = base64 of the shared debug | |
| # keystore (`base64 -i surfaces/android/agentnet-debug.keystore`). The signed APK never | |
| # contains the private key (only the public cert + signature), so the artifact is safe to | |
| # share. Manual / tag trigger only — never fork PRs, so the secret is never exposed to | |
| # untrusted code. | |
| name: android-apk | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| abi: | |
| description: "Target ABI" | |
| type: choice | |
| options: ["arm64", "x86_64"] | |
| default: "arm64" | |
| assets_run_id: | |
| description: "android-assets run id to reuse (blank = latest successful)" | |
| required: false | |
| default: "" | |
| # Auto-build a fresh signed APK whenever main gets an app/server/UI change, so the latest | |
| # team-signed APK is always waiting as an artifact. Paths-filtered so doc-only pushes don't | |
| # build. (Public repo -> GitHub-hosted minutes are free.) | |
| push: | |
| branches: [main] | |
| paths: | |
| - "surfaces/**" | |
| - "packages/**" | |
| - ".github/workflows/android-apk.yml" | |
| # One build per ref: a newer push to main cancels an in-progress build so only the latest wins. | |
| concurrency: | |
| group: android-apk-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: write | |
| actions: read | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| env: | |
| ABI: ${{ github.event.inputs.abi || 'arm64' }} | |
| GH_TOKEN: ${{ github.token }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Fail fast if the signing secret is missing — otherwise gradle would sign with an | |
| # auto-generated key whose SHA-1 isn't registered for Drive, producing a silently | |
| # wrong "team" APK. (Passed via env, not interpolated into the command.) | |
| - name: Require signing secret | |
| env: | |
| KS_B64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_B64 }} | |
| run: | | |
| if [ -z "$KS_B64" ]; then | |
| echo "::error::ANDROID_DEBUG_KEYSTORE_B64 is not set. Add it as a repo secret: base64 -i surfaces/android/agentnet-debug.keystore" | |
| exit 1 | |
| fi | |
| # Lightweight, arch-independent JS — rebuilt every run so the APK carries the latest UI. | |
| - uses: pnpm/action-setup@v4 | |
| with: { version: 9 } | |
| - uses: actions/setup-node@v4 | |
| with: { node-version: 22, cache: pnpm } | |
| - run: pnpm install --frozen-lockfile | |
| - run: pnpm --filter agentnet-localhost build | |
| - run: pnpm --filter agentnet-webview build | |
| # Reuse the rootfs + jniLibs from the latest android-assets run (no QEMU rebuild). | |
| - name: Resolve android-assets run | |
| id: assets | |
| run: | | |
| ID="${{ github.event.inputs.assets_run_id }}" | |
| if [ -z "$ID" ]; then | |
| ID=$(gh run list --workflow=android-assets.yml --status success -L 1 \ | |
| --json databaseId -q '.[0].databaseId' || true) | |
| fi | |
| if [ -z "$ID" ]; then | |
| echo "::error::No successful android-assets run found. Run the android-assets workflow first (it builds the rootfs), then re-run this." | |
| exit 1 | |
| fi | |
| echo "id=$ID" >> "$GITHUB_OUTPUT" | |
| - name: Download rootfs + jniLibs | |
| run: gh run download "${{ steps.assets.outputs.id }}" -n "android-assets-${ABI}" -D ci-assets | |
| - name: Stage assets into the source tree | |
| run: | | |
| set -e | |
| # assets/ is gitignored (its tars never get committed), so it doesn't exist on a | |
| # fresh checkout — create it before copying. | |
| mkdir -p surfaces/android/app/src/main/assets | |
| rm -rf surfaces/android/app/src/main/jniLibs | |
| cp -R ci-assets/jniLibs surfaces/android/app/src/main/jniLibs | |
| cp ci-assets/assets/rootfs-*.tar surfaces/android/app/src/main/assets/ | |
| # Repack the server bundle FRESH from this run's dist (don't reuse the artifact's | |
| # stale agentnet-server.tar) so the APK carries the current UI. | |
| STAGE="$(mktemp -d)/server-bundle" | |
| mkdir -p "$STAGE/webview" | |
| cp -R surfaces/localhost/dist/. "$STAGE/" | |
| cp -R surfaces/webview/dist/. "$STAGE/webview/" | |
| tar -cf surfaces/android/app/src/main/assets/agentnet-server.tar -C "$STAGE" . | |
| ls -lh surfaces/android/app/src/main/assets/ | |
| - uses: actions/setup-java@v4 | |
| with: { distribution: temurin, java-version: 17 } | |
| # Decode the shared keystore into the path build.gradle.kts expects. Removed at job end; | |
| # GitHub-hosted runners are ephemeral and isolated. | |
| - name: Install shared debug keystore | |
| env: | |
| KS_B64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_B64 }} | |
| run: printf '%s' "$KS_B64" | base64 -d > surfaces/android/agentnet-debug.keystore | |
| - name: Build signed debug APK | |
| working-directory: surfaces/android | |
| run: ./gradlew --no-daemon assembleDebug | |
| # SHA-1 is a public fingerprint (not the key), safe to log — proves the team key signed it. | |
| - name: Print signing SHA-1 | |
| working-directory: surfaces/android | |
| run: ./gradlew --no-daemon :app:signingReport | grep -E "Variant|SHA1" | head -20 | |
| - name: Remove keystore | |
| if: always() | |
| run: rm -f surfaces/android/agentnet-debug.keystore | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: agentnet-apk-${{ env.ABI }} | |
| path: surfaces/android/app/build/outputs/apk/debug/app-debug.apk | |
| retention-days: 7 | |
| if-no-files-found: error | |
| # Publish to the rolling `android-latest` release so the public download link | |
| # (.../releases/download/android-latest/app-debug.apk) always serves the newest build. | |
| # arm64 only, so a manual x86_64 build doesn't clobber the shareable arm64 APK. | |
| - name: Update android-latest release | |
| if: env.ABI == 'arm64' | |
| run: | | |
| APK=surfaces/android/app/build/outputs/apk/debug/app-debug.apk | |
| gh release view android-latest >/dev/null 2>&1 \ | |
| || gh release create android-latest --title "AgentNet Android (latest)" --latest \ | |
| --notes "Auto-built from main. Download app-debug.apk and install (allow unknown sources)." | |
| gh release upload android-latest "$APK" --clobber |