ci: validate Dockerfile deps stage in PR policy (#1799)

* ci: add Dockerfile deps stage validation to PR policy

Checks that all workspace package.json files and the patches/
directory are copied into the Dockerfile deps stage. Prevents the
Docker build from breaking when new packages or patches are added
without updating the Dockerfile.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* ci: scope Dockerfile check to deps stage and derive workspace roots

Address Greptile review feedback:
- Use awk to extract only the deps stage before grepping, preventing
  false positives from COPY lines in other stages
- Derive workspace search roots from pnpm-workspace.yaml instead of
  hardcoding them, so new top-level workspaces are automatically covered

* ci: guard against empty workspace roots in Dockerfile check

Fail early if pnpm-workspace.yaml parsing yields no search roots,
preventing a silent false-pass from find defaulting to cwd.

* ci: guard against empty deps stage extraction

Fail early with a clear error if awk cannot find the deps stage in the
Dockerfile, instead of producing misleading "missing COPY" errors.

* ci: deduplicate find results from overlapping workspace roots

Use sort -u instead of sort to prevent duplicate error messages when
nested workspace globs (e.g. packages/* and packages/adapters/*) cause
the same package.json to be found twice.

* ci: anchor grep to ^COPY to ignore commented-out Dockerfile lines

Prevents false negatives when a COPY directive is commented out
(e.g. # COPY packages/foo/package.json).

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Devin Foley
2026-03-25 22:42:16 -07:00
committed by GitHub
parent 1a4ed8c953
commit 9e9eec9af6

View File

@@ -40,6 +40,46 @@ jobs:
with:
node-version: 24
- name: Validate Dockerfile deps stage
run: |
missing=0
# Extract only the deps stage from the Dockerfile
deps_stage="$(awk '/^FROM .* AS deps$/{found=1; next} found && /^FROM /{exit} found{print}' Dockerfile)"
if [ -z "$deps_stage" ]; then
echo "::error::Could not extract deps stage from Dockerfile (expected 'FROM ... AS deps')"
exit 1
fi
# Derive workspace search roots from pnpm-workspace.yaml (exclude dev-only packages)
search_roots="$(grep '^ *- ' pnpm-workspace.yaml | sed 's/^ *- //' | sed 's/\*$//' | grep -v 'examples' | grep -v 'create-paperclip-plugin' | tr '\n' ' ')"
if [ -z "$search_roots" ]; then
echo "::error::Could not derive workspace roots from pnpm-workspace.yaml"
exit 1
fi
# Check all workspace package.json files are copied in the deps stage
for pkg in $(find $search_roots -maxdepth 2 -name package.json -not -path '*/examples/*' -not -path '*/create-paperclip-plugin/*' -not -path '*/node_modules/*' 2>/dev/null | sort -u); do
dir="$(dirname "$pkg")"
if ! echo "$deps_stage" | grep -q "^COPY ${dir}/package.json"; then
echo "::error::Dockerfile deps stage missing: COPY ${pkg} ${dir}/"
missing=1
fi
done
# Check patches directory is copied if it exists
if [ -d patches ] && ! echo "$deps_stage" | grep -q '^COPY patches/'; then
echo "::error::Dockerfile deps stage missing: COPY patches/ patches/"
missing=1
fi
if [ "$missing" -eq 1 ]; then
echo "Dockerfile deps stage is out of sync. Update it to include the missing files."
exit 1
fi
- name: Validate dependency resolution when manifests change
run: |
changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")"