fix(installer): git_clone_bundle + prereq check (closes #11) #13

Merged
MillasAgent merged 1 commit from fix/installer-git-clone-prereqs into main 2026-09-06 12:15:56 +00:00
Owner

Summary

Closes Forgejo issue #11 (reopened after PR #12 closed it prematurely). The canonical curl | bash install path now uses git clone --depth 1 against the public repo (no API tokens, no per-file fetch, no allowlist) instead of the broken API-REST listing that duplicated the MillasDev/memory segment in every URL.

This is a different release from PR #12 (0.3.4). PR #12 fixed the jsonc_require_python3 symbol-resolution symptom with an embedded fallback; it did not address the underlying fetch bug at install.sh:97. The new do_test_install_curl_pipe_stdin_bundle_clone regression test (which captures the actual clone URL via a git stub and asserts it byte-for-byte) is what would have caught the URL bug at PR #12 review time.

Fix

  • install.sh:fetch_bundle_to_tmpdir (49 lines, API REST) DELETED.
  • install.sh:git_clone_bundle (~33 lines, git clone --depth 1) added. Derives clone_url="${REMOTE_RAW_BASE%/raw/branch/*}". Returns $workdir/workspace/opencode (the bundle root) so callers (copy_bundle, patch_global_opencode_jsonc, patch_global_tui_json, install_openmillas_cli) work unchanged. Repo is public — no auth, no credential helper, no token.
  • install.sh:require_prereqs rewritten: now checks bash 4+, python3, git up front, in that order. Each missing prereq prints a distro-specific install heredoc (apt / dnf / brew / apk / xcode-select --install) and exits 1. The previous shape silently assumed git existed and surfaced errors only at the fetch step.

Defense-in-depth

git_clone_bundle validates the clone URL before calling git:

  • requires https:// scheme (rejects file://, ext::, etc.)
  • requires the /raw/batch/<ref> strip to have actually fired (catches a user-supplied REMOTE_RAW_BASE that lacks the expected suffix)

This closes the same family of silent URL-construction errors as the original bug.

Tests

  • NEW do_test_install_curl_pipe_stdin_bundle_clone: stubs git (not curl) so the test captures the actual clone URL via printf '%s\n' "$@" to a log file. Asserts the URL equals the expected stripped REMOTE_RAW_BASE byte-for-byte. This is the regression test that would have caught the bug at PR #12 review time.
  • NEW do_test_install_curl_pipe_stdin_missing_git: PATH without git → asserts the heredoc with apt install git / dnf install git / brew install git / xcode-select --install / apk add git reaches stderr.
  • NEW do_test_install_curl_pipe_stdin_missing_python3: mirror of the above for python3.
  • ADJUSTED do_test_install_curl_pipe_stdin_prereqs: Case B stub-binary list now includes git (so the new git prereq does not mask the python3-prereq test). Case A's obsolete STUB curl assertion removed (cmd_install no longer uses curl). Case B python3-missing assertion relaxed to accept either the legacy embedded-fallback message or the new require_prereqs message.

Acceptance criteria

  1. bash workspace/opencode/tests/test_install.sh exits 0; ALL TESTS PASS; PASS: 31 FAIL: 0. (Modulo pre-existing do_test_install_idempotent mtime flake.)
  2. bash workspace/opencode/deploy.sh --check exits 0.
  3. Manual reproduction with all prereqs present: curl -fsSL https://git.millaredos.com/MillasDev/memory/raw/branch/main/workspace/opencode/install.sh | bash -s -- install --yes --no-interaction reaches install_openmillas_cli without fetch failed, git clone failed, orden no encontrada, or command not found.
  4. Manual reproduction missing git: stderr starts with [openmillas][error] git is required but was not found on PATH and includes the install-heredoc instructions.
  5. Manual reproduction missing python3: stderr starts with [openmillas][error] python3 is required but was not found on PATH and includes the install-heredoc instructions.
  6. Byte-level URL check: do_test_install_curl_pipe_stdin_bundle_clone asserts the git stub's argv contains exactly ${REMOTE_RAW_BASE%/raw/branch/*}.

Spec deviation (acknowledged)

.internal/specs/fix-issue-11-git-clone-and-prereqs.md §3.2 originally specified git_clone_bundle returning $workdir, but the implementation returns $workdir/workspace/opencode. Spec §3.3 implicitly assumed the latter (source "$BUNDLE_DIR/lib/jsonc.sh" would resolve to a non-existent path under the spec's literal return value). Spec updated post-implementation to match the actual return path. No code change beyond the spec doc edit.

Deferred (out of scope per spec §6)

  • TD-20: SHA-256 / GPG pinning for curl | bash install.sh self-update.
  • TD-25: editorial/ auto-deploy via copy_bundle (git_clone_bundle clones editorial/ but copy_bundle correctly excludes it; TD-25 stays open).
  • TD-26: cmd_doctor git-check (install.sh:485-598, openmillas:190-311).
  • cmd_update self-update 404 (different code path; separate bugfix).
  • lib/jsonc.sh dead-code cleanup (TD-22).
  • bash 4.0+ floor accepts Shellshock-era versions (SEC-05 from review); deferred to a hardening follow-up.

Review notes

  • Security audit: 0 blockers, 1 SHOULD-FIX (SEC-01, the clone_url validation) batched in. 7 NITs deferred.
  • Architecture review: 0 blockers, 2 MAJORs (F-001 spec drift, F-002 stale line references) batched in. 4 MINOR + 3 NIT deferred.

Files

  • workspace/opencode/install.shfetch_bundle_to_tmpdir deleted; git_clone_bundle added; require_prereqs rewritten; cmd_install log lines updated.
  • workspace/opencode/tests/test_install.sh — 4 tests affected (1 adjusted, 3 new).
  • workspace/opencode/openmillas — version bump 0.3.4 → 0.3.5.
  • AGENTS.md — version line bumped.
  • CHANGELOG.md — new ## [0.3.5] - 2026-09-06 section with ### Fixed + ### Changed; new link-footer line.
  • README.md — new ## Prerequisites section.
  • .gitignore — adds .codegraph_tmp_* pattern (defense-in-depth against loose tool-output artifacts).

Spec

  • .internal/specs/fix-issue-11-git-clone-and-prereqs.md (gitignored, the implementer's contract).

Closes #11

## Summary Closes Forgejo issue #11 (reopened after PR #12 closed it prematurely). The canonical `curl | bash` install path now uses `git clone --depth 1` against the public repo (no API tokens, no per-file fetch, no allowlist) instead of the broken API-REST listing that duplicated the `MillasDev/memory` segment in every URL. This is a different release from PR #12 (`0.3.4`). PR #12 fixed the `jsonc_require_python3` symbol-resolution symptom with an embedded fallback; it did not address the underlying fetch bug at `install.sh:97`. The new `do_test_install_curl_pipe_stdin_bundle_clone` regression test (which captures the actual clone URL via a git stub and asserts it byte-for-byte) is what would have caught the URL bug at PR #12 review time. ## Fix - `install.sh:fetch_bundle_to_tmpdir` (49 lines, API REST) DELETED. - `install.sh:git_clone_bundle` (~33 lines, `git clone --depth 1`) added. Derives `clone_url="${REMOTE_RAW_BASE%/raw/branch/*}"`. Returns `$workdir/workspace/opencode` (the bundle root) so callers (`copy_bundle`, `patch_global_opencode_jsonc`, `patch_global_tui_json`, `install_openmillas_cli`) work unchanged. Repo is public — no auth, no credential helper, no token. - `install.sh:require_prereqs` rewritten: now checks `bash 4+`, `python3`, `git` up front, in that order. Each missing prereq prints a distro-specific install heredoc (`apt` / `dnf` / `brew` / `apk` / `xcode-select --install`) and exits 1. The previous shape silently assumed `git` existed and surfaced errors only at the fetch step. ## Defense-in-depth `git_clone_bundle` validates the clone URL before calling git: - requires `https://` scheme (rejects `file://`, `ext::`, etc.) - requires the `/raw/batch/<ref>` strip to have actually fired (catches a user-supplied `REMOTE_RAW_BASE` that lacks the expected suffix) This closes the same family of silent URL-construction errors as the original bug. ## Tests - **NEW** `do_test_install_curl_pipe_stdin_bundle_clone`: stubs `git` (not `curl`) so the test captures the actual clone URL via `printf '%s\n' "$@"` to a log file. Asserts the URL equals the expected stripped REMOTE_RAW_BASE byte-for-byte. This is the regression test that would have caught the bug at PR #12 review time. - **NEW** `do_test_install_curl_pipe_stdin_missing_git`: PATH without git → asserts the heredoc with `apt install git` / `dnf install git` / `brew install git` / `xcode-select --install` / `apk add git` reaches stderr. - **NEW** `do_test_install_curl_pipe_stdin_missing_python3`: mirror of the above for python3. - **ADJUSTED** `do_test_install_curl_pipe_stdin_prereqs`: Case B stub-binary list now includes `git` (so the new git prereq does not mask the python3-prereq test). Case A's obsolete `STUB curl` assertion removed (cmd_install no longer uses curl). Case B python3-missing assertion relaxed to accept either the legacy embedded-fallback message or the new require_prereqs message. ## Acceptance criteria 1. `bash workspace/opencode/tests/test_install.sh` exits 0; `ALL TESTS PASS`; `PASS: 31 FAIL: 0`. (Modulo pre-existing `do_test_install_idempotent` mtime flake.) 2. `bash workspace/opencode/deploy.sh --check` exits 0. 3. Manual reproduction with all prereqs present: `curl -fsSL https://git.millaredos.com/MillasDev/memory/raw/branch/main/workspace/opencode/install.sh | bash -s -- install --yes --no-interaction` reaches `install_openmillas_cli` without `fetch failed`, `git clone failed`, `orden no encontrada`, or `command not found`. 4. Manual reproduction missing git: stderr starts with `[openmillas][error] git is required but was not found on PATH` and includes the install-heredoc instructions. 5. Manual reproduction missing python3: stderr starts with `[openmillas][error] python3 is required but was not found on PATH` and includes the install-heredoc instructions. 6. Byte-level URL check: `do_test_install_curl_pipe_stdin_bundle_clone` asserts the git stub's argv contains exactly `${REMOTE_RAW_BASE%/raw/branch/*}`. ## Spec deviation (acknowledged) `.internal/specs/fix-issue-11-git-clone-and-prereqs.md` §3.2 originally specified `git_clone_bundle` returning `$workdir`, but the implementation returns `$workdir/workspace/opencode`. Spec §3.3 implicitly assumed the latter (`source "$BUNDLE_DIR/lib/jsonc.sh"` would resolve to a non-existent path under the spec's literal return value). Spec updated post-implementation to match the actual return path. No code change beyond the spec doc edit. ## Deferred (out of scope per spec §6) - TD-20: SHA-256 / GPG pinning for `curl | bash` install.sh self-update. - TD-25: `editorial/` auto-deploy via `copy_bundle` (`git_clone_bundle` clones `editorial/` but `copy_bundle` correctly excludes it; TD-25 stays open). - TD-26: `cmd_doctor` git-check (install.sh:485-598, openmillas:190-311). - `cmd_update` self-update 404 (different code path; separate bugfix). - `lib/jsonc.sh` dead-code cleanup (TD-22). - bash 4.0+ floor accepts Shellshock-era versions (SEC-05 from review); deferred to a hardening follow-up. ## Review notes - Security audit: 0 blockers, 1 SHOULD-FIX (SEC-01, the clone_url validation) batched in. 7 NITs deferred. - Architecture review: 0 blockers, 2 MAJORs (F-001 spec drift, F-002 stale line references) batched in. 4 MINOR + 3 NIT deferred. ## Files - `workspace/opencode/install.sh` — `fetch_bundle_to_tmpdir` deleted; `git_clone_bundle` added; `require_prereqs` rewritten; `cmd_install` log lines updated. - `workspace/opencode/tests/test_install.sh` — 4 tests affected (1 adjusted, 3 new). - `workspace/opencode/openmillas` — version bump 0.3.4 → 0.3.5. - `AGENTS.md` — version line bumped. - `CHANGELOG.md` — new `## [0.3.5] - 2026-09-06` section with `### Fixed` + `### Changed`; new link-footer line. - `README.md` — new `## Prerequisites` section. - `.gitignore` — adds `.codegraph_tmp_*` pattern (defense-in-depth against loose tool-output artifacts). ## Spec - `.internal/specs/fix-issue-11-git-clone-and-prereqs.md` (gitignored, the implementer's contract). Closes #11
Closes Forgejo issue #11 (reopened after PR #12 closed it prematurely).
PR #12 fixed the jsonc_require_python3 symbol-resolution symptom at
install.sh:51-64 (embedded fallback) but did not address the underlying
fetch bug at install.sh:97: the URL '${base%/raw/branch/*}/api/v1/repos/MillasDev/memory/contents/workspace/opencode/${path%/}?ref=main'
produces 'https://git.millaredos.com/MillasDev/memory/api/v1/repos/MillasDev/memory/...'
- the MillasDev/memory segment is duplicated by stripping '/raw/branch/*'
from a base that already contained the segment, then hard-coding the
segment again. Every API listing call 404s; no files reach the tmpdir.

Replaced the 49-line API-based fetch_bundle_to_tmpdir with a 16-line
git_clone_bundle that runs 'git clone --depth 1 $clone_url $workdir'
against the public repo (no auth, no token, no allowlist). The repo is
public per AGENTS.md, so the credential helper is intentionally not
used. clone_url is derived from REMOTE_RAW_BASE by stripping the
'/raw/branch/<ref>' suffix, and the function returns '$workdir/workspace/opencode'
(the bundle root, not the repo root) so callers like copy_bundle and
patch_global_opencode_jsonc work unchanged.

Added a defense-in-depth validation in git_clone_bundle (SEC-01 from
the security review): require https:// scheme and require that the
strip actually fired (same family of bugs as the original issue #11 URL
construction error).

require_prereqs at install.sh:196-215 now checks bash 4+, python3,
and git up front, in that order. Each missing prereq prints a
distro-specific install heredoc (apt/dnf/brew/apk/xcode-select) and
exits 1 with a friendly message. The previous shape silently assumed
git existed and surfaced errors only at the fetch step.

Tests: three new tests added (do_test_install_curl_pipe_stdin_bundle_clone
which captures the actual clone URL via a printf stub and asserts it
byte-for-byte against the expected stripped form, plus the two prereq-
missing tests). One existing test (do_test_install_curl_pipe_stdin_prereqs)
adjusted: Case B stub-binary list now includes git so the new git
prereq check does not mask the python3-prereq contract being tested.
Case A's obsolete 'STUB curl was invoked' assertion removed (cmd_install
no longer uses curl after this fix). Case B python3-missing assertion
relaxed to accept either the legacy embedded-fallback message or the
new require_prereqs message.

Spec deviation: .internal/specs/fix-issue-11-git-clone-and-prereqs.md
section 3.2 originally specified git_clone_bundle returning '$workdir',
but the implementation returns '$workdir/workspace/opencode'. Spec
section 3.3 assumed the latter implicitly (source $BUNDLE_DIR/lib/jsonc.sh).
Spec updated post-implementation to match the implementation. No code
change beyond the spec doc edit.

Also includes:
- AGENTS.md line 14: Version 0.3.4 -> 0.3.5.
- workspace/opencode/openmillas line 34: OPENMILLAS_VERSION 0.3.4 -> 0.3.5.
- CHANGELOG.md: new dated '## [0.3.5] - 2026-09-06' section with
  ### Fixed (URL bug) and ### Changed (prereq check) bullets; new
  [0.3.5] link-footer line.
- README.md: new ## Prerequisites section listing bash 4.0+,
  python3, git with a cross-ref to install.sh's require_prereqs.
- .gitignore: adds '.codegraph_tmp_*' pattern to catch loose
  tool-output artifacts at repo root (defense-in-depth).
- Stale line-number references in install.sh:54 and test_install.sh
  updated to current line locations.

Deferred (per spec section 6, out of scope):
- TD-20: SHA-256 / GPG pinning for curl|bash install.sh self-update.
- TD-25: editorial/ auto-deploy via copy_bundle (git_clone_bundle
  clones editorial/ but copy_bundle correctly excludes it).
- TD-26: cmd_doctor git-check (install.sh:485-598, openmillas:190-311).
- cmd_update self-update 404 (different code path, separate bugfix).
- lib/jsonc.sh dead-code cleanup (TD-22).
- bash 4.0+ floor accepts Shellshock-era versions (SEC-05 from review);
  deferred to a hardening follow-up.

Acceptance criteria from spec section 4 met:
1. test_install.sh exits 0; ALL TESTS PASS; PASS: 31, FAIL: 0.
2. deploy.sh --check exits 0.
3-5. Manual reproductions: prereq heredocs contain the documented
   apt/dnf/brew/apk/xcode-select commands; install reaches copy_bundle.
6. Byte-level URL check: the bundle_clone test's git stub captures
   argv and the test asserts the URL equals the stripped REMOTE_RAW_BASE
   exactly.
7. grep 'fetch_bundle_to_tmpdir' returns zero hits in workspace/opencode/.
8-10. OPENMILLAS_VERSION=0.3.5 in install.sh and openmillas;
    [0.3.5] section + link in CHANGELOG.md; 0.3.5 in AGENTS.md.

Refs:
- .internal/specs/fix-issue-11-git-clone-and-prereqs.md (gitignored,
  the implementer's contract).
- PR #12 (the previous partial fix; closed #11 prematurely).
- Security review: 0 blockers, 1 SHOULD-FIX (SEC-01) batched in.
- Architecture review: APPROVED with 2 MAJORs (F-001 spec drift, F-002
  line-refs) batched in; 4 MINOR + 3 NIT deferred to follow-up.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MillasDev/memory!13
No description provided.