Skip to content

Commit 6662645

Browse files
committed
refactor(ci): extract release manifest logic into shared script
Consolidate Play API track fetching, manifest updates, and conditional patch bumping into scripts/update-release-manifest.sh. Both bump-patch and build-fcash2-upload-android workflows now call the same script, with commits handled by the workflows themselves. Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 972254b commit 6662645

3 files changed

Lines changed: 72 additions & 148 deletions

File tree

.github/workflows/build-fcash2-upload-android.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,19 @@ jobs:
112112
PLAYSTORE_TRACK: ${{ github.event.inputs.track }}
113113
RELEASE_STATUS: ${{ github.event.inputs.release_status }}
114114

115+
- name: Update release manifest
116+
id: manifest
117+
env:
118+
SERVICE_ACCOUNT_KEY_JSON: ${{ steps.service_account_json_file.outputs.filePath }}
119+
run: bash scripts/update-release-manifest.sh
120+
121+
- name: Commit & push manifest
122+
uses: actions-js/push@master
123+
with:
124+
message: "build: update release manifest"
125+
branch: "code/cash"
126+
github_token: ${{ secrets.BOT_GITHUB_TOKEN }}
127+
115128
- name: Upload build artifacts
116129
uses: actions/upload-artifact@v4
117130
if: always()

.github/workflows/bump-patch.yml

Lines changed: 7 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -15,145 +15,25 @@ jobs:
1515
fetch-depth: 0
1616
ref: "code/cash"
1717

18-
- name: Verify Packaging.kt exists
19-
run: |
20-
if [ ! -f buildSrc/src/main/java/Packaging.kt ]; then
21-
echo "Error: Packaging.kt not found"
22-
exit 1
23-
fi
24-
2518
- name: Decode Service Account Key JSON file
2619
uses: timheuer/base64-to-file@v1
2720
id: service_account_json_file
2821
with:
2922
fileName: serviceAccount.json
3023
encodedString: ${{ secrets.FLIPCASH_SERVICE_ACCOUNT_KEY_JSON }}
3124

