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
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ Keep PRs focused — one logical change per PR.
- [ ] `ruff format .` applied
- [ ] `mypy src` passes
- [ ] `pytest` passes (new behavior is covered by tests)
- [ ] `CHANGELOG.md` updated for user-facing changes
- [ ] PR title / commits follow [Conventional Commits](https://www.conventionalcommits.org/) (drives versioning & changelog)
- [ ] Docs / docstrings updated if the public API changed
54 changes: 44 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,54 @@
name: Release

# Publishes to PyPI when a GitHub Release is published. Uses PyPI Trusted
# Publishing (OIDC) — no API tokens or secrets are stored in the repo.
# One-time setup: add a trusted publisher on PyPI for this repo + the
# `release.yml` workflow + the `pypi` environment.
# https://docs.pypi.org/trusted-publishers/
# Automated releases via release-please + PyPI Trusted Publishing.
#
# Flow:
# 1. Pushes to main run release-please, which maintains a "release PR" that
# bumps the version (src/senderkit/_version.py) and CHANGELOG.md from the
# Conventional Commit history.
# 2. Merging that release PR tags the version + creates the GitHub Release and,
# in the same run, builds and publishes to PyPI.
#
# Everything stays in ONE workflow run so it does not depend on the GitHub
# Release event (events raised by GITHUB_TOKEN do not trigger other workflows).
#
# One-time repo setup:
# - Settings → Actions → General → Workflow permissions:
# enable "Allow GitHub Actions to create and approve pull requests".
# - A PyPI Trusted Publisher for this repo → workflow "release.yml" →
# environment "pypi". https://docs.pypi.org/trusted-publishers/

on:
release:
types: [published]
push:
branches: [main]

permissions:
contents: read

jobs:
release-please:
name: Release please
runs-on: ubuntu-latest
permissions:
contents: write # create tags + GitHub releases
pull-requests: write # open/update the release PR
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

build:
name: Build distributions
needs: release-please
if: needs.release-please.outputs.release_created == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
Expand All @@ -28,9 +60,9 @@ jobs:
run: python -m build
- name: Check metadata
run: twine check dist/*
- name: Verify version matches release tag
- name: Verify built version matches the release tag
env:
TAG: ${{ github.event.release.tag_name }}
TAG: ${{ needs.release-please.outputs.tag_name }}
run: |
VERSION="$(python -c 'import senderkit; print(senderkit.__version__)')"
echo "Package version: $VERSION"
Expand All @@ -46,12 +78,14 @@ jobs:

publish:
name: Publish to PyPI
needs: build
needs: [release-please, build]
if: needs.release-please.outputs.release_created == 'true'
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/senderkit
permissions:
contents: read
id-token: write # required for Trusted Publishing
steps:
- uses: actions/download-artifact@v8
Expand Down
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.0.0"
}
27 changes: 19 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,28 @@ pytest --cov=senderkit
`mypy`-clean.
- **Style.** `ruff` handles linting and formatting (100-char lines). Match the
surrounding code; the CI format gate is enforced.
- **Changelog.** Add an entry to `CHANGELOG.md` for any user-facing change.
- **Commits & PRs.** Keep PRs focused on one logical change. Write clear commit
messages explaining the *why*.
- **Commits & PRs.** Keep PRs focused on one logical change. Use
[Conventional Commit](https://www.conventionalcommits.org/) messages — the
version bump and changelog are derived from them, so the prefix matters:
- `fix:` → patch release, `feat:` → minor release.
- `feat!:` or a `BREAKING CHANGE:` footer → breaking change.
- `docs:`, `chore:`, `test:`, `refactor:`, `ci:` → no release on their own.
- On squash-merge GitHub uses the **PR title**, so keep it conventional too.
- **Changelog.** Generated automatically from commit messages — don't edit
`CHANGELOG.md` by hand.

## Releasing (maintainers)

1. Bump `VERSION` in `src/senderkit/_version.py` (single source of truth) and
update `CHANGELOG.md`.
2. Merge to `main`.
3. Create a GitHub Release with tag `vX.Y.Z` matching the new version.
4. The `Release` workflow builds and publishes to PyPI via Trusted Publishing.
Releases are automated with
[release-please](https://github.com/googleapis/release-please):

1. Merge PRs to `main` with Conventional Commit messages.
2. release-please maintains a **release PR** that bumps `VERSION` in
`src/senderkit/_version.py` (the single source of truth) and updates
`CHANGELOG.md`. Review and edit it as needed.
3. Merging the release PR tags `vX.Y.Z`, creates the GitHub Release, and the
`Release` workflow builds and publishes to PyPI via Trusted Publishing — all
in one run.

## Code of Conduct

Expand Down
11 changes: 11 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"packages": {
".": {
"release-type": "simple",
"package-name": "senderkit",
"changelog-path": "CHANGELOG.md",
"extra-files": ["src/senderkit/_version.py"]
}
}
}
2 changes: 1 addition & 1 deletion src/senderkit/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Single source of truth for the SDK version, surfaced in the User-Agent header."""

VERSION = "0.1.0"
VERSION = "0.1.0" # x-release-please-version