Skip to content

Commit 5a81249

Browse files
Merge remote-tracking branch 'origin/develop' into george/generalized_single_port_LE
2 parents 912137a + 73f47b9 commit 5a81249

115 files changed

Lines changed: 7058 additions & 1422 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ If those overrides are omitted, deployment targets are inferred from `release_ty
7979
### `tidy3d-python-client-tests.yml`
8080

8181
Primary CI workflow; it runs on PRs (`latest`, `develop`, `pre/*`), merge queue (`merge_group`), manual dispatch, and `workflow_call`. Highlights:
82-
- **Code quality**: `ruff format`, `ruff check`, `mypy`, `zizmor`, schema regeneration, commit/branch linting.
82+
- **Code quality**: `ruff format`, `ruff check`, `mypy`, `zizmor`, schema regeneration, commit/branch linting, and changelog policy enforcement (no direct `CHANGELOG.md` edits on regular PR branches).
8383
- **Local tests**: Self-hosted Slurm runners on Python 3.10 and 3.13 (coverage enforced, diff-coverage comments for 3.13).
8484
- **Remote tests**: GitHub-hosted matrix across Windows, Linux, and macOS for Python 3.10–3.13.
8585
- **Optional suites**: CLI tests, version consistency checks, submodule validation (non-RC release tags only), and `tidy3d-extras` integration tests can be toggled via inputs.
@@ -150,6 +150,26 @@ Manual or called workflow that updates `poetry.lock`, authenticates against AWS
150150

151151
The workflow creates a PR with branch name `chore/update-poetry-lock-{source_branch}` targeting the specified source branch.
152152

153+
### `tidy3d-python-client-build-changelog-pr.yml`
154+
155+
Manual workflow that builds `CHANGELOG.md` from Towncrier fragments and opens a PR.
156+
157+
**Key inputs:**
158+
- `source_branch` – branch to checkout and build changelog from (defaults to `develop`).
159+
- `target_branch` – branch to open the PR against (defaults to `develop`).
160+
- `release_version` – optional override for the release version. If omitted, it is derived from `pyproject.toml` by stripping `.devN`.
161+
- `release_date` – optional override in `YYYY-MM-DD`. If omitted, UTC `today` is used.
162+
- `previous_version` – optional override for the compare-link previous version. If omitted, the workflow uses the latest reachable stable `vX.Y.Z` tag, and falls back to the latest stable heading in `CHANGELOG.md` when no tag is available.
163+
- `run_workflow` – boolean guard to enable/disable execution.
164+
165+
The workflow:
166+
1. Installs Poetry dependencies (`--extras dev`).
167+
2. Runs `towncrier build --yes`.
168+
3. Runs `scripts/changelog_refs.py` to update compare reference links.
169+
4. Opens a PR with the generated changelog updates.
170+
171+
If no fragments are present in `changelog.d/`, the workflow exits without opening a PR.
172+
153173
## Documentation Workflows
154174

155175
### `tidy3d-docs-sync-readthedocs-repo.yml`
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
name: public/tidy3d/python-client-build-changelog-pr
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
run_workflow:
7+
description: 'Set to true to build changelog and create a PR'
8+
required: true
9+
type: boolean
10+
default: true
11+
source_branch:
12+
description: 'Source branch to checkout and build changelog from'
13+
required: false
14+
type: string
15+
default: 'develop'
16+
target_branch:
17+
description: 'Target branch for the generated changelog PR'
18+
required: false
19+
type: string
20+
default: 'develop'
21+
release_version:
22+
description: 'Optional release version override (for example: 2.11.0)'
23+
required: false
24+
type: string
25+
default: ''
26+
release_date:
27+
description: 'Optional release date override in YYYY-MM-DD (defaults to UTC today)'
28+
required: false
29+
type: string
30+
default: ''
31+
previous_version:
32+
description: 'Optional previous release version override (for example: 2.10.2)'
33+
required: false
34+
type: string
35+
default: ''
36+
37+
permissions:
38+
contents: read
39+
40+
jobs:
41+
build-changelog-pr:
42+
if: github.event.inputs.run_workflow == 'true'
43+
runs-on: ubuntu-latest
44+
permissions:
45+
contents: write
46+
pull-requests: write
47+
steps:
48+
- name: Checkout repository
49+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
50+
with:
51+
ref: ${{ github.event.inputs.source_branch || 'develop' }}
52+
fetch-depth: 0
53+
submodules: false
54+
persist-credentials: false
55+
56+
- name: Set up Python
57+
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
58+
with:
59+
python-version: '3.10'
60+
61+
- name: Install Poetry
62+
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1
63+
with:
64+
version: 2.1.1
65+
virtualenvs-create: true
66+
virtualenvs-in-project: true
67+
68+
- name: Install dependencies
69+
run: |
70+
set -e
71+
poetry install --extras dev --no-interaction
72+
73+
- name: Build changelog
74+
id: build-changelog
75+
env:
76+
INPUT_RELEASE_VERSION: ${{ github.event.inputs.release_version }}
77+
INPUT_RELEASE_DATE: ${{ github.event.inputs.release_date }}
78+
INPUT_PREVIOUS_VERSION: ${{ github.event.inputs.previous_version }}
79+
SOURCE_BRANCH: ${{ github.event.inputs.source_branch || 'develop' }}
80+
run: |
81+
set -euo pipefail
82+
83+
if [[ ! -d changelog.d ]]; then
84+
echo "changelog.d/ directory not found; skipping changelog build."
85+
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
86+
exit 0
87+
fi
88+
89+
fragment_count=$(find changelog.d -maxdepth 1 -type f -name '*.md' ! -name 'README.md' ! -name 'template.md' | wc -l)
90+
if [[ "$fragment_count" -eq 0 ]]; then
91+
echo "No changelog fragments found in changelog.d/."
92+
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
93+
exit 0
94+
fi
95+
96+
relver="${INPUT_RELEASE_VERSION}"
97+
if [[ -z "$relver" ]]; then
98+
relver=$(poetry version -s | sed -E 's/\.dev[0-9]+$//')
99+
fi
100+
relver=$(echo "$relver" | sed -E 's/^v//')
101+
if [[ -z "$relver" ]]; then
102+
echo "::error::Release version is empty after normalization."
103+
exit 1
104+
fi
105+
106+
reldate="${INPUT_RELEASE_DATE}"
107+
if [[ -z "$reldate" ]]; then
108+
reldate=$(date -u +%F)
109+
fi
110+
111+
prevver="${INPUT_PREVIOUS_VERSION}"
112+
prevver=$(echo "$prevver" | sed -E 's/^v//')
113+
if [[ -z "$prevver" ]]; then
114+
prevver=$(git tag --merged HEAD --list 'v*' --sort=-v:refname | sed 's/^v//' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | grep -v "^${relver}$" | head -n1 || true)
115+
fi
116+
if [[ -z "$prevver" ]]; then
117+
prevver=$(grep -E '^## \[[0-9]+\.[0-9]+\.[0-9]+\] - ' CHANGELOG.md | sed -E 's/^## \[([0-9]+\.[0-9]+\.[0-9]+)\].*/\1/' | grep -v "^${relver}$" | head -n1 || true)
118+
fi
119+
120+
safe_source_branch=$(echo "$SOURCE_BRANCH" | tr '/ ' '--')
121+
122+
echo "Building changelog for version ${relver} on ${reldate}"
123+
poetry run towncrier build --yes --version "${relver}" --date "${reldate}"
124+
125+
if [[ -n "$prevver" ]]; then
126+
poetry run python scripts/changelog_refs.py --version "${relver}" --previous-version "${prevver}"
127+
else
128+
poetry run python scripts/changelog_refs.py --version "${relver}"
129+
fi
130+
131+
if git diff --quiet -- CHANGELOG.md changelog.d; then
132+
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
133+
else
134+
echo "changes_detected=true" >> "$GITHUB_OUTPUT"
135+
fi
136+
previous_version_output="${prevver}"
137+
if [[ -z "$previous_version_output" ]]; then
138+
previous_version_output="auto-detected"
139+
fi
140+
echo "release_version=${relver}" >> "$GITHUB_OUTPUT"
141+
echo "release_date=${reldate}" >> "$GITHUB_OUTPUT"
142+
echo "previous_version=${previous_version_output}" >> "$GITHUB_OUTPUT"
143+
echo "safe_source_branch=${safe_source_branch}" >> "$GITHUB_OUTPUT"
144+
145+
- name: Create Pull Request
146+
if: steps.build-changelog.outputs.changes_detected == 'true'
147+
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
148+
with:
149+
token: ${{ secrets.GITHUB_TOKEN }}
150+
commit-message: "chore(changelog): :robot: build release notes for ${{ steps.build-changelog.outputs.release_version }}"
151+
title: "chore(changelog): :robot: build release notes for ${{ steps.build-changelog.outputs.release_version }}"
152+
body: |
153+
This pull request was automatically generated by a GitHub Action.
154+
155+
It builds `CHANGELOG.md` from Towncrier fragments and updates compare reference links.
156+
157+
Source branch: `${{ github.event.inputs.source_branch || 'develop' }}`
158+
Target branch: `${{ github.event.inputs.target_branch || 'develop' }}`
159+
Version: `${{ steps.build-changelog.outputs.release_version }}`
160+
Previous version: `${{ steps.build-changelog.outputs.previous_version }}`
161+
Date: `${{ steps.build-changelog.outputs.release_date }}`
162+
branch: "chore/build-changelog-${{ steps.build-changelog.outputs.safe_source_branch }}-${{ github.run_id }}"
163+
base: "${{ github.event.inputs.target_branch || 'develop' }}"
164+
delete-branch: true

