Skip to content
Merged
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
22 changes: 21 additions & 1 deletion .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ If those overrides are omitted, deployment targets are inferred from `release_ty
### `tidy3d-python-client-tests.yml`

Primary CI workflow; it runs on PRs (`latest`, `develop`, `pre/*`), merge queue (`merge_group`), manual dispatch, and `workflow_call`. Highlights:
- **Code quality**: `ruff format`, `ruff check`, `mypy`, `zizmor`, schema regeneration, commit/branch linting.
- **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).
- **Local tests**: Self-hosted Slurm runners on Python 3.10 and 3.13 (coverage enforced, diff-coverage comments for 3.13).
- **Remote tests**: GitHub-hosted matrix across Windows, Linux, and macOS for Python 3.10–3.13.
- **Optional suites**: CLI tests, version consistency checks, submodule validation (non-RC release tags only), and `tidy3d-extras` integration tests can be toggled via inputs.
Expand Down Expand Up @@ -150,6 +150,26 @@ Manual or called workflow that updates `poetry.lock`, authenticates against AWS

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

### `tidy3d-python-client-build-changelog-pr.yml`

Manual workflow that builds `CHANGELOG.md` from Towncrier fragments and opens a PR.

**Key inputs:**
- `source_branch` – branch to checkout and build changelog from (defaults to `develop`).
- `target_branch` – branch to open the PR against (defaults to `develop`).
- `release_version` – optional override for the release version. If omitted, it is derived from `pyproject.toml` by stripping `.devN`.
- `release_date` – optional override in `YYYY-MM-DD`. If omitted, UTC `today` is used.
- `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.
- `run_workflow` – boolean guard to enable/disable execution.

The workflow:
1. Installs Poetry dependencies (`--extras dev`).
2. Runs `towncrier build --yes`.
3. Runs `scripts/changelog_refs.py` to update compare reference links.
4. Opens a PR with the generated changelog updates.

If no fragments are present in `changelog.d/`, the workflow exits without opening a PR.

## Documentation Workflows

### `tidy3d-docs-sync-readthedocs-repo.yml`
Expand Down
164 changes: 164 additions & 0 deletions .github/workflows/tidy3d-python-client-build-changelog-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
name: public/tidy3d/python-client-build-changelog-pr

on:
workflow_dispatch:
inputs:
run_workflow:
description: 'Set to true to build changelog and create a PR'
required: true
type: boolean
default: true
source_branch:
description: 'Source branch to checkout and build changelog from'
required: false
type: string
default: 'develop'
target_branch:
description: 'Target branch for the generated changelog PR'
required: false
type: string
default: 'develop'
release_version:
description: 'Optional release version override (for example: 2.11.0)'
required: false
type: string
default: ''
release_date:
description: 'Optional release date override in YYYY-MM-DD (defaults to UTC today)'
required: false
type: string
default: ''
previous_version:
description: 'Optional previous release version override (for example: 2.10.2)'
required: false
type: string
default: ''

permissions:
contents: read

jobs:
build-changelog-pr:
if: github.event.inputs.run_workflow == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.inputs.source_branch || 'develop' }}
fetch-depth: 0
submodules: false
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: '3.10'

- name: Install Poetry
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1
with:
version: 2.1.1
virtualenvs-create: true
virtualenvs-in-project: true

- name: Install dependencies
run: |
set -e
poetry install --extras dev --no-interaction

- name: Build changelog
id: build-changelog
env:
INPUT_RELEASE_VERSION: ${{ github.event.inputs.release_version }}
INPUT_RELEASE_DATE: ${{ github.event.inputs.release_date }}
INPUT_PREVIOUS_VERSION: ${{ github.event.inputs.previous_version }}
SOURCE_BRANCH: ${{ github.event.inputs.source_branch || 'develop' }}
run: |
set -euo pipefail

if [[ ! -d changelog.d ]]; then
echo "changelog.d/ directory not found; skipping changelog build."
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
exit 0
fi

fragment_count=$(find changelog.d -maxdepth 1 -type f -name '*.md' ! -name 'README.md' ! -name 'template.md' | wc -l)
if [[ "$fragment_count" -eq 0 ]]; then
Comment thread
cursor[bot] marked this conversation as resolved.
echo "No changelog fragments found in changelog.d/."
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
exit 0
fi

relver="${INPUT_RELEASE_VERSION}"
if [[ -z "$relver" ]]; then
relver=$(poetry version -s | sed -E 's/\.dev[0-9]+$//')
fi
relver=$(echo "$relver" | sed -E 's/^v//')
if [[ -z "$relver" ]]; then
echo "::error::Release version is empty after normalization."
exit 1
fi

reldate="${INPUT_RELEASE_DATE}"
if [[ -z "$reldate" ]]; then
reldate=$(date -u +%F)
fi

prevver="${INPUT_PREVIOUS_VERSION}"
prevver=$(echo "$prevver" | sed -E 's/^v//')
if [[ -z "$prevver" ]]; then
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)
fi
Comment thread
cursor[bot] marked this conversation as resolved.
if [[ -z "$prevver" ]]; then
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)
fi

safe_source_branch=$(echo "$SOURCE_BRANCH" | tr '/ ' '--')

echo "Building changelog for version ${relver} on ${reldate}"
poetry run towncrier build --yes --version "${relver}" --date "${reldate}"

if [[ -n "$prevver" ]]; then
poetry run python scripts/changelog_refs.py --version "${relver}" --previous-version "${prevver}"
else
poetry run python scripts/changelog_refs.py --version "${relver}"
fi

if git diff --quiet -- CHANGELOG.md changelog.d; then
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
else
echo "changes_detected=true" >> "$GITHUB_OUTPUT"
fi
previous_version_output="${prevver}"
if [[ -z "$previous_version_output" ]]; then
previous_version_output="auto-detected"
fi
echo "release_version=${relver}" >> "$GITHUB_OUTPUT"
echo "release_date=${reldate}" >> "$GITHUB_OUTPUT"
echo "previous_version=${previous_version_output}" >> "$GITHUB_OUTPUT"
echo "safe_source_branch=${safe_source_branch}" >> "$GITHUB_OUTPUT"

- name: Create Pull Request
if: steps.build-changelog.outputs.changes_detected == 'true'
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "chore(changelog): :robot: build release notes for ${{ steps.build-changelog.outputs.release_version }}"
title: "chore(changelog): :robot: build release notes for ${{ steps.build-changelog.outputs.release_version }}"
body: |
This pull request was automatically generated by a GitHub Action.

It builds `CHANGELOG.md` from Towncrier fragments and updates compare reference links.

Source branch: `${{ github.event.inputs.source_branch || 'develop' }}`
Target branch: `${{ github.event.inputs.target_branch || 'develop' }}`
Version: `${{ steps.build-changelog.outputs.release_version }}`
Previous version: `${{ steps.build-changelog.outputs.previous_version }}`
Date: `${{ steps.build-changelog.outputs.release_date }}`
branch: "chore/build-changelog-${{ steps.build-changelog.outputs.safe_source_branch }}-${{ github.run_id }}"
base: "${{ github.event.inputs.target_branch || 'develop' }}"
delete-branch: true
50 changes: 49 additions & 1 deletion .github/workflows/tidy3d-python-client-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,10 @@ jobs:

- name: enforce-jira-key
id: enforce-jira-key
if: github.event_name == 'pull_request' && github.event.pull_request.user.login != 'dependabot[bot]'
if: >
github.event_name == 'pull_request' &&
github.event.pull_request.user.login != 'dependabot[bot]' &&
!startsWith(github.event.pull_request.head.ref, 'chore/build-changelog-')
env:
STEPS_EXTRACT_BRANCH_NAME_OUTPUTS_BRANCH_NAME: ${{ steps.extract-branch-name.outputs.branch_name }}
run: |
Expand Down Expand Up @@ -425,6 +428,44 @@ jobs:
fi
fi

enforce-changelog-policy:
needs: determine-test-scope
runs-on: ubuntu-latest
if: needs.determine-test-scope.outputs.pr_review_tests == 'true'
name: enforce-changelog-policy
steps:
- name: Check out source code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false

- name: disallow-manual-changelog-edits
env:
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
run: |
set -euo pipefail

if ! git cat-file -e "${PR_BASE_SHA}:changelog.d/README.md" 2>/dev/null; then
echo "Towncrier changelog workflow not detected in base commit; skipping check."
exit 0
fi

if [[ "$PR_BRANCH" == chore/build-changelog-* ]]; then
echo "Auto-generated changelog branch detected; allowing CHANGELOG.md edits."
exit 0
fi

if git diff --name-only "$PR_BASE_SHA" "$PR_HEAD_SHA" | grep -Fxq "CHANGELOG.md"; then
echo "❌ Manual edits to CHANGELOG.md are not allowed."
echo "Add changelog fragments under changelog.d/ instead."
exit 1
fi

echo "✅ No direct CHANGELOG.md edits detected."

lint-commit-messages:
needs: determine-test-scope
runs-on: ubuntu-latest
Expand Down Expand Up @@ -1365,6 +1406,7 @@ jobs:
- verify-schema-change
- lint-commit-messages
- lint-branch-name
- enforce-changelog-policy
- zizmor
- develop-cli-tests
- verify-version-consistency
Expand Down Expand Up @@ -1420,6 +1462,12 @@ jobs:
echo "❌ Branch name linting failed."
exit 1

- name: check-changelog-policy
if: ${{ needs.determine-test-scope.outputs.pr_review_tests == 'true' && needs.enforce-changelog-policy.result != 'success' && needs.enforce-changelog-policy.result != 'skipped' }}
run: |
echo "❌ Changelog policy check failed."
exit 1

- name: check-zizmor-static-analysis
if: ${{ needs.determine-test-scope.outputs.code_quality_tests == 'true' && needs.zizmor.result != 'success' && needs.zizmor.result != 'skipped' }}
run: |
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
- Follow Conventional Commits per `.commitlintrc.json`.
- Branch names must use an allowed prefix (`chore`, `hotfix`, `daily-chore`) or include a Jira key to satisfy CI.
- PRs should link issues, summarize behavior changes, list the `poetry run …` checks you executed, and call out docs/schema updates.
- Add a changelog entry under `## [Unreleased]` in `CHANGELOG.md` for user-facing changes (new features, bug fixes, breaking changes).
- 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.
- 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`).

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