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
43 changes: 38 additions & 5 deletions scripts/pull-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -317,11 +317,30 @@ _pull_one() {
before_main=$(git -C "$dir" rev-parse --verify --quiet refs/heads/main || true)
before_dev=$(git -C "$dir" rev-parse --verify --quiet refs/heads/dev || true)

if ! output=$(git -C "$dir" fetch --prune origin main dev 2>&1); then
echo " FAILED $repo (fetch main/dev)"
echo " $output"
echo "FAILED" > "$result_file"
return
# OMN-16502: `git fetch origin main dev` fails WHOLESALE when either ref is
# absent on origin. Most registry repos carry both branches, but a few
# (e.g. omnigemini) are main-only -- probe before fetching so a genuinely
# main-only repo is fetched/ff'd on main alone and reported OK, instead of
# failing the fetch stage on every run.
local dev_on_remote=1
if ! git -C "$dir" ls-remote --exit-code --heads origin dev >/dev/null 2>&1; then
dev_on_remote=0
fi

if [[ "$dev_on_remote" == "1" ]]; then
if ! output=$(git -C "$dir" fetch --prune origin main dev 2>&1); then
echo " FAILED $repo (fetch main/dev)"
echo " $output"
echo "FAILED" > "$result_file"
return
fi
else
if ! output=$(git -C "$dir" fetch --prune origin main 2>&1); then
echo " FAILED $repo (fetch main)"
echo " $output"
echo "FAILED" > "$result_file"
return
fi
fi

if ! output=$(_checkout_and_ff "$dir" main 2>&1); then
Expand Down Expand Up @@ -357,6 +376,20 @@ _pull_one() {
main_converged=1
fi

# OMN-16502: origin has no dev ref for this repo -- there is no dev leg to
# pull or switch to. Report main's outcome and leave the repo on main.
if [[ "$dev_on_remote" == "0" ]]; then
local main_summary
if [[ "$main_converged" == "1" ]]; then
main_summary="main converged to origin/main (non-ff; orphaned commits preserved)"
else
main_summary=$(_branch_summary "$dir" main "$before_main")
fi
echo " OK $repo ($main_summary; no dev branch on origin -- main-only repo; left on main)"
echo "OK" > "$result_file"
return
fi

if output=$(_checkout_and_ff "$dir" dev 2>&1); then
local main_summary dev_summary
if [[ "$main_converged" == "1" ]]; then
Expand Down
123 changes: 123 additions & 0 deletions tests/unit/scripts/test_pull_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,33 @@ def _make_simple_repo_source(root: Path, name: str) -> Path:
return repo


def _make_main_only_repo_source(root: Path, name: str) -> Path:
"""Create a minimal `<root>/<name>` repo whose bare `<root>/<name>.git`
upstream carries ONLY `main` -- no `dev` ref exists locally or on origin
(OMN-16502). Models `omnigemini`, the one registry repo that never had a
`dev` branch cut, left checked out on `main`, clean tree.
"""
repo = root / name
repo.mkdir(parents=True)
(repo / "README.md").write_text(f"# {name}\n")

_git(["init", "-q", "--initial-branch=main"], cwd=repo)
assert (repo / ".git").is_dir(), (
f"OMN-14744 guard: `git init` did not create {repo / '.git'}"
)
_git(["-c", "user.email=t@t", "-c", "user.name=t", "add", "."], cwd=repo)
_git(
["-c", "user.email=t@t", "-c", "user.name=t", "commit", "-q", "-m", "init"],
cwd=repo,
)

upstream = root / f"{name}.git"
_git(["init", "-q", "--bare", "--initial-branch=main", str(upstream)], cwd=root)
_git(["remote", "add", "origin", str(upstream)], cwd=repo)
_git(["push", "-q", "-u", "origin", "main"], cwd=repo)
return repo


def _make_fake_infra_with_drift_stub(
omni_home: Path,
*,
Expand Down Expand Up @@ -500,6 +527,102 @@ def test_script_handles_bare_repo_no_crash(self) -> None:
assert result.returncode in (0, 1)


@pytest.mark.unit
class TestMainOnlyRepo:
"""A canonical repo whose origin carries only `main` -- no `dev` branch
exists anywhere (OMN-16502; `omnigemini` is the live instance).

Field failure this closes: `_pull_one` ran `git fetch --prune origin main
dev` unconditionally, which fails WHOLESALE ("fatal: couldn't find remote
ref dev") when either ref is absent -- so a main-only repo was marked
FAILED on every single pull-all.sh run (the sole failure in the OMN-16500
proof run, 2026-08-24T23:37Z), dragging down `repos_failed` and the
process exit code even though nothing was actually wrong with the repo.
"""

def test_main_only_repo_reports_ok_and_stays_on_main(self, tmp_path: Path) -> None:
omni_home = tmp_path / "omni_home"
omni_home.mkdir()
fake_home = tmp_path / "home"
fake_home.mkdir()

repo = _make_main_only_repo_source(omni_home, "omnigemini")

# Advance origin/main so the fetch+fast-forward has something real to
# prove, not just a no-op.
writer = tmp_path / "writer"
_git(
["clone", "-q", str(omni_home / "omnigemini.git"), str(writer)],
cwd=tmp_path,
)
_commit_file(writer, "main-advance.txt", "main\n", "main advance")
_git(["push", "-q", "origin", "main"], cwd=writer)

result = _run_pull_all(omni_home, fake_home, repos=["omnigemini"])

assert result.returncode == 0, (
f"stdout={result.stdout!r} stderr={result.stderr!r}"
)
assert "OK omnigemini" in result.stdout
assert "main-only repo" in result.stdout
assert "FAILED" not in result.stdout

current_branch = subprocess.check_output(
["git", "branch", "--show-current"], cwd=repo, text=True
).strip()
assert current_branch == "main"

main_sha = subprocess.check_output(
["git", "rev-parse", "main"], cwd=repo, text=True
).strip()
origin_main_sha = subprocess.check_output(
["git", "rev-parse", "origin/main"], cwd=repo, text=True
).strip()
assert main_sha == origin_main_sha

# No dev branch is fabricated locally for a main-only repo.
dev_ref = subprocess.run(
["git", "show-ref", "--verify", "--quiet", "refs/heads/dev"],
cwd=repo,
check=False,
)
assert dev_ref.returncode != 0, (
"no local dev branch should exist for a main-only repo"
)

fields = _parse_result_line(result.stdout)
assert fields.get("overall") == "OK", f"fields={fields!r}"
assert fields.get("repos_ok") == "1", f"fields={fields!r}"
assert fields.get("repos_failed") == "0", f"fields={fields!r}"

def test_main_only_repo_mixed_with_normal_repo_exits_zero(
self, tmp_path: Path
) -> None:
"""AC: a full pull-all.sh run with a main-only repo present exits 0,
alongside a normal main+dev repo (OMN-16502)."""
omni_home = tmp_path / "omni_home"
omni_home.mkdir()
fake_home = tmp_path / "home"
fake_home.mkdir()

_make_main_only_repo_source(omni_home, "omnigemini")
_make_simple_repo_source(omni_home, "omnibase_core")

result = _run_pull_all(
omni_home, fake_home, repos=["omnigemini", "omnibase_core"]
)

assert result.returncode == 0, (
f"stdout={result.stdout!r} stderr={result.stderr!r}"
)
assert "OK omnigemini" in result.stdout
assert "OK omnibase_core" in result.stdout
fields = _parse_result_line(result.stdout)
assert fields.get("overall") == "OK", f"fields={fields!r}"
assert fields.get("repos_ok") == "2", f"fields={fields!r}"
assert fields.get("repos_failed") == "0", f"fields={fields!r}"


@pytest.mark.unit
class TestPluginCacheRefresh:
"""Tests for the plugin cache refresh logic in pull-all.sh (OMN-7369)."""
Expand Down
Loading