Skip to content

Commit 90370ad

Browse files
committed
Merge branch 'ci-improvements-20260702' into chore-update-librarian-2026-06-26
2 parents 142dd59 + 4e39eeb commit 90370ad

44 files changed

Lines changed: 1800 additions & 355 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/gapic-generator-tests.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,13 @@ jobs:
135135
- name: Install System Deps
136136
run: sudo apt-get update && sudo apt-get install -y pandoc
137137
- name: Run Goldens
138+
env:
139+
LATEST_STABLE_PYTHON: ${{ needs.python_config.outputs.latest_stable_python }}
138140
run: |
139141
pip install nox
140142
cd packages/gapic-generator
141143
for pkg in credentials eventarc logging redis; do
142-
nox -f tests/integration/goldens/$pkg/noxfile.py -s format lint unit-${{ needs.python_config.outputs.latest_stable_python }}
144+
nox -f tests/integration/goldens/$pkg/noxfile.py -s format lint unit-${LATEST_STABLE_PYTHON}
143145
done
144146
# Run pylint (errors-only) over the goldens so generator regressions
145147
# like undefined names or import-time breakage that ruff/flake8 do
@@ -214,11 +216,12 @@ jobs:
214216
# Run fragment for current matrix python
215217
nox -s fragment-${MATRIX_PYTHON}
216218
# Run snippetgen only on the latest stable to avoid the "Python not found" error
217-
if [ "${MATRIX_PYTHON}" == "${{ needs.python_config.outputs.latest_stable_python }}" ]; then
219+
if [ "${MATRIX_PYTHON}" == "${LATEST_STABLE_PYTHON}" ]; then
218220
nox -s snippetgen
219221
fi
220222
env:
221223
MATRIX_PYTHON: ${{ matrix.python }}
224+
LATEST_STABLE_PYTHON: ${{ needs.python_config.outputs.latest_stable_python }}
222225
integration:
223226
needs: python_config
224227
# Only runs if the Gatekeeper passed

.github/workflows/unittest.yml

Lines changed: 116 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,48 @@ name: unittest
1212
permissions:
1313
contents: read
1414

15+
# Configurable global environment variables for batching
16+
env:
17+
TEST_ALL_PACKAGES: "false" # Set to "false" to only run tests for packages with a git diff
18+
ALL_PYTHON: "['3.9', '3.10', '3.11', '3.12', '3.13', '3.14', '3.15']"
1519

