mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
ci: share the build and isolate the release credentials
Extract checkout, nix, signing and the Gradle run into a reusable build workflow used by CI, the next build and the release build job. The build job holds only the signing secrets; the publish job is the only one with a write token and the Play credential. The Play service account is now written to a 0600 file and passed via --json_key instead of on argv.
This commit is contained in:
@@ -7,33 +7,12 @@ on:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
NIX_CONFIG: experimental-features = nix-command flakes
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1
|
||||
- uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.2
|
||||
with:
|
||||
primary-key: nix-v2-${{ hashFiles('flake.lock', '**/*.nix') }}
|
||||
restore-prefixes-first-match: nix-v2-
|
||||
gc-max-store-size-linux: 6G
|
||||
- name: Build next builds
|
||||
run: nix develop --command gradle :app:assembleDebug :app:assembleNext --no-daemon
|
||||
- name: Collect next builds
|
||||
run: |
|
||||
cp app/build/outputs/apk/debug/app-debug.apk subtracks-next-debug.apk
|
||||
cp app/build/outputs/apk/next/app-next.apk subtracks-next.apk
|
||||
- name: Upload next builds
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: subtracks-next
|
||||
path: |
|
||||
subtracks-next-debug.apk
|
||||
subtracks-next.apk
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
gradle-args: ":app:assembleDebug :app:assembleNext --no-daemon"
|
||||
artifact-name: subtracks-next
|
||||
artifact-path: app/build/outputs/apk
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
gradle-args:
|
||||
description: Gradle tasks and flags to run
|
||||
required: true
|
||||
type: string
|
||||
sign:
|
||||
description: Decode and use the store keystore
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
artifact-name:
|
||||
description: If set, upload the build outputs as an artifact with this name
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
artifact-path:
|
||||
description: Path uploaded when artifact-name is set
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
secrets:
|
||||
KEYSTORE_BASE64:
|
||||
required: false
|
||||
KEYPROPERTIES_BASE64:
|
||||
required: false
|
||||
KEYSTORE_PASSWORD:
|
||||
required: false
|
||||
KEY_PASSWORD:
|
||||
required: false
|
||||
outputs:
|
||||
version-code:
|
||||
description: versionCode read from app/build.gradle.kts
|
||||
value: ${{ jobs.build.outputs.version-code }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
NIX_CONFIG: experimental-features = nix-command flakes
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 60
|
||||
outputs:
|
||||
version-code: ${{ steps.version.outputs.code }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1
|
||||
- uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.2
|
||||
with:
|
||||
primary-key: nix-v2-${{ hashFiles('flake.lock', '**/*.nix') }}
|
||||
restore-prefixes-first-match: nix-v2-
|
||||
gc-max-store-size-linux: 6G
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
||||
restore-keys: gradle-
|
||||
- name: Decode signing material
|
||||
if: inputs.sign
|
||||
id: signing
|
||||
env:
|
||||
KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
|
||||
KEYPROPERTIES_BASE64: ${{ secrets.KEYPROPERTIES_BASE64 }}
|
||||
run: |
|
||||
echo "$KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/release.keystore"
|
||||
alias=$(printf '%s' "$KEYPROPERTIES_BASE64" | base64 -d | sed -n 's/^keyAlias=//p' | tr -d '\r')
|
||||
test -n "$alias" || { echo "no keyAlias in KEYPROPERTIES_BASE64" >&2; exit 1; }
|
||||
echo "alias=$alias" >> "$GITHUB_OUTPUT"
|
||||
- name: Build
|
||||
env:
|
||||
RELEASE_KEYSTORE: ${{ inputs.sign && format('{0}/release.keystore', runner.temp) || '' }}
|
||||
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
|
||||
RELEASE_KEY_ALIAS: ${{ steps.signing.outputs.alias }}
|
||||
RELEASE_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
|
||||
run: nix develop --command gradle ${{ inputs.gradle-args }}
|
||||
- name: Remove signing keystore
|
||||
if: always()
|
||||
run: rm -f "$RUNNER_TEMP/release.keystore"
|
||||
- name: Read version code
|
||||
id: version
|
||||
run: echo "code=$(grep -oP 'versionCode = \K\d+' app/build.gradle.kts)" >> "$GITHUB_OUTPUT"
|
||||
- name: Upload build outputs
|
||||
if: inputs.artifact-name != ''
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ inputs.artifact-name }}
|
||||
path: ${{ inputs.artifact-path }}
|
||||
+14
-12
@@ -9,22 +9,26 @@ concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.daemon=false
|
||||
NIX_CONFIG: experimental-features = nix-command flakes
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: sh
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
gradle-args: ":app:ktlintCheck :app:lintDebug :app:testDebugUnitTest --no-configuration-cache --console=plain --no-daemon"
|
||||
|
||||
integration:
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 90
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
GRADLE_OPTS: -Dorg.gradle.daemon=false
|
||||
NIX_CONFIG: experimental-features = nix-command flakes
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1
|
||||
- uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.2
|
||||
with:
|
||||
@@ -33,13 +37,11 @@ jobs:
|
||||
gc-max-store-size-linux: 6G
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
path: ~/.gradle/caches
|
||||
key: gradle-${{ hashFiles('**/*.gradle*', '**/gradle.properties', 'gradle/libs.versions.toml', 'gradle/wrapper/gradle-wrapper.properties') }}
|
||||
restore-keys: gradle-
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: .integration/music
|
||||
key: integration-music-v1
|
||||
- run: nix develop --command gradle :app:ktlintCheck :app:lintDebug :app:testDebugUnitTest --no-configuration-cache --console=plain
|
||||
- run: nix develop .#integration --command nu tools/integration-test.nu
|
||||
|
||||
@@ -6,62 +6,65 @@ on:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
NIX_CONFIG: experimental-features = nix-command flakes
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release:
|
||||
build:
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
gradle-args: ":app:assembleRelease :app:bundleRelease --no-daemon"
|
||||
sign: true
|
||||
artifact-name: release
|
||||
artifact-path: app/build/outputs
|
||||
secrets:
|
||||
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 }}
|
||||
|
||||
publish:
|
||||
needs: build
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 60
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
NIX_CONFIG: experimental-features = nix-command flakes
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: release
|
||||
path: dist
|
||||
- uses: cachix/install-nix-action@13d8dd58da0234aa297dedd986986ccb8e7f3e24 # v31.11.1
|
||||
- uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.2
|
||||
with:
|
||||
primary-key: nix-v2-${{ hashFiles('flake.lock', '**/*.nix') }}
|
||||
restore-prefixes-first-match: nix-v2-
|
||||
gc-max-store-size-linux: 6G
|
||||
- name: Decode signing material
|
||||
id: signing
|
||||
env:
|
||||
KEYSTORE_BASE64: ${{ secrets.SUBTRACKS_KEYSTORE_BASE64 }}
|
||||
KEYPROPERTIES_BASE64: ${{ secrets.SUBTRACKS_KEYPROPERTIES_BASE64 }}
|
||||
run: |
|
||||
echo "$KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/release.keystore"
|
||||
alias=$(printf '%s' "$KEYPROPERTIES_BASE64" | base64 -d | sed -n 's/^keyAlias=//p' | tr -d '\r')
|
||||
test -n "$alias" || { echo "no keyAlias found in SUBTRACKS_KEYPROPERTIES_BASE64" >&2; exit 1; }
|
||||
echo "alias=$alias" >> "$GITHUB_OUTPUT"
|
||||
- name: Build APK and bundle
|
||||
env:
|
||||
RELEASE_KEYSTORE: ${{ runner.temp }}/release.keystore
|
||||
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.SUBTRACKS_UPLOAD_STORE_PASSWORD }}
|
||||
RELEASE_KEY_ALIAS: ${{ steps.signing.outputs.alias }}
|
||||
RELEASE_KEY_PASSWORD: ${{ secrets.SUBTRACKS_UPLOAD_KEY_PASSWORD }}
|
||||
run: nix develop --command gradle :app:assembleRelease :app:bundleRelease --no-daemon
|
||||
- name: Remove signing keystore
|
||||
if: always()
|
||||
run: rm -f "$RUNNER_TEMP/release.keystore"
|
||||
- name: Read version code
|
||||
id: version
|
||||
run: echo "code=$(grep -oP 'versionCode = \K\d+' app/build.gradle.kts)" >> "$GITHUB_OUTPUT"
|
||||
- name: Upload to Google Play
|
||||
env:
|
||||
PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
|
||||
run: |
|
||||
umask 077
|
||||
printf '%s' "$PLAY_SERVICE_ACCOUNT_JSON" > "$RUNNER_TEMP/play.json"
|
||||
nix develop .#release --command fastlane supply \
|
||||
--aab app/build/outputs/bundle/release/app-release.aab \
|
||||
--aab dist/bundle/release/app-release.aab \
|
||||
--package_name com.subtracks \
|
||||
--track "${{ vars.PLAY_TRACK || 'internal' }}" \
|
||||
--release_status completed \
|
||||
--json_key_data "$PLAY_SERVICE_ACCOUNT_JSON" \
|
||||
--json_key "$RUNNER_TEMP/play.json" \
|
||||
--metadata_path metadata \
|
||||
--mapping app/build/outputs/mapping/release/mapping.txt
|
||||
--mapping dist/mapping/release/mapping.txt
|
||||
- name: Remove Play service account
|
||||
if: always()
|
||||
run: rm -f "$RUNNER_TEMP/play.json"
|
||||
- name: Collect release APK
|
||||
run: cp app/build/outputs/apk/release/app-release.apk subtracks.apk
|
||||
run: cp dist/apk/release/app-release.apk subtracks.apk
|
||||
- name: Create GitHub release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -69,4 +72,4 @@ jobs:
|
||||
gh release create "$GITHUB_REF_NAME" subtracks.apk \
|
||||
--verify-tag \
|
||||
--title "$GITHUB_REF_NAME" \
|
||||
--notes-file "metadata/en-US/changelogs/${{ steps.version.outputs.code }}.txt"
|
||||
--notes-file "metadata/en-US/changelogs/${{ needs.build.outputs.version-code }}.txt"
|
||||
|
||||
Reference in New Issue
Block a user