Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/changelog.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Generated by reflex-release; do not edit by hand.
# Re-run `uvx reflex-release@0.1.0a3 sync` after changing [tool.reflex-release] in
# pyproject.toml. `uvx reflex-release@0.1.0a3 sync --check` fails when this file drifts.
# Re-run `uvx reflex-release@0.1.0a4 sync` after changing [tool.reflex-release] in
# pyproject.toml. `uvx reflex-release@0.1.0a4 sync --check` fails when this file drifts.
name: changelog

# Two guards on every pull request:
Expand All @@ -19,7 +19,7 @@ name: changelog
#
# 3. The generated release workflows must match what the pinned reflex-release
# version produces from [tool.reflex-release] — adding a package or editing
# the configuration without re-running `uvx reflex-release@0.1.0a3 sync` fails here rather than
# the configuration without re-running `uvx reflex-release@0.1.0a4 sync` fails here rather than
# at release time.

permissions:
Expand Down Expand Up @@ -65,13 +65,13 @@ jobs:
env:
BASE_REF: origin/${{ github.base_ref }}
shell: bash
run: uvx reflex-release@0.1.0a3 check-headings
run: uvx reflex-release@0.1.0a4 check-headings
- name: Check for news fragments
if: ${{ !contains(github.event.pull_request.labels.*.name, 'skip-changelog') }}
env:
BASE_REF: origin/${{ github.base_ref }}
shell: bash
run: uvx reflex-release@0.1.0a3 changelog-check
run: uvx reflex-release@0.1.0a4 changelog-check
- name: Check the release workflows are up to date
shell: bash
run: uvx reflex-release@0.1.0a3 sync --check
run: uvx reflex-release@0.1.0a4 sync --check
14 changes: 7 additions & 7 deletions .github/workflows/dispatch_release.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Generated by reflex-release; do not edit by hand.
# Re-run `uvx reflex-release@0.1.0a3 sync` after changing [tool.reflex-release] in
# pyproject.toml. `uvx reflex-release@0.1.0a3 sync --check` fails when this file drifts.
# Re-run `uvx reflex-release@0.1.0a4 sync` after changing [tool.reflex-release] in
# pyproject.toml. `uvx reflex-release@0.1.0a4 sync --check` fails when this file drifts.
name: Dispatch release

# Kicks off a release by materializing news fragments into the selected
Expand All @@ -15,7 +15,7 @@ name: Dispatch release
# groups share one checkbox: their members only ever release together.
#
# The package list is generated from [tool.reflex-release] — after adding or
# removing a package, re-run `uvx reflex-release@0.1.0a3 sync`.
# removing a package, re-run `uvx reflex-release@0.1.0a4 sync`.
#
# Prerelease actions (new-prerelease-*, continued-prerelease) write alpha
# versions and push them straight to an r/pre-<date> branch
Expand Down Expand Up @@ -103,13 +103,13 @@ jobs:
PACKAGES: >-
${{ inputs.xy && 'xy' || '' }}
shell: bash
run: uvx reflex-release@0.1.0a3 plan
run: uvx reflex-release@0.1.0a4 plan
- name: Materialize changelogs
env:
ACTION: ${{ inputs.action }}
RELEASES_JSON: ${{ steps.plan.outputs.releases }}
shell: bash
run: uvx reflex-release@0.1.0a3 materialize
run: uvx reflex-release@0.1.0a4 materialize
- name: Push prerelease branch
if: ${{ !startsWith(inputs.action, 'release-') }}
env:
Expand All @@ -118,7 +118,7 @@ jobs:
REF_NAME: ${{ github.ref_name }}
RELEASES_JSON: ${{ steps.plan.outputs.releases }}
shell: bash
run: uvx reflex-release@0.1.0a3 push-prerelease
run: uvx reflex-release@0.1.0a4 push-prerelease
- name: Open release pull request
if: ${{ startsWith(inputs.action, 'release-') }}
env:
Expand All @@ -127,4 +127,4 @@ jobs:
REF_NAME: ${{ github.ref_name }}
RELEASES_JSON: ${{ steps.plan.outputs.releases }}
shell: bash
run: uvx reflex-release@0.1.0a3 open-release-pr
run: uvx reflex-release@0.1.0a4 open-release-pr
37 changes: 25 additions & 12 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Generated by reflex-release; do not edit by hand.
# Re-run `uvx reflex-release@0.1.0a3 sync` after changing [tool.reflex-release] in
# pyproject.toml. `uvx reflex-release@0.1.0a3 sync --check` fails when this file drifts.
# Re-run `uvx reflex-release@0.1.0a4 sync` after changing [tool.reflex-release] in
# pyproject.toml. `uvx reflex-release@0.1.0a4 sync --check` fails when this file drifts.
name: Publish to PyPI

