feat(local-ui): Snapshots gallery + fix snapshot capture race #59
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 | |
| packages: write | |
| jobs: | |
| build: | |
| name: Build ${{ matrix.target }} | |
| # Binary matrix runs only on tag push — manual dispatch is for | |
| # docker-publish smoke tests and shouldn't create GitHub releases. | |
| if: github.event_name == 'push' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: x86_64-unknown-linux-gnu | |
| os: ubuntu-latest | |
| platform: linux | |
| arch: x86_64 | |
| ext: tar.gz | |
| - target: x86_64-pc-windows-msvc | |
| os: windows-latest | |
| platform: windows | |
| arch: x86_64 | |
| ext: zip | |
| - target: x86_64-apple-darwin | |
| os: macos-latest | |
| platform: macos | |
| arch: x86_64 | |
| ext: tar.gz | |
| - target: aarch64-apple-darwin | |
| os: macos-latest | |
| platform: macos | |
| arch: aarch64 | |
| ext: tar.gz | |
| # Native aarch64 Linux runner avoids the `cross` tool entirely. | |
| # Previous attempts with `cross` failed on the rusqlite bundled | |
| # SQLite compile because the default docker image lacked the | |
| # necessary aarch64 C toolchain pieces. GitHub's `ubuntu-24.04-arm` | |
| # runner (free for public repos) builds natively — faster, simpler, | |
| # and rusqlite/aes-gcm/sha2/etc. compile against the system gcc | |
| # without any cross-toolchain configuration. | |
| - target: aarch64-unknown-linux-gnu | |
| os: ubuntu-24.04-arm | |
| platform: linux | |
| arch: aarch64 | |
| ext: tar.gz | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| # ── Build the embedded local web UI (Phase C, 2026-05-09) ─────── | |
| # rust-embed picks up `web-dist/` at compile time — without this | |
| # step the binary would ship with build.rs's "Web UI not built" | |
| # placeholder and the browser dashboard would 503 in production. | |
| # Node 20 LTS is the minimum the Vite 5.4 toolchain supports. | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: "npm" | |
| cache-dependency-path: web/package-lock.json | |
| - name: Install web/ deps | |
| run: npm ci | |
| working-directory: web | |
| - name: Build web/ → web-dist/ | |
| run: npm run build | |
| working-directory: web | |
| - name: Build release binary | |
| run: cargo build --release --target ${{ matrix.target }} | |
| shell: bash | |
| - name: Package (Unix) | |
| if: matrix.platform != 'windows' | |
| run: | | |
| cd target/${{ matrix.target }}/release | |
| tar czf ../../../sourcebox-sentry-cloudnode-${{ matrix.platform }}-${{ matrix.arch }}.tar.gz sourcebox-sentry-cloudnode | |
| cd ../../.. | |
| - name: Package (Windows) | |
| if: matrix.platform == 'windows' | |
| shell: pwsh | |
| run: | | |
| cd target/${{ matrix.target }}/release | |
| Compress-Archive -Path sourcebox-sentry-cloudnode.exe -DestinationPath ../../../sourcebox-sentry-cloudnode-${{ matrix.platform }}-${{ matrix.arch }}.zip | |
| cd ../../.. | |
| # ── Build the Windows MSI ────────────────────────────────────── | |
| # Runs only on the Windows matrix entry, only after the binary | |
| # has compiled (cargo-wix consumes target/release/...exe). Has | |
| # two side effects worth knowing about: | |
| # | |
| # 1. Installs the WiX Toolset 3.x via the official MSI. WiX 4 is | |
| # out, but cargo-wix as of 0.3 still emits 3.x-compatible | |
| # XML, so we pin to 3.14 — the latest 3.x release. | |
| # 2. Installs the cargo-wix subcommand. ~1m of build time the | |
| # first time, cached after that. | |
| # | |
| # The MSI itself is unsigned. SmartScreen will warn the user on | |
| # first install; the README documents the "More info → Run anyway" | |
| # workaround. Code signing is deferred (see docs/LAUNCH_HANDOFF | |
| # in the Command Center repo). | |
| - name: Install WiX Toolset (Windows only) | |
| if: matrix.platform == 'windows' | |
| shell: pwsh | |
| run: | | |
| # WiX 3.14 — last 3.x release. cargo-wix 0.3 emits 3.x XML. | |
| $url = 'https://github.com/wixtoolset/wix3/releases/download/wix314rtm/wix314-binaries.zip' | |
| $zip = "$env:RUNNER_TEMP\wix314.zip" | |
| $dst = "$env:RUNNER_TEMP\wix" | |
| Invoke-WebRequest -Uri $url -OutFile $zip | |
| Expand-Archive -Path $zip -DestinationPath $dst -Force | |
| # Add to PATH for subsequent steps. | |
| echo $dst | Out-File -FilePath $env:GITHUB_PATH -Append -Encoding utf8 | |
| - name: Install cargo-wix (Windows only) | |
| if: matrix.platform == 'windows' | |
| run: cargo install cargo-wix --locked | |
| shell: pwsh | |
| - name: Build MSI (Windows only) | |
| if: matrix.platform == 'windows' | |
| shell: pwsh | |
| run: | | |
| # --no-build because the cargo build step above already | |
| # produced target\release\sourcebox-sentry-cloudnode.exe with the | |
| # right release-profile flags (LTO, strip). Letting cargo-wix | |
| # rebuild would also use a stripped exe but pays the cost twice. | |
| cargo wix --no-build --nocapture --output target/wix/ | |
| - name: Rename MSI to standard naming (Windows only) | |
| if: matrix.platform == 'windows' | |
| shell: pwsh | |
| run: | | |
| # cargo-wix names the MSI `sourcebox-sentry-cloudnode-<version>-<arch>.msi` | |
| # by default. Move it to the predictable name the install | |
| # script + landing page link to. We keep the version-stamped | |
| # name in the GitHub release URL by also uploading the | |
| # original; this rename produces the "always-latest" copy | |
| # the install scripts pull. | |
| $generated = Get-ChildItem target/wix -Filter '*.msi' | Select-Object -First 1 | |
| if (-not $generated) { throw "No MSI generated under target/wix/" } | |
| Copy-Item $generated.FullName -Destination "sourcebox-sentry-cloudnode-${{ matrix.platform }}-${{ matrix.arch }}.msi" | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sourcebox-sentry-cloudnode-${{ matrix.platform }}-${{ matrix.arch }} | |
| path: sourcebox-sentry-cloudnode-${{ matrix.platform }}-${{ matrix.arch }}.${{ matrix.ext }} | |
| # Separate upload for the MSI so the `release` job's | |
| # `download-artifact` step picks it up alongside the .zip. | |
| # Conditional on Windows matrix entry — others would have nothing | |
| # to upload. | |
| - name: Upload MSI artifact (Windows only) | |
| if: matrix.platform == 'windows' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sourcebox-sentry-cloudnode-${{ matrix.platform }}-${{ matrix.arch }}-msi | |
| path: sourcebox-sentry-cloudnode-${{ matrix.platform }}-${{ matrix.arch }}.msi | |
| release: | |
| name: Create Release | |
| # Same gating as the matrix — manual dispatch skips GitHub release creation. | |
| if: github.event_name == 'push' | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| generate_release_notes: true | |
| files: artifacts/**/* | |
| # Mark hyphen-suffixed tags (v0.1.18-msi-rc3, v0.1.19-beta, etc.) | |
| # as prereleases so the GitHub `/releases/latest/download/...` | |
| # redirect — used by the landing page MSI link, the docs | |
| # download buttons, and any curl-style consumers — keeps | |
| # pointing at the most recent *stable* release. A bare | |
| # `vX.Y.Z` tag has no hyphen and | |
| # ships as the default-latest; any hyphen suffix is per | |
| # SemVer prerelease territory and we honour that. Without | |
| # this gate, an RC tag temporarily bumps GitHub's "latest" | |
| # pointer and the public install URLs serve the unverified | |
| # RC binary to anyone who curls it. | |
| prerelease: ${{ contains(github.ref_name, '-') }} | |
| docker-publish: | |
| name: Publish Docker image (multi-arch → GHCR) | |
| # Runs on both tag push and manual dispatch. Independent of the binary | |
| # matrix — rebuilds from source inside Docker, so no dependency on | |
| # `build` and can run in parallel. | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # QEMU is only used for cross-arch emulation (arm64 on x86 runner). | |
| # Rust compilation for arm64 under QEMU adds ~10 min vs native; still | |
| # well under GitHub's 6-hour job limit and acceptable for release cadence. | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # metadata-action auto-lowercases the image name, so | |
| # `SourceBox-LLC/OpenSentry-CloudNode` → `sourcebox-llc/opensentry-cloudnode`. | |
| # The GitHub repo URL is intentionally still `OpenSentry-CloudNode` | |
| # (rename deferred per the brand-transition note in MEMORY.md); | |
| # the binary inside is `sourcebox-sentry-cloudnode` per Cargo.toml. | |
| # `latest=auto` tags :latest only on stable semver tag pushes. | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ github.repository }} | |
| tags: | | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=sha,prefix=sha- | |
| flavor: | | |
| latest=auto | |
| - name: Build and push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| pull: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| # GHA cache disabled for this release to flush stale layers | |
| # from the rust:1.75-alpine era. Re-enable after a clean | |
| # build populates fresh cache: | |
| # cache-from: type=gha | |
| # cache-to: type=gha,mode=max | |
| no-cache: true |