feat: add reusable Flutter build actions for android, ios, macos, windows, and linux
Update README Actions Table / update-readme (push) Successful in 7s

This commit is contained in:
2026-10-18 13:55:08 -07:00
parent 56dd7d58b3
commit 7a12bb1cc6
11 changed files with 1669 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
# Flutter Build Android
Composite action to configure Android keystore signing, build release AAB and APK artifacts, and optionally upload to Google Play Console and Shorebird.
## Inputs
| Name | Description | Required | Default |
| :--- | :--- | :--- | :--- |
| `working_directory` | Path to the Flutter app package directory. | No | `.` |
| `build_aab` | Whether to build an Android App Bundle (`.aab`). | No | `true` |
| `build_apk` | Whether to build a release APK (`.apk`). | No | `false` |
| `build_args` | Extra arguments passed to `flutter build`. | No | `''` |
| `artifact_prefix` | Optional prefix for copied versioned artifacts in `output_dir`. | No | `''` |
| `output_dir` | Directory to copy versioned artifacts into when `artifact_prefix` is set. | No | `artifacts` |
| `keystore_base64` | Base64-encoded Android release keystore file. | No | `''` |
| `key_alias` | Android signing key alias. | No | `''` |
| `key_password` | Android signing key password. | No | `''` |
| `store_password` | Android keystore password. | No | `''` |
| `google_play_service_account_json` | Google Play service account JSON content. | No | `''` |
| `google_play_service_account_file` | Fallback path to Google Play service account JSON file. | No | `''` |
| `google_play_track` | Google Play track (`internal`, `alpha`, `beta`, `production`). | No | `beta` |
| `google_play_status` | Google Play release status (`completed`, `draft`, `inProgress`, `halted`). | No | `completed` |
| `google_play_script` | Path to the Dart Google Play upload script. | No | `scripts/upload_play_store.dart` |
| `shorebird_token` | Shorebird token for optional Shorebird release. | No | `''` |
| `shorebird_artifact` | Shorebird artifact type (`apk` or `aab`). | No | `apk` |
## Outputs
| Name | Description |
| :--- | :--- |
| `version` | Resolved package version from `pubspec.yaml`. |
| `aab_path` | Path to the built `.aab` file. |
| `apk_path` | Path to the built `.apk` file. |
## Example Usage
```yaml
- name: Build & Release Android
uses: rodydavis/shared-actions/flutter-build-android@main
with:
working_directory: packages/hfw_flutter
build_aab: 'true'
build_apk: 'true'
artifact_prefix: hfw-android
keystore_base64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
key_alias: ${{ secrets.ANDROID_KEY_ALIAS }}
key_password: ${{ secrets.ANDROID_KEY_PASSWORD }}
store_password: ${{ secrets.ANDROID_STORE_PASSWORD }}
google_play_service_account_json: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON }}
google_play_track: beta
shorebird_token: ${{ secrets.SHOREBIRD_TOKEN }}
```
+212
View File
@@ -0,0 +1,212 @@
name: 'Flutter Build Android'
description: 'Configures Android keystore signing, builds release AAB/APK artifacts, and optionally deploys to Google Play Console and Shorebird.'
inputs:
working_directory:
description: 'Path to the Flutter app package directory (e.g., packages/hfw_flutter or .)'
required: false
default: '.'
build_aab:
description: 'Whether to build an Android App Bundle (.aab)'
required: false
default: 'true'
build_apk:
description: 'Whether to build a release APK (.apk)'
required: false
default: 'false'
build_args:
description: 'Extra arguments passed to flutter build appbundle / apk (e.g., --dart-define=...)'
required: false
default: ''
artifact_prefix:
description: 'Optional prefix for copied versioned artifacts in output_dir (e.g., hfw-android -> hfw-android-v1.0.0.apk)'
required: false
default: ''
output_dir:
description: 'Directory to copy versioned artifacts into when artifact_prefix is set'
required: false
default: 'artifacts'
keystore_base64:
description: 'Base64-encoded Android release keystore file'
required: false
default: ''
key_alias:
description: 'Android signing key alias'
required: false
default: ''
key_password:
description: 'Android signing key password'
required: false
default: ''
store_password:
description: 'Android keystore password'
required: false
default: ''
google_play_service_account_json:
description: 'Google Play service account JSON content for Play Store upload'
required: false
default: ''
google_play_service_account_file:
description: 'Fallback path to Google Play service account JSON file (relative to repo root)'
required: false
default: ''
google_play_track:
description: 'Google Play track to deploy to (internal, alpha, beta, production)'
required: false
default: 'beta'
google_play_status:
description: 'Google Play release status (completed, draft, inProgress, halted)'
required: false
default: 'completed'
google_play_script:
description: 'Path to the Dart Google Play upload script (relative to repo root)'
required: false
default: 'scripts/upload_play_store.dart'
shorebird_token:
description: 'Shorebird token for optional Shorebird release'
required: false
default: ''
shorebird_artifact:
description: 'Shorebird artifact type (apk or aab)'
required: false
default: 'apk'
outputs:
version:
description: 'Resolved package version from pubspec.yaml'
value: ${{ steps.build.outputs.version }}
aab_path:
description: 'Path to the built AAB file'
value: ${{ steps.build.outputs.aab_path }}
apk_path:
description: 'Path to the built APK file'
value: ${{ steps.build.outputs.apk_path }}
runs:
using: 'composite'
steps:
- name: Configure Android Signing Keystore
shell: bash
env:
WORK_DIR: ${{ inputs.working_directory }}
KEYSTORE_BASE64: ${{ inputs.keystore_base64 }}
KEY_ALIAS: ${{ inputs.key_alias }}
KEY_PASSWORD: ${{ inputs.key_password }}
STORE_PASSWORD: ${{ inputs.store_password }}
run: |
if [ -n "$KEYSTORE_BASE64" ]; then
echo "Decoding Android Keystore..."
mkdir -p "$WORK_DIR/android/app"
echo "$KEYSTORE_BASE64" | base64 --decode > "$WORK_DIR/android/app/release.keystore"
printf "storePassword=%s\nkeyPassword=%s\nkeyAlias=%s\nstoreFile=release.keystore\n" \
"$STORE_PASSWORD" "$KEY_PASSWORD" "$KEY_ALIAS" > "$WORK_DIR/android/key.properties"
echo "Configured $WORK_DIR/android/key.properties"
else
echo "Notice: keystore_base64 not configured. Building unsigned / default debug signing."
fi
- name: Build Android Release Artifacts
id: build
shell: bash
env:
WORK_DIR: ${{ inputs.working_directory }}
BUILD_AAB: ${{ inputs.build_aab }}
BUILD_APK: ${{ inputs.build_apk }}
BUILD_ARGS: ${{ inputs.build_args }}
ARTIFACT_PREFIX: ${{ inputs.artifact_prefix }}
OUTPUT_DIR: ${{ inputs.output_dir }}
run: |
ROOT_DIR="$(pwd)"
VERSION=$(grep '^version: ' "$WORK_DIR/pubspec.yaml" | head -n 1 | cut -d ' ' -f 2 | tr -d '\r')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
cd "$WORK_DIR"
if [ "$BUILD_AAB" = "true" ]; then
echo "Building Android App Bundle (AAB)..."
# shellcheck disable=SC2086
flutter build appbundle --release $BUILD_ARGS
fi
if [ "$BUILD_APK" = "true" ]; then
echo "Building Android APK..."
# shellcheck disable=SC2086
flutter build apk --release $BUILD_ARGS
fi
cd "$ROOT_DIR"
AAB_REL="$WORK_DIR/build/app/outputs/bundle/release/app-release.aab"
APK_REL="$WORK_DIR/build/app/outputs/flutter-apk/app-release.apk"
AAB_REL="${AAB_REL#./}"
APK_REL="${APK_REL#./}"
if [ -n "$ARTIFACT_PREFIX" ]; then
mkdir -p "$OUTPUT_DIR"
if [ -f "$AAB_REL" ]; then
cp "$AAB_REL" "$OUTPUT_DIR/${ARTIFACT_PREFIX}-v${VERSION}.aab"
AAB_OUT="$OUTPUT_DIR/${ARTIFACT_PREFIX}-v${VERSION}.aab"
fi
if [ -f "$APK_REL" ]; then
cp "$APK_REL" "$OUTPUT_DIR/${ARTIFACT_PREFIX}-v${VERSION}.apk"
APK_OUT="$OUTPUT_DIR/${ARTIFACT_PREFIX}-v${VERSION}.apk"
fi
echo "Collected Android artifacts for version $VERSION in $OUTPUT_DIR"
fi
echo "aab_path=${AAB_OUT:-$AAB_REL}" >> "$GITHUB_OUTPUT"
echo "apk_path=${APK_OUT:-$APK_REL}" >> "$GITHUB_OUTPUT"
- name: Upload AAB to Google Play Console
shell: bash
env:
WORK_DIR: ${{ inputs.working_directory }}
PLAY_STORE_JSON: ${{ inputs.google_play_service_account_json }}
PLAY_STORE_FILE: ${{ inputs.google_play_service_account_file }}
PLAY_TRACK: ${{ inputs.google_play_track }}
PLAY_STATUS: ${{ inputs.google_play_status }}
PLAY_SCRIPT: ${{ inputs.google_play_script }}
run: |
AAB_FILE="$WORK_DIR/build/app/outputs/bundle/release/app-release.aab"
AAB_FILE="${AAB_FILE#./}"
if [ ! -f "$AAB_FILE" ]; then
echo "No AAB file found at $AAB_FILE; skipping Google Play upload."
exit 0
fi
DEFAULT_SA_FILE="$WORK_DIR/android/app/google_play.json"
DEFAULT_SA_FILE="${DEFAULT_SA_FILE#./}"
SA_FILE="${PLAY_STORE_FILE:-$DEFAULT_SA_FILE}"
if [ -n "$PLAY_STORE_JSON" ] && [ -f "$PLAY_SCRIPT" ]; then
echo "Google Play service account secret detected. Uploading AAB to track '$PLAY_TRACK'..."
echo "$PLAY_STORE_JSON" > /tmp/google_play_service_account.json
dart "$PLAY_SCRIPT" \
--aab "$AAB_FILE" \
--service-account /tmp/google_play_service_account.json \
--track "$PLAY_TRACK" \
--status "$PLAY_STATUS"
rm -f /tmp/google_play_service_account.json
elif [ -f "$SA_FILE" ] && [ -f "$PLAY_SCRIPT" ]; then
echo "Using repository service account file ($SA_FILE) for Google Play upload to track '$PLAY_TRACK'..."
dart "$PLAY_SCRIPT" \
--aab "$AAB_FILE" \
--service-account "$SA_FILE" \
--track "$PLAY_TRACK" \
--status "$PLAY_STATUS"
else
echo "Skipping Google Play upload (credentials or upload script not configured)."
fi
- name: Release via Shorebird (Optional)
if: inputs.shorebird_token != ''
shell: bash
env:
WORK_DIR: ${{ inputs.working_directory }}
SHOREBIRD_TOKEN: ${{ inputs.shorebird_token }}
SHOREBIRD_ARTIFACT: ${{ inputs.shorebird_artifact }}
run: |
if [ -n "$SHOREBIRD_TOKEN" ]; then
ROOT_DIR="$(pwd)"
cd "$WORK_DIR"
flutter pub global activate shorebird_cli
shorebird release android --artifact="$SHOREBIRD_ARTIFACT"
cd "$ROOT_DIR"
fi