Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
186 changes: 186 additions & 0 deletions .github/workflows/prerelease-deps.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: prerelease-deps

on:
pull_request:
paths:
- '.github/workflows/prerelease-deps.yml'
- 'ci/**'
schedule:
# Run daily at midnight UTC
- cron: '0 0 * * *'
workflow_dispatch:

permissions:
contents: read
issues: write

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true

env:
PACKAGE_DIRS: packages
NOX_DEFAULT_VENV_BACKEND: "virtualenv"
UV_PRERELEASE: "allow"
# Workaround: Allows libcst/Rust extensions to compile on Python 3.15+ while PyO3 catches up
PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1"

jobs:
prerelease:
runs-on: ubuntu-22.04
strategy:
matrix:
python: ["3.15"]
name: Nightly Pre-release Dependency Issue Reporter (${{ matrix.python }})
env:
PY_VERSION: ${{ matrix.python }}
FAILURE_LOG_DIR: "/tmp/test_failures"
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
# 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
persist-credentials: false

- name: Setup Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: ${{ env.PY_VERSION }}
allow-prereleases: true

- name: Cache pip wheels (including compiled source builds)
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ~/.cache/pip
# Save a fresh cache snapshot for each run so newly compiled wheels are persisted
key: ${{ runner.os }}-pip-prerelease-${{ env.PY_VERSION }}-${{ github.run_id }}
# Restore the most recent cache from previous runs for this Python version
restore-keys: |
${{ runner.os }}-pip-prerelease-${{ env.PY_VERSION }}-

- name: Install nox
run: |
pip install nox

- name: Get handwritten packages to test
id: get-packages
env:
BUILD_TYPE: continuous
TEST_ALL_PACKAGES: "true"
HANDWRITTEN_ONLY: "true"
run: |
python3 ci/get_package_shards.py

- name: Run prerelease_deps across handwritten packages
id: run-tests
# Do not fail the job when pre-release tests fail; instead, report via GitHub issue below.
continue-on-error: true
env:
BUILD_TYPE: continuous
TEST_TYPE: prerelease
PACKAGE_LIST: ${{ steps.get-packages.outputs.packages }}
NOXFORCEPYTHON: ${{ env.PY_VERSION }}
CONTINUE_ON_ERROR: "true"
PARALLEL_WORKERS: "1"
run: |
ci/run_conditional_tests.sh

