Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5da7791
feat(ci): run unit-tests in parallel to speed them up
bshaffer Jul 29, 2026
0d9eb53
chore(ci): reduce unit-test output due to log truncation
bshaffer Jul 29, 2026
6a14237
chore(ci): rename conformance tests for clarity
bshaffer Jul 29, 2026
740552b
chore: run all ci tests
bshaffer Jul 29, 2026
3d37a2c
fix(ci): failing unit-tests
bshaffer Jul 29, 2026
24763d2
feat(ci): add matrix sharding
bshaffer Jul 29, 2026
0c5f19a
fix bash syntax error, better job names
bshaffer Jul 29, 2026
5b0a4a4
reduce fetch-depth for github actions runs, remove concurrency
bshaffer Jul 29, 2026
37e6bcf
remove c8 text output
bshaffer Jul 29, 2026
dd49119
feat: add conditional sharding using DRY_RUN_SHARDS envvar
bshaffer Jul 29, 2026
90754d4
fix(ci): common and pack-and-play tests
bshaffer Jul 29, 2026
e42dd27
Revert "remove c8 text output" - we will handle this in a future PR
bshaffer Jul 29, 2026
f12544e
add parent job for unit test shards (by node version), remove firesto…
bshaffer Jul 29, 2026
9d358d3
add dummy shards and extend test timeout for windows
bshaffer Jul 30, 2026
127374f
improve bigtable unit-test performance
bshaffer Jul 30, 2026
9ebd7db
increase timeout to 10 minutes for windows tests
bshaffer Jul 30, 2026
ce4850e
fix bigtable-system-tests
bshaffer Jul 30, 2026
cb703c9
skip pack-n-play tests on windows
bshaffer Jul 30, 2026
52cbb6c
skip trigger for running all tests
bshaffer Jul 30, 2026
3960dab
modify google-cloud-vision to run tests in only one component
bshaffer Jul 30, 2026
0240ec2
fix bigtable unit tests
bshaffer Jul 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
pull_request:
paths:
- 'handwritten/bigtable/**'
name: conformance
name: bigtable-conformance
jobs:
conformance:
runs-on: ubuntu-latest
Expand All @@ -36,16 +36,16 @@
matrix:
node: [ 18, 20 ]
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

Check warning on line 39 in .github/workflows/bigtable-conformance.yaml

View workflow job for this annotation

GitHub Actions / zizmor-output

ref-version-mismatch

bigtable-conformance.yaml:39: action's hash pin has mismatched or missing version comment: points to commit 11d5960a3267
with:
persist-credentials: false
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

Check warning on line 42 in .github/workflows/bigtable-conformance.yaml

View workflow job for this annotation

GitHub Actions / zizmor-output

ref-version-mismatch

bigtable-conformance.yaml:42: action's hash pin has mismatched or missing version comment: points to commit 11d5960a3267
with:
repository: googleapis/cloud-bigtable-clients-test
ref: main
path: handwritten/bigtable/cloud-bigtable-clients-test
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6

Check warning on line 48 in .github/workflows/bigtable-conformance.yaml

View workflow job for this annotation

GitHub Actions / zizmor-output

ref-version-mismatch

