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
+48
View File
@@ -0,0 +1,48 @@
# Flutter Build Windows
Composite action to build Windows Flutter desktop applications, compile Inno Setup (`.exe`) and MSIX (`.msix`) installers, sign updates with WinSparkle DSA keys, and generate release metadata.
## Inputs
| Name | Description | Required | Default |
| :--- | :--- | :--- | :--- |
| `working_directory` | Path to the Flutter app package directory. | No | `.` |
| `arch` | Target Windows architecture (`x64` or `arm64`). | No | `x64` |
| `build_args` | Extra arguments passed to `flutter build windows`. | No | `''` |
| `build_inno_setup` | Whether to compile an Inno Setup installer (`.exe`). | No | `true` |
| `inno_setup_script` | Path to Inno Setup `.iss` script relative to `working_directory`. | No | `windows/installer/inno_setup.iss` |
| `output_dir` | Output directory for compiled Inno Setup installer. | No | `dist` |
| `exe_filename` | Expected installer filename for metadata JSON. | No | `''` |
| `build_msix` | Whether to build an MSIX package using `msix:create`. | No | `true` |
| `msix_output_name` | Output base name for the MSIX package. | No | `''` |
| `pfx_base64` | Base64-encoded Windows Authenticode `.pfx` certificate. | No | `''` |
| `pfx_password` | Password for the Windows `.pfx` certificate. | No | `''` |
| `sparkle_dsa_private_key` | WinSparkle DSA private key content (PEM) for signing installer. | No | `''` |
| `sparkle_dsa_key_file` | Fallback path to WinSparkle DSA private key file in repository. | No | `signing/sparkle/dsa_priv.pem` |
| `generate_metadata_json` | Whether to generate `windows_<arch>_metadata.json` and `windows_signature.txt`. | No | `false` |
## Outputs
| Name | Description |
| :--- | :--- |
| `version` | Resolved package version from `pubspec.yaml`. |
| `exe_path` | Path to compiled Inno Setup `.exe` installer. |
| `msix_path` | Path to generated `.msix` installer. |
| `signature` | WinSparkle DSA signature if generated. |
| `metadata_path` | Path to generated `windows_<arch>_metadata.json`. |
## Example Usage
```yaml
- name: Build & Package Windows Release
uses: rodydavis/shared-actions/flutter-build-windows@main
with:
working_directory: packages/sfw_desktop
arch: ${{ matrix.arch }}
build_args: --dart-define=API_BASE_URL=https://slidesforworship.app
msix_output_name: slides_for_worship_${{ matrix.arch }}
pfx_base64: ${{ secrets.WINDOWS_PFX_BASE64 }}
pfx_password: ${{ secrets.WINDOWS_PFX_PASSWORD }}
sparkle_dsa_private_key: ${{ secrets.SPARKLE_DSA_PRIVATE_KEY }}
generate_metadata_json: 'true'
```
+260
View File
@@ -0,0 +1,260 @@
name: 'Flutter Build Windows'
description: 'Builds Windows Flutter desktop apps, compiles Inno Setup (.exe) and MSIX (.msix) installers, signs updates for WinSparkle (DSA), and generates release metadata.'
inputs:
working_directory:
description: 'Path to the Flutter app package directory (e.g., packages/sfw_desktop)'
required: false
default: '.'
arch:
description: 'Target Windows architecture (x64 or arm64)'
required: false
default: 'x64'
build_args:
description: 'Extra arguments passed to flutter build windows (e.g., --dart-define=...)'
required: false
default: ''
build_inno_setup:
description: 'Whether to compile an Inno Setup installer (.exe)'
required: false
default: 'true'
inno_setup_script:
description: 'Path to Inno Setup .iss script relative to working_directory'
required: false
default: 'windows/installer/inno_setup.iss'
output_dir:
description: 'Output directory for compiled Inno Setup installer'
required: false
default: 'dist'
exe_filename:
description: 'Expected installer filename for metadata JSON (e.g., slides-for-worship-windows-x64-setup.exe)'
required: false
default: ''
build_msix:
description: 'Whether to build an MSIX package using msix:create'
required: false
default: 'true'
msix_output_name:
description: 'Output base name for the MSIX package (e.g., slides_for_worship_x64)'
required: false
default: ''
pfx_base64:
description: 'Base64-encoded Windows Authenticode .pfx certificate'
required: false
default: ''
pfx_password:
description: 'Password for the Windows .pfx certificate'
required: false
default: ''
sparkle_dsa_private_key:
description: 'WinSparkle DSA private key content (PEM) for signing installer'
required: false
default: ''
sparkle_dsa_key_file:
description: 'Fallback path to WinSparkle DSA private key file in repository'
required: false
default: 'signing/sparkle/dsa_priv.pem'
generate_metadata_json:
description: 'Whether to generate windows_<arch>_metadata.json and windows_signature.txt'
required: false
default: 'false'
outputs:
version:
description: 'Resolved package version from pubspec.yaml'
value: ${{ steps.build.outputs.version }}
exe_path:
description: 'Path to compiled Inno Setup .exe installer'
value: ${{ steps.package_meta.outputs.exe_path }}
msix_path:
description: 'Path to generated .msix installer'
value: ${{ steps.package_meta.outputs.msix_path }}
signature:
description: 'WinSparkle DSA signature if generated'
value: ${{ steps.package_meta.outputs.signature }}
metadata_path:
description: 'Path to generated windows_<arch>_metadata.json'
value: ${{ steps.package_meta.outputs.metadata_path }}
runs:
using: 'composite'
steps:
- name: Configure Windows Tooling PATH
shell: powershell
run: |
$toolPaths = @(
"C:\Program Files\Git\bin",
"C:\Program Files\Git\usr\bin",
"C:\Git\bin",
"C:\Git\usr\bin",
"C:\Users\rodyd\AppData\Local\Programs\Inno Setup 6",
"C:\Program Files (x86)\Inno Setup 6",
"C:\Program Files\Inno Setup 6",
"C:\flutter\bin"
)
foreach ($p in $toolPaths) {
if (Test-Path $p) {
$env:PATH = "$p;$env:PATH"
if (Test-Path env:GITHUB_PATH) {
[System.IO.File]::AppendAllText($env:GITHUB_PATH, "$p`r`n")
}
}
}
- name: Decode Windows PFX Certificate
if: inputs.pfx_base64 != ''
shell: powershell
env:
WINDOWS_PFX_BASE64: ${{ inputs.pfx_base64 }}
run: |
if ($env:WINDOWS_PFX_BASE64) {
$pfx_cert_byte = [System.Convert]::FromBase64String($env:WINDOWS_PFX_BASE64)
[IO.File]::WriteAllBytes("cert.pfx", $pfx_cert_byte)
Write-Output "Decoded cert.pfx"
}
- name: Build Windows Release
id: build
shell: bash
env:
WORK_DIR: ${{ inputs.working_directory }}
ARCH: ${{ inputs.arch }}
BUILD_ARGS: ${{ inputs.build_args }}
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 [ "$ARCH" = "arm64" ]; then
# shellcheck disable=SC2086
flutter build windows --release --target-platform=windows-arm64 $BUILD_ARGS
else
# shellcheck disable=SC2086
flutter build windows --release $BUILD_ARGS
fi
cd "$ROOT_DIR"
- name: Compile Inno Setup Installer
if: inputs.build_inno_setup == 'true'
shell: powershell
env:
WORK_DIR: ${{ inputs.working_directory }}
ARCH: ${{ inputs.arch }}
ISS_REL: ${{ inputs.inno_setup_script }}
OUT_DIR: ${{ inputs.output_dir }}
VERSION: ${{ steps.build.outputs.version }}
run: |
New-Item -ItemType Directory -Force -Path $env:OUT_DIR | Out-Null
$sourceRel = Join-Path $env:WORK_DIR "build\windows\$($env:ARCH)\runner\Release"
if (-not (Test-Path $sourceRel)) {
$sourceRel = Join-Path $env:WORK_DIR "build\windows\x64\runner\Release"
}
$sourceDir = (Resolve-Path $sourceRel).Path
$outputDir = (Resolve-Path $env:OUT_DIR).Path
$issScript = Join-Path $env:WORK_DIR $env:ISS_REL
if (-not (Test-Path $issScript)) {
Write-Warning "Inno Setup script not found at $issScript; skipping Inno Setup compilation."
exit 0
}
$iscc = (Get-Command iscc.exe -ErrorAction SilentlyContinue).Source
if (-not $iscc -and (Test-Path "C:\Users\rodyd\AppData\Local\Programs\Inno Setup 6\ISCC.exe")) {
$iscc = "C:\Users\rodyd\AppData\Local\Programs\Inno Setup 6\ISCC.exe"
}
if ($iscc) {
& $iscc /DMyAppVersion="$($env:VERSION)" /DAppArch="$($env:ARCH)" /DSourceDir="$sourceDir" /DOutputDir="$outputDir" "$issScript"
} else {
Write-Warning "ISCC.exe not found on runner; skipping Inno Setup installer."
}
- name: Create MSIX Package
if: inputs.build_msix == 'true'
shell: bash
env:
WORK_DIR: ${{ inputs.working_directory }}
ARCH: ${{ inputs.arch }}
MSIX_NAME: ${{ inputs.msix_output_name }}
PFX_PASS: ${{ inputs.pfx_password }}
run: |
ROOT_DIR="$(pwd)"
OUT_NAME="${MSIX_NAME:-slides_for_worship_${ARCH}}"
cd "$WORK_DIR"
if [ -f "$ROOT_DIR/cert.pfx" ]; then
flutter pub run msix:create --certificate-path "$ROOT_DIR/cert.pfx" --certificate-password "$PFX_PASS" --install-certificate false --output-name "$OUT_NAME"
else
flutter pub run msix:create --install-certificate false --output-name "$OUT_NAME"
fi
cd "$ROOT_DIR"
- name: Sign for WinSparkle & Generate Metadata
id: package_meta
shell: bash
env:
WORK_DIR: ${{ inputs.working_directory }}
ARCH: ${{ inputs.arch }}
OUT_DIR: ${{ inputs.output_dir }}
VERSION: ${{ steps.build.outputs.version }}
EXE_FILENAME_INPUT: ${{ inputs.exe_filename }}
SPARKLE_DSA_KEY: ${{ inputs.sparkle_dsa_private_key }}
SPARKLE_DSA_FILE: ${{ inputs.sparkle_dsa_key_file }}
GEN_META: ${{ inputs.generate_metadata_json }}
run: |
EXE_FILE=$(find "$OUT_DIR" -name "*.exe" 2>/dev/null | head -n 1 || true)
MSIX_FILE=$(find "$WORK_DIR/build/windows" -name "*.msix" 2>/dev/null | head -n 1 || true)
echo "exe_path=$EXE_FILE" >> "$GITHUB_OUTPUT"
echo "msix_path=$MSIX_FILE" >> "$GITHUB_OUTPUT"
SETUP_FILE="${EXE_FILE:-$MSIX_FILE}"
if [ -z "$SPARKLE_DSA_KEY" ] && [ -n "$SPARKLE_DSA_FILE" ] && [ -f "$SPARKLE_DSA_FILE" ]; then
SPARKLE_DSA_KEY=$(cat "$SPARKLE_DSA_FILE")
fi
WIN_SIG=""
if [ -n "$SPARKLE_DSA_KEY" ] && [ -n "$SETUP_FILE" ] && [ -f "$SETUP_FILE" ]; then
echo "$SPARKLE_DSA_KEY" > dsa_priv.pem
echo "Signing $SETUP_FILE with DSA key..."
WIN_SIG=$(openssl dgst -sha1 -binary < "$SETUP_FILE" | openssl dgst -sha1 -sign dsa_priv.pem | openssl enc -base64 | tr -d '\n\r' || true)
if [ -n "$WIN_SIG" ]; then
echo "$WIN_SIG" > windows_signature.txt
echo "signature=$WIN_SIG" >> "$GITHUB_OUTPUT"
fi
rm -f dsa_priv.pem
fi
if [ "$GEN_META" = "true" ]; then
SIZE="0"
if [ -n "$EXE_FILE" ] && [ -f "$EXE_FILE" ]; then
SIZE=$(stat -c%s "$EXE_FILE" 2>/dev/null || stat -f%z "$EXE_FILE" 2>/dev/null || echo "0")
fi
if [ -n "$EXE_FILENAME_INPUT" ]; then
FILENAME="$EXE_FILENAME_INPUT"
elif [ -n "$EXE_FILE" ]; then
FILENAME=$(basename "$EXE_FILE")
else
FILENAME="slides-for-worship-windows-${ARCH}-setup.exe"
fi
META_FILE="windows_${ARCH}_metadata.json"
cat <<EOF > "$META_FILE"
{
"version": "$VERSION",
"platform": "windows",
"arch": "$ARCH",
"filename": "$FILENAME",
"size": $SIZE,
"signature": "$WIN_SIG",
"signatureType": "dsaSignature"
}
EOF
cat "$META_FILE"
echo "metadata_path=$META_FILE" >> "$GITHUB_OUTPUT"
fi
- name: Clean up PFX Certificate
if: always()
shell: powershell
run: |
if (Test-Path "cert.pfx") {
Remove-Item -Force "cert.pfx" -ErrorAction SilentlyContinue
}