diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e2e611..4a5f7b5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -107,10 +107,17 @@ jobs: path: artifacts merge-multiple: true + # The MCP server ships in lockstep with the CLI; build-packages.mjs stamps + # its version + optionalDependencies and stages it as npm/dist/csdd-mcp. + - name: Build MCP server + run: | + npm --prefix mcp-server ci + npm --prefix mcp-server run build + - name: Assemble npm packages run: node npm/scripts/build-packages.mjs "${{ github.ref_name }}" artifacts - - name: Publish (platform packages first, then the root) + - name: Publish (platform packages + mcp-server first, then the root) env: # Automation token (bypasses 2FA). setup-node wrote an .npmrc that # reads this. --provenance still attaches a signed attestation via the @@ -118,6 +125,7 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | set -euo pipefail + # csdd-*/ covers the 5 platform binaries and csdd-mcp; csdd/ is the root launcher. for d in npm/dist/csdd-*/; do npm publish "$d" --access public --provenance done diff --git a/Makefile b/Makefile index e68cfe4..9e10253 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,10 @@ # # Manual npm publish (bootstrap / fallback, when CI can't do it): # make dist VERSION=v0.2.0 # cross-compile all 5 targets into dist/ -# make npm-build VERSION=v0.2.0 # assemble npm/dist/ from those artifacts -# make npm-dry-run # validate all 6 packages without publishing -# make npm-publish [OTP=123456] # publish the 6 packages (skips already-published) +# make mcp-dist # build the MCP server (tsc) +# make npm-build VERSION=v0.2.0 # assemble npm/dist/ (7 packages) from those +# make npm-dry-run # validate all 7 packages without publishing +# make npm-publish [OTP=123456] # publish the 7 packages (skips already-published) # # Auth for a manual publish: either an Automation token # npm config set //registry.npmjs.org/:_authToken @@ -55,8 +56,16 @@ vet: ## go vet ./... .PHONY: check check: fmt vet test ## Run the full CI gate (gofmt + vet + race tests) +# require-version fails fast when the release VERSION is left at the 'dev' +# default. Without it, dist/npm-build produce 'dev'-named tarballs (and an +# invalid npm semver), which only surfaces later as a cryptic `tar: Cannot open` +# when npm-build can't find a matching artifact. +.PHONY: require-version +require-version: + @case '$(VERSION)' in v*.*.*) : ;; *) echo "set a release VERSION like v0.1.1 (got '$(VERSION)'); e.g. make $(MAKECMDGOALS) VERSION=v0.1.1" >&2; exit 1 ;; esac + .PHONY: dist -dist: ## Cross-compile every npm target into $(DIST)/ (set VERSION=vX.Y.Z) +dist: require-version ## Cross-compile every npm target into $(DIST)/ (set VERSION=vX.Y.Z) @rm -rf '$(DIST)' && mkdir -p '$(DIST)' @set -euo pipefail; for p in $(PLATFORMS); do \ goos=$${p%/*}; goarch=$${p#*/}; bin=csdd; [ "$$goos" = windows ] && bin=csdd.exe; \ @@ -69,8 +78,13 @@ dist: ## Cross-compile every npm target into $(DIST)/ (set VERSION=vX.Y.Z) done; \ echo "artifacts in $(DIST)/" +.PHONY: mcp-dist +mcp-dist: ## Build the MCP server (tsc) so npm-build can stage @protonspy/csdd-mcp + npm --prefix mcp-server ci + npm --prefix mcp-server run build + .PHONY: npm-build -npm-build: ## Assemble npm/dist/ from artifacts (set VERSION=vX.Y.Z; ARTIFACTS=dir) +npm-build: require-version ## Assemble npm/dist/ (CLI + mcp-server) from artifacts (VERSION=vX.Y.Z; ARTIFACTS=dir; needs `make dist` + `make mcp-dist`) node npm/scripts/build-packages.mjs '$(VERSION)' '$(ARTIFACTS)' .PHONY: npm-dry-run @@ -79,7 +93,7 @@ npm-dry-run: ## Dry-run publish every assembled package echo "== $$d"; npm publish "$$d" --access public --dry-run; done .PHONY: npm-publish -npm-publish: ## Publish the 6 packages, platforms first (skips already-published; OTP=123456 if 2FA) +npm-publish: ## Publish the assembled packages (CLI + mcp-server), skips already-published; OTP=123456 if 2FA @set -euo pipefail; \ otp=; if [ -n "$(OTP)" ]; then otp="--otp=$(OTP)"; fi; \ for d in npm/dist/csdd-*/ npm/dist/csdd/; do \ @@ -91,8 +105,7 @@ npm-publish: ## Publish the 6 packages, platforms first (skips already-published done .PHONY: release -release: ## Tag VERSION and push -> CI builds + publishes (set VERSION=vX.Y.Z) - @case '$(VERSION)' in v*.*.*) : ;; *) echo "VERSION must look like v1.2.3 (got '$(VERSION)')"; exit 1 ;; esac +release: require-version ## Tag VERSION and push -> CI builds + publishes (set VERSION=vX.Y.Z) git tag -a '$(VERSION)' -m 'csdd $(VERSION)' git push origin '$(VERSION)' diff --git a/mcp-server/package.json b/mcp-server/package.json index 51c39f5..175f7ac 100644 --- a/mcp-server/package.json +++ b/mcp-server/package.json @@ -2,6 +2,15 @@ "name": "@protonspy/csdd-mcp", "version": "0.1.0", "description": "MCP server exposing the csdd CLI over stdio (one tool per subcommand).", + "homepage": "https://github.com/protonspy/csdd/tree/main/mcp-server#readme", + "bugs": { + "url": "https://github.com/protonspy/csdd/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/protonspy/csdd.git", + "directory": "mcp-server" + }, "type": "module", "bin": { "csdd-mcp": "dist/index.js" diff --git a/npm/scripts/build-packages.mjs b/npm/scripts/build-packages.mjs index 51063ce..217f410 100644 --- a/npm/scripts/build-packages.mjs +++ b/npm/scripts/build-packages.mjs @@ -20,6 +20,7 @@ import { execFileSync } from "node:child_process"; import { chmodSync, cpSync, + existsSync, mkdirSync, readFileSync, rmSync, @@ -114,3 +115,33 @@ rootPkg.version = version; rootPkg.optionalDependencies = optionalDependencies; writeFileSync(join(rootOut, "package.json"), JSON.stringify(rootPkg, null, 2) + "\n"); console.log(`built ${rootPkg.name}@${version}`); + +// --- mcp-server package ---------------------------------------------------- +// Ship @protonspy/csdd-mcp in lockstep with the CLI: stamp it to the same +// release version and pin the per-platform csdd binaries it resolves to that +// exact version, then stage its pre-built dist/ for publish. Build it first: +// npm --prefix mcp-server ci && npm --prefix mcp-server run build +const mcpSrc = join(repoRoot, "mcp-server"); +const mcpDist = join(mcpSrc, "dist"); +if (!existsSync(mcpDist)) { + console.error( + "error: mcp-server/dist not found — build it first:\n" + + " npm --prefix mcp-server ci && npm --prefix mcp-server run build\n" + + " (or `make mcp-dist`)" + ); + process.exit(1); +} +const mcpOut = join(outDir, "csdd-mcp"); +mkdirSync(mcpOut, { recursive: true }); +cpSync(mcpDist, join(mcpOut, "dist"), { recursive: true }); +cpSync(join(mcpSrc, "README.md"), join(mcpOut, "README.md")); + +const mcpPkg = JSON.parse(readFileSync(join(mcpSrc, "package.json"), "utf8")); +mcpPkg.version = version; +for (const k of Object.keys(mcpPkg.optionalDependencies ?? {})) { + mcpPkg.optionalDependencies[k] = version; // exact, in lockstep with the binary +} +delete mcpPkg.devDependencies; // not needed by consumers of the published package +delete mcpPkg.scripts; // prepublishOnly would re-run tsc against a src/ we don't ship +writeFileSync(join(mcpOut, "package.json"), JSON.stringify(mcpPkg, null, 2) + "\n"); +console.log(`built ${mcpPkg.name}@${version}`);