Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion host-setup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ bash menu.sh
host-setup/menu.sh # from a checkout of this repository, or of any other repo in the fleet
```

It answers a different question than `bootstrap.sh` does. `bootstrap.sh` stands a host up and stops. `menu.sh` loops, because a person sitting down with it usually wants more than one thing done in a sitting, and it tells the hub apart from whichever repo it happens to be run from: a checkout of `ptr727/ProjectTemplate` gets the hub tasks (audit a cataloged repo, check the generated Skills distributions), a checkout of any other repo gets the downstream tasks (check or pull the hub's verbatim-owned files into that repo's own worktree, per [`scripts/carry.py`][carry]), and both get the host tasks this directory's `linux/` tooling already provides. Run from neither, only the host tasks show, since there is no repository to audit or pull files into.
It answers a different question than `bootstrap.sh` does. `bootstrap.sh` stands a host up and stops. `menu.sh` loops, because a person sitting down with it usually wants more than one thing done in a sitting, and it tells the hub apart from whichever repo it happens to be run from: a checkout of `ptr727/ProjectTemplate` gets the hub tasks (audit a cataloged repo, check the generated Skills distributions), a checkout of any other repo gets the downstream tasks too (check or pull the hub's verbatim-owned files into that repo's own worktree, per [`scripts/carry.py`][carry]), and every run gets the host tasks this directory's `linux/` tooling already provides. Only the downstream tasks need a repo to run from. The hub tasks fetch the hub themselves when there is no local checkout to reuse, so they still show and still work when the menu is run entirely standalone, off no checkout at all.

Reaching `spec/audit.py` and `scripts/carry.py` from outside a hub checkout means fetching one, the same "hosted and reached, never carried" model [`scripts/README.md`][scripts-readme] states for those tools generally. `menu.sh` clones fresh rather than reusing `bootstrap.sh`'s tarball, since `scripts/carry.py` itself checks that its hub argument is a real git checkout on a freshly fetched `origin/main` with no local changes, and a full clone rather than a shallow one, since `spec/audit.py` walks the hub's own commit history to judge whether a carried copy is trailing the file it was copied from. Run from inside the hub itself, that same freshness is confirmed against the local checkout before a hub task uses it, falling back to a fresh clone when the local checkout has moved on, so an audit or a Skills-distribution check never silently reads a stale or feature-branch tree.

Expand Down
44 changes: 32 additions & 12 deletions host-setup/menu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ fetch_hub() {
fail "Could not clone $HUB_REPO. Check that this host reaches github.com."
return 1
}
# Marked as ours the moment the clone lands rather than only once every later step also succeeds, so a failure below still leaves a tree remove_unowned_hub_check will clean up on the next run instead of blocking every retry as somebody else's.
touch "$(marker_path)"
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
Resolved
# A branch name is already checked out by the clone above.
# A tag, a pull request ref, or a commit needs an explicit fetch and checkout, since "git clone --branch" only takes a branch or a tag, not an arbitrary commit.
if [[ $REF != "$DEFAULT_REF" ]]; then
Expand All @@ -114,7 +116,6 @@ fetch_hub() {
return 1
}
fi
touch "$(marker_path)"
HUB_ROOT="$DIR/hub"
HUB_FETCHED=true
info "Cloned to $HUB_ROOT"
Expand Down Expand Up @@ -196,10 +197,11 @@ host_tool() {

# The Python tools under scripts/ and spec/ resolve their own root from __file__ rather than the working directory, so they are called by absolute path from wherever this script runs and need no cd.
# Checked here rather than upfront in main, the same reasoning host-setup/linux/install-skills.sh already carries: a host with no interpreter yet can still use every host action, and only the actions that need one name it as their own prerequisite.
# The prerequisite failure returns 127, bash's own "command not found" convention, so a caller reading a specific exit code from the tool itself (build_dist.py's 0-clean/1-stale contract) can tell "python3 never ran" apart from "python3 ran and returned 1".
hub_python() {
command -v python3 >/dev/null || {
fail "python3 is required for this task; host-setup/linux/install-tools.sh provides it"
return 1
fail "python3 is required for this task. host-setup/linux/install-tools.sh provides it."
return 127
}
local script="$1"
shift
Expand All @@ -220,18 +222,32 @@ audit_repo() {

check_skills_dist() {
ensure_hub_root || return 1
if hub_python scripts/build_dist.py --check; then
info "Every generated Skills distribution matches .agents/skills/"
else
info "A generated Skills distribution is stale; this menu does not regenerate it from a fetched checkout, since the result has to be committed in the hub itself"
fi
local rc=0
hub_python scripts/build_dist.py --check || rc=$?
# Only 0 (clean) and 1 (stale) are outcomes scripts/build_dist.py --check documents for itself, so only those two read as a check result.
# Anything else, 127 included, is hub_python or the tool itself failing to run rather than a finding, and is reported as the task error it is.
case "$rc" in
0) info "Every generated Skills distribution matches .agents/skills/" ;;
1) info "A generated Skills distribution is stale. This menu does not regenerate it from a fetched checkout, since the result has to be committed in the hub itself." ;;
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
Resolved
*)
fail "scripts/build_dist.py --check did not run to completion (exit $rc)"
return 1
;;
esac
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
}

carry_action() {
local mode="$1"
[[ -n $DOWNSTREAM_ROOT ]] ||
{
fail "No downstream repo checkout found; run this menu from inside the target repo's own worktree"
fail "No downstream repo checkout found. Run this menu from inside the target repo's own worktree."
return 1
}
# Its hub argument must be exactly on origin/main by carry.py's own requirement, and a non-default --ref checks out something else entirely, so this would always fail deep inside carry.py with no clue why.
# Refused here instead, with the actual reason.
[[ $REF == "$DEFAULT_REF" ]] ||
{
fail "Pulling hub files needs the hub's $DEFAULT_REF branch, and this session was started with --ref $REF. Run without --ref, or start a separate session on $DEFAULT_REF for this task."
return 1
}
local default="$DOWNSTREAM_NAME"
Expand Down Expand Up @@ -271,7 +287,8 @@ print_menu() {
log "Hub, ptr727/ProjectTemplate:"
log " 10 Audit a cataloged repo"
log " 11 Check the generated Skills distributions are current"
if [[ -n $DOWNSTREAM_ROOT ]]; then
# Also gated on REF: carry.py always rejects a hub checkout that is not exactly on the default ref, so these tasks cannot work in a non-default --ref session regardless of a downstream repo being detected.
if [[ -n $DOWNSTREAM_ROOT && $REF == "$DEFAULT_REF" ]]; then
log ""
log "Downstream, the repo this menu is run from:"
log " 12 Check what the hub would change here, change nothing"
Expand Down Expand Up @@ -351,8 +368,11 @@ parse_args() {
--dir)
[[ $# -ge 2 ]] || die "--dir takes a path"
[[ $2 == /* ]] || die "--dir takes an absolute path, and \"$2\" is relative"
[[ $2 != "/" ]] || die "--dir may not be the root directory"
DIR="${2%/}"
# Canonicalized before the root check, since a literal "/tmp/.." is not the string "/" but resolves to it the moment anything below opens a path under it.
local canonical
canonical=$(readlink -m -- "$2") || die "--dir could not be resolved: \"$2\""
[[ $canonical != "/" ]] || die "--dir may not be the root directory"
DIR="$canonical"
shift
;;
-h | --help)
Expand Down