Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions scripts/linting/lint-summary.sh
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,8 @@ echo "$STACKS_JSON" | jq -r '.[]' | while read -r stack; do
echo ""
# Show filtered errors (remove environment variable warnings)
DOCKER_ERRORS=$(docker compose --env-file "$TEMP_ENV" -f "./$stack/compose.yaml" config 2>&1 | \
grep -v "WARNING.*interpolat" | \
grep -v "WARNING.*environment variable" | \
grep -v "WARNING.*not set" || echo "Configuration errors detected")
grep -viE 'warning.*(interpolat|environment variable|not set)' \
|| echo "Configuration errors detected")

if [[ -n "$DOCKER_ERRORS" && "$DOCKER_ERRORS" != "Configuration errors detected" ]]; then
# shellcheck disable=SC2001 # sed is appropriate for multi-line prefix addition
Expand Down
36 changes: 32 additions & 4 deletions scripts/linting/validate-image-platforms.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,45 @@ echo " Target platforms: ${REQ_PLATFORMS[*]}"
print_separator

TEMP_ENV=$(mktemp)
trap 'rm -f "$TEMP_ENV"' EXIT
IMAGES_OUT=$(mktemp)
IMAGES_ERR=$(mktemp)
trap 'rm -f "$TEMP_ENV" "$IMAGES_OUT" "$IMAGES_ERR"' EXIT
create_temp_env "$COMPOSE_FILE" "$TEMP_ENV"

# `docker compose config --images` emits one fully-resolved image ref per line.
# `docker compose config --images` emits one fully-resolved image ref per line
# on stdout; warnings go to stderr. Capture the two separately and check the
# exit status.
#
# This previously piped straight into the read loop with `2>/dev/null`, which
# discarded the diagnostics AND the exit status: any failure to resolve the
# file yielded an empty list, and the zero-images branch below then reported
# PASS. A check that verified nothing must never report success - that is the
# one way a Compose output change could break this silently rather than loudly.
if ! docker compose --env-file "$TEMP_ENV" -f "$COMPOSE_FILE" config --images \
>"$IMAGES_OUT" 2>"$IMAGES_ERR"; then
log_error "✗ 'docker compose config --images' failed for $COMPOSE_FILE"
echo " Compose could not resolve the file, so no platform check was possible:"
sed 's/^/ /' "$IMAGES_ERR"
exit 1
fi

IMAGES=()
while IFS= read -r line; do
[[ -n "$line" ]] && IMAGES+=("$line")
done < <(docker compose --env-file "$TEMP_ENV" -f "$COMPOSE_FILE" config --images 2>/dev/null | sort -u)
done < <(sort -u "$IMAGES_OUT")

if [[ ${#IMAGES[@]} -eq 0 ]]; then
echo "ℹ️ No images resolved from $COMPOSE_FILE — nothing to check."
# Zero images is legitimate only if the file declares no services at all, or
# only `build:` ones. If it declares services yet resolved no image refs, the
# output is not the shape this script expects - fail loudly instead of
# claiming a pass over an empty set.
if docker compose --env-file "$TEMP_ENV" -f "$COMPOSE_FILE" config --services 2>/dev/null | grep -q .; then
log_error "✗ $COMPOSE_FILE declares services but resolved 0 image refs."
log_error " 'config --images' returned nothing parseable - check whether the"
log_error " Compose output format changed. Refusing to pass an unverified stack."
exit 1
fi
echo "ℹ️ No images resolved from $COMPOSE_FILE — no services declared, nothing to check."
exit 0
fi

Expand Down
31 changes: 22 additions & 9 deletions scripts/linting/validate-stack.sh
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,35 @@ create_temp_env "./$STACK/compose.yaml" "$TEMP_ENV"
(set -o pipefail; docker compose --env-file "$TEMP_ENV" -f "./$STACK/compose.yaml" config 2>&1 | tee "$DOCKER_OUTPUT") &
DOCKER_PID=$!

# Wait for both processes and capture exit codes
wait "$YAML_PID"
YAML_EXIT=$?

wait "$DOCKER_PID"
DOCKER_EXIT=$?
# Wait for both processes and capture exit codes.
#
# `|| VAR=$?` is required, not stylistic: this script runs under `set -e`
# (line 14), so a bare `wait` on a failing child aborts the script right there
# - before the assignment, and before every formatted report below. That made
# the entire summary block unreachable for exactly the case it exists to
# serve: a stack that fails validation printed its raw tee'd output and then
# died silently, with no "Issues found", no fix hint and no overall status.
# The job still failed (non-zero exit), so CI verdicts were always correct -
# only the diagnostics were lost, which is why this went unnoticed.
YAML_EXIT=0
wait "$YAML_PID" || YAML_EXIT=$?

DOCKER_EXIT=0
wait "$DOCKER_PID" || DOCKER_EXIT=$?

# Filter Docker Compose output to remove environment variable warnings but keep real errors
if [ "$DOCKER_EXIT" -eq 0 ]; then
# If Docker Compose succeeded, just copy the output
cp "$DOCKER_OUTPUT" "$DOCKER_FILTERED"
else
# If Docker Compose failed, filter out common environment variable warnings but keep errors
grep -v "WARNING.*interpolat" "$DOCKER_OUTPUT" | \
grep -v "WARNING.*environment variable" | \
grep -v "WARNING.*not set" > "$DOCKER_FILTERED" || cp "$DOCKER_OUTPUT" "$DOCKER_FILTERED"
# Compose 2.x prefixes these `WARNING: ...`; Compose 5.x emits
# `time="..." level=warning msg="..."` instead. Match case-insensitively on
# the bare token `warning`, which is a substring of both spellings, so the
# filter keeps working across the engine versions in use. The `|| cp` retains
# the original behaviour: if filtering removed every line, show the raw output.
grep -viE 'warning.*(interpolat|environment variable|not set)' \
"$DOCKER_OUTPUT" > "$DOCKER_FILTERED" || cp "$DOCKER_OUTPUT" "$DOCKER_FILTERED"
fi

# Cleanup temporary env file
Expand Down