forked from iiab/iiab-android
-
-
Notifications
You must be signed in to change notification settings - Fork 3
201 lines (172 loc) · 8.8 KB
/
Copy pathandroid-release-build.yml
File metadata and controls
201 lines (172 loc) · 8.8 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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
name: Build and Release K2Go APKs
on:
push:
# Only triggers automatically when a release tag is pushed (e.g., v1.0.0)
tags:
- 'v*'
workflow_dispatch:
jobs:
build-and-release:
name: Build Release APKs & Upload to R2
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./controller
steps:
- name: Checkout Code
uses: actions/checkout@v5
with:
submodules: recursive
# TODO (Future): Add a Jira step here to verify if the version exists in Jira
# or to extract release notes directly from a Jira board.
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
distribution: 'zulu'
java-version: '17'
cache: 'gradle'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# ADFA-4466: the google-services plugin needs google-services.json at build time;
# inject it from the dedicated K2Go analytics secret so the build compiles.
- name: Create google-services.json
env:
GOOGLE_SERVICES_JSON_K2GO_ANALYTICS: ${{ secrets.GOOGLE_SERVICES_JSON_K2GO_ANALYTICS }}
run: echo "$GOOGLE_SERVICES_JSON_K2GO_ANALYTICS" > app/google-services.json
- name: Decode Keystore
env:
ENCODED_STRING: ${{ secrets.KEYSTORE_BASE64 }}
run: |
echo "$ENCODED_STRING" | base64 -d > keystore.jks
# --- VERSION AUDIT ---
- name: Log Pinned Binary Version
run: |
echo "=========================================="
echo "Compiling with native binaries pinned to:"
cat binary_version.txt
echo ""
echo "=========================================="
# --- BUILD AND SIGNING ---
- name: Build and Sign Release APKs
env:
# :app:syncNativeArtifacts queries the GitHub API for the pinned binaries
# release; unauthenticated it gets HTTP 403 (shared-runner rate limit), so
# provide the auto token for the M15 authenticated-retry fallback (build.gradle).
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
# ADFA-4533: GlitchTip DSN for release builds
SENTRY_DSN_RELEASE: ${{ secrets.SENTRY_DSN_RELEASE }}
run: |
# Release tags ship a clean version (v0.7.0-beta), no commit hash. build.gradle only
# falls back to the git SHA suffix when versionSuffix is absent; pass it empty here.
# In-APK traceability is unaffected: BuildConfig.GIT_SHA still records the commit.
./gradlew assembleRelease \
-PversionSuffix="" \
-Pandroid.injected.signing.store.file=$(pwd)/keystore.jks \
-Pandroid.injected.signing.store.password=$KEYSTORE_PASSWORD \
-Pandroid.injected.signing.key.alias=$KEY_ALIAS \
-Pandroid.injected.signing.key.password=$KEY_PASSWORD
- name: Upload APK Artifacts
uses: actions/upload-artifact@v6
with:
name: k2go-apks-release
path: controller/**/build/outputs/apk/release/*.apk
retention-days: 7
# --- GITHUB RELEASES ---
- name: Create GitHub Release
if: startsWith(github.ref, 'refs/tags/v')
uses: softprops/action-gh-release@v3
with:
# Automatically marks as Pre-release if the tag ends in "-beta" or "-rc"
prerelease: ${{ contains(github.ref, '-beta') || contains(github.ref, '-rc') }}
files: controller/**/build/outputs/apk/release/*.apk
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# --- CLOUDFLARE R2 UPLOAD ---
- name: Upload to Cloudflare R2
if: startsWith(github.ref, 'refs/tags/v')
env:
AWS_ACCESS_KEY_ID: ${{ vars.CLOUDFLARE_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
R2_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
BUCKET_NAME: "k2go-apk-repo"
run: |
# Loop through all generated APKs (universal, arm64, armeabi) and upload them
for apk in $(find . -path "*/build/outputs/apk/release/*.apk"); do
filename=$(basename "$apk")
echo "Uploading $filename to Cloudflare R2 ($BUCKET_NAME)..."
# Use aws-cli configured to point to Cloudflare's S3-compatible API
aws s3 cp "$apk" "s3://$BUCKET_NAME/$filename" \
--endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" \
--content-type "application/vnd.android.package-archive"
done
# --- OTA MANIFEST (update.json) ---
# ADFA-4984: publish the OTA manifest LAST, after the APKs are already in R2, so it never
# points at a missing binary. update.json is a fixed key -> it overwrites the previous manifest
# and always resolves to the latest release (no .1/.2 copies). The changelog is copied verbatim
# from the top entry of controller/ci/ota-release-notes.md, which must match the release tag.
- name: Generate and upload update.json
if: startsWith(github.ref, 'refs/tags/v')
working-directory: ${{ github.workspace }}
env:
AWS_ACCESS_KEY_ID: ${{ vars.CLOUDFLARE_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
R2_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
BUCKET_NAME: "k2go-apk-repo"
run: |
set -euo pipefail
TAG="${GITHUB_REF#refs/tags/}"
echo "Release tag: $TAG"
# versionCodeBase = the app module's raw versionCode (the app divides the installed,
# ABI-multiplied code by 10 to compare). First versionCode in controller/app/build.gradle.
VCODE=$(grep -oE 'versionCode[[:space:]]+[0-9]+' controller/app/build.gradle | head -1 | grep -oE '[0-9]+')
echo "versionCodeBase: $VCODE"
# Top entry of controller/ci/ota-release-notes.md; its header must equal the tag (guard against stale notes).
HEADER=$(grep -m1 '^## ' controller/ci/ota-release-notes.md | sed 's/^##[[:space:]]*//')
if [ "$HEADER" != "$TAG" ]; then
echo "::error::controller/ci/ota-release-notes.md top entry '$HEADER' does not match tag '$TAG'. Update the notes before tagging."
exit 1
fi
# Body = the lines between the first '## ' header and the next one (blank lines trimmed).
CHANGELOG=$(awk '/^## /{n++; next} n==1{print}' controller/ci/ota-release-notes.md | sed '/^[[:space:]]*$/d')
echo "Changelog:"; printf '%s\n' "$CHANGELOG"
# Built APK basenames per ABI (the binaries just uploaded to R2).
find_apk() { find controller -path "*/build/outputs/apk/release/*$1*.apk" -printf '%f\n' | head -1; }
# The APK filenames abbreviate the split's ABI (arm64-v8a -> v8a); see build.gradle.
APK_ARM64=$(find_apk "-v8a-")
APK_ARM32=$(find_apk "-v7a-")
APK_UNIVERSAL=$(find_apk "-universal-")
echo "arm64=$APK_ARM64 | arm32=$APK_ARM32 | universal=$APK_UNIVERSAL"
if [ -z "$APK_ARM64" ] || [ -z "$APK_ARM32" ] || [ -z "$APK_UNIVERSAL" ]; then
echo "::error::Missing one or more built APKs (arm64='$APK_ARM64' arm32='$APK_ARM32' universal='$APK_UNIVERSAL'); aborting instead of publishing an incomplete manifest."
exit 1
fi
# jq escapes the multiline changelog safely into a JSON string.
jq -n \
--argjson vcode "$VCODE" \
--arg vname "$TAG" \
--arg changelog "$CHANGELOG" \
--arg arm64 "$APK_ARM64" \
--arg arm32 "$APK_ARM32" \
--arg universal "$APK_UNIVERSAL" \
'{versionCodeBase: $vcode, versionName: $vname, changelog: $changelog,
apk_arm64_v8a: $arm64, apk_armeabi_v7a: $arm32, apk_universal: $universal}' \
> update.json
echo "----- update.json -----"; cat update.json
# Upload LAST (after the APKs). Fixed key -> overwrites the previous manifest.
aws s3 cp update.json "s3://$BUCKET_NAME/update.json" \
--endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" \
--content-type "application/json"
# TODO (Future): Add Jira finalization step here to automatically mark
# the Jira version as "Released" and close the corresponding tickets.
# TODO (Future): Add a Slack notification step to announce the official release
# with links to the GitHub Release page and direct Cloudflare downloads.
# --- SECURITY CLEANUP ---
- name: Cleanup Keystore
if: always()
run: rm -f keystore.jks