Fix microphone staying open when a recording is superseded before it … #200
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build desktop apps | |
| on: | |
| push: | |
| branches: [dev] | |
| tags: ['v*'] | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Release tag to build and attach desktop bundles to (e.g. v1.20.0)' | |
| required: true | |
| type: string | |
| permissions: {} | |
| concurrency: | |
| group: tauri-build-${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} | |
| cancel-in-progress: ${{ github.ref == 'refs/heads/dev' }} | |
| jobs: | |
| setup-release: | |
| name: Resolve release target | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| if: "${{ !(github.event_name == 'push' && github.ref == 'refs/heads/dev' && contains(github.event.head_commit.message, 'chore: prepare release')) }}" | |
| outputs: | |
| tag: ${{ steps.meta.outputs.tag }} | |
| version: ${{ steps.meta.outputs.version }} | |
| ref: ${{ steps.meta.outputs.ref }} | |
| nightly: ${{ steps.meta.outputs.nightly }} | |
| started_at: ${{ steps.meta.outputs.started_at }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - uses: ./.github/actions/resolve-release-meta | |
| id: meta | |
| with: | |
| input-tag: ${{ inputs.tag }} | |
| - name: Wait for release to exist | |
| if: ${{ steps.meta.outputs.nightly != 'true' }} | |
| shell: bash | |
| env: | |
| TAG: ${{ steps.meta.outputs.tag }} | |
| run: | | |
| for i in $(seq 1 12); do | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| echo "Release $TAG found." | |
| exit 0 | |
| fi | |
| echo "Waiting for release $TAG to be published ($i/12)…" | |
| sleep 15 | |
| done | |
| echo "Release $TAG was not found." >&2 | |
| exit 1 | |
| - name: Ensure nightly prerelease exists | |
| if: ${{ steps.meta.outputs.nightly == 'true' }} | |
| shell: bash | |
| env: | |
| GIT_SHA: ${{ github.sha }} | |
| run: | | |
| if gh release view nightly >/dev/null 2>&1; then | |
| echo "Reusing the existing nightly release while the new build is prepared." | |
| else | |
| gh release create nightly \ | |
| --prerelease \ | |
| --target "$GIT_SHA" \ | |
| --title "Nightly (dev)" \ | |
| --notes "Rolling nightly build from dev — $GIT_SHA" | |
| fi | |
| build: | |
| name: Build ${{ matrix.name }} | |
| needs: setup-release | |
| runs-on: ${{ matrix.platform }} | |
| timeout-minutes: 60 | |
| permissions: | |
| contents: write | |
| id-token: write | |
| attestations: write | |
| artifact-metadata: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: macOS | |
| platform: macos-latest | |
| asset-platform: macos-universal | |
| rust-targets: aarch64-apple-darwin x86_64-apple-darwin | |
| args: --target universal-apple-darwin | |
| bundle_path: src-tauri/target/universal-apple-darwin/release/bundle | |
| - name: Windows | |
| platform: windows-latest | |
| asset-platform: windows-x86_64 | |
| rust-targets: '' | |
| args: '' | |
| bundle_path: src-tauri/target/release/bundle | |
| - name: Linux (CEF) | |
| platform: ubuntu-22.04 | |
| asset-platform: linux-x86_64 | |
| rust-targets: '' | |
| cef: true | |
| bundle_path: src-tauri/target/release/bundle | |
| env: | |
| TAG: ${{ needs.setup-release.outputs.tag }} | |
| VERSION: ${{ needs.setup-release.outputs.version }} | |
| VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} | |
| SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} | |
| VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} | |
| VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| CARGO_INCREMENTAL: 0 | |
| CARGO_TERM_COLOR: always | |
| NODE_OPTIONS: --max-old-space-size=8192 | |
| TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} | |
| TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ needs.setup-release.outputs.ref }} | |
| persist-credentials: false | |
| - name: Setup app | |
| uses: ./.github/actions/setup | |
| with: | |
| tauri: 'true' | |
| - name: Add Rust targets | |
| if: ${{ matrix.rust-targets != '' }} | |
| shell: bash | |
| run: rustup target add ${{ matrix.rust-targets }} | |
| - name: Cache Rust build | |
| uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| with: | |
| workspaces: src-tauri | |
| - name: Stamp release version into tauri.conf.json | |
| shell: bash | |
| env: | |
| UPDATER_ENDPOINT: ${{ needs.setup-release.outputs.nightly == 'true' && format('https://github.com/{0}/releases/download/nightly/latest.json', github.repository) || '' }} | |
| run: node .github/scripts/set-tauri-version.mjs "$VERSION" "$UPDATER_ENDPOINT" | |
| - name: Configure build args | |
| shell: bash | |
| env: | |
| IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} | |
| MATRIX_NAME: ${{ matrix.name }} | |
| run: | | |
| ARGS="" | |
| if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ]; then | |
| ARGS='--config {"bundle":{"createUpdaterArtifacts":false}}' | |
| fi | |
| if [ "$IS_NIGHTLY" = "true" ] && [ "$MATRIX_NAME" = "Windows" ]; then | |
| ARGS="$ARGS --bundles nsis" | |
| fi | |
| if [ "$IS_NIGHTLY" = "true" ]; then | |
| echo "SABLE_BUILD_FLAVOR=dev" >> "$GITHUB_ENV" | |
| fi | |
| echo "TAURI_BUILD_ARGS=${ARGS# }" >> "$GITHUB_ENV" | |
| - name: Build desktop bundles | |
| if: ${{ !matrix.cef }} | |
| shell: bash | |
| run: pnpm tauri build ${{ matrix.args }} $TAURI_BUILD_ARGS | |
| - name: Package macOS .app for updater | |
| if: ${{ matrix.name == 'macOS' }} | |
| shell: bash | |
| run: | | |
| macos_dir="${{ matrix.bundle_path }}/macos" | |
| if [ -d "$macos_dir" ]; then | |
| for app in "$macos_dir"/*.app; do | |
| [ -e "$app" ] || continue | |
| echo "Packaging $(basename "$app") into .tar.gz" | |
| tar czf "$app.tar.gz" -C "$macos_dir" "$(basename "$app")" | |
| done | |
| fi | |
| - name: Normalize desktop artifact names | |
| if: ${{ !matrix.cef }} | |
| shell: bash | |
| env: | |
| ASSET_PLATFORM: ${{ matrix.asset-platform }} | |
| BUNDLE_PATH: ${{ matrix.bundle_path }} | |
| run: | | |
| test -d "$BUNDLE_PATH" || { echo "Bundle directory not found: $BUNDLE_PATH" >&2; exit 1; } | |
| find "$BUNDLE_PATH" -type f -print0 \ | |
| | while IFS= read -r -d '' path; do | |
| case "$path" in | |
| *.app.tar.gz.sig) suffix='.app.tar.gz.sig' ;; | |
| *.app.tar.gz) suffix='.app.tar.gz' ;; | |
| *.nsis.zip.sig) suffix='.nsis.zip.sig' ;; | |
| *.nsis.zip) suffix='.nsis.zip' ;; | |
| *-setup.exe.sig) suffix='-setup.exe.sig' ;; | |
| *-setup.exe) suffix='-setup.exe' ;; | |
| *.msi.sig) suffix='.msi.sig' ;; | |
| *.msi) suffix='.msi' ;; | |
| *.dmg) suffix='.dmg' ;; | |
| *) continue ;; | |
| esac | |
| target="$(dirname "$path")/Sable-${VERSION}-${ASSET_PLATFORM}${suffix}" | |
| [ "$path" = "$target" ] || mv "$path" "$target" | |
| done | |
| - name: Attest Windows bundles | |
| if: ${{ matrix.name == 'Windows' }} | |
| uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 | |
| with: | |
| subject-path: | | |
| src-tauri/target/release/bundle/msi/*.msi | |
| src-tauri/target/release/bundle/msi/*.sig | |
| src-tauri/target/release/bundle/nsis/*.exe | |
| src-tauri/target/release/bundle/nsis/*.sig | |
| - name: Attach bundles to release | |
| if: ${{ !matrix.cef }} | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| BUNDLE_PATH: ${{ matrix.bundle_path }} | |
| run: | | |
| test -d "$BUNDLE_PATH" || { echo "Bundle directory not found: $BUNDLE_PATH" >&2; exit 1; } | |
| find "$BUNDLE_PATH" -type f -name 'Sable-*' -print0 \ | |
| | while IFS= read -r -d '' path; do | |
| echo "Uploading $path" | |
| gh release upload "$TAG" "$path" --clobber | |
| done | |
| # --- Linux: CEF runtime, packaged as deb / rpm / AppImage --- | |
| - name: Remove cached CEF build outputs | |
| if: matrix.cef | |
| shell: bash | |
| run: | | |
| build_dir="src-tauri/target/release/build" | |
| if [ -d "$build_dir" ]; then | |
| find "$build_dir" -mindepth 1 -maxdepth 1 -type d -name 'cef-*' -print -exec rm -rf {} + | |
| fi | |
| - name: Build CEF binary | |
| if: matrix.cef | |
| shell: bash | |
| run: pnpm tauri:cef build $TAURI_BUILD_ARGS | |
| - name: Build native packages | |
| if: matrix.cef | |
| shell: bash | |
| run: mise run cef:package "$VERSION" | |
| - name: Sign the AppImage for the updater | |
| if: ${{ matrix.cef && env.TAURI_SIGNING_PRIVATE_KEY != '' }} | |
| shell: bash | |
| run: | | |
| for f in src-tauri/target/release/bundle/appimage/Sable-*-linux-x86_64.AppImage; do | |
| [ -e "$f" ] || continue | |
| pnpm tauri signer sign "$f" | |
| done | |
| - name: Attest Linux bundles | |
| if: matrix.cef | |
| uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 | |
| with: | |
| subject-path: | | |
| src-tauri/target/release/bundle/deb/*.deb | |
| src-tauri/target/release/bundle/rpm/*.rpm | |
| src-tauri/target/release/bundle/appimage/*.AppImage | |
| src-tauri/target/release/bundle/appimage/*.sig | |
| - name: Attach native packages to release | |
| if: matrix.cef | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| BUNDLE_PATH: ${{ matrix.bundle_path }} | |
| run: | | |
| BUNDLE="$BUNDLE_PATH" | |
| test -d "$BUNDLE" || { echo "Bundle directory not found: $BUNDLE" >&2; exit 1; } | |
| for f in "$BUNDLE"/deb/Sable-*-linux-x86_64.deb \ | |
| "$BUNDLE"/rpm/Sable-*-linux-x86_64.rpm \ | |
| "$BUNDLE"/appimage/Sable-*-linux-x86_64.AppImage \ | |
| "$BUNDLE"/appimage/Sable-*-linux-x86_64.AppImage.sig; do | |
| [ -e "$f" ] || continue | |
| echo "Uploading $f" | |
| gh release upload "$TAG" "$f" --clobber | |
| done | |
| android: | |
| name: Build Android | |
| needs: setup-release | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| permissions: | |
| contents: write | |
| id-token: write | |
| attestations: write | |
| artifact-metadata: write | |
| env: | |
| TAG: ${{ needs.setup-release.outputs.tag }} | |
| VERSION: ${{ needs.setup-release.outputs.version }} | |
| VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} | |
| SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} | |
| VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} | |
| VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| CARGO_INCREMENTAL: 0 | |
| CARGO_TERM_COLOR: always | |
| MISE_ENV: tauri | |
| NODE_OPTIONS: --max-old-space-size=8192 | |
| # Matches the ABIs passed to `tauri android build` below. | |
| RUST_TARGETS: aarch64-linux-android armv7-linux-androideabi | |
| ANDROID_KEY_BASE64: ${{ secrets.ANDROID_KEY_BASE64 }} | |
| ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} | |
| ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ needs.setup-release.outputs.ref }} | |
| persist-credentials: false | |
| - name: Setup app | |
| uses: ./.github/actions/setup | |
| with: | |
| tauri: 'true' | |
| - name: Setup Android SDK + NDK | |
| shell: bash | |
| run: mise run tauri:setup:android | |
| - name: Add Rust targets | |
| shell: bash | |
| run: rustup target add $RUST_TARGETS | |
| - name: Cache Rust build | |
| uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| with: | |
| workspaces: src-tauri | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0 | |
| - name: Resolve NDK | |
| shell: bash | |
| run: | | |
| echo "NDK_HOME=$ANDROID_HOME/ndk/$ANDROID_NDK_VERSION" >> "$GITHUB_ENV" | |
| - name: Stamp release version into tauri.conf.json | |
| shell: bash | |
| run: node .github/scripts/set-tauri-version.mjs "$VERSION" | |
| - name: Setup Android signing | |
| if: ${{ env.ANDROID_KEY_BASE64 != '' }} | |
| shell: bash | |
| run: | | |
| cd src-tauri/gen/android | |
| echo "keyAlias=$ANDROID_KEY_ALIAS" > keystore.properties | |
| echo "password=$ANDROID_KEY_PASSWORD" >> keystore.properties | |
| base64 -d <<< "$ANDROID_KEY_BASE64" > "$RUNNER_TEMP/keystore.jks" | |
| echo "storeFile=$RUNNER_TEMP/keystore.jks" >> keystore.properties | |
| - name: Build Android bundles | |
| shell: bash | |
| env: | |
| IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} | |
| run: | | |
| if [ "$IS_NIGHTLY" = "true" ]; then | |
| export SABLE_BUILD_FLAVOR=dev | |
| fi | |
| pnpm tauri android build --apk --aab --target aarch64 armv7 | |
| OUT='src-tauri/gen/android/app/build/outputs' | |
| APK=$(find "$OUT/apk/universal/release" -name '*.apk' -type f | head -1) | |
| AAB=$(find "$OUT/bundle/universalRelease" -name '*.aab' -type f | head -1) | |
| [ -n "$APK" ] || { echo 'APK not found' >&2; exit 1; } | |
| [ -n "$AAB" ] || { echo 'AAB not found' >&2; exit 1; } | |
| mv "$APK" "$OUT/apk/universal/release/Sable-${VERSION}-android-universal.apk" | |
| mv "$AAB" "$OUT/bundle/universalRelease/Sable-${VERSION}-android-universal.aab" | |
| - name: Attest Android bundles | |
| uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 | |
| with: | |
| subject-path: | | |
| src-tauri/gen/android/app/build/outputs/apk/universal/release/*.apk | |
| src-tauri/gen/android/app/build/outputs/bundle/universalRelease/*.aab | |
| - name: Attach Android bundles to release | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| OUT="src-tauri/gen/android/app/build/outputs" | |
| for f in "$OUT"/apk/universal/release/*.apk \ | |
| "$OUT"/bundle/universalRelease/*.aab; do | |
| [ -e "$f" ] || continue | |
| echo "Uploading $f" | |
| gh release upload "$TAG" "$f" --clobber | |
| done | |
| ios: | |
| name: Build iOS (AltStore/SideStore) | |
| needs: setup-release | |
| runs-on: macos-latest | |
| timeout-minutes: 60 | |
| permissions: | |
| contents: write | |
| id-token: write | |
| attestations: write | |
| artifact-metadata: write | |
| env: | |
| TAG: ${{ needs.setup-release.outputs.tag }} | |
| VERSION: ${{ needs.setup-release.outputs.version }} | |
| VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} | |
| SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} | |
| VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} | |
| VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} | |
| SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} | |
| SENTRY_ORG: ${{ secrets.SENTRY_ORG }} | |
| SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} | |
| CARGO_INCREMENTAL: 0 | |
| CARGO_TERM_COLOR: always | |
| NODE_OPTIONS: --max-old-space-size=8192 | |
| RUST_TARGETS: aarch64-apple-ios | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ needs.setup-release.outputs.ref }} | |
| persist-credentials: false | |
| - name: Setup app | |
| uses: ./.github/actions/setup | |
| with: | |
| tauri: 'true' | |
| - name: Add Rust targets | |
| shell: bash | |
| run: rustup target add $RUST_TARGETS | |
| - name: Cache Rust build | |
| uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 | |
| with: | |
| workspaces: src-tauri | |
| - name: Stamp release version into tauri.conf.json | |
| shell: bash | |
| run: node .github/scripts/set-tauri-version.mjs "$VERSION" | |
| - name: Set up iOS | |
| shell: bash | |
| run: mise -y run tauri:setup:ios | |
| - name: Symlink icons to the ios folder | |
| shell: bash | |
| run: mise run icons:symlink --write --force | |
| - name: Build unsigned IPA | |
| id: ipa | |
| shell: bash | |
| env: | |
| IS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} | |
| run: | | |
| if [ "$IS_NIGHTLY" = "true" ]; then | |
| export SABLE_BUILD_FLAVOR=dev | |
| fi | |
| pnpm tauri ios build --no-sign --ci | |
| IPA=$(find src-tauri/gen/apple/build -name '*.ipa' -type f | head -1) | |
| [ -n "$IPA" ] || { echo "IPA not found"; ls -R src-tauri/gen/apple/build 2>/dev/null || true; exit 1; } | |
| NORMALIZED_IPA="$(dirname "$IPA")/Sable-${VERSION}-ios-arm64.ipa" | |
| [ "$IPA" = "$NORMALIZED_IPA" ] || mv "$IPA" "$NORMALIZED_IPA" | |
| IPA="$NORMALIZED_IPA" | |
| unzip -p "$IPA" 'Payload/*.app/Info.plist' >Info.plist | |
| # version - CFBundleShortVersionString, buildVersion - CFBundleVersion : https://faq.altstore.io/developers/make-a-source#app-versions | |
| IPA_VERSION="$(plutil -extract CFBundleShortVersionString raw -o - Info.plist 2>/dev/null || true)" | |
| IPA_BUILD="$(plutil -extract CFBundleVersion raw -o - Info.plist 2>/dev/null || true)" | |
| echo "size=$(stat -f%z "$IPA")" >> "$GITHUB_OUTPUT" | |
| echo "path=$IPA" >> "$GITHUB_OUTPUT" | |
| echo "name=$(basename "$IPA")" >> "$GITHUB_OUTPUT" | |
| echo "ipa_version=${IPA_VERSION:-$VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "ipa_build=${IPA_BUILD:-$VERSION}" >> "$GITHUB_OUTPUT" | |
| - name: Attest iOS bundle | |
| uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 | |
| with: | |
| subject-path: ${{ steps.ipa.outputs.path }} | |
| - name: Attach IPA to release | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| STEPS_IPA_OUTPUTS_PATH: ${{ steps.ipa.outputs.path }} | |
| run: gh release upload "$TAG" "${STEPS_IPA_OUTPUTS_PATH}" --clobber | |
| - name: Update AltStore/SideStore source | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| REPO: ${{ github.repository }} | |
| ALTSTORE_VERSION: ${{ steps.ipa.outputs.ipa_version }} | |
| ALTSTORE_VERSION_BUILD: ${{ steps.ipa.outputs.ipa_build }} | |
| STEPS_IPA_OUTPUTS_NAME: ${{ steps.ipa.outputs.name }} | |
| STEPS_IPA_OUTPUTS_SIZE: ${{ steps.ipa.outputs.size }} | |
| run: | | |
| DOWNLOAD_URL="https://github.com/$REPO/releases/download/$TAG/${STEPS_IPA_OUTPUTS_NAME}" | |
| DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" | |
| if gh release download "$TAG" --pattern 'altstore-source.json' --dir . --clobber 2>/dev/null; then | |
| echo "Using existing altstore-source.json from release $TAG" | |
| else | |
| echo "Using repo altstore-source.json" | |
| fi | |
| node .github/scripts/update-altstore-source.mjs \ | |
| altstore-source.json "${ALTSTORE_VERSION:-$VERSION}" "${ALTSTORE_VERSION_BUILD:-$VERSION}" "${STEPS_IPA_OUTPUTS_SIZE}" "$DOWNLOAD_URL" "$DATE" "Sable $VERSION" | |
| gh release upload "$TAG" altstore-source.json --clobber | |
| updater-manifest: | |
| name: Publish updater manifest | |
| needs: [setup-release, build] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: write | |
| id-token: write | |
| attestations: write | |
| artifact-metadata: write | |
| env: | |
| TAG: ${{ needs.setup-release.outputs.tag }} | |
| VERSION: ${{ needs.setup-release.outputs.version }} | |
| VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} | |
| SABLE_BUILD_FLAVOR: ${{ needs.setup-release.outputs.nightly == 'true' && 'dev' || 'stable' }} | |
| SABLE_PRODUCT_NAME: ${{ needs.setup-release.outputs.product_name }} | |
| REPO: ${{ github.repository }} | |
| STARTED_AT: ${{ needs.setup-release.outputs.started_at }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| ref: ${{ needs.setup-release.outputs.ref }} | |
| persist-credentials: false | |
| - name: Build updater manifest | |
| id: manifest | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| NEEDS_SETUP_RELEASE_OUTPUTS_NIGHTLY: ${{ needs.setup-release.outputs.nightly }} | |
| run: | | |
| if [ "${NEEDS_SETUP_RELEASE_OUTPUTS_NIGHTLY}" = "true" ]; then | |
| gh release view "$TAG" --json assets \ | |
| | jq -r --arg cutoff "$STARTED_AT" \ | |
| '.assets[] | select(.updatedAt <= $cutoff and (.name | endswith(".sig"))) | .name' \ | |
| | while read -r name; do | |
| [ -n "$name" ] || continue | |
| echo "Removing superseded nightly signature: $name" | |
| gh release delete-asset "$TAG" "$name" --yes | |
| done | |
| fi | |
| node .github/scripts/build-updater-manifest.mjs | |
| if [ -f latest.json ]; then | |
| echo "produced=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "produced=false" >> "$GITHUB_OUTPUT" | |
| echo "No updater manifest produced (no signed artifacts); skipping attestation and upload." | |
| fi | |
| - name: Attest updater manifest | |
| if: steps.manifest.outputs.produced == 'true' | |
| uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 | |
| with: | |
| subject-path: latest.json | |
| - name: Upload updater manifest | |
| if: steps.manifest.outputs.produced == 'true' | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: gh release upload "$TAG" latest.json --clobber | |
| - name: Finalize nightly release | |
| if: ${{ needs.setup-release.outputs.nightly == 'true' && steps.manifest.outputs.produced == 'true' }} | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GIT_SHA: ${{ needs.setup-release.outputs.ref }} | |
| run: | | |
| gh api --method PATCH "repos/$REPO/git/refs/tags/nightly" \ | |
| -f sha="$GIT_SHA" -F force=true | |
| gh release edit nightly \ | |
| --prerelease \ | |
| --target "$GIT_SHA" \ | |
| --title "Nightly (dev)" \ | |
| --notes "Rolling nightly build $VERSION from dev — $GIT_SHA" | |
| # .json manifests are overwritten in place, not accumulated. | |
| gh release view nightly --json assets \ | |
| | jq -r --arg cutoff "$STARTED_AT" \ | |
| '.assets[] | select(.updatedAt <= $cutoff and (.name | endswith(".json") | not)) | .name' \ | |
| | while read -r name; do | |
| [ -n "$name" ] || continue | |
| echo "Removing superseded nightly asset: $name" | |
| gh release delete-asset nightly "$name" --yes | |
| done |