Skip to content

Migrate release automation from scripts/release.py to reflex-release package - #6941

Open
masenf wants to merge 5 commits into
mainfrom
claude/reflex-github-actions-release-28uzj0
Open

Migrate release automation from scripts/release.py to reflex-release package#6941
masenf wants to merge 5 commits into
mainfrom
claude/reflex-github-actions-release-28uzj0

Conversation

@masenf

@masenf masenf commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Type of change

  • Breaking change (the old scripts/release.py is removed and replaced with the reflex-release package)
  • This change requires a documentation update

Summary

This PR completes the migration of the changelog-driven release automation from the monolithic scripts/release.py script to the modular reflex-release package. The old script and its associated shell helper scripts are removed, and the generated CI workflows are now produced by the reflex-release package's templating system.

Key changes:

  1. Removed legacy release infrastructure:

    • scripts/release.py (1329 lines) — the original monolithic release helper
    • scripts/release.py.lock — its dependency lock file
    • tests/units/test_release.py (779 lines) — tests for the old script
    • Shell helper scripts in .github/scripts/dispatch_release/ and .github/scripts/publish/
  2. Migrated to reflex-release package:

    • All release logic now lives in packages/reflex-release/src/reflex_release/
    • Generated workflows are produced by reflex-release sync from [tool.reflex-release] config in pyproject.toml
    • Workflows now include generation headers and drift detection
  3. Enhanced workflow generation:

    • Added uv-version and python-version configuration to pin exact toolchain versions in generated workflows (defaults: uv 0.12.5, Python 3.10)
    • Increased workflow_dispatch package checkbox limit from 9 to 19 (matching GitHub's actual limit)
    • Workflows now include @@UV_SETUP_WITH@@ placeholders for pinned uv setup
    • Added optional .github/scripts/publish/post_build.sh hook for repository-specific artifact checks
  4. Configuration:

    • Added [tool.reflex-release] section to pyproject.toml with package list and workflow configuration
    • Workflows are now generated and checked for drift via reflex-release sync --check
  5. Documentation updates:

    • Updated CONTRIBUTING.md to reference reflex-release create instead of towncrier create
    • Updated packages/reflex-release/README.md with new configuration options
    • Updated AGENTS.md with reflex-release commands

Workflow improvements:

  • publish.yml: Split into five stages (prepare, build, collect, publish, tag-and-release) with clearer separation of concerns
  • dispatch_release.yml: Simplified with generated package checkboxes and improved documentation
  • changelog.yml: Added workflow drift detection and improved guards
  • release_from_changelog.yml: Cleaner structure with generated configuration
  • auto_release_internal.yml: Simplified internal package release automation

All generated workflows include a header indicating they are auto-generated and instructions for regeneration.

Testing

  • Existing unit tests in tests/units/reflex_release/ cover the reflex-release package functionality
  • Added tests for new configuration options (DEFAULT_UV_VERSION, DEFAULT_PYTHON_VERSION)
  • Added tests for workflow template pinning and drift detection
  • CI workflows validate generated workflow consistency via reflex-release sync --check

Migration notes

Repositories using this release automation should:

  1. Run uv run --frozen --package reflex-release reflex-release sync to generate workflows from the new configuration
  2. Optionally add uv-version and python-version to [tool.reflex-release] to pin toolchain versions
  3. Update any custom release scripts to use reflex-release commands instead of scripts/release.py

https://claude.ai/code/session_01KXbekKtPyfbENVnZdTRxKX

Review in cubic

claude added 2 commits August 24, 2026 20:52
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
@masenf
masenf requested a review from a team as a code owner August 24, 2026 23:54
@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR replaces the repository’s legacy release scripts with the reflex-release package and generated workflows.

  • Moves release planning, publishing, tagging, and changelog operations to package commands.
  • Adds configurable pinned uv and Python versions to generated workflows.
  • Adds support for packages that are built but never published.
  • Splits publishing into preparation, build, collection, approval, upload, and release stages.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/reflex-release/src/reflex_release/config.py Adds validated toolchain pins and never-published package configuration with consistency checks.
packages/reflex-release/src/reflex_release/commands.py Prevents excluded packages from entering explicit planning or publishing paths.
packages/reflex-release/src/reflex_release/discovery.py Excludes never-published packages from changelog discovery and release selection.
packages/reflex-release/src/reflex_release/scaffold.py Injects configured toolchain pins into generated workflows and omits release scaffolding for excluded packages.
.github/workflows/publish.yml Migrates publishing to the generated multi-stage workflow with artifact verification and approval gating.
.github/workflows/release_from_changelog.yml Generalizes ordered publishing for lockstep package groups through generated workflow outputs.
.github/workflows/dispatch_release.yml Replaces shell helpers with reflex-release commands and generated package selection.
pyproject.toml Defines repository release configuration, package exclusions, and the reflex/reflex-base lockstep group.

Reviews (3): Last reviewed commit: "Add never-publish-packages; append new C..." | Re-trigger Greptile

@codspeed-hq

codspeed-hq Bot commented Aug 24, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 27 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing claude/reflex-github-actions-release-28uzj0 (5ea57b7) with main (a4d99be)

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 33 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread pyproject.toml Outdated
Comment thread packages/reflex-release/src/reflex_release/config.py Outdated
FarhanAliRaza
FarhanAliRaza previously approved these changes Aug 25, 2026
claude added 2 commits August 27, 2026 20:02
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants