Skip to content

Commit 6ebb723

Browse files
committed
Merge branch 'main' into releases/0.7.1
2 parents f8b05b2 + 4a167fe commit 6ebb723

4 files changed

Lines changed: 41 additions & 25 deletions

File tree

.github/workflows/release-test.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ jobs:
1818
cache: 'pip'
1919
- name: Install dependencies
2020
run: |
21-
python -m pip install --upgrade pip
21+
# setuptools is upgraded because the toolcache copy periodically lags
22+
# security fixes (e.g. PYSEC-2026-3447) and would fail the audit below.
23+
python -m pip install --upgrade pip setuptools
2224
pip install build twine pip-audit
2325
- name: Version consistency check
2426
run: |

.github/workflows/release.yml

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
# Release orchestrator: verifies the artifacts attached to a published GitHub
2-
# release actually work, then publishes them stage by stage, verifying each
3-
# stage before starting the next:
1+
# Release orchestrator: verifies the artifacts attached to a DRAFT GitHub
2+
# release actually work, then publishes stage by stage, verifying each stage
3+
# before starting the next:
44
#
5-
# MSI install/upgrade test (test-installer.yml)
5+
# MSI install/upgrade test against the draft's assets (test-installer.yml)
66
# -> TestPyPI publish -> pip smoke install from TestPyPI
7+
# -> publish the GitHub release (draft -> public, marked latest)
78
# -> PyPI publish -> pip smoke install from PyPI
89
# -> Docker publish -> docker pull + smoke run from GHCR
910
#
10-
# Publishing is intentionally NOT triggered automatically by the release event -
11-
# this workflow is dispatched manually once the GitHub release is published, and
12-
# the publish steps only run after every verification step succeeds. Each stage
13-
# is driven through the existing workflows via `gh workflow run` (rather than
14-
# workflow_call) so they keep working standalone and the PyPI trusted-publisher
15-
# configuration (which is bound to publish-pypi.yml as the top-level workflow)
16-
# is unaffected.
11+
# This workflow is dispatched manually once the GitHub release has been DRAFTED
12+
# with its artifacts attached (signed MSI/zip, wheels, SHA256SUMS); nothing goes
13+
# public until artifact verification passes. Each stage is driven through the
14+
# existing workflows via `gh workflow run` (rather than workflow_call) so they
15+
# keep working standalone and the PyPI trusted-publisher configuration (which is
16+
# bound to publish-pypi.yml as the top-level workflow) is unaffected.
1717

1818
name: Release Orchestrator
1919

@@ -29,8 +29,8 @@ on:
2929
default: false
3030

3131
permissions:
32-
contents: read
33-
actions: write # `gh workflow run` on the workflows this one orchestrates
32+
contents: write # read the draft release's assets and publish it (draft -> public)
33+
actions: write # `gh workflow run` on the workflows this one orchestrates
3434

