Skip to content

ci: configure git identity for Android release tags #17

ci: configure git identity for Android release tags

ci: configure git identity for Android release tags #17

Workflow file for this run

name: Android APK
on:
pull_request:
paths:
- '.github/workflows/android-apk.yml'
- 'android/**'
- 'src/**'
- 'include/**'
- 'lib/**'
- 'CMakeLists.txt'
- 'tools/**'
push:
branches:
- main
- android
- 'android/**'
- 'ci/**'
tags:
- 'android-v*'
- 'v*'
workflow_dispatch:
inputs:
build_mode:
description: 'runtime builds the real APK; probe builds the SDL lifecycle smoke APK and does not require generated game sources'
required: true
type: choice
default: runtime
options:
- runtime
- probe
build_type:
description: 'Gradle build type'
required: true
type: choice
default: Release
options:
- Release
- Debug
upload_to_release:
description: 'Attach APK and SHA256 to the GitHub Release for the current tag'
required: true
type: boolean
default: false
release_version:
description: 'Optional: create android-v<version> release tag with changelog, then the tag build attaches the APK'
required: false
type: string
default: ''
permissions:
contents: write
concurrency:
group: android-apk-${{ github.ref }}
cancel-in-progress: true
jobs:
release:
name: Create Android release tag
if: github.event_name == 'workflow_dispatch' && inputs.release_version != ''
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: false
- name: Create tag and release notes
id: release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
RELEASE_VERSION: ${{ inputs.release_version }}
run: |
set -euo pipefail
version="${RELEASE_VERSION#android-v}"
version="${version#v}"
if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$ ]]; then
echo "release_version must look like 1.2.3, v1.2.3, or android-v1.2.3" >&2
exit 2
fi
tag="android-v${version}"
if git rev-parse -q --verify "refs/tags/${tag}" >/dev/null; then
echo "Tag already exists locally: ${tag}" >&2
exit 2
fi
if git ls-remote --exit-code --tags origin "refs/tags/${tag}" >/dev/null 2>&1; then
echo "Tag already exists on origin: ${tag}" >&2
exit 2
fi
previous_tag="$(git tag --merged HEAD --list 'android-v*' --sort=-creatordate | head -n1 || true)"
if [[ -z "$previous_tag" ]]; then
previous_tag="$(git tag --merged HEAD --list 'v*' --sort=-creatordate | head -n1 || true)"
fi
{
echo "## BanjoRecomp Android ${tag}"
echo
if [[ -n "$previous_tag" ]]; then
echo "Changes since ${previous_tag}:"
echo
git log --no-merges --pretty=format:'- %s (%h)' "${previous_tag}..HEAD"
else
echo "Changes in this release:"
echo
git log --no-merges --pretty=format:'- %s (%h)' HEAD
fi
echo
echo
echo "The APK and SHA256 checksum are attached by the tag-triggered Android APK workflow run."
} > release-notes.md
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "$tag" -m "BanjoRecomp Android ${tag}"
git push origin "$tag"
gh release create "$tag" \
--repo "$GITHUB_REPOSITORY" \
--target "$GITHUB_SHA" \
--title "BanjoRecomp Android ${tag}" \
--notes-file release-notes.md
echo "tag=$tag" >> "$GITHUB_OUTPUT"
build:
name: Build Android APK
if: github.event_name != 'workflow_dispatch' || inputs.release_version == ''
runs-on: ubuntu-24.04
timeout-minutes: 120
env:
ANDROID_API_LEVEL: '36'
ANDROID_BUILD_TOOLS_VERSION: '36.0.0'
ANDROID_NDK_VERSION: '28.2.13676358'
ANDROID_CMAKE_VERSION: '3.22.1'
ANDROID_ABI: arm64-v8a
ANDROID_PLATFORM: '24'
BANJO_ANDROID_PRIVATE_INPUTS_REPO: ${{ vars.BANJO_ANDROID_PRIVATE_INPUTS_REPO || 'AurelioB/BanjoRecomp-private-inputs' }}
BANJO_ANDROID_KEYSTORE_BASE64: ${{ secrets.BANJO_ANDROID_KEYSTORE_BASE64 }}
BANJO_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.BANJO_ANDROID_KEYSTORE_PASSWORD }}
BANJO_ANDROID_KEY_ALIAS: ${{ secrets.BANJO_ANDROID_KEY_ALIAS }}
BANJO_ANDROID_KEY_PASSWORD: ${{ secrets.BANJO_ANDROID_KEY_PASSWORD }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Select build parameters
id: params
shell: bash
run: |
set -euo pipefail
ref_name='${{ github.ref_name }}'
version_name="0.1.0-android-${{ github.run_number }}"
version_code='${{ github.run_number }}'
if [[ '${{ github.ref_type }}' == 'tag' ]]; then
version_name="${ref_name#android-v}"
version_name="${version_name#v}"
if [[ ! "$version_name" =~ ^[0-9]+[.][0-9]+[.][0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "Release tag must look like android-v1.2.3 or v1.2.3; got ${ref_name}" >&2
exit 2
fi
base_version="${version_name%%-*}"
IFS=. read -r major minor patch <<<"$base_version"
if (( 10#$minor > 99 || 10#$patch > 99 )); then
echo "Minor and patch versions must be <= 99 because versionCode is major*10000 + minor*100 + patch." >&2
exit 2
fi
version_code=$((10#$major * 10000 + 10#$minor * 100 + 10#$patch))
fi
echo "BANJO_ANDROID_VERSION_NAME=$version_name" >> "$GITHUB_ENV"
echo "BANJO_ANDROID_VERSION_CODE=$version_code" >> "$GITHUB_ENV"
echo "Android versionName: $version_name"
echo "Android versionCode: $version_code"
if [[ '${{ github.event_name }}' == 'pull_request' || ('${{ github.event_name }}' == 'push' && ('${{ github.ref_name }}' == ci/* || '${{ github.ref_name }}' == 'android')) ]]; then
mode='probe'
build_type='Debug'
else
mode='${{ inputs.build_mode || 'runtime' }}'
build_type='${{ inputs.build_type || 'Release' }}'
fi
task="assemble${build_type}"
probe_arg=''
if [[ "$mode" == 'probe' ]]; then
probe_arg='-PbanjoProbe=true'
fi
apk_dir="android/app/build/outputs/apk/${build_type,,}"
name="BanjoRecomp-android-${version_name}-${mode}-${build_type,,}.apk"
if [[ '${{ github.ref_type }}' == 'tag' && "$mode" == 'runtime' && "$build_type" == 'Release' ]]; then
name="BanjoRecompiled-v${version_name}-Android-ARM64.apk"
fi
echo "mode=$mode" >> "$GITHUB_OUTPUT"
echo "build_type=$build_type" >> "$GITHUB_OUTPUT"
echo "task=$task" >> "$GITHUB_OUTPUT"
echo "probe_arg=$probe_arg" >> "$GITHUB_OUTPUT"
echo "apk_dir=$apk_dir" >> "$GITHUB_OUTPUT"
echo "artifact_name=$name" >> "$GITHUB_OUTPUT"
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- name: Set up Android SDK
uses: android-actions/setup-android@v3
- name: Install Android SDK packages
shell: bash
run: |
set -euo pipefail
yes | sdkmanager --licenses >/dev/null || true
sdkmanager \
"platforms;android-${ANDROID_API_LEVEL}" \
"build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \
"ndk;${ANDROID_NDK_VERSION}" \
"cmake;${ANDROID_CMAKE_VERSION}"
echo "ANDROID_NDK_HOME=${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}" >> "$GITHUB_ENV"
- name: Install runtime source generation tools
if: steps.params.outputs.mode == 'runtime'
shell: bash
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y build-essential clang cmake lld make ninja-build
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: '9.5.1'
- name: Cache Android native prefixes
uses: actions/cache@v4
with:
path: ~/Android/prefixes
key: android-prefixes-${{ runner.os }}-${{ env.ANDROID_ABI }}-sdl2-2.32.10-freetype-2.13.3-ndk-${{ env.ANDROID_NDK_VERSION }}
- name: Build Android native dependencies
shell: bash
run: tools/ci/setup_android_deps.sh
- name: Checkout private runtime inputs
if: steps.params.outputs.mode == 'runtime'
uses: actions/checkout@v4
with:
repository: ${{ env.BANJO_ANDROID_PRIVATE_INPUTS_REPO }}
ssh-key: ${{ secrets.BANJO_ANDROID_PRIVATE_INPUTS_SSH_KEY }}
path: extra
persist-credentials: false
- name: Prepare generated runtime sources
shell: bash
run: tools/ci/prepare_android_generated_sources.sh '${{ steps.params.outputs.mode }}'
- name: Decode release signing key
if: env.BANJO_ANDROID_KEYSTORE_BASE64 != ''
shell: bash
run: |
set -euo pipefail
mkdir -p "$RUNNER_TEMP/banjo-signing"
printf '%s' "$BANJO_ANDROID_KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/banjo-signing/release.jks"
echo "BANJO_ANDROID_KEYSTORE_FILE=$RUNNER_TEMP/banjo-signing/release.jks" >> "$GITHUB_ENV"
- name: Require signing for runtime release APKs
if: steps.params.outputs.mode == 'runtime' && steps.params.outputs.build_type == 'Release' && env.BANJO_ANDROID_KEYSTORE_BASE64 == ''
shell: bash
run: |
echo 'Runtime release APK distribution requires signing secrets.' >&2
echo 'Set BANJO_ANDROID_KEYSTORE_BASE64, BANJO_ANDROID_KEYSTORE_PASSWORD, BANJO_ANDROID_KEY_ALIAS, and optionally BANJO_ANDROID_KEY_PASSWORD.' >&2
exit 2
- name: Static guards
shell: bash
run: |
set -euo pipefail
git diff --check
python3 tools/check_android_port_guards.py
- name: Build APK
shell: bash
run: |
set -euo pipefail
gradle -p android --no-daemon :app:${{ steps.params.outputs.task }} ${{ steps.params.outputs.probe_arg }} --stacktrace
- name: Locate APK
id: apk
shell: bash
run: |
set -euo pipefail
mapfile -t apks < <(find '${{ steps.params.outputs.apk_dir }}' -maxdepth 1 -type f -name '*.apk' | sort)
if [[ ${#apks[@]} -ne 1 ]]; then
echo "Expected exactly one APK in ${{ steps.params.outputs.apk_dir }}, found ${#apks[@]}" >&2
printf ' %s\n' "${apks[@]}" >&2
exit 2
fi
echo "path=${apks[0]}" >> "$GITHUB_OUTPUT"
echo "APK path: ${apks[0]}"
- name: Verify APK contents
shell: bash
run: tools/ci/verify_android_apk.sh '${{ steps.apk.outputs.path }}' '${{ steps.params.outputs.mode }}'
- name: Stage distributable
id: dist
shell: bash
run: |
set -euo pipefail
mkdir -p dist
cp '${{ steps.apk.outputs.path }}' "dist/${{ steps.params.outputs.artifact_name }}"
sha256sum "dist/${{ steps.params.outputs.artifact_name }}" | tee "dist/${{ steps.params.outputs.artifact_name }}.sha256"
echo "apk=dist/${{ steps.params.outputs.artifact_name }}" >> "$GITHUB_OUTPUT"
echo "sha=dist/${{ steps.params.outputs.artifact_name }}.sha256" >> "$GITHUB_OUTPUT"
- name: Upload workflow artifact
uses: actions/upload-artifact@v4
with:
name: ${{ steps.params.outputs.artifact_name }}
path: |
${{ steps.dist.outputs.apk }}
${{ steps.dist.outputs.sha }}
if-no-files-found: error
- name: Attach APK to GitHub Release
if: github.ref_type == 'tag' && (github.event_name == 'push' || inputs.upload_to_release)
uses: softprops/action-gh-release@v2
with:
files: |
${{ steps.dist.outputs.apk }}
${{ steps.dist.outputs.sha }}
fail_on_unmatched_files: true