ci: thirteen WAI workflows run only when the pull request's own diff touches them #252

Merged
dcharlot merged 2 commits from ci/gate-on-pr-diff into main 2026-10-14 10:09:41 -04:00
15 changed files with 395 additions and 4 deletions

View file

@ -35,7 +35,32 @@ on:
- '.github/workflows/conformance.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/conformance.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches conformance: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
validate-json:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: Validate conformance JSON files
runs-on: linux
steps:
@ -110,6 +135,8 @@ jobs:
# language, so a mistake shared by the generator and its own verifier still
# fails here. Standard library only: nothing to install beyond python3.
crosscheck:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: Second implementation reproduces every vector
runs-on: linux
steps:
@ -130,9 +157,10 @@ jobs:
run: python3 smart-byte/conformance/crosscheck/verify_v1.py smart-byte/conformance/v1
spec-vector-coupling:
needs: relevant
if: needs.relevant.outputs.run == 'true' && (github.event_name == 'pull_request')
name: Spec changes require vector-pack updates
runs-on: linux
if: github.event_name == 'pull_request'
steps:
- name: Checkout (with full history for diff)
uses: actions/checkout@v4
@ -176,6 +204,8 @@ jobs:
echo "coupling check passed (spec_touched=$spec_touched, conformance_touched=$conformance_touched)"
versions-present:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: VERSIONS.md and README.md must exist
runs-on: linux
steps:

View file

@ -112,7 +112,32 @@ on:
- '.github/workflows/wai-byte-equality.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-byte-equality.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-byte-equality: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
byte-equality:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: ${{ matrix.label }}
runs-on: ${{ matrix.os }}
strategy:

View file

@ -116,7 +116,32 @@ on:
# their pwsh steps stay, inactive, so restoring a leg is a one-line change.
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-conformance.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-conformance: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
cargo-classes:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: cargo-class corpora · ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
@ -530,6 +555,8 @@ jobs:
# One leg: wasm32 is the same machine on every host. Linux, so it does not queue on the
# single macOS runner.
quantum-wasip2:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: quantum core and corpus under wasm32-wasip2 (wasmtime)
runs-on: linux
# The fabric's planner test runs fabric-v1 under every planner configuration in a
@ -675,6 +702,8 @@ jobs:
# rejected; a media value no revision lists is accepted). One leg: RDF tooling,
# not arithmetic. The tool versions are pinned.
vocab-shapes:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: v2 vocabulary · manifests validate as stated
runs-on: linux
steps:
@ -703,6 +732,8 @@ jobs:
# Each exits non-zero on any failure or any difference, and the step fails with it.
# One platform: what they check is the reading of the text, not the architecture.
independent-verifiers:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: independent verifiers · claims, JWP, key discovery and integer payloads (Python)
runs-on: linux
steps:
@ -861,6 +892,8 @@ jobs:
# the expected strings back as dictionary, query and header shards, and validates every
# report with no error. One platform: string encoding, not arithmetic.
telemetry-differential:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: delivery telemetry · CMCD and CMSD against an independent library
runs-on: linux
steps:
@ -877,6 +910,8 @@ jobs:
node differential.mjs ..
byte-exact:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: cross-arch byte-equality · ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
@ -922,6 +957,8 @@ jobs:
# is how the wrappers treat hostile bytes and where their limits fall, which is the
# same code on every host. A missing library fails the build step by name.
codec-wrappers:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path
runs-on: macos
steps:
@ -1000,6 +1037,8 @@ jobs:
# this job proves is that the manifest is still well-formed, still carries the three
# assertions, and still validates — which is architecture-independent.
c2pa-emitter:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: C2PA manifest emission · signed, read back, validated
runs-on: linux
steps:

View file

@ -120,7 +120,32 @@ on:
- '.github/workflows/wai-energy-receipts.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-energy-receipts.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-energy-receipts: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
energy-receipts:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: energy receipts · ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:

View file

@ -88,10 +88,35 @@ on:
- '.github/workflows/wai-formal-verification.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-formal-verification.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-formal-verification: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
# Every harness the host compiles runs in exactly one shard of the matrix below:
# one no shard runs would be compiled and never checked. No toolchain is needed;
# the script reads the included sources and this file.
shards:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: every harness in one shard
runs-on: linux
timeout-minutes: 10
@ -106,6 +131,8 @@ jobs:
run: python3 -I wai/wai-verify/check_shards.py .
kani:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: kani (${{ matrix.shard }})
runs-on: ${{ matrix.os }}
timeout-minutes: 120

View file

@ -165,7 +165,32 @@ on:
- cron: '41 6 * * 1'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-jpegai-e2e.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-jpegai-e2e: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
jpegai-e2e:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: fetch pinned JPEG-AI inputs → whole decode + pixel hash, required
runs-on: linux
timeout-minutes: 30

View file

@ -47,7 +47,32 @@ on:
- '.github/workflows/wai-neural-e2e.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-neural-e2e.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-neural-e2e: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
neural-e2e:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: synthetic fixtures → Rust decode → reference (±4)
runs-on: linux
# The Rust decoders link `ort`, whose `download-binaries` feature pulls a

View file

@ -133,7 +133,32 @@ on:
- '.github/workflows/wai-neural-gates.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-neural-gates.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-neural-gates: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
neural-gates:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: model-free refusals of the neural sink
runs-on: linux
# The ONNX Runtime binary the `ort` crate downloads needs glibc 2.38 and a

View file

@ -75,7 +75,32 @@ on:
- '.github/workflows/wai-ops.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-ops.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-ops: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
native:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: ${{ matrix.label }}
runs-on: ${{ matrix.os }}
timeout-minutes: 40
@ -163,6 +188,8 @@ jobs:
--check $C/c6a362696d61605931ef4da98ae630362dcd73ea6be7864db6e61d6c5abbcd24.json
wasip2:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: wasm32-wasip2 (wasmtime)
runs-on: linux
timeout-minutes: 30
@ -215,6 +242,8 @@ jobs:
wasmtime run --dir .::/wai wai-verify/target/wasm32-wasip2/release/wai-verify.wasm vectors verify /wai
node:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: wasm32 under Node
runs-on: linux
timeout-minutes: 30
@ -247,6 +276,8 @@ jobs:
run: node verify.mjs target/wasm32-unknown-unknown/release/wai_ops_node.wasm ../../ops-conformance
checker:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: independent checker (Python)
runs-on: linux
timeout-minutes: 20

View file

@ -89,7 +89,32 @@ on:
- '.github/workflows/wai-quantum-webgpu.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-quantum-webgpu.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-quantum-webgpu: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
checks:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: shader checks · browser build · wasip2 inertness · MSRV
runs-on: linux
timeout-minutes: 60
@ -165,6 +190,8 @@ jobs:
|| { echo "::error::the member's unit test did not run"; exit 1; }
member-software:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: member · software Vulkan (linux)
runs-on: linux
timeout-minutes: 120
@ -205,6 +232,8 @@ jobs:
|| { echo "::error::the meter's device test found no adapter, so it checked nothing"; exit 1; }
member-metal:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: member · Metal (macos)
runs-on: macos
timeout-minutes: 90
@ -242,6 +271,8 @@ jobs:
|| { echo "::error::the tool ran no window on the CPU reference"; exit 1; }
member-hw:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: member · discrete GPU, Vulkan (jetson-hub)
runs-on: linux-hw
timeout-minutes: 150
@ -278,6 +309,8 @@ jobs:
run: CARGO_TARGET_DIR="$HOME/hw-target/wai-rs" nice -n 10 cargo run --locked --no-default-features --features quantum_fabric_webgpu --bin wai_quantum_vectors -- verify ../quantum-conformance --webgpu
browser:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: member · browser WebGPU (macos)
runs-on: macos
timeout-minutes: 60

View file