32-
- name: Get Play API access token
33-
id: token
34-
env:
35-
SA_PATH: ${{ steps.service_account_json_file.outputs.filePath }}
36-
run: |
37-
NOW=$(date +%s); EXP=$((NOW + 3600))
38-
CLIENT_EMAIL=$(jq -r .client_email "$SA_PATH")
39-
PRIVATE_KEY=$(jq -r .private_key "$SA_PATH")
40-
41-
HEADER=$(echo -n '{"alg":"RS256","typ":"JWT"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')
42-
PAYLOAD=$(jq -nc \
43-
--arg iss "$CLIENT_EMAIL" \
44-
--arg scope "https://www.googleapis.com/auth/androidpublisher" \
45-
--argjson iat $NOW --argjson exp $EXP \
46-
'{iss:$iss, scope:$scope, aud:"https://oauth2.googleapis.com/token", iat:$iat, exp:$exp}' \
47-
| base64 -w0 | tr '+/' '-_' | tr -d '=')
48-
49-
SIG=$(printf '%s.%s' "$HEADER" "$PAYLOAD" \
50-
| openssl dgst -sha256 -sign <(echo "$PRIVATE_KEY") \
51-
| base64 -w0 | tr '+/' '-_' | tr -d '=')
52-
53-
ACCESS=$(curl -s -X POST https://oauth2.googleapis.com/token \
54-
-d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
55-
-d "assertion=$HEADER.$PAYLOAD.$SIG" | jq -r .access_token)
56-
57-
if [ -z "$ACCESS" ] || [ "$ACCESS" = "null" ]; then
58-
echo "Error: Failed to mint Play API access token"
59-
exit 1
60-
fi
61-
62-
echo "::add-mask::$ACCESS"
63-
echo "access_token=$ACCESS" >> "$GITHUB_OUTPUT"
64-
65-
- name: Fetch tracks and update manifest
25+
- name: Update release manifest
6626
id: manifest
6727
env:
68-
ACCESS: ${{ steps.token.outputs.access_token }}
69-
PKG: com.flipcash.app.android
70-
MANIFEST_PATH: .well-known/release-manifest.json
71-
run: |
72-
AUTH="Authorization: Bearer $ACCESS"
73-
BASE="https://androidpublisher.googleapis.com/androidpublisher/v3/applications/$PKG"
74-
EDIT_ID=$(curl -s -X POST "$BASE/edits" -H "$AUTH" | jq -r .id)
75-
76-
if [ -z "$EDIT_ID" ] || [ "$EDIT_ID" = "null" ]; then
77-
echo "Error: Failed to create Play API edit"
78-
exit 1
79-
fi
80-
81-
fetch_track() {
82-
curl -s "$BASE/edits/$EDIT_ID/tracks/$1" -H "$AUTH" \
83-
| jq -c '[.releases[]? | select(.status == "completed") | .versionCodes[]?] | map(tonumber) | max // null'
84-
}
85-
86-
PROD=$(fetch_track production)
87-
BETA=$(fetch_track beta)
88-
ALPHA=$(fetch_track alpha)
89-
INTERNAL=$(fetch_track internal)
90-
91-
curl -s -X DELETE "$BASE/edits/$EDIT_ID" -H "$AUTH" || true
92-
93-
OLD_PROD=$(jq -r '.tracks.production // empty' "$MANIFEST_PATH" 2>/dev/null || echo "")
94-
95-
mkdir -p "$(dirname "$MANIFEST_PATH")"
96-
jq -n \
97-
--argjson production "$PROD" \
98-
--argjson beta "$BETA" \
99-
--argjson alpha "$ALPHA" \
100-
--argjson internal "$INTERNAL" \
101-
--arg updated "$(date -u +%FT%TZ)" \
102-
'{updated: $updated, tracks: {production:$production, beta:$beta, alpha:$alpha, internal:$internal}}' \
103-
> "$MANIFEST_PATH"
104-
105-
echo "old_prod=$OLD_PROD" >> "$GITHUB_OUTPUT"
106-
echo "new_prod=$PROD" >> "$GITHUB_OUTPUT"
107-
cat "$MANIFEST_PATH"
108-
109-
- name: Determine if production changed
110-
id: check
111-
run: |
112-
if [ "${{ steps.manifest.outputs.old_prod }}" = "${{ steps.manifest.outputs.new_prod }}" ]; then
113-
echo "Production unchanged (${{ steps.manifest.outputs.new_prod }}) — manifest refreshed, skipping patch bump"
114-
echo "prod_changed=false" >> "$GITHUB_OUTPUT"
115-
else
116-
echo "prod_changed=true" >> "$GITHUB_OUTPUT"
117-
fi
118-
119-
- name: Read current patch version and increment
120-
if: steps.check.outputs.prod_changed == 'true'
121-
id: version
122-
run: |
123-
CURRENT=$(sed -n '/object Flipcash : Packaging(/,/)/ s/.*patchVersion = \([0-9][0-9]*\).*/\1/p' buildSrc/src/main/java/Packaging.kt)
124-
MAJOR=$(sed -n '/object Flipcash : Packaging(/,/)/ s/.*majorVersion = \([0-9][0-9]*\).*/\1/p' buildSrc/src/main/java/Packaging.kt)
125-
MINOR=$(sed -n '/object Flipcash : Packaging(/,/)/ s/.*minorVersion = \([0-9][0-9]*\).*/\1/p' buildSrc/src/main/java/Packaging.kt)
126-
127-
if [ -z "$CURRENT" ] || [ -z "$MAJOR" ] || [ -z "$MINOR" ]; then
128-
echo "Error: Failed to parse version fields from Packaging.kt"
129-
echo " major=$MAJOR minor=$MINOR patch=$CURRENT"
130-
exit 1
131-
fi
132-
133-
NEXT=$((CURRENT + 1))
134-
echo "CURRENT=$CURRENT" >> $GITHUB_OUTPUT
135-
echo "NEXT=$NEXT" >> $GITHUB_OUTPUT
136-
echo "VERSION=$MAJOR.$MINOR.$NEXT" >> $GITHUB_OUTPUT
137-
138-
- name: Update patchVersion
139-
if: steps.check.outputs.prod_changed == 'true'
140-
run: |
141-
sed -i '/object Flipcash : Packaging(/,/)/ s/patchVersion = [0-9][0-9]*/patchVersion = ${{ steps.version.outputs.NEXT }}/' buildSrc/src/main/java/Packaging.kt
142-
143-
- name: Validate updated field
144-
if: steps.check.outputs.prod_changed == 'true'
145-
run: |
146-
if ! grep -A 10 'object Flipcash : Packaging(' buildSrc/src/main/java/Packaging.kt | grep -q "patchVersion = ${{ steps.version.outputs.NEXT }}"; then
147-
echo "Error: Failed to update patchVersion to ${{ steps.version.outputs.NEXT }}"
148-
exit 1
149-
fi
28+
SERVICE_ACCOUNT_KEY_JSON: ${{ steps.service_account_json_file.outputs.filePath }}
29+
run: bash scripts/update-release-manifest.sh
15030

