diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 40e5c18dc..e6eccb8dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -92,3 +92,10 @@ repos: language: system pass_filenames: false always_run: true + + - id: lint-workflow-size + name: lint workflow file size + entry: ./hack/lint-workflow-size + language: script + files: ^internal/scaffold/fullsend-repo/(\.github/workflows/|templates/) + pass_filenames: false diff --git a/hack/lint-workflow-size b/hack/lint-workflow-size new file mode 100755 index 000000000..3c7a6a907 --- /dev/null +++ b/hack/lint-workflow-size @@ -0,0 +1,76 @@ +#!/bin/bash + +# lint-workflow-size - Guard against logic accumulating in workflow YAML files +# +# Workflow files are GitHub-specific glue. Business logic, shell scripts, and +# agent plumbing should live in scripts/ where they can be tested, reused, and +# ported to other forges. +# +# See: +# ADR-0005 Forge abstraction layer +# ADR-0008 workflow_dispatch for cross-repo agent dispatch +# +# Default cap: 200 lines per workflow file. To raise the cap for a specific +# file, add a comment anywhere in the YAML: +# +# # lint-workflow-size: max-lines=350 +# +# This forces an intentional, reviewable decision rather than silent growth. + +set -euo pipefail + +DEFAULT_MAX=200 +errors=0 + +check_files() { + local dir="$1" + + if [[ ! -d "${dir}" ]]; then + return + fi + + for file in "${dir}"/*.yml "${dir}"/*.yaml; do + [[ -e "${file}" ]] || continue + + lines=$(wc -l < "${file}") + filename=$(basename "${file}") + relpath="${file}" + + # Check for per-file override + max="${DEFAULT_MAX}" + override=$(grep -o 'max-lines=[0-9]*' "${file}" 2>/dev/null | grep -o '[0-9]*' | tail -1 || true) + if [[ -n "${override}" ]]; then + max="${override}" + fi + + if [[ "${lines}" -gt "${max}" ]]; then + echo "ERROR: ${relpath} is ${lines} lines (max ${max})" >&2 + echo "" >&2 + echo " Workflow files are GitHub-specific glue — keep logic in scripts/" >&2 + echo " where it can be tested and ported to other forges." >&2 + echo " See ADR-0005 (forge abstraction layer)." >&2 + echo "" >&2 + echo " To fix: extract inline shell into scripts/ and call with" >&2 + echo " run: bash scripts/your-script.sh" >&2 + echo "" >&2 + echo " To raise the cap (if the size is justified), add this comment" >&2 + echo " to the file:" >&2 + echo " # lint-workflow-size: max-lines=" >&2 + echo "" >&2 + errors=$((errors + 1)) + fi + done +} + +# Agent workflow files in the .fullsend repo scaffold +check_files "internal/scaffold/fullsend-repo/.github/workflows" + +# Shim workflow template (deployed to enrolled repos) +check_files "internal/scaffold/fullsend-repo/templates" + +if [[ "${errors}" -gt 0 ]]; then + echo "FAILED: ${errors} workflow file(s) exceed size limit" >&2 + exit 1 +else + echo "OK: All workflow files within size limits" +fi diff --git a/internal/scaffold/fullsend-repo/.github/workflows/fix.yml b/internal/scaffold/fullsend-repo/.github/workflows/fix.yml index 33a02082c..298b02086 100644 --- a/internal/scaffold/fullsend-repo/.github/workflows/fix.yml +++ b/internal/scaffold/fullsend-repo/.github/workflows/fix.yml @@ -1,4 +1,5 @@ # fullsend-stage: fix +# lint-workflow-size: max-lines=310 name: Fix on: diff --git a/internal/scaffold/fullsend-repo/.github/workflows/prioritize-scheduler.yml b/internal/scaffold/fullsend-repo/.github/workflows/prioritize-scheduler.yml index 8592db785..27c65247c 100644 --- a/internal/scaffold/fullsend-repo/.github/workflows/prioritize-scheduler.yml +++ b/internal/scaffold/fullsend-repo/.github/workflows/prioritize-scheduler.yml @@ -1,3 +1,4 @@ +# lint-workflow-size: max-lines=220 name: Prioritize Scheduler on: diff --git a/internal/scaffold/fullsend-repo/templates/shim-workflow.yaml b/internal/scaffold/fullsend-repo/templates/shim-workflow.yaml index c5ff9ee39..e3767087e 100644 --- a/internal/scaffold/fullsend-repo/templates/shim-workflow.yaml +++ b/internal/scaffold/fullsend-repo/templates/shim-workflow.yaml @@ -1,3 +1,4 @@ +# lint-workflow-size: max-lines=535 # fullsend shim workflow # Routes events to agent workflows in .fullsend via the dispatch.yml workflow. #