Compare commits

...

3 Commits

4 changed files with 64 additions and 51 deletions

View File

@ -22,17 +22,18 @@ on:
type: boolean type: boolean
default: false default: false
# Read-only by default; only create-release gets write access
permissions:
contents: read
# Never cancel a running release when another is dispatched, and let stable and nightly run side by side
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }} group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.release_type }}
cancel-in-progress: true cancel-in-progress: false
env: env:
COMPOSE_WEBVIEW_REPO: KRTirtho/ComposeNativeWebview COMPOSE_WEBVIEW_REPO: KRTirtho/ComposeNativeWebview
COMPOSE_WEBVIEW_COMMIT: cbd36f88927298a28a2d64c6acf7ec3b2def6f9d COMPOSE_WEBVIEW_COMMIT: cbd36f88927298a28a2d64c6acf7ec3b2def6f9d
GRADLE_PLUGIN_REPO: team-spotube/gradle-plugin
VLCJ_BUNDLER_REPO: team-spotube/vlcj-bundler-gradle-plugin
# Nucleus lists GraalVM CE releases via the GitHub API; unauthenticated calls hit the 60/hour limit on shared runners
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs: jobs:
prepare-deps: prepare-deps:
@ -47,20 +48,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
path: compose-webview path: compose-webview
- name: Checkout gradle-plugin
uses: actions/checkout@v7
with:
repository: ${{ env.GRADLE_PLUGIN_REPO }}
token: ${{ secrets.GITHUB_TOKEN }}
path: gradle-plugin
- name: Checkout vlcj-bundler
uses: actions/checkout@v7
with:
repository: ${{ env.VLCJ_BUNDLER_REPO }}
token: ${{ secrets.GITHUB_TOKEN }}
path: vlcj-bundler
- name: Set up JDK 21 - name: Set up JDK 21
uses: actions/setup-java@v6 uses: actions/setup-java@v6
with: with:
@ -82,18 +69,6 @@ jobs:
chmod +x gradlew chmod +x gradlew
./gradlew publishToMavenLocal ./gradlew publishToMavenLocal
- name: Publish gradle-plugin to mavenLocal
working-directory: gradle-plugin
run: |
chmod +x gradlew
./gradlew publishToMavenLocal
- name: Publish vlcj-bundler to mavenLocal
working-directory: vlcj-bundler
run: |
chmod +x gradlew
./gradlew publishToMavenLocal
- name: Upload maven local - name: Upload maven local
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
with: with:
@ -103,6 +78,7 @@ jobs:
build-android: build-android:
name: Android name: Android
needs: prepare-deps needs: prepare-deps
timeout-minutes: 120
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
@ -129,17 +105,23 @@ jobs:
run: chmod +x gradlew run: chmod +x gradlew
- name: Decode keystore - name: Decode keystore
env:
KEYSTORE: ${{ secrets.KEYSTORE }}
run: | run: |
echo '${{ secrets.KEYSTORE }}' | base64 --decode > "${{ github.workspace }}/composeApp/upload-keystore.jks" echo "$KEYSTORE" | base64 --decode > "${{ github.workspace }}/composeApp/upload-keystore.jks"
- name: Configure Android signing - name: Configure Android signing
working-directory: composeApp working-directory: composeApp
env:
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }}
KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }}
run: | run: |
cat >> local.properties <<EOF cat >> local.properties <<EOF
release.signing.storeFile=${{ github.workspace }}/composeApp/upload-keystore.jks release.signing.storeFile=${{ github.workspace }}/composeApp/upload-keystore.jks
release.signing.storePassword=${{ secrets.KEYSTORE_PASSWORD }} release.signing.storePassword=${KEYSTORE_PASSWORD}
release.signing.keyAlias=${{ secrets.KEYSTORE_KEY_ALIAS }} release.signing.keyAlias=${KEYSTORE_KEY_ALIAS}
release.signing.keyPassword=${{ secrets.KEYSTORE_KEY_PASSWORD }} release.signing.keyPassword=${KEYSTORE_KEY_PASSWORD}
EOF EOF
- name: Assemble Release - name: Assemble Release
@ -170,6 +152,7 @@ jobs:
build-linux: build-linux:
name: Linux Desktop name: Linux Desktop
needs: prepare-deps needs: prepare-deps
timeout-minutes: 120
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
@ -204,9 +187,13 @@ jobs:
uses: actions/cache@v6 uses: actions/cache@v6
with: with:
path: ~/.gradle/nucleus/graalvm path: ~/.gradle/nucleus/graalvm
key: graalvm-${{ runner.os }}-${{ runner.arch }} key: graalvm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('gradle/libs.versions.toml', 'composeApp/build.gradle.kts') }}
restore-keys: graalvm-${{ runner.os }}-${{ runner.arch }}-
- name: Package Linux - name: Package Linux
env:
# Nucleus lists GraalVM CE releases via the GitHub API; unauthenticated calls hit the 60/hour limit
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew :composeApp:packageGraalvmDeb :composeApp:packageGraalvmRpm -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }} run: ./gradlew :composeApp:packageGraalvmDeb :composeApp:packageGraalvmRpm -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
- name: Rename Linux artifacts - name: Rename Linux artifacts
@ -237,6 +224,7 @@ jobs:
build-linux-arm64: build-linux-arm64:
name: Linux Desktop (aarch64) name: Linux Desktop (aarch64)
needs: prepare-deps needs: prepare-deps
timeout-minutes: 120
runs-on: ubuntu-24.04-arm runs-on: ubuntu-24.04-arm
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
@ -278,10 +266,14 @@ jobs:
uses: actions/cache@v6 uses: actions/cache@v6
with: with:
path: ~/.gradle/nucleus/graalvm path: ~/.gradle/nucleus/graalvm
key: graalvm-${{ runner.os }}-${{ runner.arch }} key: graalvm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('gradle/libs.versions.toml', 'composeApp/build.gradle.kts') }}
restore-keys: graalvm-${{ runner.os }}-${{ runner.arch }}-
- name: Package Linux - name: Package Linux
run: ./gradlew :composeApp:packageGraalvmDeb :composeApp:packageGraalvmRpm --stacktrace -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }} env:
# Nucleus lists GraalVM CE releases via the GitHub API; unauthenticated calls hit the 60/hour limit
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew :composeApp:packageGraalvmDeb :composeApp:packageGraalvmRpm -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
- name: Rename Linux artifacts - name: Rename Linux artifacts
run: | run: |
@ -311,6 +303,7 @@ jobs:
build-windows: build-windows:
name: Windows Desktop name: Windows Desktop
needs: prepare-deps needs: prepare-deps
timeout-minutes: 120
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
@ -341,9 +334,13 @@ jobs:
uses: actions/cache@v6 uses: actions/cache@v6
with: with:
path: ~/.gradle/nucleus/graalvm path: ~/.gradle/nucleus/graalvm
key: graalvm-${{ runner.os }}-${{ runner.arch }} key: graalvm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('gradle/libs.versions.toml', 'composeApp/build.gradle.kts') }}
restore-keys: graalvm-${{ runner.os }}-${{ runner.arch }}-
- name: Package EXE - name: Package EXE
env:
# Nucleus lists GraalVM CE releases via the GitHub API; unauthenticated calls hit the 60/hour limit
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew :composeApp:packageGraalvmExe -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }} run: ./gradlew :composeApp:packageGraalvmExe -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
- name: Rename Windows artifacts - name: Rename Windows artifacts
@ -369,6 +366,7 @@ jobs:
build-macos: build-macos:
name: macOS Desktop name: macOS Desktop
needs: prepare-deps needs: prepare-deps
timeout-minutes: 120
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
@ -398,9 +396,13 @@ jobs:
uses: actions/cache@v6 uses: actions/cache@v6
with: with:
path: ~/.gradle/nucleus/graalvm path: ~/.gradle/nucleus/graalvm
key: graalvm-${{ runner.os }}-${{ runner.arch }} key: graalvm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('gradle/libs.versions.toml', 'composeApp/build.gradle.kts') }}
restore-keys: graalvm-${{ runner.os }}-${{ runner.arch }}-
- name: Package DMG - name: Package DMG
env:
# Nucleus lists GraalVM CE releases via the GitHub API; unauthenticated calls hit the 60/hour limit
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew :composeApp:packageGraalvmDmg -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }} run: ./gradlew :composeApp:packageGraalvmDmg -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
- name: Rename macOS artifact - name: Rename macOS artifact
@ -430,6 +432,8 @@ jobs:
- build-windows - build-windows
- build-macos - build-macos
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
@ -475,7 +479,7 @@ jobs:
run: | run: |
echo "## Downloads" > release-notes.md echo "## Downloads" > release-notes.md
echo "" >> release-notes.md echo "" >> release-notes.md
for dir in android-apk android-aab linux-deb linux-rpm linux-appimage linux-deb-arm64 linux-rpm-arm64 windows-msi windows-exe macos-dmg; do for dir in android-apk linux-deb linux-rpm linux-deb-arm64 linux-rpm-arm64 windows-exe macos-dmg; do
if [ -d "$dir" ]; then if [ -d "$dir" ]; then
echo "### $dir" >> release-notes.md echo "### $dir" >> release-notes.md
for f in "$dir"/*; do for f in "$dir"/*; do
@ -485,22 +489,29 @@ jobs:
fi fi
done done
# The rolling nightly tag already exists, and the release action never moves an existing tag
- name: Move nightly tag to this commit
if: ${{ inputs.release_type == 'nightly' }}
run: |
git tag -f nightly "$GITHUB_SHA"
git push -f origin refs/tags/nightly
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v3 uses: softprops/action-gh-release@v3
with: with:
tag_name: ${{ steps.vars.outputs.tag }} tag_name: ${{ steps.vars.outputs.tag }}
# A new tag (stable) would otherwise point at the default branch instead of the built commit
target_commitish: ${{ github.sha }}
fail_on_unmatched_files: true
name: ${{ steps.vars.outputs.release_name }} name: ${{ steps.vars.outputs.release_name }}
body_path: release-notes.md body_path: release-notes.md
draft: ${{ steps.vars.outputs.is_draft }} draft: ${{ steps.vars.outputs.is_draft }}
prerelease: ${{ steps.vars.outputs.is_prerelease }} prerelease: ${{ steps.vars.outputs.is_prerelease }}
files: | files: |
android-apk/*.apk android-apk/*.apk
android-aab/*.aab
linux-deb/*.deb linux-deb/*.deb
linux-rpm/*.rpm linux-rpm/*.rpm
linux-appimage/*.AppImage
linux-deb-arm64/*.deb linux-deb-arm64/*.deb
linux-rpm-arm64/*.rpm linux-rpm-arm64/*.rpm
windows-msi/*.msi
windows-exe/*.exe windows-exe/*.exe
macos-dmg/*.dmg macos-dmg/*.dmg

View File

@ -59,9 +59,7 @@
## Release workflow (`.github/workflows/release.yml`) ## Release workflow (`.github/workflows/release.yml`)
- Triggered by `workflow_dispatch` with a `release_type` choice input (`stable` | `nightly`). - Triggered by `workflow_dispatch` with a `release_type` choice input (`stable` | `nightly`).
- `prepare-deps` job checks out & publishes to `mavenLocal`: - `prepare-deps` job checks out & publishes `kdroidFilter/ComposeNativeWebview` (compose-webview) to `mavenLocal`. The spotube gradle plugin and vlcj-bundler now come from Maven Central.
- `kdroidFilter/ComposeNativeWebview` (compose-webview)
- `team-spotube/gradle-plugin` (spotubeGradle + vlcjBundler plugins)
- Build jobs per platform (Android, Linux, Windows, macOS), all `needs: prepare-deps`. Desktop jobs ship GraalVM native installers (`packageGraalvm{Deb,Rpm,Exe,Dmg}`, output in `composeApp/build/compose/binaries/main/graalvm-<format>/`), not jpackage/JVM ones. - Build jobs per platform (Android, Linux, Windows, macOS), all `needs: prepare-deps`. Desktop jobs ship GraalVM native installers (`packageGraalvm{Deb,Rpm,Exe,Dmg}`, output in `composeApp/build/compose/binaries/main/graalvm-<format>/`), not jpackage/JVM ones.
- **Stable**: reads `versionName` from `composeApp/build.gradle.kts`, tag = `v{version}`, draft release. - **Stable**: reads `versionName` from `composeApp/build.gradle.kts`, tag = `v{version}`, draft release.
- **Nightly**: builds with `-PversionName=nightly`, tag = `nightly` (updates existing), prerelease. - **Nightly**: builds with `-PversionName=nightly`, tag = `nightly` (updates existing), prerelease.

View File

@ -54,10 +54,8 @@ Spotube is a Kotlin Multiplatform + Compose Multiplatform app. The desktop app i
- **JDK 21** to run Gradle. The Gradle daemon JDK and the GraalVM toolchain are provisioned automatically. - **JDK 21** to run Gradle. The Gradle daemon JDK and the GraalVM toolchain are provisioned automatically.
- **Rust** (`rustup` + `cargo`). The `composeApp` Rust library is built through [Gobley](https://gobley.dev). - **Rust** (`rustup` + `cargo`). The `composeApp` Rust library is built through [Gobley](https://gobley.dev).
- **Android SDK**, only for Android builds. - **Android SDK**, only for Android builds.
- **Unpublished dependencies.** Three dependencies aren't on Maven Central yet. Clone each one and run `./gradlew publishToMavenLocal` in it: - **Unpublished dependency.** One dependency isn't on Maven Central yet. Clone it and run `./gradlew publishToMavenLocal` in it:
- [kdroidFilter/ComposeNativeWebview](https://github.com/kdroidFilter/ComposeNativeWebview), at the commit pinned in [`spotube-release-binary.yml`](.github/workflows/spotube-release-binary.yml) (`COMPOSE_WEBVIEW_COMMIT`) - [kdroidFilter/ComposeNativeWebview](https://github.com/kdroidFilter/ComposeNativeWebview), at the commit pinned in [`spotube-release-binary.yml`](.github/workflows/spotube-release-binary.yml) (`COMPOSE_WEBVIEW_COMMIT`)
- [team-spotube/gradle-plugin](https://github.com/team-spotube/gradle-plugin)
- [team-spotube/vlcj-bundler-gradle-plugin](https://github.com/team-spotube/vlcj-bundler-gradle-plugin)
- **Linux runtime:** `webkit2gtk-4.1` for the embedded webview. - **Linux runtime:** `webkit2gtk-4.1` for the embedded webview.
Always use the Gradle wrapper (`./gradlew`, or `gradlew.bat` on Windows). Always use the Gradle wrapper (`./gradlew`, or `gradlew.bat` on Windows).

View File

@ -550,6 +550,12 @@ run {
val isLinuxArm64Host = System.getProperty("os.name").lowercase().contains("linux") && val isLinuxArm64Host = System.getProperty("os.name").lowercase().contains("linux") &&
System.getProperty("os.arch").lowercase() in setOf("aarch64", "arm64") System.getProperty("os.arch").lowercase() in setOf("aarch64", "arm64")
if (isLinuxArm64Host) { if (isLinuxArm64Host) {
// Gobley generates the UniFFI bindings from the first Android build's library by default; use the host's.
uniffi {
generateFromLibrary {
build = GobleyHost.current.rustTarget
}
}
tasks.matching { it.name.startsWith("cargoBuildAndroid") || it.name.startsWith("rustUpTargetAddAndroid") } tasks.matching { it.name.startsWith("cargoBuildAndroid") || it.name.startsWith("rustUpTargetAddAndroid") }
.configureEach { enabled = false } .configureEach { enabled = false }
} }