Skip to content
Open
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
95 changes: 95 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Release

# Triggered by pushing a tag like `v3.4.1`. See "Releasing" in README for the
# end-to-end flow and the one-time PyPI / GitHub setup this workflow expects.
on:
push:
tags: ["v*"]

# Don't cancel an in-flight publish — that could leave PyPI in a half-uploaded
# state. We only need to prevent two concurrent attempts for the same tag.
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
test:
name: Offline tests (gate)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: pyproject.toml
- run: make install
- run: make ci

build:
name: Build sdist + wheel
needs: test
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Verify tag matches pyproject.toml version
run: |
tag="${GITHUB_REF#refs/tags/v}"
pkg=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
if [ "$tag" != "$pkg" ]; then
echo "::error::Tag v$tag does not match pyproject.toml version $pkg"
exit 1
fi
echo "Tag and pyproject version match: $tag"

- run: python -m pip install --upgrade build
- run: python -m build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
if-no-files-found: error

publish-pypi:
name: Publish to PyPI
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
# The `pypi` environment is what PyPI's Trusted Publisher config is bound
# to. Add Required reviewers to this environment in repo Settings to gate
# each release on a human approval.
environment:
name: pypi
url: https://pypi.org/project/apexomni/
permissions:
id-token: write # mandatory for OIDC trusted publishing — no token needed
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1

github-release:
name: Create GitHub Release
needs: publish-pypi
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write # to create the GitHub Release
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- uses: softprops/action-gh-release@v2
with:
files: dist/*
generate_release_notes: true
fail_on_unmatched_files: true
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,42 @@ machine will automatically run `make test` first and refuse to push if
anything fails. CI runs the exact same `make test` target on every PR —
keeping the local and remote gates identical.

### Releasing

Releases publish to PyPI automatically when a `v*` tag is pushed. The
release workflow (`.github/workflows/release.yml`) reruns the test gate,
builds the wheel + sdist, publishes to PyPI via OIDC trusted publishing
(no long-lived token), and creates a GitHub Release with the artifacts.

To cut a release from `main`:

```bash
# 1. Bump version in pyproject.toml, open + merge a PR.
# 2. From the merged main commit:
git checkout main && git pull
git tag v3.4.1
git push origin v3.4.1
```

The workflow refuses to publish if the tag does not match
`pyproject.toml`'s `version`, and (if configured) waits on a Required
reviewer for the `pypi` GitHub Environment before uploading.

#### One-time setup (per project, per release target)

1. **PyPI** — go to https://pypi.org/manage/project/apexomni/settings/publishing/
and add a Trusted Publisher with:
- Owner: `ApeX-Protocol`
- Repository: `apexpro-openapi`
- Workflow filename: `release.yml`
- Environment name: `pypi`

2. **GitHub** — Settings → Environments → New environment named `pypi`:
- (Recommended) Add Required reviewers — every release waits for an
approval click before the PyPI upload step runs.
- (Recommended) Restrict deployment branches to `main` so a tag from
elsewhere can't trigger a release.

## Installation
`apex omni` supports Python 3.9 through 3.12. The module can be installed manually or via [apexomni](https://pypi.org/project/apexomni/) with `pip`:
```
Expand Down
Loading