3535
# The run-id lookup after each dispatch assumes this is the only orchestrator
3636
# running; never allow two concurrent releases.
@@ -53,10 +53,10 @@ jobs:
5353
state=$(gh release view "$TAG" --json isDraft,isPrerelease \
5454
-q 'if .isDraft then "draft" elif .isPrerelease then "prerelease" else "published" end')
5555
if [[ "$state" == "draft" ]]; then
56-
echo "::error::Release $TAG is still a draft; publish it before running the orchestrator."
57-
exit 1
56+
echo "Release $TAG is a draft; it will be published after artifact verification passes."
57+
else
58+
echo "Release $TAG is already $state; the GitHub release publish step will be a no-op (re-run mode)."
5859
fi
59-
echo "Release $TAG is $state."
6060
# Display version used by the pip smoke test; mirrors the tag
6161
# normalization in release-test.yml (both vX.Y[.Z] and the legacy
6262
# vX.Y[.Z]-release tag styles are accepted).
@@ -145,7 +145,19 @@ jobs:
145145
--index-url https://test.pypi.org/simple/ \
146146
--extra-index-url https://pypi.org/simple/
147147
148-
- name: 'Stage 3 - Publish: PyPI (production)'
148+
- name: 'Stage 3 - Publish: GitHub release (draft -> public)'
149+
if: ${{ !inputs.verify-only }}
150+
run: |
151+
set -euo pipefail
152+
is_draft=$(gh release view "$TAG" --json isDraft -q .isDraft)
153+
if [[ "$is_draft" == "true" ]]; then
154+
gh release edit "$TAG" --draft=false --latest
155+
echo "Published release $TAG (marked as latest)."
156+
else
157+
echo "Release $TAG is already published; skipping."
158+
fi
159+
160+
- name: 'Stage 4 - Publish: PyPI (production)'
149161
if: ${{ !inputs.verify-only }}
150162
run: |
151163
set -euo pipefail
@@ -154,14 +166,14 @@ jobs:
154166
# build + release-test workflows being green for the tag.
155167
dispatch_and_watch publish-pypi.yml "$GITHUB_REF_NAME" -f "tag=$TAG" -f "environment=pypi"
156168
157-
- name: 'Stage 3 - Verify: pip install from PyPI'
169+
- name: 'Stage 4 - Verify: pip install from PyPI'
158170
if: ${{ !inputs.verify-only }}
159171
run: |
160172
set -euo pipefail
161173
source "$RUNNER_TEMP/smoke.sh"
162174
pip_smoke_install smoke-pypi
163175
164-
- name: 'Stage 4 - Publish: Docker image (version tags + latest)'
176+
- name: 'Stage 5 - Publish: Docker image (version tags + latest)'
165177
if: ${{ !inputs.verify-only }}
166178
run: |
167179
set -euo pipefail
@@ -171,7 +183,7 @@ jobs:
171183
# docker-publish.yml, i.e. v0.7.1 or newer).
172184
dispatch_and_watch docker-publish.yml "$TAG" -f "tag_latest=true"
173185
174-
- name: 'Stage 4 - Verify: docker pull + smoke run from GHCR'
186+
- name: 'Stage 5 - Verify: docker pull + smoke run from GHCR'
175187
if: ${{ !inputs.verify-only }}
176188
run: |
177189
set -euo pipefail
@@ -192,7 +204,7 @@ jobs:
192204
- name: Summary
193205
run: |
194206
if [[ "${{ inputs.verify-only }}" == "true" ]]; then
195-
echo "Verification of $TAG passed. Re-run without verify-only to publish."
207+
echo "Verification of $TAG passed (release left as draft). Re-run without verify-only to publish."
196208
else
197209
echo "Release $TAG verified and published:"
198210
echo " https://pypi.org/project/scenedetect/$VERSION/"

.github/workflows/test-installer.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ on:
2121
description: 'Release tag to upgrade from (default: auto-detect previous release)'
2222
required: false
2323

24+
# NOTE: This workflow never writes to the repository; `contents: write` is required
25+
# only because draft-release assets are invisible to read-only tokens, and the
26+
# release orchestrator runs this verification while the release is still a draft.
2427
permissions:
25-
contents: read
28+
contents: write
2629

2730
jobs:
2831
resolve:

RELEASE-PLAN.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ Optional: version referenced below as `X.Y[.Z]` - replace with the real version
5353

5454
## 6. Publish & Release Checks
5555

56-
- [ ] Publish Github release
57-
- [ ] Dispatch `release.yml` (Release Orchestrator) with the release tag. It publishes stage by stage, verifying each stage before the next: MSI install/uninstall + upgrade-from-previous on a clean Windows runner (`test-installer.yml`) -> TestPyPI publish -> pip smoke install from TestPyPI -> production PyPI publish (all 6 artifacts - sdist + wheel for `scenedetect-core`, `scenedetect`, and `scenedetect-headless`) -> pip smoke install from PyPI -> Docker publish (version tags + `latest`) -> docker pull + smoke run from GHCR. Use the `verify-only` input to stop after the TestPyPI stage without publishing anything user-facing. The underlying workflows (`test-installer.yml`, `publish-pypi.yml`, `docker-publish.yml`) can still be dispatched individually as a fallback.
56+
- [ ] Dispatch `release.yml` (Release Orchestrator) with the release tag while the Github release is still a **draft**. It runs the verify-then-publish ladder (MSI install/upgrade test -> TestPyPI -> publish Github release -> PyPI -> Docker), verifying each stage before the next; `verify-only` stops before anything goes public. See the header of `release.yml` for details.
5857
- [ ] Verify all three projects: https://pypi.org/project/scenedetect/, https://pypi.org/project/scenedetect-headless/, and https://pypi.org/project/scenedetect-core/.
5958
- [ ] Deploy website: `generate-website.yml`
6059
- [ ] Deploy docs: `generate-docs.yml`

0 commit comments

Comments
 (0)