Skip to content
Open
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
86 changes: 86 additions & 0 deletions .github/workflows/changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: changelog

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.id }}
cancel-in-progress: true

on:
pull_request:
branches: ["main"]

jobs:
changelog:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check for skip-changelog label
id: skip
shell: bash
env:
SKIP_CHANGELOG: ${{ contains(github.event.pull_request.labels.*.name, 'skip-changelog') }}
run: |
if [ "$SKIP_CHANGELOG" = "true" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "PR has 'skip-changelog' label; bypassing changelog check."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
Comment thread
adhami3310 marked this conversation as resolved.
- uses: actions/checkout@v4
if: steps.skip.outputs.skip != 'true'
with:
fetch-depth: 0
- uses: ./.github/actions/setup_build_env
if: steps.skip.outputs.skip != 'true'
with:
python-version: "3.14"
run-uv-sync: true
- name: Determine affected packages
if: steps.skip.outputs.skip != 'true'
id: affected
shell: bash
run: |
set -euo pipefail
changed=$(git diff --name-only origin/main...HEAD)
affected=()
if printf '%s\n' "$changed" | grep -qE '^reflex/'; then
affected+=(".")
fi
for pkg_dir in packages/*/; do
pkg=$(basename "$pkg_dir")
[[ "$pkg" == "integrations-docs" ]] && continue
if printf '%s\n' "$changed" | grep -qE "^packages/$pkg/src/"; then
affected+=("${pkg_dir%/}")
fi
done
printf '%s\n' "${affected[@]}" > affected.txt
echo "Affected packages:"
cat affected.txt
- name: Check for news fragments
if: steps.skip.outputs.skip != 'true'
shell: bash
run: |
set -euo pipefail
if [ ! -s affected.txt ]; then
echo "No packaged source changes in this PR; no changelog fragments required."
exit 0
fi
failed=0
while IFS= read -r pkg_dir; do
[ -z "$pkg_dir" ] && continue
echo "::group::towncrier check ($pkg_dir)"
if ! uv run towncrier check --config pyproject.toml --dir "$pkg_dir" --compare-with origin/main; then
failed=1
fi
echo "::endgroup::"
done < affected.txt
if [ "$failed" -ne 0 ]; then
echo ""
echo "One or more affected packages is missing a news fragment under <package>/news/."
echo "Add a fragment named <pr-number>.<type>.md where <type> is one of:"
echo " breaking, deprecation, feature, bugfix, performance, docs, misc"
echo "Or apply the 'skip-changelog' label if the change is genuinely not user-facing."
exit 1
fi
32 changes: 32 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,38 @@ Within the 'test' directory of Reflex you can add to a test file already there o
- Any edge cases or potential problem areas.
- Any interactions between different parts of the code.

## 📝 Changelog Fragments

Each PR that changes the source of a published package must add a news fragment describing the change. Fragments are assembled into `CHANGELOG.md` at release time by [towncrier](https://towncrier.readthedocs.io/).

**Where:** add the fragment under the affected package's `news/` directory. For the main `reflex` package, that's the repo-root `news/`. For sub-packages it's `packages/<name>/news/`.

**Filename:** `<pr-or-issue-number>.<type>.md`, where `<type>` is one of:

| Type | When to use |
| --- | --- |
| `breaking` | Backwards-incompatible change users need to adapt to |
| `deprecation` | API marked deprecated but still functional |
| `feature` | New user-facing functionality |
| `bugfix` | Fix for an incorrect behavior |
| `performance` | Speed, memory, or startup improvement |
| `docs` | Documentation or docstring changes |
| `misc` | Internal refactor, build, or dependency change that still warrants mention |

**Content:** one or two sentences, written for users reading release notes (not reviewers of the diff).

**Create a fragment from the CLI:**

```bash
uv run towncrier create --config pyproject.toml --dir packages/reflex-ui 1234.feature.md
```

Drop `--dir` for a fragment against the main `reflex` package.

If you don't yet know the PR number, use an [orphan fragment](https://towncrier.readthedocs.io/en/stable/cli.html#towncrier-create) (`+.feature.md`) and rename it after opening the PR.

**Skipping the fragment check:** for PRs that are genuinely not user-facing (CI-only tweaks, script fixes, test-only changes), apply the `skip-changelog` label on the PR to bypass the changelog CI check.

## ✅ Making a PR

Once you solve a current issue or improvement to Reflex, you can make a PR, and we will review the changes.
Expand Down
Empty file added news/.gitkeep
Empty file.
1 change: 1 addition & 0 deletions news/6350.misc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Introduced towncrier-based changelog management. Each PR that changes package source now adds a fragment under the affected package's `news/` directory; fragments are assembled into `CHANGELOG.md` at release time. See CONTRIBUTING.md for the full workflow.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
50 changes: 49 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ dev = [
"sqlmodel",
"starlette-admin",
"toml",
"towncrier",
"uvicorn",
]

Expand Down Expand Up @@ -166,7 +167,6 @@ target-version = "py310"
output-format = "concise"
extend-exclude = ["tests/units/reflex_base/utils/pyi_generator/golden"]
lint.isort.split-on-trailing-comma = false
preview = true
lint.select = ["ALL"]
lint.ignore = [
"A",
Expand Down Expand Up @@ -212,6 +212,7 @@ lint.flake8-bugbear.extend-immutable-calls = [
"reflex_base.utils.types.Unset",
"reflex_base.vars.base.Var.create",
]
preview = true

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
Expand Down Expand Up @@ -301,6 +302,53 @@ exclude_also = [
[tool.coverage.html]
directory = "coverage_html_report"

[tool.towncrier]
# Shared monorepo config. package/name intentionally empty — each invocation
# passes --dir <package_path> --name <package> --version <vX.Y.Z> to target a
# specific package. See https://towncrier.readthedocs.io/en/stable/monorepo.html
package = ""
name = ""
directory = "news"
filename = "CHANGELOG.md"
title_format = "## {version} ({project_date})"
issue_format = "[#{issue}](https://github.com/reflex-dev/reflex/issues/{issue})"
start_string = "<!-- towncrier release notes start -->\n"

[[tool.towncrier.type]]
directory = "breaking"
name = "Breaking Changes"
showcontent = true

[[tool.towncrier.type]]
directory = "deprecation"
name = "Deprecations"
showcontent = true

[[tool.towncrier.type]]
directory = "feature"
name = "Features"
showcontent = true

[[tool.towncrier.type]]
directory = "bugfix"
name = "Bug Fixes"
showcontent = true

[[tool.towncrier.type]]
directory = "performance"
name = "Performance"
showcontent = true

[[tool.towncrier.type]]
directory = "docs"
name = "Documentation"
showcontent = true

[[tool.towncrier.type]]
directory = "misc"
name = "Miscellaneous"
showcontent = true

[tool.uv]
required-version = ">=0.7.0"

Expand Down
16 changes: 16 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading