Skip to content

Commit 81febf5

Browse files
chore: python-client autorelease flow
1 parent ac9970b commit 81febf5

14 files changed

+1901
-375
lines changed

.github/workflows/README.md

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
# GitHub Actions Workflows
2+
3+
This directory houses the manual CI/CD workflows that drive the Tidy3D Python client release, test, maintenance, and documentation automation.
4+
5+
## Release Workflows
6+
7+
All release workflows in this repository now rely on `workflow_dispatch` (manual) events or explicit `workflow_call`s. Nothing runs automatically after merging to a branch, which prevents unintentional releases.
8+
9+
### `tidy3d-python-client-release.yml`
10+
11+
The orchestrator for the entire release pipeline. It sequences:
12+
13+
1. **Scope detection** (`determine-workflow-scope`) – figures out which stages need to run, how `release_type` should map to deployments, whether to push docs to `latest`, and if submodule tests must be enforced.
14+
2. **Tagging** – delegates to `tidy3d-python-client-create-tag.yml` when tagging is enabled.
15+
3. **Testing** – reuses `tidy3d-python-client-tests.yml` with knobs for local, remote, CLI, and submodule suites. The workflow consumes the `workflow_success` output from the tests job before proceeding.
16+
4. **Docs sync & GitHub release** – mirrors the release tag to ReadTheDocs (`tidy3d-docs-sync-readthedocs-repo.yml`) and creates a GitHub release when the deployment stage is active.
17+
5. **Package deployment** – invokes `tidy3d-python-client-deploy.yml` with the resolved TestPyPI/PyPI targets.
18+
19+
**Trigger**
20+
- Manual run through the GitHub Actions UI (`workflow_dispatch` inputs).
21+
- `workflow_call` so other workflows/scripts can orchestrate releases with pre-filled inputs.
22+
23+
**Key inputs**
24+
- `release_tag` *(required)* – tag to create and test (e.g., `v2.10.0`, `v2.10.0rc1`).
25+
- `release_type` – controls defaults for downstream deployment jobs. Options:
26+
- `draft`
27+
- `testpypi`
28+
- `pypi`
29+
- `workflow_control` – allows resuming or skipping stages:
30+
- `start-tag` (default), `start-tests`, `start-deploy`
31+
- `only-tag`, `only-tests`, `only-tag-tests`, `only-tag-deploy`
32+
- Test toggles:
33+
- `client_tests`
34+
- `cli_tests`
35+
- `submodule_tests` (auto-enabled for non-RC `pypi` releases even if left `false`)
36+
37+
When invoked via `workflow_call`, two optional overrides are also honored:
38+
- `deploy_testpypi`
39+
- `deploy_pypi`
40+
41+
If those overrides are omitted, deployment targets are inferred from `release_type`:
42+
43+
| `release_type` | Automatic deployments | Notes |
44+
| --- | --- | --- |
45+
| `draft` | none | Runs tagging/tests/sync but does not publish packages. |
46+
| `testpypi` | TestPyPI | Requires version parity with `pyproject.toml`. Good for validating artifacts. |
47+
| `pypi` | TestPyPI + PyPI | Enforces semver tag format, auto-runs submodule tests when the tag is non-RC, and mirrors docs to the `latest` ref. |
48+
49+
**Testing stage**
50+
- Uses the unified `tidy3d-python-client-tests.yml` workflow instead of the retired release-specific test workflow.
51+
- Automatically passes `release_tag` so tests run against the tagged commit.
52+
- `compile-tests-results` blocks deployment until every requested suite reports success through the tests workflow’s `workflow_success` output.
53+
54+
**Deployment stage**
55+
- Always creates a GitHub release and syncs docs when `run_deploy` is `true`.
56+
- ReadTheDocs sync pushes the tag to the mirror repository and targets `latest` automatically for non-RC `pypi` releases (semver tags only).
57+
- Package publication happens through `tidy3d-python-client-deploy.yml`; deployment targets can still be narrowed by re-running the orchestrator with `only-tag-deploy` or `start-deploy`.
58+
59+
**Outputs**
60+
- `workflow_success``true` only when deployments (or the chosen stages) complete successfully. Use this signal when chaining workflows.
61+
62+
### Supporting release workflows
63+
64+
#### `tidy3d-python-client-create-tag.yml`
65+
- Manual or called workflow that (re)creates tags.
66+
- For `release_type: testpypi` and `release_type: pypi`, validates that `pyproject.toml` matches the tag (minus the `v` prefix) before pushing.
67+
- Retags automatically by deleting the old tag locally and on origin.
68+
- Output: `tag_created`.
69+
70+
#### `tidy3d-python-client-deploy.yml`
71+
- Manual deployment entry point (can also be called from the release orchestrator).
72+
- Requires selecting at least one of `deploy_testpypi` or `deploy_pypi`.
73+
- Builds the distribution with Poetry, uploads artifacts, and publishes via Twine.
74+
- Emits a short deployment summary and fails if any requested target fails.
75+
76+
## Test Workflows
77+
78+
### `tidy3d-python-client-tests.yml`
79+
80+
Primary CI workflow; it runs on PRs (`latest`, `develop`, `pre/*`), merge queue (`merge_group`), manual dispatch, and `workflow_call`. Highlights:
81+
- **Code quality**: `ruff format`, `ruff check`, `mypy`, `zizmor`, schema regeneration, commit/branch linting.
82+
- **Local tests**: Self-hosted Slurm runners on Python 3.10 and 3.13 (coverage enforced, diff-coverage comments for 3.13).
83+
- **Remote tests**: GitHub-hosted matrix across Windows, Linux, and macOS for Python 3.10–3.13.
84+
- **Optional suites**: CLI tests, version consistency checks, and submodule validation (non-RC release tags only) can be toggled via inputs.
85+
- **Dynamic scope**: Determines which jobs to run based on the event (draft PRs, approvals, merge queue, manual overrides).
86+
- **Outputs**: `workflow_success` summarizes whether every required job succeeded; the release workflow uses this to decide if deployment can continue.
87+
88+
> The previous `tidy3d-python-client-release-tests.yml` workflow has been removed. Release-specific suites now live entirely inside this unified workflow.
89+
90+
### `tidy3d-python-client-develop-cli.yml`
91+
92+
Reusable workflow that runs the develop-CLI integration tests. It is usually invoked by the main tests workflow when `cli_tests` is requested but can also be triggered directly.
93+
94+
## Maintenance Workflows
95+
96+
### `tidy3d-python-client-daily.yml`
97+
98+
Scheduled at 05:00 UTC and also manually runnable. It fans out to:
99+
- `tidy3d-python-client-update-lockfile.yml` – keeps dependencies fresh.
100+
- The submodule smoke-test workflow – ensures docs/notebooks submodules stay aligned (same helper the release tests call).
101+
102+
### `tidy3d-python-client-update-lockfile.yml`
103+
104+
Manual or called workflow that updates `poetry.lock`, authenticates against AWS CodeArtifact, and opens a PR on `develop` with the refreshed lockfile (`daily-chore/update-poetry-lock`). Requires `AWS_CODEARTIFACT_ACCESS_KEY` and `AWS_CODEARTIFACT_ACCESS_SECRET`.
105+
106+
## Documentation Workflows
107+
108+
### `tidy3d-docs-sync-readthedocs-repo.yml`
109+
110+
Mirrors a source ref (branch or tag) to the ReadTheDocs mirror repository.
111+
- Inputs: `source_ref` (defaults to the triggering ref) and optional `target_ref`.
112+
- Outputs: `workflow_success`, `synced_ref`.
113+
- Used automatically by the release workflow and can also be run manually when docs need to be re-synced without a full release.
114+
115+
## Best Practices
116+
117+
### For releases
118+
119+
1. **Dry-run first** – kick off the release workflow with `release_type: draft` to verify tagging and tests without publishing packages.
120+
2. **Use `testpypi` before `pypi`** – it enforces version parity and helps catch packaging issues before production uploads.
121+
3. **Respect semver tags**`release_type: pypi` will fail early if the tag is not `v{major}.{minor}.{patch}[rc{num}]`.
122+
4. **Leverage `workflow_control`** – resume from `start-tests` or `start-deploy` instead of repeating earlier successful stages.
123+
5. **Watch `workflow-validation`** – that job in the tests workflow aggregates lint, schema, CLI, and test failures.
124+
6. **Let submodule tests run for stable releases** – they are auto-enabled for non-RC PyPI releases; only disable when you have a compelling reason.
125+
126+
### Version validation
127+
128+
For `release_type: testpypi` or `release_type: pypi`, the tagging workflow enforces version alignment:
129+
130+
```bash
131+
# pyproject.toml must contain:
132+
version = "2.10.0"
133+
134+
# And the release workflow must be invoked with:
135+
release_tag: v2.10.0
136+
```
137+
138+
Additionally, `release_type: pypi` enables strict tag-format validation inside the orchestrator before anything runs.
139+
140+
### Recommended release flow
141+
142+
1. **Draft dry run**
143+
144+
```yaml
145+
release_tag: v2.10.0rc1
146+
release_type: draft
147+
workflow_control: start-tag
148+
```
149+
150+
2. **TestPyPI publishing (after the draft run passes)**
151+
152+
```yaml
153+
release_tag: v2.10.0rc1
154+
release_type: testpypi
155+
workflow_control: start-deploy # reuse prior tag/tests
156+
```
157+
158+
3. **Stable PyPI release**
159+
160+
```yaml
161+
release_tag: v2.10.0
162+
release_type: pypi
163+
workflow_control: start-deploy
164+
# Submodule tests run automatically when the tag is non-RC.
165+
```
166+
167+
Re-running with `only-tag` or `only-tag-deploy` is helpful when you must recreate a tag or redo deployments without re-running every test.
168+
169+
### Troubleshooting
170+
171+
- **Version mismatch (`create-tag`)**
172+
```
173+
Version mismatch!
174+
pyproject.toml: 2.9.0
175+
Release tag: 2.10.0
176+
```
177+
Update `pyproject.toml` (and `tidy3d/version.py`) so the version matches `release_tag` minus the `v`.
178+
179+
- **Invalid tag format (`release_type: pypi`)**
180+
```
181+
Invalid tag format: v2.10
182+
Expected format: v{major}.{minor}.{patch}[rc{num}]
183+
```
184+
Use `v2.10.0`, `v2.10.1rc1`, etc.
185+
186+
- **Tag already exists**
187+
The tagging workflow deletes and recreates the tag automatically. No manual cleanup is needed.
188+
189+
- **Tests blocking deployment**
190+
Inspect the `workflow-validation` job inside `tidy3d-python-client-tests`. After fixing the issue, rerun the release workflow with `workflow_control: start-tests` or `start-deploy` as appropriate.
191+
192+
- **Manual deployment run fails immediately**
193+
`tidy3d-python-client-deploy.yml` requires at least one of `deploy_testpypi` or `deploy_pypi` to be set to `true`; otherwise it aborts during input validation.
194+
195+
## Workflow outputs
196+
197+
- `tidy3d-python-client-release.yml`: `workflow_success`
198+
- `tidy3d-python-client-tests.yml`: `workflow_success`
199+
- `tidy3d-python-client-create-tag.yml`: `tag_created`
200+
- `tidy3d-docs-sync-readthedocs-repo.yml`: `workflow_success`, `synced_ref`
201+
202+
Use these outputs when chaining workflows or when external automation needs to know whether a stage succeeded.
203+
204+
## AWS CodeArtifact Integration
205+
206+
Private dependencies are sourced through AWS CodeArtifact:
207+
- Configured inside `tidy3d-python-client-update-lockfile.yml`.
208+
- Credentials come from `AWS_CODEARTIFACT_ACCESS_KEY` and `AWS_CODEARTIFACT_ACCESS_SECRET`.
209+
- The workflow injects a temporary auth token into Poetry before running `poetry update --lock`.
210+
211+
## Related documentation
212+
213+
- Release workflow details: `docs/development/release/version.rst`
214+
- Development guidelines: `AGENTS.md`
215+
- General repository info: `README.md`

