build(deps): bump the actions group with 3 updates #162
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
| # Identical in every *-le repo. The artifact name is derived from the repo | |
| # rather than hardcoded, so this file is byte-for-byte the same everywhere and | |
| # a change can be copied across without a per-repo edit. | |
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| # Least privilege: CI reads the repo and nothing else. | |
| permissions: | |
| contents: read | |
| # A newer push supersedes an in-flight run instead of both burning a 3-OS matrix. | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # A prose commit cannot behave differently on Windows, so repeating the whole | |
| # matrix for one is three runners answering a question with one answer. This | |
| # narrows the matrix rather than skipping anything: on a docs-only push every | |
| # check still runs, once, on the leg that carries the checks the other two do | |
| # not — the README coverage gate, the integration suite and the installed | |
| # VSIX end-to-end are all Linux-only already. | |
| # | |
| # "Docs" is deliberately narrow: `*.md` and `LICENSE`, nothing else. | |
| # Anything unrecognised counts as code, and an unreadable diff counts as | |
| # code — the default is always to run more, never less. | |
| # | |
| # Note what this is NOT: `GEMINI.md` is documentation by that rule and is | |
| # also read by `src/agent-files.test.ts`. That is fine precisely because | |
| # nothing is skipped. `.cursorrules` and the other non-`.md` mirrors count | |
| # as code and widen the matrix; a `.md` one narrows it, and the suite that | |
| # covers it still runs on the leg that remains. | |
| changes: | |
| name: What changed | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| code: ${{ steps.classify.outputs.code }} | |
| matrix: ${{ steps.classify.outputs.matrix }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - id: classify | |
| run: | | |
| set -euo pipefail | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| range="origin/${{ github.base_ref }}...HEAD" | |
| else | |
| before="${{ github.event.before }}" | |
| if [ -z "$before" ] || [ "$before" = "0000000000000000000000000000000000000000" ]; then | |
| before="$(git rev-list --max-parents=0 HEAD | tail -1)" | |
| fi | |
| range="$before..HEAD" | |
| fi | |
| # An unreadable range is not evidence of a docs-only push. Fall back | |
| # to the full matrix rather than quietly checking less. | |
| if ! files="$(git diff --name-only "$range")"; then files=""; fi | |
| code=false | |
| if [ -z "$files" ]; then code=true; fi | |
| while IFS= read -r file; do | |
| [ -z "$file" ] && continue | |
| case "$file" in | |
| *.md | LICENSE) ;; | |
| *) code=true; break ;; | |
| esac | |
| done <<EOF | |
| $files | |
| EOF | |
| if [ "$code" = "true" ]; then | |
| echo 'matrix=["ubuntu-latest","macos-latest","windows-latest"]' >> "$GITHUB_OUTPUT" | |
| else | |
| echo 'matrix=["ubuntu-latest"]' >> "$GITHUB_OUTPUT" | |
| fi | |
| echo "code=$code" >> "$GITHUB_OUTPUT" | |
| echo "code=$code, files changed:" | |
| echo "$files" | |
| commits: | |
| name: Commit messages | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| # The full history of the pushed range, so every new subject is checked | |
| # rather than only the tip. | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| # The local commit-msg hook is skippable with --no-verify; this is not. | |
| - name: Validate conventional commits | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| RANGE="origin/${{ github.base_ref }}..HEAD" | |
| else | |
| RANGE="${{ github.event.before }}..${{ github.sha }}" | |
| fi | |
| node scripts/commit-lint.js --range "$RANGE" | |
| ci: | |
| name: CI on ${{ matrix.os }} | |
| needs: changes | |
| runs-on: ${{ matrix.os }} | |
| # Integration tests drive a real extension host; without a cap a hung | |
| # vscode-test sits for the 6h runner default. | |
| timeout-minutes: 20 | |
| strategy: | |
| matrix: | |
| os: ${{ fromJSON(needs.changes.outputs.matrix) }} | |
| fail-fast: false | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version: 1.3.13 | |
| - name: Install dependencies | |
| run: bun install --frozen-lockfile | |
| # PR-only: shows what a dependency change actually pulls in, and fails on | |
| # a known-vulnerable addition — before Dependabot's auto-merge can act. | |
| - name: Dependency review | |
| if: github.event_name == 'pull_request' && runner.os == 'Linux' | |
| uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 | |
| with: | |
| fail-on-severity: high | |
| - name: Lint | |
| run: bun run lint | |
| - name: Typecheck | |
| run: bun run typecheck | |
| - name: Unit tests with coverage | |
| run: bun run test:coverage | |
| # The README's Testing section is generated from the coverage summary. | |
| # Failing here means the committed numbers no longer match a real run — | |
| # which is exactly how the pre-2.0 READMEs drifted into fiction. | |
| - name: README coverage section is current | |
| if: runner.os == 'Linux' | |
| run: bun run coverage:readme:check | |
| - name: Build bundle | |
| run: bun run build | |
| - name: Bundle gate | |
| run: bun run check:bundle | |
| - name: Package VSIX | |
| run: bun run package | |
| - name: Integration tests (Linux) | |
| if: runner.os == 'Linux' | |
| run: xvfb-run -a bun run test:integration | |
| - name: Integration tests (macOS/Windows) | |
| if: runner.os != 'Linux' | |
| run: bun run test:integration | |
| # The only test that exercises the artifact users actually install. | |
| # Linux only: it drives a second VS Code instance and needs xvfb. | |
| - name: Installed-VSIX end-to-end (Linux) | |
| if: runner.os == 'Linux' | |
| run: xvfb-run -a bun run test:e2e-vsix | |
| - name: Upload VSIX artifact | |
| if: runner.os == 'Linux' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ${{ github.event.repository.name }}-vsix | |
| path: release/*.vsix | |
| if-no-files-found: error | |
| retention-days: 14 | |
| # The Zed extension is a separate toolchain and would otherwise only break at | |
| # submission time, days after the change that broke it. It is a thin launcher | |
| # for the same npm package, so building it is the whole check — there is no | |
| # extraction logic in the crate to test. | |
| zed: | |
| name: Zed extension | |
| needs: changes | |
| # A wasm build and a version-agreement check over package.json, server.json | |
| # and the Zed manifests. Every input is a code file, so a docs-only push | |
| # cannot move it. | |
| if: needs.changes.outputs.code == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| # Ubuntu runners ship a Rust toolchain; adding an action to install one | |
| # would be a new dependency for a target this already has. | |
| - name: Add the wasm target | |
| run: rustup target add wasm32-wasip2 | |
| - name: Build | |
| working-directory: zed | |
| run: cargo build --release --target wasm32-wasip2 --locked | |
| - name: Clippy | |
| working-directory: zed | |
| run: rustup component add clippy && cargo clippy --release --target wasm32-wasip2 --locked -- -D warnings | |
| - name: Formatting | |
| working-directory: zed | |
| run: cargo fmt --check | |
| # One version across five files, and the registry identity across two more. | |
| # build-npm.js writes most of these, but only when it is run — CI is where | |
| # a hand-edit that skipped it gets caught. | |
| - name: Versions and registry identity agree | |
| run: | | |
| extension_version=$(node -p "require('./package.json').version") | |
| zed_version=$(grep -m1 '^version' zed/extension.toml | cut -d'"' -f2) | |
| crate_version=$(grep -m1 '^version' zed/Cargo.toml | cut -d'"' -f2) | |
| npm_version=$(node -p "require('./mcp/package.json').version") | |
| npm_name=$(node -p "require('./mcp/package.json').name") | |
| mcp_name=$(node -p "require('./mcp/package.json').mcpName || ''") | |
| registry_name=$(node -p "require('./server.json').name") | |
| registry_version=$(node -p "require('./server.json').version") | |
| registry_identifier=$(node -p "require('./server.json').packages[0].identifier") | |
| registry_pkg_version=$(node -p "require('./server.json').packages[0].version") | |
| fail=0 | |
| for pair in \ | |
| "zed/extension.toml:$zed_version" \ | |
| "zed/Cargo.toml:$crate_version" \ | |
| "mcp/package.json:$npm_version" \ | |
| "server.json:$registry_version" \ | |
| "server.json packages[0]:$registry_pkg_version"; do | |
| file=${pair%:*} | |
| value=${pair##*:} | |
| if [ "$value" != "$extension_version" ]; then | |
| echo "::error::$file is $value, package.json is $extension_version" | |
| fail=1 | |
| fi | |
| done | |
| # The registry verifies ownership by reading mcpName out of the | |
| # PUBLISHED package, so a mismatch is only discoverable after the | |
| # version is spent and can never be republished. | |
| if [ -z "$mcp_name" ]; then | |
| echo "::error::mcp/package.json has no mcpName" | |
| fail=1 | |
| fi | |
| if [ "$registry_name" != "$mcp_name" ]; then | |
| echo "::error::server.json is $registry_name, mcpName is $mcp_name" | |
| fail=1 | |
| fi | |
| if [ "$registry_identifier" != "$npm_name" ]; then | |
| echo "::error::server.json publishes $registry_identifier, expected $npm_name" | |
| fail=1 | |
| fi | |
| if ! grep -q "\"$npm_name\"" zed/src/lib.rs; then | |
| echo "::error::zed/src/lib.rs does not install $npm_name" | |
| fail=1 | |
| fi | |
| exit $fail |