#!/usr/bin/env bash # Self-test for pr-touches-paths.py, run by the workflow-hygiene job. # # A gate that decides whether tests run is itself load-bearing: a wrong `false` # silently skips a whole workflow. These cases pin both directions and, most # importantly, that every way of failing fails OPEN. set -euo pipefail cd "$(dirname "$0")/.." G=scripts/pr-touches-paths.py tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT fail=0 check() { # name expected actual if [ "$2" = "$3" ]; then printf ' ok %s\n' "$1" else printf ' FAIL %s: expected %s, got %s\n' "$1" "$2" "$3"; fail=1; fi } # A throwaway repo, so the diffs are real git diffs and not a mock. repo="$tmp/r"; mkdir -p "$repo/wai/wai-pack" "$repo/jcp" git -C "$repo" init -q git -C "$repo" config user.email t@e; git -C "$repo" config user.name t echo x > "$repo/jcp/SPEC.md"; echo y > "$repo/wai/wai-pack/lib.rs" git -C "$repo" add -A; git -C "$repo" commit -qm base BASE=$(git -C "$repo" rev-parse HEAD) echo changed > "$repo/jcp/SPEC.md" git -C "$repo" add -A; git -C "$repo" commit -qm jcp-only JCP=$(git -C "$repo" rev-parse HEAD) echo changed > "$repo/wai/wai-pack/lib.rs" git -C "$repo" add -A; git -C "$repo" commit -qm wai-too WAI=$(git -C "$repo" rev-parse HEAD) wf="$tmp/w.yml" cat > "$wf" <<'Y' on: pull_request: paths: - 'wai/wai-pack/**' Y run() { ( cd "$repo" && python3 "$OLDPWD/$G" "$1" "$2" "$3" ); } check "a jcp-only diff does not touch a wai filter" false "$(run "$wf" "$BASE" "$JCP")" check "a diff that touches the filter runs" true "$(run "$wf" "$BASE" "$WAI")" # Every failure mode must fail OPEN. check "a missing workflow file" true "$(run "$tmp/nope.yml" "$BASE" "$JCP")" check "a workflow with no filter" true "$(printf 'on:\n pull_request:\n' > "$tmp/nofilter.yml"; run "$tmp/nofilter.yml" "$BASE" "$JCP")" check "an empty base sha" true "$(run "$wf" "" "$JCP")" check "a sha git cannot resolve" true "$(run "$wf" "$BASE" deadbeefdeadbeefdeadbeefdeadbeefdeadbeef)" check "too few arguments" true "$( cd "$repo" && python3 "$OLDPWD/$G" "$wf" )" check "an empty diff" true "$(run "$wf" "$JCP" "$JCP")" # `**` spans separators; `*` does not. cat > "$tmp/star.yml" <<'Y' on: pull_request: paths: - 'jcp/*.md' Y # Only the NESTED file changes, so `jcp/*.md` must not match it. (A top-level # .md in the same commit would match legitimately and prove nothing — which is # how this case failed the first time it was written.) mkdir -p "$repo/jcp/sub"; echo z > "$repo/jcp/sub/x.md" git -C "$repo" add -A >/dev/null; git -C "$repo" commit -qm sub >/dev/null SUB=$(git -C "$repo" rev-parse HEAD) check "a single star does not span a separator" false "$(run "$tmp/star.yml" "$WAI" "$SUB")" # And it does match a file at that level, so the glob is not simply never matching. echo z > "$repo/jcp/top.md" git -C "$repo" add -A >/dev/null; git -C "$repo" commit -qm top >/dev/null check "a single star matches at its own level" true "$(run "$tmp/star.yml" "$SUB" "$(git -C "$repo" rev-parse HEAD)")" # A comment or a blank line inside a list does not end it, and a trailing comment is not # part of a glob. (Lists with a comment used to be read only up to it, so a pull request # touching a path after the comment was answered `false` and its tests skipped.) cat > "$tmp/commented.yml" <<'Y' on: pull_request: paths: - 'jcp/nothing-here/**' # a comment between entries - 'wai/wai-pack/**' # and one after an entry Y check "a path listed after a comment and a blank line" true "$(run "$tmp/commented.yml" "$BASE" "$WAI")" check "and the list still says false for a diff outside it" false "$(run "$tmp/commented.yml" "$BASE" "$JCP")" # --paths-from and --wide: the files of a directory bring their own paths, and the wide glob # the forge filters on stands for them alone. A step's file (`# paths:` in its header) names # one extension text; a change to it runs the workflow, and a change to an extension text no # file names does not, though the forge's wide glob matches both. mkdir -p "$repo/wai/extensions" "$repo/steps" echo a > "$repo/wai/extensions/owned.md"; echo b > "$repo/wai/extensions/other.md" printf '# step: one\n# order: 10\n# paths: wai/extensions/owned.md\n#\n# prose: not a header\necho ok\n' > "$repo/steps/one.sh" printf '# step: two\n# paths: wai/extensions/second/**\necho ok\n# paths: wai/extensions/other.md\n' > "$repo/steps/two.sh" git -C "$repo" add -A >/dev/null; git -C "$repo" commit -qm steps >/dev/null S0=$(git -C "$repo" rev-parse HEAD) echo changed > "$repo/wai/extensions/owned.md"; git -C "$repo" commit -qam owned >/dev/null; OWN=$(git -C "$repo" rev-parse HEAD) echo changed > "$repo/wai/extensions/other.md"; git -C "$repo" commit -qam other >/dev/null; OTH=$(git -C "$repo" rev-parse HEAD) mkdir -p "$repo/wai/extensions/second"; echo c > "$repo/wai/extensions/second/x.md" git -C "$repo" add -A >/dev/null; git -C "$repo" commit -qm second >/dev/null; SEC=$(git -C "$repo" rev-parse HEAD) cat > "$tmp/wide.yml" <<'Y' on: pull_request: paths: - 'wai/wai-pack/**' - 'wai/extensions/**' Y wide() { ( cd "$repo" && python3 "$OLDPWD/$G" "$tmp/wide.yml" "$1" "$2" --paths-from steps --wide 'wai/extensions/**' ); } check "a path a step's file names runs the workflow" true "$(wide "$S0" "$OWN")" check "an extension text no file names does not, under the wide glob" false "$(wide "$OWN" "$OTH")" check "a glob a file names matches below it" true "$(wide "$OTH" "$SEC")" check "the workflow's own globs still count" true "$(wide "$BASE" "$WAI")" check "without the flags the wide glob matches everything under it" true "$( cd "$repo" && python3 "$OLDPWD/$G" "$tmp/wide.yml" "$OWN" "$OTH" )" check "a header line after the header is not read" false "$(printf '# step: two\necho ok\n# paths: wai/extensions/other.md\n' > "$repo/steps/two.sh"; wide "$OWN" "$OTH")" # Every way of misreading them fails OPEN. check "a --paths-from directory that does not exist" true "$( cd "$repo" && python3 "$OLDPWD/$G" "$tmp/wide.yml" "$OWN" "$OTH" --paths-from nowhere --wide 'wai/extensions/**' )" check "a --wide glob the workflow does not list" true "$( cd "$repo" && python3 "$OLDPWD/$G" "$tmp/wide.yml" "$OWN" "$OTH" --paths-from steps --wide 'wai/nothing/**' )" check "a --wide glob with no --paths-from" true "$( cd "$repo" && python3 "$OLDPWD/$G" "$tmp/wide.yml" "$OWN" "$OTH" --wide 'wai/extensions/**' )" check "an argument the script does not read" true "$( cd "$repo" && python3 "$OLDPWD/$G" "$tmp/wide.yml" "$OWN" "$OTH" --what x )" [ "$fail" = 0 ] && echo "pr-touches-paths self-test: all cases pass" || { echo "pr-touches-paths self-test FAILED"; exit 1; }