NuvioStreaming/.github/workflows/release.yml
2026-05-10 00:26:18 +02:00

289 lines
9.6 KiB
YAML

name: Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-*'
permissions:
contents: write
jobs:
prepare:
name: Prepare release metadata
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
is_prerelease: ${{ steps.version.outputs.is_prerelease }}
steps:
- name: Extract version from tag
id: version
run: |
set -euo pipefail
tag="${GITHUB_REF##*/}"
version="${tag#v}"
if [[ "$version" == *-* ]]; then
is_prerelease=true
else
is_prerelease=false
fi
{
echo "tag=$tag"
echo "version=$version"
echo "is_prerelease=$is_prerelease"
} >>"$GITHUB_OUTPUT"
echo "Tag: $tag (version: $version, prerelease: $is_prerelease)"
android:
name: Android release
runs-on: ubuntu-latest
needs: prepare
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 1
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Configure Android release signing
env:
KEYSTORE_BASE64: ${{ secrets.NUVIO_RELEASE_KEYSTORE_BASE64 }}
KEYSTORE_PASSWORD: ${{ secrets.NUVIO_RELEASE_STORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.NUVIO_RELEASE_KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.NUVIO_RELEASE_KEY_PASSWORD }}
run: |
set -euo pipefail
if [[ -n "${KEYSTORE_BASE64:-}" ]]; then
keystore_path="$RUNNER_TEMP/nuvio-release.keystore"
printf '%s' "$KEYSTORE_BASE64" | base64 -d > "$keystore_path"
store_password="$KEYSTORE_PASSWORD"
key_alias="$KEY_ALIAS"
key_password="$KEY_PASSWORD"
echo "Using release keystore from secrets."
else
echo "::warning::Release signing secrets not set — generating an ephemeral debug-style keystore so the build can complete. APKs will not be installable over signed production builds."
keystore_path="$RUNNER_TEMP/nuvio-fallback.keystore"
store_password="android"
key_alias="androiddebugkey"
key_password="android"
keytool -genkeypair -v \
-keystore "$keystore_path" \
-storepass "$store_password" \
-keypass "$key_password" \
-alias "$key_alias" \
-keyalg RSA -keysize 2048 -validity 10000 \
-dname "CN=Android Debug,O=Android,C=US"
fi
{
echo ""
echo "NUVIO_RELEASE_STORE_FILE=$keystore_path"
echo "NUVIO_RELEASE_STORE_PASSWORD=$store_password"
echo "NUVIO_RELEASE_KEY_ALIAS=$key_alias"
echo "NUVIO_RELEASE_KEY_PASSWORD=$key_password"
} >> local.properties
- name: Build per-ABI release APKs (full + playstore)
run: |
./gradlew \
:composeApp:assembleFullRelease \
:composeApp:assemblePlaystoreRelease \
-Pnuvio.splitAbi=true \
--stacktrace
- name: Stage and rename APK artifacts
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
set -euo pipefail
mkdir -p artifacts
for flavor in full playstore; do
for abi in arm64-v8a armeabi-v7a x86_64; do
src=$(ls "composeApp/build/outputs/apk/${flavor}/release/composeApp-${flavor}-${abi}-release"*.apk 2>/dev/null | head -n1 || true)
if [[ -z "$src" ]]; then
echo "::error::Missing APK for ${flavor}/${abi}"
ls -la "composeApp/build/outputs/apk/${flavor}/release/" || true
exit 1
fi
dest="artifacts/Nuvio-${VERSION}-android-${flavor}-${abi}.apk"
cp "$src" "$dest"
echo " $src -> $dest"
done
done
ls -la artifacts/
- name: Upload Android APK artifacts
uses: actions/upload-artifact@v4
with:
name: android-apks
path: artifacts/*.apk
if-no-files-found: error
retention-days: 7
ios:
name: iOS release
runs-on: macos-15
needs: prepare
timeout-minutes: 90
env:
NUVIO_IOS_DISTRIBUTION: full
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 1
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Select latest stable Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Make gradlew executable
run: chmod +x ./gradlew
- name: Archive iOS app (Release, unsigned)
run: |
set -euo pipefail
xcodebuild \
-project iosApp/iosApp.xcodeproj \
-scheme iosApp \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath build/Nuvio.xcarchive \
-skipPackagePluginValidation \
-skipMacroValidation \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY="" \
DEVELOPMENT_TEAM="" \
archive
- name: Repackage .app into unsigned .ipa
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
set -euo pipefail
mkdir -p artifacts build/Payload
app_path="build/Nuvio.xcarchive/Products/Applications/Nuvio.app"
if [[ ! -d "$app_path" ]]; then
echo "::error::Built .app not found at $app_path"
ls -laR build/Nuvio.xcarchive || true
exit 1
fi
cp -R "$app_path" build/Payload/
ipa_name="Nuvio-${VERSION}-ios.ipa"
(
cd build
zip -ry "$ipa_name" Payload >/dev/null
)
mv "build/$ipa_name" "artifacts/$ipa_name"
ls -la artifacts/
- name: Upload iOS IPA artifact
uses: actions/upload-artifact@v4
with:
name: ios-ipa
path: artifacts/*.ipa
if-no-files-found: error
retention-days: 7
release:
name: Publish GitHub release
runs-on: ubuntu-latest
needs: [prepare, android, ios]
timeout-minutes: 15
steps:
- name: Download Android artifacts
uses: actions/download-artifact@v4
with:
name: android-apks
path: release-assets/
- name: Download iOS artifact
uses: actions/download-artifact@v4
with:
name: ios-ipa
path: release-assets/
- name: List release assets
run: ls -la release-assets/
- name: Compose release notes
env:
VERSION: ${{ needs.prepare.outputs.version }}
TAG: ${{ needs.prepare.outputs.tag }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
base="https://github.com/${REPO}/releases/download/${TAG}"
link() {
local file="$1"
local label="$2"
if [[ -f "release-assets/$file" ]]; then
printf '[%s](%s/%s)' "$label" "$base" "$file"
else
printf '—'
fi
}
{
echo "## Nuvio ${VERSION}"
echo
echo "### Downloads"
echo
echo "| Architecture | Android (Full) | Android (Play Store) | iOS |"
echo "| --- | --- | --- | --- |"
printf '| arm64 | %s | %s | %s |\n' \
"$(link "Nuvio-${VERSION}-android-full-arm64-v8a.apk" "APK")" \
"$(link "Nuvio-${VERSION}-android-playstore-arm64-v8a.apk" "APK")" \
"$(link "Nuvio-${VERSION}-ios.ipa" "IPA")"
printf '| armeabi-v7a | %s | %s | — |\n' \
"$(link "Nuvio-${VERSION}-android-full-armeabi-v7a.apk" "APK")" \
"$(link "Nuvio-${VERSION}-android-playstore-armeabi-v7a.apk" "APK")"
printf '| x86_64 | %s | %s | — |\n' \
"$(link "Nuvio-${VERSION}-android-full-x86_64.apk" "APK")" \
"$(link "Nuvio-${VERSION}-android-playstore-x86_64.apk" "APK")"
echo
echo "### Notes"
echo
echo "- **Android (Full)** is the sideload-distribution flavor with the full feature set. **Android (Play Store)** matches the Play Store-distribution flavor."
echo "- The iOS .ipa is **unsigned** — install with AltStore, Sideloadly, or another sideloading tool."
echo "- Desktop builds (Windows / macOS / Linux) are not yet published from this workflow."
} > release-notes.md
echo "--- release notes ---"
cat release-notes.md
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
name: "Nuvio ${{ needs.prepare.outputs.version }}"
tag_name: ${{ needs.prepare.outputs.tag }}
body_path: release-notes.md
prerelease: ${{ needs.prepare.outputs.is_prerelease == 'true' }}
files: |
release-assets/*.apk
release-assets/*.ipa
fail_on_unmatched_files: true