.github/workflows/tidy3d-docs-sync-readthedocs-repo.yml

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,35 @@ name: "docs/tidy3d/sync-to-readthedocs-repo"
33
on:
44
workflow_dispatch:
55
inputs:
6-
target_branch:
7-
description: 'Target mirror repo branch. Defaults to source branch/tag.'
6+
source_ref:
7+
description: 'Source ref (branch/tag) to sync. Defaults to current ref.'
88
required: false
99
type: string
10-
push:
11-
branches:
12-
- main
13-
- latest
14-
- develop
15-
- 'pre/*'
16-
- 'demo/*'
17-
tags:
18-
- 'v*'
19-
- 'demo/*'
10+
default: ''
11+
target_ref:
12+
description: 'Target mirror repo ref. Defaults to source ref.'
13+
required: false
14+
type: string
15+
default: ''
16+
17+
workflow_call:
18+
inputs:
19+
source_ref:
20+
description: 'Source ref (branch/tag) to sync. Required for workflow_call.'
21+
required: true
22+
type: string
23+
target_ref:
24+
description: 'Target mirror repo ref. Defaults to source ref.'
25+
required: false
26+
type: string
27+
default: ''
28+
outputs:
29+
workflow_success:
30+
description: 'Sync workflow success status'
31+
value: ${{ jobs.build-and-deploy.result == 'success' }}
32+
synced_ref:
33+
description: 'The ref that was synced to the mirror'
34+
value: ${{ jobs.build-and-deploy.outputs.synced_ref }}
2035

