Files
subtracks/.github/workflows/release.yml
T
austinried 4bea164906 release: split the Play key write, guard the tag and attach the mapping
- write the Play service account in its own step so fastlane never has
  the secret in its environment (only the 0600 file path)
- fail the build when the tag does not match versionName
- serialize releases with a concurrency group
- attach the R8 mapping.txt to the GitHub release
2026-10-08 19:21:26 +00:00

148 lines
5.8 KiB
YAML

name: Release
on:
push:
tags:
- "v*"
concurrency:
group: release
cancel-in-progress: false
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-26.04
timeout-minutes: 60
permissions:
contents: read
outputs:
version-code: ${{ steps.build.outputs.version-code }}
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Check tag matches versionName
run: |
version=$(grep -oP 'versionName = "\K[^"]+' app/build.gradle.kts)
case "$GITHUB_REF_NAME" in
"v$version" | "v$version"-*) ;;
*)
echo "tag $GITHUB_REF_NAME does not match versionName $version" >&2
exit 1
;;
esac
- id: build
uses: ./.github/actions/build
with:
gradle-args: ":app:assembleRelease :app:bundleRelease --no-daemon"
sign: "true"
keystore-base64: ${{ secrets.SUBTRACKS_KEYSTORE_BASE64 }}
keyproperties-base64: ${{ secrets.SUBTRACKS_KEYPROPERTIES_BASE64 }}
keystore-password: ${{ secrets.SUBTRACKS_UPLOAD_STORE_PASSWORD }}
key-password: ${{ secrets.SUBTRACKS_UPLOAD_KEY_PASSWORD }}
- name: Export release certificate
env:
KEYSTORE_BASE64: ${{ secrets.SUBTRACKS_KEYSTORE_BASE64 }}
KEYPROPERTIES_BASE64: ${{ secrets.SUBTRACKS_KEYPROPERTIES_BASE64 }}
KEYSTORE_PASSWORD: ${{ secrets.SUBTRACKS_UPLOAD_STORE_PASSWORD }}
run: |
umask 077
trap 'rm -f "$RUNNER_TEMP/keystore"' EXIT
echo "$KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/keystore"
alias=$(printf '%s' "$KEYPROPERTIES_BASE64" | base64 -d | sed -n 's/^keyAlias=//p' | tr -d '\r')
nix develop --command keytool -exportcert -rfc -keystore "$RUNNER_TEMP/keystore" -alias "$alias" -storepass "$KEYSTORE_PASSWORD" -file app/build/outputs/release-certificate.pem
- uses: actions/upload-artifact@v7
with:
name: release
path: app/build/outputs
publish:
needs: build
runs-on: ubuntu-26.04
timeout-minutes: 60
permissions:
contents: write
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/download-artifact@v8
with:
name: release
path: dist
- uses: ./.github/actions/nix
- name: Detect stable tag
id: stable
run: |
if [[ "$GITHUB_REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "value=true" >> "$GITHUB_OUTPUT"
else
echo "value=false" >> "$GITHUB_OUTPUT"
fi
- name: Check release notes exist
run: test -f "metadata/en-US/changelogs/${{ needs.build.outputs.version-code }}.txt"
- name: Write Play service account
if: steps.stable.outputs.value == 'true'
env:
PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
run: |
umask 077
printf '%s' "$PLAY_SERVICE_ACCOUNT_JSON" > "$RUNNER_TEMP/play.json"
- name: Upload to Google Play
if: steps.stable.outputs.value == 'true'
run: |
nix develop .#release --command fastlane supply \
--aab dist/bundle/release/app-release.aab \
--package_name com.subtracks \
--track "${{ vars.PLAY_TRACK || 'internal' }}" \
--json_key "$RUNNER_TEMP/play.json" \
--mapping dist/mapping/release/mapping.txt
- name: Remove Play service account
if: always()
run: rm -f "$RUNNER_TEMP/play.json"
- name: Checksums and signature
id: checksums
run: |
cp dist/apk/release/app-release.apk subtracks.apk
cp dist/bundle/release/app-release.aab subtracks.aab
sha256sum subtracks.apk subtracks.aab > SHA256SUMS
echo "Artifact SHA-256:"
sha256sum subtracks.apk subtracks.aab
echo "APK signature:"
certs=$(nix develop --command bash -c '"$ANDROID_HOME/build-tools/37.0.0/apksigner" verify --print-certs subtracks.apk')
printf '%s\n' "$certs"
fingerprint=$(printf '%s\n' "$certs" | sed -n 's/.*certificate SHA-256 digest: //p' | head -1 | tr '[:lower:]' '[:upper:]' | sed 's/\(..\)/\1:/g; s/:$//')
echo "fingerprint=$fingerprint" >> "$GITHUB_OUTPUT"
- name: Compose release notes
run: |
{
cat "metadata/en-US/changelogs/${{ needs.build.outputs.version-code }}.txt"
echo
echo '---'
echo
echo "APK SHA-256: \`$(grep 'subtracks\.apk$' SHA256SUMS | cut -d ' ' -f1)\`"
echo
echo "AAB SHA-256: \`$(grep 'subtracks\.aab$' SHA256SUMS | cut -d ' ' -f1)\`"
echo
echo "Signing certificate SHA-256: \`${{ steps.checksums.outputs.fingerprint }}\`"
echo
echo 'The signing certificate is attached as `release-certificate.pem`.'
if [ "${{ steps.stable.outputs.value }}" = "true" ]; then
echo 'The `.aab` is the exact bundle uploaded to Google Play; the `.apk` is the universal APK for sideloading.'
else
echo 'The `.aab` is the built app bundle (this pre-release was not uploaded to Google Play); the `.apk` is the universal APK for sideloading.'
fi
} > release-notes.md
- name: Create GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
args=(--verify-tag --title "$GITHUB_REF_NAME" --notes-file release-notes.md)
if [ "${{ steps.stable.outputs.value }}" != "true" ]; then
args+=(--prerelease)
fi
gh release create "$GITHUB_REF_NAME" subtracks.apk subtracks.aab SHA256SUMS dist/release-certificate.pem dist/mapping/release/mapping.txt "${args[@]}"