feat(release): add beta release type

A beta is the stable build released as <version>-beta.N, with N set via
betaNumber in composeApp/build.gradle.kts. Betas and stable releases are
drafts; only nightly publishes immediately.
This commit is contained in:
Kingkor Roy Tirtho 2026-10-26 16:34:02 +06:00
parent d7c7e3108d
commit 67e452493a
3 changed files with 14 additions and 8 deletions

View File

@ -10,6 +10,7 @@ on:
default: 'stable' default: 'stable'
options: options:
- stable - stable
- beta
- nightly - nightly
dry_run: dry_run:
description: 'Dry run (build & upload artifacts only, skip creating release)' description: 'Dry run (build & upload artifacts only, skip creating release)'
@ -26,7 +27,7 @@ on:
permissions: permissions:
contents: read contents: read
# Never cancel a running release when another is dispatched, and let stable and nightly run side by side # Never cancel a running release when another is dispatched, and let stable, beta and nightly run side by side
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.release_type }} group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.release_type }}
cancel-in-progress: false cancel-in-progress: false
@ -215,7 +216,7 @@ jobs:
fi fi
# Desktop entry + metainfo, with the release history from git-cliff # Desktop entry + metainfo, with the release history from git-cliff
./gradlew :composeApp:renderPackagingLinux -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }} ./gradlew :composeApp:renderPackagingLinux -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
packaging/package-linux-tarball.sh "$VERSION" "${{ inputs.release_type }}" . packaging/package-linux-tarball.sh "$VERSION" "${{ inputs.release_type == 'nightly' && 'nightly' || 'stable' }}" .
- name: Upload tarball - name: Upload tarball
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
@ -335,7 +336,7 @@ jobs:
fi fi
# Desktop entry + metainfo, with the release history from git-cliff # Desktop entry + metainfo, with the release history from git-cliff
./gradlew :composeApp:renderPackagingLinux -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }} ./gradlew :composeApp:renderPackagingLinux -Pnightly=${{ inputs.release_type == 'nightly' }} -PbuildNumber=${{ github.run_number }}
packaging/package-linux-tarball.sh "$VERSION" "${{ inputs.release_type }}" . packaging/package-linux-tarball.sh "$VERSION" "${{ inputs.release_type == 'nightly' && 'nightly' || 'stable' }}" .
- name: Upload tarball - name: Upload tarball
uses: actions/upload-artifact@v7 uses: actions/upload-artifact@v7
@ -538,8 +539,9 @@ jobs:
- name: Set release variables - name: Set release variables
id: vars id: vars
run: | run: |
if [ "${{ inputs.release_type }}" = "stable" ]; then if [ "${{ inputs.release_type }}" != "nightly" ]; then
chmod +x gradlew chmod +x gradlew
# printVersion includes the -beta.N suffix when betaNumber is set in build.gradle.kts
VERSION=$(./gradlew -q :composeApp:printVersion) VERSION=$(./gradlew -q :composeApp:printVersion)
TAG="v${VERSION}" TAG="v${VERSION}"
echo "is_prerelease=false" >> "$GITHUB_OUTPUT" echo "is_prerelease=false" >> "$GITHUB_OUTPUT"

View File

@ -166,7 +166,7 @@ Android picks the Rust profile from its build type (`debug` or `release`) on its
### Releases ### Releases
Releases are built by [`spotube-release-binary.yml`](.github/workflows/spotube-release-binary.yml), run manually (`workflow_dispatch`) as `stable` or `nightly`. Each desktop platform (Linux x86_64 and aarch64, Windows, macOS) builds its native installers on its own runner. Android builds a signed APK. Releases are built by [`spotube-release-binary.yml`](.github/workflows/spotube-release-binary.yml), run manually (`workflow_dispatch`) as `stable`, `beta` or `nightly`. A beta is the stable build released like a stable release (draft, not a pre-release), tagged `v<version>-beta.N` (set `betaNumber` in `composeApp/build.gradle.kts` to N, and back to `null` for stable). Each desktop platform (Linux x86_64 and aarch64, Windows, macOS) builds its native installers on its own runner. Android builds a signed APK.
## 👥 The Spotube team ## 👥 The Spotube team

View File

@ -48,6 +48,10 @@ plugins {
val bundleId = "org.spotube.spotube" val bundleId = "org.spotube.spotube"
val version = "6.0.0" val version = "6.0.0"
// Set to N to release <version>-beta.N (same app id, icons and name as stable); null for a plain stable release.
// Installer metadata (packageVersion) must stay numeric, so it keeps using plain `version`.
val betaNumber: Int? = 0
// Betas share the stable app id, so bump this for every beta: Android only updates to a higher versionCode.
val buildNumber = 46 val buildNumber = 46
// Desktop icon channel; Android picks its icons via the stable/nightly product flavor. Use -Pnightly=true. // Desktop icon channel; Android picks its icons via the stable/nightly product flavor. Use -Pnightly=true.
val isNightly = providers.gradleProperty("nightly").map { it.toBoolean() }.getOrElse(false) val isNightly = providers.gradleProperty("nightly").map { it.toBoolean() }.getOrElse(false)
@ -70,7 +74,7 @@ val isProductionBuild = providers.gradleProperty("production").map { it.toBoolea
val targetName = "Spotube" val targetName = "Spotube"
val homePageUrl = "https://spotube.cc" val homePageUrl = "https://spotube.cc"
val repositoryUrl = "https://github.com/team-spotube/spotube" val repositoryUrl = "https://github.com/team-spotube/spotube"
val appVersionName = version val appVersionName = if (betaNumber != null) "$version-beta.$betaNumber" else version
val appBuildNumber = if (isNightly) nightlyBuildNumber else buildNumber val appBuildNumber = if (isNightly) nightlyBuildNumber else buildNumber
val appRepositoryUrl = repositoryUrl val appRepositoryUrl = repositoryUrl
// CI reads the release version from here instead of scraping this file. // CI reads the release version from here instead of scraping this file.
@ -375,8 +379,8 @@ android {
applicationId = bundleId applicationId = bundleId
minSdk = libs.versions.android.minSdk.get().toInt() minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt() targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = buildNumber versionCode = appBuildNumber
versionName = version versionName = appVersionName
} }
// stable = production channel, nightly = beta channel (own icon, name and installable side by side) // stable = production channel, nightly = beta channel (own icon, name and installable side by side)
flavorDimensions += "channel" flavorDimensions += "channel"