2136
permissions:
2237
contents: read
@@ -30,16 +45,26 @@ jobs:
3045
- id: extract
3146
name: Extract branch or tag name
3247
shell: bash
48+
env:
49+
INPUT_SOURCE_REF: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.source_ref || inputs.source_ref }}
3350
run: |
34-
REF_NAME="${GITHUB_REF#refs/*/}"
51+
if [[ -n "$INPUT_SOURCE_REF" ]]; then
52+
REF_NAME="$INPUT_SOURCE_REF"
53+
echo "Using provided source_ref: $REF_NAME"
54+
else
55+
REF_NAME="${GITHUB_REF#refs/*/}"
56+
echo "Extracted ref from GITHUB_REF: $REF_NAME"
57+
fi
3558
echo "ref_name=$REF_NAME" >> $GITHUB_OUTPUT
36-
echo "Extracted ref: $REF_NAME"
59+
echo "Final ref: $REF_NAME"
3760
3861
build-and-deploy:
3962
permissions:
4063
contents: write
4164
needs: extract_branch_or_tag
4265
runs-on: ubuntu-latest
66+
outputs:
67+
synced_ref: ${{ steps.sync-result.outputs.synced_ref }}
4368
steps:
4469
- name: full-checkout
4570
uses: actions/checkout@v4
@@ -52,20 +77,25 @@ jobs:
5277
persist-credentials: true
5378

