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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,9 @@ jobs:
- name: Run smoke-gate self-test
run: bash scripts/test-wait-for-healthy.sh

- name: Run Terraform output reader self-test
run: bash scripts/test-read-tf-outputs.sh

# Execute database-migration.yml's down-migration gate (#68): the gate script
# over adversarial inputs, then the workflow's own validate and migrate-* run
# blocks extracted from the YAML with a stub `migrate`, so a request that
Expand Down
29 changes: 17 additions & 12 deletions .github/workflows/deploy-azure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -282,16 +282,13 @@ jobs:
cd terraform/environments/azure
terraform apply -auto-approve tfplan 2>&1 | tee "${RUNNER_TEMP}/tf-apply.log"

# Get app URL
APP_URL=$(terraform output -raw container_app_url 2>/dev/null | grep -v '::' || echo "")
echo "app_url=$APP_URL" >> "$GITHUB_OUTPUT"

# Get app name
APP_NAME=$(terraform output -raw container_app_name 2>/dev/null | grep -v '::' || echo "")
echo "app_name=$APP_NAME" >> "$GITHUB_OUTPUT"

RESOURCE_GROUP_NAME=$(terraform output -raw resource_group_name 2>/dev/null | grep -v '::' || echo "")
echo "resource_group_name=$RESOURCE_GROUP_NAME" >> "$GITHUB_OUTPUT"
# A missing or unreadable output fails this step with terraform's own
# error, instead of passing an empty value on to test-deployment
# (#438). The script still drops setup-terraform's `::debug::` lines.
bash "${GITHUB_WORKSPACE}/scripts/read-tf-outputs.sh" \
app_url=container_app_url \
app_name=container_app_name \
resource_group_name=resource_group_name

# The provider fails the data read outright when the custom role is absent,
# so a Terraform precondition can never run for this case. Issue #1794: the
Expand Down Expand Up @@ -413,6 +410,7 @@ jobs:

steps:
- name: Azure Login
id: azure-login
uses: azure/login@532459ea530d8321f2fb9bb10d1e0bcf23869a43 # v3.0.0
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
Expand Down Expand Up @@ -472,14 +470,21 @@ jobs:
HEALTH_INTERVAL_SECONDS: "5"
run: bash scripts/wait-for-healthy.sh "$APP_URL"

# Runs when an earlier step failed too (#438): a failed readiness or smoke
# gate is exactly when the logs are needed. It still needs a working
# Azure login. Missing outputs only warn here, because build-and-deploy
# now fails at the source when it cannot read them, so this job never
# starts without them; failing here would only bury the real error. An
# az lookup error still fails the step (#217).
- name: Check Container App logs
if: always() && steps.azure-login.outcome == 'success'
env:
APP_NAME: ${{ needs.build-and-deploy.outputs.app_name }}
RESOURCE_GROUP_NAME: ${{ needs.build-and-deploy.outputs.resource_group_name }}
run: |
if [ -z "$APP_NAME" ] || [ -z "$RESOURCE_GROUP_NAME" ]; then
echo "::error::Terraform outputs missing: app_name='$APP_NAME' resource_group_name='$RESOURCE_GROUP_NAME'"
exit 1
echo "::warning::No Container App to read logs from: app_name='$APP_NAME' resource_group_name='$RESOURCE_GROUP_NAME'"
exit 0
fi
echo "Recent Container App logs:"
az containerapp logs show \
Expand Down
91 changes: 91 additions & 0 deletions scripts/read-tf-outputs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env bash
# read-tf-outputs.sh KEY=OUTPUT_NAME [KEY=OUTPUT_NAME ...]
#
# Reads Terraform outputs from the working directory with
# `terraform output -raw OUTPUT_NAME` and appends them to $GITHUB_OUTPUT as
# KEY=value step outputs. Run it from the Terraform root module directory,
# after the apply.
#
# It fails loudly, at the source, instead of handing an empty value to a later
# job (#438). The inline form it replaces in deploy-azure.yml was
#
# APP_URL=$(terraform output -raw container_app_url 2>/dev/null | grep -v '::' || echo "")
#
# which hid terraform's error message and turned any failure (missing output,
# state read error, renamed output) into an empty step output. The failure then
# surfaced jobs later, as "Failed to get app URL" or "Terraform outputs
# missing", far from its cause.
#
# Wrapper noise: hashicorp/setup-terraform installs a `terraform` wrapper that
# writes `::debug::` workflow-command lines to stdout next to the real output
# (3d0d9dee). Lines containing `::` are dropped, as before; what is left must
# be exactly one non-empty line. Anything else (nothing left, or several lines)
# is an error that prints what terraform returned, rather than a guess.
#
# Every output is read before anything is written, so a failure leaves
# $GITHUB_OUTPUT untouched rather than half-written.
#
# Exit codes:
# 0 every output was read and written
# 1 terraform failed, or an output was empty or not a single line
# 2 usage error (no arguments, malformed KEY=OUTPUT_NAME, GITHUB_OUTPUT unset)

set -euo pipefail

usage() {
echo "usage: $(basename "$0") KEY=OUTPUT_NAME [KEY=OUTPUT_NAME ...]" >&2
echo " appends KEY=<terraform output -raw OUTPUT_NAME> to \$GITHUB_OUTPUT" >&2
exit 2
}

[[ $# -ge 1 ]] || usage
if [[ -z "${GITHUB_OUTPUT:-}" ]]; then
echo "::error::GITHUB_OUTPUT is not set" >&2
exit 2
fi

ident='^[A-Za-z_][A-Za-z0-9_]*$'
for pair in "$@"; do
key="${pair%%=*}"
name="${pair#*=}"
if [[ "$pair" != *=* || ! "$key" =~ $ident || ! "$name" =~ $ident ]]; then
echo "::error::malformed argument '${pair}', expected KEY=OUTPUT_NAME" >&2
exit 2
fi
done

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT

lines=()
for pair in "$@"; do
key="${pair%%=*}"
name="${pair#*=}"

# stderr is left alone so terraform's own error message reaches the log.
if ! terraform output -raw "$name" > "$tmp/raw"; then
echo "::error::terraform output -raw ${name} failed; see the terraform error above" >&2
exit 1
fi

# `|| true`: grep exits 1 when it selects nothing, which is judged below.
# grep also terminates the last line, which -raw leaves without a newline.
grep -v '::' "$tmp/raw" | grep -v '^[[:space:]]*$' > "$tmp/value" || true
count=$(wc -l < "$tmp/value")

if ((count != 1)); then
if ((count == 0)); then
reason="is empty"
else
reason="has ${count} lines, expected one"
fi
echo "::error::terraform output ${name} ${reason}. Raw stdout:" >&2
sed 's/^/ | /' "$tmp/raw" >&2
exit 1
fi

lines+=("${key}=$(cat "$tmp/value")")
echo "Read terraform output ${name} into ${key}"
done

printf '%s\n' "${lines[@]}" >> "$GITHUB_OUTPUT"
Loading
Loading