Files
Meshtastic-Android/.github/workflows/release.yml
T

587 lines
24 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Make Release
on:
workflow_call:
inputs:
base_version:
description: 'The base version for the release (e.g., 2.3.0)'
required: true
type: string
tag_name:
description: 'The tag that triggered the release'
required: true
type: string
commit_sha:
description: 'The commit SHA to build and tag'
required: false
type: string
channel:
description: 'The channel to create a release for or promote to'
required: true
type: string
build_desktop:
description: 'Whether to build the desktop distribution'
required: false
type: boolean
default: false
build_flatpak_src:
description: 'Whether to build the Flatpak sources'
required: false
type: boolean
default: false
secrets:
GSERVICES:
required: true
KEYSTORE:
required: true
KEYSTORE_FILENAME:
required: true
KEYSTORE_PROPERTIES:
required: true
DATADOG_APPLICATION_ID:
required: true
DATADOG_CLIENT_TOKEN:
required: true
GOOGLE_MAPS_API_KEY:
required: true
GOOGLE_PLAY_JSON_KEY:
required: true
GRADLE_ENCRYPTION_KEY:
required: true
DEVELOCITY_ACCESS_KEY:
required: false
APPLE_SIGNING_IDENTITY:
required: false
APPLE_ID:
required: false
APPLE_APP_SPECIFIC_PASSWORD:
required: false
APPLE_TEAM_ID:
required: false
# Windows Authenticode signing via Azure Trusted Signing (now "Artifact Signing").
# All optional: the signing step skips cleanly when absent, like macOS above.
AZURE_TENANT_ID:
required: false
AZURE_CLIENT_ID:
required: false
AZURE_CLIENT_SECRET:
required: false
AZURE_TRUSTED_SIGNING_ENDPOINT:
required: false
AZURE_TRUSTED_SIGNING_ACCOUNT:
required: false
AZURE_TRUSTED_SIGNING_CERT_PROFILE:
required: false
concurrency:
group: ${{ github.workflow }}-${{ inputs.tag_name }}
cancel-in-progress: true
permissions:
contents: write
pull-requests: read
id-token: write
attestations: write
jobs:
prepare-build-info:
runs-on: ubuntu-24.04-arm
timeout-minutes: 10
outputs:
APP_VERSION_NAME: ${{ steps.prep_version.outputs.APP_VERSION_NAME }}
APP_VERSION_CODE: ${{ steps.calculate_version_code.outputs.versionCode }}
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag_name }}
fetch-depth: 0
submodules: 'recursive'
- name: Prep APP_VERSION_NAME
id: prep_version
env:
INPUT_TAG_NAME: ${{ inputs.tag_name }}
run: |
VERSION_NAME=$(echo "$INPUT_TAG_NAME" | sed 's/-.*//' | sed 's/v//')
echo "APP_VERSION_NAME=$VERSION_NAME" >> "$GITHUB_OUTPUT"
echo "Parsed Version: $VERSION_NAME"
- name: Extract VERSION_CODE_OFFSET from config.properties
id: get_version_code_offset
run: |
OFFSET=$(grep '^VERSION_CODE_OFFSET=' config.properties | cut -d'=' -f2)
echo "VERSION_CODE_OFFSET=$OFFSET" >> "$GITHUB_OUTPUT"
- name: Calculate Version Code from Git Commit Count
id: calculate_version_code
env:
VERSION_CODE_OFFSET: ${{ steps.get_version_code_offset.outputs.VERSION_CODE_OFFSET }}
run: |
COMMIT_COUNT=$(git rev-list --count HEAD)
if ! [[ "$VERSION_CODE_OFFSET" =~ ^[0-9]+$ ]]; then
echo "::error::VERSION_CODE_OFFSET from config.properties is not numeric: '$VERSION_CODE_OFFSET'"
exit 1
fi
VERSION_CODE=$((COMMIT_COUNT + VERSION_CODE_OFFSET))
echo "versionCode=$VERSION_CODE" >> "$GITHUB_OUTPUT"
shell: bash
release-google:
runs-on: ubuntu-24.04
timeout-minutes: 90
needs: [prepare-build-info]
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag_name }}
fetch-depth: 0
submodules: 'recursive'
- name: Gradle Setup
uses: ./.github/actions/gradle-setup
with:
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
develocity_access_key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
cache_read_only: 'false'
- name: Load secrets
env:
GSERVICES: ${{ secrets.GSERVICES }}
KEYSTORE: ${{ secrets.KEYSTORE }}
KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }}
KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }}
DATADOG_APPLICATION_ID: ${{ secrets.DATADOG_APPLICATION_ID }}
DATADOG_CLIENT_TOKEN: ${{ secrets.DATADOG_CLIENT_TOKEN }}
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
GOOGLE_PLAY_JSON_KEY: ${{ secrets.GOOGLE_PLAY_JSON_KEY }}
run: |
rm -f ./androidApp/google-services.json
echo "$GSERVICES" > ./androidApp/google-services.json
echo "$KEYSTORE" | base64 -di > "./androidApp/$KEYSTORE_FILENAME"
echo "$KEYSTORE_PROPERTIES" > ./keystore.properties
{
echo "datadogApplicationId=$DATADOG_APPLICATION_ID"
echo "datadogClientToken=$DATADOG_CLIENT_TOKEN"
echo "MAPS_API_KEY=$GOOGLE_MAPS_API_KEY"
} >> ./secrets.properties
echo "$GOOGLE_PLAY_JSON_KEY" > ./fastlane/play-store-credentials.json
- name: Setup Fastlane
uses: ruby/setup-ruby@v1
with:
ruby-version: '4.0.6'
bundler-cache: true
- name: Build and Deploy Google Play to Internal Track with Fastlane
env:
VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }}
VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }}
run: bundle exec fastlane internal
- name: List outputs
run: ls -R androidApp/build/outputs/
- name: Upload Google AAB artifact
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: google-aab
path: androidApp/build/outputs/bundle/googleRelease/androidApp-google-release.aab
retention-days: 1
- name: Upload Google APK artifact
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: google-apk
path: androidApp/build/outputs/apk/google/release/*.apk
retention-days: 1
- name: Attest Google AAB provenance
if: success()
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
with:
subject-path: androidApp/build/outputs/bundle/googleRelease/androidApp-google-release.aab
- name: Attest Google APK provenance
if: success()
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
with:
subject-path: androidApp/build/outputs/apk/google/release/*.apk
release-fdroid:
runs-on: ubuntu-24.04
timeout-minutes: 90
needs: [prepare-build-info]
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag_name }}
fetch-depth: 0
submodules: 'recursive'
- name: Gradle Setup
uses: ./.github/actions/gradle-setup
with:
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
develocity_access_key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
cache_read_only: 'false'
- name: Load secrets
env:
KEYSTORE: ${{ secrets.KEYSTORE }}
KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }}
KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }}
run: |
echo "$KEYSTORE" | base64 -di > "./androidApp/$KEYSTORE_FILENAME"
echo "$KEYSTORE_PROPERTIES" > ./keystore.properties
- name: Setup Fastlane
uses: ruby/setup-ruby@v1
with:
ruby-version: '4.0.6'
bundler-cache: true
- name: Build F-Droid with Fastlane
env:
VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }}
VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }}
run: bundle exec fastlane fdroid_build
- name: List outputs
run: ls -R androidApp/build/outputs/
- name: Upload F-Droid APK artifact
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: fdroid-apk
path: androidApp/build/outputs/apk/fdroid/release/*.apk
retention-days: 1
- name: Attest F-Droid APK provenance
if: success()
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
with:
subject-path: androidApp/build/outputs/apk/fdroid/release/*.apk
release-desktop:
if: ${{ inputs.build_desktop }}
runs-on: ${{ matrix.os }}
timeout-minutes: 90
needs: [prepare-build-info]
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-24.04, ubuntu-24.04-arm]
env:
# Secrets aren't readable in step `if:` expressions, so surface presence here.
SIGN_WINDOWS: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT != '' && 'true' || 'false' }}
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag_name }}
fetch-depth: 0
submodules: 'recursive'
- name: Gradle Setup
uses: ./.github/actions/gradle-setup
with:
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
develocity_access_key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
cache_read_only: 'false'
install_jetbrains_jdk: 'true'
- name: Install dependencies for AppImage
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y libfuse2t64
- name: Package Native Distributions
env:
ORG_GRADLE_PROJECT_appVersionName: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }}
VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }}
APPIMAGE_EXTRACT_AND_RUN: 1
SIGN_MACOS: ${{ runner.os == 'macOS' && secrets.APPLE_SIGNING_IDENTITY != '' && 'true' || 'false' }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# Build the release distribution for the current OS.
# On macOS, also build a ReleaseUberJar for the Homebrew Cask.
# Quote the -P flag: PowerShell on Windows interprets the dot in
# `-PaboutLibraries.release=true` as member access on `-PaboutLibraries`,
# splitting the token and feeding `.release=true` to Gradle as a task name.
# --no-isolated-projects --no-configuration-cache: CMP's Windows MSI/WiX packaging
# reads Project.layout on the root project, which IP and the CC each reject; both
# must be flags, not -D properties (inert on windows-latest), and --no-isolated-projects
# needs Gradle 9.7+ — full rationale on the desktop packaging step in reusable-check.yml.
run: >
./gradlew :desktopApp:packageReleaseDistributionForCurrentOS
${{ contains(runner.os, 'macOS') && ':desktopApp:packageReleaseUberJarForCurrentOS' || '' }}
'-PaboutLibraries.release=true' '-Pdesktop.release=true' --no-daemon
--no-isolated-projects --no-configuration-cache
# Authenticode-signs the MSI/EXE installers via Azure Trusted Signing
# (rebranded "Artifact Signing"). Skips cleanly when the secrets are
# absent, mirroring SIGN_MACOS. Required repository secrets:
# AZURE_TENANT_ID Entra tenant ID
# AZURE_CLIENT_ID app registration (service principal) client ID
# AZURE_CLIENT_SECRET client secret for that app registration
# AZURE_TRUSTED_SIGNING_ENDPOINT e.g. https://eus.codesigning.azure.net
# AZURE_TRUSTED_SIGNING_ACCOUNT Trusted Signing account name
# AZURE_TRUSTED_SIGNING_CERT_PROFILE public-trust certificate profile name
# The service principal needs the "Trusted Signing Certificate Profile
# Signer" role on the account. Presence is gated on the ENDPOINT secret;
# a half-configured set fails here loudly rather than shipping unsigned.
# Note: only the installers are signed, not the launcher .exe packaged
# inside them — SmartScreen reputation attaches to the downloaded file.
# Sign the inner .exe between createReleaseDistributable and packaging
# if that ever becomes necessary.
- name: Sign Windows installers (Azure Trusted Signing)
if: runner.os == 'Windows' && env.SIGN_WINDOWS == 'true'
uses: azure/artifact-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
endpoint: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
signing-account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT }}
certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERT_PROFILE }}
files-folder: desktopApp/build/compose/binaries/main-release
files-folder-filter: msi,exe
files-folder-recurse: true
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
# jpackage computes the .deb Depends: line from the build host's package names.
# Ubuntu 24.04 records time_t64 transition names (libasound2t64, libpng16-16t64)
# that don't exist on Debian 12 Bookworm / Raspberry Pi OS, making the .deb
# uninstallable there (#5233 pinned ubuntu-22.04 for this; those runners retire
# 2027-04, brownouts from 2026-09). Strip the t64 suffix instead: Bookworm ships
# the plain names, and on Trixie / Ubuntu 24.04+ the t64 packages Provide them.
# Rewriting the control file is the only host-independent fix — jpackage's
# --linux-package-deps is additive-only, and the Compose plugin clears its
# --resource-dir during the task, so a custom control template can't be injected.
- name: Rewrite t64 dependency names in .deb (Debian Bookworm / Pi OS compatibility)
if: runner.os == 'Linux'
run: |
shopt -s nullglob
for deb in desktopApp/build/compose/binaries/main-release/deb/*.deb; do
work=$(mktemp -d)
dpkg-deb -R "$deb" "$work"
echo "$(basename "$deb") before: $(grep '^Depends:' "$work/DEBIAN/control")"
sed -i -E '/^(Depends|Pre-Depends|Recommends):/ s/(lib[a-z0-9.+-]*)t64([, (]|$)/\1\2/g' "$work/DEBIAN/control"
echo "$(basename "$deb") after: $(grep '^Depends:' "$work/DEBIAN/control")"
if grep -nE '^[A-Za-z-]+:.*\blib[a-z0-9.+-]*t64\b' "$work/DEBIAN/control"; then
echo "::error::t64 dependency name survived rewrite in $(basename "$deb") — extend the sed above"
exit 1
fi
dpkg-deb -b --root-owner-group "$work" "$deb"
rm -rf "$work"
done
# CMP's TargetFormat.AppImage is jpackage's "app-image" — an unpacked
# directory, not a Linux .AppImage. Wrap it into a real AppImage so
# Linux users get a portable no-install binary alongside .deb/.rpm.
# This is what the libfuse2t64 install above is for: appimagetool is
# itself an AppImage (APPIMAGE_EXTRACT_AND_RUN is the no-FUSE fallback).
- name: Build AppImage from jpackage app-image
if: runner.os == 'Linux'
env:
APP_VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }}
run: scripts/build-appimage.sh
- name: List Desktop Binaries
if: runner.os == 'Linux' || runner.os == 'macOS'
run: ls -R desktopApp/build/compose/binaries/main-release
- name: Upload Desktop Artifacts
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: desktop-${{ runner.os }}-${{ runner.arch }}
path: |
desktopApp/build/compose/binaries/main-release/*/*.dmg
desktopApp/build/compose/binaries/main-release/*/*.msi
desktopApp/build/compose/binaries/main-release/*/*.exe
desktopApp/build/compose/binaries/main-release/*/*.deb
desktopApp/build/compose/binaries/main-release/*/*.rpm
desktopApp/build/compose/binaries/main-release/*/*.AppImage
desktopApp/build/compose/jars/*-release.jar
retention-days: 1
if-no-files-found: ignore
- name: Attest Desktop artifact provenance
if: success()
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
with:
subject-path: |
desktopApp/build/compose/binaries/main-release/*/*.dmg
desktopApp/build/compose/binaries/main-release/*/*.msi
desktopApp/build/compose/binaries/main-release/*/*.exe
desktopApp/build/compose/binaries/main-release/*/*.deb
desktopApp/build/compose/binaries/main-release/*/*.rpm
desktopApp/build/compose/binaries/main-release/*/*.AppImage
desktopApp/build/compose/jars/*-release.jar
create-flatpak-src:
if: ${{ inputs.build_flatpak_src }}
runs-on: ${{ matrix.os }}
timeout-minutes: 60
needs: [prepare-build-info]
strategy:
fail-fast: false
matrix:
# No .deb host-coupling here (pure-Java uber jar + URL manifest) — free to track
# the current LTS image.
os: [ubuntu-24.04, ubuntu-24.04-arm]
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag_name }}
fetch-depth: 0
submodules: 'recursive'
- name: Gradle Setup
uses: ./.github/actions/gradle-setup
with:
gradle_encryption_key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
develocity_access_key: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
cache_read_only: 'true'
install_jetbrains_jdk: 'true'
# Uses an isolated Gradle user home so every artifact actually traverses the
# network — the flatpak-sources plugin captures URLs via BuildOperationListener and
# only sees ExternalResourceReadBuildOperation events for cache misses. With the
# shared cache, downloads would be skipped and the manifest would be (nearly) empty.
# We drive packageUberJarForCurrentOS — the same task the in-flatpak build invokes —
# so the runtime classpath is fully resolved (skiko, ktor-cio, kable-btleplug-ffi, …
# are runtime-only and would be missed by :assemble).
- name: Generate Flatpak Sources
run: >
./gradlew --no-build-cache
-Dgradle.user.home=${{ runner.temp }}/flatpak-gradle-home
:desktopApp:packageUberJarForCurrentOS :captureFlatpakSources
- name: Stage manifest
run: cp build/flatpak-sources.json flatpak-sources.json
- name: List Flatpak source files
run: ls -l flatpak-sources.json
- name: Upload Flatpak source artifacts
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: flatpak-multisrc-${{ runner.arch }}
path: flatpak-sources.json
retention-days: 1
release-flatpak-src:
if: ${{ inputs.build_flatpak_src }}
runs-on: ubuntu-24.04
timeout-minutes: 30
needs: [create-flatpak-src]
steps:
- name: Download Flatpak source artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: flatpak-multisrc-*
path: flatpak-multisrc
- name: List Flatpak source files
run: ls -R flatpak-multisrc/
- name: Validate Flatpak source files
run: jq empty flatpak-multisrc/*/flatpak-sources.json
- name: Combine Flatpak source files
run: >
jq -s 'add | unique_by(.dest + "/" + .["dest-filename"])'
flatpak-multisrc/*/flatpak-sources.json
> flatpak-sources.json
- name: Upload combined Flatpak source artifact
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: flatpak-sources
path: flatpak-sources.json
retention-days: 1
- name: Attest combined Flatpak source artifact provenance
if: success()
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4
with:
subject-path: flatpak-sources.json
github-release:
if: ${{ !cancelled() && !failure() }}
runs-on: ubuntu-24.04-arm
timeout-minutes: 15
needs:
- prepare-build-info
- release-google
- release-fdroid
- release-desktop
- release-flatpak-src
permissions:
contents: write
id-token: write
attestations: write
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag_name }}
- name: Download all artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
path: ./artifacts
- name: Exclude flatpak-multisrc artifacts from release
run: rm -rf ./artifacts/flatpak-multisrc-*
# Generate the notes ourselves with an explicit previous tag. GitHub's automatic
# previous-release selection considers any published release, including the rolling
# "snapshot" prerelease that main-check.yml recreates on every push to main — when
# snapshot's commit is an ancestor of this tag it wins and the notes come out empty
# (see v2.8.0-open.3). Internal releases are drafts and invisible to the API's
# auto-selection, so pick the newest published v* release ourselves.
- name: Generate release notes
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
TAG: ${{ inputs.tag_name }}
TARGET: ${{ inputs.commit_sha || github.sha }}
run: |
# Fail the step if the listing itself fails — an empty PREV must only ever
# mean "no published v* release exists yet", never a swallowed API error,
# because empty PREV re-enables the automatic selection this step avoids.
PUBLISHED_TAGS=$(gh api "repos/${REPO}/releases" --paginate \
--jq '.[] | select(.draft == false) | .tag_name')
PREV=$(printf '%s\n' "$PUBLISHED_TAGS" | grep -E '^v[0-9]' | grep -Fxv "$TAG" | head -1 || true)
echo "Previous release tag: ${PREV:-<none — falling back to automatic selection>}"
gh api "repos/${REPO}/releases/generate-notes" \
-f tag_name="$TAG" \
-f target_commitish="$TARGET" \
${PREV:+-f previous_tag_name="$PREV"} \
--jq '.body' > release-notes.md
- name: Create or Update GitHub Release
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3
with:
tag_name: ${{ inputs.tag_name }}
target_commitish: ${{ inputs.commit_sha || github.sha }}
name: ${{ inputs.tag_name }} (${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }})
body_path: release-notes.md
files: ./artifacts/**/*
draft: true
prerelease: true