1620
jobs:
21+
# Dynamic package discovery job to calculate required matrix size automatically
22+
python_config:
23+
runs-on: ubuntu-latest
24+
outputs:
25+
all_python: ${{ steps.export.outputs.python_list }}
26+
steps:
27+
- id: export
28+
run: echo "python_list=${{ env.ALL_PYTHON }}" >> "$GITHUB_OUTPUT"
29+
30+
discover-packages:
31+
runs-on: ubuntu-latest
32+
outputs:
33+
batch-indices: ${{ steps.set-matrix.outputs.indices }}
34+
steps:
35+
- name: Checkout
36+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
37+
with:
38+
persist-credentials: false
39+
- name: Setup Python
40+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
41+
with:
42+
python-version: "3.14"
43+
- name: Generate Batch Indices
44+
id: set-matrix
45+
run: |
46+
INDICES=$(python -c 'import sys; sys.path.append("ci"); import get_batches; print(get_batches.get_batch_indices())')
47+
echo "indices=${INDICES}" >> "$GITHUB_OUTPUT"
48+
1749
unit:
50+
name: "unit-run (${{ matrix.python }}, Batch ${{ matrix.batch-index }})"
1851
runs-on: ubuntu-22.04
52+
needs: [python_config, discover-packages]
1953
strategy:
20-
fail-fast: true
2154
matrix:
22-
python: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15"]
55+
python: ${{ fromJSON(needs.python_config.outputs.all_python) }}
56+
batch-index: ${{ fromJson(needs.discover-packages.outputs.batch-indices) }}
2357
steps:
2458
- name: Checkout
2559
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
@@ -54,9 +88,11 @@ jobs:
5488
restore-keys: |
5589
${{ runner.os }}-uv-wheels-3.15-
5690
- name: Run unit tests
91+
id: run-tests
5792
env:
58-
COVERAGE_FILE: ${{ github.workspace }}/.coverage-${{ matrix.python }}
59-
BUILD_TYPE: presubmit
93+
COVERAGE_FILE: ${{ github.workspace }}/.coverage-${{ matrix.python }}-${{ matrix.batch-index }}
94+
# Dynamically set BUILD_TYPE to an empty string to skip the diff calculation if TEST_ALL_PACKAGES is true
95+
BUILD_TYPE: ${{ env.TEST_ALL_PACKAGES == 'true' && '' || 'presubmit' }}
6096
TARGET_BRANCH: ${{ github.base_ref || github.event.merge_group.base_ref }}
6197
TEST_TYPE: unit
6298
PY_VERSION: ${{ matrix.python }}
@@ -65,6 +101,15 @@ jobs:
65101
# Follow https://github.com/Instagram/LibCST/issues/1445 for updates.
66102
PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1"
67103
run: |
104+
UNIQUE_BATCH_PACKAGES=$(python -c 'import sys; sys.path.append("ci"); import get_batches; print(get_batches.get_batch_slice(${{ matrix.batch-index }}))')
105+
106+
if [ -z "$UNIQUE_BATCH_PACKAGES" ]; then
107+
echo "No structural units allocated to this matrix slice."
108+
exit 0
109+
fi
110+
111+
echo "Running tests for packages in this weighted batch: ${UNIQUE_BATCH_PACKAGES}"
112+
68113
# Only inject overrides if we are strictly on the 3.15 pre-release
69114
# This is needed to speed up builds
70115
if [ "${{ matrix.python }}" = "3.15" ]; then
@@ -74,18 +119,31 @@ jobs:
74119
# Route uv's wheel and environment cache to our persistent workspace directory
75120
export UV_CACHE_DIR="${{ github.workspace }}/.uv-wheel-cache"
76121
fi
77-
ci/run_conditional_tests.sh
122+
123+
ci/run_conditional_tests.sh ${UNIQUE_BATCH_PACKAGES}
124+
125+
- name: Save Status Footprint
126+
run: |
127+
mkdir -p footprints
128+
echo "${{ steps.run-tests.outcome }}" > footprints/status.txt
129+
130+
- name: Upload Status Footprint
131+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
132+
with:
133+
name: footprint-${{ matrix.python }}-${{ matrix.batch-index }}
134+
path: footprints/
135+
78136
- name: Upload coverage results
79137
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
80138
with:
81-
name: coverage-artifact-${{ matrix.python }}
82-
path: .coverage-${{ matrix.python }}
139+
# Appended batch-index to separate parallel coverage uploads cleanly
140+
name: coverage-artifact-${{ matrix.python }}-${{ matrix.batch-index }}
141+
path: .coverage-${{ matrix.python }}-${{ matrix.batch-index }}
83142
include-hidden-files: true
84143

85144
cover:
86145
runs-on: ubuntu-latest
87-
needs:
88-
- unit
146+
needs: [unit]
89147
steps:
90148
- name: Checkout
91149
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
@@ -197,3 +255,52 @@ jobs:
197255
echo "This usually means the unit tests did not run or failed to upload their coverage files."
198256
exit 1
199257
fi
258+
259+
unittest-runtime-result:
260+
name: "unit (${{ matrix.python }})"
261+
needs: [python_config, discover-packages, unit]
262+
if: always()
263+
strategy:
264+
fail-fast: false
265+
matrix:
266+
python: ${{ fromJSON(needs.python_config.outputs.all_python) }}
267+
runs-on: ubuntu-latest
268+
steps:
269+
- name: Download all status footprints for this runtime
270+
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
271+
with:
272+
pattern: footprint-${{ matrix.python }}-*
273+
path: footprint-results/
274+
275+
- name: Validate complete batch execution footprint status
276+
run: |
277+
EXPECTED_BATCHES=$(echo '${{ needs.discover-packages.outputs.batch-indices }}' | jq '. | length')
278+
279+
if [ -d "footprint-results" ]; then
280+
ACTUAL_BATCHES=$(find footprint-results -type f -name 'status.txt' | wc -l | tr -d ' ')
281+
else
282+
ACTUAL_BATCHES=0
283+
fi
284+
285+
echo "Validation metrics for Python ${{ matrix.python }}:"
286+
echo " -> Expected footprint files: $EXPECTED_BATCHES"
287+
echo " -> Downloaded footprint files: $ACTUAL_BATCHES"
288+
289+
if [ "$ACTUAL_BATCHES" -ne "$EXPECTED_BATCHES" ]; then
290+
echo "Error: Footprint count mismatch! Expected $EXPECTED_BATCHES files, but found $ACTUAL_BATCHES."
291+
exit 1
292+
fi
293+
294+
FAILED_RUNS=0
295+
while read -r STATUS_FILE; do
296+
RUN_STATUS=$(cat "$STATUS_FILE" | tr -d '[:space:]')
297+
if [[ "$RUN_STATUS" != "success" ]]; then
298+
echo "Failure detected in batch profile path: $STATUS_FILE (Status: $RUN_STATUS)"
299+
FAILED_RUNS=$((FAILED_RUNS + 1))
300+
fi
301+
done < <(find footprint-results -type f -name 'status.txt' 2>/dev/null)
302+
303+
if [ "$FAILED_RUNS" -gt 0 ]; then
304+
echo "Error: Validation failed. Found $FAILED_RUNS failing test batches."
305+
exit 1
306+
fi

