style: rustfmt fixes in hooks.rs and main.rs #8
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*'] | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: Build ${{ matrix.target }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: x86_64-unknown-linux-musl | |
| os: ubuntu-latest | |
| cross: true | |
| - target: aarch64-unknown-linux-musl | |
| os: ubuntu-latest | |
| cross: true | |
| - target: aarch64-apple-darwin | |
| os: macos-14 | |
| cross: false | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| key: ${{ matrix.target }} | |
| # Workspace version defaults to whatever was last committed to | |
| # Cargo.toml, which drifts from the release tag if nobody remembers to | |
| # bump it by hand — stamp it from the tag so `calm --version` always | |
| # matches the artifact it shipped in. | |
| - name: Set crate version from tag | |
| # `-i.bak` (suffix glued to the flag, no space) is the one `sed -i` | |
| # spelling both GNU sed (Linux runners) and BSD sed (macOS runner) | |
| # accept the same way — a bare `-i` is GNU-only and errors on BSD. | |
| run: | | |
| sed -i.bak "s/^version = \".*\"/version = \"${GITHUB_REF_NAME#v}\"/" Cargo.toml | |
| rm -f Cargo.toml.bak | |
| # `cross` runs the build in a container that already has the C | |
| # cross-toolchain tree-sitter / stack-graphs / rusqlite need. | |
| - name: Install cross | |
| if: matrix.cross | |
| run: cargo install cross --locked | |
| - name: Build (cross) | |
| if: matrix.cross | |
| run: cross build --release --bin calm --target ${{ matrix.target }} | |
| - name: Build (native) | |
| if: ${{ !matrix.cross }} | |
| run: cargo build --release --bin calm --target ${{ matrix.target }} | |
| - name: Package | |
| run: | | |
| bin="target/${{ matrix.target }}/release/calm" | |
| tar -czf "calm-${{ matrix.target }}.tar.gz" -C "$(dirname "$bin")" calm | |
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: calm-${{ matrix.target }} | |
| path: calm-${{ matrix.target }}.tar.gz | |
| release: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| # Needed by attest-build-provenance below: `id-token` to mint the | |
| # Sigstore/Fulcio OIDC signing cert (same keyless mechanism the | |
| # `docker` job's cosign step already uses), `attestations` to publish | |
| # the resulting attestation to this repo's attestation store. | |
| id-token: write | |
| attestations: write | |
| steps: | |
| - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 | |
| with: | |
| path: dist | |
| merge-multiple: true | |
| - name: Generate checksums | |
| working-directory: dist | |
| run: sha256sum calm-*.tar.gz > SHA256SUMS | |
| # GitHub-native build provenance for the binary tarballs — previously | |
| # only SHA256-checksummed (integrity against transfer corruption, not | |
| # authenticity) while the `docker` job's container image alone got a | |
| # real cosign signature (identity/authenticity). This closes that gap | |
| # cheaply: no key management, verifiable by any user via `gh | |
| # attestation verify calm-<target>.tar.gz --owner ${{ github.repository_owner }}`. | |
| # Complements rather than replaces the `docker` job's cosign signing — | |
| # provenance attestation proves "built by this exact workflow run from | |
| # this commit", cosign proves "signed by this identity"; 2025 guidance | |
| # (github.blog/changelog, sigstore.dev) treats these as layered, not | |
| # redundant. Deliberately not extended to the container image too: | |
| # cosign's signature already has the widest adoption in that ecosystem | |
| # (Kubernetes admission controllers, etc.) and doubling up there isn't | |
| # this pass's scope. | |
| - name: Attest build provenance for release binaries | |
| uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 | |
| with: | |
| subject-path: "dist/calm-*.tar.gz" | |
| - name: Publish release | |
| uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 | |
| with: | |
| files: | | |
| dist/*.tar.gz | |
| dist/SHA256SUMS | |
| generate_release_notes: true | |
| docker: | |
| name: Publish container image | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| # Keyless cosign signing (below) provisions an ephemeral signing | |
| # cert from Sigstore's Fulcio via this OIDC token — no key material | |
| # to generate, store, or rotate. Ported from github/github-mcp-server's | |
| # own .github/workflows/docker-publish.yml (verified against that | |
| # real source, including its exact cosign-installer pin, before | |
| # porting) — the same pattern GitHub's own MCP server release | |
| # pipeline uses. | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Resolve image tag | |
| id: version | |
| run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" | |
| - name: Install cosign | |
| uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 | |
| with: | |
| cosign-release: "v2.2.4" | |
| - name: Build and push container image | |
| id: build-and-push | |
| uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 | |
| with: | |
| context: . | |
| file: Containerfile | |
| push: true | |
| tags: | | |
| ghcr.io/eilodon/calm-mcp:${{ steps.version.outputs.version }} | |
| ghcr.io/eilodon/calm-mcp:latest | |
| # Signs the image DIGEST (not the mutable tags) so the signature | |
| # stays valid regardless of tag reassignment, then attaches it to | |
| # every tag pointing at that digest — `xargs` over the same | |
| # newline-separated tags list `build-push-action` was given above. | |
| # Writes to the public Rekor transparency log (this repo/image is | |
| # public); see docker-publish.yml's own comment on that if this ever | |
| # needs to be a private image instead. | |
| - name: Sign the published container image | |
| env: | |
| TAGS: | | |
| ghcr.io/eilodon/calm-mcp:${{ steps.version.outputs.version }} | |
| ghcr.io/eilodon/calm-mcp:latest | |
| DIGEST: ${{ steps.build-and-push.outputs.digest }} | |
| run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} | |
| # Auto-publish the npm distribution once the GitHub Release for this tag | |
| # exists (stage-release.sh downloads its assets — hence `needs: release`). | |
| # This was deliberately manual until the first publish had been proven by | |
| # hand (npm/README.md); v0.1.4 cleared that bar, so it now runs in CI, | |
| # gated only on the NPM_TOKEN repo secret. Kept independent of build/docker | |
| # so a missing/expired token can't block the GitHub Release itself — this | |
| # job just fails visibly on 401 until the secret is (re)added. | |
| npm-publish: | |
| name: Publish npm packages | |
| needs: release | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| # GitHub masks secrets in logs; the token only lands in $HOME/.npmrc on | |
| # the ephemeral runner. ubuntu-latest already ships node + npm. | |
| - name: Authenticate to npm | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > "$HOME/.npmrc" | |
| # Downloads all 3 platform binaries from THIS tag's release + stamps | |
| # every package.json under npm/ (wrapper's optionalDependencies pins | |
| # included) to the tag version — fixing any wrapper/platform version | |
| # skew automatically at publish time. | |
| - name: Stage platform binaries + versions from this tag's release | |
| run: npm/stage-release.sh "${GITHUB_REF_NAME}" | |
| # Platform packages first so the wrapper's optionalDependencies are | |
| # already resolvable the instant anyone installs it (npm/README.md). | |
| - name: Publish platform packages, then the wrapper | |
| run: | | |
| for pkg in calm-mcp-linux-x64 calm-mcp-linux-arm64 calm-mcp-darwin-arm64 calm-mcp; do | |
| ( cd "npm/$pkg" && npm publish --access public ) | |
| done | |
| # Register the just-published version's metadata in the official MCP | |
| # Registry so cold clients (VS Code / Cursor / Claude Code registry search) | |
| # can discover CALM by name. Runs after npm-publish because the registry | |
| # validates that the npm package at this version already exists. Reuses the | |
| # standalone publish-mcp-registry.yml (still runnable by hand for re-pushes) | |
| # via workflow_call so the mcp-publisher logic lives in exactly one place. | |
| mcp-registry: | |
| name: Publish to MCP Registry | |
| needs: npm-publish | |
| permissions: | |
| id-token: write # OIDC auth to the registry — this repo's identity | |
| contents: read | |
| uses: ./.github/workflows/publish-mcp-registry.yml | |
| with: | |
| version: ${{ github.ref_name }} |