From e4974a3dfc1ece1bef51038e627a83ae5531eb90 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Fri, 23 Oct 2026 14:40:03 -0700 Subject: [PATCH 1/4] ci: gate merge on CI completion and enable core package Node unit tests --- .github/actions/wait-for-ci/action.yaml | 127 +++++++++++++++++++++++ .github/workflows/generator-tests.yaml | 55 +++++++++- .github/workflows/presubmit.yaml | 33 +++++- .github/workflows/windows-presubmit.yaml | 33 +++++- ci/run_conditional_tests.sh | 11 +- 5 files changed, 248 insertions(+), 11 deletions(-) create mode 100644 .github/actions/wait-for-ci/action.yaml diff --git a/.github/actions/wait-for-ci/action.yaml b/.github/actions/wait-for-ci/action.yaml new file mode 100644 index 00000000000..74759e7d13c --- /dev/null +++ b/.github/actions/wait-for-ci/action.yaml @@ -0,0 +1,127 @@ +name: "Wait for CI" +description: "Waits for all PR CI workflow runs to finish and verifies generator-tests succeeded when applicable" +inputs: + generator-changed: + required: false + default: "false" + description: "Whether generator files were modified in the PR" +runs: + using: "composite" + steps: + - name: Wait for all CI workflows to finish + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + GENERATOR_CHANGED: ${{ inputs.generator-changed }} + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const headSha = context.payload.pull_request.head.sha; + const generatorChanged = process.env.GENERATOR_CHANGED === 'true'; + const pollIntervalMs = 30000; + const maxWaitMs = 46 * 60 * 1000; + const startTime = Date.now(); + const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); + + const isPresubmitUnitWorkflow = run => + run.path === '.github/workflows/presubmit.yaml' || + run.path === '.github/workflows/windows-presubmit.yaml' || + run.name === 'presubmit' || + run.name === 'presubmit-windows'; + + const isGateJob = jobName => + jobName === 'Wait for CI' || /^units \(\d+\)$/.test(jobName); + + const isGeneratorWorkflow = run => + run.path === '.github/workflows/generator-tests.yaml' || + run.name === 'generator-tests'; + + while (Date.now() - startTime < maxWaitMs) { + const { data: { workflow_runs: runs } } = await github.rest.actions.listWorkflowRunsForRepo({ + owner, + repo, + head_sha: headSha, + event: 'pull_request', + per_page: 100, + }); + + // Keep only the most recent workflow run per workflow_id + const latestByWorkflow = new Map(); + for (const run of runs) { + const existing = latestByWorkflow.get(run.workflow_id); + if (!existing || new Date(run.created_at) > new Date(existing.created_at)) { + latestByWorkflow.set(run.workflow_id, run); + } + } + + const pendingItems = []; + let generatorRun = null; + + for (const run of latestByWorkflow.values()) { + if (isGeneratorWorkflow(run)) { + generatorRun = run; + } + + if (isPresubmitUnitWorkflow(run)) { + if (run.status === 'completed') { + continue; + } + const { data: { jobs } } = await github.rest.actions.listJobsForWorkflowRun({ + owner, + repo, + run_id: run.id, + per_page: 100, + }); + const workJobs = jobs.filter(j => !isGateJob(j.name)); + if (workJobs.length === 0) { + pendingItems.push(`${run.name} (initializing jobs)`); + } else { + const runningJobs = workJobs.filter(j => j.status !== 'completed'); + for (const j of runningJobs) { + pendingItems.push(`${run.name} / ${j.name} (${j.status})`); + } + } + } else if (run.status !== 'completed') { + pendingItems.push(`${run.name} (${run.status})`); + } + } + + if (generatorChanged && !generatorRun) { + pendingItems.push('generator-tests (waiting for workflow run to register)'); + } + + if (pendingItems.length === 0) { + core.info(`All CI workflows for SHA ${headSha} have finished.`); + for (const run of latestByWorkflow.values()) { + if (isPresubmitUnitWorkflow(run)) { + continue; + } + core.info(`- ${run.name}: conclusion=${run.conclusion} (${run.html_url})`); + if ( + !isGeneratorWorkflow(run) && + run.conclusion !== 'success' && + run.conclusion !== 'skipped' && + run.conclusion !== 'neutral' + ) { + core.warning(`Non-required workflow '${run.name}' finished with conclusion '${run.conclusion}': ${run.html_url}`); + } + } + + if (generatorRun && generatorRun.conclusion !== 'success' && generatorRun.conclusion !== 'skipped') { + core.setFailed(`generator-tests finished with conclusion '${generatorRun.conclusion}': ${generatorRun.html_url}`); + return; + } + if (generatorChanged && (!generatorRun || generatorRun.conclusion !== 'success')) { + core.setFailed( + `generator-tests did not succeed (conclusion: ${generatorRun ? generatorRun.conclusion : 'missing'})`, + ); + return; + } + return; + } + + core.info(`Waiting for ${pendingItems.length} CI step(s) to finish:\n- ${pendingItems.join('\n- ')}`); + await sleep(pollIntervalMs); + } + + core.setFailed(`Timed out waiting for CI workflows to finish on SHA ${headSha}`); diff --git a/.github/workflows/generator-tests.yaml b/.github/workflows/generator-tests.yaml index 385fef42e84..368d37968b8 100644 --- a/.github/workflows/generator-tests.yaml +++ b/.github/workflows/generator-tests.yaml @@ -10,9 +10,6 @@ on: - 'core/generator/**' - '.github/workflows/generator-tests.yaml' pull_request: - paths: - - 'core/generator/**' - - '.github/workflows/generator-tests.yaml' workflow_dispatch: concurrency: @@ -20,8 +17,29 @@ concurrency: cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: - presubmit: - if: github.event_name == 'pull_request' + changes: + runs-on: ubuntu-latest + outputs: + generator: ${{ steps.filter.outputs.generator }} + steps: + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + with: + fetch-depth: 2 + persist-credentials: false + - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 + id: filter + with: + filters: | + generator: + - 'core/generator/**' + - '.github/workflows/generator-tests.yaml' + + generator-tests: + needs: changes + if: >- + (github.event_name == 'pull_request' && needs.changes.outputs.generator == 'true') || + github.event_name == 'push' || + github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest timeout-minutes: 45 container: gcr.io/gapic-images/googleapis:20250404 @@ -154,3 +172,30 @@ jobs: echo "Some ESM library tests failed!" exit 1 fi + + # Consolidate generator test status to satisfy branch protection requirements + presubmit: + needs: [changes, generator-tests] + runs-on: ubuntu-latest + if: always() + env: + CHANGES_RESULT: ${{ needs.changes.result }} + GENERATOR_CHANGED: ${{ needs.changes.outputs.generator }} + TESTS_RESULT: ${{ needs.generator-tests.result }} + EVENT_NAME: ${{ github.event_name }} + steps: + - name: Check generator test status + run: | + if [ "$CHANGES_RESULT" != "success" ]; then + echo "Path filter job failed (result: $CHANGES_RESULT)." + exit 1 + fi + if [ "$EVENT_NAME" = "pull_request" ] && [ "$GENERATOR_CHANGED" != "true" ]; then + echo "No generator changes detected; skipping generator tests." + exit 0 + fi + if [ "$TESTS_RESULT" != "success" ]; then + echo "Generator tests did not succeed (result: $TESTS_RESULT)." + exit 1 + fi + echo "Generator tests passed!" diff --git a/.github/workflows/presubmit.yaml b/.github/workflows/presubmit.yaml index 675a0988b9a..7911821120d 100644 --- a/.github/workflows/presubmit.yaml +++ b/.github/workflows/presubmit.yaml @@ -15,11 +15,19 @@ jobs: outputs: shard-matrix: ${{ steps.set-matrix.outputs.shard_matrix }} shard-total: ${{ steps.set-matrix.outputs.shard_total }} + generator-changed: ${{ steps.changes.outputs.generator }} steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 with: fetch-depth: 2 persist-credentials: false + - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 + id: changes + with: + filters: | + generator: + - 'core/generator/**' + - '.github/workflows/generator-tests.yaml' - id: set-matrix uses: ./.github/actions/calculate-shard-matrix units: @@ -74,20 +82,43 @@ jobs: env: GIT_DIFF_ARG: "HEAD^1...HEAD -- :!packages" + wait-for-ci: + name: Wait for CI + needs: [setup, units, lint] + runs-on: ubuntu-latest + if: always() + timeout-minutes: 50 + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + with: + persist-credentials: false + - name: Wait for all CI workflows to finish + uses: ./.github/actions/wait-for-ci + with: + generator-changed: ${{ needs.setup.outputs.generator-changed }} + # Consolidate shards into jobs representing aggregate status to simplify branch protection requirements units-status: name: units (${{ matrix.node-version }}) - needs: units + needs: [setup, units, wait-for-ci] runs-on: ubuntu-latest if: always() strategy: fail-fast: false matrix: node-version: [22, 24, 26] + env: + WAIT_FOR_CI_RESULT: ${{ needs.wait-for-ci.result }} steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 with: persist-credentials: false + - name: Verify CI completion and generator test status + run: | + if [ "$WAIT_FOR_CI_RESULT" != "success" ]; then + echo "CI completion check or generator-tests failed (result: $WAIT_FOR_CI_RESULT)." + exit 1 + fi - name: Check shard status uses: ./.github/actions/check-shard-status with: diff --git a/.github/workflows/windows-presubmit.yaml b/.github/workflows/windows-presubmit.yaml index d883e5d22c3..362e9f4caec 100644 --- a/.github/workflows/windows-presubmit.yaml +++ b/.github/workflows/windows-presubmit.yaml @@ -15,11 +15,19 @@ jobs: outputs: shard-matrix: ${{ steps.set-matrix.outputs.shard_matrix }} shard-total: ${{ steps.set-matrix.outputs.shard_total }} + generator-changed: ${{ steps.changes.outputs.generator }} steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 with: fetch-depth: 2 persist-credentials: false + - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 + id: changes + with: + filters: | + generator: + - 'core/generator/**' + - '.github/workflows/generator-tests.yaml' - id: set-matrix uses: ./.github/actions/calculate-shard-matrix units: @@ -42,20 +50,43 @@ jobs: shard-total: ${{ needs.setup.outputs.shard-total }} shard-index: ${{ matrix.shard-index }} + wait-for-ci: + name: Wait for CI + needs: [setup, units] + runs-on: ubuntu-latest + if: always() + timeout-minutes: 50 + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 + with: + persist-credentials: false + - name: Wait for all CI workflows to finish + uses: ./.github/actions/wait-for-ci + with: + generator-changed: ${{ needs.setup.outputs.generator-changed }} + # Dummy jobs to satisfy branch protection requirements for each node version units-status: name: units (${{ matrix.node-version }}) - needs: units + needs: [setup, units, wait-for-ci] runs-on: ubuntu-latest if: always() strategy: fail-fast: false matrix: node-version: [22, 24, 26] + env: + WAIT_FOR_CI_RESULT: ${{ needs.wait-for-ci.result }} steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 with: persist-credentials: false + - name: Verify CI completion and generator test status + run: | + if [ "$WAIT_FOR_CI_RESULT" != "success" ]; then + echo "CI completion check or generator-tests failed (result: $WAIT_FOR_CI_RESULT)." + exit 1 + fi - name: Check shard status uses: ./.github/actions/check-shard-status with: diff --git a/ci/run_conditional_tests.sh b/ci/run_conditional_tests.sh index dbe8d01ad89..90543568509 100755 --- a/ci/run_conditional_tests.sh +++ b/ci/run_conditional_tests.sh @@ -240,8 +240,8 @@ for subdir in "${subdirs[@]}"; do echo "run samples tests for core/packages in ${d}" should_test=true fi - elif [[ "${d}" == core/packages/* ]] && [[ "${JS_RUNTIME}" == "bun" || "${TEST_CMD}" == *bun* ]] && [[ "${TEST_TYPE}" == "units" ]]; then - echo "change detected in core package ${d} for Bun ${TEST_TYPE} test" + elif ([[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]) && [[ "${TEST_TYPE}" == "units" ]]; then + echo "change detected in core package ${d} for ${TEST_TYPE} test" should_test=true elif [[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]; then echo "skipping core package ${d} in non-core trigger" @@ -270,8 +270,11 @@ for subdir in "${subdirs[@]}"; do echo "run samples tests for core/packages in ${d}" should_test=true fi - # elif [[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]; then - # echo "skipping core package ${d} in non-core trigger" + elif ([[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]) && [[ "${TEST_TYPE}" == "units" ]]; then + echo "run ${TEST_TYPE} test for core package ${d}" + should_test=true + elif [[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]; then + echo "skipping core package ${d} in non-core trigger" elif [[ "${TEST_TYPE}" == "system" ]] || [[ "${TEST_TYPE}" == "lint" ]] || [[ "${TEST_TYPE}" == "units" ]]; then echo "run ${TEST_TYPE} test for ${d}" should_test=true From cbb42fae535ed774af19e6ed4efdbda387dc959c Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Fri, 23 Oct 2026 14:53:10 -0700 Subject: [PATCH 2/4] ci: wrap wait-for-ci polling loop in try-catch for transient API errors --- .github/actions/wait-for-ci/action.yaml | 141 ++++++++++++------------ 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/.github/actions/wait-for-ci/action.yaml b/.github/actions/wait-for-ci/action.yaml index 74759e7d13c..0c6c98bcf04 100644 --- a/.github/actions/wait-for-ci/action.yaml +++ b/.github/actions/wait-for-ci/action.yaml @@ -37,90 +37,95 @@ runs: run.name === 'generator-tests'; while (Date.now() - startTime < maxWaitMs) { - const { data: { workflow_runs: runs } } = await github.rest.actions.listWorkflowRunsForRepo({ - owner, - repo, - head_sha: headSha, - event: 'pull_request', - per_page: 100, - }); + try { + const { data: { workflow_runs: runs } } = await github.rest.actions.listWorkflowRunsForRepo({ + owner, + repo, + head_sha: headSha, + event: 'pull_request', + per_page: 100, + }); - // Keep only the most recent workflow run per workflow_id - const latestByWorkflow = new Map(); - for (const run of runs) { - const existing = latestByWorkflow.get(run.workflow_id); - if (!existing || new Date(run.created_at) > new Date(existing.created_at)) { - latestByWorkflow.set(run.workflow_id, run); + // Keep only the most recent workflow run per workflow_id + const latestByWorkflow = new Map(); + for (const run of runs) { + const existing = latestByWorkflow.get(run.workflow_id); + if (!existing || new Date(run.created_at) > new Date(existing.created_at)) { + latestByWorkflow.set(run.workflow_id, run); + } } - } - - const pendingItems = []; - let generatorRun = null; - for (const run of latestByWorkflow.values()) { - if (isGeneratorWorkflow(run)) { - generatorRun = run; - } + const pendingItems = []; + let generatorRun = null; - if (isPresubmitUnitWorkflow(run)) { - if (run.status === 'completed') { - continue; + for (const run of latestByWorkflow.values()) { + if (isGeneratorWorkflow(run)) { + generatorRun = run; } - const { data: { jobs } } = await github.rest.actions.listJobsForWorkflowRun({ - owner, - repo, - run_id: run.id, - per_page: 100, - }); - const workJobs = jobs.filter(j => !isGateJob(j.name)); - if (workJobs.length === 0) { - pendingItems.push(`${run.name} (initializing jobs)`); - } else { - const runningJobs = workJobs.filter(j => j.status !== 'completed'); - for (const j of runningJobs) { - pendingItems.push(`${run.name} / ${j.name} (${j.status})`); + + if (isPresubmitUnitWorkflow(run)) { + if (run.status === 'completed') { + continue; + } + const { data: { jobs } } = await github.rest.actions.listJobsForWorkflowRun({ + owner, + repo, + run_id: run.id, + per_page: 100, + }); + const workJobs = jobs.filter(j => !isGateJob(j.name)); + if (workJobs.length === 0) { + pendingItems.push(`${run.name} (initializing jobs)`); + } else { + const runningJobs = workJobs.filter(j => j.status !== 'completed'); + for (const j of runningJobs) { + pendingItems.push(`${run.name} / ${j.name} (${j.status})`); + } } + } else if (run.status !== 'completed') { + pendingItems.push(`${run.name} (${run.status})`); } - } else if (run.status !== 'completed') { - pendingItems.push(`${run.name} (${run.status})`); } - } - if (generatorChanged && !generatorRun) { - pendingItems.push('generator-tests (waiting for workflow run to register)'); - } + if (generatorChanged && !generatorRun) { + pendingItems.push('generator-tests (waiting for workflow run to register)'); + } - if (pendingItems.length === 0) { - core.info(`All CI workflows for SHA ${headSha} have finished.`); - for (const run of latestByWorkflow.values()) { - if (isPresubmitUnitWorkflow(run)) { - continue; - } - core.info(`- ${run.name}: conclusion=${run.conclusion} (${run.html_url})`); - if ( - !isGeneratorWorkflow(run) && - run.conclusion !== 'success' && - run.conclusion !== 'skipped' && - run.conclusion !== 'neutral' - ) { - core.warning(`Non-required workflow '${run.name}' finished with conclusion '${run.conclusion}': ${run.html_url}`); + if (pendingItems.length === 0) { + core.info(`All CI workflows for SHA ${headSha} have finished.`); + for (const run of latestByWorkflow.values()) { + if (isPresubmitUnitWorkflow(run)) { + continue; + } + core.info(`- ${run.name}: conclusion=${run.conclusion} (${run.html_url})`); + if ( + !isGeneratorWorkflow(run) && + run.conclusion !== 'success' && + run.conclusion !== 'skipped' && + run.conclusion !== 'neutral' + ) { + core.warning(`Non-required workflow '${run.name}' finished with conclusion '${run.conclusion}': ${run.html_url}`); + } } - } - if (generatorRun && generatorRun.conclusion !== 'success' && generatorRun.conclusion !== 'skipped') { - core.setFailed(`generator-tests finished with conclusion '${generatorRun.conclusion}': ${generatorRun.html_url}`); - return; - } - if (generatorChanged && (!generatorRun || generatorRun.conclusion !== 'success')) { - core.setFailed( - `generator-tests did not succeed (conclusion: ${generatorRun ? generatorRun.conclusion : 'missing'})`, - ); + if (generatorRun && generatorRun.conclusion !== 'success' && generatorRun.conclusion !== 'skipped') { + core.setFailed(`generator-tests finished with conclusion '${generatorRun.conclusion}': ${generatorRun.html_url}`); + return; + } + if (generatorChanged && (!generatorRun || generatorRun.conclusion !== 'success')) { + core.setFailed( + `generator-tests did not succeed (conclusion: ${generatorRun ? generatorRun.conclusion : 'missing'})`, + ); + return; + } return; } - return; + + core.info(`Waiting for ${pendingItems.length} CI step(s) to finish:\n- ${pendingItems.join('\n- ')}`); + } catch (err) { + core.warning(`Error polling CI workflow status (will retry in ${pollIntervalMs / 1000}s): ${err instanceof Error ? err.message : String(err)}`); } - core.info(`Waiting for ${pendingItems.length} CI step(s) to finish:\n- ${pendingItems.join('\n- ')}`); await sleep(pollIntervalMs); } From 75a2288f08064d1c1ad1fc9487f20a6ce2c18989 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Fri, 23 Oct 2026 14:53:59 -0700 Subject: [PATCH 3/4] ci: fall back to context.sha when pull_request payload is absent --- .github/actions/wait-for-ci/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/wait-for-ci/action.yaml b/.github/actions/wait-for-ci/action.yaml index 0c6c98bcf04..1ef84d1d3df 100644 --- a/.github/actions/wait-for-ci/action.yaml +++ b/.github/actions/wait-for-ci/action.yaml @@ -16,7 +16,7 @@ runs: script: | const owner = context.repo.owner; const repo = context.repo.repo; - const headSha = context.payload.pull_request.head.sha; + const headSha = context.payload.pull_request?.head?.sha || context.sha; const generatorChanged = process.env.GENERATOR_CHANGED === 'true'; const pollIntervalMs = 30000; const maxWaitMs = 46 * 60 * 1000; From 853142bb3494ad98a58f91540d60cf669f6c25c6 Mon Sep 17 00:00:00 2001 From: Shivanee Persaud Date: Fri, 23 Oct 2026 14:55:06 -0700 Subject: [PATCH 4/4] ci: avoid subshell parentheses in run_conditional_tests.sh conditionals --- ci/run_conditional_tests.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ci/run_conditional_tests.sh b/ci/run_conditional_tests.sh index 90543568509..b67217d2081 100755 --- a/ci/run_conditional_tests.sh +++ b/ci/run_conditional_tests.sh @@ -228,11 +228,11 @@ for subdir in "${subdirs[@]}"; do if [[ "${changed}" -eq 0 ]]; then echo "no change detected in ${d}, skipping" else - if ([[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]) && [[ "${IS_CORE}" == "true" ]] && [[ "${TEST_TYPE}" == "system" ]]; then + if [[ ( "${d}" == core/packages/* || "${d}" == core/dev-packages/* ) && "${IS_CORE}" == "true" && "${TEST_TYPE}" == "system" ]]; then echo "run system tests for core/packages in ${d}" export RUN_INTERDEPENDENT_TESTS=true should_test=true - elif ([[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]) && [[ "${IS_CORE}" == "true" ]] && [[ "${TEST_TYPE}" == "samples" ]]; then + elif [[ ( "${d}" == core/packages/* || "${d}" == core/dev-packages/* ) && "${IS_CORE}" == "true" && "${TEST_TYPE}" == "samples" ]]; then if [[ "${tests_with_credentials[*]}" =~ "${d}" ]] && [[ -n "${GOOGLE_APPLICATION_CREDENTIALS}" ]]; then echo "run samples tests with credentials for core/packages in ${d}" should_test=true @@ -240,10 +240,10 @@ for subdir in "${subdirs[@]}"; do echo "run samples tests for core/packages in ${d}" should_test=true fi - elif ([[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]) && [[ "${TEST_TYPE}" == "units" ]]; then + elif [[ ( "${d}" == core/packages/* || "${d}" == core/dev-packages/* ) && "${TEST_TYPE}" == "units" ]]; then echo "change detected in core package ${d} for ${TEST_TYPE} test" should_test=true - elif [[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]; then + elif [[ "${d}" == core/packages/* || "${d}" == core/dev-packages/* ]]; then echo "skipping core package ${d} in non-core trigger" elif [[ "${TEST_TYPE}" == "system" ]] || [[ "${TEST_TYPE}" == "lint" ]] || [[ "${TEST_TYPE}" == "units" ]]; then echo "change detected in ${d} for ${TEST_TYPE} test" @@ -258,11 +258,11 @@ for subdir in "${subdirs[@]}"; do fi else # If GIT_DIFF_ARG is empty, run all the tests. - if ([[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]) && [[ "${IS_CORE}" == "true" ]] && [[ "${TEST_TYPE}" == "system" ]]; then + if [[ ( "${d}" == core/packages/* || "${d}" == core/dev-packages/* ) && "${IS_CORE}" == "true" && "${TEST_TYPE}" == "system" ]]; then echo "run system tests for core/packages in ${d}" export RUN_INTERDEPENDENT_TESTS=true should_test=true - elif ([[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]) && [[ "${IS_CORE}" == "true" ]] && [[ "${TEST_TYPE}" == "samples" ]]; then + elif [[ ( "${d}" == core/packages/* || "${d}" == core/dev-packages/* ) && "${IS_CORE}" == "true" && "${TEST_TYPE}" == "samples" ]]; then if [[ "${tests_with_credentials[*]}" =~ "${d}" ]] && [[ -n "${GOOGLE_APPLICATION_CREDENTIALS}" ]]; then echo "run samples tests with credentials for core/packages in ${d}" should_test=true @@ -270,10 +270,10 @@ for subdir in "${subdirs[@]}"; do echo "run samples tests for core/packages in ${d}" should_test=true fi - elif ([[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]) && [[ "${TEST_TYPE}" == "units" ]]; then + elif [[ ( "${d}" == core/packages/* || "${d}" == core/dev-packages/* ) && "${TEST_TYPE}" == "units" ]]; then echo "run ${TEST_TYPE} test for core package ${d}" should_test=true - elif [[ "${d}" == core/packages/* ]] || [[ "${d}" == core/dev-packages/* ]]; then + elif [[ "${d}" == core/packages/* || "${d}" == core/dev-packages/* ]]; then echo "skipping core package ${d} in non-core trigger" elif [[ "${TEST_TYPE}" == "system" ]] || [[ "${TEST_TYPE}" == "lint" ]] || [[ "${TEST_TYPE}" == "units" ]]; then echo "run ${TEST_TYPE} test for ${d}"