ci/get_batches.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#!/usr/bin/env python3
15+
import os
16+
import glob
17+
import math
18+
import json
19+
import sys
20+
21+
BATCH_SIZE = 10
22+
23+
# Packages that take significantly longer to run tests.
24+
# These will always be assigned to their own dedicated runner.
25+
ISOLATED_PACKAGES = [
26+
"google-cloud-compute",
27+
"google-cloud-compute-v1beta",
28+
"google-cloud-dialogflow",
29+
"google-cloud-dialogflow-cx",
30+
"google-cloud-retail",
31+
]
32+
33+
def get_batches():
34+
"""Splits packages into dedicated isolated batches and evenly chunked standard batches."""
35+
raw_paths = sorted(glob.glob("packages/*"))
36+
package_paths = [p for p in raw_paths if os.path.isdir(p)]
37+
38+
batches = []
39+
standard_packages = []
40+
41+
for path in package_paths:
42+
pkg_name = os.path.basename(path)
43+
44+
if pkg_name in ISOLATED_PACKAGES:
45+
# Put heavy packages into their own standalone batches immediately
46+
batches.append([path])
47+
else:
48+
standard_packages.append(path)
49+
50+
# Chunk the remaining standard packages by BATCH_SIZE
51+
num_standard_packages = len(standard_packages)
52+
num_standard_batches = math.ceil(num_standard_packages / BATCH_SIZE)
53+
54+
if num_standard_batches == 0 and not batches:
55+
num_standard_batches = 1
56+
57+
for i in range(num_standard_batches):
58+
start_idx = i * BATCH_SIZE
59+
chunk = standard_packages[start_idx : start_idx + BATCH_SIZE]
60+
if chunk:
61+
batches.append(chunk)
62+
63+
return batches
64+
65+
def get_batch_indices():
66+
"""Returns a JSON string of the array of batch indices for GitHub Actions matrix."""
67+
batches = get_batches()
68+
return json.dumps(list(range(len(batches))))
69+
70+
def get_batch_slice(batch_index):
71+
"""Returns a space-separated string of unique package paths for a specific batch index."""
72+
batches = get_batches()
73+
if batch_index < 0 or batch_index >= len(batches):
74+
return ""
75+
return " ".join(batches[batch_index])
76+
77+
if __name__ == "__main__":
78+
if len(sys.argv) > 1 and sys.argv[1] == "--count":
79+
print(get_batch_indices())
80+
elif len(sys.argv) > 2 and sys.argv[1] == "--slice":
81+
print(get_batch_slice(int(sys.argv[2])))

ci/run_conditional_tests.sh

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121

2222
# `TEST_TYPE` and `PY_VERSION` are required by the script `ci/run_single_test.sh`
2323

24+
# Optional Arguments:
25+
# Pass specific space-separated package paths (e.g., "packages/google-cloud-storage") to only test those directories.
26+
# If no arguments are provided, the script automatically determines which directories have changed
27+
#
2428
# This script will determine which directories have changed
2529
# under the `packages` folder. For `BUILD_TYPE=="presubmit"`,
2630
# we'll compare against the `packages` folder in HEAD,
@@ -78,16 +82,43 @@ set -e
7882
# Now we have a fixed list, but we can change it to autodetect if
7983
# necessary.
8084

