Skip to content
Merged
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
51 changes: 42 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@ name: Release

on:
push:
tags: [ 'v*.*.*' ]
workflow_dispatch: {}
tags: [ 'v*.*.*' ] # only runs on semver-like tags
workflow_dispatch: {} # manual runs won't publish unless also with a tag

permissions:
contents: read

jobs:
build-publish:
# uncomment the next line ONLY if you set an Environment name on PyPI's Trusted Publisher
# environment: pypi
runs-on: ubuntu-latest
permissions:
contents: write # attach assets to GH Release
id-token: write # OIDC for PyPI Trusted Publisher
contents: write # attach assets to GH Release
id-token: write # OIDC for PyPI Trusted Publisher
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
Expand All @@ -25,7 +23,7 @@ jobs:
with:
python-version: "3.12"

# Install build + twine via pipx (no raw pip)
# Tools via pipx (pinned; no raw pip)
- name: Install pipx + tools
run: |
set -euxo pipefail
Expand All @@ -36,8 +34,23 @@ jobs:
pipx install build==1.2.2
pipx install twine==5.1.1

# tag (vX.Y.Z) must match pyproject.toml version (X.Y.Z)
- name: Check version matches tag
id: vercheck
run: |
TAG="${GITHUB_REF_NAME#v}"
PYVER=$(python - <<'PY'
import sys, tomllib, pathlib
data = tomllib.loads(pathlib.Path("pyproject.toml").read_text(encoding="utf-8"))
print(data["project"]["version"])
PY
)
echo "tag=${TAG}"
echo "pyproject=${PYVER}"
test "$PYVER" = "$TAG" || { echo "ERROR: pyproject.toml version != tag"; exit 1; }

- name: Build distributions
run: python -m build -o dist
run: pyproject-build -o dist

- name: Twine check
run: twine check dist/*
Expand All @@ -51,8 +64,28 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# skip if version already on PyPI (prevents "File already exists")
- name: Skip if version exists on PyPI
id: pypi-exists
run: |
PKG="decision-security"
VER="${GITHUB_REF_NAME#v}"
python - <<PY
import json, sys, urllib.request
pkg = sys.argv[1]; ver = sys.argv[2]
try:
with urllib.request.urlopen(f"https://pypi.org/pypi/{pkg}/json", timeout=10) as r:
data = json.load(r)
if ver in data.get("releases", {}):
print(f"Version {ver} already on PyPI; skipping publish.")
sys.exit(78) # neutral
except Exception:
pass
PY
continue-on-error: true

- name: Publish to PyPI (OIDC Trusted Publisher)
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/') && steps.pypi-exists.outcome != 'success'
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
print-hash: true