Files
substrate/hack/verify/python-licenses.sh
T

90 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# 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
#
# http://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.
# Verifies that no Python dependency in any of our requirements.txt files
# (and their transitive closure) carries a disallowed license. Modeled on
# hack/verify/licenses.sh (Go side), but Python deps live in per-project
# venvs that may be ephemeral, so this script creates them on demand.
#
# CNCF allowed third-party licenses:
# https://github.com/cncf/foundation/blob/main/allowed-third-party-license-policy.md
set -o errexit -o nounset -o pipefail
ROOT="$(git rev-parse --show-toplevel)"
cd "${ROOT}"
# Python projects with a requirements.txt to check. Each entry is a directory
# that contains requirements.txt; its venv lives at <dir>/venv.
PROJECTS=(
"benchmarking/locust"
"benchmarking/automation"
)
# Fail if any installed package's license string contains one of these
# semicolon-separated entries. pip-licenses substring-matches, so single
# substrings like "AGPL" / "GPL" / "SSPL" catch every common spelling.
# Add entries as needed; prefer this denylist over an allowlist because
# pip-licenses license strings vary widely (classifier vs. License field
# vs. PEP-639 expression).
DISALLOWED="AGPL;GPLv2;GPLv3;GPL-2.0;GPL-3.0;GNU General Public License;GNU Affero General Public License;Server Side Public License;SSPL;Commons Clause"
ensure_venv() {
local dir="$1"
local venv="${dir}/venv"
if [[ ! -d "${venv}" ]]; then
echo "Creating venv at ${venv}..."
python3 -m venv "${venv}" || return 1
"${venv}/bin/pip" install --quiet --upgrade pip || return 1
"${venv}/bin/pip" install --quiet -r "${dir}/requirements.txt" || {
echo "ERROR: failed to install ${dir}/requirements.txt into ${venv}" >&2
return 1
}
fi
if ! "${venv}/bin/pip" show pip-licenses >/dev/null 2>&1; then
"${venv}/bin/pip" install --quiet pip-licenses || {
echo "ERROR: failed to install pip-licenses into ${venv}" >&2
return 1
}
fi
}
check_project() {
local dir="$1"
local venv="${dir}/venv"
ensure_venv "${dir}" || return 1
echo "==> ${dir}"
# --ignore-packages skips the tools themselves so the venv doesn't fail
# itself; pip / setuptools / pip-licenses are infrastructure.
"${venv}/bin/pip-licenses" \
--fail-on="${DISALLOWED}" \
--ignore-packages pip setuptools pip-licenses prettytable wcwidth tomli
}
fail=0
for proj in "${PROJECTS[@]}"; do
if ! check_project "${proj}"; then
echo "ERROR: ${proj} contains disallowed Python license(s)" >&2
fail=1
fi
done
if [[ "${fail}" -ne 0 ]]; then
exit 1
fi
echo "All Python licenses pass."