Skip to content

Release

Release #25

Workflow file for this run

name: Release
on:
push:
tags:
- "v*"
workflow_run:
workflows:
- Sync Version Tag
types:
- completed
workflow_dispatch:
inputs:
tag:
description: Existing release tag to build, for example v1.0.0
required: true
type: string
permissions:
contents: write
env:
FLUTTER_VERSION: 3.41.4
JAVA_VERSION: "17"
jobs:
metadata:
name: Resolve release metadata
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
outputs:
ref: ${{ steps.version.outputs.ref }}
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
steps:
- name: Resolve source ref
id: ref
shell: bash
env:
DISPATCH_TAG: ${{ inputs.tag }}
EVENT_NAME: ${{ github.event_name }}
WORKFLOW_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
case "$EVENT_NAME" in
push)
source_ref="$GITHUB_REF_NAME"
;;
workflow_dispatch)
source_ref="$DISPATCH_TAG"
;;
workflow_run)
source_ref="$WORKFLOW_HEAD_SHA"
;;
*)
echo "Unsupported event: $EVENT_NAME" >&2
exit 1
;;
esac
if [ -z "$source_ref" ]; then
echo "Could not determine source ref for release workflow." >&2
exit 1
fi
echo "source_ref=$source_ref" >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ steps.ref.outputs.source_ref }}
- name: Fetch tags
shell: bash
run: git fetch --force --tags origin
- name: Resolve tag and validate pubspec version
id: version
shell: bash
env:
DISPATCH_TAG: ${{ inputs.tag }}
EVENT_NAME: ${{ github.event_name }}
PUSH_TAG: ${{ github.ref_name }}
run: |
version=$(sed -n 's/^version: *//p' pubspec.yaml | head -n1 | cut -d+ -f1)
if [ -z "$version" ]; then
echo "Could not determine app version from pubspec.yaml." >&2
exit 1
fi
expected_tag="v$version"
case "$EVENT_NAME" in
push)
actual_tag="$PUSH_TAG"
;;
workflow_dispatch)
actual_tag="$DISPATCH_TAG"
;;
workflow_run)
actual_tag="$expected_tag"
if ! git rev-parse -q --verify "refs/tags/$actual_tag" >/dev/null; then
echo "Expected tag '$actual_tag' was not found after Sync Version Tag completed." >&2
exit 1
fi
;;
*)
echo "Unsupported event: $EVENT_NAME" >&2
exit 1
;;
esac
if [ "$actual_tag" != "$expected_tag" ]; then
echo "Tag '$actual_tag' does not match pubspec version '$expected_tag'." >&2
exit 1
fi
echo "ref=$actual_tag" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "tag=$actual_tag" >> "$GITHUB_OUTPUT"
windows-release:
name: Build Windows release artifacts
runs-on: windows-latest
needs: metadata
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
ref: ${{ needs.metadata.outputs.ref }}
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Enable Windows desktop
shell: pwsh
run: flutter config --enable-windows-desktop
- name: Install dependencies
shell: pwsh
run: flutter pub get
- name: Build Windows release
shell: pwsh
env:
KICK_APTABASE_APP_KEY_RELEASE: ${{ secrets.KICK_APTABASE_APP_KEY_RELEASE }}
KICK_APTABASE_HOST_RELEASE: ${{ secrets.KICK_APTABASE_HOST_RELEASE }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
KICK_GLITCHTIP_TRACES_SAMPLE_RATE: ${{ secrets.KICK_GLITCHTIP_TRACES_SAMPLE_RATE }}
run: |
$flutterArgs = @('build', 'windows', '--release')
if (-not [string]::IsNullOrWhiteSpace($env:KICK_APTABASE_APP_KEY_RELEASE)) {
$flutterArgs += "--dart-define=KICK_APTABASE_APP_KEY_RELEASE=$($env:KICK_APTABASE_APP_KEY_RELEASE)"
}
if (-not [string]::IsNullOrWhiteSpace($env:KICK_APTABASE_HOST_RELEASE)) {
$flutterArgs += "--dart-define=KICK_APTABASE_HOST_RELEASE=$($env:KICK_APTABASE_HOST_RELEASE)"
}
if (-not [string]::IsNullOrWhiteSpace($env:SENTRY_DSN)) {
$flutterArgs += "--dart-define=SENTRY_DSN=$($env:SENTRY_DSN)"
}
$flutterArgs += "--dart-define=SENTRY_ENVIRONMENT=production"
if (-not [string]::IsNullOrWhiteSpace($env:KICK_GLITCHTIP_TRACES_SAMPLE_RATE)) {
$flutterArgs += "--dart-define=KICK_GLITCHTIP_TRACES_SAMPLE_RATE=$($env:KICK_GLITCHTIP_TRACES_SAMPLE_RATE)"
}
flutter @flutterArgs
- name: Install Inno Setup
shell: pwsh
run: choco install innosetup --no-progress -y
- name: Build Windows installer and portable bundle
shell: pwsh
run: |
& .\scripts\build-windows-installer.ps1 `
-AppVersion '${{ needs.metadata.outputs.version }}' `
-SkipBuild
- name: Upload Windows release artifacts
uses: actions/upload-artifact@v7
with:
name: kick-windows-release
path: build/dist/*
if-no-files-found: error
android-release:
name: Build Android release artifacts
runs-on: ubuntu-latest
needs: metadata
env:
HAS_SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN != '' }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
ref: ${{ needs.metadata.outputs.ref }}
- name: Set up Java
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
- name: Set up Gradle cache
uses: gradle/actions/setup-gradle@v5
- name: Set up Android SDK
uses: android-actions/setup-android@v3
- name: Install Android SDK packages
run: sdkmanager "platform-tools" "platforms;android-36" "build-tools;36.1.0"
- name: Validate Android signing secrets
env:
KICK_ANDROID_KEYSTORE_BASE64: ${{ secrets.KICK_ANDROID_KEYSTORE_BASE64 }}
KICK_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.KICK_ANDROID_KEYSTORE_PASSWORD }}
KICK_ANDROID_KEY_ALIAS: ${{ secrets.KICK_ANDROID_KEY_ALIAS }}
KICK_ANDROID_KEY_PASSWORD: ${{ secrets.KICK_ANDROID_KEY_PASSWORD }}
shell: bash
run: |
for secret_name in \
KICK_ANDROID_KEYSTORE_BASE64 \
KICK_ANDROID_KEYSTORE_PASSWORD \
KICK_ANDROID_KEY_ALIAS \
KICK_ANDROID_KEY_PASSWORD; do
if [ -z "${!secret_name}" ]; then
echo "Missing required secret: $secret_name" >&2
exit 1
fi
done
- name: Prepare Android signing config
env:
KICK_ANDROID_KEYSTORE_BASE64: ${{ secrets.KICK_ANDROID_KEYSTORE_BASE64 }}
KICK_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.KICK_ANDROID_KEYSTORE_PASSWORD }}
KICK_ANDROID_KEY_ALIAS: ${{ secrets.KICK_ANDROID_KEY_ALIAS }}
KICK_ANDROID_KEY_PASSWORD: ${{ secrets.KICK_ANDROID_KEY_PASSWORD }}
shell: bash
run: |
keystore_path="$RUNNER_TEMP/kick-upload.jks"
echo "$KICK_ANDROID_KEYSTORE_BASE64" | base64 --decode > "$keystore_path"
{
echo "KICK_ANDROID_KEYSTORE_PATH=$keystore_path"
echo "KICK_ANDROID_KEYSTORE_PASSWORD=$KICK_ANDROID_KEYSTORE_PASSWORD"
echo "KICK_ANDROID_KEY_ALIAS=$KICK_ANDROID_KEY_ALIAS"
echo "KICK_ANDROID_KEY_PASSWORD=$KICK_ANDROID_KEY_PASSWORD"
} >> "$GITHUB_ENV"
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install dependencies
run: flutter pub get
- name: Build Android release APK
env:
KICK_APTABASE_APP_KEY_RELEASE: ${{ secrets.KICK_APTABASE_APP_KEY_RELEASE }}
KICK_APTABASE_HOST_RELEASE: ${{ secrets.KICK_APTABASE_HOST_RELEASE }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
KICK_GLITCHTIP_TRACES_SAMPLE_RATE: ${{ secrets.KICK_GLITCHTIP_TRACES_SAMPLE_RATE }}
shell: bash
run: |
flutter_args=(build apk --release --target-platform android-arm,android-arm64)
if [ -n "${KICK_APTABASE_APP_KEY_RELEASE:-}" ]; then
flutter_args+=("--dart-define=KICK_APTABASE_APP_KEY_RELEASE=$KICK_APTABASE_APP_KEY_RELEASE")
fi
if [ -n "${KICK_APTABASE_HOST_RELEASE:-}" ]; then
flutter_args+=("--dart-define=KICK_APTABASE_HOST_RELEASE=$KICK_APTABASE_HOST_RELEASE")
fi
if [ -n "${SENTRY_DSN:-}" ]; then
flutter_args+=("--dart-define=SENTRY_DSN=$SENTRY_DSN")
fi
flutter_args+=("--dart-define=SENTRY_ENVIRONMENT=production")
if [ -n "${KICK_GLITCHTIP_TRACES_SAMPLE_RATE:-}" ]; then
flutter_args+=("--dart-define=KICK_GLITCHTIP_TRACES_SAMPLE_RATE=$KICK_GLITCHTIP_TRACES_SAMPLE_RATE")
fi
flutter "${flutter_args[@]}"
- name: Build Android release App Bundle
env:
KICK_APTABASE_APP_KEY_RELEASE: ${{ secrets.KICK_APTABASE_APP_KEY_RELEASE }}
KICK_APTABASE_HOST_RELEASE: ${{ secrets.KICK_APTABASE_HOST_RELEASE }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
KICK_GLITCHTIP_TRACES_SAMPLE_RATE: ${{ secrets.KICK_GLITCHTIP_TRACES_SAMPLE_RATE }}
shell: bash
run: |
flutter_args=(build appbundle --release --target-platform android-arm,android-arm64)
if [ -n "${KICK_APTABASE_APP_KEY_RELEASE:-}" ]; then
flutter_args+=("--dart-define=KICK_APTABASE_APP_KEY_RELEASE=$KICK_APTABASE_APP_KEY_RELEASE")
fi
if [ -n "${KICK_APTABASE_HOST_RELEASE:-}" ]; then
flutter_args+=("--dart-define=KICK_APTABASE_HOST_RELEASE=$KICK_APTABASE_HOST_RELEASE")
fi
if [ -n "${SENTRY_DSN:-}" ]; then
flutter_args+=("--dart-define=SENTRY_DSN=$SENTRY_DSN")
fi
flutter_args+=("--dart-define=SENTRY_ENVIRONMENT=production")
if [ -n "${KICK_GLITCHTIP_TRACES_SAMPLE_RATE:-}" ]; then
flutter_args+=("--dart-define=KICK_GLITCHTIP_TRACES_SAMPLE_RATE=$KICK_GLITCHTIP_TRACES_SAMPLE_RATE")
fi
flutter "${flutter_args[@]}"
- name: Install Sentry CLI
if: ${{ env.HAS_SENTRY_AUTH_TOKEN == 'true' }}
shell: bash
run: curl -sL https://sentry.io/get-cli/ | bash
- name: Upload Android debug files to GlitchTip
if: ${{ env.HAS_SENTRY_AUTH_TOKEN == 'true' }}
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_URL: https://app.glitchtip.com/
SENTRY_ORG: kick
SENTRY_PROJECT: kick
shell: bash
run: |
chmod +x scripts/upload-android-debug-files.sh
scripts/upload-android-debug-files.sh
- name: Prepare Android release artifacts
id: android_artifacts
shell: bash
run: |
artifact_dir="build/dist/android"
mkdir -p "$artifact_dir"
cp build/app/outputs/flutter-apk/app-release.apk "$artifact_dir/kick-android-${{ needs.metadata.outputs.version }}.apk"
cp build/app/outputs/bundle/release/app-release.aab "$artifact_dir/kick-android-${{ needs.metadata.outputs.version }}.aab"
echo "artifact_dir=$artifact_dir" >> "$GITHUB_OUTPUT"
- name: Upload Android release artifacts
uses: actions/upload-artifact@v7
with:
name: kick-android-release
path: ${{ steps.android_artifacts.outputs.artifact_dir }}/*
if-no-files-found: error
publish-release:
name: Publish GitHub release
runs-on: ubuntu-latest
needs:
- metadata
- windows-release
- android-release
steps:
- name: Checkout workflow sources
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Fetch tags
shell: bash
run: git fetch --force --tags origin
- name: Download release artifacts
uses: actions/download-artifact@v8
with:
path: release-artifacts
merge-multiple: true
- name: Generate release notes
shell: bash
run: |
chmod +x scripts/generate-release-notes.sh
scripts/generate-release-notes.sh \
--tag "${{ needs.metadata.outputs.tag }}" \
--version "${{ needs.metadata.outputs.version }}" \
--repository "${{ github.repository }}" \
--template ".github/release_template.md" \
--output "release-notes.md"
- name: Create or update GitHub release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.metadata.outputs.tag }}
body_path: release-notes.md
overwrite_files: true
files: |
release-artifacts/**/*.zip
release-artifacts/**/*.exe
release-artifacts/**/*.apk
release-artifacts/**/*.aab