Repository navigation
fix(docker): shell-based CodeLLDB vendoring in the image build; cut v… #60
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.24.2' | |
| 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@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 | |
| with: | |
| version: 10 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: '22.x' | |
| cache: 'pnpm' | |
| - 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@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '21' | |
| - name: Setup Ruby | |
| uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.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 | |
| 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@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 | |
| - 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 | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.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 | |
| pypi-publish: | |
| name: Publish Python Launcher to PyPI | |
| needs: 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: 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: Publish to PyPI | |
| env: | |
| TWINE_USERNAME: __token__ | |
| TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} | |
| run: | | |
| cd mcp_debugger_launcher | |
| python -m twine check dist/* | |
| python -m twine upload --skip-existing dist/* | |
| 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@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10 | |
| with: | |
| version: 10 | |
| # 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@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '21' | |
| - name: Setup Ruby | |
| uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.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 | |
| - 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 }} | |
| # 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 | |
| # 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 | |
| - 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 }} | |
| - 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 | |
| - name: Generate changelog | |
| id: changelog | |
| run: | | |
| # Extract version from tag | |
| VERSION=${RELEASE_REF#refs/tags/v} | |
| echo "VERSION=$VERSION" >> $GITHUB_OUTPUT | |
| # Get changelog for this version | |
| CHANGELOG=$(sed -n "/^## \[$VERSION\]/,/^## \[/p" CHANGELOG.md | sed '$ d') | |
| echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT | |
| echo "$CHANGELOG" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| # 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@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 | |
| 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@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 | |
| 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 | |
| cat << 'EOF' > release_notes.md | |
| ## 🎉 Release ${{ steps.changelog.outputs.VERSION }} | |
| ${{ steps.changelog.outputs.CHANGELOG }} | |
| ### 📦 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 | |
| npm audit signatures # in a project that installs @debugmcp packages | |
| ``` | |
| 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 |