Skip to content

Commit 75c773e

Browse files
yinghsienwucopybara-github
authored andcommitted
feat: Publish companion constraints-3.11.txt and constraints-3.12.txt file for transitive dependency protection (4 day buffer to protect from supply chain attack)
For example, use pip install google-adk -c constraints-3.12.txt PiperOrigin-RevId: 954863904
1 parent 0a70337 commit 75c773e

3 files changed

Lines changed: 172 additions & 0 deletions

File tree

.pre-commit-config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ repos:
6464
# This script documents and matches the very patterns it forbids, so
6565
# it must not scan itself or other dev-only tooling.
6666
exclude: ^scripts/
67+
- id: update-constraints
68+
name: update-constraints
69+
entry: ./scripts/update_constraints.sh
70+
language: system
71+
files: ^(pyproject\.toml|constraints-.*\.txt)$
72+
pass_filenames: false
6773
- repo: https://github.com/executablebooks/mdformat
6874
rev: 0.7.22
6975
hooks:

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ pip install google-adk
4949

5050
**Requirements:** Python 3.10+.
5151

52+
For transitive dependency protection, we recommend to install with our companion
53+
constraints files (for python 3.10 to 3.14).
54+
55+
Choose the constraints file matching your Python version:
56+
57+
```bash
58+
# For example, for Python 3.10
59+
curl -o constraints-3.10.txt https://github.com/google/adk-python/blob/main/constraints-3.10.txt
60+
pip install google-adk -c constraints-3.10.txt
61+
rm constraints-3.10.txt
62+
```
63+
5264
To install optional integrations, you can use the following command:
5365

