chore(deps-dev): bump the python group across 1 directory with 10 updates #9287
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: | |
| 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 | |
| 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 | |
| # 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 | |
| test_selection="testmon" | |
| fi | |
| if [[ "$EVENT_NAME" == "merge_group" ]]; then | |
| local_tests=true | |
| remote_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 "extras_integration_tests=$extras_integration_tests" >> $GITHUB_OUTPUT | |
| echo "test_type=$test_type" >> $GITHUB_OUTPUT | |
| echo "test_selection=$test_selection" >> $GITHUB_OUTPUT | |
| 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" | |
| 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 | |
| if git cat-file -e HEAD:uv.lock 2>/dev/null; then | |
| git show HEAD:uv.lock | |
| else | |
| git show HEAD:poetry.lock | |
| fi | |
| } | 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| 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 | |
| if git cat-file -e HEAD:uv.lock 2>/dev/null; then | |
| git show HEAD:uv.lock | |
| else | |
| git show HEAD:poetry.lock | |
| fi | |
| } | 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: set-python-${{ matrix.python-version }} | |
| uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: install-uv | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install uv | |
| - name: install-project | |
| shell: bash | |
| run: | | |
| uv --version | |
| python --version | |
| uv sync --frozen --extra dev | |
| PROJECT_PYTHON=$(uv run --frozen python -c "import sys; print(sys.executable)") | |
| uv pip install --python "$PROJECT_PYTHON" --upgrade wheel setuptools | |
| uv pip install --python "$PROJECT_PYTHON" gdstk --only-binary gdstk | |
| - 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: | | |
| uv run --frozen 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: | | |
| uv run --frozen pytest ${PYTEST_SELECTION_FLAGS} --cov=tidy3d -rF --tb=short tests/_test_data/_test_datasets_no_vtk.py | |
| uv run --frozen pytest ${PYTEST_SELECTION_FLAGS} --cov=tidy3d -rF --tb=short tests | |
| uv run --frozen coverage report -m | |
| TOTAL_COVERAGE=$(uv run --frozen 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| 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 | |
| - develop-cli-tests | |
| - verify-version-consistency | |
| - test-submodules | |
| - extras-integration-tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - 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-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!" |