name: Lint curl status-code capture

# Pins the workflow-bash anti-pattern that produced "HTTP 000000" on the
# 2026-05-04 redeploy-tenants-on-main run for sha 2b862f6:
#
#   HTTP_CODE=$(curl ... -w '%{http_code}' ... || echo "000")
#
# When curl exits non-zero (connection reset → 56, --fail-with-body 4xx/5xx
# → 22), the `-w '%{http_code}'` already wrote a status to stdout — usually
# "000" for connection failures or the actual code for HTTP errors. The
# `|| echo "000"` then fires AND appends ANOTHER "000" to the captured
# stdout, producing values like "000000" or "409000" that fail string
# comparisons against "200" while looking superficially right.
#
# Same class of bug the synth-E2E §7c gate hit twice (PRs #2779/#2783 +
# #2797). Memory: feedback_curl_status_capture_pollution.md.
#
# Fix shape (route -w into a tempfile so curl's exit code can't pollute):
#
#   set +e
#   curl ... -w '%{http_code}' >code.txt 2>/dev/null
#   set -e
#   HTTP_CODE=$(cat code.txt 2>/dev/null)
#   [ -z "$HTTP_CODE" ] && HTTP_CODE="000"

on:
  pull_request:
    paths: ['.github/workflows/**']
  push:
    branches: [main, staging]
    paths: ['.github/workflows/**']
  merge_group:
    types: [checks_requested]

jobs:
  scan:
    name: Scan workflows for curl status-capture pollution
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
      - name: Find curl ... -w '%{http_code}' ... || echo "000" subshells
        run: |
          set -uo pipefail
          # Multi-line aware: look for `$(curl ... -w '%{http_code}' ... || echo "000")`
          # subshell where the entire command-substitution wraps a curl that
          # ends with `|| echo "000"`. Must distinguish from the SAFE shape
          # `$(cat tempfile 2>/dev/null || echo "000")` — `cat` with a missing
          # tempfile produces empty stdout, no pollution.
          python3 <<'PY'
          import os, re, sys, glob

          BAD_FILES = []

          # Match the buggy substitution across newlines: $(curl ... -w '%{http_code}' ... || echo "000")
          # The `\\n` is the bash line-continuation that lets curl flags span lines.
          # We collapse continuation lines first, then look for the single-line bad pattern.
          PATTERN = re.compile(
              r'\$\(\s*curl\b[^)]*-w\s*[\'"]%\{http_code\}[\'"][^)]*\|\|\s*echo\s+"000"\s*\)',
              re.DOTALL,
          )

          # Self-skip: this lint workflow contains the literal anti-pattern in
          # its own docstring — that's intentional, not a bug.
          SELF = ".github/workflows/lint-curl-status-capture.yml"

          for f in sorted(glob.glob(".github/workflows/*.yml")):
              if f == SELF:
                  continue
              with open(f) as fh:
                  content = fh.read()
              # Collapse bash line-continuations (\\\n + leading whitespace)
              # into a single logical line so the regex can see the full
              # curl invocation as one chunk.
              flat = re.sub(r'\\\s*\n\s*', ' ', content)
              for m in PATTERN.finditer(flat):
                  BAD_FILES.append((f, m.group(0)[:120]))

          if not BAD_FILES:
              print("✓ No curl-status-capture pollution patterns detected")
              sys.exit(0)

          print(f"::error::Found {len(BAD_FILES)} curl-status-capture pollution site(s):")
          for f, snippet in BAD_FILES:
              print(f"::error file={f}::Curl status-capture pollution: '|| echo \"000\"' inside a $(curl ... -w '%{{http_code}}' ...) subshell. On non-2xx or connection failure, curl's -w writes a status, then exits non-zero, then the || echo appends another '000' — producing 'HTTP 000000' or '409000' that fails comparisons silently. Fix: route -w into a tempfile so the exit code can't pollute stdout. See memory feedback_curl_status_capture_pollution.md.")
              print(f"   matched: {snippet}…")
          print()
          print("Fix template:")
          print('  set +e')
          print('  curl ... -w \'%{http_code}\' >code.txt 2>/dev/null')
          print('  set -e')
          print('  HTTP_CODE=$(cat code.txt 2>/dev/null)')
          print('  [ -z "$HTTP_CODE" ] && HTTP_CODE="000"')
          sys.exit(1)
          PY
