chore(security): pin Actions and harden test lock (#273)
* chore(security): pin Actions and update aiohttp * fix(security): generate universal test lock * fix(security): harden relock safeguards
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
open-pull-requests-limit: 1
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
@@ -17,13 +17,13 @@ jobs:
|
||||
name: Lint & Smoke
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.0
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- uses: actions/setup-python@v6.2.0
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- uses: actions/setup-node@v6.4.0
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: "22"
|
||||
|
||||
@@ -46,9 +46,9 @@ jobs:
|
||||
name: Security Gate
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.0
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- uses: actions/setup-python@v6.2.0
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
@@ -61,13 +61,18 @@ jobs:
|
||||
- name: Dependency audit
|
||||
run: pip-audit -r constraints-test.txt
|
||||
|
||||
- name: Audit all platform dependency pins
|
||||
run: |
|
||||
sed -E 's/[[:space:]]*;.*$//' constraints-test.txt > /tmp/constraints-all-platforms.txt
|
||||
pip-audit --no-deps -r /tmp/constraints-all-platforms.txt
|
||||
|
||||
preflight:
|
||||
name: Pre-flight Release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.0
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- uses: actions/setup-python@v6.2.0
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
@@ -89,9 +94,9 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v7.0.0
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- uses: actions/setup-python@v6.2.0
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
|
||||
@@ -41,16 +41,16 @@ jobs:
|
||||
build-mode: none
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v7.0.0
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@c4dd10e44af883a891fe31ced449bcb4a6728b9b # v3.37.6
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@c4dd10e44af883a891fe31ced449bcb4a6728b9b # v3.37.6
|
||||
with:
|
||||
category: "/language:${{ matrix.language }}"
|
||||
|
||||
@@ -12,11 +12,11 @@ jobs:
|
||||
if: ${{ github.repository_owner == 'malkuthro' }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v7.0.0
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
submodules: true
|
||||
- name: Publish Custom Node
|
||||
uses: Comfy-Org/publish-node-action@v1
|
||||
uses: Comfy-Org/publish-node-action@0eb6cd742945443bee7f79eeddd4f732780029a1 # v1 branch head
|
||||
with:
|
||||
## Add your own personal access token to your Github Repository secrets and reference it here.
|
||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
|
||||
@@ -14,10 +14,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out source
|
||||
uses: actions/checkout@v7.0.0
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6.2.0
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
issues: write # publish-node-action opens an issue on failure
|
||||
steps:
|
||||
- name: Check out the tag
|
||||
uses: actions/checkout@v7.0.0
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 0 # full history so the tag-on-main ancestry check resolves
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Publish Custom Node
|
||||
uses: Comfy-Org/publish-node-action@v1
|
||||
uses: Comfy-Org/publish-node-action@0eb6cd742945443bee7f79eeddd4f732780029a1 # v1 branch head
|
||||
with:
|
||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
contents: write # create the GitHub Release
|
||||
steps:
|
||||
- name: Check out the tag
|
||||
uses: actions/checkout@v7.0.0
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: Extract CHANGELOG section
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -6,6 +6,12 @@ The format is inspired by Keep a Changelog and SemVer.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Security
|
||||
- **Supply-chain hardening.** Raised the test-dependency floors to patched
|
||||
`aiohttp` and Pillow releases, regenerated the audited universal Python 3.11
|
||||
test lock with a forced upgrade, pinned every GitHub Action to an immutable
|
||||
commit SHA, and enabled one grouped weekly Dependabot update for those pins.
|
||||
|
||||
### Added
|
||||
- **Independent output type for published setups.** A `Koolook_PublishRouter`
|
||||
is auto-detected as the setup's **output selector**: whichever writer branches
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
resolve of `.[test]` + its full transitive closure), then **audit** it
|
||||
with `pip-audit`; a known CVE fails the bootstrap. Idempotent (no-op if
|
||||
`.venv` exists) — pass `-Force` / `--force` to recreate, `-Relock` /
|
||||
`--relock` to re-resolve and rewrite the lock, `-NoAudit` / `--no-audit`
|
||||
`--relock` to re-resolve and upgrade the lock (requires `uv`, which is
|
||||
checked before an existing `.venv` is replaced), `-NoAudit` / `--no-audit`
|
||||
to skip the audit (e.g. offline). Do not install into system Python or
|
||||
user-site packages. If `.venv` cannot be used, stop and explain the
|
||||
blocker before installing anywhere broader.
|
||||
@@ -47,7 +48,7 @@
|
||||
|
||||
Running the bootstrap script above is **pre-approved** — run it
|
||||
automatically whenever `.venv` is missing or `pytest` isn't importable,
|
||||
without asking first. It is safe by construction: it installs only the
|
||||
without asking first. Normal bootstrap is safe by construction: it installs only the
|
||||
already-reviewed, pinned `constraints-test.txt` set and `pip-audit`-gates
|
||||
the result, so it can never silently pull an unreviewed package or version.
|
||||
This **supersedes the global `/warmup` skill's `MISSING_VENV` hard-stop**:
|
||||
@@ -56,9 +57,9 @@ and continue — do not surface it as a blocker.
|
||||
|
||||
Two things still need your attention, and only these:
|
||||
|
||||
- **Bootstrap exits non-zero** (pip-audit flagged a CVE, or no Python was
|
||||
found) — surface *that* as the blocker. A failed audit is the one signal
|
||||
that genuinely warrants a human.
|
||||
- **Bootstrap exits non-zero** (pip-audit flagged a CVE, no Python was found,
|
||||
or a requested relock cannot find `uv`) — surface *that* as the blocker. A
|
||||
failed audit is the one signal that genuinely warrants a human.
|
||||
- **Dependency changes** — editing the `[test]` extras in `pyproject.toml`
|
||||
or regenerating `constraints-test.txt` (`--relock`) — still go through a
|
||||
reviewed PR diff. The lock file *is* the review surface; never relock
|
||||
|
||||
+21
-28
@@ -1,28 +1,17 @@
|
||||
# Locked test dependency set for ComfyUI-Koolook.
|
||||
#
|
||||
# Pinned resolve of the `[test]` extras in pyproject.toml plus their full
|
||||
# transitive closure. The bootstrap scripts install with `-c
|
||||
# constraints-test.txt`, so every fresh .venv is reproducible and
|
||||
# pip-audit-verifiable.
|
||||
#
|
||||
# DO NOT hand-edit the version pins. To change the set: edit the `[test]`
|
||||
# extras in pyproject.toml, then regenerate this file with
|
||||
# bash scripts/bootstrap_test_env.sh --force --relock (POSIX)
|
||||
# scripts\bootstrap_test_env.ps1 -Force -Relock (Windows)
|
||||
# and commit the diff -- that diff is the dependency-change review surface.
|
||||
#
|
||||
aiohappyeyeballs==2.6.2
|
||||
aiohttp==3.14.1
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv pip compile pyproject.toml --extra test --universal --python-version 3.11 --no-annotate --no-emit-package pip --output-file constraints-test.txt
|
||||
aiohappyeyeballs==2.7.1
|
||||
aiohttp==3.14.3
|
||||
aiosignal==1.4.0
|
||||
attrs==26.1.0
|
||||
boolean.py==5.0
|
||||
CacheControl==0.14.4
|
||||
certifi==2026.6.17
|
||||
charset-normalizer==3.4.7
|
||||
colorama==0.4.6
|
||||
boolean-py==5.0
|
||||
cachecontrol==0.14.4
|
||||
certifi==2026.7.22
|
||||
charset-normalizer==3.4.9
|
||||
colorama==0.4.6 ; sys_platform == 'win32'
|
||||
cyclonedx-python-lib==11.11.0
|
||||
defusedxml==0.7.1
|
||||
filelock==3.29.4
|
||||
filelock==3.32.2
|
||||
frozenlist==1.8.0
|
||||
idna==3.18
|
||||
iniconfig==2.3.0
|
||||
@@ -32,22 +21,26 @@ mdurl==0.1.2
|
||||
msgpack==1.2.1
|
||||
multidict==6.7.1
|
||||
packageurl-python==0.17.6
|
||||
packaging==26.2
|
||||
packaging==26.3
|
||||
pillow==12.3.0
|
||||
pip-api==0.0.34
|
||||
pip_audit==2.10.1
|
||||
pip-audit==2.10.1
|
||||
pip-requirements-parser==32.0.1
|
||||
platformdirs==4.10.0
|
||||
platformdirs==4.11.0
|
||||
pluggy==1.6.0
|
||||
propcache==0.5.2
|
||||
py-serializable==2.1.0
|
||||
Pygments==2.20.0
|
||||
pygments==2.20.0
|
||||
pyparsing==3.3.2
|
||||
pytest==9.1.0
|
||||
pytest==9.1.1
|
||||
requests==2.34.2
|
||||
rich==15.0.0
|
||||
sortedcontainers==2.4.0
|
||||
tomli==2.4.1
|
||||
tomli_w==1.2.0
|
||||
tomli-w==1.2.0
|
||||
typing-extensions==4.16.0 ; python_full_version < '3.13'
|
||||
urllib3==2.7.0
|
||||
yarl==1.24.2
|
||||
yarl==1.24.5
|
||||
|
||||
# The following packages were excluded from the output:
|
||||
# pip
|
||||
|
||||
@@ -12,13 +12,33 @@ check `constraints-test.txt` with `pip-audit`.
|
||||
`pip-audit` on the installed environment.
|
||||
- CI audits the committed lock with `pip-audit -r constraints-test.txt` on each
|
||||
pull request and on a weekly schedule, so newly disclosed CVEs in old pins are
|
||||
caught even when nobody has touched the dependency files.
|
||||
caught even when nobody has touched the dependency files. It also strips
|
||||
environment markers into a temporary manifest and audits that manifest, so
|
||||
Windows-only and Linux-only pins are each checked.
|
||||
- Bandit remains a separate first-party-code scan. A green Bandit job does not
|
||||
mean the dependency lock is clean.
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
Every third-party `uses:` reference in `.github/workflows/` is pinned to an
|
||||
immutable commit SHA, with its source release or branch reference left in a
|
||||
comment for readability. When updating an Action, resolve that reference to a
|
||||
commit with the GitHub API, review the change in the PR, then update both the
|
||||
SHA and its comment together. Do not use mutable major-version tags such as
|
||||
`@v3` or `@v7` in committed workflows.
|
||||
|
||||
Dependabot groups all GitHub Action updates into one weekly pull request. Python
|
||||
test dependencies are updated through the deliberate universal relock below, so
|
||||
the complete cross-platform lock diff stays one reviewable security unit.
|
||||
|
||||
## Regenerating the lock
|
||||
|
||||
Regenerate the lock only as an intentional dependency-change PR:
|
||||
Regenerate the lock only as an intentional dependency-change PR. The bootstrap
|
||||
uses `uv` to resolve a universal Python 3.11 lock, including the conditional
|
||||
dependencies needed by both CI platforms. The scripts check that `uv` is
|
||||
available before replacing an existing `.venv`, then use `--upgrade` so the
|
||||
relock can move stale vulnerable pins. Install `uv` before relocking if it is
|
||||
not already available.
|
||||
|
||||
```powershell
|
||||
scripts\bootstrap_test_env.ps1 -Force -Relock
|
||||
@@ -29,8 +49,7 @@ bash scripts/bootstrap_test_env.sh --force --relock
|
||||
```
|
||||
|
||||
Review and commit the resulting `constraints-test.txt` diff. Do not hand-edit
|
||||
individual pins unless you are immediately re-running the relock command to
|
||||
prove the full resolved set.
|
||||
individual pins.
|
||||
|
||||
## Responding to a CVE
|
||||
|
||||
|
||||
+3
-4
@@ -71,7 +71,6 @@ addopts = ["--import-mode=importlib"]
|
||||
# ComfyUI custom node is not installed as a package in the workflow.
|
||||
# Locally use ``.venv/bin/pip install -e '.[test]'`` from the repo root.
|
||||
# ``aiohttp`` is a runtime dep of ``koolook_routes`` and must be importable
|
||||
# even when only a pure helper is under test. The lower bound is pinned to
|
||||
# the CVE-2026-54273..54280 fix line (>=3.14.1) so the declared range can't
|
||||
# resolve a vulnerable aiohttp; the lock (constraints-test.txt) matches.
|
||||
test = ["pytest>=8", "aiohttp>=3.14.1", "pip-audit>=2.7", "Pillow>=10"]
|
||||
# even when only a pure helper is under test. Minimum versions exclude known
|
||||
# vulnerabilities; constraints-test.txt pins the exact audited test set.
|
||||
test = ["pytest>=8", "aiohttp>=3.14.3", "pip-audit>=2.7", "Pillow>=12.3.0"]
|
||||
|
||||
@@ -19,14 +19,26 @@ $ErrorActionPreference = "Stop"
|
||||
|
||||
$Constraints = "constraints-test.txt"
|
||||
|
||||
if (Test-Path .venv) {
|
||||
if (-not $Force) {
|
||||
if ($Relock) {
|
||||
Write-Host "-Relock requires -Force (the lock is rewritten from a fresh resolve); nothing was changed."
|
||||
}
|
||||
Write-Host ".venv already exists. Pass -Force to recreate."
|
||||
exit 0
|
||||
if ((Test-Path .venv) -and (-not $Force)) {
|
||||
if ($Relock) {
|
||||
Write-Host "-Relock requires -Force (the lock is rewritten from a fresh resolve); nothing was changed."
|
||||
}
|
||||
Write-Host ".venv already exists. Pass -Force to recreate."
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ($Relock) {
|
||||
if (-not (Get-Command uv -ErrorAction SilentlyContinue)) {
|
||||
throw "-Relock requires uv to generate the universal Python 3.11 lock; nothing was changed."
|
||||
}
|
||||
Write-Host "Resolving upgraded universal Python 3.11 test lock ..."
|
||||
uv pip compile pyproject.toml --extra test --universal --python-version 3.11 --upgrade --no-annotate --no-emit-package pip --output-file $Constraints
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "uv could not generate $Constraints."
|
||||
}
|
||||
}
|
||||
|
||||
if (Test-Path .venv) {
|
||||
Write-Host "Removing existing .venv ..."
|
||||
Remove-Item -Recurse -Force .venv
|
||||
}
|
||||
@@ -37,31 +49,13 @@ python -m venv .venv
|
||||
Write-Host "Upgrading pip + setuptools ..."
|
||||
.\.venv\Scripts\python -m pip install --quiet --upgrade pip setuptools
|
||||
|
||||
if ((Test-Path $Constraints) -and (-not $Relock)) {
|
||||
Write-Host "Installing project + test extras (locked via $Constraints) ..."
|
||||
.\.venv\Scripts\python -m pip install --quiet -e ".[test]" -c $Constraints
|
||||
} else {
|
||||
Write-Host "Resolving + installing project + test extras ..."
|
||||
.\.venv\Scripts\python -m pip install --quiet -e ".[test]"
|
||||
Write-Host "Writing locked set to $Constraints ..."
|
||||
@'
|
||||
# Locked test dependency set for ComfyUI-Koolook.
|
||||
#
|
||||
# Pinned resolve of the `[test]` extras in pyproject.toml plus their full
|
||||
# transitive closure. The bootstrap scripts install with `-c
|
||||
# constraints-test.txt`, so every fresh .venv is reproducible and
|
||||
# pip-audit-verifiable.
|
||||
#
|
||||
# DO NOT hand-edit the version pins. To change the set: edit the `[test]`
|
||||
# extras in pyproject.toml, then regenerate this file with
|
||||
# bash scripts/bootstrap_test_env.sh --force --relock (POSIX)
|
||||
# scripts\bootstrap_test_env.ps1 -Force -Relock (Windows)
|
||||
# and commit the diff -- that diff is the dependency-change review surface.
|
||||
#
|
||||
'@ | Out-File -Encoding ascii $Constraints
|
||||
.\.venv\Scripts\python -m pip list --format=freeze --exclude pip --exclude setuptools --exclude wheel --exclude koolook | Out-File -Encoding ascii -Append $Constraints
|
||||
if (-not (Test-Path $Constraints)) {
|
||||
throw "$Constraints is missing. Run: scripts\\bootstrap_test_env.ps1 -Force -Relock"
|
||||
}
|
||||
|
||||
Write-Host "Installing project + test extras (locked via $Constraints) ..."
|
||||
.\.venv\Scripts\python -m pip install --quiet -e ".[test]" -c $Constraints
|
||||
|
||||
if (-not $NoAudit) {
|
||||
Write-Host "Auditing installed set (pip-audit) ..."
|
||||
.\.venv\Scripts\pip-audit --skip-editable
|
||||
|
||||
@@ -41,14 +41,25 @@ if [ -z "$PYTHON_BIN" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -d .venv ]; then
|
||||
if [ -z "$FORCE" ]; then
|
||||
if [ -n "$RELOCK" ]; then
|
||||
echo "--relock requires --force (the lock is rewritten from a fresh resolve); nothing was changed." >&2
|
||||
fi
|
||||
echo ".venv already exists. Pass --force to recreate."
|
||||
exit 0
|
||||
if [ -d .venv ] && [ -z "$FORCE" ]; then
|
||||
if [ -n "$RELOCK" ]; then
|
||||
echo "--relock requires --force (the lock is rewritten from a fresh resolve); nothing was changed." >&2
|
||||
fi
|
||||
echo ".venv already exists. Pass --force to recreate."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -n "$RELOCK" ]; then
|
||||
if ! command -v uv >/dev/null 2>&1; then
|
||||
echo "--relock requires uv to generate the universal Python 3.11 lock; nothing was changed." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Resolving upgraded universal Python 3.11 test lock ..."
|
||||
uv pip compile pyproject.toml --extra test --universal --python-version 3.11 \
|
||||
--upgrade --no-annotate --no-emit-package pip --output-file "$CONSTRAINTS"
|
||||
fi
|
||||
|
||||
if [ -d .venv ]; then
|
||||
echo "Removing existing .venv ..."
|
||||
rm -rf .venv
|
||||
fi
|
||||
@@ -59,31 +70,14 @@ echo "Creating .venv ..."
|
||||
echo "Upgrading pip + setuptools ..."
|
||||
.venv/bin/python -m pip install --quiet --upgrade pip setuptools
|
||||
|
||||
if [ -f "$CONSTRAINTS" ] && [ -z "$RELOCK" ]; then
|
||||
echo "Installing project + test extras (locked via $CONSTRAINTS) ..."
|
||||
.venv/bin/python -m pip install --quiet -e '.[test]' -c "$CONSTRAINTS"
|
||||
else
|
||||
echo "Resolving + installing project + test extras ..."
|
||||
.venv/bin/python -m pip install --quiet -e '.[test]'
|
||||
echo "Writing locked set to $CONSTRAINTS ..."
|
||||
cat > "$CONSTRAINTS" <<'EOF'
|
||||
# Locked test dependency set for ComfyUI-Koolook.
|
||||
#
|
||||
# Pinned resolve of the `[test]` extras in pyproject.toml plus their full
|
||||
# transitive closure. The bootstrap scripts install with `-c
|
||||
# constraints-test.txt`, so every fresh .venv is reproducible and
|
||||
# pip-audit-verifiable.
|
||||
#
|
||||
# DO NOT hand-edit the version pins. To change the set: edit the `[test]`
|
||||
# extras in pyproject.toml, then regenerate this file with
|
||||
# bash scripts/bootstrap_test_env.sh --force --relock (POSIX)
|
||||
# scripts\bootstrap_test_env.ps1 -Force -Relock (Windows)
|
||||
# and commit the diff -- that diff is the dependency-change review surface.
|
||||
#
|
||||
EOF
|
||||
.venv/bin/python -m pip list --format=freeze --exclude pip --exclude setuptools --exclude wheel --exclude koolook >> "$CONSTRAINTS"
|
||||
if [ ! -f "$CONSTRAINTS" ]; then
|
||||
echo "$CONSTRAINTS is missing. Run: bash scripts/bootstrap_test_env.sh --force --relock" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installing project + test extras (locked via $CONSTRAINTS) ..."
|
||||
.venv/bin/python -m pip install --quiet -e '.[test]' -c "$CONSTRAINTS"
|
||||
|
||||
if [ -n "$AUDIT" ]; then
|
||||
echo "Auditing installed set (pip-audit) ..."
|
||||
if .venv/bin/pip-audit --skip-editable; then
|
||||
|
||||
@@ -35,7 +35,7 @@ def _pinned_names() -> dict[str, str]:
|
||||
continue
|
||||
assert "==" in line, f"constraint not pinned with '==': {line!r}"
|
||||
name, version = line.split("==", 1)
|
||||
pins[_canonical(name)] = version.strip()
|
||||
pins[_canonical(name)] = version.split(";", 1)[0].strip()
|
||||
return pins
|
||||
|
||||
|
||||
@@ -66,12 +66,12 @@ def test_every_top_level_test_extra_is_pinned():
|
||||
|
||||
|
||||
def test_no_duplicate_pins():
|
||||
names: list[str] = []
|
||||
pins: list[str] = []
|
||||
for raw in CONSTRAINTS.read_text(encoding="utf-8").splitlines():
|
||||
line = raw.strip()
|
||||
if line and not line.startswith("#"):
|
||||
names.append(_canonical(line.split("==", 1)[0]))
|
||||
dupes = sorted({name for name in names if names.count(name) > 1})
|
||||
pins.append(re.sub(r"\s+", " ", line))
|
||||
dupes = sorted({pin for pin in pins if pins.count(pin) > 1})
|
||||
assert not dupes, f"duplicate pins in constraints-test.txt: {dupes}"
|
||||
|
||||
|
||||
@@ -84,15 +84,41 @@ def test_bootstrap_upgrades_setuptools_before_audit():
|
||||
assert "--upgrade pip setuptools" in sh
|
||||
|
||||
|
||||
def test_bootstrap_relock_does_not_inspect_editable_git_metadata():
|
||||
"""Relock should work from cross-drive git worktrees on Windows."""
|
||||
def test_bootstrap_relock_checks_uv_before_replacing_venv():
|
||||
"""A missing relock prerequisite must not discard a usable test env."""
|
||||
ps1 = (REPO_ROOT / "scripts" / "bootstrap_test_env.ps1").read_text(encoding="utf-8")
|
||||
sh = (REPO_ROOT / "scripts" / "bootstrap_test_env.sh").read_text(encoding="utf-8")
|
||||
|
||||
assert "pip list --format=freeze" in ps1
|
||||
assert "pip list --format=freeze" in sh
|
||||
assert "pip freeze --exclude-editable" not in ps1
|
||||
assert "pip freeze --exclude-editable" not in sh
|
||||
assert ps1.index("Get-Command uv") < ps1.index("Remove-Item -Recurse -Force .venv")
|
||||
assert sh.index("command -v uv") < sh.index("rm -rf .venv")
|
||||
assert "pip list --format=freeze" not in ps1
|
||||
assert "pip list --format=freeze" not in sh
|
||||
|
||||
|
||||
def test_bootstrap_relock_generates_a_universal_ci_lock():
|
||||
"""Relocks include conditional dependencies for both CI platforms."""
|
||||
ps1 = (REPO_ROOT / "scripts" / "bootstrap_test_env.ps1").read_text(encoding="utf-8")
|
||||
sh = (REPO_ROOT / "scripts" / "bootstrap_test_env.sh").read_text(encoding="utf-8")
|
||||
|
||||
for script in (ps1, sh):
|
||||
assert "uv pip compile pyproject.toml --extra test --universal" in script
|
||||
assert "--python-version 3.11" in script
|
||||
assert "--upgrade" in script
|
||||
assert "--no-emit-package pip" in script
|
||||
|
||||
|
||||
def test_constraints_cover_conditional_ci_dependencies():
|
||||
pins = _pinned_names()
|
||||
|
||||
assert "typing-extensions" in pins
|
||||
assert "colorama" in pins
|
||||
|
||||
|
||||
def test_marker_versions_are_parsed_without_environment_markers():
|
||||
pins = _pinned_names()
|
||||
|
||||
assert pins["typing-extensions"] == "4.16.0"
|
||||
assert pins["colorama"] == "0.4.6"
|
||||
|
||||
|
||||
def test_ci_audits_committed_lock_on_prs_and_schedule():
|
||||
@@ -100,6 +126,8 @@ def test_ci_audits_committed_lock_on_prs_and_schedule():
|
||||
|
||||
assert "schedule:" in ci
|
||||
assert "pip-audit -r constraints-test.txt" in ci
|
||||
assert "pip-audit --no-deps -r /tmp/constraints-all-platforms.txt" in ci
|
||||
assert "sed -E 's/[[:space:]]*;.*$//' constraints-test.txt" in ci
|
||||
|
||||
|
||||
def test_ci_pytest_installs_from_committed_lock():
|
||||
|
||||
Reference in New Issue
Block a user