Skip to content

SDL-MCP v0.13.3

SDL-MCP v0.13.3 #69

Workflow file for this run

name: Release Publish
on:
release:
types:
- published
workflow_dispatch:
inputs:
release_tag:
description: "Release tag to publish (for example: v0.8.1)"
required: true
type: string
dry_run:
description: "Dry run (skip actual publish)"
required: false
default: false
type: boolean
permissions:
contents: read
jobs:
resolve-version:
name: Resolve Release Version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve.outputs.version }}
npm_dist_tag: ${{ steps.resolve.outputs.npm_dist_tag }}
dry_run: ${{ steps.resolve.outputs.dry_run }}
raw_tag: ${{ steps.resolve.outputs.raw_tag }}
should_publish: ${{ steps.resolve.outputs.should_publish }}
steps:
- name: Resolve version and npm dist-tag
id: resolve
shell: bash
run: |
set -euo pipefail
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
RAW_TAG="${{ github.event.inputs.release_tag }}"
DRY_RUN_INPUT="${{ github.event.inputs.dry_run }}"
else
RAW_TAG="${{ github.event.release.tag_name }}"
DRY_RUN_INPUT="false"
fi
if [[ -z "${RAW_TAG}" ]]; then
echo "Release tag is empty"
exit 1
fi
VERSION="${RAW_TAG#v}"
if ! [[ "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]]; then
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "Release tag '${RAW_TAG}' is not a valid semver tag (expected vX.Y.Z)"
exit 1
fi
# Allow asset-only releases (for example models-v1) without failing publish CI.
echo "Non-semver release tag '${RAW_TAG}' detected; skipping npm publish workflow."
echo "raw_tag=${RAW_TAG}" >> "${GITHUB_OUTPUT}"
echo "version=" >> "${GITHUB_OUTPUT}"
echo "npm_dist_tag=" >> "${GITHUB_OUTPUT}"
echo "dry_run=false" >> "${GITHUB_OUTPUT}"
echo "should_publish=false" >> "${GITHUB_OUTPUT}"
exit 0
fi
NPM_DIST_TAG="latest"
if [[ "${VERSION}" == *-* ]]; then
NPM_DIST_TAG="next"
fi
DRY_RUN="false"
if [[ "${DRY_RUN_INPUT}" == "true" ]]; then
DRY_RUN="true"
fi
echo "raw_tag=${RAW_TAG}" >> "${GITHUB_OUTPUT}"
echo "version=${VERSION}" >> "${GITHUB_OUTPUT}"
echo "npm_dist_tag=${NPM_DIST_TAG}" >> "${GITHUB_OUTPUT}"
echo "dry_run=${DRY_RUN}" >> "${GITHUB_OUTPUT}"
echo "should_publish=true" >> "${GITHUB_OUTPUT}"
echo "Resolved release tag: ${RAW_TAG}"
echo "Resolved version: ${VERSION}"
echo "Resolved npm dist-tag: ${NPM_DIST_TAG}"
echo "Dry run: ${DRY_RUN}"
echo "Should publish: true"
build-native:
name: Build Native (${{ matrix.target }})
runs-on: ${{ matrix.os }}
needs: resolve-version
if: needs.resolve-version.outputs.should_publish == 'true'
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-pc-windows-msvc
os: windows-latest
- target: x86_64-apple-darwin
os: macos-latest
- target: aarch64-apple-darwin
os: macos-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24.x
cache: npm
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools (aarch64-linux-gnu)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
- name: Install cross-compilation tools (x86_64-linux-musl)
if: matrix.target == 'x86_64-unknown-linux-musl'
run: |
sudo apt-get update
sudo apt-get install -y musl-tools
- name: Cache Cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
native/target
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('native/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.target }}-cargo-
- name: Install dependencies
run: npm ci --ignore-scripts --legacy-peer-deps
- name: Setup tree-sitter
uses: ./.github/actions/setup-tree-sitter
- name: Build native addon
run: npx napi build --release --cargo-cwd native --config native/package.json --target ${{ matrix.target }} native
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
- name: Upload native artifact
uses: actions/upload-artifact@v7
with:
name: bindings-${{ matrix.target }}
path: native/*.node
if-no-files-found: error
verify-packed-install:
name: Verify Packed Install (${{ matrix.os }}, accelerators=${{ matrix.accelerators }})
runs-on: ${{ matrix.os }}
needs:
- resolve-version
- build-native
if: needs.resolve-version.outputs.should_publish == 'true'
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
accelerators: included
- os: windows-latest
accelerators: disabled
- os: ubuntu-latest
accelerators: included
- os: macos-latest
accelerators: included
env:
VERSION: ${{ needs.resolve-version.outputs.version }}
SDL_MCP_SKIP_MODEL_DOWNLOAD: "1"
SDL_MCP_STRICT_TREE_SITTER_POSTINSTALL: "1"
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24.x
cache: npm
- name: Refresh package-lock for release publish
run: npm install --package-lock-only --ignore-scripts --legacy-peer-deps
- name: Install dependencies
run: npm ci --ignore-scripts --legacy-peer-deps
- name: Setup tree-sitter
uses: ./.github/actions/setup-tree-sitter
- name: Set package versions from release
shell: bash
run: |
set -euo pipefail
npm version "${VERSION}" --no-git-tag-version --allow-same-version
node scripts/sync-native-version.mjs "${VERSION}"
- name: Download Windows native artifact
if: matrix.os == 'windows-latest' && matrix.accelerators == 'included'
uses: actions/download-artifact@v7
with:
name: bindings-x86_64-pc-windows-msvc
path: native/artifacts/bindings-x86_64-pc-windows-msvc
- name: Stage Windows native package
if: matrix.os == 'windows-latest' && matrix.accelerators == 'included'
shell: bash
run: |
set -euo pipefail
native_node=$(find native/artifacts/bindings-x86_64-pc-windows-msvc -name "*.node" -type f | head -n 1)
test -n "${native_node}"
cp "${native_node}" native/npm/win32-x64-msvc/sdl-mcp-native.win32-x64-msvc.node
- name: Build runtime package
run: npm run build:all
- name: Pack main package
shell: bash
run: |
set -euo pipefail
mkdir -p release-pack
npm pack --pack-destination release-pack
if [[ "${{ matrix.os }}" == "windows-latest" && "${{ matrix.accelerators }}" == "included" ]]; then
# The main publish is gated by this job, so verify with the native tarballs built in this run.
npm pack ./native/npm/win32-x64-msvc --pack-destination release-pack
npm pack ./native --pack-destination release-pack
fi
- name: Install packed package and verify runtime
shell: bash
run: |
set -euo pipefail
tarball=$(node -e "const fs=require('fs');const path=require('path');const f='sdl-mcp-'+process.env.VERSION+'.tgz';if(!fs.existsSync(path.join('release-pack',f)))process.exit(1);console.log(path.resolve('release-pack',f).replace(/\\\\/g,'/'));")
fixture_test=$(node -e "console.log(require('path').resolve('tests/integration/live-index-symbol-fts-crash.test.ts').replace(/\\\\/g,'/'))")
fixture_child=$(node -e "console.log(require('path').resolve('tests/fixtures/ladybug/windows-fts-clean-env-child.mjs').replace(/\\\\/g,'/'))")
context_source=$(node -e "console.log(require('path').resolve('src/context/profiles.ts').replace(/\\\\/g,'/'))")
install_args=("${tarball}" "--legacy-peer-deps")
if [[ "${{ matrix.os }}" == "windows-latest" && "${{ matrix.accelerators }}" == "included" ]]; then
native_tarball=$(node -e "console.log(require('path').resolve('release-pack/sdl-mcp-native-'+process.env.VERSION+'.tgz').replace(/\\\\/g,'/'))")
native_platform_tarball=$(node -e "console.log(require('path').resolve('release-pack/sdl-mcp-native-win32-x64-msvc-'+process.env.VERSION+'.tgz').replace(/\\\\/g,'/'))")
install_args+=("${native_tarball}" "${native_platform_tarball}")
fi
smoke_dir=$(node -e "const fs=require('fs');const os=require('os');const path=require('path');console.log(fs.mkdtempSync(path.join(os.tmpdir(),'sdl-mcp-pack-smoke-')).replace(/\\\\/g,'/'));")
cd "${smoke_dir}"
npm init -y > /dev/null
npm pkg set type=module
npm install "${install_args[@]}"
node node_modules/sdl-mcp/scripts/postinstall-tree-sitter.mjs --verify-only
openssl_package="node_modules/@sdl-mcp/ladybug-openssl-win32-x64/package.json"
ladybug_platform_package="node_modules/@ladybugdb/core-win32-x64/package.json"
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
node -e "const fs=require('fs');if(!fs.existsSync('$ladybug_platform_package'))throw new Error('missing Ladybug platform binary: $ladybug_platform_package')"
fi
if [[ "${{ matrix.os }}" == "windows-latest" && "${{ matrix.accelerators }}" == "included" ]]; then
node -e "const fs=require('fs');for(const file of ['$openssl_package','node_modules/sdl-mcp-native/package.json','node_modules/sdl-mcp-native-win32-x64-msvc/package.json'])if(!fs.existsSync(file))throw new Error('missing packed-install accelerator: '+file)"
elif [[ "${{ matrix.os }}" != "windows-latest" ]]; then
node -e "const fs=require('fs');if(fs.existsSync('$openssl_package'))throw new Error('Windows OpenSSL package was not filtered')"
fi
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
# The disabled MCP probe reads the root config explicitly; the clean-env FTS
# child uses the repository fallback under config/.
printf '%s\n' '{"repos":[],"policy":{}}' > sdlmcp.config.json
mkdir -p config
cp sdlmcp.config.json config/sdlmcp.config.json
fi
if [[ "${{ matrix.os }}" == "windows-latest" && "${{ matrix.accelerators }}" == "disabled" ]]; then
node --input-type=module <<'NODE'
import assert from "node:assert/strict";
import path from "node:path";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const client = new Client({ name: "release-packed-install", version: "1.0.0" });
const transport = new StdioClientTransport({
command: process.execPath,
args: ["node_modules/sdl-mcp/dist/main.js"],
env: {
...process.env,
NODE_ENV: "test",
SDL_CONFIG: path.resolve("sdlmcp.config.json"),
SDL_GRAPH_DB_PATH: path.resolve("packed-install-graph.lbug"),
SDL_MCP_DISABLE_NATIVE_ADDON: "1",
SDL_CONSOLE_LOGGING: "false",
},
});
try {
await client.connect(transport);
const response = await client.listTools();
assert.ok(response.tools.some((tool) => tool.name === "sdl.info"));
const info = await client.callTool({ name: "sdl.info", arguments: { redactPaths: true } });
assert.equal(info.structuredContent?.ladybug?.available, true);
assert.equal(info.structuredContent?.native?.disabledByEnv, true);
} finally {
await client.close().catch(() => {});
}
NODE
fi
if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
# Node 24 does not strip TypeScript under node_modules. Build a temporary harness beside it.
mkdir -p "${smoke_dir}/tests/integration" "${smoke_dir}/tests/fixtures/ladybug" "${smoke_dir}/src/context"
cp "${fixture_test}" "${smoke_dir}/tests/integration/live-index-symbol-fts-crash.test.ts"
cp "${fixture_child}" "${smoke_dir}/tests/fixtures/ladybug/windows-fts-clean-env-child.mjs"
cp "${context_source}" "${smoke_dir}/src/context/profiles.ts"
cp -R node_modules/sdl-mcp/dist "${smoke_dir}/dist"
if [[ "${{ matrix.accelerators }}" == "included" ]]; then
SDL_LADYBUG_WINDOWS_FTS_TEST_MODE=fixed-regression SDL_LADYBUG_WINDOWS_FTS_REQUIRE_MODE=1 \
node --experimental-strip-types --test-concurrency=1 --test tests/integration/live-index-symbol-fts-crash.test.ts
else
SDL_MCP_DISABLE_NATIVE_ADDON=1 node --experimental-strip-types --test --test-name-pattern="probes upstream" tests/integration/live-index-symbol-fts-crash.test.ts
fi
fi
publish:
name: Publish npm Packages
runs-on: ubuntu-latest
needs:
- resolve-version
- build-native
- verify-packed-install
if: needs.resolve-version.outputs.should_publish == 'true'
env:
VERSION: ${{ needs.resolve-version.outputs.version }}
NPM_DIST_TAG: ${{ needs.resolve-version.outputs.npm_dist_tag }}
DRY_RUN: ${{ needs.resolve-version.outputs.dry_run }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24.x
registry-url: https://registry.npmjs.org
cache: npm
- name: Refresh package-lock for release publish
run: npm install --package-lock-only --ignore-scripts --legacy-peer-deps
- name: Install dependencies
run: npm ci --ignore-scripts --legacy-peer-deps
- name: Setup tree-sitter
uses: ./.github/actions/setup-tree-sitter
- name: Set package versions from release
shell: bash
run: |
set -euo pipefail
npm version "${VERSION}" --no-git-tag-version --allow-same-version
node scripts/sync-native-version.mjs "${VERSION}"
- name: Validate package version sync
shell: bash
run: |
set -euo pipefail
node -e "
const fs = require('fs');
const path = require('path');
const expected = process.env.VERSION;
const pkg = (p) => JSON.parse(fs.readFileSync(p, 'utf8'));
const root = pkg('package.json');
const native = pkg(path.join('native', 'package.json'));
const createPackage = pkg(path.join('packages', 'create-sdl-mcp', 'package.json'));
if (root.version !== expected) throw new Error('package.json version mismatch');
if (createPackage.version !== expected) throw new Error('packages/create-sdl-mcp/package.json version mismatch');
for (const depName of ['sdl-mcp-native', 'sdl-mcp-watchman', 'sdl-mcp-watchman-linux-x64', 'sdl-mcp-watchman-win32-x64']) {
if ((root.optionalDependencies || {})[depName] !== expected) {
throw new Error('package.json optionalDependencies.' + depName + ' mismatch');
}
}
if (native.version !== expected) throw new Error('native/package.json version mismatch');
for (const [name, depVersion] of Object.entries(native.optionalDependencies || {})) {
if (depVersion !== expected) throw new Error('native optional dependency mismatch: ' + name);
}
for (const dir of fs.readdirSync(path.join('native', 'npm'))) {
const p = path.join('native', 'npm', dir, 'package.json');
if (!fs.existsSync(p)) continue;
const platformPkg = pkg(p);
if (platformPkg.version !== expected) {
throw new Error('platform package version mismatch: ' + platformPkg.name);
}
}
const watchman = pkg(path.join('watchman', 'package.json'));
if (watchman.version !== expected) throw new Error('watchman/package.json version mismatch');
for (const [name, depVersion] of Object.entries(watchman.optionalDependencies || {})) {
if (depVersion !== expected) throw new Error('watchman optional dependency mismatch: ' + name);
}
for (const dir of fs.readdirSync(path.join('watchman', 'npm'))) {
const p = path.join('watchman', 'npm', dir, 'package.json');
if (!fs.existsSync(p)) continue;
const platformPkg = pkg(p);
if (platformPkg.version !== expected) {
throw new Error('Watchman platform package version mismatch: ' + platformPkg.name);
}
}
console.log('Version sync validation passed for ' + expected);
"
- name: Download native artifacts
uses: actions/download-artifact@v7
with:
path: native/artifacts
- name: Place artifacts into platform npm packages
shell: bash
run: |
set -euo pipefail
declare -A TARGET_TO_DIR=(
["x86_64-pc-windows-msvc"]="win32-x64-msvc"
["x86_64-apple-darwin"]="darwin-x64"
["aarch64-apple-darwin"]="darwin-arm64"
["x86_64-unknown-linux-gnu"]="linux-x64-gnu"
["x86_64-unknown-linux-musl"]="linux-x64-musl"
["aarch64-unknown-linux-gnu"]="linux-arm64-gnu"
)
declare -A TARGET_TO_FILE=(
["x86_64-pc-windows-msvc"]="sdl-mcp-native.win32-x64-msvc.node"
["x86_64-apple-darwin"]="sdl-mcp-native.darwin-x64.node"
["aarch64-apple-darwin"]="sdl-mcp-native.darwin-arm64.node"
["x86_64-unknown-linux-gnu"]="sdl-mcp-native.linux-x64-gnu.node"
["x86_64-unknown-linux-musl"]="sdl-mcp-native.linux-x64-musl.node"
["aarch64-unknown-linux-gnu"]="sdl-mcp-native.linux-arm64-gnu.node"
)
for target in "${!TARGET_TO_DIR[@]}"; do
src_dir="native/artifacts/bindings-${target}"
dst_dir="native/npm/${TARGET_TO_DIR[${target}]}"
dst_file="${TARGET_TO_FILE[${target}]}"
src_file=$(find "${src_dir}" -name "*.node" -type f | head -n 1 || true)
if [[ -z "${src_file}" ]]; then
echo "Missing native artifact for ${target} in ${src_dir}"
exit 1
fi
cp "${src_file}" "${dst_dir}/${dst_file}"
echo "Copied ${src_file} -> ${dst_dir}/${dst_file}"
done
- name: Verify platform package binaries exist
shell: bash
run: |
set -euo pipefail
for dir in native/npm/*/; do
pkg_name=$(node -e "console.log(require('./${dir}package.json').name)")
node_count=$(find "${dir}" -name "*.node" -type f | wc -l)
if [[ "${node_count}" -eq 0 ]]; then
echo "Missing .node binary for ${pkg_name}"
exit 1
fi
echo "OK ${pkg_name}: ${node_count} binary file(s)"
done
- name: Publish platform native packages
shell: bash
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
FAILED=0
for dir in native/npm/*/; do
pkg_name=$(node -e "console.log(require('./${dir}package.json').name)")
echo "Publishing ${pkg_name}@${VERSION} (${NPM_DIST_TAG})"
existing=$(npm view "${pkg_name}@${VERSION}" version 2>/dev/null || true)
if [[ "${existing}" == "${VERSION}" ]]; then
echo "SKIP ${pkg_name}@${VERSION} already exists"
continue
fi
if [[ "${DRY_RUN}" == "true" ]]; then
npm publish "${dir}" --access public --tag "${NPM_DIST_TAG}" --dry-run
continue
fi
if npm publish "${dir}" --access public --tag "${NPM_DIST_TAG}" 2>&1; then
echo "OK ${pkg_name}@${VERSION} published"
else
# Tolerate "already published" errors (race with publish-native workflow)
recheck=$(npm view "${pkg_name}@${VERSION}" version 2>/dev/null || true)
if [[ "${recheck}" == "${VERSION}" ]]; then
echo "SKIP ${pkg_name}@${VERSION} already published (confirmed after retry)"
else
echo "FAIL ${pkg_name}@${VERSION} publish failed"
FAILED=1
fi
fi
done
if [[ "${FAILED}" -eq 1 ]]; then
echo "ERROR: Some platform packages failed to publish"
exit 1
fi
- name: Publish native umbrella package
shell: bash
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
echo "Publishing sdl-mcp-native@${VERSION} (${NPM_DIST_TAG})"
existing=$(npm view "sdl-mcp-native@${VERSION}" version 2>/dev/null || true)
if [[ "${existing}" == "${VERSION}" ]]; then
echo "SKIP sdl-mcp-native@${VERSION} already exists"
exit 0
fi
if [[ "${DRY_RUN}" == "true" ]]; then
npm publish native/ --access public --tag "${NPM_DIST_TAG}" --dry-run
exit 0
fi
if npm publish native/ --access public --tag "${NPM_DIST_TAG}" --ignore-scripts 2>&1; then
echo "OK sdl-mcp-native@${VERSION} published"
else
recheck=$(npm view "sdl-mcp-native@${VERSION}" version 2>/dev/null || true)
if [[ "${recheck}" == "${VERSION}" ]]; then
echo "SKIP sdl-mcp-native@${VERSION} already published (confirmed after retry)"
else
echo "FAIL sdl-mcp-native@${VERSION} publish failed"
exit 1
fi
fi
- name: Prepare Watchman platform packages
run: node scripts/prepare-watchman-packages.mjs
- name: Verify Watchman package binaries exist
shell: bash
run: |
set -euo pipefail
for dir in watchman/npm/*/; do
pkg_name=$(node -e "console.log(require('./${dir}package.json').name)")
binary_path=$(node -e "console.log(require('./${dir}package.json').sdlMcp.watchmanBinary)")
if [[ ! -f "${dir}${binary_path}" ]]; then
echo "Missing Watchman binary for ${pkg_name}: ${dir}${binary_path}"
exit 1
fi
echo "OK ${pkg_name}: ${binary_path}"
done
- name: Publish platform Watchman packages
shell: bash
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
FAILED=0
for dir in watchman/npm/*/; do
pkg_name=$(node -e "console.log(require('./${dir}package.json').name)")
echo "Publishing ${pkg_name}@${VERSION} (${NPM_DIST_TAG})"
existing=$(npm view "${pkg_name}@${VERSION}" version 2>/dev/null || true)
if [[ "${existing}" == "${VERSION}" ]]; then
echo "SKIP ${pkg_name}@${VERSION} already exists"
continue
fi
if [[ "${DRY_RUN}" == "true" ]]; then
npm publish "${dir}" --access public --tag "${NPM_DIST_TAG}" --dry-run
continue
fi
if npm publish "${dir}" --access public --tag "${NPM_DIST_TAG}" 2>&1; then
echo "OK ${pkg_name}@${VERSION} published"
else
recheck=$(npm view "${pkg_name}@${VERSION}" version 2>/dev/null || true)
if [[ "${recheck}" == "${VERSION}" ]]; then
echo "SKIP ${pkg_name}@${VERSION} already published (confirmed after retry)"
else
echo "FAIL ${pkg_name}@${VERSION} publish failed"
FAILED=1
fi
fi
done
if [[ "${FAILED}" -eq 1 ]]; then
echo "ERROR: Some Watchman platform packages failed to publish"
exit 1
fi
- name: Publish Watchman umbrella package
shell: bash
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
echo "Publishing sdl-mcp-watchman@${VERSION} (${NPM_DIST_TAG})"
existing=$(npm view "sdl-mcp-watchman@${VERSION}" version 2>/dev/null || true)
if [[ "${existing}" == "${VERSION}" ]]; then
echo "SKIP sdl-mcp-watchman@${VERSION} already exists"
exit 0
fi
if [[ "${DRY_RUN}" == "true" ]]; then
npm publish watchman/ --access public --tag "${NPM_DIST_TAG}" --dry-run
exit 0
fi
if npm publish watchman/ --access public --tag "${NPM_DIST_TAG}" --ignore-scripts 2>&1; then
echo "OK sdl-mcp-watchman@${VERSION} published"
else
recheck=$(npm view "sdl-mcp-watchman@${VERSION}" version 2>/dev/null || true)
if [[ "${recheck}" == "${VERSION}" ]]; then
echo "SKIP sdl-mcp-watchman@${VERSION} already published (confirmed after retry)"
else
echo "FAIL sdl-mcp-watchman@${VERSION} publish failed"
exit 1
fi
fi
- name: Build runtime package
run: npm run build:all
- name: Publish main package
shell: bash
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
echo "Publishing sdl-mcp@${VERSION} (${NPM_DIST_TAG})"
existing=$(npm view "sdl-mcp@${VERSION}" version 2>/dev/null || true)
if [[ "${existing}" == "${VERSION}" ]]; then
echo "SKIP sdl-mcp@${VERSION} already exists"
exit 0
fi
if [[ "${DRY_RUN}" == "true" ]]; then
npm publish . --access public --tag "${NPM_DIST_TAG}" --dry-run
exit 0
fi
if npm publish . --access public --tag "${NPM_DIST_TAG}" 2>&1; then
echo "OK sdl-mcp@${VERSION} published"
else
recheck=$(npm view "sdl-mcp@${VERSION}" version 2>/dev/null || true)
if [[ "${recheck}" == "${VERSION}" ]]; then
echo "SKIP sdl-mcp@${VERSION} already published (confirmed after retry)"
else
echo "FAIL sdl-mcp@${VERSION} publish failed"
exit 1
fi
fi
- name: Publish create-sdl-mcp wrapper package
shell: bash
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
echo "Publishing create-sdl-mcp@${VERSION} (${NPM_DIST_TAG})"
existing=$(npm view "create-sdl-mcp@${VERSION}" version 2>/dev/null || true)
if [[ "${existing}" == "${VERSION}" ]]; then
echo "SKIP create-sdl-mcp@${VERSION} already exists"
exit 0
fi
if [[ "${DRY_RUN}" == "true" ]]; then
npm publish packages/create-sdl-mcp/ --access public --tag "${NPM_DIST_TAG}" --dry-run
exit 0
fi
if npm publish packages/create-sdl-mcp/ --access public --tag "${NPM_DIST_TAG}" 2>&1; then
echo "OK create-sdl-mcp@${VERSION} published"
else
recheck=$(npm view "create-sdl-mcp@${VERSION}" version 2>/dev/null || true)
if [[ "${recheck}" == "${VERSION}" ]]; then
echo "SKIP create-sdl-mcp@${VERSION} already published (confirmed after retry)"
else
echo "FAIL create-sdl-mcp@${VERSION} publish failed"
exit 1
fi
fi