-
Notifications
You must be signed in to change notification settings - Fork 2
133 lines (119 loc) · 4.82 KB
/
Copy pathon-release-main.yml
File metadata and controls
133 lines (119 loc) · 4.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# Automated release: python-semantic-release analyzes the conventional
# commits on main, and when a release is due it bumps the static version in
# pyproject.toml and CITATION.cff, updates CHANGELOG.md, relocks uv.lock,
# commits, tags vX.Y.Z, pushes and creates the GitHub release. Publishing
# happens in this same run: pushes made with GITHUB_TOKEN do not retrigger
# workflows, so a tag-triggered publish would never fire.
#
# Filename and environment name are load-bearing: the PyPI trusted
# publisher (OIDC) references workflow "on-release-main.yml" and
# environment "pypi". That same "pypi" environment is also the release
# gate: it has a required-reviewer protection rule (repo Settings ->
# Environments -> pypi, configured out-of-repo), so this job pauses right
# after checkout until a reviewer approves. Reject and nothing is tagged
# or published. This is the deliberate-release control in place of a
# staging branch.
name: release-main
on:
push:
branches: [main]
workflow_dispatch:
# One release run at a time: python-semantic-release pushes commits/tags
# to main, so overlapping runs would race each other.
concurrency:
group: release
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
environment:
name: pypi
permissions:
# required for PyPI trusted publishing (OIDC)
id-token: write
# push the release commit/tag and create the GitHub release (PSR)
contents: write
outputs:
released: ${{ steps.psr.outputs.released }}
version: ${{ steps.psr.outputs.version }}
steps:
# The default GITHUB_TOKEN cannot push to a ruleset-protected main
# (fails with GH013). Mint a short-lived, repo-scoped (Contents: write)
# GitHub App token instead; the app is on main's branch-protection
# bypass list, so PSR's release commit and tag push succeed.
- name: Generate app token
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.RELEASE_APP_ID }}
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
- name: Check out
uses: actions/checkout@v4
with:
# full history so PSR can analyze commits and tags
fetch-depth: 0
# latest main, not the triggering SHA: runs queued behind the
# concurrency group must include release commits pushed by the
# previous run, or their own push is rejected
ref: main
token: ${{ steps.app-token.outputs.token }}
- name: Set up the environment
uses: ./.github/actions/setup-python-env
- name: Version, tag and changelog with python-semantic-release
id: psr
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
before=$(git rev-parse HEAD)
uv run semantic-release version
after=$(git rev-parse HEAD)
if [ "$before" != "$after" ]; then
ver=$(git tag --points-at HEAD | grep -E '^v[0-9]' | head -1)
echo "released=true" >> "$GITHUB_OUTPUT"
echo "version=${ver#v}" >> "$GITHUB_OUTPUT"
echo "released $ver"
else
echo "released=" >> "$GITHUB_OUTPUT"
echo "no release necessary"
fi
- name: Build package distribution files
if: steps.psr.outputs.released != ''
run: uv build
- name: Check package metadata
if: steps.psr.outputs.released != ''
run: uvx twine check dist/*
- name: Publish package
if: steps.psr.outputs.released != ''
run: uv publish --trusted-publishing always
deploy-docs:
needs: release
if: needs.release.outputs.released != ''
runs-on: ubuntu-latest
permissions:
# mike commits the built site to the gh-pages branch
contents: write
concurrency:
group: pages
cancel-in-progress: false
steps:
- uses: actions/checkout@v4
with:
# deep clone incl. the release commit and tags pushed by PSR
fetch-depth: 0
ref: main
- name: Set up the environment
uses: ./.github/actions/setup-python-env
- name: Configure git identity
run: |
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
- name: Deploy versioned documentation
env:
VERSION: ${{ needs.release.outputs.version }}
run: |
# --alias-type=copy keeps /latest/ a real directory instead of a
# redirect, so machine-readable files stay fetchable there
uv run mike deploy --push --alias-type=copy --update-aliases "$VERSION" latest
uv run mike set-default --push latest