-
Notifications
You must be signed in to change notification settings - Fork 0
231 lines (219 loc) · 7.96 KB
/
Copy pathrelease.yml
File metadata and controls
231 lines (219 loc) · 7.96 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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
name: Release
on:
workflow_dispatch:
inputs:
bump:
description: Semantic version component to increment
required: true
default: patch
type: choice
options:
- patch
- minor
- major
permissions:
contents: read
concurrency:
group: release
cancel-in-progress: false
jobs:
prepare:
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
pull-requests: write
outputs:
published: ${{ steps.release.outputs.published }}
sha: ${{ steps.release.outputs.sha }}
tag: ${{ steps.release.outputs.tag }}
version: ${{ steps.release.outputs.version }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
ref: ${{ github.sha }}
- uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Create and merge version bump
id: release
env:
BUMP: ${{ inputs.bump }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
set -euo pipefail
if [[ "$GITHUB_REF_NAME" != "$DEFAULT_BRANCH" ]]; then
echo "Release must be dispatched from $DEFAULT_BRANCH, not $GITHUB_REF_NAME" >&2
exit 1
fi
current_version="$(python scripts/bump_version.py current)"
version="$(python scripts/bump_version.py "$BUMP")"
tag="v$version"
git fetch origin "$DEFAULT_BRANCH" --tags
if sha="$(git rev-list -n 1 "$tag" 2>/dev/null)" && [[ -n "$sha" ]]; then
echo "Reusing existing tag $tag at $sha"
else
git show "origin/$DEFAULT_BRANCH:pyproject.toml" > "$RUNNER_TEMP/main-pyproject.toml"
main_version="$(python scripts/bump_version.py current --pyproject "$RUNNER_TEMP/main-pyproject.toml")"
if [[ "$main_version" == "$version" ]]; then
sha="$(git rev-parse "origin/$DEFAULT_BRANCH")"
echo "Reusing version bump already merged at $sha"
elif [[ "$main_version" != "$current_version" ]]; then
echo "main moved from version $current_version to $main_version; dispatch again" >&2
exit 1
else
branch="release/$tag-$GITHUB_RUN_ID"
git switch --create "$branch" "origin/$DEFAULT_BRANCH"
written_version="$(python scripts/bump_version.py "$BUMP" --write)"
[[ "$written_version" == "$version" ]]
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add pyproject.toml
git commit -m "Release $tag"
head_sha="$(git rev-parse HEAD)"
git push --set-upstream origin "$branch"
gh label create skip-changelog \
--description "Exclude this PR from generated release notes" \
--color "ededed" \
--force
pr_url="$(gh pr create \
--base "$DEFAULT_BRANCH" \
--head "$branch" \
--title "Release $tag" \
--body "Automated **$BUMP** version bump from $current_version to $version.")"
pr_number="${pr_url##*/}"
gh pr edit "$pr_number" --add-label skip-changelog
mergeable=UNKNOWN
for _attempt in {1..30}; do
mergeable="$(gh pr view "$pr_number" --json mergeable --jq ".mergeable")"
[[ "$mergeable" == "MERGEABLE" ]] && break
if [[ "$mergeable" == "CONFLICTING" ]]; then
echo "Release PR $pr_number conflicts with $DEFAULT_BRANCH" >&2
exit 1
fi
sleep 2
done
if [[ "$mergeable" != "MERGEABLE" ]]; then
echo "Release PR $pr_number did not become mergeable" >&2
exit 1
fi
gh pr merge "$pr_number" \
--squash \
--delete-branch \
--match-head-commit "$head_sha" \
--subject "Release $tag" \
--body "Automated semantic version bump."
sha="$(gh pr view "$pr_number" --json mergeCommit --jq ".mergeCommit.oid")"
fi
fi
pypi_status="$(curl \
--silent \
--output /dev/null \
--write-out '%{http_code}' \
--retry 3 \
--retry-all-errors \
"https://pypi.org/pypi/pineforge-data/$version/json")"
case "$pypi_status" in
200) published=true ;;
404) published=false ;;
*)
echo "Unexpected PyPI status $pypi_status while checking $version" >&2
exit 1
;;
esac
{
echo "published=$published"
echo "sha=$sha"
echo "tag=$tag"
echo "version=$version"
} >> "$GITHUB_OUTPUT"
printf "Prepared %s at %s (already on PyPI: %s).\n" \
"$tag" "$sha" "$published" >> "$GITHUB_STEP_SUMMARY"
build:
if: needs.prepare.outputs.published != 'true'
needs: prepare
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: ${{ needs.prepare.outputs.sha }}
persist-credentials: false
- uses: actions/setup-python@v6
with:
python-version: "3.13"
cache: pip
- run: python -m pip install --upgrade pip
- run: python -m pip install -e '.[dev,ccxt,release]'
- name: Verify release tag and source
run: python scripts/verify_release.py "${{ needs.prepare.outputs.tag }}"
- name: Run static and unit checks
run: |
ruff check .
mypy src
pytest
- name: Run Docker integration checks
run: PINEFORGE_DOCKER_TEST=1 pytest tests/test_docker_integration.py
- name: Build wheel and source distribution
run: python -m build
- name: Check distribution metadata
run: python -m twine check --strict dist/*
- name: Install wheel in a clean environment
run: |
python -m venv /tmp/pineforge-wheel
/tmp/pineforge-wheel/bin/python -m pip install dist/*.whl
/tmp/pineforge-wheel/bin/python -c "import pineforge_data; assert 'Bar' in pineforge_data.__all__"
- name: Upload validated distributions
uses: actions/upload-artifact@v5
with:
name: python-package-distributions
path: dist/
if-no-files-found: error
github-release:
if: needs.prepare.outputs.published != 'true'
needs:
- prepare
- build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Create GitHub Release with generated notes
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
SHA: ${{ needs.prepare.outputs.sha }}
TAG: ${{ needs.prepare.outputs.tag }}
run: |
set -euo pipefail
if gh release view "$TAG" >/dev/null 2>&1; then
echo "GitHub Release $TAG already exists"
else
gh release create "$TAG" \
--target "$SHA" \
--title "pineforge-data $TAG" \
--generate-notes \
--latest
fi
publish:
if: needs.prepare.outputs.published != 'true'
needs:
- prepare
- build
- github-release
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/pineforge-data/
permissions:
id-token: write
steps:
- name: Download validated distributions
uses: actions/download-artifact@v6
with:
name: python-package-distributions
path: dist/
- name: Publish to PyPI with Trusted Publishing
uses: pypa/gh-action-pypi-publish@release/v1