Ctrl K
ComunidadGuías…

hack/downstream-trigger-map.bats

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bats
# Guards the "Downstream Repositories" trigger map in docs/agents/contributing.md.
#
# The map tells a contributor which change in this repo forces a follow-up in a
# satellite repository. Each trigger names a file on this side: hack/update-crd.sh,
# packages/core/platform/values.yaml, and so on. Rename one of those and the map
# keeps confidently pointing at a path that no longer exists, from inside the PR
# template, to every reader. The repository list is deliberately not restated here
# — it would be one more copy to drift; the map and the PR template are the two
# sources, and the tests below hold them to each other.
#
# The three tests pin, in order: every in-repo path the map cites to a file that
# exists; the workflow guarantee that Bats contracts run on every docs-only PR;
# and the map and the PR-template checklist to the same repository list, so a
# repository cannot be listed in one and forgotten in the other.
#
# WHAT THIS DOES NOT CHECK, so nobody mistakes a green tick for a correct map:
#
#   * Whether a trigger is TRUE. "Change X here and Y breaks over there" is a
#     claim about a satellite repo. Verifying it needs that repo checked out at
#     a known ref, which this suite deliberately does not do: a unit test must
#     not depend on the network or on six sibling clones. A trigger can cite a
#     path that exists here and still describe a coupling that is nonsense.
#   * Anything on the satellite side. A file named there (its Makefile, its
#     scripts/, its playbooks) is not reachable from this repo and is not
#     validated. Those names are exactly where the map is most likely to rot.
#   * Whether the map is COMPLETE. Deleting a whole trigger, or a whole block of
#     them, passes: the repository-list test compares section headings, not bodies.
#   * A path written without backticks. Extraction keys off `code spans`, which
#     is the section's convention; a lone unquoted path slips through. Stripping
#     the backticks wholesale does fail, via the anti-vacuum guard below.
#
# In short: this catches a rename on THIS side, which is the cheap half. The
# expensive half — is the coupling real, and is it still real over there — stays
# a human's job, and reviewers should not assume CI did it for them.
#
# A docs-only PR skips builds and E2E, but the `checks` job still runs the Bats
# lane. Otherwise editing this map, or another document audited by a unit
# contract, would exempt the changed input from its own guard.

load test_helper

REPO_ROOT="$(cd "$(dirname "${BATS_TEST_FILENAME:-$0}")/.." && pwd)"
MAP_FILE="$REPO_ROOT/docs/agents/contributing.md"
TEMPLATE_FILE="$REPO_ROOT/.github/PULL_REQUEST_TEMPLATE.md"

# Body of the "## Downstream Repositories" section, up to the next h2.
map_section() {
  awk '/^## Downstream Repositories$/ { inside = 1; next }
       /^## / { inside = 0 }
       inside' "$MAP_FILE"
}

@test "every in-repo path cited by the downstream trigger map exists" {
  [ -f "$MAP_FILE" ] || { echo "missing $MAP_FILE" >&2; exit 1; }

  section="$(map_section)"
  [ -n "$section" ] || { echo "Could not locate the '## Downstream Repositories' section in $MAP_FILE" >&2; exit 1; }

  # Backticked tokens rooted at a top-level directory of THIS repo. Satellite
  # paths cited by the map (scripts/package.mk, content/en/docs/...) do not match
  # these roots and so fall out. Note this is a heuristic, not a guarantee: a
  # satellite repo has its own packages/ tree, so a backticked path from over
  # there that happens to share a root would be checked against this tree.
  # A trailing slash is prose, not a distinct path: `packages/apps/` and
  # `packages/apps` are the same anchor and would otherwise both inflate the count.
  paths="$(printf '%s\n' "$section" \
    | grep -o '`[^`]*`' \
    | tr -d '`' \
    | grep -E '^(hack|packages|cmd|api|internal|pkg|\.github)/' \
    | sed 's#/$##' \
    | sort -u)"

  # Guard against a vacuous pass: if the extraction breaks, or the section is
  # reworded so it no longer cites paths, this test must fail loudly rather than
  # silently verify nothing. The floor sits well under the real count (28 as of
  # writing) because the map legitimately shrinks too, and a red build should not
  # send the reader hunting for a bug that is really a deleted section.
  count="$(printf '%s\n' "$paths" | grep -c . || true)"
  [ "$count" -ge 20 ] || {
    echo "Extracted only $count in-repo path(s) from the trigger map; expected at least 20." >&2
    echo "Either the extraction broke and this test is verifying nothing, or the map genuinely" >&2
    echo "shrank - if a downstream repository went away, lower this floor deliberately." >&2
    exit 1
  }

  missing=""
  for path in $paths; do
    # A placeholder segment (<name>) stands for any package, so match it as a glob.
    pattern="$(printf '%s' "$path" | sed 's/<[^>]*>/*/g')"
    # Unquoted on purpose: the shell must expand the glob. No match => ls fails.
    if ! (cd "$REPO_ROOT" && ls -d $pattern) >/dev/null 2>&1; then
      missing="$missing $path"
    fi
  done

  if [ -n "$missing" ]; then
    echo "The downstream trigger map in docs/agents/contributing.md cites paths that do not exist:" >&2
    for path in $missing; do echo "  - $path" >&2; done
    echo "Fix: update the map to the new path, or drop the trigger if it no longer applies." >&2
    echo "The map is read by contributors from inside the PR template; a stale path there sends them to a dead end." >&2
    exit 1
  fi

  echo "All $count in-repo path(s) cited by the trigger map exist"
}

