fix: unique build number and split build/upload steps
- Pass --build-number $(date +%s) to flutter build for both APK and AAB so each CI run gets a unique version code (fixes "already been used" error) - Extract UploadToPlayStore(aab, playStoreConfig) as its own Dagger function so the build and upload are independently callable - Add build-android-bundle task (exports AAB via dagger export) and upload-android-bundle task (calls UploadToPlayStore with the local file) - CI deploy-playstore job now has two steps: Build Android Bundle and Upload to Play Store, so a failed upload can be retried without rebuilding - deploy-apk also gets --build-number to avoid version code collisions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
484a183a19
commit
8783bcf5f0
@@ -105,13 +105,18 @@ jobs:
|
||||
DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }}
|
||||
run: scripts/setup_dagger_remote.sh
|
||||
|
||||
- name: Build & Deploy to Play Store
|
||||
- name: Build Android Bundle
|
||||
env:
|
||||
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
|
||||
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
||||
DAGGER_NO_NAG: "1"
|
||||
run: task build-android-bundle
|
||||
|
||||
- name: Upload to Play Store
|
||||
env:
|
||||
PLAY_STORE_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG_JSON }}
|
||||
DAGGER_NO_NAG: "1"
|
||||
run: task publish-android
|
||||
run: task upload-android-bundle
|
||||
|
||||
- name: Build & Deploy APK to server
|
||||
continue-on-error: true
|
||||
|
||||
+25
-3
@@ -203,8 +203,29 @@ tasks:
|
||||
cmds:
|
||||
- HASH=$(git rev-parse --short HEAD) && dagger call --progress=plain -q -m ci --source=. deploy-linux --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" --commit-hash "$HASH"
|
||||
|
||||
build-android-bundle:
|
||||
desc: Build signed AAB via Dagger and export to build/app/outputs/bundle/release/
|
||||
preconditions:
|
||||
- sh: test -n "$ANDROID_KEYSTORE_BASE64"
|
||||
msg: "ANDROID_KEYSTORE_BASE64 is not set"
|
||||
- sh: test -n "$ANDROID_KEYSTORE_PASSWORD"
|
||||
msg: "ANDROID_KEYSTORE_PASSWORD is not set"
|
||||
cmds:
|
||||
- mkdir -p build/app/outputs/bundle/release
|
||||
- dagger call --progress=plain -m ci --source=. build-android-release --keystore-base64 env:ANDROID_KEYSTORE_BASE64 --keystore-password env:ANDROID_KEYSTORE_PASSWORD --build-number "$(date +%s)" export --path build/app/outputs/bundle/release/app-release.aab
|
||||
|
||||
upload-android-bundle:
|
||||
desc: Upload AAB from build/ to Play Store via Dagger
|
||||
preconditions:
|
||||
- sh: test -n "$PLAY_STORE_CONFIG_JSON"
|
||||
msg: "PLAY_STORE_CONFIG_JSON is not set"
|
||||
- sh: test -f build/app/outputs/bundle/release/app-release.aab
|
||||
msg: "AAB not found — run build-android-bundle first"
|
||||
cmds:
|
||||
- dagger call --progress=plain -q -m ci --source=. upload-to-play-store --aab build/app/outputs/bundle/release/app-release.aab --play-store-config env:PLAY_STORE_CONFIG_JSON
|
||||
|
||||
publish-android:
|
||||
desc: Build and publish Android App Bundle to Play Store via Dagger
|
||||
desc: Build signed AAB and publish to Play Store via Dagger (build + upload)
|
||||
preconditions:
|
||||
- sh: test -n "$PLAY_STORE_CONFIG_JSON"
|
||||
msg: "PLAY_STORE_CONFIG_JSON is not set"
|
||||
@@ -213,7 +234,8 @@ tasks:
|
||||
- sh: test -n "$ANDROID_KEYSTORE_PASSWORD"
|
||||
msg: "ANDROID_KEYSTORE_PASSWORD is not set"
|
||||
cmds:
|
||||
- dagger call --progress=plain -q -m ci --source=. publish-android --play-store-config env:PLAY_STORE_CONFIG_JSON --keystore-base64 env:ANDROID_KEYSTORE_BASE64 --keystore-password env:ANDROID_KEYSTORE_PASSWORD
|
||||
- task: build-android-bundle
|
||||
- task: upload-android-bundle
|
||||
|
||||
deploy-apk:
|
||||
desc: Build and deploy Android APK via Dagger
|
||||
@@ -225,7 +247,7 @@ tasks:
|
||||
- sh: test -n "$ANDROID_KEYSTORE_PASSWORD"
|
||||
msg: "ANDROID_KEYSTORE_PASSWORD is not set"
|
||||
cmds:
|
||||
- HASH=$(git rev-parse --short HEAD) && dagger call --progress=plain -q -m ci --source=. deploy-apk --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" --commit-hash "$HASH" --keystore-base64 env:ANDROID_KEYSTORE_BASE64 --keystore-password env:ANDROID_KEYSTORE_PASSWORD
|
||||
- HASH=$(git rev-parse --short HEAD) && dagger call --progress=plain -q -m ci --source=. deploy-apk --ssh-key env:SSH_PRIVATE_KEY --ssh-user "$SSH_USER" --ssh-host "$SSH_HOST" --commit-hash "$HASH" --keystore-base64 env:ANDROID_KEYSTORE_BASE64 --keystore-password env:ANDROID_KEYSTORE_PASSWORD --build-number "$(date +%s)"
|
||||
|
||||
publish-website:
|
||||
desc: Build and publish website via Dagger
|
||||
|
||||
+11
-18
@@ -351,10 +351,10 @@ func (m *Ci) setupKeystore(keystoreBase64 *dagger.Secret, keystorePassword *dagg
|
||||
WithExec([]string{"/bin/sh", "-c", `echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > android/app/upload-keystore.jks`})
|
||||
}
|
||||
|
||||
// Build and return the Android APK
|
||||
func (m *Ci) BuildAndroidApk(keystoreBase64 *dagger.Secret, keystorePassword *dagger.Secret) *dagger.File {
|
||||
// Build and return the Android APK signed with the release key.
|
||||
func (m *Ci) BuildAndroidApk(keystoreBase64 *dagger.Secret, keystorePassword *dagger.Secret, buildNumber string) *dagger.File {
|
||||
return m.setupKeystore(keystoreBase64, keystorePassword).
|
||||
WithExec([]string{"flutter", "build", "apk", "--release"}).
|
||||
WithExec([]string{"flutter", "build", "apk", "--release", "--build-number", buildNumber}).
|
||||
File("build/app/outputs/flutter-apk/app-release.apk")
|
||||
}
|
||||
|
||||
@@ -367,11 +367,10 @@ func (m *Ci) DeployApk(
|
||||
commitHash string,
|
||||
keystoreBase64 *dagger.Secret,
|
||||
keystorePassword *dagger.Secret,
|
||||
buildNumber string,
|
||||
) (string, error) {
|
||||
// 1. Build the APK
|
||||
apk := m.BuildAndroidApk(keystoreBase64, keystorePassword)
|
||||
apk := m.BuildAndroidApk(keystoreBase64, keystorePassword, buildNumber)
|
||||
|
||||
// 2. Deploy
|
||||
datePath := time.Now().Format("2006/01/02")
|
||||
remoteDir := fmt.Sprintf("public_html/builds/%s", datePath)
|
||||
apkName := fmt.Sprintf("sharedinbox-mua-%s.apk", commitHash)
|
||||
@@ -383,29 +382,23 @@ func (m *Ci) DeployApk(
|
||||
Stdout(ctx)
|
||||
}
|
||||
|
||||
// Build and return the Android App Bundle (AAB)
|
||||
func (m *Ci) BuildAndroidRelease(keystoreBase64 *dagger.Secret, keystorePassword *dagger.Secret) *dagger.File {
|
||||
// Build and return the Android App Bundle (AAB) signed with the release key.
|
||||
func (m *Ci) BuildAndroidRelease(keystoreBase64 *dagger.Secret, keystorePassword *dagger.Secret, buildNumber string) *dagger.File {
|
||||
return m.setupKeystore(keystoreBase64, keystorePassword).
|
||||
WithExec([]string{"flutter", "build", "appbundle", "--release"}).
|
||||
WithExec([]string{"flutter", "build", "appbundle", "--release", "--build-number", buildNumber}).
|
||||
File("build/app/outputs/bundle/release/app-release.aab")
|
||||
}
|
||||
|
||||
// Publish the Android App Bundle to Google Play Store
|
||||
func (m *Ci) PublishAndroid(
|
||||
// UploadToPlayStore uploads a pre-built AAB to the Play Store internal track.
|
||||
func (m *Ci) UploadToPlayStore(
|
||||
ctx context.Context,
|
||||
aab *dagger.File,
|
||||
playStoreConfig *dagger.Secret,
|
||||
keystoreBase64 *dagger.Secret,
|
||||
keystorePassword *dagger.Secret,
|
||||
) (string, error) {
|
||||
// 1. Build the AAB
|
||||
aab := m.BuildAndroidRelease(keystoreBase64, keystorePassword)
|
||||
|
||||
// 2. Prepare script source
|
||||
scriptSource := m.Source.Filter(dagger.DirectoryFilterOpts{
|
||||
Include: []string{"scripts/deploy_playstore.py"},
|
||||
})
|
||||
|
||||
// 3. Deploy
|
||||
return dag.Container().
|
||||
From("python:3.12-alpine").
|
||||
WithExec([]string{"apk", "add", "--no-cache", "curl"}).
|
||||
|
||||
Reference in New Issue
Block a user