From dfb98fa08ab3c124a4a4ed483037f54379f29ded Mon Sep 17 00:00:00 2001 From: deemwar Date: Thu, 25 Jun 2026 12:28:40 +0530 Subject: [PATCH] ci: self-discovering per-tool test matrix + run-tests.sh harness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The public repo had no CI. This adds a standardized harness and a GitHub Actions workflow so every tool's test suite runs on push + PR. - scripts/run-tests.sh: discover/run/aggregate; pass-fail is each suite's exit code (no output parsing). Tolerant of the test_/test- naming split. - .github/workflows/ci.yml: a self-discovering per-tool matrix (one green check per tool) gated by a single ci-ok status; sets up python3 + a git identity for the few suites that build throwaway repos. Add a tool + test_.sh and CI picks it up — no workflow edit. Verified: scripts/run-tests.sh runs clean locally — 29 tools, 29 green, 0 failed. (The 4 channel-lint tests in PR #1 get auto-covered on merge.) No tool logic changed; purely additive. --- .github/workflows/ci.yml | 66 ++++++++++++++++++++++++++++++++++++++ scripts/run-tests.sh | 69 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100755 scripts/run-tests.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0b2a2cb --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,66 @@ +name: ci + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + # Discover every tool that ships a test suite, so the matrix is self-maintaining: + # add a tool + its test_.sh and CI picks it up with no workflow edit. + discover: + runs-on: ubuntu-latest + outputs: + tools: ${{ steps.list.outputs.tools }} + steps: + - uses: actions/checkout@v4 + - id: list + run: | + tools=$(for d in */; do + d="${d%/}" + [ "$d" = "scripts" ] && continue + if compgen -G "$d/test_*.sh" >/dev/null || compgen -G "$d/test-*.sh" >/dev/null; then + printf '%s\n' "$d" + fi + done | sort | jq -R . | jq -cs .) + echo "tools=$tools" >> "$GITHUB_OUTPUT" + echo "Discovered tools: $tools" + + # One job per tool — a green check per tool inside one shared workflow. + test: + needs: discover + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + tool: ${{ fromJson(needs.discover.outputs.tools) }} + name: test ${{ matrix.tool }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Configure git identity (a few suites build temp repos) + run: | + git config --global user.email "ci@agent-ops.local" + git config --global user.name "agent-ops ci" + git config --global init.defaultBranch main + - name: Test ${{ matrix.tool }} + run: bash scripts/run-tests.sh "${{ matrix.tool }}" + + # Single required status: green only if every tool's job passed. + ci-ok: + needs: test + if: always() + runs-on: ubuntu-latest + steps: + - name: Gate on the matrix result + run: | + if [ "${{ needs.test.result }}" != "success" ]; then + echo "One or more tool suites failed." + exit 1 + fi + echo "All tool suites passed ✅" diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh new file mode 100755 index 0000000..a3b3fb9 --- /dev/null +++ b/scripts/run-tests.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# Standardized test harness for the agent-ops fleet. +# +# scripts/run-tests.sh # run every tool's test suite, print a summary +# scripts/run-tests.sh agent-frisk # run just one tool's suite +# +# Contract (every tool follows it): a tool lives in its own top-level directory and +# ships a bash test script named test_.sh (or test-.sh). The script tallies +# its own checks and EXITS NON-ZERO if any check failed. This harness simply discovers, +# runs, and aggregates them — pass/fail is the script's exit code, nothing is parsed. +set -uo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +# Find the test script inside a tool directory, tolerant of the test_ / test- split. +find_test() { + local dir="$1" t + for t in "$dir"/test_*.sh "$dir"/test-*.sh; do + [ -f "$t" ] && { printf '%s\n' "$t"; return 0; } + done + return 1 +} + +# Every top-level directory that ships a test script is a tool. +list_tools() { + local dir + for dir in */; do + dir="${dir%/}" + [ "$dir" = ".git" ] || [ "$dir" = "scripts" ] && continue + find_test "$dir" >/dev/null 2>&1 && printf '%s\n' "$dir" + done +} + +run_one() { + local tool="$1" test_script + if ! test_script="$(find_test "$tool")"; then + echo "::ERROR:: $tool — no test script found" + return 2 + fi + echo "── $tool ($(basename "$test_script")) ──────────────────────────────" + ( cd "$tool" && bash "$(basename "$test_script")" ) +} + +main() { + if [ "$#" -ge 1 ]; then + run_one "$1" + exit $? + fi + + local failed=() tool rc total=0 + while IFS= read -r tool; do + total=$((total + 1)) + if ! run_one "$tool"; then + failed+=("$tool") + fi + echo + done < <(list_tools) + + echo "==================== SUMMARY ====================" + echo "tools tested: $total passed: $((total - ${#failed[@]})) failed: ${#failed[@]}" + if [ "${#failed[@]}" -gt 0 ]; then + printf 'FAILED: %s\n' "${failed[*]}" + exit 1 + fi + echo "all green ✅" +} + +main "$@"