diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2946136 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,162 @@ +name: Release + +# Triggered by pushing a version tag, e.g. `git tag v0.22.0 && git push origin v0.22.0`. +# Builds a Linux x86_64 tarball and a signed Android APK, then publishes +# both as assets on a GitHub Release. Obtainium can track this repo's +# releases and download the APK automatically. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# ANDROID_KEYSTORE_BASE64 base64-encoded .jks file (see README for gen command) +# ANDROID_KEYSTORE_PASSWORD password used with -storepass when creating the keystore +# ANDROID_KEY_ALIAS alias used with -alias when creating the keystore +# ANDROID_KEY_PASSWORD password used with -keypass when creating the keystore + +on: + push: + tags: + - 'v*' + +permissions: + contents: write # gh release create needs write access + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: "-D warnings" + +# --------------------------------------------------------------------------- +# Job 1: Linux x86_64 binary + assets tarball +# --------------------------------------------------------------------------- +jobs: + build-linux: + name: Build · Linux x86_64 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Install system deps + run: | + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends \ + libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev + + - name: Cache cargo registry + build artifacts + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: linux-release-${{ hashFiles('**/Cargo.lock') }} + restore-keys: linux-release- + + - name: Build release binary + run: cargo build --release -p solitaire_app + + - name: Package tarball + run: | + mkdir solitaire-quest + cp target/release/solitaire_app solitaire-quest/ + cp -r assets solitaire-quest/ + tar -czf solitaire-quest-linux-x86_64.tar.gz solitaire-quest + + - uses: actions/upload-artifact@v4 + with: + name: linux + path: solitaire-quest-linux-x86_64.tar.gz + +# --------------------------------------------------------------------------- +# Job 2: Android APK (multi-arch) — debug-built then release-signed +# --------------------------------------------------------------------------- + build-android: + name: Build · Android APK + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install Rust stable + Android targets + uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android + + - name: Expose NDK root to cargo-apk + # ANDROID_NDK_LATEST_HOME is set by the GitHub-hosted runner. + # cargo-apk reads ANDROID_NDK_ROOT; write it to GITHUB_ENV so + # all subsequent steps in this job inherit it. + run: echo "ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV + + - name: Cache cargo registry + cargo-apk binary + build artifacts + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ~/.cargo/bin + target + key: android-release-${{ hashFiles('**/Cargo.lock') }} + restore-keys: android-release- + + - name: Install cargo-apk + # --locked: use the dependency versions cargo-apk was tested with. + # cargo install is a no-op when the cached binary is already current. + run: cargo install --locked cargo-apk + + - name: Build APK (release profile) + run: cargo apk build -p solitaire_app --release + + - name: Sign APK with release keystore + run: | + # Restore the keystore from the base64-encoded secret. + echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > release.keystore + + # Locate apksigner from whichever build-tools version the runner provides. + BUILD_TOOLS=$(ls "$ANDROID_HOME/build-tools" | sort -V | tail -1) + APKSIGNER="$ANDROID_HOME/build-tools/$BUILD_TOOLS/apksigner" + + "$APKSIGNER" sign \ + --ks release.keystore \ + --ks-key-alias "${{ secrets.ANDROID_KEY_ALIAS }}" \ + --ks-pass "pass:${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" \ + --key-pass "pass:${{ secrets.ANDROID_KEY_PASSWORD }}" \ + --out "solitaire-quest-${{ github.ref_name }}.apk" \ + target/release/apk/solitaire-quest.apk + + # Remove keystore immediately — never leave it as a build artifact. + rm release.keystore + + - uses: actions/upload-artifact@v4 + with: + name: android + path: solitaire-quest-${{ github.ref_name }}.apk + +# --------------------------------------------------------------------------- +# Job 3: Create the GitHub Release once both builds succeed +# --------------------------------------------------------------------------- + release: + name: Publish GitHub Release + runs-on: ubuntu-latest + needs: [build-linux, build-android] + + steps: + - uses: actions/download-artifact@v4 + with: + name: linux + + - uses: actions/download-artifact@v4 + with: + name: android + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create "${{ github.ref_name }}" \ + --repo "${{ github.repository }}" \ + --title "Solitaire Quest ${{ github.ref_name }}" \ + --generate-notes \ + "solitaire-quest-linux-x86_64.tar.gz" \ + "solitaire-quest-${{ github.ref_name }}.apk"