name: Publish to PyPI and npm
on:
release:
types: [published]
workflow_dispatch:
inputs:
publish_pypi:
description: 'Publish to PyPI'
required: true
default: true
type: boolean
publish_npm:
description: 'Publish to npm'
required: true
default: true
type: boolean
permissions:
contents: read
id-token: write # Required for PyPI trusted publishing
jobs:
test-mcp:
name: Test MCP Server
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Test MCP server starts
run: |
timeout 10 python pomera_mcp_server.py --list-tools || true
echo "MCP server tool listing completed"
- name: Verify mcp.json exists
run: |
if [ ! -f "mcp.json" ]; then
echo "ERROR: mcp.json manifest is missing!"
exit 1
fi
echo "✅ mcp.json manifest found"
cat mcp.json
- name: Verify llms.txt exists
run: |
if [ ! -f "llms.txt" ]; then
echo "⚠️ WARNING: llms.txt is missing (recommended for AI discoverability)"
else
echo "✅ llms.txt found"
fi
build-pypi:
name: Build PyPI Package
runs-on: ubuntu-latest
needs: test-mcp
steps:
- uses: actions/checkout@v4
- name: Get version from release tag
id: version
run: |
if [ "${{ github.event_name }}" == "release" ]; then
# Strip 'v' prefix from tag (v1.0.0 -> 1.0.0)
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "📦 Building version: $VERSION"
else
# Use version from pyproject.toml for manual runs
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "📦 Building version: $VERSION (from pyproject.toml)"
fi
# Note: File modifications removed - they cause uncommitted changes
# which make setuptools_scm generate .dev0 versions
# - package.json is updated by bump_version.py before tagging
# - pyproject.toml version is dynamic (uses setuptools_scm)
# - mcp.json version is updated by publish-mcp-registry job
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install build twine
- name: Build package
run: python -m build
- name: Check package
run: twine check dist/*
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: pypi-dist
path: dist/
- name: Upload version files
uses: actions/upload-artifact@v4
with:
name: version-files
path: |
pyproject.toml
package.json
mcp.json
publish-pypi:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: build-pypi
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.publish_pypi)
environment:
name: pypi
url: https://pypi.org/p/pomera-ai-commander
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: pypi-dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
# Uses trusted publishing (OIDC) - no API token needed
# Configure at: https://pypi.org/manage/project/pomera-ai-commander/settings/publishing/
skip-existing: true
publish-npm:
name: Publish to npm
runs-on: ubuntu-latest
needs: build-pypi # Wait for build to complete (includes version updates)
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.publish_npm)
steps:
- uses: actions/checkout@v4
- name: Download version files
uses: actions/download-artifact@v4
with:
name: version-files
path: .
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Verify package.json
run: |
if [ ! -f "package.json" ]; then
echo "ERROR: package.json is missing!"
exit 1
fi
echo "✅ package.json found"
echo "Version: $(jq -r '.version' package.json)"
- name: Verify bin script exists
run: |
if [ ! -f "bin/pomera-ai-commander.js" ]; then
echo "ERROR: bin/pomera-ai-commander.js is missing!"
exit 1
fi
echo "✅ bin script found"
- name: Generate version file from tag
run: |
# Extract clean version from git tag (strip 'v' prefix)
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#v}"
echo "Generating pomera/_version.py for npm package: ${VERSION}"
# Create properly formatted _version.py
mkdir -p pomera
cat > pomera/_version.py << EOF
# AUTO-GENERATED by setuptools_scm - DO NOT EDIT
__version__ = "${VERSION}"
EOF
# Verify the generated file
echo "Generated pomera/_version.py:"
cat pomera/_version.py
# Set up Python to test import
if command -v python3 > /dev/null 2>&1; then
python3 -c "import sys; sys.path.insert(0, '.'); from pomera._version import __version__; print(f'✅ Version verified: {__version__}')"
else
echo "✅ Version file created (Python verification skipped)"
fi
- name: Publish to npm
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
verify-publication:
name: Verify Publications
runs-on: ubuntu-latest
needs: [publish-pypi, publish-npm]
if: always() && (needs.publish-pypi.result == 'success' || needs.publish-npm.result == 'success')
steps:
- name: Wait for package availability
run: sleep 60
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Verify PyPI installation
if: needs.publish-pypi.result == 'success'
run: |
pip install pomera-ai-commander --upgrade
python -c "import pomera_mcp_server; print('✅ PyPI package works!')"
- name: Verify npm installation
if: needs.publish-npm.result == 'success'
run: |
npm install -g pomera-ai-commander
echo "✅ npm package installed successfully!"
publish-mcp-registry:
name: Publish to MCP Registry
runs-on: ubuntu-latest
needs: [publish-pypi, publish-npm]
if: github.event_name == 'release'
permissions:
id-token: write # Required for OIDC authentication
contents: read
steps:
- uses: actions/checkout@v4
- name: Download version files
uses: actions/download-artifact@v4
with:
name: version-files
path: .
- name: Update server.json version
run: |
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#v}"
# Update both the server version and package versions
jq --arg v "$VERSION" '.version = $v | .packages[0].version = $v | .packages[1].version = $v' server.json > server.json.tmp && mv server.json.tmp server.json
echo "Updated server.json to version $VERSION"
cat 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
chmod +x mcp-publisher
- name: Authenticate to MCP Registry
run: ./mcp-publisher login github-oidc
- name: Wait for PyPI package availability
run: |
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#v}"
echo "Waiting for PyPI to propagate version $VERSION..."
# Retry up to 10 times with 30 second delays (5 minutes total)
for i in {1..10}; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/pomera-ai-commander/$VERSION/json")
if [ "$HTTP_CODE" = "200" ]; then
echo "✓ PyPI package $VERSION is available!"
exit 0
fi
echo "Attempt $i/10: PyPI returned $HTTP_CODE, waiting 30 seconds..."
sleep 30
done
echo "✗ PyPI package not available after 5 minutes"
exit 1
- name: Publish server to MCP Registry
run: ./mcp-publisher publish