Repository navigation
129 lines (109 loc) · 4.99 KB
/
Copy pathrelease.yml
File metadata and controls
129 lines (109 loc) · 4.99 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
name: Release
# Push a tag matching the app's versionName (e.g. v1.1.0-beta1) to build signed APKs and open a
# draft GitHub Release with them. Review the draft, then publish it by hand.
on:
push:
tags: ['v*']
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: write
env:
# SHA-256 of the release signing certificate. The build fails if the APKs are signed with any
# other key, so a wrong or rotated keystore secret can never ship.
EXPECTED_CERT_SHA256: bbb0e6655747e2b1df8304995b0784d213be63774c91277668dc8ab70161294e
jobs:
release:
name: Build signed release
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- name: Check out app
uses: actions/checkout@v5
- name: Check tag matches versionName
id: version
run: |
version=$(grep -oP 'versionName = "\K[^"]+' app/build.gradle.kts)
tag="${GITHUB_REF_NAME}"
if [ "$tag" != "v$version" ]; then
echo "::error::Tag $tag does not match versionName $version (expected v$version)"
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Set up build
uses: ./.github/actions/setup-build
- name: Unit tests
run: ./gradlew test --console=plain
# Tolerates line breaks, CRs and PEM-style header lines in the secret; on failure it reports
# only lengths and counts, never the secret itself.
- name: Decode release keystore
env:
RELEASE_KEYSTORE_BASE64: ${{ secrets.RELEASE_KEYSTORE_BASE64 }}
STORE_PASSWORD: ${{ secrets.RELEASE_STORE_PASSWORD }}
run: |
if [ -z "$RELEASE_KEYSTORE_BASE64" ]; then
echo "::error::RELEASE_KEYSTORE_BASE64 secret is not set"
exit 1
fi
b64=$(printf '%s' "$RELEASE_KEYSTORE_BASE64" | grep -v -- '-----' | tr -d ' \t\r\n')
if ! printf '%s' "$b64" | base64 -d > "$RUNNER_TEMP/release.jks" 2>/dev/null; then
bad=$(printf '%s' "$b64" | tr -d 'A-Za-z0-9+/=' | wc -c)
echo "::error::RELEASE_KEYSTORE_BASE64 is not valid base64: ${#b64} chars after trimming, $bad outside the base64 alphabet. Re-set it from the .jks file."
exit 1
fi
if ! keytool -list -keystore "$RUNNER_TEMP/release.jks" -storepass "$STORE_PASSWORD" -alias upload > /dev/null 2>&1; then
echo "::error::Decoded keystore ($(stat -c %s "$RUNNER_TEMP/release.jks") bytes) could not be opened with RELEASE_STORE_PASSWORD, or has no 'upload' alias."
exit 1
fi
- name: Build signed release APKs
env:
KEYSTORE_PATH: ${{ runner.temp }}/release.jks
STORE_PASSWORD: ${{ secrets.RELEASE_STORE_PASSWORD }}
KEY_PASSWORD: ${{ secrets.RELEASE_KEY_PASSWORD }}
run: ./gradlew assembleRelease --console=plain
- name: Remove release keystore
if: always()
run: rm -f "$RUNNER_TEMP/release.jks"
- name: Verify signatures and collect APKs
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
apksigner=$(ls -d "$ANDROID_HOME"/build-tools/* | sort -V | tail -1)/apksigner
mkdir dist
for apk in app/build/outputs/apk/release/app-*-release.apk; do
abi=$(basename "$apk" | sed -E 's/^app-(.+)-release\.apk$/\1/')
"$apksigner" verify "$apk"
cert=$("$apksigner" verify --print-certs "$apk" | grep -oP 'certificate SHA-256 digest: \K[0-9a-f]+' | head -1)
if [ "$cert" != "$EXPECTED_CERT_SHA256" ]; then
echo "::error::$apk is signed with $cert, expected $EXPECTED_CERT_SHA256"
exit 1
fi
cp "$apk" "dist/FlashForgeUI-$VERSION-$abi.apk"
done
ls dist/*.apk > /dev/null
(cd dist && sha256sum *.apk > SHA256SUMS.txt && cat SHA256SUMS.txt)
- name: Create draft release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.version.outputs.version }}
run: |
fingerprint=$(echo "$EXPECTED_CERT_SHA256" | tr 'a-f' 'A-F' | sed 's/../&:/g; s/:$//')
cat > notes.md <<EOF
## Install
Download the APK for your phone and open it to install:
| File | For |
|---|---|
| \`FlashForgeUI-$VERSION-arm64-v8a.apk\` | Almost all phones |
| \`FlashForgeUI-$VERSION-armeabi-v7a.apk\` | Older 32-bit phones |
| \`FlashForgeUI-$VERSION-x86_64.apk\` / \`x86\` | Emulators and x86 devices |
If you installed a test build from somewhere other than this page, uninstall it first.
## Verify (optional)
Signing certificate SHA-256:
\`$fingerprint\`
File checksums are in \`SHA256SUMS.txt\`.
EOF
prerelease=""
case "$VERSION" in *-*) prerelease="--prerelease" ;; esac
gh release create "$GITHUB_REF_NAME" dist/* --draft $prerelease \
--title "FlashForgeUI $VERSION" --notes-file notes.md