Skip to content

Commit 3afaf44

Browse files
authored
deps-scan: report drift on .pre-commit-config.yaml hook revs (#62)
* deps-scan: report drift on .pre-commit-config.yaml hook revs Adds a new section to .github/scripts/dependency-scan.sh that walks every repo: block in .pre-commit-config.yaml, asks the upstream Git host for the highest semver-shaped tag, and reports drift when the pinned rev: is older. Today every hook in the project's config is on GitHub, so the lookup uses GET /repos/{owner}/{repo}/tags unauthenticated — one call per hook is well under the 60 req/h public limit, no GITHUB_TOKEN coupling needed. Hooks pinned to SHAs, hosted off GitHub, or sitting under repo: local / repo: meta sentinels are silently skipped (same pattern as the AWS-creds-gated checks). New helpers in .github/scripts/lib_dependency_scan.sh: - extract_precommit_hooks: parses the YAML and emits repo|rev pairs - get_latest_precommit_hook_release: GitHub-tags lookup with semver filtering (drops -rc/-beta and date-tagged repos) Wired into the summary, no-drift early-exit, temp-file cleanup, and the Markdown report that the deps-scan workflow turns into a GitHub issue. Also updated .github/CI.md, CONTRIBUTING.md, and the BATS README to mention the new surface; the BATS suite gets 14 new tests (6 for the YAML extractor, 8 for the GitHub-API helper using a PATH-shimmed curl for the network-less branches) bringing the dep-scan file to 88 tests and the BATS folder to 299 across 9 files. The presentation deck was refreshed to match the new test counts. * chore(pre-commit): bump ruff to v0.15.13 and mirrors-mypy to v2.1.0 Picked up by the new pre-commit-hook section of dependency-scan.sh. Confirmed locally: ruff-format and ruff (legacy alias) still pass on the full tree at the new rev. The mirrors-mypy v1→v2 jump is the mirror repo's own versioning scheme and shipped no behavioural change to the published hook config — entry, language, types_or, args, and require_serial are byte-identical between v1.19.1 and v2.1.0; the underlying mypy version follows mypy's own release cadence as before. The mypy hook in our config has a pre-existing latent issue (pass_filenames: false with no positional target) that fails on both v1.19.1 and v2.1.0. Out of scope for this bump — flagged separately. * chore(docker): bump python base from 3.14.4 to 3.14.5 Picks up the Debian 13.4 base refresh, which fixes CVE-2026-27135 (libnghttp2-14 1.64.0-1.1 → 1.64.0-1.1+deb13u1). Trivy was flagging this on health-monitor, manifest-processor, and queue-processor; bumping inference-monitor and Dockerfile.dev too since they share the same base and the same CVE. 3.14.5 is the fifth maintenance release on the 3.14 line and ships ~113 bugfixes since 3.14.4. No 3.14.x source-incompatible changes; aws-cdk-lib's LAMBDA_PYTHON_RUNTIME stays on PYTHON_3_14. Skipped .gitlab-ci.yml on purpose — the file is marked frozen reference (see top-of-file banner) and the deps-scan workflow only walks .github/workflows/, so it doesn't double-flag this. * ci(security): scan Dockerfile.dev in trivy container-scan matrix Adds a sixth row to the security:trivy:container-scan matrix so the contributor-side dev image is rebuilt and CVE-scanned on every push and PR, alongside the four service images and helm-installer. Why per-PR (not weekly cve-scan): the dev container is what contributors run gco / cdk / kubectl out of locally, so a CVE there matters as soon as it lands. Same trigger semantics as the rest of security.yml, same Trivy invocation, same artifact retention. Build cost: one extra ~3-5 min image build per run, parallelised with the other matrix legs and cached via type=gha (scope=dev), so incremental rebuilds are cheap once the cache is warm. * ci(dev): pin npm to 11.14.1 for reproducible builds The nodesource nodejs apt package doesn't pin npm to a specific patch — it ships whatever was current when the apt cache was last refreshed — so the dev image's npm version drifts every rebuild. Pin npm the same way Dockerfile.dev already pins CDK CLI, kubectl, AWS CLI v2, and Docker CLI: explicit ARG NPM_VERSION, single `npm install -g npm@${NPM_VERSION}` step, easy for deps-scan to flag drift on. NPM_VERSION joins the existing pin family — wiring it into the deps-scan allowlist + helper lookup follows in a separate commit. Side benefit: this clears Trivy CVE-2026-33671. npm 11.12.1 (the version that currently ships with Node 24.x via nodesource) bundles picomatch@4.0.3 transitively through tinyglobby; npm 11.13.0+ bundles picomatch@4.0.4, which is on the fixed-versions list. * deps-scan: track NPM_VERSION pin in Dockerfile.dev Adds NPM_VERSION to the Dockerfile.dev pin allowlist in extract_dockerfile_pins so it shows up in the deps-scan report, plus an NPM_VERSION branch in check_dockerfile_pin that hits registry.npmjs.org/npm/latest (same source as CDK_VERSION) for the upstream check. Six pins are now tracked across rebuilds: NODE_MAJOR, NPM_VERSION, CDK_VERSION, KUBECTL_VERSION, AWSCLI_VERSION, DOCKER_VERSION. The "finds all five pins" BATS test grew to six and a new test asserts the NPM_VERSION value is bare semver (no v-prefix) so it concatenates cleanly into npm install -g npm@${NPM_VERSION}. Bumps the BATS file to 90 tests. Presentation deck refreshed to match the new test count.
1 parent 6b36815 commit 3afaf44

16 files changed

Lines changed: 480 additions & 20 deletions

.github/CI.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,10 @@ Ecosystems tracked:
162162
| Helm charts | `lambda/helm-installer/charts.yaml` | Uses `helm show chart` for OCI charts and `helm search repo` for traditional repos |
163163
| EKS add-ons | `addon_name`/`addon_version` pairs extracted from `gco/stacks/constants.py` | Requires AWS credentials (via OIDC). The script pre-flights `sts get-caller-identity`; without valid creds the add-on section is explicitly **skipped** and the report notes why — everything else still runs |
164164
| Aurora PostgreSQL engine | `AURORA_POSTGRES_VERSION_DISPLAY` from `gco/stacks/constants.py` | Requires AWS credentials (via OIDC). Queries `rds describe-db-engine-versions` for the latest minor release within the same major line |
165+
| Pre-commit hooks | `repo:` / `rev:` blocks in `.pre-commit-config.yaml` | Calls `GET /repos/{owner}/{repo}/tags` on GitHub for each hook and reports drift when our pinned `rev:` is older than the highest semver-shaped tag. Unauthenticated; SHA pins and non-GitHub repos are skipped silently |
165166
| CDK enum constants | `LAMBDA_PYTHON_RUNTIME` and `AURORA_POSTGRES_VERSION` from `gco/stacks/constants.py` | Introspects the installed `aws-cdk-lib` (the `deps-scan` workflow installs the latest) for `aws_lambda.Runtime.PYTHON_X_Y` and `aws_rds.AuroraPostgresEngineVersion.VER_X_Y` and reports drift when our pinned enum is older than the highest member exposed by the library. Skipped with a note when `aws-cdk-lib` isn't importable |
166167
| Python release | `LAMBDA_PYTHON_RUNTIME` (the major Python version we standardise on across Lambdas) | Queries `https://endoflife.date/api/python.json` for the highest currently-supported stable cycle and reports drift compared to the `LAMBDA_PYTHON_RUNTIME` constant. Public endpoint, no AWS creds |
168+
| Pre-commit hooks | `repo:` / `rev:` pairs in `.pre-commit-config.yaml` | Queries `api.github.com/repos/<owner>/<repo>/tags` for each hook and compares against the pinned `rev:`. Uses `GITHUB_TOKEN` for the authenticated rate limit when CI runs the workflow. SHA-pinned hooks and non-GitHub repos are silently skipped — no false drift |
167169

168170
Images matching `gco/*` are skipped (we build those). Non-semver tags (`latest`, branch names, SHAs) are ignored.
169171

@@ -203,6 +205,7 @@ The console output shows each surface's drift inline. To trigger the exact workf
203205
- **New Helm chart** — nothing to change; the script walks every entry in `lambda/helm-installer/charts.yaml`.
204206
- **New EKS add-on** — add the constant in `gco/stacks/constants.py` and reference it in `regional_stack.py`. The scanner imports from the constants module.
205207
- **New Aurora engine version** — update `AURORA_POSTGRES_VERSION` and `AURORA_POSTGRES_VERSION_DISPLAY` in `gco/stacks/constants.py`.
208+
- **New pre-commit hook** — nothing to change; `extract_precommit_hooks` walks every `repo:` block in `.pre-commit-config.yaml` and the GitHub-tags lookup picks up the hook automatically (as long as the upstream lives on GitHub and tags semver-shaped releases).
206209
- **New CDK enum constant** — add the constant in `gco/stacks/constants.py`, then add a comparison block in `dependency-scan.sh`'s "Checking CDK enum constants" section that calls a new `get_latest_<name>` helper from `lib_dependency_scan.sh`. Pattern-match the existing `LAMBDA_PYTHON_RUNTIME` and `AURORA_POSTGRES_VERSION` blocks.
207210

208211
#### Failure modes & debugging

.github/scripts/dependency-scan.sh

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
# - EKS Kubernetes minor from cdk.json (AWS creds)
1616
# - Aurora PostgreSQL engine versions (AWS creds)
1717
# - EMR Serverless release labels (AWS creds)
18-
# - Dockerfile.dev ARG pins (Node LTS major, CDK CLI, kubectl, AWS CLI v2,
19-
# Docker CLI) — public endpoints, no AWS creds needed
18+
# - Dockerfile.dev ARG pins (Node LTS major, npm, CDK CLI, kubectl,
19+
# AWS CLI v2, Docker CLI) — public endpoints, no AWS creds needed
20+
# - Pre-commit hook revisions in .pre-commit-config.yaml compared
21+
# against the latest tag published upstream (GitHub API)
2022
# - CDK enum constants from gco/stacks/constants.py compared against the
2123
# installed aws-cdk-lib (LAMBDA_PYTHON_RUNTIME, AURORA_POSTGRES_VERSION)
2224
# - Latest stable Python release from endoflife.date — public endpoint
@@ -489,6 +491,7 @@ EMR_COUNT="$(wc -l < "$EMR_RESULTS" 2>/dev/null | tr -d ' ')"
489491
# Each pin has its own upstream:
490492
#
491493
# NODE_MAJOR github://nodejs/Release → schedule.json (LTS majors)
494+
# NPM_VERSION registry.npmjs.org/npm/latest
492495
# CDK_VERSION registry.npmjs.org/aws-cdk/latest
493496
# KUBECTL_VERSION https://dl.k8s.io/release/stable-<minor>.txt
494497
# (minor from cdk.json::kubernetes_version)
@@ -539,6 +542,17 @@ if candidates:
539542
"https://registry.npmjs.org/aws-cdk/latest" 2>/dev/null \
540543
| jq -r '.version // empty' 2>/dev/null)" || true
541544
;;
545+
NPM_VERSION)
546+
# ``npm`` is part of the dev container's pinned tooling — the
547+
# version that ships bundled inside nodesource's nodejs apt
548+
# package isn't pinned to a patch, so the Dockerfile installs a
549+
# specific ``npm@X.Y.Z`` to keep rebuilds reproducible (same
550+
# rationale as CDK_VERSION above). The canonical "latest" is the
551+
# ``latest`` dist-tag on npmjs.org, same source CDK uses.
552+
latest="$(curl -fsSL --max-time 15 \
553+
"https://registry.npmjs.org/npm/latest" 2>/dev/null \
554+
| jq -r '.version // empty' 2>/dev/null)" || true
555+
;;
542556
KUBECTL_VERSION)
543557
# Match the minor line already committed to cdk.json so the pin
544558
# and the EKS cluster stay within the ±1 minor skew policy.
@@ -604,6 +618,55 @@ fi
604618
DOCKERFILE_COUNT="$(wc -l < "$DOCKERFILE_RESULTS" 2>/dev/null | tr -d ' ')"
605619
[ -z "$DOCKERFILE_COUNT" ] && DOCKERFILE_COUNT=0
606620