run-name: Publish ${{ inputs.package }} ${{ inputs.version }}
Expand Down Expand Up @@ -116,7 +116,7 @@ jobs:
VERSION: ${{ inputs.version }}
REF_NAME: ${{ github.ref_name }}
shell: bash
run: uvx reflex-release@0.1.0a3 prepare-publish
run: uvx reflex-release@0.1.0a4 prepare-publish

# Custom-built packages never reach the `build` job, where this gate
# normally runs after the lockstep pin rewrites their metadata. They
Expand All @@ -129,7 +129,7 @@ jobs:
env:
PACKAGE: ${{ inputs.package }}
shell: bash
run: uvx reflex-release@0.1.0a3 check-dev-pins "$PACKAGE"
run: uvx reflex-release@0.1.0a4 check-dev-pins "$PACKAGE"

build:
needs: prepare
Expand All @@ -155,7 +155,7 @@ jobs:
PACKAGE: ${{ inputs.package }}
VERSION: ${{ needs.prepare.outputs.version }}
shell: bash
run: uvx reflex-release@0.1.0a3 pin-lockstep
run: uvx reflex-release@0.1.0a4 pin-lockstep

# A *.dev dependency pin references an unpublished version, so it must
# never reach released package metadata. Scoped to the package being
Expand All @@ -166,7 +166,7 @@ jobs:
env:
PACKAGE: ${{ inputs.package }}
shell: bash
run: uvx reflex-release@0.1.0a3 check-dev-pins "$PACKAGE"
run: uvx reflex-release@0.1.0a4 check-dev-pins "$PACKAGE"

# The dynamic versioning backend derives each package's version from the
# newest reachable tag with the package's prefix, so tagging HEAD locally
Expand Down Expand Up @@ -278,7 +278,7 @@ jobs:
PACKAGE: ${{ inputs.package }}
VERSION: ${{ needs.prepare.outputs.version }}
shell: bash
run: uvx reflex-release@0.1.0a3 verify-dist
run: uvx reflex-release@0.1.0a4 verify-dist

- name: Repository-specific post-build checks
if: hashFiles('.github/scripts/publish/post_build.sh') != ''
Expand All @@ -296,7 +296,7 @@ jobs:
VERSION: ${{ needs.prepare.outputs.version }}
NOTES_PATH: release_notes.md
shell: bash
run: uvx reflex-release@0.1.0a3 extract-notes
run: uvx reflex-release@0.1.0a4 extract-notes

# The manifest covers exactly the files that go to PyPI. It lets the
# gated publish job re-verify (with coreutils only) that what it uploads
Expand Down Expand Up @@ -336,7 +336,14 @@ jobs:
# dependencies — it only uploads the artifact collected above.
publish:
needs: [prepare, collect]
if: needs.prepare.outputs.skipped != 'true'
# The status function is load-bearing. With none, GitHub applies an
# implicit success() evaluated over the whole transitive dependency
# closure rather than the direct needs — and exactly one build path ever
# runs, so the skipped one reaches this job straight through the `collect`
# written to absorb it and the upload silently never happens.
if: >-
!cancelled() && needs.collect.result == 'success' &&
needs.prepare.outputs.skipped != 'true'
runs-on: ubuntu-latest
environment:
name: pypi
Expand Down Expand Up @@ -411,6 +418,12 @@ jobs:

tag-and-release:
needs: [prepare, publish]
# The same transitive implicit success() reaches this job through
# `publish`, so it needs a status function of its own. Not `!failure()`:
# a skipped publish is neither failed nor cancelled, and tolerating it
# would push the tag for a release that uploaded nothing.
if: >-
!cancelled() && needs.publish.result == 'success'
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -436,7 +449,7 @@ jobs:
TAG: ${{ needs.prepare.outputs.tag }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: uvx reflex-release@0.1.0a3 push-tag
run: uvx reflex-release@0.1.0a4 push-tag

- name: Create GitHub release
env:
Expand All @@ -449,7 +462,7 @@ jobs:
CHECKSUMS_PATH: SHA256SUMS
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: uvx reflex-release@0.1.0a3 create-release
run: uvx reflex-release@0.1.0a4 create-release

# One dispatch per published tag, on the tag itself, after the upload,
# the tag and the GitHub release: the workflow sees exactly the tree that
Expand All @@ -462,4 +475,4 @@ jobs:
VERSION: ${{ needs.prepare.outputs.version }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: uvx reflex-release@0.1.0a3 post-release
run: uvx reflex-release@0.1.0a4 post-release
42 changes: 28 additions & 14 deletions .github/workflows/release_from_changelog.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Generated by reflex-release; do not edit by hand.
# Re-run `uvx reflex-release@0.1.0a3 sync` after changing [tool.reflex-release] in
# pyproject.toml. `uvx reflex-release@0.1.0a3 sync --check` fails when this file drifts.
# Re-run `uvx reflex-release@0.1.0a4 sync` after changing [tool.reflex-release] in
# pyproject.toml. `uvx reflex-release@0.1.0a4 sync --check` fails when this file drifts.
name: Release from changelog

# The CHANGELOG.md files are the source of truth for publishing. On every push
Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:
env:
REF_NAME: ${{ github.ref_name }}
shell: bash
run: uvx reflex-release@0.1.0a3 detect
run: uvx reflex-release@0.1.0a4 detect

publish:
needs: detect
Expand Down Expand Up @@ -124,23 +124,37 @@ jobs:
DETECT: ${{ needs.detect.result }}
PUBLISH: ${{ needs.publish.result }}
PUBLISH_LAST: ${{ needs.publish-last.result }}
ANY: ${{ needs.detect.outputs.any }}
ANY_LAST: ${{ needs.detect.outputs.any_last }}
shell: bash
run: |
set -euo pipefail
echo "detect: $DETECT, publish: $PUBLISH, publish-last: $PUBLISH_LAST"
failed=0
# Anything that is not success/skipped (failure, cancelled,
# timed_out, a rejected environment approval, ...) is a failed leg.
for leg in "detect:$DETECT" "publish:$PUBLISH" "publish-last:$PUBLISH_LAST"; do
case "${leg#*:}" in
success | skipped) ;;
*)
echo "::error::release leg '${leg%%:*}' ended '${leg#*:}'."
failed=1
;;
esac
done
# A leg is healthy only in the state detect's findings call for:
# 'success' when it had packages to publish, 'skipped' only when it
# had none. Accepting every 'skipped' would report green on a leg
# GitHub skipped despite having work — a skipped job is neither
# failed nor cancelled, so nothing else here would catch it.
check_leg() {
local name=$1 result=$2 had_work=$3
if [[ "$result" == "success" ]]; then
return 0
fi
if [[ "$result" == "skipped" ]]; then
if [[ "$had_work" != "true" ]]; then
return 0
fi
echo "::error::release leg '$name' was skipped even though there were packages to publish."
else
# failure, cancelled, timed_out, a rejected environment approval...
echo "::error::release leg '$name' ended '$result'."
fi
failed=1
}
check_leg detect "$DETECT" true
check_leg publish "$PUBLISH" "$ANY"
check_leg publish-last "$PUBLISH_LAST" "$ANY_LAST"
if [[ "$failed" -eq 1 ]]; then
if [[ "$DETECT" != "success" ]]; then
echo "::error::Changelog detection did not complete (check for a lockstep violation) — no packages were published."
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ NAME ?=
# [tool.reflex-release] — keep the two together, or a local `make news` and the
# release workflows would run different pipelines. Installed into a throwaway
# environment, so the tool is never a dependency of the dev venv.
RELEASE_VERSION ?= 0.1.0a3
RELEASE_VERSION ?= 0.1.0a4
RELEASE_CLI = uvx reflex-release@$(RELEASE_VERSION)