15131
- name: Commit & Push changes
15232
uses: actions-js/push@master
15333
with:
15434
message: >-
155-
${{ steps.check.outputs.prod_changed == 'true'
156-
&& format('build: release versionCode {0}, bump Flipcash to {1}', steps.manifest.outputs.new_prod, steps.version.outputs.VERSION)
157-
|| 'chore: refresh release manifest' }}
35+
${{ steps.manifest.outputs.prod_changed == 'true'
36+
&& format('build: release versionCode {0}, bump Flipcash to {1}', steps.manifest.outputs.new_prod, steps.manifest.outputs.version)
37+
|| 'build: refresh release manifest' }}
15838
branch: "code/cash"
159-
github_token: ${{ secrets.BOT_GITHUB_TOKEN }}
39+
github_token: ${{ secrets.BOT_GITHUB_TOKEN }}
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
#!/usr/bin/env bash
2-
# scripts/dry-run-release.sh
2+
# scripts/update-release-manifest.sh
3+
#
4+
# Fetches current track versionCodes from the Play Developer API,
5+
# updates .well-known/release-manifest.json in place, and bumps
6+
# Flipcash.patchVersion in Packaging.kt when production changes.
7+
#
8+
# Env:
9+
# SERVICE_ACCOUNT_KEY_JSON — path to the Google service account JSON file
10+
# PKG — package name (default: com.flipcash.app.android)
11+
# MANIFEST_PATH — manifest location (default: .well-known/release-manifest.json)
12+
#
13+
# Outputs (written to $GITHUB_OUTPUT when running in CI):
14+
# old_prod / new_prod — previous and current production versionCode
15+
# prod_changed — "true" | "false"
16+
# version — new versionName (only when prod_changed=true)
17+
318
set -euo pipefail
419

5-
set -a
6-
source .env.local
7-
set +a
20+
# Source .env.local for local development if present
21+
if [ -f .env.local ]; then
22+
set -a; source .env.local; set +a
23+
fi
824

925
: "${SERVICE_ACCOUNT_KEY_JSON:?SERVICE_ACCOUNT_KEY_JSON not set}"
1026
[ -f "$SERVICE_ACCOUNT_KEY_JSON" ] || { echo "SA file not found at $SERVICE_ACCOUNT_KEY_JSON"; exit 1; }
@@ -13,6 +29,12 @@ SA_PATH="$SERVICE_ACCOUNT_KEY_JSON"
1329
PKG="${PKG:-com.flipcash.app.android}"
1430
MANIFEST_PATH="${MANIFEST_PATH:-.well-known/release-manifest.json}"
1531

32+
# --- helper: write to $GITHUB_OUTPUT when in CI, otherwise just print ---
33+
emit() {
34+
echo "$1=$2"
35+
[ -n "${GITHUB_OUTPUT:-}" ] && echo "$1=$2" >> "$GITHUB_OUTPUT"
36+
}
37+
1638
# --- mint access token ---
1739
NOW=$(date +%s); EXP=$((NOW + 3600))
1840
CLIENT_EMAIL=$(jq -r .client_email "$SA_PATH")
@@ -33,13 +55,13 @@ ACCESS=$(curl -s -X POST https://oauth2.googleapis.com/token \
3355
-d "assertion=$HEADER.$PAYLOAD.$SIG" | jq -r .access_token)
3456

3557
[ "$ACCESS" = "null" ] && { echo "Token request failed"; exit 1; }
36-
echo "Got access token"
58+
echo "Got access token"
3759

3860
# --- fetch tracks ---
3961
AUTH="Authorization: Bearer $ACCESS"
4062
BASE="https://androidpublisher.googleapis.com/androidpublisher/v3/applications/$PKG"
4163
EDIT_ID=$(curl -s -X POST "$BASE/edits" -H "$AUTH" | jq -r .id)
42-
echo "Created edit $EDIT_ID"
64+
echo "Created edit $EDIT_ID"
4365