621+
# ---------------------------------------------------------------------------
622+
# Pre-commit hook revisions
623+
#
624+
# Compares the ``rev:`` pinned for each ``repo:`` block in
625+
# ``.pre-commit-config.yaml`` against the latest semver-shaped tag
626+
# published by the upstream Git host. This catches drift Dependabot
627+
# can't see — pre-commit pins live in YAML, not in the package
628+
# ecosystems Dependabot monitors — and matters in practice because
629+
# stale hook pins quietly miss new lint rules and bug fixes.
630+
#
631+
# Each hook's repo URL is resolved to a tag list via the GitHub API
632+
# (the only host we use today). The helper returns empty for
633+
# non-GitHub repos or SHA-pinned ``rev:`` values, in which case that
634+
# hook is silently skipped — same pattern used by the AWS-creds-gated
635+
# checks above. Calls are unauthenticated; we make one request per
636+
# hook, which is well below the 60 req/h public limit.
637+
# ---------------------------------------------------------------------------
638+
echo ""
639+
echo "=== Checking pre-commit hook revisions ==="
640+
641+
PRECOMMIT_RESULTS="$(mktemp)"
642+
PRECOMMIT_CONFIG=".pre-commit-config.yaml"
643+
644+
if [ -f "$PRECOMMIT_CONFIG" ]; then
645+
extract_precommit_hooks "$PRECOMMIT_CONFIG" | while IFS='|' read -r repo current_rev; do
646+
[ -z "$repo" ] && continue
647+
[ -z "$current_rev" ] && continue
648+
649+
latest_rev="$(get_latest_precommit_hook_release "$repo")"
650+
[ -z "$latest_rev" ] && continue
651+
652+
# Strip ``v`` so compare_semver ranks ``v0.22.1`` vs ``v0.22.2``
653+
# (and the rare unprefixed ``1.38.0`` from yamllint historically)
654+
# consistently. We keep the original ``current_rev`` / ``latest_rev``
655+
# strings in the report so the operator copy-pastes the exact
656+
# value pre-commit expects.
657+
if [ "$current_rev" != "$latest_rev" ] \
658+
&& [ "$(compare_semver "$current_rev" "$latest_rev")" = "newer" ]; then
659+
echo " - ${repo}: ${current_rev} -> ${latest_rev}"
660+
echo "${repo}|${current_rev}|${latest_rev}" >> "$PRECOMMIT_RESULTS"
661+
fi
662+
done
663+
else
664+
echo " $PRECOMMIT_CONFIG not found, skipping."
665+
fi
666+
667+
PRECOMMIT_COUNT="$(wc -l < "$PRECOMMIT_RESULTS" 2>/dev/null | tr -d ' ')"
668+
[ -z "$PRECOMMIT_COUNT" ] && PRECOMMIT_COUNT=0
669+
607670
# ---------------------------------------------------------------------------
608671
# CDK enum constants
609672
#
@@ -733,6 +796,7 @@ else
733796
echo "EMR Serverless release: $EMR_COUNT"
734797
fi
735798
echo "Dockerfile.dev pins: $DOCKERFILE_COUNT"
799+
echo "Pre-commit hooks: $PRECOMMIT_COUNT"
736800
if [ -n "$CDK_ENUM_SKIP_REASON" ]; then
737801
echo "CDK enum constants: (skipped)"
738802
else
@@ -749,6 +813,7 @@ if [ "$PYTHON_COUNT" -eq 0 ] && [ "$DOCKER_COUNT" -eq 0 ] \
749813
&& [ "$EKS_K8S_COUNT" -eq 0 ] \
750814
&& [ "$AURORA_COUNT" -eq 0 ] && [ "$EMR_COUNT" -eq 0 ] \
751815
&& [ "$DOCKERFILE_COUNT" -eq 0 ] \
816+
&& [ "$PRECOMMIT_COUNT" -eq 0 ] \
752817
&& [ "$CDK_ENUM_COUNT" -eq 0 ] \
753818
&& [ "$PYTHON_RELEASE_COUNT" -eq 0 ]; then
754819
echo ""
@@ -781,7 +846,7 @@ if [ "$PYTHON_COUNT" -eq 0 ] && [ "$DOCKER_COUNT" -eq 0 ] \
781846
else
782847
echo "All dependencies are up to date."
783848
fi
784-
rm -f "$DOCKER_RESULTS" "$HELM_RESULTS" "$ADDON_RESULTS" "$EKS_K8S_RESULTS" "$AURORA_RESULTS" "$EMR_RESULTS" "$DOCKERFILE_RESULTS" "$CDK_ENUM_RESULTS" "$PYTHON_RELEASE_RESULTS"
849+
rm -f "$DOCKER_RESULTS" "$HELM_RESULTS" "$ADDON_RESULTS" "$EKS_K8S_RESULTS" "$AURORA_RESULTS" "$EMR_RESULTS" "$DOCKERFILE_RESULTS" "$PRECOMMIT_RESULTS" "$CDK_ENUM_RESULTS" "$PYTHON_RELEASE_RESULTS"
785850
if [ -n "${GITHUB_OUTPUT:-}" ]; then
786851
echo "has_drift=false" >> "$GITHUB_OUTPUT"
787852
fi
@@ -918,6 +983,22 @@ fi
918983
echo ""
919984
fi
920985

