-
Notifications
You must be signed in to change notification settings - Fork 0
135 lines (117 loc) · 5.56 KB
/
Copy pathandroid.yml
File metadata and controls
135 lines (117 loc) · 5.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
name: android
on:
# No `paths` filter on push: a paths filter also applies to tag pushes, so a tag on a commit
# that did not touch android/ would never build or release. The workflow is cheap enough to
# run on every push to main; pull requests keep the filter.
push:
branches: [main, master]
tags:
- "android-v*"
pull_request:
paths:
- "android/**"
- ".github/workflows/android.yml"
workflow_dispatch:
permissions:
contents: write # needed only for the release step on tags
jobs:
build:
name: assembleRelease + assembleDebug + lint + unit tests
runs-on: ubuntu-latest # ships with the Android SDK; the build downloads platform 35 itself
defaults:
run:
working-directory: android
env:
# Release signing. The permanent key is the ANDROID_KEYSTORE_B64 secret (base64 of the
# .jks); it is decoded into the runner's temp dir below, never into the checkout. When the
# secrets are absent (a fork's pull request) the build still runs and produces an unsigned
# release APK; the signature check below then fails loudly instead of releasing it.
# ANDROID_KEYSTORE_FILE is set by the decode step (the runner context is not
# available in job-level env, only in steps).
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
distribution: temurin
java-version: "17"
- uses: gradle/actions/setup-gradle@v6
- name: Make the wrapper executable
run: chmod +x gradlew
- name: Decode the release keystore
env:
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
run: |
if [ -z "$ANDROID_KEYSTORE_B64" ]; then
echo "::warning::ANDROID_KEYSTORE_B64 secret not available; the release APK will be unsigned."
exit 0
fi
KS="$RUNNER_TEMP/localflow.jks"
# Strip any whitespace/CR the secret may carry (a Windows pipe adds CRLF); base64 -d
# rejects stray bytes with "invalid input".
printf '%s' "$ANDROID_KEYSTORE_B64" | tr -d '\r\n\t ' | base64 -d > "$KS"
if [ ! -s "$KS" ]; then echo "::error::keystore decoded to an empty file"; exit 1; fi
chmod 600 "$KS"
echo "ANDROID_KEYSTORE_FILE=$KS" >> "$GITHUB_ENV"
echo "Keystore decoded to $KS ($(stat -c %s "$KS") bytes)"
- name: Unit tests (WAV builder, text splice)
run: ./gradlew testDebugUnitTest --no-daemon --stacktrace
- name: Build debug APK
run: ./gradlew assembleDebug --no-daemon --stacktrace
- name: Build release APK (signed with the permanent key)
run: ./gradlew assembleRelease --no-daemon --stacktrace
- name: Lint
run: ./gradlew lintDebug --no-daemon
- name: Verify the release signature
# Fails if the APK is unsigned (secrets absent) or signed with anything but a valid key,
# and prints the certificate digest so every run shows which key signed it. The
# permanent LocalFlow key is documented in docs/ANDROID.md ("Verify the download").
run: |
if [ ! -f "$ANDROID_KEYSTORE_FILE" ]; then
if [[ "$GITHUB_REF" == refs/tags/android-v* ]]; then
echo "::error::No keystore on a release tag build; refusing to publish an unsigned APK."
exit 1
fi
echo "::warning::No keystore (secrets unavailable); skipping signature check. The release APK from this run is unsigned."
exit 0
fi
APKSIGNER=$(ls -1 "$ANDROID_HOME"/build-tools/*/apksigner | sort -V | tail -n 1)
echo "Using $APKSIGNER"
"$APKSIGNER" verify --print-certs app/build/outputs/apk/release/app-release.apk | tee apksigner.txt
DIGEST=$(grep -i 'SHA-256 digest' apksigner.txt | head -n 1 | awk '{print $NF}')
echo "Release APK signed by certificate SHA-256: $DIGEST"
echo "### Release APK signing certificate" >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
cat apksigner.txt >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"
- name: Rename release APK
# Tags are named android-vX.Y.Z; drop that prefix so the asset is LocalFlow-android-vX.Y.Z.apk
run: |
REF="${GITHUB_REF_NAME//\//-}"; REF="${REF#android-}"
echo "ASSET_NAME=LocalFlow-android-${REF}.apk" >> "$GITHUB_ENV"
cp app/build/outputs/apk/release/app-release.apk "LocalFlow-android-${REF}.apk"
- uses: actions/upload-artifact@v7
with:
name: app-release.apk
path: android/app/build/outputs/apk/release/app-release.apk
if-no-files-found: error
- uses: actions/upload-artifact@v7
with:
# Debug-key build for PR reviewers only; it is never attached to a release.
name: app-debug.apk
path: android/app/build/outputs/apk/debug/app-debug.apk
if-no-files-found: error
- uses: actions/upload-artifact@v7
with:
name: lint-report
path: android/app/build/reports/lint-results-debug.html
if-no-files-found: ignore
- name: Release on tag
if: startsWith(github.ref, 'refs/tags/android-v')
uses: softprops/action-gh-release@v3
with:
name: ${{ github.ref_name }}
files: android/${{ env.ASSET_NAME }}
generate_release_notes: true