Skip to content

chore(deps): bump the github-actions group across 1 directory with 3 updates #9088

chore(deps): bump the github-actions group across 1 directory with 3 updates

chore(deps): bump the github-actions group across 1 directory with 3 updates #9088

name: "public/tidy3d/python-client-tests"
on:
merge_group:
push:
branches:
- develop
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
test_type:
description: 'test-type (basic or full)'
type: choice
options:
- basic
- full
default: 'basic'
test_selection:
description: 'test-selection (testmon or full)'
type: choice
options:
- testmon
- full
default: 'testmon'
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
test_type:
description: 'Test type for extras integration tests (basic or full)'
type: string
required: false
default: 'basic'
test_selection:
description: 'Test selection mode for local and remote tests (testmon or full)'
type: string
required: false
default: 'testmon'
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']
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref == 'refs/heads/develop' }}
env:
VERIFICATIONS_PY_VERSION: '3.11'
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 }}
test_type: ${{ steps.determine-test-type.outputs.test_type }}
test_selection: ${{ steps.determine-test-type.outputs.test_selection }}
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 }}
INPUT_TEST_TYPE: ${{ github.event.inputs.test_type || inputs.test_type }}
INPUT_TEST_SELECTION: ${{ github.event.inputs.test_selection || inputs.test_selection }}
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"
echo "Input test_type: $INPUT_TEST_TYPE"
echo "Input test_selection: $INPUT_TEST_SELECTION"
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
test_type="basic"
test_selection="testmon"
# Workflow_dispatch and workflow_call input override
if [[ "$EVENT_NAME" == "workflow_dispatch" || "$EVENT_NAME" == "workflow_call" ]]; 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
if [[ -n "$INPUT_TEST_SELECTION" ]]; then
test_selection="$INPUT_TEST_SELECTION"
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
test_selection="testmon"
fi
if [[ "$EVENT_NAME" == "merge_group" ]]; then
local_tests=true
remote_tests=true
code_quality_tests=true
extras_integration_tests=true
test_type="basic"
test_selection="full"
fi
if [[ "$EVENT_NAME" == "push" ]]; then
local_tests=true
remote_tests=true
extras_integration_tests=true
test_type="basic"
test_selection="full"
fi
# Set test_type based on input or event
if [[ -n "$INPUT_TEST_TYPE" ]]; then
test_type="$INPUT_TEST_TYPE"
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 "test_type=$test_type" >> $GITHUB_OUTPUT
echo "test_selection=$test_selection" >> $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"
echo "test_type=$test_type"
echo "test_selection=$test_selection"
move-type-imports:
name: move-type-imports
needs: determine-test-scope
if: needs.determine-test-scope.outputs.code_quality_tests == 'true'
runs-on: ubuntu-latest
steps:
- name: checkout-branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0
persist-credentials: false
- name: setup-python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.VERIFICATIONS_PY_VERSION }}
- name: install-dependencies
run: |
set -euo pipefail
python -m venv .venv
source .venv/bin/activate
pip install libcst
- name: verify-type-import-guards
run: |
set -euo pipefail
source .venv/bin/activate
python scripts/move_type_imports.py --mode check_on_change
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
submodules: false
persist-credentials: false
- uses: astral-sh/ruff-action@4919ec5cf1f49eff0871dbcea0da843445b837e6 # v3.6.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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
submodules: false
persist-credentials: false
- name: setup-python-${{ env.VERIFICATIONS_PY_VERSION }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ env.VERIFICATIONS_PY_VERSION }}
- 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:
contents: read
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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 --from "zizmor==1.19.0" zizmor .github/workflows/*.y* --format=sarif . > results.sarif
env:
GH_TOKEN: ${{ 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 --from "zizmor==1.19.0" zizmor .github/workflows/*.y*
env:
GH_TOKEN: ${{ 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
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: |
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
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
if: needs.determine-test-scope.outputs.code_quality_tests == 'true'
name: lint-commit-messages
# Soft-fail on PRs (early feedback), hard-fail in merge queue (enforced)
continue-on-error: ${{ github.event_name == 'pull_request' }}
steps:
- name: Check out source code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0 # fetch all commits in the PR
persist-credentials: false
- name: Setup node
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
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 (pull_request)
if: github.event_name == 'pull_request'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
npx commitlint --from $BASE_SHA --to $HEAD_SHA --verbose || {
echo "::warning::Commit messages don't follow conventional commits format. Fix before merge queue."
exit 1
}
- 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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 "$VERIFICATIONS_PY_VERSION"
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* ]] || [[ "$base_version" == *dev* ]]; then
echo "✅ Passing: Schema changed on a pre-release version ($base_version) in base branch '${base_ref}', which is permitted."
else
echo "❌ Failing: Schema changed on a non-pre-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 }}
TEST_SELECTION: ${{ needs.determine-test-scope.outputs.test_selection }}
permissions:
pull-requests: write
steps:
- name: checkout-head
if: ${{ !env.RELEASE_TAG }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
submodules: false
persist-credentials: false
lfs: true
- name: checkout-tag
if: ${{ env.RELEASE_TAG }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: refs/tags/${{ env.RELEASE_TAG }}
fetch-depth: 0
submodules: false
persist-credentials: false
lfs: true
- name: compute-testmon-cache-key
id: testmon-cache-key
if: needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group'
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch || 'develop' }}
run: |
default_branch="$DEFAULT_BRANCH"
case "$default_branch" in
''|*[!A-Za-z0-9._/-]*)
echo "::warning::Invalid default branch '$default_branch'; using 'develop'."
default_branch="develop"
;;
esac
default_branch_sha=$(git ls-remote --heads origin "refs/heads/${default_branch}" | awk '{print $1}')
if [[ -z "${default_branch_sha}" ]]; then
echo "::warning::Unable to resolve SHA for default branch '${default_branch}'."
default_branch_sha="unknown"
fi
dep_hash=$(
{
git show HEAD:pyproject.toml
git show HEAD:poetry.lock
} | sha256sum | awk '{print $1}'
)
cache_layout="v2"
if [[ "${GITHUB_EVENT_NAME}" == "merge_group" ]]; then
cache_sha="${GITHUB_SHA}"
else
cache_sha="${default_branch_sha}"
fi
echo "default_branch=$default_branch" >> "$GITHUB_OUTPUT"
echo "default_branch_sha=$default_branch_sha" >> "$GITHUB_OUTPUT"
echo "dep_hash=$dep_hash" >> "$GITHUB_OUTPUT"
echo "cache_layout=$cache_layout" >> "$GITHUB_OUTPUT"
echo "cache_sha=$cache_sha" >> "$GITHUB_OUTPUT"
- name: restore-testmon-cache
id: testmon-cache-restore
if: needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group'
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: .testmondata
key: testmon-local-${{ steps.testmon-cache-key.outputs.cache_layout }}-${{ runner.os }}-py${{ matrix.python-version }}-${{ steps.testmon-cache-key.outputs.default_branch }}-${{ steps.testmon-cache-key.outputs.dep_hash }}-${{ steps.testmon-cache-key.outputs.cache_sha }}
restore-keys: |
testmon-local-${{ steps.testmon-cache-key.outputs.cache_layout }}-${{ runner.os }}-py${{ matrix.python-version }}-${{ steps.testmon-cache-key.outputs.default_branch }}-${{ steps.testmon-cache-key.outputs.dep_hash }}-
- name: telemetry-cache-exact-hit
if: >-
(needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group') &&
steps.testmon-cache-restore.outputs.cache-hit == 'true'
run: echo "testmon cache exact hit"
- name: telemetry-cache-fallback-hit
if: >-
(needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group') &&
steps.testmon-cache-restore.outputs.cache-hit != 'true' &&
steps.testmon-cache-restore.outputs.cache-matched-key != ''
run: echo "testmon cache fallback hit"
- name: telemetry-cache-miss
if: >-
(needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group') &&
steps.testmon-cache-restore.outputs.cache-matched-key == ''
run: echo "testmon cache miss"
- 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: determine-pytest-selection-flags
id: pytest-selection-flags
env:
EVENT_NAME: ${{ github.event_name }}
run: |
if [[ "${TEST_SELECTION}" == "testmon" ]]; then
echo "flags=--testmon --testmon-forceselect" >> "$GITHUB_OUTPUT"
echo "mode=testmon_forceselect" >> "$GITHUB_OUTPUT"
echo "Using pytest testmon selection mode."
elif [[ "${TEST_SELECTION}" == "full" && "${EVENT_NAME}" == "merge_group" ]]; then
echo "flags=--testmon-noselect" >> "$GITHUB_OUTPUT"
echo "mode=full_with_testmon_collection" >> "$GITHUB_OUTPUT"
echo "Using full test execution with testmon collection mode."
else
echo "flags=--no-testmon" >> "$GITHUB_OUTPUT"
echo "mode=full_no_testmon" >> "$GITHUB_OUTPUT"
echo "Using full pytest execution mode."
fi
- name: telemetry-selection-testmon
if: steps.pytest-selection-flags.outputs.mode == 'testmon_forceselect'
run: echo "pytest selection testmon forceselect"
- name: telemetry-selection-full-with-testmon-collection
if: steps.pytest-selection-flags.outputs.mode == 'full_with_testmon_collection'
run: echo "pytest selection full with testmon collection"
- name: telemetry-selection-full-no-testmon
if: steps.pytest-selection-flags.outputs.mode == 'full_no_testmon'
run: echo "pytest selection full no testmon"
- name: run-tests-coverage
env:
PYTHONUNBUFFERED: "1"
PYTEST_SELECTION_FLAGS: ${{ steps.pytest-selection-flags.outputs.flags }}
run: |
source ${GITHUB_WORKSPACE}/.venv/bin/activate
# pytest --cov=tidy3d -rF --tb=short tests/_test_data/_test_datasets_no_vtk.py
pytest ${PYTEST_SELECTION_FLAGS} --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: stage-testmon-cache-candidate
if: success() && github.event_name == 'merge_group' && hashFiles('.testmondata') != ''
env:
CACHE_KEY: testmon-local-${{ steps.testmon-cache-key.outputs.cache_layout }}-${{ runner.os }}-py${{ matrix.python-version }}-${{ steps.testmon-cache-key.outputs.default_branch }}-${{ steps.testmon-cache-key.outputs.dep_hash }}-${{ steps.testmon-cache-key.outputs.cache_sha }}
run: |
set -euo pipefail
candidate_dir="$RUNNER_TEMP/testmon-cache-candidate-local-py${{ matrix.python-version }}"
rm -rf "$candidate_dir"
mkdir -p "$candidate_dir"
cp -R .testmondata "$candidate_dir/.testmondata"
cat > "$candidate_dir/metadata.json" <<EOF
{
"cache_key": "${CACHE_KEY}",
"job_class": "local",
"runner_os": "${{ runner.os }}",
"python_version": "${{ matrix.python-version }}"
}
EOF
- name: upload-testmon-cache-candidate
if: success() && github.event_name == 'merge_group' && hashFiles('.testmondata') != ''
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: testmon-cache-candidate-local-${{ runner.os }}-py${{ matrix.python-version }}-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/testmon-cache-candidate-local-py${{ matrix.python-version }}
include-hidden-files: true
retention-days: 1
- name: save-testmon-cache
if: success() && github.event_name == 'merge_group' && steps.testmon-cache-restore.outputs.cache-hit != 'true' && hashFiles('.testmondata') != ''
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: .testmondata
key: testmon-local-${{ steps.testmon-cache-key.outputs.cache_layout }}-${{ runner.os }}-py${{ matrix.python-version }}-${{ steps.testmon-cache-key.outputs.default_branch }}-${{ steps.testmon-cache-key.outputs.dep_hash }}-${{ steps.testmon-cache-key.outputs.cache_sha }}
- 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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
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 }}
TEST_SELECTION: ${{ needs.determine-test-scope.outputs.test_selection }}
steps:
- name: checkout-head
if: ${{ !env.RELEASE_TAG }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
submodules: false
persist-credentials: false
lfs: true
- name: checkout-tag
if: ${{ env.RELEASE_TAG }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: refs/tags/${{ env.RELEASE_TAG }}
fetch-depth: 1
submodules: false
persist-credentials: false
lfs: true
- name: compute-testmon-cache-key
id: testmon-cache-key
if: needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group'
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch || 'develop' }}
run: |
default_branch="$DEFAULT_BRANCH"
case "$default_branch" in
''|*[!A-Za-z0-9._/-]*)
echo "::warning::Invalid default branch '$default_branch'; using 'develop'."
default_branch="develop"
;;
esac
default_branch_sha=$(git ls-remote --heads origin "refs/heads/${default_branch}" | awk '{print $1}')
if [[ -z "${default_branch_sha}" ]]; then
echo "::warning::Unable to resolve SHA for default branch '${default_branch}'."
default_branch_sha="unknown"
fi
dep_hash=$(
{
git show HEAD:pyproject.toml
git show HEAD:poetry.lock
} | sha256sum | awk '{print $1}'
)
cache_layout="v2"
if [[ "${GITHUB_EVENT_NAME}" == "merge_group" ]]; then
cache_sha="${GITHUB_SHA}"
else
cache_sha="${default_branch_sha}"
fi
echo "default_branch=$default_branch" >> "$GITHUB_OUTPUT"
echo "default_branch_sha=$default_branch_sha" >> "$GITHUB_OUTPUT"
echo "dep_hash=$dep_hash" >> "$GITHUB_OUTPUT"
echo "cache_layout=$cache_layout" >> "$GITHUB_OUTPUT"
echo "cache_sha=$cache_sha" >> "$GITHUB_OUTPUT"
- name: restore-testmon-cache
id: testmon-cache-restore
if: needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group'
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: .testmondata
key: testmon-remote-${{ steps.testmon-cache-key.outputs.cache_layout }}-${{ matrix.platform }}-py${{ matrix.python-version }}-${{ steps.testmon-cache-key.outputs.default_branch }}-${{ steps.testmon-cache-key.outputs.dep_hash }}-${{ steps.testmon-cache-key.outputs.cache_sha }}
restore-keys: |
testmon-remote-${{ steps.testmon-cache-key.outputs.cache_layout }}-${{ matrix.platform }}-py${{ matrix.python-version }}-${{ steps.testmon-cache-key.outputs.default_branch }}-${{ steps.testmon-cache-key.outputs.dep_hash }}-
- name: telemetry-cache-exact-hit
if: >-
(needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group') &&
steps.testmon-cache-restore.outputs.cache-hit == 'true'
run: echo "testmon cache exact hit"
- name: telemetry-cache-fallback-hit
if: >-
(needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group') &&
steps.testmon-cache-restore.outputs.cache-hit != 'true' &&
steps.testmon-cache-restore.outputs.cache-matched-key != ''
run: echo "testmon cache fallback hit"
- name: telemetry-cache-miss
if: >-
(needs.determine-test-scope.outputs.test_selection == 'testmon' || github.event_name == 'merge_group') &&
steps.testmon-cache-restore.outputs.cache-matched-key == ''
run: echo "testmon cache miss"
- name: install-poetry
uses: snok/install-poetry@76e04a911780d5b312d89783f7b1cd627778900a # v1.4.1
with:
version: 2.1.1
virtualenvs-create: false
virtualenvs-in-project: true
- name: set-python-${{ matrix.python-version }}
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: ${{ matrix.python-version }}
- name: configure-aws-credentials
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-access-key-id: ${{ secrets.AWS_CODEARTIFACT_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.AWS_CODEARTIFACT_ACCESS_SECRET }}
aws-region: us-east-1
- name: configure-codeartifact-authentication
run: |
set -e
echo "Getting CodeArtifact token..."
CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token \
--domain flexcompute \
--domain-owner 625554095313 \
--query authorizationToken \
--output text)
echo "Configuring Poetry with CodeArtifact credentials..."
poetry config http-basic.codeartifact aws $CODEARTIFACT_AUTH_TOKEN
echo "✅ CodeArtifact authentication configured"
- name: update-tidy3d-extras-lockfile
run: |
set -e
echo "Updating tidy3d-extras in poetry.lock..."
poetry update tidy3d-extras --lock
echo "✅ tidy3d-extras updated in lockfile"
- name: install-project
shell: bash
run: |
poetry --version
python --version
rm -rf .venv
if [[ "${{ runner.os }}" == "Windows" ]]; then
python -m venv .venv
source .venv/Scripts/activate
else
echo "DEBUG: pythonLocation=$pythonLocation"
"$pythonLocation/bin/python" -m venv .venv
source .venv/bin/activate
echo "DEBUG: .venv/bin/python --version = $(.venv/bin/python --version)"
ls -la .venv/bin/python*
fi
poetry config virtualenvs.create false --local
poetry env info
poetry run pip install --upgrade pip wheel setuptools
poetry run pip install gdstk --only-binary gdstk
poetry install -E dev
- name: determine-pytest-selection-flags
id: pytest-selection-flags
env:
EVENT_NAME: ${{ github.event_name }}
run: |
if [[ "${TEST_SELECTION}" == "testmon" ]]; then
echo "flags=--testmon --testmon-forceselect" >> "$GITHUB_OUTPUT"
echo "mode=testmon_forceselect" >> "$GITHUB_OUTPUT"
echo "Using pytest testmon selection mode."
elif [[ "${TEST_SELECTION}" == "full" && "${EVENT_NAME}" == "merge_group" ]]; then
echo "flags=--testmon-noselect" >> "$GITHUB_OUTPUT"
echo "mode=full_with_testmon_collection" >> "$GITHUB_OUTPUT"
echo "Using full test execution with testmon collection mode."
else
echo "flags=--no-testmon" >> "$GITHUB_OUTPUT"
echo "mode=full_no_testmon" >> "$GITHUB_OUTPUT"
echo "Using full pytest execution mode."
fi
- name: telemetry-selection-testmon
if: steps.pytest-selection-flags.outputs.mode == 'testmon_forceselect'
run: echo "pytest selection testmon forceselect"
- name: telemetry-selection-full-with-testmon-collection
if: steps.pytest-selection-flags.outputs.mode == 'full_with_testmon_collection'
run: echo "pytest selection full with testmon collection"
- name: telemetry-selection-full-no-testmon
if: steps.pytest-selection-flags.outputs.mode == 'full_no_testmon'
run: echo "pytest selection full no testmon"
- name: run-doctests
env:
PYTEST_SELECTION_FLAGS: ${{ steps.pytest-selection-flags.outputs.flags }}
run: |
poetry run pytest ${PYTEST_SELECTION_FLAGS} -rF --tb=short tidy3d
- name: run-tests-coverage
env:
PYTHONUNBUFFERED: "1"
PYTEST_SELECTION_FLAGS: ${{ steps.pytest-selection-flags.outputs.flags }}
run: |
poetry run pytest ${PYTEST_SELECTION_FLAGS} --cov=tidy3d -rF --tb=short tests/_test_data/_test_datasets_no_vtk.py
poetry run pytest ${PYTEST_SELECTION_FLAGS} --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: stage-testmon-cache-candidate
if: success() && github.event_name == 'merge_group' && hashFiles('.testmondata') != ''
env:
CACHE_KEY: testmon-remote-${{ steps.testmon-cache-key.outputs.cache_layout }}-${{ matrix.platform }}-py${{ matrix.python-version }}-${{ steps.testmon-cache-key.outputs.default_branch }}-${{ steps.testmon-cache-key.outputs.dep_hash }}-${{ steps.testmon-cache-key.outputs.cache_sha }}
run: |
set -euo pipefail
candidate_dir="$RUNNER_TEMP/testmon-cache-candidate-remote-${{ matrix.platform }}-py${{ matrix.python-version }}"
rm -rf "$candidate_dir"
mkdir -p "$candidate_dir"
cp -R .testmondata "$candidate_dir/.testmondata"
cat > "$candidate_dir/metadata.json" <<EOF
{
"cache_key": "${CACHE_KEY}",
"job_class": "remote",
"platform": "${{ matrix.platform }}",
"python_version": "${{ matrix.python-version }}"
}
EOF
- name: upload-testmon-cache-candidate
if: success() && github.event_name == 'merge_group' && hashFiles('.testmondata') != ''
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: testmon-cache-candidate-remote-${{ matrix.platform }}-py${{ matrix.python-version }}-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/testmon-cache-candidate-remote-${{ matrix.platform }}-py${{ matrix.python-version }}
include-hidden-files: true
retention-days: 1
- name: save-testmon-cache
if: success() && github.event_name == 'merge_group' && steps.testmon-cache-restore.outputs.cache-hit != 'true' && hashFiles('.testmondata') != ''
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
with:
path: .testmondata
key: testmon-remote-${{ steps.testmon-cache-key.outputs.cache_layout }}-${{ matrix.platform }}-py${{ matrix.python-version }}-${{ steps.testmon-cache-key.outputs.default_branch }}-${{ steps.testmon-cache-key.outputs.dep_hash }}-${{ steps.testmon-cache-key.outputs.cache_sha }}
- 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: 'recursive'
fetch-depth: 0
persist-credentials: true
- name: checkout-tag
if: ${{ env.RELEASE_TAG }}
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
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-tests-integration.yml
with:
release_tag: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag || inputs.release_tag }}
test_type: ${{ needs.determine-test-scope.outputs.test_type }}
secrets: inherit # zizmor: ignore[secrets-inherit]
workflow-validation:
name: workflow-validation
if: always()
needs:
- determine-test-scope
- local-tests
- remote-tests
- move-type-imports
- lint
- mypy
- verify-schema-change
- lint-commit-messages
- lint-branch-name
- enforce-changelog-policy
- zizmor
- develop-cli-tests
- verify-version-consistency
- test-submodules
- extras-integration-tests
runs-on: ubuntu-latest
steps:
- name: move-type-imports
if: ${{ needs.determine-test-scope.outputs.code_quality_tests == 'true' && needs.move-type-imports.result != 'success' && needs.move-type-imports.result != 'skipped' }}
run: |
echo "❌ Found imports used only for typing that are not guarded by if TYPE_CHECKING."
exit 1
- 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-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: |
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!"