diff --git a/.github/workflows/pypi-publish-on-release.yml b/.github/workflows/pypi-publish-on-release.yml new file mode 100644 index 0000000..d7567c9 --- /dev/null +++ b/.github/workflows/pypi-publish-on-release.yml @@ -0,0 +1,73 @@ +name: Publish to PyPI + +on: + release: + types: + - published + +permissions: {} + +jobs: + call-test: + permissions: + contents: read + pull-requests: read + security-events: write + actions: read + uses: ./.github/workflows/python.yml + with: + ref: ${{ github.event.release.target_commitish }} + + call-integ-tests: + permissions: + id-token: write + contents: read + actions: read + uses: ./.github/workflows/python-integ.yml + with: + ref: ${{ github.event.release.target_commitish }} + secrets: inherit + + build: + needs: [call-test, call-integ-tests] + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up uv + uses: astral-sh/setup-uv@v4 + + - name: Build distribution packages + run: uv build + + - name: Upload distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/mcp-proxy-for-aws + permissions: + id-token: write + steps: + - name: Download distribution packages + uses: actions/download-artifact@v5 + with: + name: python-package-distributions + path: dist/ + + - name: Set up uv + uses: astral-sh/setup-uv@v4 + + - name: Publish to PyPI + run: uv publish diff --git a/.github/workflows/python-integ.yml b/.github/workflows/python-integ.yml index 43d454d..a6302c3 100644 --- a/.github/workflows/python-integ.yml +++ b/.github/workflows/python-integ.yml @@ -2,10 +2,15 @@ name: Python Integration Tests on: - workflow_call: workflow_dispatch: push: branches: main + workflow_call: + inputs: + ref: + description: 'Git ref to checkout' + required: false + type: string jobs: integration: @@ -24,6 +29,8 @@ jobs: steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + ref: ${{ inputs.ref || github.ref }} - name: Install uv uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 20b1dee..fbc2384 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -4,6 +4,12 @@ on: push: pull_request: workflow_dispatch: + workflow_call: + inputs: + ref: + description: 'Git ref to checkout' + required: false + type: string permissions: {} @@ -25,6 +31,8 @@ jobs: actions: read steps: - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + ref: ${{ inputs.ref || github.ref }} - name: Install uv uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 diff --git a/.github/workflows/test-pypi-publish.yml b/.github/workflows/test-pypi-publish.yml new file mode 100644 index 0000000..3ced2fd --- /dev/null +++ b/.github/workflows/test-pypi-publish.yml @@ -0,0 +1,71 @@ +name: Test PyPI Publishing + +on: + workflow_dispatch: + +permissions: {} + +jobs: + call-test: + permissions: + contents: read + pull-requests: read + security-events: write + actions: read + with: + ref: ${{ github.event.release.target_commitish }} + uses: ./.github/workflows/python.yml + + call-integ-tests: + permissions: + id-token: write + contents: read + actions: read + uses: ./.github/workflows/python-integ.yml + with: + ref: ${{ github.event.release.target_commitish }} + secrets: inherit + + build: + needs: [call-test, call-integ-tests] + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Set up uv + uses: astral-sh/setup-uv@v4 + + - name: Build distribution packages + run: uv build + + - name: Upload distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + deploy-test: + needs: build + runs-on: ubuntu-latest + environment: + name: testpypi + url: https://test.pypi.org/p/mcp-proxy-for-aws + permissions: + id-token: write + steps: + - name: Download distribution packages + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + - name: Set up uv + uses: astral-sh/setup-uv@v4 + + - name: Publish to TestPyPI + run: uv publish --publish-url https://test.pypi.org/legacy/ diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 39cf77c..7a33dda 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -379,12 +379,96 @@ export LOG_LEVEL=DEBUG uv run mcp_proxy_for_aws/server.py ``` +## Releasing to PyPI + +The project uses automated PyPI publishing through GitHub Actions. Releases are triggered by creating a GitHub Release. + +### Release Process + +1. **Ensure all changes are merged to main branch** + ```bash + git checkout main + git pull origin main + ``` + +2. **Create GitHub Release** + + Go to the [Releases page](https://github.com/aws/mcp-proxy-for-aws/releases) and click "Draft a new release", then fill in: + + - **Tag**: `v1.10.0` (must start with 'v' and follow semantic versioning) + - **Target**: `main` + - **Title**: `v1.10.0` + - **Description**: Click "Generate release notes" for auto-generated notes + + Click **"Publish release"** (not "Save draft") + +3. **Automated Publishing** + + Once the release is published, GitHub Actions will automatically: + - Run all tests and linting checks + - Build distribution packages (wheel and source) + - Publish to PyPI using Trusted Publishing + + Monitor the workflow at: [Actions tab](https://github.com/aws/mcp-proxy-for-aws/actions) + +### Version Numbering + +Follow [Semantic Versioning](https://semver.org/): +- **MAJOR** (v2.0.0): Breaking changes +- **MINOR** (v1.10.0): New features, backward compatible +- **PATCH** (v1.10.1): Bug fixes, backward compatible + +Version is managed in `pyproject.toml`: `version = "1.10.0"` + +Use Commitizen to bump versions automatically: +```bash +# Bump version based on conventional commits +uv run cz bump + +# This will update both files and create a git tag +``` + +### Testing Releases + +Before creating a production release, you can test with TestPyPI: + +```bash +# Create a test tag +git tag v1.10.0-beta +git push origin v1.10.0-beta + +# This triggers the TestPyPI workflow +# Monitor at: https://github.com/aws/mcp-proxy-for-aws/actions +``` + +### Troubleshooting Releases + +**Release workflow failed:** +- Check the Actions tab for error details +- Ensure all tests pass locally: `uv run pytest` +- Verify the tag follows semantic versioning format + +**Version already exists on PyPI:** +- PyPI doesn't allow re-uploading the same version +- Create a new patch version (e.g., v1.10.1) +- Delete and recreate the tag if needed: + ```bash + git tag -d v1.10.0 + git push origin --delete v1.10.0 + ``` + +**Trusted Publishing authentication failed:** +- Verify PyPI Trusted Publisher is configured correctly +- Check that the workflow file name matches the PyPI configuration +- Ensure the environment name is set to `pypi` + ## Additional Resources - [MCP Specification](https://spec.modelcontextprotocol.io/) - [FastMCP Documentation](https://fastmcp.readthedocs.io/) - [AWS SDK for Python (Boto3)](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html) - [Project README](README.md) +- [PyPI Publishing Guide](https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/) ---