81-
subdirs=(
82-
packages
83-
)
85+
if [ $# -gt 0 ]; then
86+
subdirs=("$@")
87+
else
88+
subdirs=(
89+
packages
90+
)
91+
fi
8492

8593
RETVAL=0
8694

87-
for subdir in ${subdirs[@]}; do
88-
for d in `ls -d ${subdir}/*/`; do
95+
for subdir in "${subdirs[@]}"; do
96+
if [ ! -d "${subdir}" ]; then
97+
echo "Error: Directory '${subdir}' does not exist." >&2
98+
exit 1
99+
fi
100+
101+
if [[ "${subdir%/}" == "packages" ]]; then
102+
loop_dirs=("${subdir}"/*/)
103+
else
104+
loop_dirs=("${subdir}")
105+
fi
106+
107+
for d in "${loop_dirs[@]}"; do
108+
if [ ! -d "$d" ]; then
109+
continue
110+
fi
111+
# Ensure the directory path always ends with a trailing slash for git diff safety
112+
if [[ "$d" != */ ]]; then
113+
d="$d/"
114+
fi
89115
should_test=false
90-
if [ -n "${GIT_DIFF_ARG}" ]; then
116+
117+
# Override check: Force test if explicitly asked to test all packages
118+
if [[ "${TEST_ALL_PACKAGES}" == "true" ]]; then
119+
echo "TEST_ALL_PACKAGES is true, forcing execution for ${d}"
120+
should_test=true
121+
elif [ -n "${GIT_DIFF_ARG}" ]; then
91122
echo "checking changes with 'git diff --quiet ${GIT_DIFF_ARG} ${d}'"
92123
set +e
93124
git diff --quiet ${GIT_DIFF_ARG} ${d}
@@ -119,4 +150,4 @@ for subdir in ${subdirs[@]}; do
119150
done
120151
done
121152

122-
exit ${RETVAL}
153+
exit ${RETVAL}

packages/bigframes/bigframes/ml/llm.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
_GEMINI_2P5_PRO_ENDPOINT = "gemini-2.5-pro"
6060
_GEMINI_2P5_FLASH_ENDPOINT = "gemini-2.5-flash"
6161
_GEMINI_2P5_FLASH_LITE_ENDPOINT = "gemini-2.5-flash-lite"
62+
_GEMINI_3P1_FLASH_LITE_ENDPOINT = "gemini-3.1-flash-lite"
63+
_GEMINI_3P5_FLASH_ENDPOINT = "gemini-3.5-flash"
6264

6365
_GEMINI_ENDPOINTS = (
6466
_GEMINI_1P5_PRO_PREVIEW_ENDPOINT,
@@ -73,6 +75,8 @@
7375
_GEMINI_2P5_PRO_ENDPOINT,
7476
_GEMINI_2P5_FLASH_ENDPOINT,
7577
_GEMINI_2P5_FLASH_LITE_ENDPOINT,
78+
_GEMINI_3P1_FLASH_LITE_ENDPOINT,
79+
_GEMINI_3P5_FLASH_ENDPOINT,
7680
)
7781
_GEMINI_PREVIEW_ENDPOINTS = (
7882
_GEMINI_1P5_PRO_PREVIEW_ENDPOINT,
@@ -96,6 +100,8 @@
96100
_GEMINI_2P5_PRO_ENDPOINT,
97101
_GEMINI_2P5_FLASH_ENDPOINT,
98102
_GEMINI_2P5_FLASH_LITE_ENDPOINT,
103+
_GEMINI_3P1_FLASH_LITE_ENDPOINT,
104+
_GEMINI_3P5_FLASH_ENDPOINT,
99105
)
100106

101107
_CLAUDE_3_SONNET_ENDPOINT = "claude-3-sonnet"
@@ -440,7 +446,8 @@ class GeminiTextGenerator(base.RetriableRemotePredictor):
440446
"gemini-1.5-pro-001", "gemini-1.5-pro-002", "gemini-1.5-flash-001",
441447
"gemini-1.5-flash-002", "gemini-2.0-flash-exp",
442448
"gemini-2.0-flash-lite-001", "gemini-2.0-flash-001",
443-
"gemini-2.5-pro", "gemini-2.5-flash" and "gemini-2.5-flash-lite".
449+
"gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite",
450+
"gemini-3.1-flash-lite" and "gemini-3.5-flash".
444451
If no setting is provided, "gemini-2.0-flash-001" will be used by
445452
default and a warning will be issued.
446453
@@ -478,6 +485,8 @@ def __init__(
478485
"gemini-2.5-pro",
479486
"gemini-2.5-flash",
480487
"gemini-2.5-flash-lite",
488+
"gemini-3.1-flash-lite",
489+
"gemini-3.5-flash",
481490
]
482491
] = None,
483492
session: Optional[bigframes.Session] = None,

0 commit comments

Comments
 (0)