5479
- name: push-mirror-repo
80+
id: sync-result
5581
env:
5682
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
5783
SOURCE_REF: ${{ needs.extract_branch_or_tag.outputs.ref_name }}
58-
TARGET_BRANCH_INPUT: ${{ github.event.inputs.target_branch }}
84+
TARGET_REF: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.target_ref || inputs.target_ref }}
5985
run: |
6086
echo "Source reference: $SOURCE_REF"
6187
git pull origin "$SOURCE_REF"
6288
git remote add mirror https://github.com/flexcompute-readthedocs/tidy3d-docs.git
6389
64-
if [[ -n "$TARGET_BRANCH_INPUT" && "${{ github.event_name }}" == "workflow_dispatch" ]]; then
65-
echo "Manual trigger detected. Pushing contents of '$SOURCE_REF' to remote branch '$TARGET_BRANCH_INPUT'."
66-
git push mirror "$SOURCE_REF:refs/heads/$TARGET_BRANCH_INPUT" --force
90+
if [[ -n "$TARGET_REF" ]]; then
91+
echo "Pushing contents of '$SOURCE_REF' to remote ref '$TARGET_REF'."
92+
git push mirror "$SOURCE_REF:refs/heads/$TARGET_REF" --force
93+
SYNCED_REF="$TARGET_REF"
6794
else
68-
echo "Automatic trigger or manual run without target. Pushing '$SOURCE_REF' to the same ref on the mirror."
69-
# This preserves the original behavior: pushes a branch to a branch, or a tag to a tag.
95+
echo "Pushing '$SOURCE_REF' to the same ref on the mirror."
7096
git push mirror "$SOURCE_REF" --force
97+
SYNCED_REF="$SOURCE_REF"
7198
fi
99+
100+
echo "synced_ref=$SYNCED_REF" >> $GITHUB_OUTPUT
101+
echo "? Successfully synced to: $SYNCED_REF"

0 commit comments

Comments
 (0)