4466
fetch_track() {
4567
curl -s "$BASE/edits/$EDIT_ID/tracks/$1" -H "$AUTH" \
@@ -50,48 +72,57 @@ PROD=$(fetch_track production)
5072
BETA=$(fetch_track beta)
5173
ALPHA=$(fetch_track alpha)
5274
INTERNAL=$(fetch_track internal)
53-
echo "Tracks: prod=$PROD beta=$BETA alpha=$ALPHA internal=$INTERNAL"
75+
echo "Tracks: prod=$PROD beta=$BETA alpha=$ALPHA internal=$INTERNAL"
5476

5577
curl -s -X DELETE "$BASE/edits/$EDIT_ID" -H "$AUTH" >/dev/null || true
5678

57-
# --- read previous prod from committed manifest (not /tmp) ---
79+
# --- read previous prod ---
5880
OLD_PROD=$(jq -r '.tracks.production // empty' "$MANIFEST_PATH" 2>/dev/null || echo "")
59-
echo "Previous prod: ${OLD_PROD:-<none>} | New prod: $PROD"
81+
echo "Previous prod: ${OLD_PROD:-<none>} | New prod: $PROD"
6082

61-
# --- write manifest to /tmp (not the real path) ---
62-
OUT=.well-known/release-manifest.json
83+
# --- write manifest ---
84+
mkdir -p "$(dirname "$MANIFEST_PATH")"
6385
jq -n \
6486
--argjson production "$PROD" \
6587
--argjson beta "$BETA" \
6688
--argjson alpha "$ALPHA" \
6789
--argjson internal "$INTERNAL" \
6890
--arg updated "$(date -u +%FT%TZ)" \
6991
'{updated: $updated, tracks: {production:$production, beta:$beta, alpha:$alpha, internal:$internal}}' \
70-
> "$OUT"
92+
> "$MANIFEST_PATH"
93+
94+
echo "Manifest written:"
95+
cat "$MANIFEST_PATH"
7196

72-
echo "✓ Manifest written to $OUT"
73-
cat "$OUT"
97+
emit "old_prod" "${OLD_PROD:-null}"
98+
emit "new_prod" "$PROD"
7499

75100
# --- decide whether prod changed ---
76101
if [ "$OLD_PROD" = "$PROD" ]; then
77-
echo "→ Production unchanged, skipping patch bump"
102+
echo "Production unchanged, skipping patch bump"
103+
emit "prod_changed" "false"
78104
exit 0
79105
fi
80106

81-
echo "→ Production changed ($OLD_PROD$PROD), prepping development"
107+
emit "prod_changed" "true"
108+
echo "Production changed ($OLD_PROD -> $PROD), bumping patch version"
82109

83-
# --- test the sed against a copy of Packaging.kt ---
110+
# --- bump patch version ---
84111
KOTLIN_FILE=buildSrc/src/main/java/Packaging.kt
85112
[ -f "$KOTLIN_FILE" ] || { echo "Skipping patch bump — not in repo root"; exit 0; }
86113

87-
CURRENT=$(sed -n '/object Flipcash : Packaging(/,/)/ s/.*patchVersion = \([0-9][0-9]*\).*/\1/p' $KOTLIN_FILE)
114+
CURRENT=$(sed -n '/object Flipcash : Packaging(/,/)/ s/.*patchVersion = \([0-9][0-9]*\).*/\1/p' "$KOTLIN_FILE")
115+
MAJOR=$(sed -n '/object Flipcash : Packaging(/,/)/ s/.*majorVersion = \([0-9][0-9]*\).*/\1/p' "$KOTLIN_FILE")
116+
MINOR=$(sed -n '/object Flipcash : Packaging(/,/)/ s/.*minorVersion = \([0-9][0-9]*\).*/\1/p' "$KOTLIN_FILE")
117+
88118
[ -z "$CURRENT" ] && { echo "Failed to parse current patchVersion"; exit 1; }
89119
NEXT=$((CURRENT + 1))
90120

91121
if sed --version >/dev/null 2>&1; then
92-
sed -i "/object Flipcash : Packaging(/,/)/ s/patchVersion = [0-9][0-9]*/patchVersion = $NEXT/" $KOTLIN_FILE
122+
sed -i "/object Flipcash : Packaging(/,/)/ s/patchVersion = [0-9][0-9]*/patchVersion = $NEXT/" "$KOTLIN_FILE"
93123
else
94-
sed -i '' "/object Flipcash : Packaging(/,/)/ s/patchVersion = [0-9][0-9]*/patchVersion = $NEXT/" $KOTLIN_FILE
124+
sed -i '' "/object Flipcash : Packaging(/,/)/ s/patchVersion = [0-9][0-9]*/patchVersion = $NEXT/" "$KOTLIN_FILE"
95125
fi
96126

97-
echo "✓ Patch bump: $CURRENT$NEXT"
127+
emit "version" "$MAJOR.$MINOR.$NEXT"
128+
echo "Patch bump: $CURRENT -> $NEXT ($MAJOR.$MINOR.$NEXT)"

0 commit comments

Comments
 (0)