Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions docs/specs/2026-10-18-repoman-run-path-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,10 +461,12 @@ leans on the capability gate).
remove the hardcoded org/person defaults — `REPORT_TARGET_REPO` and
`SOURCE_REPO` in `link-health-scanner.sh` / `automation-health-dashboard.sh`,
the committer identity in `link-health-fixer.sh`, and the personal `REPORT_PY`
default in `weekly-report.sh` (already env-overridable). 4 behavioral
constants across 3 files. #116 carries the authoritative per-line locations;
distinct from #99 (that is config *plumbing*; this is embedded org/person
*defaults*).
**fallback** in `weekly-report.sh` (already env-overridable). As of #127 that
Comment thread
rubambiza marked this conversation as resolved.
Resolved
wrapper resolves `REPORT_PY` from a candidate list — the bundled plugin path
first, the personal `$HOME/workspaces/...` path only as a fallback — so #116's
remaining task here is dropping that fallback candidate, not the whole default.
#116 carries the authoritative per-line locations; distinct from #99 (that is
config *plumbing*; this is embedded org/person *defaults*).
- **#99** — scanner/fixer/dashboard param-driven decoupling (v0.1.1).
- **#82** — automation commit/sign-off identity (gates fixer orchestration).
- **#76** — unify program registry.
Expand Down
22 changes: 18 additions & 4 deletions scripts/weekly-report.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ OPTIONS:
--help, -h Show this help

ENVIRONMENT:
REPORT_PY Path to report.py (default: the deployed report generator)
REPORT_PY Path to report.py (default: first of the bundled generator
beside this skill, then the $HOME/workspaces deploy path)

PREREQUISITES:
python3, gh (authenticated). The repo list comes from the RepoMan enrolled set
Expand All @@ -74,9 +75,22 @@ if [ -z "$repos" ]; then
exit 1
fi

# Locate the generator. Default points at the deployed github-weekly-report skill
# (the upstream skill name in agent-skills); override with REPORT_PY in dev.
REPORT_PY="${REPORT_PY:-$HOME/workspaces/shared/skills/github-weekly-report/scripts/report.py}"
# Locate the generator. When REPORT_PY is unset, try candidates in order and use
# the first that exists, so both deployment layouts work with no per-host config:
# 1. bundled — the plugin layout, where github-weekly-report sits beside the
# repoman skill (the self-contained install; the primary target).
# 2. deployed — the OpenClaw host layout under $HOME/workspaces. Transitional:
# the personal-path default is tracked for removal in #116, and
# falls away entirely if OpenClaw deployment is retired.
# If neither exists, keep the bundled path so the error names the expected location.
if [ -z "${REPORT_PY:-}" ]; then
bundled="$SCRIPT_DIR/../../github-weekly-report/scripts/report.py"
deployed="${HOME:-}/workspaces/shared/skills/github-weekly-report/scripts/report.py"
for candidate in "$bundled" "$deployed"; do
if [ -f "$candidate" ]; then REPORT_PY="$candidate"; break; fi
done
REPORT_PY="${REPORT_PY:-$bundled}"
fi
if [ ! -f "$REPORT_PY" ]; then
echo "Error: report generator not found at: $REPORT_PY" >&2
echo "Set REPORT_PY to the path of report.py." >&2
Expand Down
54 changes: 54 additions & 0 deletions tests/test-weekly-report.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,59 @@ if REPOMAN_REPOS_FILE="$TEST_TMPDIR/empty.json" \
echo "FAIL wrapper should error on empty enrolled set"; fail=1
fi