986+
if [ "$PRECOMMIT_COUNT" -gt 0 ]; then
987+
echo "## Pre-commit Hooks"
988+
echo ""
989+
echo "Hook \`rev:\` pins in \`.pre-commit-config.yaml\` are behind the"
990+
echo "latest tag published by their upstream repos. Bump in"
991+
echo "\`.pre-commit-config.yaml\`, then run \`pre-commit autoupdate\`"
992+
echo "locally (or edit by hand) and verify the hooks still pass."
993+
echo ""
994+
echo "| Repo | Current | Latest |"
995+
echo "|------|---------|--------|"
996+
while IFS='|' read -r repo cur lat; do
997+
echo "| $repo | \`$cur\` | \`$lat\` |"
998+
done < "$PRECOMMIT_RESULTS"
999+
echo ""
1000+
fi
1001+
9211002
if [ "$CDK_ENUM_COUNT" -gt 0 ]; then
9221003
echo "## CDK Enum Constants"
9231004
echo ""
@@ -977,7 +1058,7 @@ fi
9771058
echo "_Automatically created by the \`deps-scan\` workflow._"
9781059
} > "$REPORT_FILE"
9791060

980-
rm -f "$DOCKER_RESULTS" "$HELM_RESULTS" "$ADDON_RESULTS" "$EKS_K8S_RESULTS" "$AURORA_RESULTS" "$EMR_RESULTS" "$DOCKERFILE_RESULTS" "$CDK_ENUM_RESULTS" "$PYTHON_RELEASE_RESULTS"
1061+
rm -f "$DOCKER_RESULTS" "$HELM_RESULTS" "$ADDON_RESULTS" "$EKS_K8S_RESULTS" "$AURORA_RESULTS" "$EMR_RESULTS" "$DOCKERFILE_RESULTS" "$PRECOMMIT_RESULTS" "$CDK_ENUM_RESULTS" "$PYTHON_RELEASE_RESULTS"
9811062

