Skip to content

Commit 3a2680e

Browse files
authored
ci(workflows): add PyPI publishing workflow with manual approval (#1428)
* ci(workflows): add PyPI publishing workflow with manual approval Add publish-pypi-approval.yml workflow that triggers after successful builds on version tags. Includes manual approval gate, version validation, PyPI publishing via trusted publishing, draft GitHub release creation.
1 parent 554ec30 commit 3a2680e

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
name: Publish to PyPI (with Approval)
2+
3+
on:
4+
workflow_run:
5+
workflows: ["Build and Test Distribution"]
6+
types:
7+
- completed
8+
9+
jobs:
10+
publish-pypi:
11+
if: github.event.workflow_run.conclusion == 'success' && startsWith(github.event.workflow_run.head_branch, 'v')
12+
runs-on: ubuntu-latest
13+
environment:
14+
name: pypi-production
15+
url: https://pypi.org/project/nemoguardrails/
16+
permissions:
17+
contents: read
18+
id-token: write
19+
20+
steps:
21+
- name: Extract version from tag
22+
id: version
23+
run: |
24+
TAG_NAME="${{ github.event.workflow_run.head_branch }}"
25+
VERSION="${TAG_NAME#v}"
26+
echo "version=${VERSION}" >> $GITHUB_OUTPUT
27+
echo "tag=${TAG_NAME}" >> $GITHUB_OUTPUT
28+
echo "artifact_name=${TAG_NAME}-build" >> $GITHUB_OUTPUT
29+
30+
- name: Checkout repository
31+
uses: actions/checkout@v4
32+
with:
33+
ref: ${{ steps.version.outputs.tag }}
34+
sparse-checkout: |
35+
pyproject.toml
36+
CHANGELOG.md
37+
38+
- name: Validate version matches tag
39+
run: |
40+
VERSION_IN_FILE=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
41+
TAG_VERSION="${{ steps.version.outputs.version }}"
42+
if [ "$VERSION_IN_FILE" != "$TAG_VERSION" ]; then
43+
echo "❌ Version mismatch: pyproject.toml=$VERSION_IN_FILE, tag=$TAG_VERSION"
44+
exit 1
45+
fi
46+
echo "✅ Version validated: $VERSION_IN_FILE matches tag $TAG_VERSION"
47+
48+
- name: Download artifact
49+
uses: actions/download-artifact@v4
50+
with:
51+
name: ${{ steps.version.outputs.artifact_name }}
52+
github-token: ${{ secrets.GITHUB_TOKEN }}
53+
repository: ${{ github.repository }}
54+
run-id: ${{ github.event.workflow_run.id }}
55+
56+
- name: List files
57+
run: ls -la
58+
59+
- name: Publish to PyPI
60+
uses: pypa/gh-action-pypi-publish@release/v1
61+
with:
62+
verbose: true
63+
packages-dir: ./
64+
65+
- name: Create GitHub Release
66+
env:
67+
GH_TOKEN: ${{ github.token }}
68+
run: |
69+
TAG_NAME="${{ steps.version.outputs.tag }}"
70+
71+
git config --global user.name "github-actions[bot]"
72+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
73+
74+
CHANGELOG_SECTION=$(awk -v version="${{ steps.version.outputs.version }}" '
75+
/^## \[/ {
76+
if (found) exit
77+
if ($0 ~ "\\[" version "\\]") {
78+
found=1
79+
next
80+
}
81+
}
82+
found && /^## \[/ { exit }
83+
found { print }
84+
' CHANGELOG.md || echo "No changelog entry found for this version.")
85+
86+
echo "$CHANGELOG_SECTION" > release_notes.md
87+
88+
gh release create "$TAG_NAME" \
89+
--draft \
90+
--title "$TAG_NAME" \
91+
--notes-file release_notes.md \
92+
--repo ${{ github.repository }} \
93+
|| echo "Release already exists or failed to create"
94+
95+
rm -f release_notes.md

0 commit comments

Comments
 (0)