Skip to content

Commit de15aff

Browse files
authored
Merge branch 'main' into feat/openspec-dogfooding
2 parents 3ab88d0 + 39a2cd5 commit de15aff

8 files changed

Lines changed: 197 additions & 100 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
name: Build PyPI
22
on:
3-
workflow_call:
3+
workflow_call:
4+
inputs:
5+
ref:
6+
description: "Branch, tag or SHA to build. Empty = the caller's ref."
7+
required: false
8+
type: string
9+
default: ""
10+
artifact-name:
11+
description: >
12+
Name to upload the build output under. The release flow runs this
13+
workflow twice in one run -- once on the branch, once on the tag -- and
14+
upload-artifact rejects a duplicate name.
15+
required: false
16+
type: string
17+
default: "dist"
418
workflow_dispatch:
519
push:
620
branches:
@@ -27,12 +41,17 @@ jobs:
2741
matrix:
2842
reqstool-source: [pypi, main]
2943
steps:
44+
# Full history and tags: hatch-vcs derives the version from git state, so a
45+
# shallow clone would build the wrong number rather than fail.
3046
- name: Check out source repository
31-
uses: actions/checkout@v6
47+
uses: actions/checkout@v7
3248
with:
33-
fetch-depth: 0 #full history
49+
persist-credentials: false
50+
fetch-depth: 0
51+
fetch-tags: true
52+
ref: ${{ inputs.ref || github.ref }}
3453
- name: Set up Python
35-
uses: actions/setup-python@v6
54+
uses: actions/setup-python@v7
3655
with:
3756
python-version: "3.13"
3857
- name: Install dependencies
@@ -69,7 +88,7 @@ jobs:
6988
if: matrix.reqstool-source == 'pypi'
7089
uses: actions/upload-artifact@v7
7190
with:
72-
name: dist
91+
name: ${{ inputs.artifact-name || 'dist' }}
7392
path: dist/
7493

7594
validate-openspec:

‎.github/workflows/check_release.yml‎

Lines changed: 0 additions & 21 deletions
This file was deleted.

‎.github/workflows/lint.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ jobs:
88
runs-on: ubuntu-latest
99
steps:
1010
- name: Check out source repository
11-
uses: actions/checkout@v6
11+
uses: actions/checkout@v7
1212
- name: Set up Python
13-
uses: actions/setup-python@v6
13+
uses: actions/setup-python@v7
1414
with:
1515
python-version: "3.13"
1616
- name: Install pip package(s)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Publish dev build to Test PyPI
2+
3+
# The continuous dev feed: every push to main lands on Test PyPI as a `.devN`
4+
# build. Separate from the release flow, which publishes the tagged version to
5+
# Test PyPI and then PyPI -- both use skip-existing, so they cannot collide.
6+
7+
on:
8+
workflow_dispatch:
9+
push:
10+
branches:
11+
- main
12+
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
build:
18+
name: Reuse build
19+
uses: ./.github/workflows/build.yml
20+
permissions:
21+
contents: read
22+
23+
publish-to-test-pypi:
24+
needs: build
25+
uses: reqstool/.github/.github/workflows/python-publish-to-pypi.yml@main
26+
permissions:
27+
id-token: write
28+
with:
29+
target: testpypi

‎.github/workflows/publish_pypi_prod.yml‎

Lines changed: 0 additions & 36 deletions
This file was deleted.

‎.github/workflows/publish_pypi_test.yml‎

Lines changed: 0 additions & 35 deletions
This file was deleted.

