diff --git a/.github/workflows/app-publish.yaml b/.github/workflows/app-publish.yaml index c3b558d1..11bd2f45 100644 --- a/.github/workflows/app-publish.yaml +++ b/.github/workflows/app-publish.yaml @@ -10,93 +10,75 @@ jobs: name: Publish runs-on: ubuntu-24.04 if: ${{ contains(github.repository_owner, 'jellyfin') }} + permissions: + contents: write steps: - name: Checkout repository uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - name: Setup Java uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 with: distribution: temurin java-version-file: .tools-versions + - name: Setup Gradle uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0 + - name: Set JELLYFIN_VERSION run: echo "JELLYFIN_VERSION=$(echo ${GITHUB_REF#refs/tags/v} | tr / -)" >> $GITHUB_ENV + + - name: Decode keystore + run: echo "${{ secrets.KEYSTORE }}" | base64 --decode > keystore.jks + - name: Assemble release files - run: ./gradlew assemble bundleProprietaryRelease versionTxt - - name: Sign libre APK - id: libreSign - uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # tag=v1 env: - BUILD_TOOLS_VERSION: "34.0.0" - with: - releaseDirectory: app/build/outputs/apk/libre/release - signingKeyBase64: ${{ secrets.KEYSTORE }} - keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }} - alias: ${{ secrets.KEY_ALIAS }} - keyPassword: ${{ secrets.KEY_PASSWORD }} - - name: Sign proprietary APK - id: proprietarySign - uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # tag=v1 - env: - BUILD_TOOLS_VERSION: "34.0.0" - with: - releaseDirectory: app/build/outputs/apk/proprietary/release - signingKeyBase64: ${{ secrets.KEYSTORE }} - keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }} - alias: ${{ secrets.KEY_ALIAS }} - keyPassword: ${{ secrets.KEY_PASSWORD }} - - name: Sign proprietary app bundle - id: proprietaryBundleSign - uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # tag=v1 - env: - BUILD_TOOLS_VERSION: "34.0.0" - with: - releaseDirectory: app/build/outputs/bundle/proprietaryRelease - signingKeyBase64: ${{ secrets.KEYSTORE }} - keyStorePassword: ${{ secrets.KEYSTORE_PASSWORD }} - alias: ${{ secrets.KEY_ALIAS }} - keyPassword: ${{ secrets.KEY_PASSWORD }} + KEYSTORE_FILE: ${{ github.workspace }}/keystore.jks + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + SIGNING_KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + SIGNING_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} + run: ./gradlew assemble bundleRelease versionTxt + - name: Prepare release archive run: | mkdir -p build/jellyfin-publish - mv app/build/outputs/apk/*/*/jellyfin-android-*-libre-debug.apk build/jellyfin-publish/ - mv app/build/outputs/apk/*/*/jellyfin-android-*-proprietary-debug.apk build/jellyfin-publish/ - mv app/build/outputs/apk/*/*/jellyfin-android-*-libre-release-unsigned.apk build/jellyfin-publish/ - mv app/build/outputs/apk/*/*/jellyfin-android-*-proprietary-release-unsigned.apk build/jellyfin-publish/ - mv ${{ steps.libreSign.outputs.signedReleaseFile }} build/jellyfin-publish/jellyfin-android-v${{ env.JELLYFIN_VERSION }}-libre-release.apk - mv ${{ steps.proprietarySign.outputs.signedReleaseFile }} build/jellyfin-publish/jellyfin-android-v${{ env.JELLYFIN_VERSION }}-proprietary-release.apk - mv ${{ steps.proprietaryBundleSign.outputs.signedReleaseFile }} build/jellyfin-publish/jellyfin-android-v${{ env.JELLYFIN_VERSION }}-proprietary-release.aab + mv app/build/outputs/apk/libre/debug/jellyfin-android-*-libre-debug.apk build/jellyfin-publish/ + mv app/build/outputs/apk/libre/release/jellyfin-android-*-libre-release.apk build/jellyfin-publish/ + mv app/build/outputs/bundle/libreRelease/jellyfin-android-*-libre-release.aab build/jellyfin-publish/ + mv app/build/outputs/apk/proprietary/debug/jellyfin-android-*-proprietary-debug.apk build/jellyfin-publish/ + mv app/build/outputs/apk/proprietary/release/jellyfin-android-*-proprietary-release.apk build/jellyfin-publish/ + mv app/build/outputs/bundle/proprietaryRelease/jellyfin-android-*-proprietary-release.aab build/jellyfin-publish/ mv app/build/version.txt build/jellyfin-publish/ + - name: Upload release archive to GitHub release - uses: alexellis/upload-assets@13926a61cdb2cb35f5fdef1c06b8b591523236d3 # 0.4.1 env: - GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }} - with: - asset_paths: '["build/jellyfin-publish/*"]' + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release upload "$GITHUB_REF_NAME" build/jellyfin-publish/* --clobber + + - name: Configure deployment + run: | + mkdir -p ~/.ssh + echo "${{ secrets.REPO_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + - name: Upload release archive to repo.jellyfin.org - uses: burnett01/rsync-deployments@66257cad6bfeb2171d3b6bfa6c9a22279dd9c3a1 # 8.0.5 - with: - switches: -vrptz - path: build/jellyfin-publish/ - remote_path: /srv/incoming/android/v${{ env.JELLYFIN_VERSION }}/ - remote_host: ${{ secrets.REPO_HOST }} - remote_user: ${{ secrets.REPO_USER }} - remote_key: ${{ secrets.REPO_KEY }} + env: + TARGET_PATH: /srv/incoming/android/v${{ env.JELLYFIN_VERSION }}/ + SOURCE_PATH: build/jellyfin-publish/ + run: rsync -vrptz -e "ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no" "$SOURCE_PATH" "${{ secrets.REPO_USER }}@${{ secrets.REPO_HOST }}:$TARGET_PATH" + - name: Update repo.jellyfin.org symlinks - uses: appleboy/ssh-action@0ff4204d59e8e51228ff73bce53f80d53301dee2 # v1.2.5 - with: - host: ${{ secrets.REPO_HOST }} - username: ${{ secrets.REPO_USER }} - key: ${{ secrets.REPO_KEY }} - envs: JELLYFIN_VERSION - script_stop: true - script: | + env: + REPO_HOST: ${{ secrets.REPO_HOST }} + REPO_USER: ${{ secrets.REPO_USER }} + run: | + ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no "${REPO_USER}@${REPO_HOST}" "bash -s" < - signingConfigs.create("release") { - storeFile = config.storeFile - storePassword = config.storePassword - keyAlias = config.keyAlias - keyPassword = config.keyPassword + signingConfigs { + val keystoreFile = getProperty("keystore.file") + val keystorePassword = getProperty("keystore.password") + val signingKeyAlias = getProperty("signing.key.alias") + val signingKeyPassword = getProperty("signing.key.password") + + if (keystoreFile != null && keystorePassword != null && signingKeyAlias != null && signingKeyPassword != null) { + create("release") { + storeFile = file(keystoreFile) + storePassword = keystorePassword + keyAlias = signingKeyAlias + keyPassword = signingKeyPassword + } } } + dependenciesInfo { + includeInBundle = false + includeInApk = false + } + buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources = true proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") - signingConfig = releaseSigningConfig + signingConfig = signingConfigs.findByName("release") } + getByName("debug") { applicationIdSuffix = ".debug" isDebuggable = true diff --git a/buildSrc/src/main/kotlin/Properties.kt b/buildSrc/src/main/kotlin/Properties.kt new file mode 100644 index 00000000..20782630 --- /dev/null +++ b/buildSrc/src/main/kotlin/Properties.kt @@ -0,0 +1,11 @@ +import org.gradle.api.Project + +/** + * Helper function to retrieve configuration variable values + */ +fun Project.getProperty(name: String): String? { + // sample.var --> SAMPLE_VAR + val environmentName = name.uppercase().replace(".", "_") + + return findProperty(name)?.toString() ?: System.getenv(environmentName) ?: null +} diff --git a/buildSrc/src/main/kotlin/SigningHelper.kt b/buildSrc/src/main/kotlin/SigningHelper.kt deleted file mode 100644 index 5881768e..00000000 --- a/buildSrc/src/main/kotlin/SigningHelper.kt +++ /dev/null @@ -1,49 +0,0 @@ -import org.gradle.api.Project -import java.io.File -import java.util.* - -object SigningHelper { - - fun loadSigningConfig(project: Project): Config? { - val serializedKeystore = System.getenv("KEYSTORE") ?: return null - val storeFile = try { - project.file("/tmp/keystore.jks").apply { - writeBytes(Base64.getDecoder().decode(serializedKeystore)) - } - } catch (e: RuntimeException) { - return null - } - val storePassword = System.getenv("KEYSTORE_PASSWORD") ?: return null - val keyAlias = System.getenv("KEY_ALIAS") ?: return null - val keyPassword = System.getenv("KEY_PASSWORD") ?: return null - - return Config( - storeFile, - storePassword, - keyAlias, - keyPassword - ) - } - - data class Config( - /** - * Store file used when signing. - */ - val storeFile: File, - - /** - * Store password used when signing. - */ - val storePassword: String, - - /** - * Key alias used when signing. - */ - val keyAlias: String, - - /** - * Key password used when signing. - */ - val keyPassword: String - ) -} diff --git a/buildSrc/src/main/kotlin/VersionUtils.kt b/buildSrc/src/main/kotlin/VersionUtils.kt index cb123fbd..efca67e2 100644 --- a/buildSrc/src/main/kotlin/VersionUtils.kt +++ b/buildSrc/src/main/kotlin/VersionUtils.kt @@ -10,29 +10,26 @@ import org.gradle.api.Project * v2.0.0 -> 2.0.0 * null -> 0.0.0-dev.1 (unless different fallback set) */ -fun Project.getVersionName(fallback: String = "0.0.0-dev.1"): String { - val configuredVersion = System.getenv("JELLYFIN_VERSION") - ?: findProperty("jellyfin.version")?.toString() - - return configuredVersion?.removePrefix("v") ?: fallback -} +fun Project.getVersionName(fallback: String = "0.0.0-dev.1") = + getProperty("jellyfin.version") + ?.removePrefix("v") + ?: fallback /** * Get the version code for a given semantic version. * Does not validate the input and thus will throw an exception when parts are missing. * - * The pre-release part ("-rc.1", "-beta.1" etc.) defaults to 99 when not specified. + * The pre-release part ("-rc.1", "-beta.1" etc.) defaults to 99 * * Sample output: - * MA.MI.PA-PR > MA MI PA PR - * 0.0.0-dev.1 > 1 - * 0.0.0 > 99 - * 1.1.1 > 1 01 01 99 - * 0.7.0 > 7 00 99 - * 99.99.99 > 99 99 99 99 - * 2.0.0-rc.3 > 2 00 00 03 - * 2.0.0 > 2 00 00 99 - * 99.99.99-rc.1 > 99 99 99 01 + * MA.MI.PA-PR -> MAMIPAPR + * 0.0.0 -> 99 + * 1.1.1 -> 1010199 + * 0.7.0 -> 70099 + * 99.99.99 -> 99999999 + * 2.0.0-rc.3 -> 2000003 + * 2.0.0 -> 2000099 + * 99.99.99-rc.1 -> 99999901 */ fun getVersionCode(versionName: String): Int { // Split to core and pre release parts with a default for pre release (null)