@test "the workflow runs Bats contracts for docs-only changes" {
  WORKFLOW="$REPO_ROOT/.github/workflows/pull-requests.yaml"
  [ -f "$WORKFLOW" ] || { echo "missing $WORKFLOW" >&2; exit 1; }

  job_if=$(yq -r '.jobs.checks.if' "$WORKFLOW")
  code_if=$(yq -r '.jobs.checks.steps[] | select(.name == "Run unit and controller tests") | .if' "$WORKFLOW")
  docs_if=$(yq -r '.jobs.checks.steps[] | select(.name == "Run Bats unit tests for docs-only changes") | .if' "$WORKFLOW")
  docs_run=$(yq -r '.jobs.checks.steps[] | select(.name == "Run Bats unit tests for docs-only changes") | .run' "$WORKFLOW")
  code_run=$(yq -r '.jobs.checks.steps[] | select(.name == "Run unit and controller tests") | .run' "$WORKFLOW")

  [ "$job_if" = "!contains(github.event.pull_request.labels.*.name, 'release')" ]
  [ "$code_if" = "needs.plan.outputs.code == 'true'" ]
  [ "$docs_if" = "needs.plan.outputs.code == 'false'" ]
  [ "$docs_run" = 'make bats-unit-tests' ]
  [ "$code_run" = 'make unit-tests test-controllers test-controllers-envtest -j4 -k --output-sync=target' ]
}

@test "the trigger map and the PR-template checklist list the same repositories" {
  [ -f "$TEMPLATE_FILE" ] || { echo "missing $TEMPLATE_FILE" >&2; exit 1; }

  # Checklist lines look like: - [ ] [cozystack/website](https://...) - follow-up:
  template_repos="$(grep -oE '^- \[ \] \[cozystack/[a-z0-9.-]+\]' "$TEMPLATE_FILE" \
    | grep -oE 'cozystack/[a-z0-9.-]+' \
    | sort -u)"

  # Map headings look like: ### cozystack/website
  map_repos="$(map_section \
    | grep -oE '^### cozystack/[a-z0-9.-]+' \
    | grep -oE 'cozystack/[a-z0-9.-]+' \
    | sort -u)"

  template_count="$(printf '%s\n' "$template_repos" | grep -c . || true)"
  [ "$template_count" -ge 1 ] || { echo "Found no cozystack/* checklist entries in $TEMPLATE_FILE - extraction is broken" >&2; exit 1; }

  if [ "$template_repos" != "$map_repos" ]; then
    echo "The PR-template checklist and the trigger map disagree on which repositories are downstream." >&2
    echo "In the checklist (.github/PULL_REQUEST_TEMPLATE.md):" >&2
    printf '%s\n' "$template_repos" | sed 's/^/  /' >&2
    echo "In the map (docs/agents/contributing.md, '### cozystack/<repo>' headings):" >&2
    printf '%s\n' "$map_repos" | sed 's/^/  /' >&2
    echo "Fix: add the repository to both, or remove it from both." >&2
    echo "A repository listed in only one of them gets ticked with no guidance, or documented with no box to tick." >&2
    exit 1
  fi

  echo "Checklist and trigger map agree on $template_count repositories"
}