mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-03 00:17:47 +00:00
fix(ci): reviewer loop 6 — REV12 compose image parsing, REV13 fork bypass, REV14 :full audit (#1278)
* fix(ci): breaking-change-guard handles \${VAR:-default} compose image syntax (REV12)
The sed 's/:.*//' pattern truncated at the first colon in the
\${CCS_IMAGE:-ghcr.io/...} expression, yielding "\${CCS_IMAGE" as the
image name instead of the actual registry path. Any change to the default
image namespace was therefore undetectable.
Introduce extract_image_name() that first strips the \${VAR:-default}
wrapper with a sed -E expression, then strips only the trailing :tag
suffix using a pattern that preserves internal colons (e.g. registry:5000/
owner/repo). Applied to both OLD_RAW and NEW_RAW extraction paths.
* fix(ci): breaking-change-guard runs on ubuntu-latest to cover forked PRs (REV13)
The trusted-author gate (COLLABORATOR|MEMBER|OWNER) caused forked-PR
contributors to bypass the breaking-change check entirely. A forked
contributor could rename services.ccs or change the image namespace
without a feat!/fix! marker and the guard would never run.
This workflow is a documented exception to the self-hosted-first policy:
it performs ONLY pure YAML diff parsing (git show / awk / sed). No build,
install, or arbitrary PR-branch scripts are executed. The checkout uses
persist-credentials: false. There is no untrusted code execution, so
ubuntu-latest is safe and necessary for universal fork coverage.
Update self-hosted-runner-policy.test.ts to:
- Introduce GITHUB_HOSTED_RUNNER_EXCEPTIONS registry with required
justification comments for each entry
- Skip exception workflows in the "keeps active workflows on local runners"
and "gates pull-request workflows" assertions
- Add a new "documented exceptions use github-hosted runners" test that
verifies each exception entry actually uses a GitHub-hosted runner
(prevents stale entries accumulating without cleanup)
This commit is contained in:
@@ -22,9 +22,17 @@ on:
|
||||
jobs:
|
||||
guard:
|
||||
name: Verify breaking changes are intentional
|
||||
if: >-
|
||||
contains(fromJSON('["COLLABORATOR","MEMBER","OWNER"]'), github.event.pull_request.author_association)
|
||||
runs-on: [self-hosted, linux, x64, cliproxy]
|
||||
# Exception to self-hosted-first policy (documented in CLAUDE.md "Self-Hosted Runner Policy"):
|
||||
# This workflow MUST cover ALL PRs including forks — a forked contributor can rename
|
||||
# services.ccs or change the image namespace without a breaking-change marker, which
|
||||
# silently breaks sibling-container setups for every user. Gating on trusted-author
|
||||
# association would let forked PRs bypass the check entirely.
|
||||
#
|
||||
# Safety justification: this workflow does ONLY pure YAML diff parsing (git show / git diff
|
||||
# + shell + awk). It checks out the code with persist-credentials: false and runs no build,
|
||||
# install, or arbitrary scripts from the PR branch. There is no untrusted code execution,
|
||||
# so running on a GitHub-hosted runner is safe and required for universal coverage.
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout PR branch
|
||||
@@ -54,10 +62,26 @@ jobs:
|
||||
BREAKING=0
|
||||
|
||||
# 1. Image name change (repo path, not tag — tags change every release)
|
||||
OLD_IMAGE=$(git show "${BASE}:docker/compose.yaml" \
|
||||
| grep -m1 '^\s*image:' | sed 's/.*image:\s*//' | sed 's/:.*//' | tr -d ' ')
|
||||
NEW_IMAGE=$(grep -m1 '^\s*image:' docker/compose.yaml \
|
||||
| sed 's/.*image:\s*//' | sed 's/:.*//' | tr -d ' ')
|
||||
#
|
||||
# compose.yaml uses ${CCS_IMAGE:-ghcr.io/kaitranntt/ccs:latest}. A naive
|
||||
# sed 's/:.*//' truncates at the FIRST colon, yielding "${CCS_IMAGE" instead
|
||||
# of the actual image name. We strip the ${VAR:-default} wrapper first, then
|
||||
# strip only the trailing :tag suffix (preserving internal colons such as
|
||||
# those in registry:port/owner/repo).
|
||||
extract_image_name() {
|
||||
# $1 = raw image line content (everything after "image: ")
|
||||
local raw="$1"
|
||||
# Strip ${VAR:-default} wrapper if present
|
||||
raw=$(printf '%s' "$raw" | sed -E 's/^\$\{[A-Za-z_][A-Za-z0-9_]*:-//; s/\}$//')
|
||||
# Strip only the trailing :tag — preserve internal colons (e.g. registry:5000/owner/repo)
|
||||
printf '%s' "$raw" | sed 's|:[^:/]*$||'
|
||||
}
|
||||
OLD_RAW=$(git show "${BASE}:docker/compose.yaml" \
|
||||
| grep -m1 '^\s*image:' | sed 's/.*image:\s*//' | tr -d ' ')
|
||||
NEW_RAW=$(grep -m1 '^\s*image:' docker/compose.yaml \
|
||||
| sed 's/.*image:\s*//' | tr -d ' ')
|
||||
OLD_IMAGE=$(extract_image_name "$OLD_RAW")
|
||||
NEW_IMAGE=$(extract_image_name "$NEW_RAW")
|
||||
if [[ "${OLD_IMAGE}" != "${NEW_IMAGE}" ]]; then
|
||||
echo "[!] BREAKING: image name changed: '${OLD_IMAGE}' -> '${NEW_IMAGE}'"
|
||||
BREAKING=1
|
||||
|
||||
Reference in New Issue
Block a user