Repository navigation
chore(release): cut v0.25.0 (#833) #62
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: | |
| inputs: | |
| ref: | |
| description: 'Git ref (tag or branch) to release from' | |
| required: true | |
| default: 'refs/tags/v0.25.0' | |
| permissions: {} | |
| jobs: | |
| build-and-test: | |
| name: Build and Test | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.inputs.ref || github.ref }} | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 | |
| # version comes from package.json "packageManager" (issue #478) | |
| - name: Setup Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '22.x' | |
| cache: 'pnpm' | |
| # Every publishing job needs this one, so a CHANGELOG section that is missing, or too long | |
| # for a GitHub Release body without a Highlights block, stops the release here instead of | |
| # in create-release after npm, Docker and PyPI have already published. | |
| - name: Check release notes fit | |
| env: | |
| RELEASE_REF: ${{ github.event.inputs.ref || github.ref }} | |
| run: node scripts/release-notes.mjs "$RELEASE_REF" --check | |
| - name: Setup Python 3.11 | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Python dependencies | |
| run: | | |
| python -m pip install --require-hashes -r requirements/pip.txt | |
| python -m pip install --require-hashes -r requirements/debugpy.txt | |
| - name: Setup Go | |
| uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| go-version: '1.21' | |
| - name: Install Delve debugger | |
| run: go install github.com/go-delve/delve/cmd/dlv@v1.24.2 | |
| - name: Setup Java 21 | |
| uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '21' | |
| - name: Setup Ruby | |
| uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0 | |
| with: | |
| ruby-version: '3.3' | |
| - name: Verify rdbg (bundled debug gem) | |
| run: rdbg --version | |
| - name: Sanity versions | |
| run: | | |
| node -v | |
| npm -v | |
| pnpm -v | |
| - name: Approve build scripts (esbuild) | |
| run: pnpm approve-builds esbuild | |
| continue-on-error: true | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile --ignore-scripts | |
| - name: Audit production dependencies | |
| run: pnpm audit --prod --audit-level=high | |
| - name: Build project | |
| run: pnpm run build | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Run tests | |
| run: pnpm run test:ci-no-python | |
| docker-publish: | |
| name: Build and Push Docker Image | |
| needs: build-and-test | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write # Required for signing the image attestation (OIDC) | |
| attestations: write # Required to store the attestation | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.inputs.ref || github.ref }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 | |
| with: | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PASSWORD }} | |
| - name: Determine Docker latest tag strategy | |
| shell: bash | |
| env: | |
| GITHUB_REF_TYPE: ${{ github.ref_type }} | |
| GITHUB_REF_NAME: ${{ github.ref_name }} | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| run: | | |
| if [[ "${GITHUB_REF_TYPE}" == "branch" && "${GITHUB_REF_NAME}" == "${DEFAULT_BRANCH}" ]]; then | |
| echo "PUBLISH_LATEST=true" >> "$GITHUB_ENV" | |
| echo "latest will track default branch build (${GITHUB_REF_NAME})" | |
| elif [[ "${GITHUB_REF_TYPE}" == "tag" && "${GITHUB_REF_NAME}" != *"-alpha"* && "${GITHUB_REF_NAME}" != *"-beta"* && "${GITHUB_REF_NAME}" != *"-rc"* ]]; then | |
| echo "PUBLISH_LATEST=true" >> "$GITHUB_ENV" | |
| echo "latest will point to release ${GITHUB_REF_NAME}" | |
| else | |
| echo "PUBLISH_LATEST=false" >> "$GITHUB_ENV" | |
| echo "latest tag update skipped for ref ${GITHUB_REF_NAME}" | |
| fi | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 | |
| with: | |
| images: debugmcp/mcp-debugger | |
| tags: | | |
| type=ref,event=tag | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=raw,value=latest,enable=${{ env.PUBLISH_LATEST == 'true' }} | |
| - name: Build and push Docker image | |
| id: build | |
| uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 | |
| with: | |
| context: . | |
| platforms: linux/amd64,linux/arm64 | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Attest the pushed image digest so the Docker artifact is verifiable back | |
| # to this workflow and commit, like the npm tarballs already are: | |
| # gh attestation verify oci://index.docker.io/debugmcp/mcp-debugger:<tag> \ | |
| # --repo debugmcp/mcp-debugger | |
| # Same composite-action approach as the provenance job below (see the note | |
| # there for why slsa-github-generator's reusable workflows are not usable | |
| # in this org). | |
| - name: Attest Docker image provenance | |
| uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 | |
| with: | |
| subject-name: index.docker.io/debugmcp/mcp-debugger | |
| subject-digest: ${{ steps.build.outputs.digest }} | |
| push-to-registry: true | |
| pypi-publish: | |
| name: Publish Python Launcher to PyPI | |
| needs: build-and-test | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write # Required for PyPI trusted publishing (OIDC) | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.inputs.ref || github.ref }} | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Python build dependencies | |
| run: | | |
| python -m pip install "pip==25.0.1" | |
| pip install "build==1.2.2" "twine==6.1.0" "tomlkit==0.13.2" | |
| - name: Resolve release ref | |
| run: | | |
| if [ -n "${{ github.event.inputs.ref }}" ]; then | |
| echo "RELEASE_REF=${{ github.event.inputs.ref }}" >> $GITHUB_ENV | |
| else | |
| echo "RELEASE_REF=${GITHUB_REF}" >> $GITHUB_ENV | |
| fi | |
| - name: Set launcher version from tag | |
| run: | | |
| VERSION="${RELEASE_REF#refs/tags/v}" | |
| echo "VERSION=$VERSION" >> $GITHUB_ENV | |
| - name: Sync version into pyproject.toml | |
| run: | | |
| python - <<'PY' | |
| import os | |
| from tomlkit import parse, dumps | |
| p = os.path.join('mcp_debugger_launcher','pyproject.toml') | |
| with open(p,'r',encoding='utf-8') as f: | |
| doc = parse(f.read()) | |
| ver = os.environ.get('VERSION','0.0.0') | |
| if 'project' in doc and 'version' in doc['project']: | |
| doc['project']['version'] = ver | |
| elif 'tool' in doc and 'poetry' in doc['tool'] and 'version' in doc['tool']['poetry']: | |
| doc['tool']['poetry']['version'] = ver | |
| else: | |
| doc.setdefault('project', {})['version'] = ver | |
| with open(p,'w',encoding='utf-8') as f: | |
| f.write(dumps(doc)) | |
| print(f"Set pyproject version to {ver}") | |
| PY | |
| - name: Build Python package | |
| run: | | |
| cd mcp_debugger_launcher | |
| rm -rf dist build *.egg-info | |
| python -m build | |
| - name: Check package metadata | |
| run: | | |
| cd mcp_debugger_launcher | |
| python -m twine check dist/* | |
| # PyPI trusted publishing (OIDC) - no long-lived token. The action also | |
| # generates and uploads PEP 740 digital attestations for the dists. | |
| # Requires the trusted publisher configured on pypi.org for | |
| # debug-mcp-server-launcher (repo debugmcp/mcp-debugger, workflow | |
| # release.yml). | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 | |
| with: | |
| packages-dir: mcp_debugger_launcher/dist | |
| skip-existing: true | |
| npm-publish: | |
| name: Publish to npm | |
| needs: build-and-test | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write # Required for npm trusted publishing (OIDC) | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.inputs.ref || github.ref }} | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 | |
| # version comes from package.json "packageManager" (issue #478) | |
| # No registry-url here: setup-node would write an _authToken=${NODE_AUTH_TOKEN} | |
| # placeholder into .npmrc, which breaks token-less OIDC publishes when the | |
| # env var is unset. The token step below writes its own scoped .npmrc. | |
| - name: Setup Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '22.x' | |
| cache: 'pnpm' | |
| # npm >= 11.5.1 is required for OIDC trusted publishing; Node 22 bundles npm 10. | |
| - name: Upgrade npm for trusted publishing | |
| run: npm install -g npm@12.0.2 | |
| - name: Setup Java 21 | |
| uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '21' | |
| - name: Setup Ruby | |
| uses: ruby/setup-ruby@14594264cd68ce8a2345dd349bc3d138a4ef85c8 # v1.327.0 | |
| with: | |
| ruby-version: '3.3' | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile --ignore-scripts | |
| - name: Sanity versions | |
| run: | | |
| node -v | |
| npm -v | |
| pnpm -v | |
| - name: Resolve release ref | |
| run: | | |
| if [ -n "${{ github.event.inputs.ref }}" ]; then | |
| echo "RELEASE_REF=${{ github.event.inputs.ref }}" >> $GITHUB_ENV | |
| else | |
| echo "RELEASE_REF=${GITHUB_REF}" >> $GITHUB_ENV | |
| fi | |
| - name: Set CLI package version from tag (monorepo-safe) | |
| run: | | |
| VERSION="${RELEASE_REF#refs/tags/v}" | |
| echo "Setting CLI package version to $VERSION" | |
| VERSION_STRIPPED="$VERSION" node -e "const fs=require('fs');const p='packages/mcp-debugger/package.json';const pkg=JSON.parse(fs.readFileSync(p,'utf8'));const ver=process.env.VERSION_STRIPPED; if(!/^[0-9]+\\.[0-9]+\\.[0-9]+(-.+)?$/.test(ver)){console.error('Invalid semver:',ver);process.exit(1);} pkg.version=ver; fs.writeFileSync(p,JSON.stringify(pkg,null,2)+'\\n');console.log('Updated',p,'to',pkg.version)" | |
| - name: Capture workspace package versions (robust) | |
| run: | | |
| node -e 'console.log("SHARED_VERSION="+require("./packages/shared/package.json").version)' >> $GITHUB_ENV | |
| node -e 'console.log("ADAPTER_MOCK_VERSION="+require("./packages/adapter-mock/package.json").version)' >> $GITHUB_ENV | |
| node -e 'console.log("ADAPTER_PYTHON_VERSION="+require("./packages/adapter-python/package.json").version)' >> $GITHUB_ENV | |
| node -e 'console.log("ADAPTER_RUBY_VERSION="+require("./packages/adapter-ruby/package.json").version)' >> $GITHUB_ENV | |
| node -e 'console.log("ADAPTER_JAVASCRIPT_VERSION="+require("./packages/adapter-javascript/package.json").version)' >> $GITHUB_ENV | |
| node -e 'console.log("ADAPTER_GO_VERSION="+require("./packages/adapter-go/package.json").version)' >> $GITHUB_ENV | |
| node -e 'console.log("ADAPTER_JAVA_VERSION="+require("./packages/adapter-java/package.json").version)' >> $GITHUB_ENV | |
| node -e 'console.log("ADAPTER_DOTNET_VERSION="+require("./packages/adapter-dotnet/package.json").version)' >> $GITHUB_ENV | |
| node -e 'console.log("CLI_VERSION="+require("./packages/mcp-debugger/package.json").version)' >> $GITHUB_ENV | |
| # CodeLLDB platform packages are versioned by the vendored CodeLLDB release (issue #383) | |
| node -e 'console.log("CODELLDB_PKG_VERSION="+require("./packages/codelldb-common/vendor-manifest.json").codelldb.version)' >> $GITHUB_ENV | |
| - name: Set npm dist-tag | |
| run: | | |
| if [[ "${CLI_VERSION}" == *"-beta"* ]] || [[ "${CLI_VERSION}" == *"-alpha"* ]]; then | |
| echo "NPM_TAG=beta" >> $GITHUB_ENV | |
| else | |
| echo "NPM_TAG=latest" >> $GITHUB_ENV | |
| fi | |
| - name: Build project | |
| run: pnpm run build | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Downloads the digest-pinned VSIXs for all five platforms (the build above | |
| # vendored linux-x64 only under CI=true) and copies the payloads into the | |
| # @debugmcp/codelldb-* platform packages. Verification runs in a SEPARATE | |
| # process: a vendoring process that died silently (issue #389) cannot | |
| # vouch for itself, and --verify-only fails the job if any package would | |
| # pack without its binaries (issue #383). | |
| - name: Stage CodeLLDB platform packages | |
| run: | | |
| node scripts/stage-codelldb-packages.mjs | |
| node scripts/stage-codelldb-packages.mjs --verify-only | |
| # npm (unlike pnpm) does not rewrite workspace:* at publish time; without | |
| # this step the published adapter packages carry an uninstallable | |
| # "workspace:*" dependency on @debugmcp/shared (EUNSUPPORTEDPROTOCOL). | |
| # The rewrite happens only in the runner checkout, never committed. | |
| - name: Resolve workspace deps for publish | |
| run: node scripts/resolve-workspace-deps.cjs | |
| - name: Pack npm tarballs (dry-run) | |
| run: | | |
| npm pack --dry-run -w @debugmcp/shared | |
| npm pack --dry-run -w @debugmcp/adapter-mock | |
| npm pack --dry-run -w @debugmcp/adapter-python | |
| npm pack --dry-run -w @debugmcp/adapter-ruby | |
| npm pack --dry-run -w @debugmcp/adapter-javascript | |
| npm pack --dry-run -w @debugmcp/adapter-go | |
| npm pack --dry-run -w @debugmcp/adapter-java | |
| npm pack --dry-run -w @debugmcp/adapter-dotnet | |
| npm pack --dry-run -w @debugmcp/mcp-debugger | |
| npm pack --dry-run -w @debugmcp/codelldb-win32-x64 | |
| npm pack --dry-run -w @debugmcp/codelldb-darwin-x64 | |
| npm pack --dry-run -w @debugmcp/codelldb-darwin-arm64 | |
| npm pack --dry-run -w @debugmcp/codelldb-linux-x64 | |
| npm pack --dry-run -w @debugmcp/codelldb-linux-arm64 | |
| # CodeLLDB platform packages (issue #383). Published BEFORE the CLI package | |
| # so its optionalDependencies always resolve on the registry. Versioned by | |
| # the CodeLLDB release and tagged latest explicitly (they are shared across | |
| # beta/latest channels), so re-releases at an unchanged pin are skipped by | |
| # the npm-view guards. First publish must use the token flow; once the five | |
| # exist, configure trusted publishers and move them into the OIDC step. | |
| - name: Publish CodeLLDB platform packages to npm (token, first publish) | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| # trap (not a trailing rm) so the token file cannot survive a | |
| # mid-loop publish failure on the runner. | |
| trap 'rm -f "$HOME/.npmrc"' EXIT | |
| echo "//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}" > "$HOME/.npmrc" | |
| # "already exists" is the NORMAL path here (every re-release at an | |
| # unchanged CodeLLDB pin walks it), so the guard must distinguish a | |
| # true 404 from a transient registry error - publishing over an | |
| # existing version would otherwise kill the job before the OIDC step. | |
| for pkg in codelldb-win32-x64 codelldb-darwin-x64 codelldb-darwin-arm64 codelldb-linux-x64 codelldb-linux-arm64; do | |
| set +e | |
| VIEW_OUT=$(npm view @debugmcp/${pkg}@${CODELLDB_PKG_VERSION} version 2>&1) | |
| VIEW_CODE=$? | |
| set -e | |
| if [ "$VIEW_CODE" -eq 0 ]; then | |
| echo "@debugmcp/${pkg}@${CODELLDB_PKG_VERSION} already exists, skipping" | |
| elif echo "$VIEW_OUT" | grep -q "E404"; then | |
| npm publish -w @debugmcp/${pkg} --access public --provenance --tag latest | |
| else | |
| echo "npm view for @debugmcp/${pkg}@${CODELLDB_PKG_VERSION} failed with a non-404 error; refusing to guess:" | |
| echo "$VIEW_OUT" | |
| exit 1 | |
| fi | |
| done | |
| # Existing packages publish via OIDC trusted publishing: no token anywhere, | |
| # npm mints a short-lived credential from the workflow's OIDC identity and | |
| # generates provenance automatically. Each package must have this repo + | |
| # workflow configured as a trusted publisher on npmjs.com BEFORE tagging. | |
| - name: Publish existing packages to npm (OIDC trusted publishing) | |
| run: | | |
| # Publish packages in dependency order if the target version does NOT yet exist | |
| if npm view @debugmcp/shared@${SHARED_VERSION} version >/dev/null 2>&1; then | |
| echo "@debugmcp/shared@${SHARED_VERSION} already exists, skipping" | |
| else | |
| npm publish -w @debugmcp/shared --access public --provenance --tag ${NPM_TAG} | |
| fi | |
| if npm view @debugmcp/adapter-mock@${ADAPTER_MOCK_VERSION} version >/dev/null 2>&1; then | |
| echo "@debugmcp/adapter-mock@${ADAPTER_MOCK_VERSION} already exists, skipping" | |
| else | |
| npm publish -w @debugmcp/adapter-mock --access public --provenance --tag ${NPM_TAG} | |
| fi | |
| if npm view @debugmcp/adapter-python@${ADAPTER_PYTHON_VERSION} version >/dev/null 2>&1; then | |
| echo "@debugmcp/adapter-python@${ADAPTER_PYTHON_VERSION} already exists, skipping" | |
| else | |
| npm publish -w @debugmcp/adapter-python --access public --provenance --tag ${NPM_TAG} | |
| fi | |
| if npm view @debugmcp/adapter-ruby@${ADAPTER_RUBY_VERSION} version >/dev/null 2>&1; then | |
| echo "@debugmcp/adapter-ruby@${ADAPTER_RUBY_VERSION} already exists, skipping" | |
| else | |
| npm publish -w @debugmcp/adapter-ruby --access public --provenance --tag ${NPM_TAG} | |
| fi | |
| if npm view @debugmcp/mcp-debugger@${CLI_VERSION} version >/dev/null 2>&1; then | |
| echo "@debugmcp/mcp-debugger@${CLI_VERSION} already exists, skipping" | |
| else | |
| npm publish -w @debugmcp/mcp-debugger --access public --provenance --tag ${NPM_TAG} | |
| fi | |
| # First-time publishes cannot use trusted publishing (the trusted-publisher | |
| # config lives on an existing npm package). These four publish with the | |
| # scoped NPM_TOKEN this release only; once they exist, configure trusted | |
| # publishers for them and move them into the OIDC step above. | |
| - name: Publish new packages to npm (token, first publish) | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| echo "//registry.npmjs.org/:_authToken=\${NODE_AUTH_TOKEN}" > "$HOME/.npmrc" | |
| if npm view @debugmcp/adapter-javascript@${ADAPTER_JAVASCRIPT_VERSION} version >/dev/null 2>&1; then | |
| echo "@debugmcp/adapter-javascript@${ADAPTER_JAVASCRIPT_VERSION} already exists, skipping" | |
| else | |
| npm publish -w @debugmcp/adapter-javascript --access public --provenance --tag ${NPM_TAG} | |
| fi | |
| if npm view @debugmcp/adapter-go@${ADAPTER_GO_VERSION} version >/dev/null 2>&1; then | |
| echo "@debugmcp/adapter-go@${ADAPTER_GO_VERSION} already exists, skipping" | |
| else | |
| npm publish -w @debugmcp/adapter-go --access public --provenance --tag ${NPM_TAG} | |
| fi | |
| if npm view @debugmcp/adapter-java@${ADAPTER_JAVA_VERSION} version >/dev/null 2>&1; then | |
| echo "@debugmcp/adapter-java@${ADAPTER_JAVA_VERSION} already exists, skipping" | |
| else | |
| npm publish -w @debugmcp/adapter-java --access public --provenance --tag ${NPM_TAG} | |
| fi | |
| if npm view @debugmcp/adapter-dotnet@${ADAPTER_DOTNET_VERSION} version >/dev/null 2>&1; then | |
| echo "@debugmcp/adapter-dotnet@${ADAPTER_DOTNET_VERSION} already exists, skipping" | |
| else | |
| npm publish -w @debugmcp/adapter-dotnet --access public --provenance --tag ${NPM_TAG} | |
| fi | |
| rm -f "$HOME/.npmrc" | |
| # The packed tarballs become GitHub Release assets, attested by the | |
| # provenance job below (OpenSSF Scorecard Signed-Releases). | |
| - name: Pack release artifacts for provenance | |
| run: | | |
| mkdir -p release-artifacts | |
| npm pack -w @debugmcp/shared --pack-destination release-artifacts | |
| npm pack -w @debugmcp/adapter-mock --pack-destination release-artifacts | |
| npm pack -w @debugmcp/adapter-python --pack-destination release-artifacts | |
| npm pack -w @debugmcp/adapter-ruby --pack-destination release-artifacts | |
| npm pack -w @debugmcp/adapter-javascript --pack-destination release-artifacts | |
| npm pack -w @debugmcp/adapter-go --pack-destination release-artifacts | |
| npm pack -w @debugmcp/adapter-java --pack-destination release-artifacts | |
| npm pack -w @debugmcp/adapter-dotnet --pack-destination release-artifacts | |
| npm pack -w @debugmcp/mcp-debugger --pack-destination release-artifacts | |
| npm pack -w @debugmcp/codelldb-win32-x64 --pack-destination release-artifacts | |
| npm pack -w @debugmcp/codelldb-darwin-x64 --pack-destination release-artifacts | |
| npm pack -w @debugmcp/codelldb-darwin-arm64 --pack-destination release-artifacts | |
| npm pack -w @debugmcp/codelldb-linux-x64 --pack-destination release-artifacts | |
| npm pack -w @debugmcp/codelldb-linux-arm64 --pack-destination release-artifacts | |
| - name: Upload release artifacts | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: release-artifacts | |
| path: release-artifacts/*.tgz | |
| if-no-files-found: error | |
| provenance: | |
| name: Generate build provenance attestation | |
| needs: npm-publish | |
| runs-on: ubuntu-latest | |
| permissions: | |
| id-token: write # sign the attestation (sigstore) | |
| attestations: write # persist it to the GitHub Attestations API | |
| contents: read | |
| # Uses actions/attest-build-provenance (a plain composite action) rather than | |
| # slsa-framework/slsa-github-generator's reusable workflow: this org has | |
| # "Write permissions for workflows" disabled, and GitHub validates a calling | |
| # job's permissions against that policy at PARSE TIME for external reusable | |
| # *workflow* calls specifically -- even read-only-looking permission sets on | |
| # such a job made the whole run fail with startup_failure before any job | |
| # (even unrelated ones) could start. A normal action inside a normal job | |
| # (like npm-publish's existing id-token: write) isn't subject to that check. | |
| steps: | |
| - name: Download release artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: release-artifacts | |
| path: release-artifacts | |
| - name: Generate attestation | |
| id: attest | |
| uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 | |
| with: | |
| subject-path: release-artifacts/*.tgz | |
| # The bundle is a JSON-serialized Sigstore bundle wrapping an in-toto | |
| # statement -- genuinely valid under both extensions Scorecard's | |
| # Signed-Releases probes scan release assets for (releasesAreSigned: | |
| # .sigstore.json; releasesHaveProvenance: .intoto.jsonl). | |
| - name: Name provenance files for release assets | |
| run: | | |
| cp "${{ steps.attest.outputs.bundle-path }}" multiple.intoto.jsonl | |
| cp "${{ steps.attest.outputs.bundle-path }}" multiple.sigstore.json | |
| - name: Upload provenance files | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: provenance | |
| path: | | |
| multiple.intoto.jsonl | |
| multiple.sigstore.json | |
| if-no-files-found: error | |
| create-release: | |
| name: Create GitHub Release | |
| needs: [docker-publish, pypi-publish, npm-publish, provenance] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # Grant permission to create releases | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.inputs.ref || github.ref }} | |
| # Pinned rather than relying on the runner image's Node: this job runs after every publish, | |
| # so it must not depend on anything that can drift underneath it. | |
| - name: Setup Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '22.x' | |
| - name: Resolve release ref | |
| run: | | |
| if [ -n "${{ github.event.inputs.ref }}" ]; then | |
| echo "RELEASE_REF=${{ github.event.inputs.ref }}" >> $GITHUB_ENV | |
| else | |
| echo "RELEASE_REF=${GITHUB_REF}" >> $GITHUB_ENV | |
| fi | |
| - name: Download release artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: release-artifacts | |
| path: release-artifacts | |
| - name: Download provenance attestation | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: provenance | |
| path: provenance | |
| # The CHANGELOG section goes to a file, not a step output: interpolating a ~100 KB output into | |
| # the release script is fragile, and a section over GitHub's release-body limit is replaced by | |
| # its Highlights block and a link (scripts/release-notes.mjs; build-and-test already checked it). | |
| - name: Generate changelog | |
| id: changelog | |
| run: | | |
| # Extract version from tag | |
| VERSION=${RELEASE_REF#refs/tags/v} | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| node scripts/release-notes.mjs "$VERSION" --out release-changelog.md | |
| # SBOMs (SPDX + CycloneDX) are generated from the exact release ref and | |
| # attached to the GitHub release — required by many corporate/government | |
| # procurement processes (EO 14028) and useful for downstream scanners. | |
| - name: Generate SBOM (SPDX) | |
| uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2 | |
| with: | |
| path: . | |
| format: spdx-json | |
| output-file: sbom.spdx.json | |
| upload-artifact: false | |
| upload-release-assets: false | |
| - name: Generate SBOM (CycloneDX) | |
| uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2 | |
| with: | |
| path: . | |
| format: cyclonedx-json | |
| output-file: sbom.cyclonedx.json | |
| upload-artifact: false | |
| upload-release-assets: false | |
| - name: Create Release with GitHub CLI | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # Create the release body in a file: heading, the changelog body chosen by | |
| # scripts/release-notes.mjs, then the fixed installation and verification text. | |
| { | |
| printf '## 🎉 Release %s\n\n' "${{ steps.changelog.outputs.VERSION }}" | |
| cat release-changelog.md | |
| printf '\n' | |
| } > release_notes.md | |
| cat << 'EOF' >> release_notes.md | |
| ### 📦 Installation | |
| **Docker:** | |
| ```bash | |
| docker pull debugmcp/mcp-debugger:${{ steps.changelog.outputs.VERSION }} | |
| ``` | |
| **npm (global install):** | |
| ```bash | |
| npm install -g @debugmcp/mcp-debugger@${{ steps.changelog.outputs.VERSION }} | |
| ``` | |
| **npx (no install):** | |
| ```bash | |
| npx @debugmcp/mcp-debugger@${{ steps.changelog.outputs.VERSION }} stdio | |
| ``` | |
| **PyPI:** | |
| ```bash | |
| pip install debug-mcp-server-launcher==${{ steps.changelog.outputs.VERSION }} | |
| ``` | |
| **Optional adapters** (for programmatic embedding; the CLI above bundles all of them): | |
| ```bash | |
| npm install @debugmcp/adapter-python # or: adapter-ruby, adapter-javascript, | |
| # adapter-go, adapter-java, adapter-dotnet, | |
| # adapter-mock | |
| ``` | |
| ### 🔏 Verify this release | |
| ```bash | |
| gh attestation verify <asset>.tgz --repo debugmcp/mcp-debugger | |
| gh attestation verify oci://index.docker.io/debugmcp/mcp-debugger:${{ steps.changelog.outputs.VERSION }} --repo debugmcp/mcp-debugger | |
| npm audit signatures # in a project that installs @debugmcp packages | |
| ``` | |
| The PyPI launcher carries PEP 740 attestations — see the "Verified details" on its [PyPI page](https://pypi.org/project/debug-mcp-server-launcher/${{ steps.changelog.outputs.VERSION }}/). | |
| SBOMs (SPDX + CycloneDX) are attached as release assets. | |
| ### 📚 Documentation | |
| See the [README](https://github.com/debugmcp/mcp-debugger#readme) for usage instructions. | |
| EOF | |
| # Determine if this is a prerelease | |
| if [[ "${{ github.ref_name }}" == *"-beta"* ]] || [[ "${{ github.ref_name }}" == *"-alpha"* ]]; then | |
| PRERELEASE_FLAG="--prerelease" | |
| else | |
| PRERELEASE_FLAG="" | |
| fi | |
| # Create the release using GitHub CLI, attaching the npm tarballs and | |
| # their build provenance attestation (verify with: | |
| # gh attestation verify <tarball> --repo debugmcp/mcp-debugger) | |
| gh release create "${{ github.ref_name }}" \ | |
| --title "Release ${{ steps.changelog.outputs.VERSION }}" \ | |
| --notes-file release_notes.md \ | |
| $PRERELEASE_FLAG \ | |
| release-artifacts/*.tgz \ | |
| provenance/multiple.intoto.jsonl \ | |
| provenance/multiple.sigstore.json \ | |
| sbom.spdx.json \ | |
| sbom.cyclonedx.json |