.github/workflows/tidy3d-python-client-tests.yml

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,10 @@ jobs:
387387
388388
- name: enforce-jira-key
389389
id: enforce-jira-key
390-
if: github.event_name == 'pull_request' && github.event.pull_request.user.login != 'dependabot[bot]'
390+
if: >
391+
github.event_name == 'pull_request' &&
392+
github.event.pull_request.user.login != 'dependabot[bot]' &&
393+
!startsWith(github.event.pull_request.head.ref, 'chore/build-changelog-')
391394
env:
392395
STEPS_EXTRACT_BRANCH_NAME_OUTPUTS_BRANCH_NAME: ${{ steps.extract-branch-name.outputs.branch_name }}
393396
run: |
@@ -425,6 +428,44 @@ jobs:
425428
fi
426429
fi
427430
431+
enforce-changelog-policy:
432+
needs: determine-test-scope
433+
runs-on: ubuntu-latest
434+
if: needs.determine-test-scope.outputs.pr_review_tests == 'true'
435+
name: enforce-changelog-policy
436+
steps:
437+
- name: Check out source code
438+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
439+
with:
440+
fetch-depth: 0
441+
persist-credentials: false
442+
443+
- name: disallow-manual-changelog-edits
444+
env:
445+
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
446+
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
447+
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
448+
run: |
449+
set -euo pipefail
450+
451+
if ! git cat-file -e "${PR_BASE_SHA}:changelog.d/README.md" 2>/dev/null; then
452+
echo "Towncrier changelog workflow not detected in base commit; skipping check."
453+
exit 0
454+
fi
455+
456+
if [[ "$PR_BRANCH" == chore/build-changelog-* ]]; then
457+
echo "Auto-generated changelog branch detected; allowing CHANGELOG.md edits."
458+
exit 0
459+
fi
460+
461+
if git diff --name-only "$PR_BASE_SHA" "$PR_HEAD_SHA" | grep -Fxq "CHANGELOG.md"; then
462+
echo "❌ Manual edits to CHANGELOG.md are not allowed."
463+
echo "Add changelog fragments under changelog.d/ instead."
464+
exit 1
465+
fi
466+
467+
echo "✅ No direct CHANGELOG.md edits detected."
468+
428469
lint-commit-messages:
429470
needs: determine-test-scope
430471
runs-on: ubuntu-latest
@@ -1365,6 +1406,7 @@ jobs:
13651406
- verify-schema-change
13661407
- lint-commit-messages
13671408
- lint-branch-name
1409+
- enforce-changelog-policy
13681410
- zizmor
13691411
- develop-cli-tests
13701412
- verify-version-consistency
@@ -1420,6 +1462,12 @@ jobs:
14201462
echo "❌ Branch name linting failed."
14211463
exit 1
14221464
1465+
- name: check-changelog-policy
1466+
if: ${{ needs.determine-test-scope.outputs.pr_review_tests == 'true' && needs.enforce-changelog-policy.result != 'success' && needs.enforce-changelog-policy.result != 'skipped' }}
1467+
run: |
1468+
echo "❌ Changelog policy check failed."
1469+
exit 1
1470+
14231471
- name: check-zizmor-static-analysis
14241472
if: ${{ needs.determine-test-scope.outputs.code_quality_tests == 'true' && needs.zizmor.result != 'success' && needs.zizmor.result != 'skipped' }}
14251473
run: |

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
- Follow Conventional Commits per `.commitlintrc.json`.
4949
- Branch names must use an allowed prefix (`chore`, `hotfix`, `daily-chore`) or include a Jira key to satisfy CI.
5050
- PRs should link issues, summarize behavior changes, list the `poetry run …` checks you executed, and call out docs/schema updates.
51-
- Add a changelog entry under `## [Unreleased]` in `CHANGELOG.md` for user-facing changes (new features, bug fixes, breaking changes).
51+
- For user-facing changes (new features, bug fixes, breaking changes), add a changelog fragment under `changelog.d/` using the pattern `<PR_NUMBER>.<type>.md` (for example `1234.added.md`) instead of editing `CHANGELOG.md` directly; CI rejects direct `CHANGELOG.md` edits on regular PR branches.
52+
- Release managers can use the GitHub Actions workflow `public/tidy3d/python-client-build-changelog-pr` to generate `CHANGELOG.md` from fragments and open a PR (defaults source/target to `develop`).
5253

5354
_Reminder: update this AGENTS.md whenever workflow, tooling, or review expectations change so agents stay in sync with the repo._

0 commit comments

Comments
 (0)