5466
```bash

scripts/update_constraints.sh

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#!/bin/bash
2+
# Copyright 2026 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
# Manage constraints.txt: check if up-to-date or automatically update it.
17+
#
18+
# Usage:
19+
# ./scripts/update_constraints.sh # Updates constraints.txt in-place if out of date
20+
# ./scripts/update_constraints.sh --check # Check only, exits with 1 if out of date (for CI)
21+
22+
set -e
23+
24+
# Parse arguments
25+
CHECK_ONLY=false
26+
for arg in "$@"; do
27+
case $arg in
28+
--check)
29+
CHECK_ONLY=true
30+
shift
31+
;;
32+
esac
33+
done
34+
35+
# Ensure uv is in PATH
36+
export PATH="$HOME/.local/bin:$PATH"
37+
38+
cleanup() {
39+
rm -f constraints-*.tmp
40+
}
41+
trap cleanup EXIT
42+
43+
PYTHON_VERSIONS=("3.10" "3.11" "3.12" "3.13" "3.14")
44+
EXIT_CODE=0
45+
46+
# Calculate 4 days ago date
47+
if [ "$CHECK_ONLY" = false ]; then
48+
if date -v-4d +%Y-%m-%d >/dev/null 2>&1; then
49+
EXCLUDE_NEWER_DATE=$(date -v-4d +%Y-%m-%d)
50+
else
51+
EXCLUDE_NEWER_DATE=$(date -d "4 days ago" +%Y-%m-%d)
52+
fi
53+
fi
54+
55+
for ver in "${PYTHON_VERSIONS[@]}"; do
56+
TARGET_FILE="constraints-${ver}.txt"
57+
echo "Processing $TARGET_FILE..."
58+
59+
if [ ! -f "$TARGET_FILE" ]; then
60+
if [ "$CHECK_ONLY" = true ]; then
61+
echo "$TARGET_FILE is missing!"
62+
EXIT_CODE=1
63+
continue
64+
fi
65+
fi
66+
67+
# Default date to what we calculated (for update mode)
68+
date_to_use="$EXCLUDE_NEWER_DATE"
69+
70+
if [ -f "$TARGET_FILE" ]; then
71+
if [ "$CHECK_ONLY" = true ]; then
72+
# In check mode, extract the date used when it was generated
73+
date_to_use=$(grep -h "# uv pip compile" "$TARGET_FILE" | grep -oE -- '--exclude-newer [0-9]{4}-[0-9]{2}-[0-9]{2}' | cut -d' ' -f2 || true)
74+
fi
75+
fi
76+
77+
# Construct the command from scratch
78+
GENERATION_CMD="uv pip compile pyproject.toml --all-extras --python-version $ver"
79+
if [ -n "$date_to_use" ]; then
80+
GENERATION_CMD="$GENERATION_CMD --exclude-newer $date_to_use"
81+
fi
82+
GENERATION_CMD="$GENERATION_CMD --index-url https://pypi.org/simple -o $TARGET_FILE"
83+
84+
echo "Found generation command: $GENERATION_CMD"
85+
86+
STABLE_FILE="constraints-${ver}.txt.stable.tmp"
87+
NEW_FILE="constraints-${ver}.txt.new.tmp"
88+
89+
# Copy the existing constraints to STABLE_FILE if it exists and is not empty
90+
if [ -s "$TARGET_FILE" ]; then
91+
cp "$TARGET_FILE" "$STABLE_FILE"
92+
else
93+
touch "$STABLE_FILE"
94+
fi
95+
96+
# Modify the GENERATION_CMD to output to NEW_FILE.
97+
RUN_CMD=$(echo "$GENERATION_CMD" | sed -E "s/-o [^ ]+/-o $NEW_FILE/")
98+
RUN_CMD=$(echo "$RUN_CMD" | sed -E "s/--output-file [^ ]+/--output-file $NEW_FILE/")
99+
RUN_CMD=$(echo "$RUN_CMD" | sed -E "s/--output-file=[^ ]+/--output-file=$NEW_FILE/")
100+
101+
# Execute the command, also adding STABLE_FILE as a constraint to stabilize resolution.
102+
echo "Running: $RUN_CMD --constraint $STABLE_FILE"
103+
if ! eval "$RUN_CMD --constraint $STABLE_FILE"; then
104+
if [ "$CHECK_ONLY" = true ]; then
105+
echo "❌ Resolution failed with stable constraints for $TARGET_FILE."
106+
echo " This usually happens when a new dependency requirement in pyproject.toml conflicts with existing pinned versions."
107+
echo " To fix this, run the update script locally to resolve conflicts and update constraints:"
108+
echo " $ ./scripts/update_constraints.sh"
109+
rm -f "$STABLE_FILE" "$NEW_FILE"
110+
EXIT_CODE=1
111+
continue
112+
else
113+
echo "⚠️ Resolution failed with stable constraints. Retrying without constraints to allow upgrades..."
114+
echo "Running: $RUN_CMD"
115+
if ! eval "$RUN_CMD"; then
116+
echo "❌ Resolution failed even without constraints."
117+
rm -f "$STABLE_FILE" "$NEW_FILE"
118+
EXIT_CODE=1
119+
continue
120+
fi
121+
fi
122+
fi
123+
124+
# Reconstruct NEW_FILE to have the clean GENERATION_CMD in its header
125+
CLEAN_FILE="constraints-${ver}.txt.clean.tmp"
126+
{
127+
echo "# This file was autogenerated by uv via the following command:"
128+
echo "# $GENERATION_CMD"
129+
tail -n +3 "$NEW_FILE"
130+
} > "$CLEAN_FILE"
131+
mv "$CLEAN_FILE" "$NEW_FILE"
132+
133+
# Compare
134+
if diff -u "$TARGET_FILE" "$NEW_FILE"; then
135+
echo "$TARGET_FILE is up-to-date."
136+
rm -f "$STABLE_FILE" "$NEW_FILE"
137+
else
138+
if [ "$CHECK_ONLY" = true ]; then
139+
echo "$TARGET_FILE is OUT OF DATE!"
140+
echo " Please run the update script locally to update it and commit the changes:"
141+
echo " $ ./scripts/update_constraints.sh"
142+
rm -f "$STABLE_FILE" "$NEW_FILE"
143+
EXIT_CODE=1
144+
else
145+
echo "🔄 $TARGET_FILE was OUT OF DATE. Updating it automatically..."
146+
cp "$NEW_FILE" "$TARGET_FILE"
147+
echo "$TARGET_FILE has been updated locally."
148+
rm -f "$STABLE_FILE" "$NEW_FILE"
149+
EXIT_CODE=1
150+
fi
151+
fi
152+
done
153+
154+
exit $EXIT_CODE

0 commit comments

Comments
 (0)