9821063
if [ -n "${GITHUB_OUTPUT:-}" ]; then
9831064
echo "has_drift=true" >> "$GITHUB_OUTPUT"

.github/scripts/lib_dependency_scan.sh

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ extract_k8s_version() {
242242
# Example output for Dockerfile.dev:
243243
#
244244
# NODE_MAJOR|24
245+
# NPM_VERSION|11.14.1
245246
# CDK_VERSION|2.1120.0
246247
# KUBECTL_VERSION|v1.35.4
247248
# AWSCLI_VERSION|2.34.42
@@ -253,6 +254,7 @@ extract_dockerfile_pins() {
253254
import re, sys
254255
allowlist = {
255256
'NODE_MAJOR',
257+
'NPM_VERSION',
256258
'CDK_VERSION',
257259
'KUBECTL_VERSION',
258260
'AWSCLI_VERSION',
@@ -271,6 +273,43 @@ with open(sys.argv[1]) as f:
271273
" "$file" 2>/dev/null
272274
}
273275

276+
# extract_precommit_hooks [config_path]
277+
#
278+
# Parses ``.pre-commit-config.yaml`` and emits one ``repo|rev`` pair per
279+
# hook ``repo:`` block. The repo URL is left intact (it's needed to
280+
# resolve the upstream releases endpoint), and ``rev`` is the literal
281+
# string committed to the config — usually a tag like ``v0.15.7`` or
282+
# ``v1.19.1`` but pre-commit also tolerates plain semver and full SHAs.
283+
# Local hook stanzas (``repo: local``) and the pre-commit hook
284+
# meta-stanza (``repo: meta``) are skipped: there's no upstream release
285+
# to compare against.
286+
#
287+
# Falls back silently to an empty list if the file is missing or the
288+
# YAML can't be parsed — the caller treats that as "skip" rather than
289+
# "no drift", same pattern as the other extractors in this file.
290+
extract_precommit_hooks() {
291+
local file="${1:-.pre-commit-config.yaml}"
292+
[ -f "$file" ] || return 0
293+
python3 -c "
294+
import sys, yaml
295+
try:
296+
with open(sys.argv[1]) as f:
297+
data = yaml.safe_load(f)
298+
except Exception:
299+
sys.exit(0)
300+
for entry in (data or {}).get('repos', []) or []:
301+
repo = (entry or {}).get('repo', '') or ''
302+
rev = (entry or {}).get('rev', '') or ''
303+
# ``local`` and ``meta`` are pre-commit conventions for hooks
304+
# that aren't backed by an upstream repo; skip them.
305+
if not repo or repo in ('local', 'meta'):
306+
continue
307+
if not rev:
308+
continue
309+
print(f'{repo}|{rev}')
310+
" "$file" 2>/dev/null
311+
}
312+
274313
# extract_emr_versions <file>
275314
#
276315
# Extracts the pinned EMR Serverless release label from the constants module.
@@ -425,3 +464,82 @@ if candidates:
425464
print(max(candidates)[1])
426465
" 2>/dev/null
427466
}
467+
# get_latest_precommit_hook_release <repo_url>
468+
#
469+
# Given the ``repo:`` URL committed to ``.pre-commit-config.yaml``,
470+
# prints the latest semver-shaped tag from the upstream Git host so
471+
# the dep-scan can compare it against the pinned ``rev:``. Empty
472+
# output on network failure, an unsupported host, or when no tag
473+
# matches — callers treat that as "skip" rather than as drift.
474+
#
475+
# Today only GitHub repos are supported. Every hook in the project's
476+
# ``.pre-commit-config.yaml`` is hosted there, and the pre-commit
477+
# ecosystem is overwhelmingly GitHub-based. If a future hook lives
478+
# elsewhere (GitLab, Codeberg) the helper will return empty and the
479+
# scan logs a one-line skip note for that hook — no false drift.
480+
#
481+
# We use ``GET /repos/{owner}/{repo}/tags`` rather than
482+
# ``releases/latest`` because pre-commit pins ``rev:`` to a Git tag,
483+
# not a GitHub Release — and several hooks (yamllint, mirrors-mypy,
484+
# markdownlint-cli2) tag without ever cutting a Release. The tags
485+
# endpoint returns newest-first, so we filter to ``vX.Y.Z`` /
486+
# ``X.Y.Z`` / ``X.Y`` shapes, drop pre-release suffixes (``-rc1``,
487+
# ``-beta``), and take the highest by semver.
488+
#
489+
# Unauthenticated. The monthly scan calls this once per hook (four
490+
# times against today's config) — the unauthenticated GitHub API
491+
# limit is 60 req/h per IP, so a per-PAT/GITHUB_TOKEN bump to the
492+
# 5000 req/h authenticated bucket isn't worth the extra coupling.
493+
get_latest_precommit_hook_release() {
494+
local repo_url="$1"
495+
[ -n "$repo_url" ] || return 0
496+
497+
# Only GitHub is supported today. Strip any trailing ``.git`` or
498+
# ``/`` so the owner/repo extraction works for both forms commonly
499+
# seen in pre-commit configs.
500+
local cleaned="${repo_url%.git}"
501+
cleaned="${cleaned%/}"
502+
case "$cleaned" in
503+
https://github.com/*) ;;
504+
*) return 0 ;;
505+
esac
506+
507+
local owner_repo="${cleaned#https://github.com/}"
508+
# Reject anything that isn't ``owner/repo`` (no extra path segments).
509+
case "$owner_repo" in
510+
*/*/*) return 0 ;;
511+
*/*) ;;
512+
*) return 0 ;;
513+
esac
514+
515+
curl -fsSL --max-time 15 \
516+
-H "Accept: application/vnd.github+json" \
517+
-H "X-GitHub-Api-Version: 2022-11-28" \
518+
"https://api.github.com/repos/${owner_repo}/tags?per_page=100" 2>/dev/null \
519+
| python3 -c "
520+
import json, re, sys
521+
try:
522+
data = json.load(sys.stdin)
523+
except Exception:
524+
sys.exit(0)
525+
# pre-commit's ``rev:`` accepts ``vX.Y[.Z]``, ``X.Y[.Z]``, or full
526+
# SHAs. We compare on the semver-shaped ones; SHA-pinned hooks are
527+
# left alone (the helper returns empty and the caller skips them).
528+
pat = re.compile(r'^v?\d+\.\d+(?:\.\d+)?$')
529+
candidates = []
530+
for entry in data or []:
531+
name = (entry or {}).get('name', '')
532+
if not pat.match(name):
533+
continue
534+
stripped = name.lstrip('v')
535+
parts = stripped.split('.')
536+
try:
537+
nums = tuple(int(p) for p in parts)
538+
except ValueError:
539+
continue
540+
candidates.append((nums, name))
541+
if candidates:
542+
print(max(candidates)[1])
543+
" 2>/dev/null
544+
}
545+