- name: Prepare issue body on failure
if: ${{ steps.run-tests.outcome == 'failure' }}
id: issue-body
run: |
shopt -s nullglob
log_files=("${FAILURE_LOG_DIR}"/*.log.txt)
if [ ${#log_files[@]} -gt 0 ]; then
COUNT=${#log_files[@]}
PKG_LIST=""
DETAILS=""
TRUNCATED_NOTE=""
for log_file in "${log_files[@]}"; do
pkg=$(basename "$log_file" .log.txt)
pip_file="${FAILURE_LOG_DIR}/${pkg}.pip.txt"
log_tail=$(cat "$log_file" 2>/dev/null || echo "Unavailable")
pip_list=$(cat "$pip_file" 2>/dev/null || echo "No dependencies installed (failed during build)")
PKG_LIST="${PKG_LIST}- \`${pkg}\`"$'\n'

entry=$(cat <<EOF
<details>
<summary><code>${pkg}</code></summary>

**Test Failure Output (last 50 lines):**
\`\`\`text
${log_tail}
\`\`\`

<details>
<summary>Installed Dependencies (<code>pip list</code>)</summary>

\`\`\`text
${pip_list}
\`\`\`
</details>
</details>
EOF
)
if [ $(( ${#DETAILS} + ${#entry} )) -le 48000 ]; then
DETAILS="${DETAILS}"$'\n'"${entry}"$'\n'
elif [ -z "${TRUNCATED_NOTE}" ]; then
TRUNCATED_NOTE=$'\n\n_Note: Detailed logs for remaining packages were omitted to fit GitHub issue length limits. Please check the workflow run logs for full details._'
fi
done
else
COUNT="unknown"
PKG_LIST="- (Workflow failed outside package test execution)"
DETAILS=""
TRUNCATED_NOTE=""
fi

echo "::warning title=Pre-release Dependency Failures::Detected pre-release test failures in ${COUNT} package(s). See job summary or GitHub issues for details."

{
echo "### ⚠️ Pre-release Dependency Failures (${COUNT} packages)"
echo ""
echo "The pre-release check detected failures on Python \`${PY_VERSION}\`:"
echo ""
printf "%s" "${PKG_LIST}"
} >> "$GITHUB_STEP_SUMMARY"

{
echo "body<<EOF_ISSUE_BODY"
echo "The [pre-release check](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}) failed for **${COUNT}** handwritten package(s) on Python \`${PY_VERSION}\`."
echo ""
echo "### Failed Packages"
printf "%s" "${PKG_LIST}"
echo ""
echo "- **Python Version:** \`${PY_VERSION}\`"
echo "- **Workflow Run:** [${GITHUB_RUN_ID}](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})"
echo ""
echo "### Failure Details"
echo "${DETAILS}${TRUNCATED_NOTE}"
echo "EOF_ISSUE_BODY"
} >> "$GITHUB_OUTPUT"

- name: Create issue on failure
if: ${{ steps.run-tests.outcome == 'failure' && github.repository == 'googleapis/google-cloud-python' }}
uses: googleapis/librarian/.github/actions/create-issue-on-failure@bb2ee61752bd0f5387195b54c509e4d01a52b41d
with:
title: "prerelease_deps check failed"
labels: "priority: p1,nightly"
body: ${{ steps.issue-body.outputs.body }}
4 changes: 4 additions & 0 deletions .github/workflows/regenerate-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ jobs:
elif [ -n "$PACKAGES" ]; then
for pkg_path in $PACKAGES; do
pkg=$(basename "$pkg_path")
if ! grep -q "^ - name: ${pkg}$" librarian.yaml; then
echo "Skipping $pkg (not in librarian.yaml)..."
continue
fi
Comment on lines +81 to +84

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.

It seems like this change doesn't belong in here?

echo "Regenerating $pkg..."
librarian generate "$pkg"
done
Expand Down
7 changes: 0 additions & 7 deletions .kokoro/continuous/prerelease.cfg

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.

Do we have a CL for removing this from the Kokoro side?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I will send it to you

This file was deleted.

7 changes: 3 additions & 4 deletions .kokoro/system.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ run_package_test() {
local PROJECT_ID
local GOOGLE_APPLICATION_CREDENTIALS
local NOX_FILE
# Inherit NOX_SESSION from environment to allow configs (like prerelease.cfg) to pass it in
# Inherit NOX_SESSION from environment to allow configs to pass it in
local NOX_SESSION="${NOX_SESSION}"

# ISOLATION: Create a unique gcloud config dir for this run
Expand All @@ -77,9 +77,8 @@ run_package_test() {

PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/google-auth-project-id.json")
GOOGLE_APPLICATION_CREDENTIALS="${KOKORO_GFILE_DIR}/google-auth-service-account.json"
# Note: system.sh is also reused for monorepo-wide continuous unit test jobs
# like `core_deps_from_source` and `prerelease_deps`. For google-auth, we only
# want to override NOX_FILE to system_tests/noxfile.py when running actual system tests.
# Note: For google-auth, we only want to override NOX_FILE to system_tests/noxfile.py
# when running actual system tests.
if [[ -z "${NOX_SESSION}" || "${NOX_SESSION}" == "system-"* ]]; then
NOX_FILE="system_tests/noxfile.py"
NOX_SESSION=""
Expand Down
3 changes: 2 additions & 1 deletion ci/get_package_shards.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,9 @@ def get_packages_to_test():
build_type = os.environ.get("BUILD_TYPE", "presubmit")
target_branch = os.environ.get("TARGET_BRANCH", "main")
test_all_packages = os.environ.get("TEST_ALL_PACKAGES", "false").lower() == "true"
handwritten_only = os.environ.get("HANDWRITTEN_ONLY", "false").lower() == "true"

all_packages = get_packages()
all_packages = get_packages(handwritten_only=handwritten_only)

if test_all_packages:
return all_packages
Expand Down
25 changes: 24 additions & 1 deletion ci/run_conditional_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ run_test_in_dir() {
local log_file="/tmp/test_log_${PY_VERSION}_${pkg_name_clean}.log"
export COVERAGE_FILE="${PROJECT_ROOT}/.coverage.${PY_VERSION}.${pkg_name_clean}"

# Isolate setuptools build directories per worker to prevent parallel build collisions.
local worker_build_dir="/tmp/build_${PY_VERSION}_${pkg_name_clean}"
local dist_cfg="/tmp/dist_cfg_${PY_VERSION}_${pkg_name_clean}.cfg"
mkdir -p "${worker_build_dir}"
printf "[build]\nbuild_base = %s/build\n[bdist_wheel]\nbdist_dir = %s/bdist\n[egg_info]\negg_base = %s\n" \
"${worker_build_dir}" "${worker_build_dir}" "${worker_build_dir}" > "${dist_cfg}"
export DIST_EXTRA_CONFIG="${dist_cfg}"

local div="============================================================"
local header="\n${div}\nRunning tests in ${d}\n${div}"
local footer
Expand All @@ -97,6 +105,8 @@ run_test_in_dir() {
set -e
popd > /dev/null

rm -rf "${worker_build_dir}" "${dist_cfg}"

if [ ${ret} -ne 0 ]; then
footer="❌ Tests failed in ${d} with exit code ${ret}"
else
Expand All @@ -114,13 +124,26 @@ run_test_in_dir() {
echo "${footer}"
fi

if [ ${ret} -ne 0 ] && [ -n "${FAILURE_LOG_DIR}" ]; then
mkdir -p "${FAILURE_LOG_DIR}"
local pkg_name=$(basename "${d}")
if grep -q "short test summary info" "${log_file}" 2>/dev/null; then
grep -A 35 -B 2 "short test summary info" "${log_file}" 2>/dev/null | sed $'s/\x1b\\[[0-9;]*[a-zA-Z]//g' > "${FAILURE_LOG_DIR}/${pkg_name}.log.txt" || true
else
tail -n 50 "${log_file}" 2>/dev/null | sed $'s/\x1b\\[[0-9;]*[a-zA-Z]//g' > "${FAILURE_LOG_DIR}/${pkg_name}.log.txt" || true
fi
fi

rm -f "${log_file}"
if [ ${ret} -ne 0 ]; then
if [ "${CONTINUE_ON_ERROR}" = "true" ]; then
exit 1
fi
exit 255 # Cancel xargs parallel jobs
fi
}
export -f run_test_in_dir
export test_script PROJECT_ROOT PY_VERSION TEST_TYPE
export test_script PROJECT_ROOT PY_VERSION TEST_TYPE CONTINUE_ON_ERROR FAILURE_LOG_DIR

dirs_to_test=()

Expand Down
27 changes: 23 additions & 4 deletions ci/run_single_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ case ${TEST_TYPE} in
retval=$?
;;
prerelease)
nox -s prerelease_deps-3.14
if [[ "$(pwd)" == */preview-packages/* ]]; then
echo "Skipping prerelease for preview package $(pwd)"
exit 0
fi
nox --stop-on-first-error -s prerelease_deps
retval=$?
;;
core_deps_from_source)
Expand Down Expand Up @@ -241,9 +245,24 @@ case ${TEST_TYPE} in
;;
esac

# Clean up `__pycache__` and `.nox` directories to avoid error
# `No space left on device` seen when running tests in Github Actions
if [ ${retval} -ne 0 ] && [ -n "${FAILURE_LOG_DIR}" ]; then
mkdir -p "${FAILURE_LOG_DIR}"
pkg_name=$(basename "$(pwd)")
for pip_bin in "${NOX_ENVDIR:-.nox}"/*/bin/pip; do
if [ -x "$pip_bin" ]; then
pip_out=$("$pip_bin" list 2>/dev/null || true)
if echo "$pip_out" | grep -qvE "^(Package|-+|pip|setuptools|wheel)[[:space:]]"; then
echo "$pip_out" > "${FAILURE_LOG_DIR}/${pkg_name}.pip.txt"
fi
break
fi
done
fi

# Clean up `__pycache__`, `.nox`, and build artifact directories to avoid error
# `No space left on device` and prevent leftover build/ files from polluting
# downstream local dependency builds.
find . | grep -E "(__pycache__)" | xargs rm -rf
rm -rf .nox
rm -rf .nox build *.egg-info

exit ${retval}
2 changes: 1 addition & 1 deletion packages/google-auth/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
long_description=long_description,
url="https://github.com/googleapis/google-cloud-python/tree/main/packages/google-auth",
packages=find_namespace_packages(
exclude=("tests*", "system_tests*", "docs*", "samples*")
exclude=("tests*", "system_tests*", "docs*", "samples*", "build*")
),
package_data={"google.auth": ["py.typed"], "google.oauth2": ["py.typed"]},
install_requires=DEPENDENCIES,
Expand Down
Loading