Skip to content

[DRAFT] experiment: add test sharding #15621

[DRAFT] experiment: add test sharding

[DRAFT] experiment: add test sharding #15621

Workflow file for this run

on:
pull_request:
branches:
- main
- preview
# Trigger workflow on GitHub merge queue events
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#merge_group
merge_group:
types: [checks_requested]
name: unittest
permissions:
contents: read
jobs:
initialize:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Get package shards
id: set-matrix
env:
BUILD_TYPE: presubmit
TARGET_BRANCH: ${{ github.base_ref || github.event.merge_group.base_ref }}
run: |
if [ -n "$TARGET_BRANCH" ]; then
git fetch origin "$TARGET_BRANCH" --deepen=200 || true
fi
echo "matrix=$(python3 ci/get_package_shards.py)" >> $GITHUB_OUTPUT
unit:
needs: initialize
if: needs.initialize.outputs.matrix != '[]' && needs.initialize.outputs.matrix != ''
runs-on: ubuntu-22.04
strategy:
fail-fast: true
matrix:
python: ['3.9', '3.10', "3.11", "3.12", "3.13", "3.14"]
package_shard: ${{ fromJson(needs.initialize.outputs.matrix) }}
name: ${{ matrix.package_shard.is_sharded && format('unit ({0}, {1})', matrix.python, matrix.package_shard.name) || format('unit ({0})', matrix.python) }}
steps:
- name: Checkout
uses: actions/checkout@v4
# Use a fetch-depth of 2 to avoid error `fatal: origin/main...HEAD: no merge base`
# See https://github.com/googleapis/google-cloud-python/issues/12013
# and https://github.com/actions/checkout#checkout-head.
with:
fetch-depth: 2
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install nox
run: |
python -m pip install --upgrade setuptools pip wheel
python -m pip install nox
- name: Run unit tests for ${{ matrix.package_shard.description }}
env:
BUILD_TYPE: presubmit
TARGET_BRANCH: ${{ github.base_ref || github.event.merge_group.base_ref }}
TEST_TYPE: unit
PY_VERSION: ${{ matrix.python }}
PACKAGE_LIST: ${{ matrix.package_shard.packages }}
run: |
ci/run_conditional_tests.sh
- name: Upload coverage results
uses: actions/upload-artifact@v4
with:
name: coverage-artifact-${{ matrix.python }}-${{ matrix.package_shard.index }}
path: .coverage.${{ matrix.python }}.*
include-hidden-files: true
all-tests:
needs: [initialize, unit]
if: always()
runs-on: ubuntu-latest
steps:
- name: Check unit test results
run: |
# 1. Check initialize job
if [[ "${{ needs.initialize.result }}" != "success" ]]; then
echo "Error: The initialize job status was: ${{ needs.initialize.result }}"
exit 1
fi
# 2. Check unit test shards
if [[ "${{ needs.unit.result }}" != "success" && "${{ needs.unit.result }}" != "skipped" ]]; then
echo "Unit tests failed"
exit 1
fi
echo "All unit tests passed or were skipped"
cover:
runs-on: ubuntu-latest
needs:
- unit
steps:
- name: Checkout
uses: actions/checkout@v4
# Use a fetch-depth of 2 to avoid error `fatal: origin/main...HEAD: no merge base`
# See https://github.com/googleapis/google-cloud-python/issues/12013
# and https://github.com/actions/checkout#checkout-head.
with:
fetch-depth: 2
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Set number of files changes in packages directory
id: packages
run: echo "num_files_changed=$(git diff HEAD~1 -- packages | wc -l)" >> $GITHUB_OUTPUT
- name: Install coverage
if: steps.packages.outputs.num_files_changed > 0
run: |
python -m pip install --upgrade setuptools pip wheel
python -m pip install coverage
- name: Download coverage results
if: steps.packages.outputs.num_files_changed > 0
uses: actions/download-artifact@v4
with:
path: .coverage-results/
- name: Report coverage results
if: steps.packages.outputs.num_files_changed > 0
env:
# TODO: default to 100% coverage after next gapic-generator release
# https://github.com/googleapis/google-cloud-python/issues/17459
DEFAULT_FAIL_UNDER: 99
run: |
if [ -d .coverage-results ]; then
find .coverage-results -type f -name '.coverage*' -exec mv {} . \;
coverage combine
# Find all modified packages
modified_packages=$(git diff --name-only HEAD~1 -- packages | cut -d/ -f1,2 | sort -u)
failed_packages=()
passed_packages=()
for pkg in ${modified_packages}; do
if [ -d "${pkg}" ]; then
echo "============================================================"
echo "Evaluating coverage for package: ${pkg}"
echo "============================================================"
set +e
if [ -f "${pkg}/.coveragerc" ]; then
echo "Using package-specific configuration: ${pkg}/.coveragerc"
# If fail_under is specified in the package-specific .coveragerc, coverage report
# will automatically enforce it. Otherwise, we enforce the default.
if grep -q "fail_under" "${pkg}/.coveragerc"; then
coverage report --rcfile="${pkg}/.coveragerc" --include="${pkg}/**"
else
echo "No fail_under specified in ${pkg}/.coveragerc, enforcing default"
coverage report --rcfile="${pkg}/.coveragerc" --include="${pkg}/**" --fail-under="${DEFAULT_FAIL_UNDER}"
fi
else
echo "No .coveragerc found for ${pkg}, enforcing default"
coverage report --include="${pkg}/**" --fail-under="${DEFAULT_FAIL_UNDER}"
fi
status=$?
set -e
if [ ${status} -ne 0 ]; then
failed_packages+=("${pkg}")
else
passed_packages+=("${pkg}")
fi
fi
done
echo "============================================================"
echo "Coverage Evaluation Summary"
echo "============================================================"
if [ ${#passed_packages[@]} -gt 0 ]; then
echo "Passed packages:"
for pkg in "${passed_packages[@]}"; do
echo " - ${pkg}"
done
fi
if [ ${#failed_packages[@]} -gt 0 ]; then
echo "Failed packages:"
for pkg in "${failed_packages[@]}"; do
echo " - ${pkg}"
done
exit 1
fi
else
echo "Error: No coverage results were downloaded from the unit test jobs."
echo "This usually means the unit tests did not run or failed to upload their coverage files."
exit 1
fi