@ -183,7 +183,32 @@ on:
- cron: '17 6 * * 1'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-sota-vectors.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-sota-vectors: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
sota-vectors:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: regenerate mbt2018_mean integer vectors → Rust decode, required
# The `linux` runner is a docker executor on catthehacker/ubuntu:act-22.04
# (glibc 2.35, no Rust). The Python half of this setup — setup-python 3.11,

View file

@ -83,7 +83,32 @@ on:
- '.github/workflows/wai-vector.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-vector.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-vector: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
native:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: ${{ matrix.label }}
runs-on: ${{ matrix.os }}
timeout-minutes: 40
@ -134,6 +159,8 @@ jobs:
run: cargo build --locked --no-default-features --lib
wasip2:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: wasm32-wasip2 (wasmtime)
runs-on: linux
timeout-minutes: 30
@ -186,6 +213,8 @@ jobs:
wasmtime run --dir .::/wai wai-vector/target/wasm32-wasip2/release/wai-vector.wasm vectors verify /wai
node:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: wasm32 under Node
runs-on: linux
timeout-minutes: 30
@ -218,6 +247,8 @@ jobs:
run: node verify.mjs target/wasm32-unknown-unknown/release/wai_vector_wasm.wasm ../../vector-conformance
browser:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: wasm32 in three browser engines
runs-on: linux
timeout-minutes: 45
@ -299,6 +330,8 @@ jobs:
../../vector-conformance
fuzz:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: fuzz, peak allocation, comparison count
runs-on: linux
timeout-minutes: 30
@ -335,6 +368,8 @@ jobs:
run: bash scripts/check-workflow-concurrency.sh
checker:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: independent checker (Python)
runs-on: linux
timeout-minutes: 30

View file

@ -91,7 +91,32 @@ on:
- '.github/workflows/wai-web-wasm.yml'
jobs:
# Does this pull request's own diff touch the paths below? This forge matches a
# workflow's `paths:` against the push, so a merge of main into a branch fires the
# workflow whatever the pull request changes; every job here waits on this answer.
# A push to main, a schedule, or anything the script cannot determine answers
# `true` (scripts/pr-touches-paths.py).
relevant:
name: does this change touch it
runs-on: linux-arm64
outputs:
run: ${{ steps.gate.outputs.run }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: gate
run: |
set -euo pipefail
r=$(python3 scripts/pr-touches-paths.py .github/workflows/wai-web-wasm.yml \
"${{ github.event.pull_request.base.sha }}" \
"${{ github.event.pull_request.head.sha }}")
echo "this change touches wai-web-wasm: $r"
echo "run=$r" >> "$GITHUB_OUTPUT"
wasm32:
needs: relevant
if: needs.relevant.outputs.run == 'true'
name: wai-web wasm32 build · corpus, menu and writers
runs-on: linux
timeout-minutes: 45

View file

@ -67,4 +67,19 @@ 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")"
[ "$fail" = 0 ] && echo "pr-touches-paths self-test: all cases pass" || { echo "pr-touches-paths self-test FAILED"; exit 1; }

View file

@ -39,12 +39,18 @@ def paths_of(workflow: Path) -> list[str]:
in_paths = True
continue
if in_paths:
m = re.match(r"\s*-\s*'([^']+)'\s*$", line) or re.match(r'\s*-\s*"([^"]+)"\s*$', line)
stripped = line.strip()
# A comment or a blank line inside the list does not end it. (It used to:
# every glob after a list's first comment was dropped, and a pull request
# touching only those paths was answered `false`.)
if not stripped or stripped.startswith("#"):
continue
m = re.match(r"\s*-\s*'([^']+)'\s*(#.*)?$", line) or re.match(r'\s*-\s*"([^"]+)"\s*(#.*)?$', line)
if m:
globs.append(m.group(1))
continue
if line.strip().startswith("- "):
globs.append(line.strip()[2:].strip())
if stripped.startswith("- "):
globs.append(re.sub(r"\s+#.*$", "", stripped[2:]).strip())
continue
in_paths = False
return globs