name: Release # 하나의 버전 태그(vX.Y.Z)에서 레인별로 게시한다(SPEC.md §2): # - NuGet 도구(dnx): C# .NET tool 패키지(TableCloth.Mcp) -> nuget.org [OIDC] # - npm(npx): Node 순수 JS 패키지(tablecloth-mcp) -> npmjs.org [OIDC, provenance] # - .mcpb: Node 번들 기반 Claude Desktop 확장 -> GitHub Release 첨부 # - MCP Registry: server.json 메타데이터 게시 [GitHub OIDC] # npm 은 더 이상 OS별 네이티브 바이너리를 담지 않는다(순수 JS). 구 플랫폼 패키지는 npm deprecate 로 정리. on: push: tags: ['v*'] workflow_dispatch: inputs: version: description: 'Version to release (e.g. 0.3.0), no leading v' required: true type: string permissions: contents: read jobs: meta: runs-on: ubuntu-latest outputs: version: ${{ steps.v.outputs.version }} steps: - id: v shell: bash run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then V="${{ inputs.version }}" else V="${GITHUB_REF_NAME#v}" fi if ! echo "$V" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+'; then echo "::error::invalid version '$V' (expected X.Y.Z)"; exit 1 fi echo "version=$V" >> "$GITHUB_OUTPUT" echo "Release version: $V" # ---- NuGet 도구(dnx): C# .NET tool (Trusted Publishing / OIDC) ---- nuget: needs: meta runs-on: ubuntu-latest environment: production permissions: id-token: write # OIDC 토큰 발급(신뢰 게시). 없으면 login 이 조용히 실패. contents: read steps: - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 with: dotnet-version: '10.0.x' - name: Pack .NET tool run: dotnet pack -c Release -p:Version=${{ needs.meta.outputs.version }} -o out - name: NuGet trusted publishing login uses: NuGet/login@v1 id: nuget-login with: user: ${{ secrets.NUGET_USER }} - name: Push to NuGet run: dotnet nuget push "out/*.nupkg" --api-key "${{ steps.nuget-login.outputs.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate - name: Upload nupkg artifact uses: actions/upload-artifact@v4 with: name: nupkg path: out/*.nupkg if-no-files-found: error # ---- npm(npx): Node 순수 JS 패키지 (Trusted Publishing / OIDC, provenance) ---- npm: needs: meta runs-on: ubuntu-latest environment: production permissions: id-token: write # OIDC 신뢰 게시 — 장기 npm 토큰 불필요, provenance 자동. contents: read steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '24' registry-url: 'https://registry.npmjs.org' # OIDC 신뢰 게시는 npm CLI >= 11.5.1 필요. - name: Ensure npm supports OIDC trusted publishing run: npm install -g npm@latest - name: Build pure-JS bundle working-directory: node run: | npm ci npm run build - name: Stamp version working-directory: node run: npm pkg set version="${{ needs.meta.outputs.version }}" - name: Publish (OIDC) working-directory: node run: npm publish --access public # ---- .mcpb: Node 번들 기반 Claude Desktop 확장 ---- # 순수 JS(server/index.js) + icon + manifest. Claude Desktop 번들 node 로 실행 → 크로스플랫폼, # 네이티브 바이너리 없음(자기잠금/미서명/Gatekeeper 이슈 없음). mcpb: needs: meta runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '24' - name: Build node bundle working-directory: node run: | npm ci npm run build - name: Assemble bundle dir shell: bash run: | set -e VER="${{ needs.meta.outputs.version }}" mkdir -p mcpb-build/server cp node/dist/index.js mcpb-build/server/index.js cp mcpb/icon.png mcpb-build/icon.png jq --arg v "$VER" '.version=$v' mcpb/manifest.json > mcpb-build/manifest.json cp LICENSE-AGPL LICENSE-COMMERCIAL mcpb-build/ 2>/dev/null || true - name: Pack .mcpb run: | npm install -g @anthropic-ai/mcpb mcpb pack mcpb-build tablecloth-mcp.mcpb - name: Upload .mcpb uses: actions/upload-artifact@v4 with: name: mcpb path: tablecloth-mcp.mcpb if-no-files-found: error # ---- 공식 MCP Registry 메타데이터 게시 (GitHub OIDC, 시크릿 불필요) ---- registry: needs: [meta, npm] if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - uses: actions/checkout@v4 - name: Sync server.json version run: | VER="${{ needs.meta.outputs.version }}" jq --arg v "$VER" '.version=$v | .packages[0].version=$v' server.json > server.tmp && mv server.tmp server.json - name: Install mcp-publisher run: curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher - name: Authenticate to MCP Registry (GitHub OIDC) run: ./mcp-publisher login github-oidc - name: Publish to MCP Registry run: ./mcp-publisher publish # ---- GitHub Release (태그 푸시에만): nupkg + .mcpb 첨부 + 릴리스 노트 ---- github-release: needs: [meta, nuget, npm, mcpb] if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 - name: Download all build artifacts uses: actions/download-artifact@v4 with: path: artifacts - name: Stage release assets shell: bash run: | set -e mkdir -p release-assets cp artifacts/nupkg/*.nupkg release-assets/ 2>/dev/null || true cp artifacts/mcpb/*.mcpb release-assets/ 2>/dev/null || true ls -la release-assets - name: Create or update GitHub Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} shell: bash run: | set -e TAG="${GITHUB_REF_NAME}" VER="${{ needs.meta.outputs.version }}" REPO="${{ github.repository }}" # CHANGELOG.md 에서 이 버전 섹션만 뽑아 릴리스 노트로(없으면 자동 생성 폴백). awk -v ver="$VER" ' index($0, "## [" ver "]")==1 { flag=1; next } flag && index($0, "## [")==1 { exit } flag { print } ' CHANGELOG.md | sed "/./,\$!d" > notes.md if gh release view "$TAG" --repo "$REPO" >/dev/null 2>&1; then gh release upload "$TAG" release-assets/* --repo "$REPO" --clobber [ -s notes.md ] && gh release edit "$TAG" --repo "$REPO" --notes-file notes.md || true elif [ -s notes.md ]; then gh release create "$TAG" release-assets/* --repo "$REPO" --title "$TAG" --notes-file notes.md else gh release create "$TAG" release-assets/* --repo "$REPO" --title "$TAG" --generate-notes fi