Skip to content

ci: tidy3d-extras integration tests and docs/dev setup (FXC-4441) #4919

ci: tidy3d-extras integration tests and docs/dev setup (FXC-4441)

ci: tidy3d-extras integration tests and docs/dev setup (FXC-4441) #4919

name: "public/tidy3d/python-client-tests"
on:
merge_group:
workflow_dispatch:
inputs:
remote_tests:
description: 'remote-tests'
type: boolean
default: true
local_tests:
description: 'local-tests'
type: boolean
default: false
cli_tests:
description: 'develop-cli'
type: boolean
default: false
submodule_tests:
description: 'submodule-tests'
type: boolean
default: false
version_match_tests:
description: 'version-consistency-checks'
type: boolean
default: false
extras_integration_tests:
description: 'integration-tidy3d-extras'
type: boolean
default: false
release_tag:
description: 'Release Tag (v2.10.0, v2.10.0rc1)'
required: false
type: string
default: ''
workflow_call:
inputs:
remote_tests:
description: 'remote-tests'
type: boolean
required: false
default: true
local_tests:
description: 'local-tests'
type: boolean
required: false
default: true
cli_tests:
description: 'Run develop-cli tests'
type: boolean
required: false
default: false
submodule_tests:
description: 'Run submodule tests'
type: boolean
required: false
default: false
version_match_tests:
description: 'Run version consistency checks'
type: boolean
required: false
default: false
extras_integration_tests:
description: 'Run tidy3d-extras integration tests'
type: boolean
required: false
default: false
release_tag:
description: 'Release Tag (v2.10.0, v2.10.0rc1)'
required: false
type: string
default: ''
outputs:
workflow_success:
description: 'Overall test workflow success status'
value: ${{ jobs.workflow-validation.result == 'success' }}
pull_request:
branches:
- latest
- develop
- 'pre/*'
types: ['opened', 'reopened', 'synchronize', 'ready_for_review', 'edited']
pull_request_review:
types: [submitted]
permissions:
contents: read
jobs:
determine-test-scope:
runs-on: ubuntu-latest
if: |
github.event.pull_request.draft == false ||
github.ref == 'refs/heads/develop' ||
github.event_name == 'workflow_dispatch'
outputs:
code_quality_tests: ${{ steps.determine-test-type.outputs.code_quality_tests }}
pr_review_tests: ${{ steps.determine-test-type.outputs.pr_review_tests }}
local_tests: ${{ steps.determine-test-type.outputs.local_tests }}
remote_tests: ${{ steps.determine-test-type.outputs.remote_tests }}
cli_tests: ${{ steps.determine-test-type.outputs.cli_tests }}
submodule_tests: ${{ steps.determine-test-type.outputs.submodule_tests }}
version_match_tests: ${{ steps.determine-test-type.outputs.version_match_tests }}
extras_integration_tests: ${{ steps.determine-test-type.outputs.extras_integration_tests }}
steps:
- name: determine-test-type
id: determine-test-type
env:
DRAFT_STATE: ${{ github.event.pull_request.draft }}
EVENT_NAME: ${{ github.event_name }}
REVIEW_STATE: ${{ github.event.review.state }}
REF: ${{ github.ref }}
INPUT_LOCAL: ${{ github.event.inputs.local_tests || inputs.local_tests }}
INPUT_REMOTE: ${{ github.event.inputs.remote_tests || inputs.remote_tests }}
INPUT_CLI: ${{ github.event.inputs.cli_tests || inputs.cli_tests }}
INPUT_SUBMODULE: ${{ github.event.inputs.submodule_tests || inputs.submodule_tests }}
INPUT_VERSION_MATCH: ${{ github.event.inputs.version_match_tests || inputs.version_match_tests }}
INPUT_EXTRAS_INTEGRATION: ${{ github.event.inputs.extras_integration_tests || inputs.extras_integration_tests }}
run: |
echo "Event: $EVENT_NAME"
echo "Draft: $DRAFT_STATE"
echo "Review State: $REVIEW_STATE"
echo "Git REF: $REF"
echo "Input local: $INPUT_LOCAL"
echo "Input remote: $INPUT_REMOTE"
echo "Input cli: $INPUT_CLI"
echo "Input submodule: $INPUT_SUBMODULE"
echo "Input version_match: $INPUT_VERSION_MATCH"
echo "Input extras_integration: $INPUT_EXTRAS_INTEGRATION"
remote_tests=false
local_tests=false
cli_tests=false
submodule_tests=false
version_match_tests=false
code_quality_tests=false
pr_review_tests=false
extras_integration_tests=false
# Workflow_dispatch input override
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
code_quality_tests=true
# Each option is self contained
if [[ "$INPUT_REMOTE" == "true" ]]; then
remote_tests=true
fi
if [[ "$INPUT_LOCAL" == "true" ]]; then
local_tests=true
fi
if [[ "$INPUT_CLI" == "true" ]]; then
cli_tests=true
fi
if [[ "$INPUT_SUBMODULE" == "true" ]]; then
submodule_tests=true
fi
if [[ "$INPUT_VERSION_MATCH" == "true" ]]; then
version_match_tests=true
fi
if [[ "$INPUT_EXTRAS_INTEGRATION" == "true" ]]; then
extras_integration_tests=true
fi
fi
# All PRs that have been triggered need local tests (remote reserved for merge queue/manual)
if [[ "$EVENT_NAME" == "pull_request" ]]; then
local_tests=true
code_quality_tests=true
pr_review_tests=true
fi
if [[ "$EVENT_NAME" == "merge_group" ]]; then
local_tests=true
remote_tests=true
code_quality_tests=true
extras_integration_tests=true
fi
echo "local_tests=$local_tests" >> $GITHUB_OUTPUT
echo "remote_tests=$remote_tests" >> $GITHUB_OUTPUT
echo "cli_tests=$cli_tests" >> $GITHUB_OUTPUT
echo "submodule_tests=$submodule_tests" >> $GITHUB_OUTPUT
echo "version_match_tests=$version_match_tests" >> $GITHUB_OUTPUT
echo "code_quality_tests=$code_quality_tests" >> $GITHUB_OUTPUT
echo "pr_review_tests=$pr_review_tests" >> $GITHUB_OUTPUT
echo "extras_integration_tests=$extras_integration_tests" >> $GITHUB_OUTPUT
echo "code_quality_tests=$code_quality_tests"
echo "pr_review_tests=$pr_review_tests"
echo "local_tests=$local_tests"
echo "remote_tests=$remote_tests"
echo "cli_tests=$cli_tests"
echo "submodule_tests=$submodule_tests"
echo "version_match_tests=$version_match_tests"
echo "extras_integration_tests=$extras_integration_tests"
lint:
needs: determine-test-scope
if: needs.determine-test-scope.outputs.code_quality_tests == 'true'
name: verify-linting
runs-on: ubuntu-latest
container: ghcr.io/astral-sh/uv:debian
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: false
persist-credentials: false
- uses: astral-sh/ruff-action@57714a7c8a2e59f32539362ba31877a1957dded1 # v3.5.1
with:
version: 0.11.11
- name: Run ruff format
run: ruff format --check --diff
- name: Run ruff check
run: ruff check tidy3d
mypy:
name: static-type-checks (mypy)
needs: determine-test-scope
if: needs.determine-test-scope.outputs.code_quality_tests == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: false
persist-credentials: false
- name: set-python-3.10
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install mypy
run: |
python -m pip install --upgrade pip
pip install "mypy==1.13.0"
- name: Run mypy
run: |
mypy --config-file=pyproject.toml
zizmor:
name: Run zizmor 🌈
runs-on: ubuntu-latest
needs: determine-test-scope
if: needs.determine-test-scope.outputs.code_quality_tests == 'true'
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Install the latest version of uv
uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 # v6.7.0
- name: Run zizmor 🌈
run: uvx zizmor .github/workflows/*.y* --format=sarif . > results.sarif
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
with:
sarif_file: results.sarif
category: zizmor
- name: run zizmor directly # this gets a success or fail result
run: uvx zizmor .github/workflows/*.y*
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
lint-branch-name:
needs: determine-test-scope
runs-on: ubuntu-latest
if: needs.determine-test-scope.outputs.pr_review_tests == 'true'
name: lint-branch-name
env:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
steps:
- name: extract-branch-name
id: extract-branch-name
run: |
BRANCH_NAME="${GITHUB_HEAD_REF}"
if [[ -z "$BRANCH_NAME" && -n "$PR_BRANCH" ]]; then
BRANCH_NAME="$PR_BRANCH"
echo "(fallback) Using PR head branch name: $BRANCH_NAME"
fi
if [[ -z "$BRANCH_NAME" ]]; then
BRANCH_NAME="${GITHUB_REF_NAME:-${GITHUB_REF#refs/heads/}}"
echo "(fallback) Using ref-derived branch name: $BRANCH_NAME"
fi
echo "Branch name: $BRANCH_NAME"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
- name: enforce-jira-key
id: enforce-jira-key
env:
STEPS_EXTRACT_BRANCH_NAME_OUTPUTS_BRANCH_NAME: ${{ steps.extract-branch-name.outputs.branch_name }}
run: |
BRANCH_NAME="${STEPS_EXTRACT_BRANCH_NAME_OUTPUTS_BRANCH_NAME}"
echo $BRANCH_NAME
# Allow only Jira keys from known projects, even if the branch has an author prefix
ALLOWED_JIRA_PROJECTS=("FXC" "SCEM" "SCRF")
JIRA_PROJECT_PATTERN=$(IFS='|'; echo "${ALLOWED_JIRA_PROJECTS[*]}")
JIRA_PATTERN="(${JIRA_PROJECT_PATTERN})-[0-9]+"
# List of exempt prefixes (case-insensitive)
EXEMPT_PREFIXES=("chore" "hotfix" "daily-chore")
# Convert branch name to lowercase for comparison
BRANCH_LOWER=$(echo "$BRANCH_NAME" | tr '[:upper:]' '[:lower:]')
# Check if branch starts with any exempt prefix
for prefix in "${EXEMPT_PREFIXES[@]}"; do
if [[ "$BRANCH_LOWER" == $prefix* ]]; then
echo "ℹ️ Branch starts with '$prefix' - Jira key not required"
exit 0
fi
done
if [[ "$BRANCH_NAME" =~ $JIRA_PATTERN ]]; then
echo "✅ Jira key found in branch name: ${BASH_REMATCH[0]}"
else
echo "❌ No Jira key found in branch name, checking PR name as fallback"
if [[ "$PR_TITLE" =~ $JIRA_PATTERN ]]; then
echo "✅ Jira key found in PR-title: ${BASH_REMATCH[0]}"
else
echo "❌ No Jira key found in branch name and PR title"
echo "ℹ️ Expected Jira key prefixes: ${ALLOWED_JIRA_PROJECTS[*]}"
exit 1
fi
fi
lint-commit-messages:
needs: determine-test-scope
runs-on: ubuntu-latest
if: needs.determine-test-scope.outputs.code_quality_tests == 'true'
name: lint-commit-messages
steps:
- name: Check out source code
uses: actions/checkout@v4
with:
fetch-depth: 0 # fetch all commits in the PR
persist-credentials: false
- name: Setup node
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install commitlint
run: npm install -D @commitlint/cli @commitlint/config-conventional
- name: Print versions
run: |
git --version
node --version
npm --version
npx commitlint --version
- name: Check commit messages (merge_group)
if: github.event_name == 'merge_group'
env:
GITHUB_EVENT_MERGE_GROUP_HEAD_SHA: ${{ github.event.merge_group.head_sha }}
run: |
# For merge groups, check the commits being merged
npx commitlint --from ${{ github.event.merge_group.base_sha }} --to ${GITHUB_EVENT_MERGE_GROUP_HEAD_SHA} --verbose || {
echo "Commit message linting failed; please follow the conventional commits format at https://www.conventionalcommits.org/"
exit 1
}
verify-schema-change:
name: verify-schema-change
needs: determine-test-scope
if: needs.determine-test-scope.outputs.code_quality_tests == 'true'
runs-on: ubuntu-latest
container: ghcr.io/astral-sh/uv:debian
defaults:
run:
shell: bash
steps:
- name: checkout-branch
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
persist-credentials: false
- name: git-config
run: |
cd $GITHUB_WORKSPACE
git config --global --add safe.directory $GITHUB_WORKSPACE
- name: install-depedencies
run: |
uv venv $GITHUB_WORKSPACE/.venv -p 3.11
source $GITHUB_WORKSPACE/.venv/bin/activate
uv pip install -e "$GITHUB_WORKSPACE"
- name: get-tidy3d-version
id: get-version
run: |
source $GITHUB_WORKSPACE/.venv/bin/activate
version=$(python -c "import tidy3d; print(tidy3d.__version__)")
echo "tidy3d version is $version"
echo "version=$version" >> $GITHUB_OUTPUT
- name: verify-committed-schema
run: |
set -euo pipefail
echo "Regenerating docs-free canonical schemas into repo schemas/ ..."
source $GITHUB_WORKSPACE/.venv/bin/activate
python $GITHUB_WORKSPACE/scripts/regenerate_schema.py
echo "Verifying committed schemas match generated output..."
if ! git diff --name-status --exit-code -- schemas; then
echo "❌ Committed schemas are not up-to-date. See diff above."
exit 1
fi
echo "✅ Committed schemas are up-to-date."
- name: run-schema-diff
id: schema-diff
env:
GITHUB_EVENT_PULL_REQUEST_BASE_REPO_FULL_NAME: ${{ github.event.pull_request.base.repo.full_name }}
GITHUB_EVENT_PULL_REQUEST_BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE"
# Determine base repo/ref for PRs; default to current repo and 'develop' otherwise
BASE_REPO="${GITHUB_EVENT_PULL_REQUEST_BASE_REPO_FULL_NAME}"
BASE_REF="${GITHUB_EVENT_PULL_REQUEST_BASE_REF}"
if [ -z "$BASE_REPO" ]; then
BASE_REPO="${{ github.repository }}"
fi
if [ -z "$BASE_REF" ]; then
BASE_REF="develop"
fi
echo "Fetching base branch $BASE_REPO@$BASE_REF (shallow)..."
git remote add upstream "https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${BASE_REPO}.git" || true
git fetch --no-tags --prune --depth=1 upstream "+refs/heads/${BASE_REF}:refs/remotes/upstream/${BASE_REF}"
# Store base repo/ref for use in subsequent steps
echo "base_repo=${BASE_REPO}" >> "$GITHUB_OUTPUT"
echo "base_ref=${BASE_REF}" >> "$GITHUB_OUTPUT"
# Name-status diff between base and head limited to schemas/
DIFF_OUTPUT=$(git diff --name-status "upstream/${BASE_REF}...HEAD" -- schemas || true)
if [ -z "$DIFF_OUTPUT" ]; then
echo "✅ No schema changes relative to ${BASE_REF}."
echo "changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Schema changes detected relative to ${BASE_REF}."
echo "changed=true" >> "$GITHUB_OUTPUT"
# Summarize changes
{
echo "### Schema Change Summary"
echo "| Status | File |"
echo "|:---:|:---|"
} >> "$GITHUB_STEP_SUMMARY"
while IFS=$'\t' read -r status file; do
# Map short status to human-friendly
case "$status" in
A|AM) label="Added 🟢" ;;
M|MM) label="Modified 🟡" ;;
D) label="Removed 🔴" ;;
R*) label="Renamed 🟠" ;;
*) label="$status" ;;
esac
echo "| $label | \`$file\` |" >> "$GITHUB_STEP_SUMMARY"
done <<< "$DIFF_OUTPUT"
- name: compare-versions
id: compare-versions
if: steps.schema-diff.outputs.changed == 'true'
env:
PR_VERSION: ${{ steps.get-version.outputs.version }}
BASE_REPO: ${{ steps.schema-diff.outputs.base_repo }}
BASE_REF: ${{ steps.schema-diff.outputs.base_ref }}
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE"
PR_VERSION="${PR_VERSION}"
BASE_REPO="${BASE_REPO}"
BASE_REF="${BASE_REF}"
echo "PR branch version: $PR_VERSION"
# Checkout base branch temporarily to get its version
echo "Checking out base branch ${BASE_REF} to get version..."
git checkout -q "refs/remotes/upstream/${BASE_REF}"
# Install dependencies and get version from base branch
source $GITHUB_WORKSPACE/.venv/bin/activate
uv pip install -e "$GITHUB_WORKSPACE" --quiet
BASE_VERSION=$(python -c "import tidy3d; print(tidy3d.__version__)")
echo "Base branch (${BASE_REF}) version: $BASE_VERSION"
echo "base_version=${BASE_VERSION}" >> "$GITHUB_OUTPUT"
# Return to HEAD branch
git checkout -q HEAD
# Compare versions
if [[ "$PR_VERSION" != "$BASE_VERSION" ]]; then
echo "⚠️ Version mismatch detected: PR branch ($PR_VERSION) != base branch ($BASE_VERSION)"
echo "This appears to be a version bump PR. Skipping schema change verification."
echo "is_version_bump=true" >> "$GITHUB_OUTPUT"
else
echo "✅ Versions match: PR branch ($PR_VERSION) == base branch ($BASE_VERSION)"
echo "Proceeding with schema change verification."
echo "is_version_bump=false" >> "$GITHUB_OUTPUT"
fi
- name: verify-allowed-changes
if: steps.schema-diff.outputs.changed == 'true' && steps.compare-versions.outputs.is_version_bump == 'false'
env:
BASE_VERSION: ${{ steps.compare-versions.outputs.base_version }}
BASE_REF: ${{ steps.schema-diff.outputs.base_ref }}
run: |
set -e
base_version="${BASE_VERSION}"
base_ref="${BASE_REF}"
if [[ "$base_version" == *rc* ]]; then
echo "✅ Passing: Schema changed on a release candidate version ($base_version) in base branch '${base_ref}', which is permitted."
else
echo "❌ Failing: Schema changed on a non-rc release version ($base_version) in base branch '${base_ref}'."
exit 1
fi
local-tests:
# Run on open PRs OR when manually triggered with local_tests=true
needs: determine-test-scope
if: needs.determine-test-scope.outputs.local_tests == 'true'
name: python-${{ matrix.python-version }}-self-hosted-runner
runs-on: [ slurm-runner, 4xcpu, container=ghcr.io/astral-sh/uv:debian ]
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.event_name }}-${{ matrix.python-version }}-local
cancel-in-progress: true
strategy:
matrix:
python-version: ['3.10', '3.13']
defaults:
run:
shell: bash
env:
PIP_ONLY_BINARY: gdstk
MPLBACKEND: agg
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || inputs.release_tag }}
permissions:
pull-requests: write
steps:
- name: checkout-head
if: ${{ !env.RELEASE_TAG }}
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: false
persist-credentials: false
- name: checkout-tag
if: ${{ env.RELEASE_TAG }}
uses: actions/checkout@v4
with:
ref: refs/tags/${{ env.RELEASE_TAG }}
fetch-depth: 0
submodules: false
persist-credentials: false
- name: install-project
env:
PYTHON_VERSION: ${{ matrix.python-version }}
run: |
if [ -f /.dockerenv ]; then
echo "Running inside a Docker container (detected via /.dockerenv)"
else
echo "Not running inside a Docker container (/.dockerenv not found)"
fi
uv venv -p $PYTHON_VERSION ${GITHUB_WORKSPACE}/.venv
source ${GITHUB_WORKSPACE}/.venv/bin/activate
which python
which uv
python --version
uv pip list
uv pip install gdstk --only-binary gdstk
uv pip install -e ".[dev]"
echo "Testing vtk is correctly installed."
python -c "import vtk"
- name: run-tests-coverage
env:
PYTHONUNBUFFERED: "1"
run: |
source ${GITHUB_WORKSPACE}/.venv/bin/activate
# pytest --cov=tidy3d -rF --tb=short tests/_test_data/_test_datasets_no_vtk.py
pytest --cov=tidy3d -rF --tb=short tests
coverage report -m
coverage xml -o ${GITHUB_WORKSPACE}/coverage.xml
TOTAL_COVERAGE=$(coverage report --format=total)
echo "total=$TOTAL_COVERAGE" >> "$GITHUB_ENV"
echo "### Total coverage: ${TOTAL_COVERAGE}%"
- name: diff-coverage-report
if: >-
matrix.python-version == '3.13' &&
github.event_name == 'pull_request' &&
!contains(github.event.pull_request.labels.*.name, 'ignore_diff_coverage')
env:
GITHUB_EVENT_PULL_REQUEST_BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
source ${GITHUB_WORKSPACE}/.venv/bin/activate
git config --global --add safe.directory ${GITHUB_WORKSPACE}
diff-cover ${GITHUB_WORKSPACE}/coverage.xml \
--compare-branch origin/${GITHUB_EVENT_PULL_REQUEST_BASE_REF} \
--format markdown:diff-coverage.md
- uses: actions/github-script@v7
if: >-
matrix.python-version == '3.13' &&
github.event_name == 'pull_request' &&
!contains(github.event.pull_request.labels.*.name, 'ignore_diff_coverage') &&
github.event.pull_request.head.repo.fork == false
with:
result-encoding: string
script: |
const fs = require('fs');
const marker = '<!-- diff-cover-report -->';
const body = fs.readFileSync('diff-coverage.md','utf8');
const report = `${marker}\n${body}`;
const {data:comments}=await github.rest.issues.listComments({
owner:context.repo.owner,
repo:context.repo.repo,
issue_number:context.issue.number,
});
const existing = comments.find(c=>c.body.startsWith(marker));
if(existing) {
await github.rest.issues.updateComment({
owner:context.repo.owner,
repo:context.repo.repo,
comment_id:existing.id,
body:report,
});
} else {
await github.rest.issues.createComment({
owner:context.repo.owner,
repo:context.repo.repo,
issue_number:context.issue.number,
body:report,
});
}
remote-tests:
# Run tests on a push event OR a workflow dispatch with remote_tests
needs: determine-test-scope
if: needs.determine-test-scope.outputs.remote_tests == 'true'
name: python-${{ matrix.python-version }}-${{ matrix.platform }}
runs-on: ${{ matrix.platform }}
permissions:
contents: read
pull-requests: write
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}-${{ github.event_name }}-${{ matrix.platform }}-${{ matrix.python-version }}-remote
cancel-in-progress: true
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
platform: [windows-latest, ubuntu-latest, macos-latest]
defaults:
run:
shell: bash
env:
PIP_ONLY_BINARY: gdstk
MPLBACKEND: agg
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || inputs.release_tag }}
steps:
- name: checkout-head
if: ${{ !env.RELEASE_TAG }}
uses: actions/checkout@v4
with:
fetch-depth: 1
submodules: false
persist-credentials: false
- name: checkout-tag
if: ${{ env.RELEASE_TAG }}
uses: actions/checkout@v4
with:
ref: refs/tags/${{ env.RELEASE_TAG }}
fetch-depth: 1
submodules: false
persist-credentials: false
- name: install-poetry
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1
with:
version: 2.1.1
virtualenvs-create: true
virtualenvs-in-project: true
- name: set-python-${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: install-project
shell: bash
run: |
poetry --version
python --version
python -m venv .venv
if [[ "${{ runner.os }}" == "Windows" ]]; then
source .venv/Scripts/activate
python --version
else
source .venv/bin/activate
which python
fi
poetry env use python
poetry env info
poetry run pip install --upgrade pip wheel setuptools
poetry run pip install gdstk --only-binary gdstk
poetry install -E dev
- name: run-doctests
run: |
poetry run pytest -rF --tb=short tidy3d
- name: run-tests-coverage
env:
PYTHONUNBUFFERED: "1"
run: |
poetry run pytest --cov=tidy3d -rF --tb=short tests/_test_data/_test_datasets_no_vtk.py
poetry run pytest --cov=tidy3d -rF --tb=short tests
poetry run coverage report -m
TOTAL_COVERAGE=$(poetry run coverage report --format=total)
echo "total=$TOTAL_COVERAGE" >> "$GITHUB_ENV"
echo "### Total coverage: ${TOTAL_COVERAGE}%"
- name: create-badge
if: ${{ github.ref == 'refs/heads/develop' }}
# https://gist.githubusercontent.com/nedbat/8c6980f77988a327348f9b02bbaf67f5
uses: schneegans/dynamic-badges-action@e9a478b16159b4d31420099ba146cdc50f134483 # v1.7.0
with:
auth: ${{ secrets.GH_TIDY3D_COVERAGE_GIST }}
gistID: 4702549574741e87deaadba436218ebd
filename: tidy3d_extension.json
label: Coverage
message: ${{ env.total }}%
minColorRange: 60
maxColorRange: 95
valColorRange: ${{ env.total }}
style: "for-the-badge"
develop-cli-tests:
name: develop-cli-tests
needs: determine-test-scope
if: needs.determine-test-scope.outputs.cli_tests == 'true'
uses: ./.github/workflows/tidy3d-python-client-develop-cli.yml
with:
release_tag: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || inputs.release_tag }}
verify-version-consistency:
name: verify-version-consistency
runs-on: ubuntu-latest
needs: determine-test-scope
if: needs.determine-test-scope.outputs.version_match_tests == 'true'
steps:
- name: checkout-code
uses: actions/checkout@v4
with:
ref: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || inputs.release_tag) || github.ref }}
persist-credentials: false
- name: check-version-consistency
env:
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || inputs.release_tag }}
run: |
set -e
echo "=== Verifying Version Consistency ==="
echo ""
# Extract version from pyproject.toml
PYPROJECT_VERSION=$(grep '^version = ' pyproject.toml | head -n 1 | sed 's/version = "\(.*\)"/\1/')
echo "pyproject.toml version: $PYPROJECT_VERSION"
# Extract version from tidy3d/version.py
VERSION_PY=$(grep '__version__ = ' tidy3d/version.py | sed 's/__version__ = "\(.*\)"/\1/')
echo "tidy3d/version.py version: $VERSION_PY"
echo ""
# Compare versions
if [[ "$PYPROJECT_VERSION" != "$VERSION_PY" ]]; then
echo "❌ ERROR: Version mismatch detected!"
echo " pyproject.toml: $PYPROJECT_VERSION"
echo " tidy3d/version.py: $VERSION_PY"
echo ""
echo "These versions must match before release."
echo "Please update both files to the same version."
exit 1
fi
echo "✅ Version consistency check passed: $PYPROJECT_VERSION"
echo ""
# If release tag provided, validate it matches the version
if [[ -n "$RELEASE_TAG" ]]; then
echo "=== Validating Release Tag ==="
echo "Release tag: $RELEASE_TAG"
# Strip 'v' prefix from tag if present
TAG_VERSION="${RELEASE_TAG#v}"
echo "Tag version (without 'v'): $TAG_VERSION"
if [[ "$TAG_VERSION" != "$PYPROJECT_VERSION" ]]; then
echo "❌ ERROR: Release tag does not match package version!"
echo " Release tag: $RELEASE_TAG (version: $TAG_VERSION)"
echo " Package version: $PYPROJECT_VERSION"
echo ""
echo "The release tag should be 'v$PYPROJECT_VERSION'"
exit 1
fi
echo "✅ Release tag matches package version"
fi
echo ""
echo "=== Version Checks Passed ==="
test-submodules:
name: test-submodules
runs-on: ubuntu-latest
needs: determine-test-scope
if: ${{ always() && (needs.determine-test-scope.outputs.submodule_tests == 'true') && (github.event.inputs.release_tag || inputs.release_tag) && !contains(github.event.inputs.release_tag || inputs.release_tag, 'rc') }}
env:
RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || inputs.release_tag }}
steps:
- name: checkout-head
if: ${{ !env.RELEASE_TAG }}
uses: actions/checkout@v4
with:
submodules: 'recursive'
fetch-depth: 0
persist-credentials: true
- name: checkout-tag
if: ${{ env.RELEASE_TAG }}
uses: actions/checkout@v4
with:
ref: ${{ env.RELEASE_TAG }}
submodules: 'recursive'
fetch-depth: 0
persist-credentials: true
- name: initialize-submodules
run: |
git submodule update --init --recursive
- name: check-submodules-for-multiple-branches
shell: bash
run: |
BRANCHES=("develop") # Add your branches here
for BRANCH in "${BRANCHES[@]}"; do
echo "Analyzing branch: $BRANCH"
# Fetch all branches and tags
git fetch --all --verbose
# Checkout the branch
git checkout $BRANCH
NOTEBOOKS_PATH=docs/notebooks
FAQ_PATH=docs/faq
# Checking Notebooks submodule
echo "Checking $NOTEBOOKS_PATH for updates..."
cd $NOTEBOOKS_PATH
NOTEBOOKS_CURRENT_COMMIT=$(git rev-parse HEAD)
echo $(git fetch --all --verbose)
echo $(git remote get-url origin)
if git show-ref --verify refs/remotes/origin/$BRANCH; then
echo "Branch $BRANCH exists."
else
echo "::error::Branch $BRANCH does not exist on remote."
exit 1
fi
NOTEBOOKS_LATEST_COMMIT=$(git rev-parse refs/remotes/origin/${BRANCH})
echo "NOTEBOOKS_LATEST_COMMIT: $NOTEBOOKS_LATEST_COMMIT"
echo "NOTEBOOKS_CURRENT_COMMIT: $NOTEBOOKS_CURRENT_COMMIT"
cd ../..
if [ "$NOTEBOOKS_LATEST_COMMIT" != "$NOTEBOOKS_CURRENT_COMMIT" ]; then
echo "::error::Submodule $NOTEBOOKS_PATH is not up to date with the $BRANCH branch. Please update it."
exit 1
else
echo "Submodule $NOTEBOOKS_PATH is up to date with the $BRANCH branch."
fi
# Checking FAQs only on the develop branch
if [[ "$BRANCH" == "develop" ]]; then
echo "Checking $FAQ_PATH for updates..."
cd $FAQ_PATH
FAQ_CURRENT_COMMIT=$(git rev-parse HEAD)
echo $(git fetch --all --verbose)
echo $(git remote get-url origin)
FAQ_LATEST_COMMIT=$(git rev-parse refs/remotes/origin/develop)
echo "FAQ_LATEST_COMMIT: $FAQ_LATEST_COMMIT"
echo "FAQ_CURRENT_COMMIT: $FAQ_CURRENT_COMMIT"
cd ../..
if [ "$FAQ_LATEST_COMMIT" != "$FAQ_CURRENT_COMMIT" ]; then
echo "::error::Submodule $FAQ_PATH is not up to date. Please update it."
exit 1
else
echo "Submodule $FAQ_PATH is up to date."
fi
fi
done
echo ""
echo "=== Submodule Checks Passed ==="
extras-integration-tests:
name: extras-integration-tests
needs: determine-test-scope
if: needs.determine-test-scope.outputs.extras_integration_tests == 'true'
uses: ./.github/workflows/tidy3d-extras-python-client-integration-tests.yml
with:
release_tag: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || inputs.release_tag }}
secrets: inherit # zizmor: ignore[secrets-inherit]
workflow-validation:
name: workflow-validation
if: always()
needs:
- determine-test-scope
- local-tests
- remote-tests
- lint
- mypy
- verify-schema-change
- lint-commit-messages
- lint-branch-name
- zizmor
- develop-cli-tests
- verify-version-consistency
- test-submodules
- extras-integration-tests
runs-on: ubuntu-latest
steps:
- name: check-linting-result
if: ${{ needs.determine-test-scope.outputs.code_quality_tests == 'true' && needs.lint.result != 'success' && needs.lint.result != 'skipped' }}
run: |
echo "❌ Linting failed."
exit 1
- name: check-mypy-result
if: ${{ needs.determine-test-scope.outputs.code_quality_tests == 'true' && needs.mypy.result != 'success' && needs.mypy.result != 'skipped' }}
run: |
echo "❌ Mypy type checking failed."
exit 1
- name: check-schema-change-verification
if: ${{ needs.determine-test-scope.outputs.code_quality_tests == 'true' && needs.verify-schema-change.result != 'success' && needs.verify-schema-change.result != 'skipped' }}
run: |
echo "❌ Schema change verification failed."
exit 1
- name: check-local-tests-result
if: ${{ needs.determine-test-scope.outputs.local_tests == 'true' && needs.local-tests.result != 'success' && needs.local-tests.result != 'skipped' }}
run: |
echo "❌ Local tests failed."
exit 1
- name: check-remote-tests-result
if: ${{ needs.determine-test-scope.outputs.remote_tests == 'true' && needs.remote-tests.result != 'success' && needs.remote-tests.result != 'skipped' }}
run: |
echo "❌ Remote tests failed."
exit 1
- name: check-commit-message-linting
if: ${{ needs.determine-test-scope.outputs.code_quality_tests == 'true' && needs.lint-commit-messages.result != 'success' && needs.lint-commit-messages.result != 'skipped' }}
run: |
echo "❌ Commit message linting failed."
exit 1
- name: check-branch-name-linting
if: ${{ needs.determine-test-scope.outputs.pr_review_tests == 'true' && needs.lint-branch-name.result != 'success' && needs.lint-branch-name.result != 'skipped' }}
run: |
echo "❌ Branch name linting 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: |
echo "❌ Zizmor static analysis failed."
exit 1
- name: check-cli-tests-result
if: ${{ needs.determine-test-scope.outputs.cli_tests == 'true' && needs.develop-cli-tests.result != 'success' && needs.develop-cli-tests.result != 'skipped' }}
run: |
echo "❌ CLI tests failed."
exit 1
- name: check-version-consistency-result
if: ${{ needs.determine-test-scope.outputs.version_match_tests == 'true' && needs.verify-version-consistency.result != 'success' && needs.verify-version-consistency.result != 'skipped' }}
run: |
echo "❌ Version consistency check failed."
exit 1
- name: check-submodule-tests-result
if: ${{ needs.determine-test-scope.outputs.submodule_tests == 'true' && needs.test-submodules.result != 'success' && needs.test-submodules.result != 'skipped' }}
run: |
echo "❌ Submodule tests failed."
exit 1
- name: check-extras-integration-tests-result
if: ${{ needs.determine-test-scope.outputs.extras_integration_tests == 'true' && needs.extras-integration-tests.result != 'success' && needs.extras-integration-tests.result != 'skipped' }}
run: |
echo "❌ tidy3d-extras integration tests failed."
exit 1
- name: all-checks-passed
if: ${{ success() }}
run: echo "✅ All required jobs passed!"
pr-requirements-pass:
name: pr-requirements-pass
if: |
always() &&
(github.event_name == 'pull_request' || github.event_name == 'pull_request_review' || github.event_name == 'merge_group') &&
(needs.determine-test-scope.outputs.local_tests == 'true' || needs.determine-test-scope.outputs.remote_tests == 'true')
needs:
- determine-test-scope
- workflow-validation
runs-on: ubuntu-latest
steps:
- name: check-workflow-validation
if: ${{ needs.workflow-validation.result != 'success' }}
run: |
echo "❌ Workflow validation failed. See workflow-validation job for details."
exit 1
- name: all-checks-passed
if: ${{ success() }}
run: echo "✅ All required jobs passed!"