Migrate release automation from scripts/release.py to reflex-release package - #6941
Conversation
The publish pipeline was a hand-maintained set of workflows over
scripts/release.py and .github/scripts/*. reflex-release, which lives in
this repository at packages/reflex-release, is that same pipeline packaged
and generalized, so the workflows now come from its templates instead.
Every reflex-specific behavior moves into [tool.reflex-release] in the
repo-root pyproject.toml: the reflex/reflex-base lockstep pair (one
version, exact pin rewritten at build time, reflex uploaded last), the
internal packages that patch-release on every push, the America/Los_Angeles
release timezone, and the packages exempt from the news-fragment check. The
.pyi check on the reflex wheel becomes the pipeline's post_build.sh hook,
and the dev-pin gate is now reflex-release's own check-dev-pins, which
reports identically to scripts/check_min_deps.py --check-dev-pins.
cli-command runs the copy in this repository straight from uv.lock rather
than a version published to PyPI:
uv run --frozen --package reflex-release reflex-release
That pins the pipeline to one commit. The workflows are rendered from the
templates of the commit that contains them, so `sync --check` — which
changelog.yml now runs on every pull request — fails both on a workflow
edited by hand and on a template change that was never regenerated. The
two can no longer drift apart.
What changes in behavior, beyond the move:
- publish.yml splits validation, build and verification into separate
unprivileged jobs and puts the SHA-256 manifest in front of the approver;
the manifest is also attached to the GitHub release.
- changelog.yml also runs on pull requests targeting the publishing
branches, and its release-branch exemption for version headings now
requires the pull request to be authored by github-actions[bot].
- Dispatch release takes a comma-separated package list instead of one
checkbox per package, which no longer fits GitHub's workflow_dispatch
input limit.
- auto_release_internal.yml triggers on an internal package's src/ rather
than its whole directory, matching what detection counts as its source,
and diffs the whole pushed range instead of the last commit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KXbekKtPyfbENVnZdTRxKX
Four changes to the tool, and the regenerated workflows that follow from them. Pinned toolchain. New uv-version and python-version keys write the uv and Python the generated workflows install verbatim into every setup-uv step, in place of whatever that action resolves at run time. Both default to a version reflex-release itself pins, so upgrading the tool moves the release toolchain with it and `sync --check` reports that as drift until the workflows are regenerated — the same signal a template change already gives. A repository that wants its own cadence sets either key; "" leaves that version to the setup action. Both are interpolated into a quoted YAML scalar, so they are validated against a version-or-specifier pattern rather than trusted. The pins live in one rendered block instead of in each template, and a test asserts every setup-uv step in every template carries the placeholder that receives it: render() only fails on a placeholder it cannot substitute, so a step added without one would otherwise silently install an unpinned uv. Pinned build backend. reflex-release pins hatchling and uv-dynamic-versioning exactly. Build requirements are resolved fresh rather than locked, so a repository that vendors the tool — as this one now does, running it out of uv.lock — no longer has the backend that builds its release tooling move underneath it. Checkbox limit. Ten was wrong: workflow_dispatch takes twenty inputs, and this repository's own dispatch form has been running eighteen checkboxes. Raised to nineteen packages plus the release action, which brings the checkboxes back here — the comma-separated fallback was a regression, not a fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KXbekKtPyfbENVnZdTRxKX
Greptile SummaryThe PR replaces the legacy release script and shell helpers with the config-driven
Confidence Score: 5/5The PR appears safe to merge because no blocking failure remains within the scope of the supplied follow-up review. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| packages/reflex-release/src/reflex_release/commands.py | Implements the migrated planning, materialization, publishing, tagging, and release-branch command flows. |
| packages/reflex-release/src/reflex_release/config.py | Adds and validates release configuration for pinned toolchains, package exclusions, lockstep groups, and workflow generation. |
| packages/reflex-release/src/reflex_release/devpins.py | Implements dependency-floor repinning and lock-file refresh behavior used during release materialization. |
| packages/reflex-release/src/reflex_release/scaffold.py | Renders config-driven workflow templates and supports generated-workflow drift detection. |
| packages/reflex-release/src/reflex_release/templates/workflows/publish.yml | Defines the generated staged publishing pipeline, artifact verification, approval gate, and post-publish operations. |
| .github/workflows/publish.yml | Materializes the generated five-stage package publication workflow for this repository. |
| .github/workflows/dispatch_release.yml | Migrates release selection, planning, changelog materialization, and release branch or PR creation to reflex-release commands. |
| pyproject.toml | Configures repository packages, lockstep publishing, internal packages, excluded packages, and workflow toolchain versions. |
Reviews (5): Last reviewed commit: "Merge branch 'main' into claude/reflex-g..." | Re-trigger Greptile
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
All reported issues were addressed across 33 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…actions-release-28uzj0
Merge brings in two reflex-release changes that alter the templates: dependency-pin lifting during materialize (#6889) and the skip-propagation fix in publish.yml and release_from_changelog.yml (#6950). `sync --check` flagged all three affected workflows as drifted, which is the mechanism working — a template change on main that never reached this repository's own workflows is exactly what it is there to catch. Regenerated; the uv and Python pins and the dispatch checkboxes are unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KXbekKtPyfbENVnZdTRxKX
Both from cubic's review of #6941. never-publish-packages, for the packages a repository builds but never releases. changelog-exempt-packages was the closest thing, and it only waives the news-fragment requirement — it left integrations-docs with a release checkbox and publishable by hand, which the checkbox form made visible. A listed package now gets no checkbox, is never auto-selected, is skipped by changelog detection even when it has a CHANGELOG.md, needs no fragment, and is refused by prepare-publish, so the one remaining way to reach it — typing it into the publish workflow — fails in the first unprivileged job rather than at verify-dist after a build. Being unreleasable it cannot also be a lockstep member, a custom-build package, latest-release-package or internal; each is rejected when the configuration loads. integrations-docs moves to the new key, which drops it from the Dispatch release form. Config's new fields move to the end of the dataclass. It is exported, so the generated __init__ has a positional contract: uv_version and python_version sat after cli_command, shifting every later argument for a caller that does not pass everything by keyword. A test pins the historical field order as a prefix so the next field added lands in the right place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KXbekKtPyfbENVnZdTRxKX
Ports #6916 into the reflex-release changelog template, so the fix applies to every repository the tool scaffolds rather than to this one's copy of a workflow that is now generated. skip-changelog and changelog-version-edit waive parts of the check, so a verdict is only valid for the label set it was computed under. Three ways that broke: applying a label after the last push started no run at all, removing one left the green run that label produced standing with the gate silently open, and re-running a failed run replayed the original event payload — where the label does not exist yet — so the check kept failing until someone pushed again. So: labeled/unlabeled join the trigger types, and the labels are read back from the API into a step output the two guarded steps test, in a step that runs before the checkout because it needs nothing but `gh`. The job itself stays ungated: a job skipped by `if` reports its check as skipped, which branch protection counts as passing, so a cheap no-op on an unrelated label would overwrite a real failure with a green. Regenerated reflex's own changelog.yml from the template. The jq program was checked against the label sets it has to distinguish, running the generated step itself against a stubbed `gh`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KXbekKtPyfbENVnZdTRxKX
…-context-refactor-jv3pig Restores the second parent that the previous commit dropped, and picks up #6941 (release automation moved into the ``reflex-release`` package). The previous merge of ``origin/main`` resolved correctly but was recorded with a single parent, so every commit on main's side of that merge read as belonging to this branch — 120 files against a true diff of a fraction of that. The four files that conflict here (``components/memo.py``, ``pyi_hashes.json``, ``tests_playwright/test_memo.py``, ``compiler/test_memoize_plugin.py``) are untouched by main since, so their resolutions carry over unchanged.
Type of change
scripts/release.pyis removed and replaced with thereflex-releasepackage)Summary
This PR completes the migration of the changelog-driven release automation from the monolithic
scripts/release.pyscript to the modularreflex-releasepackage. The old script and its associated shell helper scripts are removed, and the generated CI workflows are now produced by thereflex-releasepackage's templating system.Key changes:
Removed legacy release infrastructure:
scripts/release.py(1329 lines) — the original monolithic release helperscripts/release.py.lock— its dependency lock filetests/units/test_release.py(779 lines) — tests for the old script.github/scripts/dispatch_release/and.github/scripts/publish/Migrated to reflex-release package:
packages/reflex-release/src/reflex_release/reflex-release syncfrom[tool.reflex-release]config inpyproject.tomlEnhanced workflow generation:
uv-versionandpython-versionconfiguration to pin exact toolchain versions in generated workflows (defaults: uv 0.12.5, Python 3.10)workflow_dispatchpackage checkbox limit from 9 to 19 (matching GitHub's actual limit)@@UV_SETUP_WITH@@placeholders for pinned uv setup.github/scripts/publish/post_build.shhook for repository-specific artifact checksConfiguration:
[tool.reflex-release]section topyproject.tomlwith package list and workflow configurationreflex-release sync --checkDocumentation updates:
CONTRIBUTING.mdto referencereflex-release createinstead oftowncrier createpackages/reflex-release/README.mdwith new configuration optionsAGENTS.mdwith reflex-release commandsWorkflow improvements:
All generated workflows include a header indicating they are auto-generated and instructions for regeneration.
Testing
tests/units/reflex_release/cover the reflex-release package functionalityDEFAULT_UV_VERSION,DEFAULT_PYTHON_VERSION)reflex-release sync --checkMigration notes
Repositories using this release automation should:
uv run --frozen --package reflex-release reflex-release syncto generate workflows from the new configurationuv-versionandpython-versionto[tool.reflex-release]to pin toolchain versionsreflex-releasecommands instead ofscripts/release.pyhttps://claude.ai/code/session_01KXbekKtPyfbENVnZdTRxKX