NuvioStreaming/.github/workflows/release.yml

333 lines
12 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: 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: Write runtime config to local.properties
uses: ./.github/actions/write-runtime-config
with:
supabase-url: ${{ secrets.SUPABASE_URL }}
supabase-anon-key: ${{ secrets.SUPABASE_ANON_KEY }}
trakt-client-id: ${{ secrets.TRAKT_CLIENT_ID }}
trakt-client-secret: ${{ secrets.TRAKT_CLIENT_SECRET }}
trakt-redirect-uri: ${{ secrets.TRAKT_REDIRECT_URI }}
introdb-api-url: ${{ secrets.INTRODB_API_URL }}
imdb-ratings-api-base-url: ${{ secrets.IMDB_RATINGS_API_BASE_URL }}
imdb-tapframe-api-base-url: ${{ secrets.IMDB_TAPFRAME_API_BASE_URL }}
contributions-url: ${{ secrets.CONTRIBUTIONS_URL }}
donations-base-url: ${{ secrets.DONATIONS_BASE_URL }}
donations-donate-url: ${{ secrets.DONATIONS_DONATE_URL }}
- 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: Write runtime config to local.properties
uses: ./.github/actions/write-runtime-config
with:
supabase-url: ${{ secrets.SUPABASE_URL }}
supabase-anon-key: ${{ secrets.SUPABASE_ANON_KEY }}
trakt-client-id: ${{ secrets.TRAKT_CLIENT_ID }}
trakt-client-secret: ${{ secrets.TRAKT_CLIENT_SECRET }}
trakt-redirect-uri: ${{ secrets.TRAKT_REDIRECT_URI }}
introdb-api-url: ${{ secrets.INTRODB_API_URL }}
imdb-ratings-api-base-url: ${{ secrets.IMDB_RATINGS_API_BASE_URL }}
imdb-tapframe-api-base-url: ${{ secrets.IMDB_TAPFRAME_API_BASE_URL }}
contributions-url: ${{ secrets.CONTRIBUTIONS_URL }}
donations-base-url: ${{ secrets.DONATIONS_BASE_URL }}
donations-donate-url: ${{ secrets.DONATIONS_DONATE_URL }}
# The Release Kotlin/Native link of the iOS framework regularly needs
# 810 GB of heap (Compose + Ktor + Supabase). Override at the user
# level so we don't have to raise the project default for local devs.
- name: Raise Gradle heap for Kotlin/Native release link
run: |
set -euo pipefail
mkdir -p "$HOME/.gradle"
cat >> "$HOME/.gradle/gradle.properties" <<'EOF'
org.gradle.jvmargs=-Xmx10g -Dfile.encoding=UTF-8 -XX:MaxMetaspaceSize=1g
kotlin.daemon.jvmargs=-Xmx4g
EOF
# Make sure no stale daemon is hanging around with the old args.
./gradlew --stop || true
- 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