# Release builds on native runners — one per platform, no cross-compilation # (nokhwa's camera backends want each platform's own SDK; see NEXT-STEPS judgment). # # Manually triggered (Actions → Release → Run workflow) against an existing tag # until a run has been validated end-to-end; then this can move to a tag trigger. # Android is built locally via scripts/android.sh for now — the gradle `ci` # flavor expects maven credentials this repository does not carry. name: Release on: # macOS builds on its native runner automatically when a release is published # (the macOS job has no dispatch-only gate). Linux/Windows stay dispatch-only — # they are built locally and uploaded with the release; run the workflow by hand # to (re)build those on runners against an existing tag. release: types: [published] workflow_dispatch: inputs: tag: description: "Existing release tag to build and upload artifacts to (e.g. build27)" required: true permissions: contents: write env: TAG: ${{ inputs.tag || github.event.release.tag_name }} jobs: linux: name: Linux x86_64 runs-on: ubuntu-latest # Built locally and uploaded with the release; only run on manual dispatch. if: github.event_name == 'workflow_dispatch' steps: - uses: actions/checkout@v6 with: ref: ${{ inputs.tag || github.event.release.tag_name }} submodules: recursive - uses: ./.github/actions/fetch-nip44 - name: Build shell: bash run: GOBLIN_BUILD="${TAG#build}" cargo build --release - name: Package run: | tar -C target/release -czf "goblin-$TAG-linux-x86_64.tar.gz" goblin sha256sum "goblin-$TAG-linux-x86_64.tar.gz" > "goblin-$TAG-linux-x86_64-sha256sum.txt" - uses: softprops/action-gh-release@v2 with: tag_name: ${{ inputs.tag || github.event.release.tag_name }} files: | goblin-${{ inputs.tag || github.event.release.tag_name }}-linux-x86_64.tar.gz goblin-${{ inputs.tag || github.event.release.tag_name }}-linux-x86_64-sha256sum.txt windows: name: Windows x86_64 (MSVC) runs-on: windows-latest # Built locally and uploaded with the release; only run on manual dispatch. if: github.event_name == 'workflow_dispatch' steps: - uses: actions/checkout@v6 with: ref: ${{ inputs.tag || github.event.release.tag_name }} submodules: recursive - uses: ./.github/actions/fetch-nip44 - name: Build shell: bash run: GOBLIN_BUILD="${TAG#build}" cargo build --release - name: Build MSI installer (cargo-wix / WiX 3 — same packaging as GRIM) shell: pwsh run: | # The .msi is built from wix/main.wxs (the cargo-wix default template: # WixUI_Minimal + launch-after-install), so `cargo wix` wires up the # WixUI/WixUtil extensions, cultures and CargoTargetBinDir for us. The # installer + shortcuts + Add/Remove-Programs entry carry wix/Product.ico # (the yellow Goblin icon). --no-build reuses the release exe above so the # embedded GOBLIN_BUILD number is preserved. cargo install cargo-wix --locked $wix = Get-ChildItem 'C:\Program Files (x86)' -Directory -Filter 'WiX Toolset v3*' -ErrorAction SilentlyContinue | Select-Object -Last 1 if (-not $wix) { choco install wixtoolset --no-progress -y | Out-Null $wix = Get-ChildItem 'C:\Program Files (x86)' -Directory -Filter 'WiX Toolset v3*' | Select-Object -Last 1 } $env:WIX = "$($wix.FullName)\" $env:PATH = "$($wix.FullName)\bin;$env:PATH" $msi = "goblin-$env:TAG-win-x86_64.msi" cargo wix --no-build --nocapture -o "$msi" if ($LASTEXITCODE -ne 0 -or -not (Test-Path "$msi")) { throw "cargo wix failed to produce $msi" } (Get-FileHash "$msi" -Algorithm SHA256).Hash.ToLower() + " $msi" | Out-File -Encoding ascii "goblin-$env:TAG-win-x86_64-msi-sha256sum.txt" - name: Package portable zip shell: bash run: | 7z a "goblin-$TAG-win-x86_64.zip" ./target/release/goblin.exe sha256sum "goblin-$TAG-win-x86_64.zip" > "goblin-$TAG-win-x86_64-sha256sum.txt" - uses: softprops/action-gh-release@v2 with: tag_name: ${{ inputs.tag || github.event.release.tag_name }} files: | goblin-${{ inputs.tag || github.event.release.tag_name }}-win-x86_64.msi goblin-${{ inputs.tag || github.event.release.tag_name }}-win-x86_64-msi-sha256sum.txt goblin-${{ inputs.tag || github.event.release.tag_name }}-win-x86_64.zip goblin-${{ inputs.tag || github.event.release.tag_name }}-win-x86_64-sha256sum.txt macos: name: macOS universal runs-on: macos-latest steps: - uses: actions/checkout@v6 with: ref: ${{ inputs.tag || github.event.release.tag_name }} submodules: recursive - uses: ./.github/actions/fetch-nip44 - name: Build both architectures run: | export GOBLIN_BUILD="${TAG#build}" rustup target add aarch64-apple-darwin x86_64-apple-darwin cargo build --release --target aarch64-apple-darwin cargo build --release --target x86_64-apple-darwin - name: Universal binary into Goblin.app bundle run: | # Combine both arches into one universal Mach-O and drop it into the # app bundle's executable slot (CFBundleExecutable=goblin). lipo -create -output goblin \ target/aarch64-apple-darwin/release/goblin \ target/x86_64-apple-darwin/release/goblin cp goblin macos/Goblin.app/Contents/MacOS/goblin chmod +x macos/Goblin.app/Contents/MacOS/goblin # Drop the placeholder that kept the empty dir tracked in git. rm -f macos/Goblin.app/Contents/MacOS/.gitignore # Ad-hoc sign (no Apple cert in CI). REQUIRED on Apple Silicon: lipo # strips the per-arch signatures cargo/ld add, and an unsigned arm64 # Mach-O is killed by the OS. Ad-hoc gives a valid (if unidentified) # signature; users still right-click → Open past Gatekeeper. codesign --force --sign - macos/Goblin.app/Contents/MacOS/goblin codesign --force --sign - macos/Goblin.app # ditto is the macOS-correct way to zip an .app (preserves the bundle # layout, symlinks and permissions; plain `zip` mangles bundles). ditto -c -k --keepParent macos/Goblin.app "goblin-$TAG-macos-universal.zip" shasum -a 256 "goblin-$TAG-macos-universal.zip" > "goblin-$TAG-macos-universal-sha256sum.txt" - uses: softprops/action-gh-release@v2 with: tag_name: ${{ inputs.tag || github.event.release.tag_name }} files: | goblin-${{ inputs.tag || github.event.release.tag_name }}-macos-universal.zip goblin-${{ inputs.tag || github.event.release.tag_name }}-macos-universal-sha256sum.txt