.PHONY: help setup setup-browser check check-full check-browser check-conformance check-docs check-examples check-security check-errors check-api check-import check-ci check-benchmark-harness check-pyplot check-pyplot-speed check-sdist check-wheel check-artifacts check-benchmark-report list-checks test lint format typecheck public-api python-floor js-check rust-check abi-smoke news news-check
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ pythonpath = ["."]
# How the release workflows invoke the tool, pinned to one published version so
# a local run and a CI run cannot drift apart. Bump it (and the `Makefile`'s
# RELEASE_VERSION) and re-run `reflex-release sync`.
cli-command = "uvx reflex-release@0.1.0a3"
cli-command = "uvx reflex-release@0.1.0a4"

# Dispatched once per published tag, on the tag itself, after the upload, the tag
# and the GitHub release exist. The docs site ships the version it documents, and
Expand Down
21 changes: 17 additions & 4 deletions spec/process/production-readiness.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ artifacts carry it. Three consequences worth knowing:
| `build_release_artifacts.yml` | called by `publish.yml`, or manual for a dry run | **This repository's own**: the release matrix — eleven platform wheels, the runtime-verified PyEmscripten wheel, and the sdist. |
| `deploy-docs-stg.yml` | dispatched by `publish.yml` per published tag, or manual | **This repository's own**: builds and deploys the docs site for the version just published. |

The first four come from `reflex-release` (pinned at `0.1.0a3` in
The first four come from `reflex-release` (pinned at `0.1.0a4` in
`[tool.reflex-release] cli-command`), which owns their invariants and tests them
where the tool lives. This repository deliberately does not re-assert their
contents. The last two are its own, and they are the whole integration surface:
Expand Down Expand Up @@ -592,16 +592,29 @@ Keep pushing these in low-conflict increments:
follow-up is wiring an actual TestPyPI upload into that dry-run path (today it
reaches no publish path at all, it doesn't yet push to a test index) plus
refreshed benchmark reports.
- `reflex-release` is pinned at `0.1.0a3`, an alpha. Track its releases and bump
- `reflex-release` is pinned at `0.1.0a4`, an alpha. Track its releases and bump
the pin as it stabilizes, keeping `cli-command` and the `Makefile`'s
`RELEASE_VERSION` on the same version — otherwise `make news` and the release
workflows run different pipelines. `sync --check` on every pull request catches
the half of a partial bump that reaches the workflows.
- Two review findings land in `reflex-release`'s generated workflows, so they are
upstream fixes rather than local edits — patching them here would fork a file
`sync --check` then reports as drift forever, which is the arrangement this
repository just removed. Both were still open as of `0.1.0a3`, which did pick up
a third (an explicit `shell: bash` on every generated `run` step):
repository just removed. Both were still open as of `0.1.0a4`, which did pick up
two job-level `if` fixes in the publish path instead — the second of them
load-bearing for this repository, since `custom-build` is exactly the shape it
describes:
- `publish.yml`'s `publish` and `tag-and-release` jobs now carry explicit
status functions (`!cancelled() && needs.<job>.result == 'success'`). Without
one, GitHub evaluates an implicit `success()` over the whole transitive
dependency closure rather than the direct `needs`, so the build path that
*doesn't* run — here the tool's own `build` job, replaced by
`build_release_artifacts.yml` — would pass straight through `collect` and
the upload would silently never happen.
- `release_from_changelog.yml`'s health check no longer accepts every
`skipped` leg: a leg is healthy as `skipped` only when `detect` found no
packages for it, so a job GitHub skipped despite having work to publish is
now an error rather than a green run that shipped nothing.
- `publish.yml`'s approval gate interpolates the captured `gh api` error into a
`::error::` message. A `gh` error line containing `::` would be re-parsed as a
workflow command, truncating the diagnostic. It cannot leak anything (the job
Expand Down
Loading