# REPORT_PY default is resolved RELATIVE TO THE WRAPPER (the bundled generator
# at ../../github-weekly-report/scripts/report.py), not a host-specific
# $HOME/workspaces path (rossoctl/automation#126). With REPORT_PY unset and the
# bundled copy absent (automation has the wrapper but not report.py), the
# "not found" error must name the wrapper-relative path and must NOT name a
# $HOME/workspaces path.
# Assert the PROPERTY, not an exact recomputed path: with no bundled generator
# present (automation ships the wrapper, not report.py) and no $HOME/workspaces
# copy, the error names the bundled github-weekly-report generator and never a
# workspaces path. A property check (not an exact string) keeps this green across
# the candidate-list resolution order.
err=$(HOME="$TEST_TMPDIR/nohome" REPOMAN_REPOS_FILE="$TEST_TMPDIR/repos.json" \
bash "$WRAPPER" --output "$TEST_TMPDIR/x.md" 2>&1 >/dev/null) || true
case "$err" in
*workspaces*)
echo "FAIL default error still names a \$HOME/workspaces path: [$err]"; fail=1 ;;
*github-weekly-report/scripts/report.py*)
: ;; # names the bundled generator — correct
*)
echo "FAIL default error does not name the bundled generator: [$err]"; fail=1 ;;
esac

# Positive path (candidate 1, bundled — #127): a real bundle layout with the
# generator present and REPORT_PY unset must FIND it, run it, and emit the report
# — proving "succeeds on a clean plugin install", not just the error.
bundle="$TEST_TMPDIR/bundle"
mkdir -p "$bundle/skills/repoman/scripts" "$bundle/skills/github-weekly-report/scripts"
cp "$SCRIPT_DIR/../scripts/"*.sh "$bundle/skills/repoman/scripts/"
printf 'import sys\nprint("RAN", " ".join(sys.argv[1:]))\n' \
> "$bundle/skills/github-weekly-report/scripts/report.py"
# Capture full output first, then take line 1 — piping the wrapper directly into
# `head` makes head close the pipe early, and under `set -o pipefail` the SIGPIPE
# surfaces as exit 141 even though the assertion is fine.
full=$(HOME="$TEST_TMPDIR/nohome" REPOMAN_REPOS_FILE="$TEST_TMPDIR/repos.json" \
REPOMAN_SKILL_DIR="$TEST_TMPDIR/meta" \
bash "$bundle/skills/repoman/scripts/weekly-report.sh" 2>/dev/null)
got=$(printf '%s\n' "$full" | head -n 1)
[ "$got" = "RAN --repos rossoctl/alpha alice/beta" ] \
|| { echo "FAIL bundled default not found/run (REPORT_PY unset): [$got]"; fail=1; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion — This case proves the bundled candidate end to end, but the deployed-fallback branch — bundled copy absent, $HOME/workspaces/shared/... present, so the wrapper resolves and runs the deployed generator — is the OpenClaw-host scenario that motivated the candidate list (Jim's P1) and is now the only resolution path with no test. A sibling case mirroring this one (fake $HOME containing only the deployed layout, REPORT_PY unset, assert the generator runs) would pin it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2ddab94 — added a candidate-2 positive test (bundled absent, $HOME/workspaces present); verified it goes red when the fallback is dropped or reordered.


# Positive path (candidate 2, deployed fallback — #127): bundled copy ABSENT,
# the $HOME/workspaces copy present. Proves the OpenClaw layout still resolves
# with REPORT_PY unset, so dropping or reordering the fallback candidate fails here.
rm "$bundle/skills/github-weekly-report/scripts/report.py"
mkdir -p "$TEST_TMPDIR/home/workspaces/shared/skills/github-weekly-report/scripts"
printf 'import sys\nprint("RAN", " ".join(sys.argv[1:]))\n' \
> "$TEST_TMPDIR/home/workspaces/shared/skills/github-weekly-report/scripts/report.py"
full=$(HOME="$TEST_TMPDIR/home" REPOMAN_REPOS_FILE="$TEST_TMPDIR/repos.json" \
REPOMAN_SKILL_DIR="$TEST_TMPDIR/meta" \
bash "$bundle/skills/repoman/scripts/weekly-report.sh" 2>/dev/null) || true
got=$(printf '%s\n' "$full" | head -n 1)
[ "$got" = "RAN --repos rossoctl/alpha alice/beta" ] \
|| { echo "FAIL deployed fallback not used (REPORT_PY unset): [$got]"; fail=1; }

if [ "$fail" -eq 0 ]; then echo "PASS test-weekly-report.sh"; fi
exit "$fail"
Loading