bigtable-conformance.yaml:48: action's hash pin has mismatched or missing version comment: points to commit 249970729cb0
with:
node-version: ${{ matrix.node }}
- uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
Expand Down
63 changes: 61 additions & 2 deletions .github/workflows/presubmit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,37 @@
pull_request:
name: presubmit
jobs:
setup:
runs-on: ubuntu-latest
outputs:
shard-matrix: ${{ steps.set-matrix.outputs.shard_matrix }}
shard-total: ${{ steps.set-matrix.outputs.shard_total }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 2
persist-credentials: false
- name: Calculate Matrix
id: set-matrix
run: |
export DRY_RUN_SHARDS=true
export BUILD_TYPE=presubmit
export TEST_TYPE=units
export GIT_DIFF_ARG=HEAD^1
bash ci/run_conditional_tests.sh
units:
needs: setup
name: units (Node ${{ matrix.node-version }}, Shard ${{ matrix.shard-index }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [22, 24, 26]
shard-index: ${{ fromJSON(needs.setup.outputs.shard-matrix) }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 300
fetch-depth: 2
persist-credentials: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
Expand All @@ -28,13 +50,16 @@
env:
BUILD_TYPE: presubmit
TEST_TYPE: units
SHARD_TOTAL: ${{ needs.setup.outputs.shard-total }}
SHARD_INDEX: ${{ matrix.shard-index }}
GIT_DIFF_ARG: HEAD^1
lint:
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 300
fetch-depth: 2
persist-credentials: false
- name: Use Node.js 24
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
Expand All @@ -43,3 +68,37 @@
- run: npm install
- run: npm run lint
name: Run monorepo linter

# Dummy jobs to satisfy branch protection requirements for each node version
units-status:
name: units (${{ matrix.node-version }})
needs: units
runs-on: ubuntu-latest
if: always()
strategy:
fail-fast: false
matrix:
node-version: [22, 24, 26]
steps:
- name: Check shard status
uses: actions/github-script@v6

Check failure on line 84 in .github/workflows/presubmit.yaml

View workflow job for this annotation

GitHub Actions / zizmor-output

zizmor/unpinned-uses

unpinned action reference: action is not pinned to a hash (required by blanket policy)

Check failure on line 84 in .github/workflows/presubmit.yaml

View workflow job for this annotation

GitHub Actions / zizmor-output

unpinned-uses

presubmit.yaml:84: unpinned action reference: action is not pinned to a hash (required by blanket policy)
with:
script: |
const { data: { jobs } } = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId,
});
const nodeVersion = '${{ matrix.node-version }}';
const shardJobs = jobs.filter(j => j.name.includes(`units (Node ${nodeVersion},`));
if (shardJobs.length === 0) {
core.setFailed(`No shard jobs found for Node ${nodeVersion}`);
return;
}
const failed = shardJobs.filter(j => j.conclusion === 'failure' || j.conclusion === 'cancelled');
if (failed.length > 0) {
const failedLinks = failed.map(j => `- ${j.name}: ${j.html_url}`).join('\n');
core.setFailed(`${failed.length} shards failed for Node ${nodeVersion}:\n${failedLinks}`);
} else {
core.info(`All shards passed for Node ${nodeVersion}`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
pull_request:
paths:
- 'handwritten/storage/**'
name: conformance
name: storage-conformance
jobs:
conformance-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

Check warning on line 18 in .github/workflows/storage-conformance.yaml

View workflow job for this annotation

GitHub Actions / zizmor-output

ref-version-mismatch

storage-conformance.yaml:18: action's hash pin has mismatched or missing version comment: points to commit 11d5960a3267
with:
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6

Check warning on line 21 in .github/workflows/storage-conformance.yaml

View workflow job for this annotation

GitHub Actions / zizmor-output

ref-version-mismatch

storage-conformance.yaml:21: action's hash pin has mismatched or missing version comment: points to commit 249970729cb0
with:
node-version: 14
- run: node --version
Expand Down
61 changes: 60 additions & 1 deletion .github/workflows/windows-presubmit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,37 @@
pull_request:
name: presubmit-windows
jobs:
setup:
runs-on: ubuntu-latest
outputs:
shard-matrix: ${{ steps.set-matrix.outputs.shard_matrix }}
shard-total: ${{ steps.set-matrix.outputs.shard_total }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 2
persist-credentials: false
- name: Calculate Matrix
id: set-matrix
run: |
export DRY_RUN_SHARDS=true
export BUILD_TYPE=presubmit
export TEST_TYPE=units
export GIT_DIFF_ARG=HEAD^1
bash ci/run_conditional_tests.sh
units:
needs: setup
name: units (Node ${{ matrix.node-version }}, Shard ${{ matrix.shard-index }})
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
node-version: [22, 24, 26]
shard-index: ${{ fromJSON(needs.setup.outputs.shard-matrix) }}
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 300
fetch-depth: 2
persist-credentials: false
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
Expand All @@ -29,3 +51,40 @@
env:
BUILD_TYPE: presubmit
TEST_TYPE: units
SHARD_TOTAL: ${{ needs.setup.outputs.shard-total }}
SHARD_INDEX: ${{ matrix.shard-index }}
GIT_DIFF_ARG: HEAD^1

# Dummy jobs to satisfy branch protection requirements for each node version
units-status:
name: units (${{ matrix.node-version }})
needs: units
runs-on: ubuntu-latest
if: always()
strategy:
fail-fast: false
matrix:
node-version: [22, 24, 26]
steps:
- name: Check shard status
uses: actions/github-script@v6

Check failure on line 70 in .github/workflows/windows-presubmit.yaml

View workflow job for this annotation

GitHub Actions / zizmor-output

zizmor/unpinned-uses

unpinned action reference: action is not pinned to a hash (required by blanket policy)

Check failure on line 70 in .github/workflows/windows-presubmit.yaml

View workflow job for this annotation

GitHub Actions / zizmor-output

unpinned-uses

windows-presubmit.yaml:70: unpinned action reference: action is not pinned to a hash (required by blanket policy)
with:
script: |
const { data: { jobs } } = await github.rest.actions.listJobsForWorkflowRun({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId,
});
const nodeVersion = '${{ matrix.node-version }}';
const shardJobs = jobs.filter(j => j.name.includes(`units (Node ${nodeVersion},`));
if (shardJobs.length === 0) {
core.setFailed(`No shard jobs found for Node ${nodeVersion}`);
return;
}
const failed = shardJobs.filter(j => j.conclusion === 'failure' || j.conclusion === 'cancelled');
if (failed.length > 0) {
const failedLinks = failed.map(j => `- ${j.name}: ${j.html_url}`).join('\n');
core.setFailed(`${failed.length} shards failed for Node ${nodeVersion}:\n${failedLinks}`);
} else {
core.info(`All shards passed for Node ${nodeVersion}`);
}
110 changes: 69 additions & 41 deletions ci/run_conditional_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,27 @@ fi
test_script="${PROJECT_ROOT}/ci/run_single_test.sh"


if [ ${BUILD_TYPE} == "presubmit" ]; then
# For presubmit build, we want to know the difference from the
# common commit in origin/main.
GIT_DIFF_ARG="origin/main..."

# Then fetch enough history for finding the common commit.
git fetch origin main --deepen=300

elif [ ${BUILD_TYPE} == "continuous" ]; then
# For continuous build, we want to know the difference in the last
# commit. This assumes we use squash commit when merging PRs.
GIT_DIFF_ARG="HEAD~.."

# Then fetch one last commit for getting the diff.
git fetch origin main --deepen=1

else
# Run everything.
GIT_DIFF_ARG=""
if [ -z "${GIT_DIFF_ARG}" ]; then
if [ ${BUILD_TYPE} == "presubmit" ]; then
# For presubmit build, we want to know the difference from the
# common commit in origin/main.
GIT_DIFF_ARG="origin/main..."

# Then fetch enough history for finding the common commit.
git fetch origin main --deepen=300

elif [ ${BUILD_TYPE} == "continuous" ]; then
# For continuous build, we want to know the difference in the last
# commit. This assumes we use squash commit when merging PRs.
GIT_DIFF_ARG="HEAD~.."

# Then fetch one last commit for getting the diff.
git fetch origin main --deepen=1

else
# Run everything.
GIT_DIFF_ARG=""
fi
fi

# Then detect changes in the test scripts.
Expand All @@ -62,10 +64,10 @@ if [[ "${changed}" -eq 0 ]]; then
echo "no change detected in ci"
else
echo "skipping trigger of tests for now: tracking in #7540"
# echo "change detected in ci, we should test everything"
# echo "result of git diff ${GIT_DIFF_ARG} ci:"
# git diff ${GIT_DIFF_ARG} ci
# GIT_DIFF_ARG=""
#echo "change detected in ci, we should test everything"
#echo "result of git diff ${GIT_DIFF_ARG} ci:"
#git diff ${GIT_DIFF_ARG} ci
#GIT_DIFF_ARG=""
fi

# Now we have a fixed list, but we can change it to autodetect if
Expand All @@ -82,18 +84,21 @@ subdirs=(
)

RETVAL=0
# These following APIs need an explicit credential file to run properly (or oAuth2, which we don't support in this repo).
# These following APIs need an explicit credential file to run properly (or oAuth2, which we don't support in this repo).
# When we hit these packages, we will run the "samples with credentials" trigger, which contains the credentials as an env variable

tests_with_credentials="core/packages/google-auth-library-nodejs/ packages/google-analytics-admin/ packages/google-area120-tables/ packages/google-analytics-data/ packages/google-iam-credentials/ packages/google-apps-meet/ packages/google-chat/ packages/google-streetview-publish/ packages/google-cloud-developerconnect/"

# Some packages are only used by our bots and automation. These packages do not need to run on Windows and
# often employ platform specific code like file system interaction. Some packages may also fail
# on Windows due to incompatible npm scripts.
#
#
# Until these packages can be updated to be OS agnostic, we will skip them on Windows.
windows_exempt_tests="core/ core/packages/ core/dev-packages/ .github/scripts/fixtures/ .github/scripts/tests/ core/packages/gapic-node-processing/ core/packages/typeless-sample-bot/ handwritten/cloud-profiler/"

# Gather all test directories into an array
test_dirs=()

for subdir in ${subdirs[@]}; do
for d in `ls -d ${subdir}/*/`; do
if [ -s "ignore.json" ] && jq -e ".ignored[] | select(. == \"$d\")" ignore.json > /dev/null 2>&1; then
Expand All @@ -116,7 +121,7 @@ for subdir in ${subdirs[@]}; do
# System tests for packages are broken and blocking PRs.
# See https://github.com/googleapis/google-cloud-node/issues/7976.
#
# Per https://github.com/googleapis/google-cloud-node/issues/7921,
# Per https://github.com/googleapis/google-cloud-node/issues/7921,
# we are likely to permanently remove these tests in the near future.
if [[ "${subdir}" == "packages" && "${TEST_TYPE}" == "system" ]]; then
echo "Skipping ${TEST_TYPE} test for packages: ${d}"
Expand All @@ -126,7 +131,7 @@ for subdir in ${subdirs[@]}; do
# Sample tests for packages are broken/flaky and blocking PRs.
# See https://github.com/googleapis/google-cloud-node/issues/7976#issuecomment-4210458096.
#
# Per https://github.com/googleapis/google-cloud-node/issues/7921,
# Per https://github.com/googleapis/google-cloud-node/issues/7921,
# we are likely to permanently remove these tests in the near future.
if [[ "${subdir}" == "packages" && "${TEST_TYPE}" == "samples" ]]; then
echo "Skipping ${TEST_TYPE} test for packages: ${d}"
Expand Down Expand Up @@ -204,21 +209,44 @@ for subdir in ${subdirs[@]}; do
fi
fi
if [ "${should_test}" = true ]; then
echo "running test in ${d}"
pushd ${d}
# Temporarily allow failure.
set +e
${test_script}
ret=$?
set -e
if [ ${ret} -ne 0 ]; then
RETVAL=${ret}
# Since there are so many APIs, we should exit early if there's an error
exit ${RETVAL}
fi
popd
test_dirs+=("${d}")
fi
done
done
# If DRY_RUN_SHARDS is set, output dynamic matrix values to GitHub Actions and exit
if [[ "${DRY_RUN_SHARDS}" == "true" ]]; then
count=${#test_dirs[@]}
if [[ $count -gt 15 ]]; then
echo "shard_matrix=[0, 1, 2, 3, 4]" >> $GITHUB_OUTPUT
echo "shard_total=5" >> $GITHUB_OUTPUT
else
echo "shard_matrix=[0]" >> $GITHUB_OUTPUT
echo "shard_total=1" >> $GITHUB_OUTPUT
fi
exit 0
fi
Comment on lines +217 to +227

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If $GITHUB_OUTPUT is unset or empty (for example, when running the script locally or in a non-GitHub Actions environment), redirecting to it via >> $GITHUB_OUTPUT will fail with an ambiguous redirect error. Checking if the variable is set before redirecting prevents this failure.

Suggested change
if [[ "${DRY_RUN_SHARDS}" == "true" ]]; then
count=${#test_dirs[@]}
if [[ $count -gt 15 ]]; then
echo "shard_matrix=[0, 1, 2, 3, 4]" >> $GITHUB_OUTPUT
echo "shard_total=5" >> $GITHUB_OUTPUT
else
echo "shard_matrix=[0]" >> $GITHUB_OUTPUT
echo "shard_total=1" >> $GITHUB_OUTPUT
fi
exit 0
fi
if [[ "${DRY_RUN_SHARDS}" == "true" ]]; then
count=${#test_dirs[@]}
if [[ $count -gt 15 ]]; then
matrix="[0, 1, 2, 3, 4]"
total="5"
else
matrix="[0]"
total="1"
fi
if [[ -n "${GITHUB_OUTPUT}" ]]; then
echo "shard_matrix=${matrix}" >> "${GITHUB_OUTPUT}"
echo "shard_total=${total}" >> "${GITHUB_OUTPUT}"
else
echo "shard_matrix=${matrix}"
echo "shard_total=${total}"
fi
exit 0
fi


# If SHARD_TOTAL and SHARD_INDEX are provided, we will only run a subset of the tests.
for i in "${!test_dirs[@]}"; do
d="${test_dirs[$i]}"

if [[ -n "${SHARD_TOTAL}" && -n "${SHARD_INDEX}" ]]; then
if (( i % SHARD_TOTAL != SHARD_INDEX )); then
continue
fi
fi
Comment on lines +233 to +237

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If SHARD_TOTAL is set to 0 or a non-numeric value, the modulo operation i % SHARD_TOTAL will cause a division-by-zero or arithmetic evaluation error in bash. Adding a guard SHARD_TOTAL > 0 inside the arithmetic evaluation safely avoids this.

Suggested change
if [[ -n "${SHARD_TOTAL}" && -n "${SHARD_INDEX}" ]]; then
if (( i % SHARD_TOTAL != SHARD_INDEX )); then
continue
fi
fi
if [[ -n "${SHARD_TOTAL}" && -n "${SHARD_INDEX}" ]]; then
if (( SHARD_TOTAL > 0 && i % SHARD_TOTAL != SHARD_INDEX )); then
continue
fi
fi


echo "running test in ${d}"
pushd ${d} >/dev/null
# Temporarily allow failure.
set +e
${test_script}
ret=$?
set -e
if [ ${ret} -ne 0 ]; then
exit ${ret}
fi
popd >/dev/null
done

exit ${RETVAL}
exit 0
6 changes: 4 additions & 2 deletions ci/run_single_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ if [ ${BUILD_TYPE} != "presubmit" ]; then
export MOCHA_REPORTER_OUTPUT=${PROJECT}_sponge_log.xml
export MOCHA_REPORTER_SUITENAME=${PROJECT}
export MOCHA_REPORTER=xunit
else
export MOCHA_REPORTER=dot
fi

# Install dependencies
Expand All @@ -47,8 +49,8 @@ if command -v cygpath >/dev/null 2>&1; then
PNPMFILE_PATH=$(cygpath -m "${PNPMFILE_PATH}")
fi

echo "pnpm install --ignore-scripts --engine-strict --prod --pnpmfile \"${PNPMFILE_PATH}\"; pnpm install --pnpmfile \"${PNPMFILE_PATH}\""
pnpm install --ignore-scripts --engine-strict --prod --pnpmfile "${PNPMFILE_PATH}"; pnpm install --pnpmfile "${PNPMFILE_PATH}"
echo "pnpm install --reporter=silent --engine-strict --pnpmfile \"${PNPMFILE_PATH}\""
pnpm install --reporter=silent --engine-strict --pnpmfile "${PNPMFILE_PATH}"


retval=0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {packNTest} from 'pack-n-play';
import * as assert from 'assert';
import {describe, it} from 'mocha';

describe('ESM package', () => {
describe('ESM package', function () {
this.timeout(120000);
it('should support esm property', () =>
packNTest({
sample: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {packNTest} from 'pack-n-play';
import * as assert from 'assert';
import {describe, it} from 'mocha';

describe('leaky tests', () => {
describe('leaky tests', function () {
this.timeout(120000);
it('should fail packing n testing', async () => {
await assert.rejects(
packNTest({
Expand Down
Loading
Loading