Skip to content

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

Open
masenf wants to merge 2 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 2 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 script and shell helpers with the bundled reflex-release package and generated workflows.

  • Adds centralized release configuration and generated-workflow drift checks.
  • Splits publishing into preparation, build, collection, approval-gated upload, and tagging/release stages.
  • Pins uv and Python versions across generated workflows and restores package checkbox dispatch within GitHub’s input limit.
  • Preserves repository-specific wheel validation through a post-build hook.

Confidence Score: 5/5

The PR appears safe to merge because no concrete blocking or independently actionable changed-code issue was established.

The generated workflows consistently invoke the bundled release package, validate configuration and generated-file drift, preserve artifact checks, and keep publication behind the existing environment approval boundary.

Important Files Changed

Filename Overview
packages/reflex-release/src/reflex_release/config.py Adds validated uv and Python workflow pins with safe defaults and explicit opt-out behavior.
packages/reflex-release/src/reflex_release/scaffold.py Renders pinned setup-uv blocks and expands generated dispatch checkboxes to GitHub’s current input limit.
packages/reflex-release/src/reflex_release/templates/workflows/publish.yml Defines the staged release pipeline, artifact verification, approval gate, publication, and post-publish release flow.
.github/workflows/publish.yml Generated repository workflow implements the new five-stage publishing pipeline using the bundled release package.
.github/workflows/dispatch_release.yml Migrates package selection, planning, changelog materialization, and branch or PR creation to reflex-release commands.
.github/workflows/changelog.yml Migrates changelog validation and adds generated-workflow drift detection.
pyproject.toml Adds the repository-specific release package, lockstep, internal-package, and changelog configuration.
.github/scripts/publish/post_build.sh Retains the repository-specific check that reflex wheels contain generated type stubs.
tests/units/reflex_release/test_config.py Covers defaults, overrides, opt-outs, and validation for toolchain pins.
tests/units/reflex_release/test_scaffold.py Verifies every generated setup-uv step receives configured toolchain pins and remains valid when pins are disabled.

Reviews (1): Last reviewed commit: "Pin the release toolchain in reflex-rele..." | 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 (a85624a) with main (6b44604)

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.

2 issues found across 33 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="pyproject.toml">

<violation number="1" location="pyproject.toml:351">
P1: `changelog-exempt-packages` only removes the news-fragment requirement; it does not exclude `integrations-docs` from release selection or PyPI publishing. Add a supported non-publish exclusion and regenerate workflows, or change this classification to match the package's intended release destination.</violation>
</file>

<file name="packages/reflex-release/src/reflex_release/config.py">

<violation number="1" location="packages/reflex-release/src/reflex_release/config.py:198">
P2: Adding these fields before the existing dataclass fields breaks positional `Config(...)` callers: the old fourth argument now populates `uv_version`, and subsequent values shift as well. Append the new fields after the existing fields to preserve the exported constructor’s positional contract.</violation>
</file>

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

Re-trigger cubic

Comment thread pyproject.toml
internal-packages = ["reflex-components-internal", "reflex-site-shared"]
# Ships inside the docs site rather than to PyPI, so pull requests touching it
# need no news fragment.
changelog-exempt-packages = ["integrations-docs"]

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.

P1: changelog-exempt-packages only removes the news-fragment requirement; it does not exclude integrations-docs from release selection or PyPI publishing. Add a supported non-publish exclusion and regenerate workflows, or change this classification to match the package's intended release destination.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At pyproject.toml, line 351:

<comment>`changelog-exempt-packages` only removes the news-fragment requirement; it does not exclude `integrations-docs` from release selection or PyPI publishing. Add a supported non-publish exclusion and regenerate workflows, or change this classification to match the package's intended release destination.</comment>

<file context>
@@ -327,6 +327,36 @@ exclude_also = [
+internal-packages = ["reflex-components-internal", "reflex-site-shared"]
+# Ships inside the docs site rather than to PyPI, so pull requests touching it
+# need no news fragment.
+changelog-exempt-packages = ["integrations-docs"]
+
+# reflex's metadata pins reflex-base exactly, so the two always release together
</file context>

root: Path
allow_self_review: bool = True
cli_command: str = "uvx reflex-release"
uv_version: str = DEFAULT_UV_VERSION

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.

P2: Adding these fields before the existing dataclass fields breaks positional Config(...) callers: the old fourth argument now populates uv_version, and subsequent values shift as well. Append the new fields after the existing fields to preserve the exported constructor’s positional contract.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/reflex-release/src/reflex_release/config.py, line 198:

<comment>Adding these fields before the existing dataclass fields breaks positional `Config(...)` callers: the old fourth argument now populates `uv_version`, and subsequent values shift as well. Append the new fields after the existing fields to preserve the exported constructor’s positional contract.</comment>

<file context>
@@ -172,6 +195,8 @@ class Config:
     root: Path
     allow_self_review: bool = True
     cli_command: str = "uvx reflex-release"
+    uv_version: str = DEFAULT_UV_VERSION
+    python_version: str = DEFAULT_PYTHON_VERSION
     dispatch_package_inputs: str = "auto"
</file context>

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.

2 participants