Skip to content

Commit 4f5e33e

Browse files
authored
Merge pull request #1 from dedev-llc/release-tooling
Add bump.sh and switch PyPI publish to Trusted Publishing
2 parents 74d09f6 + 6782d8b commit 4f5e33e

2 files changed

Lines changed: 83 additions & 11 deletions

File tree

.github/workflows/release.yml

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,20 @@ name: release
55
# version hasn't changed (i.e. v<version> is already tagged).
66
#
77
# Required secrets (Settings → Secrets and variables → Actions):
8-
# PYPI_API_TOKEN — pypi.org token (scope: project rpr, or whole-account
9-
# until the project exists). __token__ user is implied.
108
# NPM_TOKEN — npmjs.com automation token with publish rights.
119
# HOMEBREW_TAP_TOKEN — fine-grained PAT with Contents: read/write on
1210
# dedev-llc/homebrew-rpr. Used to commit the bumped
1311
# formula to the tap repo.
12+
#
13+
# PyPI uses Trusted Publishing (OIDC) — no token required. One-time setup:
14+
# 1. https://pypi.org/manage/account/publishing/
15+
# 2. Add a "pending publisher" (or, after first publish, a real one):
16+
# Project name: rpr
17+
# Owner: dedev-llc
18+
# Repository: rpr
19+
# Workflow name: release.yml
20+
# Environment: release
21+
# 3. The pypi job below claims an OIDC token and PyPI verifies it matches.
1422

1523
on:
1624
push:
@@ -62,6 +70,8 @@ jobs:
6270
if: needs.check-version.outputs.should_release == 'true'
6371
runs-on: ubuntu-latest
6472
environment: release
73+
permissions:
74+
id-token: write # required for Trusted Publishing (OIDC)
6575
steps:
6676
- uses: actions/checkout@v4
6777

@@ -70,19 +80,13 @@ jobs:
7080
python-version: "3.12"
7181

7282
- name: Install build tooling
73-
run: python -m pip install --upgrade build twine
83+
run: python -m pip install --upgrade build
7484

7585
- name: Build sdist + wheel
7686
run: python -m build
7787

78-
- name: Validate distributions
79-
run: twine check dist/*
80-
81-
- name: Upload to PyPI
82-
env:
83-
TWINE_USERNAME: __token__
84-
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
85-
run: twine upload dist/*
88+
- name: Upload to PyPI (Trusted Publishing)
89+
uses: pypa/gh-action-pypi-publish@release/v1
8690

8791
npm:
8892
needs: [check-version, pypi]

scripts/bump.sh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#!/usr/bin/env bash
2+
# bump.sh — bump rpr version in pyproject.toml and npm/package.json in lockstep.
3+
#
4+
# Usage:
5+
# scripts/bump.sh 0.2.0
6+
#
7+
# Does NOT commit, tag, or push — just edits the two files so you can review
8+
# the diff and open a PR. The release workflow runs on merge to main and
9+
# detects the new version automatically.
10+
11+
set -euo pipefail
12+
13+
if [ "$#" -ne 1 ]; then
14+
echo "usage: $0 <version>" >&2
15+
exit 1
16+
fi
17+
18+
new="$1"
19+
20+
# PEP 440 / semver-ish: MAJOR.MINOR.PATCH with optional pre/post/dev suffix.
21+
if ! [[ "$new" =~ ^[0-9]+\.[0-9]+\.[0-9]+([a-zA-Z0-9.+-]*)?$ ]]; then
22+
echo "error: '$new' is not a valid version (expected MAJOR.MINOR.PATCH[suffix])" >&2
23+
exit 1
24+
fi
25+
26+
repo_root=$(git rev-parse --show-toplevel)
27+
cd "$repo_root"
28+
29+
current=$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')
30+
echo "Current version: $current"
31+
echo "New version: $new"
32+
33+
if [ "$current" = "$new" ]; then
34+
echo "Already at $new — nothing to do."
35+
exit 0
36+
fi
37+
38+
# pyproject.toml — surgical replace of the project.version line.
39+
NEW="$new" python3 - <<'PY'
40+
import os, pathlib, re
41+
p = pathlib.Path("pyproject.toml")
42+
s = p.read_text()
43+
s, n = re.subn(r'^version\s*=\s*"[^"]+"',
44+
f'version = "{os.environ["NEW"]}"',
45+
s, count=1, flags=re.M)
46+
if n != 1:
47+
raise SystemExit("error: could not find 'version = ...' line in pyproject.toml")
48+
p.write_text(s)
49+
PY
50+
51+
# npm/package.json — preserve 2-space indent and trailing newline.
52+
NEW="$new" node -e '
53+
const fs = require("fs");
54+
const p = "npm/package.json";
55+
const j = JSON.parse(fs.readFileSync(p, "utf8"));
56+
j.version = process.env.NEW;
57+
fs.writeFileSync(p, JSON.stringify(j, null, 2) + "\n");
58+
'
59+
60+
echo
61+
echo "Updated:"
62+
echo " pyproject.toml"
63+
echo " npm/package.json"
64+
echo
65+
echo "Next steps:"
66+
echo " git checkout -b release/v$new"
67+
echo " git commit -am \"Release v$new\""
68+
echo " gh pr create --fill"

0 commit comments

Comments
 (0)