Release v0.32.0 #90
Workflow file for this run
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: Release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| permissions: | |
| contents: write | |
| env: | |
| CARGO_TERM_COLOR: always | |
| BIN_NAME: bowecho | |
| jobs: | |
| create-release: | |
| if: startsWith(github.ref, 'refs/tags/') | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Create release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| notes_file="docs/releases/${GITHUB_REF_NAME}.md" | |
| if gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then | |
| echo "Release ${GITHUB_REF_NAME} already exists." | |
| if [ -f "$notes_file" ]; then | |
| gh release edit "${GITHUB_REF_NAME}" \ | |
| --repo "${GITHUB_REPOSITORY}" \ | |
| --notes-file "$notes_file" | |
| fi | |
| else | |
| release_args=( | |
| "${GITHUB_REF_NAME}" | |
| --repo "${GITHUB_REPOSITORY}" | |
| --title "BowEcho ${GITHUB_REF_NAME}" | |
| --draft | |
| ) | |
| if [ -f "$notes_file" ]; then | |
| release_args+=(--notes-file "$notes_file") | |
| else | |
| release_args+=(--generate-notes) | |
| fi | |
| gh release create "${release_args[@]}" | |
| fi | |
| build: | |
| needs: create-release | |
| if: ${{ always() && (needs.create-release.result == 'success' || needs.create-release.result == 'skipped') }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # update_asset bakes the exact release-asset name into the binary | |
| # (BOWECHO_UPDATE_ASSET) so the in-app updater can only download | |
| # the variant it is already running. Windows and macOS matrix | |
| # builds receive it; Linux and local builds without it keep the | |
| # browser-based update flow. | |
| - os: windows-latest | |
| artifact_name: bowecho-windows-x64 | |
| package: windows | |
| update_asset: bowecho-windows-x64.exe | |
| # AVX2 variant for ~2015+ CPUs: benched -21% on the dealiased | |
| # velocity raster and -30% on reflectivity vs baseline x86-64 | |
| # (KTLX 2013-05-20 Moore volume, pixel-identical output). | |
| - os: windows-latest | |
| artifact_name: bowecho-windows-x64-v3 | |
| package: windows | |
| rustflags: "-C target-cpu=x86-64-v3" | |
| update_asset: bowecho-windows-x64-v3.exe | |
| - os: windows-11-arm | |
| artifact_name: bowecho-windows-arm64 | |
| package: windows | |
| update_asset: bowecho-windows-arm64.exe | |
| - os: ubuntu-24.04 | |
| artifact_name: bowecho-linux-x64 | |
| package: linux | |
| - os: ubuntu-24.04-arm | |
| artifact_name: bowecho-linux-arm64 | |
| package: linux | |
| - os: macos-15-intel | |
| artifact_name: bowecho-macos-intel | |
| package: macos | |
| update_asset: bowecho-macos-intel.zip | |
| - os: macos-15 | |
| artifact_name: bowecho-macos-apple-silicon | |
| package: macos | |
| update_asset: bowecho-macos-apple-silicon.zip | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| - name: Install Linux desktop build dependencies | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| pkg-config \ | |
| libgtk-3-dev \ | |
| libgl1-mesa-dev \ | |
| libx11-dev \ | |
| libxi-dev \ | |
| libxkbcommon-dev | |
| - name: Use stable Rust | |
| run: | | |
| rustup toolchain install stable --profile minimal | |
| rustup default stable | |
| # Fat LTO links the whole app in one LLVM pass; the hosted Windows | |
| # runner's default commit limit is what OOM'd the v0.27.1 build and | |
| # forced the thin-LTO downgrade. A larger pagefile lets the linker | |
| # spill instead of dying, so shipped binaries keep full optimization. | |
| - name: Expand Windows pagefile for fat-LTO link | |
| if: runner.os == 'Windows' | |
| uses: al-cheb/configure-pagefile-action@v1.4 | |
| with: | |
| minimum-size: 16GB | |
| maximum-size: 32GB | |
| disk-root: "C:" | |
| - name: Build release binary | |
| env: | |
| RUSTFLAGS: ${{ matrix.rustflags }} | |
| # In-app updater self-identification (empty for Linux matrix | |
| # entries, which the code treats as "cannot self-update"). | |
| BOWECHO_UPDATE_ASSET: ${{ matrix.update_asset }} | |
| run: cargo build --release -p app_ui --bin bowecho | |
| - name: Detect Windows signing secrets | |
| id: windows_signing | |
| if: runner.os == 'Windows' | |
| shell: bash | |
| env: | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | |
| AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| AZURE_TS_ENDPOINT: ${{ secrets.AZURE_TS_ENDPOINT }} | |
| AZURE_TS_ACCOUNT: ${{ secrets.AZURE_TS_ACCOUNT }} | |
| AZURE_TS_PROFILE: ${{ secrets.AZURE_TS_PROFILE }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -n "$AZURE_TENANT_ID" \ | |
| && -n "$AZURE_CLIENT_ID" \ | |
| && -n "$AZURE_CLIENT_SECRET" \ | |
| && -n "$AZURE_TS_ENDPOINT" \ | |
| && -n "$AZURE_TS_ACCOUNT" \ | |
| && -n "$AZURE_TS_PROFILE" ]]; then | |
| echo "enabled=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "enabled=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # Signs bowecho.exe via Azure Trusted Signing when the secrets exist | |
| # (docs/SIGNING.md) — silently skipped until then, so adding the | |
| # secrets is the ONLY step needed to start shipping signed builds. | |
| - name: Sign Windows binary (Azure Trusted Signing) | |
| if: runner.os == 'Windows' && steps.windows_signing.outputs.enabled == 'true' | |
| env: | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | |
| AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| uses: azure/trusted-signing-action@v0 | |
| with: | |
| azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| endpoint: ${{ secrets.AZURE_TS_ENDPOINT }} | |
| trusted-signing-account-name: ${{ secrets.AZURE_TS_ACCOUNT }} | |
| certificate-profile-name: ${{ secrets.AZURE_TS_PROFILE }} | |
| files-folder: target/release | |
| files-folder-filter: exe | |
| file-digest: SHA256 | |
| timestamp-rfc3161: http://timestamp.acs.microsoft.com | |
| timestamp-digest: SHA256 | |
| # Never cache the signing tooling: GitHub's 10 GB cache limit evicts | |
| # the small SignCli entry while its siblings survive, and the action | |
| # then skips the install and fails with "Invoke-TrustedSigning is | |
| # not recognized" (hit v0.30.4 and v0.30.5; upstream issue). A fresh | |
| # install costs ~1 minute per release build and cannot be poisoned. | |
| cache-dependencies: false | |
| - name: Package Windows binary | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| New-Item -ItemType Directory -Force dist | Out-Null | |
| Copy-Item target\release\bowecho.exe "dist\${{ matrix.artifact_name }}.exe" | |
| if ("${{ matrix.artifact_name }}" -eq "bowecho-windows-x64") { | |
| New-Item -ItemType Directory -Force package | Out-Null | |
| Copy-Item target\release\bowecho.exe package\bowecho.exe | |
| Copy-Item README.md package\README.md | |
| Copy-Item LICENSE-MIT package\LICENSE-MIT | |
| Copy-Item LICENSE-APACHE package\LICENSE-APACHE | |
| Compress-Archive -Path package\* -DestinationPath "dist\${{ matrix.artifact_name }}.zip" -Force | |
| } | |
| - name: Package macOS app | |
| if: runner.os == 'macOS' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p dist | |
| app="BowEcho.app" | |
| mkdir -p "$app/Contents/MacOS" "$app/Contents/Resources" | |
| cp "target/release/${BIN_NAME}" "$app/Contents/MacOS/${BIN_NAME}" | |
| cp README.md "$app/Contents/Resources/README.md" | |
| cp LICENSE-MIT "$app/Contents/Resources/LICENSE-MIT" | |
| cp LICENSE-APACHE "$app/Contents/Resources/LICENSE-APACHE" | |
| chmod +x "$app/Contents/MacOS/${BIN_NAME}" | |
| if [[ "${GITHUB_REF_NAME:-}" =~ ^v[0-9] ]]; then | |
| version="${GITHUB_REF_NAME#v}" | |
| else | |
| version="$(awk -F '"' '/^version =/ { print $2; exit }' Cargo.toml)" | |
| fi | |
| cat > "$app/Contents/Info.plist" <<PLIST | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| <plist version="1.0"> | |
| <dict> | |
| <key>CFBundleDevelopmentRegion</key> | |
| <string>en</string> | |
| <key>CFBundleDisplayName</key> | |
| <string>BowEcho</string> | |
| <key>CFBundleExecutable</key> | |
| <string>bowecho</string> | |
| <key>CFBundleIdentifier</key> | |
| <string>research.fahrenheit.bowecho</string> | |
| <key>CFBundleInfoDictionaryVersion</key> | |
| <string>6.0</string> | |
| <key>CFBundleName</key> | |
| <string>BowEcho</string> | |
| <key>CFBundlePackageType</key> | |
| <string>APPL</string> | |
| <key>CFBundleShortVersionString</key> | |
| <string>${version}</string> | |
| <key>CFBundleVersion</key> | |
| <string>${version}</string> | |
| <key>LSMinimumSystemVersion</key> | |
| <string>12.0</string> | |
| <key>NSHighResolutionCapable</key> | |
| <true/> | |
| </dict> | |
| </plist> | |
| PLIST | |
| - name: Detect macOS signing secrets | |
| id: macos_signing | |
| if: runner.os == 'macOS' | |
| shell: bash | |
| env: | |
| MACOS_CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE_BASE64 }} | |
| MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }} | |
| ASC_API_KEY_BASE64: ${{ secrets.ASC_API_KEY_BASE64 }} | |
| ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} | |
| ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -n "$MACOS_CERTIFICATE_BASE64" \ | |
| && -n "$MACOS_CERTIFICATE_PWD" \ | |
| && -n "$ASC_API_KEY_BASE64" \ | |
| && -n "$ASC_KEY_ID" \ | |
| && -n "$ASC_ISSUER_ID" ]]; then | |
| echo "enabled=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "enabled=false" >> "$GITHUB_OUTPUT" | |
| if [[ "${GITHUB_REF:-}" == refs/tags/* ]]; then | |
| echo "::error::Tagged macOS releases require every Developer ID and notarization secret (see docs/SIGNING.md)." | |
| exit 1 | |
| fi | |
| fi | |
| # Signs + notarizes BowEcho.app with the repo's Developer ID | |
| # certificate and App Store Connect API key (docs/SIGNING.md). Tagged | |
| # release jobs cannot reach this point without every required secret. | |
| - name: Sign and notarize macOS app | |
| if: runner.os == 'macOS' && steps.macos_signing.outputs.enabled == 'true' | |
| shell: bash | |
| env: | |
| MACOS_CERTIFICATE_BASE64: ${{ secrets.MACOS_CERTIFICATE_BASE64 }} | |
| MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }} | |
| ASC_API_KEY_BASE64: ${{ secrets.ASC_API_KEY_BASE64 }} | |
| ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} | |
| ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }} | |
| run: | | |
| set -euo pipefail | |
| app="BowEcho.app" | |
| keychain="$RUNNER_TEMP/signing.keychain-db" | |
| keychain_password="$(openssl rand -base64 24)" | |
| security create-keychain -p "$keychain_password" "$keychain" | |
| security set-keychain-settings -lut 1800 "$keychain" | |
| security unlock-keychain -p "$keychain_password" "$keychain" | |
| echo "$MACOS_CERTIFICATE_BASE64" | base64 --decode > "$RUNNER_TEMP/cert.p12" | |
| security import "$RUNNER_TEMP/cert.p12" -k "$keychain" \ | |
| -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign | |
| security set-key-partition-list -S apple-tool:,apple: \ | |
| -k "$keychain_password" "$keychain" > /dev/null | |
| security list-keychains -d user -s "$keychain" login.keychain-db | |
| identity="$(security find-identity -v -p codesigning "$keychain" \ | |
| | awk -F '"' '/Developer ID Application/ {print $2; exit}')" | |
| if [ -z "$identity" ]; then | |
| echo "No Developer ID Application identity found in certificate" >&2 | |
| exit 1 | |
| fi | |
| echo "Signing with: $identity" | |
| codesign --force --deep --options runtime --timestamp \ | |
| --sign "$identity" "$app" | |
| codesign --verify --deep --strict "$app" | |
| echo "$ASC_API_KEY_BASE64" | base64 --decode > "$RUNNER_TEMP/AuthKey.p8" | |
| ditto -c -k --sequesterRsrc --keepParent "$app" "$RUNNER_TEMP/notarize.zip" | |
| xcrun notarytool submit "$RUNNER_TEMP/notarize.zip" \ | |
| --key "$RUNNER_TEMP/AuthKey.p8" --key-id "$ASC_KEY_ID" \ | |
| --issuer "$ASC_ISSUER_ID" --wait | |
| xcrun stapler staple "$app" | |
| security delete-keychain "$keychain" | |
| - name: Validate signed macOS app | |
| if: runner.os == 'macOS' && steps.macos_signing.outputs.enabled == 'true' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| app="BowEcho.app" | |
| codesign --verify --deep --strict --verbose=2 "$app" | |
| xcrun stapler validate "$app" | |
| spctl --assess --type execute --verbose=4 "$app" | |
| - name: Zip macOS app | |
| if: runner.os == 'macOS' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| ditto -c -k --sequesterRsrc --keepParent "BowEcho.app" "dist/${{ matrix.artifact_name }}.zip" | |
| - name: Package Linux binary | |
| if: runner.os == 'Linux' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "dist" | |
| cp "target/release/${BIN_NAME}" "dist/${{ matrix.artifact_name }}" | |
| chmod +x "dist/${{ matrix.artifact_name }}" | |
| - name: Upload workflow artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.artifact_name }} | |
| path: dist/* | |
| if-no-files-found: error | |
| - name: Generate SHA-256 checksums | |
| shell: bash | |
| # Published beside every asset so users can verify a download is the | |
| # CI-built binary (antivirus ML heuristics flag unsigned Rust exes; | |
| # a hash check against the release page settles provenance). | |
| run: | | |
| set -euo pipefail | |
| cd dist | |
| for f in *; do | |
| sha256sum "$f" > "$f.sha256" | |
| done | |
| - name: Upload release asset | |
| if: startsWith(github.ref, 'refs/tags/') | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: gh release upload "${GITHUB_REF_NAME}" dist/* --clobber --repo "${GITHUB_REPOSITORY}" | |
| publish-release: | |
| needs: build | |
| if: startsWith(github.ref, 'refs/tags/') && needs.build.result == 'success' | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - name: Publish release after all assets upload | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: gh release edit "${GITHUB_REF_NAME}" --draft=false --repo "${GITHUB_REPOSITORY}" |