.github/workflows/deps-scan.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@
1414
# via OIDC; falls back gracefully when no creds are configured)
1515
# - Aurora PostgreSQL engine versions (AWS creds via OIDC)
1616
# - EMR Serverless release labels (AWS creds via OIDC)
17-
# - Dockerfile.dev ARG pins (NODE_MAJOR, CDK_VERSION, KUBECTL_VERSION,
18-
# AWSCLI_VERSION, DOCKER_VERSION) — public endpoints, no AWS creds needed
17+
# - Dockerfile.dev ARG pins (NODE_MAJOR, NPM_VERSION, CDK_VERSION,
18+
# KUBECTL_VERSION, AWSCLI_VERSION, DOCKER_VERSION) — public endpoints,
19+
# no AWS creds needed
20+
# - Pre-commit hook revisions in .pre-commit-config.yaml compared against
21+
# the latest tag from the upstream Git host (unauthenticated; one call
22+
# per hook well below the 60 req/h limit)
1923
# - CDK enum constants in gco/stacks/constants.py compared against the
2024
# latest aws-cdk-lib (LAMBDA_PYTHON_RUNTIME, AURORA_POSTGRES_VERSION)
2125
# - Latest stable Python release from endoflife.date

.github/workflows/security.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
# - security:pip-audit:deps — pip-audit on requirements-lock.txt
1717
# - security:semgrep:sast — semgrep scan --config auto
1818
# - security:trivy:filesystem — trivy fs on the source tree
19-
# - security:trivy:container-scan — trivy image on each built service image
19+
# - security:trivy:container-scan — trivy image on each built image
20+
# (4 service images + helm-installer
21+
# + Dockerfile.dev)
2022
# - security:trufflehog:secrets — trufflehog filesystem scan
2123
#
2224
# All scans are strict: if a scan finds issues the job fails. Fix the code,
@@ -170,6 +172,12 @@ jobs:
170172
- image: helm-installer
171173
dockerfile: lambda/helm-installer/Dockerfile
172174
context: lambda/helm-installer
175+
# Dev container — never deployed, but contributors run host-side
176+
# gco/cdk/kubectl commands inside it. Scanning here means a CVE
177+
# in the dev image surfaces on the same PR that introduced the
178+
# bump (rather than waiting for the next weekly cve-scan run).
179+
- image: dev
180+
dockerfile: Dockerfile.dev
173181
steps:
174182
- uses: actions/checkout@v6
175183
- uses: docker/setup-buildx-action@v4

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: v0.15.7
3+
rev: v0.15.13
44
hooks:
55
# Formatter. Run before ``ruff`` so the check sees formatter output.
66
- id: ruff-format
@@ -11,7 +11,7 @@ repos:
1111
args: [--fix, --exclude, lambda/kubectl-applier-simple-build, --exclude, lambda/helm-installer-build]
1212

1313
- repo: https://github.com/pre-commit/mirrors-mypy
14-
rev: v1.19.1
14+
rev: v2.1.0
1515
hooks:
1616
- id: mypy
1717
additional_dependencies: [types-requests, types-PyYAML]

0 commit comments

Comments
 (0)