-
Notifications
You must be signed in to change notification settings - Fork 0
148 lines (133 loc) · 5.09 KB
/
Copy pathrelease.yml
File metadata and controls
148 lines (133 loc) · 5.09 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
name: Release
on:
push:
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Need full history (or at least the merge-base) for the
# ancestry check below.
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install build deps
run: python -m pip install --upgrade pip build
- name: Build sdist + wheel
run: python -m build
- name: Verify build
run: |
ls -lh dist/
# furqan-lint is pure Python; expect exactly one wheel
# (py3-none-any) and one sdist (.tar.gz).
test "$(ls dist/*.whl | wc -l)" -eq 1
test "$(ls dist/*.tar.gz | wc -l)" -eq 1
# Wheel must be py3-none-any (we are pure Python).
ls dist/*.whl | grep -q "py3-none-any.whl"
- name: Verify tag is an ancestor of origin/main
run: |
# Defends against tags pushed at local-only commits that
# never made it to main. Release artifacts must reflect
# what is actually on the published branch.
git fetch origin main --depth=50
if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/main; then
echo "Tag commit $GITHUB_SHA is not an ancestor of origin/main"
exit 1
fi
echo "Tag commit is on origin/main: ok"
- name: Verify version-tag-CHANGELOG sync
run: |
TAG_VERSION="${GITHUB_REF#refs/tags/v}"
# Workflow uses Python 3.12; tomllib is stdlib since 3.11.
# No tomli shim needed.
PYPROJECT_VERSION=$(python -c "
import tomllib
with open('pyproject.toml', 'rb') as f:
print(tomllib.load(f)['project']['version'])
")
if [ "$TAG_VERSION" != "$PYPROJECT_VERSION" ]; then
echo "Tag version $TAG_VERSION != pyproject.toml version $PYPROJECT_VERSION"
exit 1
fi
if ! grep -q "^## \[${TAG_VERSION}\]" CHANGELOG.md; then
echo "CHANGELOG.md has no entry for [${TAG_VERSION}]"
exit 1
fi
echo "Tag/pyproject/CHANGELOG version sync: $TAG_VERSION"
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
publish:
needs: build
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
- name: Verify PyPI publication
# Phase G10.5 (al-Mubin) T01: closes F7 by verifying that
# the just-published version is reachable through PyPI's
# index. Twelve polls at ten-second intervals (120s total)
# per the S5 calibration. PyPI CDN propagation is typically
# 1-15 seconds; tail observations of 90-180 seconds exist;
# the 60-second window we considered earlier risks false-
# failure on a successful publish.
run: |
set -euo pipefail
VERSION="${GITHUB_REF#refs/tags/v}"
echo "Verifying furqan-lint==${VERSION} is installable from PyPI..."
for i in 1 2 3 4 5 6 7 8 9 10 11 12; do
if pip index versions furqan-lint 2>/dev/null | grep -q "Available versions:.*${VERSION}"; then
echo "furqan-lint ${VERSION} is now installable."
exit 0
fi
echo "Attempt ${i}/12: not yet visible, sleeping 10s..."
sleep 10
done
echo "ERROR: furqan-lint ${VERSION} did not become visible on PyPI within 120 seconds."
exit 1
- name: Create GitHub Release
# Phase G10.5 (al-Mubin) T02: closes F8 going forward by
# automatically creating a Release object with notes
# extracted from CHANGELOG.md whenever a v* tag is pushed.
# Notes are extracted by scripts/extract_changelog_section.py
# which slices between the version's ## [X.Y.Z] header and
# the next ## [ header. --verify-tag asserts the tag exists
# at the workflow's GITHUB_SHA, defending against re-runs
# that point at a different SHA.
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
VERSION="${GITHUB_REF#refs/tags/v}"
NOTES_FILE="$(mktemp)"
python scripts/extract_changelog_section.py "${VERSION}" > "${NOTES_FILE}"
if [ ! -s "${NOTES_FILE}" ]; then
echo "ERROR: No CHANGELOG section found for v${VERSION}."
exit 1
fi
gh release create "v${VERSION}" \
--title "v${VERSION}" \
--notes-file "${NOTES_FILE}" \
--verify-tag