v3.8.0 #53
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: Publish to npm | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: 'Dry run — skip npm publish steps' | |
| type: boolean | |
| default: false | |
| jobs: | |
| publish: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20.x | |
| registry-url: https://registry.npmjs.org | |
| cache: npm | |
| - run: npm ci | |
| - run: npm run verify:release-gate | |
| - name: Verify tag matches root CLI package.json version | |
| if: github.event_name == 'release' | |
| run: | | |
| PKG_VERSION=$(node -p "require('./package.json').version") | |
| TAG_VERSION="${GITHUB_REF_NAME#v}" | |
| if [ "$PKG_VERSION" != "$TAG_VERSION" ]; then | |
| echo "Tag $TAG_VERSION does not match root package.json version $PKG_VERSION" | |
| echo "Plugin versions are independent and read from packages/*/package.json directly." | |
| exit 1 | |
| fi | |
| - name: Show resolved versions | |
| run: | | |
| CLI_VERSION=$(node -p "require('./package.json').version") | |
| CODEX_VERSION=$(node -p "require('./packages/codex-plugin/package.json').version") | |
| OPENCLAW_VERSION=$(node -p "require('./packages/openclaw-skill/package.json').version") | |
| CLAUDE_CODE_VERSION=$(node -p "require('./packages/claude-code-plugin/package.json').version") | |
| GEMINI_VERSION=$(node -p "require('./packages/gemini-extension/package.json').version") | |
| echo "Root CLI: @switchbot/openapi-cli@$CLI_VERSION" | |
| echo "Codex plugin: @switchbot/codex-plugin@$CODEX_VERSION" | |
| echo "OpenClaw plugin: @switchbot/openclaw-skill@$OPENCLAW_VERSION" | |
| echo "Claude Code plugin: @switchbot/claude-code-plugin@$CLAUDE_CODE_VERSION" | |
| echo "Gemini extension: @switchbot/gemini-extension@$GEMINI_VERSION" | |
| { | |
| echo "cli_version=$CLI_VERSION" | |
| echo "codex_version=$CODEX_VERSION" | |
| echo "openclaw_version=$OPENCLAW_VERSION" | |
| echo "claude_code_version=$CLAUDE_CODE_VERSION" | |
| echo "gemini_version=$GEMINI_VERSION" | |
| } >> "$GITHUB_OUTPUT" | |
| id: versions | |
| - name: Detect which packages need publishing | |
| id: detect | |
| env: | |
| CLI_VERSION: ${{ steps.versions.outputs.cli_version }} | |
| CODEX_VERSION: ${{ steps.versions.outputs.codex_version }} | |
| OPENCLAW_VERSION: ${{ steps.versions.outputs.openclaw_version }} | |
| CLAUDE_CODE_VERSION: ${{ steps.versions.outputs.claude_code_version }} | |
| GEMINI_VERSION: ${{ steps.versions.outputs.gemini_version }} | |
| run: | | |
| # For each package, query npm; if the exact version is already published, skip. | |
| check_unpublished() { | |
| local name="$1" | |
| local version="$2" | |
| local found | |
| found=$(npm view "${name}@${version}" version 2>/dev/null || true) | |
| if [ "$found" = "$version" ]; then | |
| echo "false" | |
| else | |
| echo "true" | |
| fi | |
| } | |
| CLI_PUBLISH=$(check_unpublished "@switchbot/openapi-cli" "$CLI_VERSION") | |
| CODEX_PUBLISH=$(check_unpublished "@switchbot/codex-plugin" "$CODEX_VERSION") | |
| OPENCLAW_PUBLISH=$(check_unpublished "@switchbot/openclaw-skill" "$OPENCLAW_VERSION") | |
| CLAUDE_CODE_PUBLISH=$(check_unpublished "@switchbot/claude-code-plugin" "$CLAUDE_CODE_VERSION") | |
| GEMINI_PUBLISH=$(check_unpublished "@switchbot/gemini-extension" "$GEMINI_VERSION") | |
| echo "cli_publish=$CLI_PUBLISH" | |
| echo "codex_publish=$CODEX_PUBLISH" | |
| echo "openclaw_publish=$OPENCLAW_PUBLISH" | |
| echo "claude_code_publish=$CLAUDE_CODE_PUBLISH" | |
| echo "gemini_publish=$GEMINI_PUBLISH" | |
| { | |
| echo "cli_publish=$CLI_PUBLISH" | |
| echo "codex_publish=$CODEX_PUBLISH" | |
| echo "openclaw_publish=$OPENCLAW_PUBLISH" | |
| echo "claude_code_publish=$CLAUDE_CODE_PUBLISH" | |
| echo "gemini_publish=$GEMINI_PUBLISH" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Verify codex-plugin tarball peerDep is a concrete range | |
| if: steps.detect.outputs.codex_publish == 'true' | |
| run: | | |
| TARBALL=$(npm pack -w @switchbot/codex-plugin --pack-destination /tmp/ 2>&1 | tail -1) | |
| PEER=$(tar -xOzf "/tmp/$TARBALL" package/package.json | node -e " | |
| const p = JSON.parse(require('fs').readFileSync(0, 'utf8')); | |
| console.log(p.peerDependencies?.['@switchbot/openapi-cli'] || ''); | |
| ") | |
| if [ -z "$PEER" ] || echo "$PEER" | grep -q "workspace:"; then | |
| echo "FAIL: codex-plugin peerDep is missing or unrewritten workspace:* — got: '$PEER'" | |
| exit 1 | |
| fi | |
| echo "OK: codex-plugin peerDep = '$PEER'" | |
| rm -f "/tmp/$TARBALL" | |
| - name: Verify openclaw-skill tarball peerDep is a concrete range | |
| if: steps.detect.outputs.openclaw_publish == 'true' | |
| run: | | |
| TARBALL=$(npm pack -w @switchbot/openclaw-skill --pack-destination /tmp/ 2>&1 | tail -1) | |
| PEER=$(tar -xOzf "/tmp/$TARBALL" package/package.json | node -e " | |
| const p = JSON.parse(require('fs').readFileSync(0, 'utf8')); | |
| console.log(p.peerDependencies?.['@switchbot/openapi-cli'] || ''); | |
| ") | |
| if [ -z "$PEER" ] || echo "$PEER" | grep -q "workspace:"; then | |
| echo "FAIL: openclaw-skill peerDep is missing or unrewritten workspace:* — got: '$PEER'" | |
| exit 1 | |
| fi | |
| echo "OK: openclaw-skill peerDep = '$PEER'" | |
| rm -f "/tmp/$TARBALL" | |
| - name: Verify claude-code-plugin tarball peerDep is a concrete range | |
| if: steps.detect.outputs.claude_code_publish == 'true' | |
| run: | | |
| TARBALL=$(npm pack -w @switchbot/claude-code-plugin --pack-destination /tmp/ 2>&1 | tail -1) | |
| PEER=$(tar -xOzf "/tmp/$TARBALL" package/package.json | node -e " | |
| const p = JSON.parse(require('fs').readFileSync(0, 'utf8')); | |
| console.log(p.peerDependencies?.['@switchbot/openapi-cli'] || ''); | |
| ") | |
| if [ -z "$PEER" ] || echo "$PEER" | grep -q "workspace:"; then | |
| echo "FAIL: claude-code-plugin peerDep is missing or unrewritten workspace:* — got: '$PEER'" | |
| exit 1 | |
| fi | |
| echo "OK: claude-code-plugin peerDep = '$PEER'" | |
| rm -f "/tmp/$TARBALL" | |
| - name: Verify gemini-extension tarball peerDep is a concrete range | |
| if: steps.detect.outputs.gemini_publish == 'true' | |
| run: | | |
| TARBALL=$(npm pack -w @switchbot/gemini-extension --pack-destination /tmp/ 2>&1 | tail -1) | |
| PEER=$(tar -xOzf "/tmp/$TARBALL" package/package.json | node -e " | |
| const p = JSON.parse(require('fs').readFileSync(0, 'utf8')); | |
| console.log(p.peerDependencies?.['@switchbot/openapi-cli'] || ''); | |
| ") | |
| if [ -z "$PEER" ] || echo "$PEER" | grep -q "workspace:"; then | |
| echo "FAIL: gemini-extension peerDep is missing or unrewritten workspace:* — got: '$PEER'" | |
| exit 1 | |
| fi | |
| echo "OK: gemini-extension peerDep = '$PEER'" | |
| rm -f "/tmp/$TARBALL" | |
| - name: Publish root CLI to npm | |
| if: steps.detect.outputs.cli_publish == 'true' && inputs.dry_run != true | |
| run: npm publish --provenance --access public | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Publish codex-plugin to npm | |
| id: publish_codex | |
| if: steps.detect.outputs.codex_publish == 'true' && inputs.dry_run != true | |
| continue-on-error: true | |
| run: npm publish -w @switchbot/codex-plugin --provenance --access public | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Publish openclaw-skill to npm | |
| id: publish_openclaw | |
| if: steps.detect.outputs.openclaw_publish == 'true' && inputs.dry_run != true | |
| continue-on-error: true | |
| run: npm publish -w @switchbot/openclaw-skill --provenance --access public | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Publish claude-code-plugin to npm | |
| id: publish_claude_code | |
| if: steps.detect.outputs.claude_code_publish == 'true' && inputs.dry_run != true | |
| continue-on-error: true | |
| run: npm publish -w @switchbot/claude-code-plugin --provenance --access public | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Publish gemini-extension to npm | |
| id: publish_gemini | |
| if: steps.detect.outputs.gemini_publish == 'true' && inputs.dry_run != true | |
| continue-on-error: true | |
| run: npm publish -w @switchbot/gemini-extension --provenance --access public | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Annotate plugin publish failures | |
| if: steps.detect.outputs.codex_publish == 'true' && steps.publish_codex.outcome == 'failure' | |
| run: | | |
| echo "::warning::codex-plugin publish step failed; root CLI promotion is unaffected. Investigate before next release." | |
| - name: Annotate OpenClaw plugin publish failures | |
| if: steps.detect.outputs.openclaw_publish == 'true' && steps.publish_openclaw.outcome == 'failure' | |
| run: | | |
| echo "::warning::openclaw-skill publish step failed; root CLI promotion is unaffected. Investigate before next release." | |
| - name: Annotate Claude Code plugin publish failures | |
| if: steps.detect.outputs.claude_code_publish == 'true' && steps.publish_claude_code.outcome == 'failure' | |
| run: | | |
| echo "::warning::claude-code-plugin publish step failed; root CLI promotion is unaffected. Investigate before next release." | |
| - name: Annotate Gemini extension publish failures | |
| if: steps.detect.outputs.gemini_publish == 'true' && steps.publish_gemini.outcome == 'failure' | |
| run: | | |
| echo "::warning::gemini-extension publish step failed; root CLI promotion is unaffected. Investigate before next release." |