‎.github/workflows/release.yml‎

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
name: Release
2+
3+
# The whole release, start to finish. See RELEASING.md in reqstool/.github for
4+
# what each step does, and for why the release is created as a prerelease rather
5+
# than a draft.
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
version:
11+
description: "Version to release (PEP 440, no v prefix), e.g. 0.2.0. Leave empty to auto-detect from Conventional Commits."
12+
required: false
13+
type: string
14+
prerelease:
15+
description: "Publish as a release candidate instead of a release: verified like any release, but never promoted to latest. The number is chosen for you (0.2.0 -> 0.2.0rc1, then the next)."
16+
required: false
17+
type: choice
18+
options: [none, rc, b, a]
19+
default: none
20+
ref:
21+
description: "Branch to release from. Leave empty for the branch this workflow was dispatched on."
22+
required: false
23+
type: string
24+
force:
25+
description: "Allow a version that disagrees with the auto-detected one."
26+
required: false
27+
type: boolean
28+
default: false
29+
dry-run:
30+
description: "Validate and preview only -- nothing tagged, nothing published."
31+
required: false
32+
type: boolean
33+
default: true
34+
35+
concurrency:
36+
group: release
37+
cancel-in-progress: false
38+
39+
permissions:
40+
contents: read
41+
42+
jobs:
43+
prepare:
44+
uses: reqstool/.github/.github/workflows/common-release-prepare.yml@main
45+
permissions:
46+
contents: read
47+
with:
48+
version-format: pep440
49+
version: ${{ inputs.version }}
50+
prerelease: ${{ inputs.prerelease }}
51+
ref: ${{ inputs.ref }}
52+
force: ${{ inputs.force }}
53+
dry-run: ${{ inputs.dry-run }}
54+
55+
# The same checks that guard main, called rather than reimplemented, and run
56+
# before the approval gate so the reviewer approves something already green
57+
# rather than a version string.
58+
checks:
59+
needs: prepare
60+
if: ${{ !inputs.dry-run }}
61+
uses: ./.github/workflows/build.yml
62+
permissions:
63+
contents: read
64+
65+
# THE APPROVAL GATE -- bound to the `stable` environment, so it sits pending
66+
# until a required reviewer approves it on the run page.
67+
tag:
68+
needs: [prepare, checks]
69+
if: ${{ !inputs.dry-run }}
70+
uses: reqstool/.github/.github/workflows/common-release-tag.yml@main
71+
permissions:
72+
contents: write
73+
with:
74+
version: ${{ needs.prepare.outputs.version }}
75+
version-format: pep440
76+
ref: ${{ inputs.ref }}
77+
78+
# Rebuilt from the tag, which is what gives the artifacts their version:
79+
# hatch-vcs reads it from git rather than from a version string in the tree.
80+
build-tagged:
81+
needs: [prepare, tag]
82+
uses: ./.github/workflows/build.yml
83+
permissions:
84+
contents: read
85+
with:
86+
ref: ${{ needs.prepare.outputs.version }}
87+
artifact-name: dist-tagged
88+
89+
assets:
90+
needs: [prepare, build-tagged]
91+
uses: reqstool/.github/.github/workflows/common-release-assets.yml@main
92+
permissions:
93+
contents: write
94+
with:
95+
version: ${{ needs.prepare.outputs.version }}
96+
artifact: dist-tagged
97+
98+
publish-to-testpypi:
99+
needs: [prepare, assets]
100+
uses: reqstool/.github/.github/workflows/python-publish-to-pypi.yml@main
101+
permissions:
102+
id-token: write
103+
with:
104+
target: testpypi
105+
artifact: dist-tagged
106+
107+
# PyPI is the only step here that cannot be undone: a version can be yanked but
108+
# never replaced. A release candidate stops at Test PyPI -- pip needs --pre to
109+
# see a prerelease anyway.
110+
publish-to-pypi:
111+
needs: [prepare, publish-to-testpypi]
112+
if: ${{ needs.prepare.outputs.prerelease != 'true' }}
113+
uses: reqstool/.github/.github/workflows/python-publish-to-pypi.yml@main
114+
permissions:
115+
id-token: write
116+
with:
117+
target: pypi
118+
artifact: dist-tagged
119+
120+
# Last, deliberately. Everything above can fail, and until this runs nothing
121+
# resolving "the latest release" can see what was built -- the release is still
122+
# a prerelease. Promotion itself is one API call against a release that already
123+
# has its artifacts.
124+
#
125+
# The guard is `no job failed`, not the default `every job succeeded`: a release
126+
# candidate deliberately skips the publish jobs that a real release runs, and a
127+
# skipped dependency would otherwise cascade and skip this too -- leaving the
128+
# candidate unpromoted, which is right, and every *real* release unpromoted the
129+
# moment any optional job is skipped, which is not.
130+
#
131+
# `!inputs.dry-run` has to be spelled out for the same reason: on a dry run
132+
# every job above is skipped, and "nothing failed" would otherwise be true.
133+
promote:
134+
needs: [prepare, assets, publish-to-testpypi, publish-to-pypi]
135+
if: ${{ !inputs.dry-run && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }}
136+
uses: reqstool/.github/.github/workflows/common-release-promote.yml@main
137+
permissions:
138+
contents: write
139+
with:
140+
version: ${{ needs.prepare.outputs.version }}
141+
prerelease: ${{ needs.prepare.outputs.prerelease == 'true' }}

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ classifiers = [
1818
dependencies = [
1919
"reqstool-python-decorators==0.1.0",
2020
"ruamel.yaml==0.19.1",
21-
"hatchling==1.29.0",
21+
"hatchling==1.32.0",
2222
]
2323

2424
[project.urls]

0 commit comments

Comments
 (0)