Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 28 additions & 34 deletions .github/PUBLISHING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,18 @@ This repository uses GitHub Actions to automatically publish packages to PyPI wh

## Setup Instructions

### 1. PyPI API Token
### 1. PyPI Trusted Publisher

1. Go to [PyPI Account Settings](https://pypi.org/manage/account/)
2. Scroll to "API tokens" section
3. Click "Add API token"
4. Set the token name (e.g., "rootly-python-github-actions")
5. Set the scope to "Entire account" or specific to this project
6. Copy the generated token (starts with `pypi-`)
The `rootly` project uses PyPI Trusted Publishing with these values:

### 2. GitHub Repository Secrets
- **Owner**: `rootlyhq`
- **Repository**: `rootly-python`
- **Workflow**: `publish.yml`
- **Environment**: `pypi`

1. Go to your GitHub repository
2. Navigate to Settings → Secrets and variables → Actions
3. Click "New repository secret"
4. Add the following secret:
- **Name**: `PYPI_API_TOKEN`
- **Value**: The PyPI API token from step 1
The GitHub `pypi` environment requires approval and only permits tags matching `v*`. The publishing job obtains a short-lived PyPI credential through OpenID Connect, so no long-lived PyPI token is stored in GitHub.

### 3. Publishing a New Version
### 2. Publishing a New Version

To publish a new version:

Expand All @@ -44,30 +37,31 @@ To publish a new version:
- Go to the "Actions" tab in your GitHub repository
- Watch the "Publish Python 🐍 distribution 📦 to PyPI on tag" workflow run
- The workflow will:
- Install build tools (uv, build, twine)
- Install build tools (uv and build)
- Test the SDK imports
- Build the package using Python build
- Publish to PyPI using twine
- Wait for approval on the `pypi` environment
- Publish to PyPI using a short-lived Trusted Publishing credential

### 4. Version Numbering
### 3. Version Numbering

Follow [Semantic Versioning](https://semver.org/):
- **MAJOR**: Incompatible API changes (e.g., `v2.0.0`)
- **MINOR**: New functionality, backward compatible (e.g., `v1.1.0`)
- **PATCH**: Bug fixes, backward compatible (e.g., `v1.0.1`)

### 5. Workflow Details
### 4. Workflow Details

The GitHub Action workflow (`.github/workflows/publish.yml`) will:

1. **Trigger**: On any tag push (pattern `*`)
2. **Environment**: Ubuntu latest with Python 3.12
3. **Dependencies**: Install uv, build, and twine
1. **Trigger**: On version tag pushes (pattern `v*`)
2. **Environment**: Blacksmith Ubuntu 24.04 runner with Python 3.12
3. **Dependencies**: Install uv and build
4. **Testing**: Verify SDK imports correctly
5. **Build**: Create distribution packages using Python build
6. **Publish**: Upload to PyPI using twine with API token
6. **Publish**: Upload to PyPI using Trusted Publishing after environment approval

### 6. Manual Publishing (Alternative)
### 5. Manual Publishing (Alternative)

If you need to publish manually:

Expand All @@ -85,19 +79,19 @@ poetry build
poetry publish
```

### 7. Testing on Test PyPI
### 6. Testing on Test PyPI

For testing the publishing process, you can use Test PyPI:

1. Create a Test PyPI account and API token
2. Add `PYPI_TEST_TOKEN` to GitHub secrets
3. Modify the workflow to use `--repository testpypi` flag
1. Create a TestPyPI account
2. Register a TestPyPI trusted publisher using a `testpypi` GitHub environment
3. Add a publishing job using `repository-url: https://test.pypi.org/legacy/`

### 8. Troubleshooting
### 7. Troubleshooting

**Common Issues:**

- **Permission denied**: Ensure the `PYPI_TOKEN` secret is correctly set
- **Permission denied**: Ensure the PyPI publisher values match the workflow and `pypi` environment exactly
- **Version conflict**: Make sure the version tag doesn't already exist on PyPI
- **Import errors**: Check that all dependencies are correctly specified in `pyproject.toml`
- **Build failures**: Verify the package structure and ensure all required files are included
Expand All @@ -109,7 +103,7 @@ For testing the publishing process, you can use Test PyPI:

## Security Notes

- Never commit PyPI tokens to the repository
- Use repository secrets for sensitive information
- Consider using environment-specific tokens for different deployment stages
- Regularly rotate API tokens for security
- Keep the build and publishing jobs separate
- Require approval on the production `pypi` environment
- Restrict production deployments to version tags
- Do not add a long-lived PyPI API token to GitHub
40 changes: 32 additions & 8 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,28 @@ on:
tags:
- 'v*'

permissions:
contents: read

jobs:
build-and-publish:
build:
name: Build distribution 📦
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'

- name: Install uv and build tools
- name: Install uv and build
run: |
pip install uv
uv pip install --system build twine
uv pip install --system build

- name: Install package dependencies
run: |
Expand All @@ -34,9 +40,27 @@ jobs:
run: |
python -m build

- name: Store distribution packages
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: python-package-distributions
path: dist/

publish-to-pypi:
name: Publish distribution 📦 to PyPI
needs: build
runs-on: blacksmith-2vcpu-ubuntu-2404
environment:
name: pypi
url: https://pypi.org/p/rootly
permissions:
id-token: write
steps:
- name: Download distribution packages
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: python-package-distributions
path: dist/

- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
twine upload dist/*
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1