chore(deps): bump the github-actions group across 1 directory with 3 updates #9088
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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!" |