fix: discover DMG by find rather than hard-coding tag-derived filename #2
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[0-9]+.[0-9]+.[0-9]+*' # v0.2.0, v1.0.0, v0.2.0-rc.1, … | |
| # ── Platform note ───────────────────────────────────────────────────────────── | |
| # macOS Apple Silicon ONLY. Lumen is a macOS-specific product (system tray, | |
| # ~/.claude/ integration, Tauri native APIs). Windows/Linux matrix is a separate | |
| # port effort — do NOT add runners here until there are working artifacts. | |
| env: | |
| PNPM_VERSION: '10' | |
| NODE_VERSION: '22' | |
| TRIPLE: 'aarch64-apple-darwin' | |
| jobs: | |
| # ════════════════════════════════════════════════════════════════════════════ | |
| # Job 1: Build both artifacts on Apple Silicon | |
| # ════════════════════════════════════════════════════════════════════════════ | |
| build: | |
| name: Build (macOS Apple Silicon) | |
| runs-on: macos-14 # M1 runner — aarch64 native | |
| permissions: | |
| contents: write # required to create a GitHub Release | |
| outputs: | |
| version: ${{ steps.meta.outputs.version }} | |
| dmg_name: ${{ steps.checksums.outputs.dmg_name }} | |
| tarball_name: ${{ steps.meta.outputs.tarball_name }} | |
| dmg_sha256: ${{ steps.checksums.outputs.dmg_sha256 }} | |
| tarball_sha256: ${{ steps.checksums.outputs.tarball_sha256 }} | |
| steps: | |
| # ── 1. Checkout ───────────────────────────────────────────────────────── | |
| - uses: actions/checkout@v4 | |
| # ── 2. Version metadata ───────────────────────────────────────────────── | |
| - name: Extract version from tag | |
| id: meta | |
| run: | | |
| VERSION="${GITHUB_REF_NAME#v}" # strip leading 'v' | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "dmg_name=Lumen_${VERSION}_aarch64.dmg" >> "$GITHUB_OUTPUT" | |
| echo "tarball_name=lumen-v${VERSION}-aarch64-apple-darwin.tar.gz" >> "$GITHUB_OUTPUT" | |
| # ── 3. Node + pnpm ────────────────────────────────────────────────────── | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: ${{ env.PNPM_VERSION }} | |
| # ── 4. Rust toolchain ─────────────────────────────────────────────────── | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ env.TRIPLE }} | |
| - uses: swatinem/rust-cache@v2 | |
| with: | |
| workspaces: ". -> target" | |
| cache-on-failure: true | |
| # ── 5. Install frontend dependencies ──────────────────────────────────── | |
| - name: pnpm install | |
| working-directory: lumenator | |
| run: pnpm install --frozen-lockfile | |
| # ── 6. Build sidecars (MUST run before tauri build) ───────────────────── | |
| # lumen-daemon, lumen-mcp, lumen-tok, and the lumen CLI binary are built | |
| # here and staged into lumenator/src-tauri/binaries/ with the target-triple | |
| # suffix that Tauri's sidecar mechanism requires. | |
| - name: Build sidecars | |
| working-directory: lumenator | |
| run: bash ./build-sidecar.sh | |
| # ── 7. Build CLI tar.gz ───────────────────────────────────────────────── | |
| # lumen-cli is also a sidecar (built above) but we ship it separately as | |
| # a standalone tarball for the Homebrew formula. | |
| - name: Package CLI tarball | |
| id: cli | |
| run: | | |
| VERSION="${{ steps.meta.outputs.version }}" | |
| TARBALL="${{ steps.meta.outputs.tarball_name }}" | |
| mkdir -p dist | |
| cp target/release/lumen dist/lumen | |
| tar -czf "dist/${TARBALL}" -C dist lumen | |
| rm dist/lumen | |
| echo "tarball=dist/${TARBALL}" >> "$GITHUB_OUTPUT" | |
| # ── 8. Build the Tauri app (.dmg) ─────────────────────────────────────── | |
| # NO SIGNING — unsigned build, users run xattr or install via cask. | |
| # | |
| # ┌─────────────────────────────────────────────────────────────────────┐ | |
| # │ SIGNING PLACEHOLDER — slot in notarization here when ready │ | |
| # │ │ | |
| # │ Required secrets (set in repo Settings → Secrets): │ | |
| # │ APPLE_CERTIFICATE base64-encoded .p12 Developer ID cert │ | |
| # │ APPLE_CERTIFICATE_PASSWORD passphrase for the .p12 │ | |
| # │ APPLE_ID AppleID email for notarytool │ | |
| # │ APPLE_TEAM_ID 10-char Team ID │ | |
| # │ APPLE_ID_PASSWORD app-specific password for notarytool │ | |
| # │ │ | |
| # │ Tauri signing env vars to add when implementing: │ | |
| # │ APPLE_SIGNING_IDENTITY, APPLE_ID, APPLE_PASSWORD, APPLE_TEAM_ID │ | |
| # │ See: https://tauri.app/distribute/sign/macos/ │ | |
| # └─────────────────────────────────────────────────────────────────────┘ | |
| - name: Build Tauri app | |
| working-directory: lumenator | |
| run: pnpm tauri build | |
| env: | |
| # Disable Tauri's code-sign attempt for unsigned builds | |
| CI: true | |
| # ── 9. Compute sha256 checksums ───────────────────────────────────────── | |
| - name: Compute checksums | |
| id: checksums | |
| run: | | |
| # Tauri names the DMG from tauri.conf.json version, not the git tag. | |
| # Discover the actual file rather than constructing the path from the tag. | |
| DMG=$(find target/release/bundle/dmg -name "Lumen_*_aarch64.dmg" | head -1) | |
| [[ -z "$DMG" ]] && { echo "ERROR: DMG not found in target/release/bundle/dmg"; exit 1; } | |
| DMG_FILENAME=$(basename "$DMG") | |
| TARBALL="${{ steps.cli.outputs.tarball }}" | |
| DMG_SHA=$(shasum -a 256 "$DMG" | awk '{print $1}') | |
| TAR_SHA=$(shasum -a 256 "$TARBALL" | awk '{print $1}') | |
| echo "dmg_sha256=${DMG_SHA}" >> "$GITHUB_OUTPUT" | |
| echo "tarball_sha256=${TAR_SHA}" >> "$GITHUB_OUTPUT" | |
| echo "dmg_path=${DMG}" >> "$GITHUB_OUTPUT" | |
| echo "dmg_name=${DMG_FILENAME}" >> "$GITHUB_OUTPUT" | |
| echo "DMG: $DMG" | |
| echo "DMG sha256: $DMG_SHA" | |
| echo "Tarball sha256: $TAR_SHA" | |
| # ── 10. Create GitHub Release with both assets ────────────────────────── | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| VERSION="${{ steps.meta.outputs.version }}" | |
| TAG="${GITHUB_REF_NAME}" | |
| DMG_PATH="${{ steps.checksums.outputs.dmg_path }}" | |
| TARBALL="${{ steps.cli.outputs.tarball }}" | |
| DMG_NAME="${{ steps.checksums.outputs.dmg_name }}" | |
| TARBALL_NAME="${{ steps.meta.outputs.tarball_name }}" | |
| # Use CHANGELOG.md entry for this version as release notes, if present | |
| NOTES_OPT="" | |
| if [[ -f CHANGELOG.md ]]; then | |
| # Extract just this version's block (between its header and the next one) | |
| awk "/^## \[${VERSION}\]/{found=1; next} found && /^## \[/{exit} found" \ | |
| CHANGELOG.md > /tmp/release_notes.md | |
| [[ -s /tmp/release_notes.md ]] && NOTES_OPT="--notes-file /tmp/release_notes.md" | |
| fi | |
| if [[ -z "$NOTES_OPT" ]]; then | |
| if [[ -f RELEASE_NOTES.md ]]; then | |
| NOTES_OPT="--notes-file RELEASE_NOTES.md" | |
| else | |
| NOTES_OPT="--generate-notes" | |
| fi | |
| fi | |
| gh release create "$TAG" \ | |
| "${DMG_PATH}#${DMG_NAME}" \ | |
| "${TARBALL}#${TARBALL_NAME}" \ | |
| --title "Lumen ${TAG}" \ | |
| $NOTES_OPT | |
| # ════════════════════════════════════════════════════════════════════════════ | |
| # Job 2: Update the Homebrew tap (runs after build succeeds) | |
| # ════════════════════════════════════════════════════════════════════════════ | |
| update-tap: | |
| name: Update Homebrew tap | |
| needs: build | |
| runs-on: ubuntu-latest # no macOS toolchain needed for a git commit | |
| steps: | |
| - name: Checkout lumen (for formula templates) | |
| uses: actions/checkout@v4 | |
| # TAP_PUSH_TOKEN is a GitHub Personal Access Token with `repo` scope on | |
| # HackPoint/homebrew-tap. GITHUB_TOKEN is scoped to this repo and cannot | |
| # push to another repo. | |
| # | |
| # To create the secret: | |
| # 1. github.com → Settings → Developer settings → Personal access tokens (classic) | |
| # 2. New token: name "tap-push", scope = repo (or fine-grained: Contents write | |
| # on HackPoint/homebrew-tap) | |
| # 3. Copy the token value | |
| # 4. HackPoint/lumen → Settings → Secrets and variables → Actions → New secret | |
| # Name: TAP_PUSH_TOKEN Value: (paste token) | |
| - name: Checkout homebrew-tap | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: HackPoint/homebrew-tap | |
| token: ${{ secrets.TAP_PUSH_TOKEN }} | |
| path: homebrew-tap | |
| - name: Update formulae with new version + sha256 | |
| run: | | |
| VERSION="${{ needs.build.outputs.version }}" | |
| DMG_SHA="${{ needs.build.outputs.dmg_sha256 }}" | |
| TAR_SHA="${{ needs.build.outputs.tarball_sha256 }}" | |
| # ── Casks/lumen.rb ──────────────────────────────────────────────── | |
| CASK="homebrew-tap/Casks/lumen.rb" | |
| sed -i "s/^ version \"[^\"]*\"/ version \"${VERSION}\"/" "$CASK" | |
| sed -i "s/^ sha256 \"[^\"]*\"/ sha256 \"${DMG_SHA}\"/" "$CASK" | |
| echo "Cask updated:" | |
| grep -E 'version|sha256' "$CASK" | |
| # ── Formula/lumen-cli.rb ────────────────────────────────────────── | |
| FORMULA="homebrew-tap/Formula/lumen-cli.rb" | |
| sed -i "s/^ version \"[^\"]*\"/ version \"${VERSION}\"/" "$FORMULA" | |
| sed -i "s/^ sha256 \"[^\"]*\"/ sha256 \"${TAR_SHA}\"/" "$FORMULA" | |
| echo "Formula updated:" | |
| grep -E 'version|sha256' "$FORMULA" | |
| - name: Commit + push tap update | |
| working-directory: homebrew-tap | |
| run: | | |
| VERSION="${{ needs.build.outputs.version }}" | |
| git config user.name "HackPoint" | |
| git config user.email "6758579+HackPoint@users.noreply.github.com" | |
| git add Casks/lumen.rb Formula/lumen-cli.rb | |
| git diff --cached --quiet && echo "No tap changes needed." && exit 0 | |
| git commit -m "chore: bump lumen to v${VERSION}" | |
| git push |