NuvioMobile/.github/workflows/android-release.yml
2026-07-20 19:30:51 +05:30

193 lines
6.6 KiB
YAML

name: Build Android Release
on:
workflow_dispatch:
inputs:
mode:
description: Build only, create a draft, or publish the release
required: true
type: choice
options:
- dry-run
- draft
- publish
default: dry-run
exclude_commits:
description: Optional comma-separated commit hashes to omit from the notes
required: false
type: string
permissions:
contents: write
concurrency:
group: android-release-${{ github.ref }}
cancel-in-progress: false
jobs:
release:
name: ${{ inputs.mode }} Android release
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
RELEASE_PROPERTIES_BASE64: ${{ secrets.NUVIO_LOCAL_PROPERTIES_BASE64 }}
RELEASE_KEYSTORE_BASE64: ${{ secrets.NUVIO_RELEASE_KEYSTORE_BASE64 }}
steps:
- name: Check out full history
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Read release metadata
id: release
run: ./scripts/release-metadata.sh "${GITHUB_SHA}" >> "${GITHUB_OUTPUT}"
- name: Validate release state
env:
RELEASE_TAG: ${{ steps.release.outputs.tag }}
CURRENT_BUMP: ${{ steps.release.outputs.current_bump }}
RELEASE_COMMIT: ${{ steps.release.outputs.release_commit }}
RELEASE_BRANCH: ${{ github.ref_name }}
RELEASE_REF_TYPE: ${{ github.ref_type }}
RELEASE_MODE: ${{ inputs.mode }}
run: |
if [[ "${RELEASE_REF_TYPE}" != "branch" ]]; then
echo "Releases must be dispatched from a branch, not ${RELEASE_REF_TYPE}." >&2
exit 1
fi
if git rev-parse --verify --quiet "refs/tags/${RELEASE_TAG}"; then
echo "Tag ${RELEASE_TAG} already exists." >&2
exit 1
fi
if gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${RELEASE_TAG}" >/dev/null 2>&1; then
echo "A GitHub release for ${RELEASE_TAG} already exists." >&2
exit 1
fi
unexpected_changes="$(
git diff --name-only "${CURRENT_BUMP}..${RELEASE_COMMIT}" \
| grep -Ev '^(\.github/workflows/android-release\.yml|scripts/(generate-release-notes|release-metadata)\.sh)$' \
|| true
)"
if [[ -n "${unexpected_changes}" ]]; then
echo "The version bump must be the final application change before release." >&2
echo "Unexpected files changed after the bump:" >&2
echo "${unexpected_changes}" >&2
exit 1
fi
if [[ "${RELEASE_MODE}" != "dry-run" ]]; then
required_secrets=(
RELEASE_PROPERTIES_BASE64
RELEASE_KEYSTORE_BASE64
)
missing=()
for secret_name in "${required_secrets[@]}"; do
if [[ -z "${!secret_name}" ]]; then
missing+=("${secret_name}")
fi
done
if (( ${#missing[@]} > 0 )); then
printf 'Missing required release secrets: %s\n' "${missing[*]}" >&2
exit 1
fi
fi
- name: Generate release notes
env:
PREVIOUS_BUMP: ${{ steps.release.outputs.previous_bump }}
CURRENT_BUMP: ${{ steps.release.outputs.current_bump }}
EXCLUDE_COMMITS: ${{ inputs.exclude_commits }}
run: |
./scripts/generate-release-notes.sh \
--from "${PREVIOUS_BUMP}" \
--to "${CURRENT_BUMP}" \
--repository "${GITHUB_REPOSITORY}" \
--exclude "${EXCLUDE_COMMITS}" \
> release-notes.md
if [[ ! -s release-notes.md ]]; then
echo "No release-note commits were found." >&2
exit 1
fi
{
echo "## ${{ steps.release.outputs.version }} release notes"
echo
cat release-notes.md
} >> "${GITHUB_STEP_SUMMARY}"
- name: Set up Java
if: inputs.mode != 'dry-run'
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17
- name: Set up Gradle
if: inputs.mode != 'dry-run'
uses: gradle/actions/setup-gradle@v6
- name: Configure release properties
if: inputs.mode != 'dry-run'
run: |
keystore_path="${RUNNER_TEMP}/nuvio-release.jks"
printf '%s' "${RELEASE_KEYSTORE_BASE64}" | base64 --decode > "${keystore_path}"
printf '%s' "${RELEASE_PROPERTIES_BASE64}" | base64 --decode > local.properties
# These paths only apply to the machine where local.properties was encoded.
sed -i -E '/^[[:space:]]*(sdk\.dir|NUVIO_RELEASE_STORE_FILE)[[:space:]]*=/d' local.properties
printf '\nNUVIO_RELEASE_STORE_FILE=%s\n' "${keystore_path}" >> local.properties
required_properties=(
NUVIO_RELEASE_STORE_PASSWORD
NUVIO_RELEASE_KEY_ALIAS
NUVIO_RELEASE_KEY_PASSWORD
TRAKT_CLIENT_ID
TRAKT_CLIENT_SECRET
)
for property_name in "${required_properties[@]}"; do
if ! grep -Eq "^${property_name}=.+" local.properties; then
echo "Missing required property: ${property_name}" >&2
exit 1
fi
done
- name: Build full release APKs
if: inputs.mode != 'dry-run'
run: ./gradlew :androidApp:assembleFullRelease --stacktrace
- name: Upload build artifacts
if: inputs.mode != 'dry-run'
uses: actions/upload-artifact@v6
with:
name: nuvio-${{ steps.release.outputs.version }}-full-release
path: androidApp/build/outputs/apk/full/release/*.apk
if-no-files-found: error
- name: Create GitHub release
if: inputs.mode != 'dry-run'
env:
RELEASE_MODE: ${{ inputs.mode }}
RELEASE_TAG: ${{ steps.release.outputs.tag }}
RELEASE_VERSION: ${{ steps.release.outputs.version }}
RELEASE_BRANCH: ${{ github.ref_name }}
run: |
mapfile -t release_apks < <(find androidApp/build/outputs/apk/full/release -maxdepth 1 -type f -name '*.apk' -print | sort)
if (( ${#release_apks[@]} == 0 )); then
echo "No release APKs were produced." >&2
exit 1
fi
release_args=(
"${RELEASE_TAG}"
--target "${RELEASE_BRANCH}"
--title "${RELEASE_VERSION}"
--notes-file release-notes.md
)
if [[ "${RELEASE_MODE}" == "draft" ]]; then
release_args+=(--draft)
fi
gh release create "${release_args[@]}" "${release_apks[@]}"