Skip to content

Scaffolder generate project git push

Status: APPROVED — GTB-parity gap from the v0.22.0 audit (Phase 2, §A). RTB already does git-init + an initial conventional commit (commands/generate/project.rs:254, gated by --no-git), but stops there — no remote, no push, no existing-repo detection. GTB (internal/generator/gitinit.go) does the full sequence. No open design decisions (mechanism mirrors GTB; transport reuses rtb-vcs).

1. Surface

rtb generate project <name> [--no-git]
                            [--git-branch <name>]      # default: main
                            [--push] [--remote <url>]
  • --no-git (existing) → skip all git work.
  • --git-branch → initial branch name (default main); today RTB's initial branch is implicit.
  • --push → after the initial commit, add origin and push.
  • --remote <url> → explicit origin URL; if omitted with --push, derive from the project name + a configured default host (best-effort; a non-derivable remote is a clear error, not a panic).

2. Mechanism

Mirrors GTB gitinit.go, RTB-idiomatically via rtb-vcs (the git/init.rs / git/commit.rs / git/push.rs modules already exist — push.rs:48 is the unused capability this spec wires up):

  1. Existing-repo detection — if the destination is already inside a git work-tree (gix discovery walking ancestors), skip init and log it (don't nest a repo). GTB does this (gitinit.go:51).
  2. Init at --git-branch (configure the initial branch name rather than relying on the git default).
  3. Author resolution — use the host git identity (user.name/user.email) when present; fall back to a neutral scaffolder identity for the initial commit so it never fails on an unconfigured machine (GTB gitinit.go:162).
  4. Initial commit (existing behaviour, retained).
  5. Push (when --push): create origin from --remote/derived URL, push --git-branch. All push steps are best-effort/non-fatal — a push failure warns and leaves a fully-initialised local repo (GTB's posture: scaffolding succeeded even if the remote isn't reachable yet).

3. Security / validation

  • --remote URL validated: NFC-normalise, reject control chars, enforce a scheme allowlist consistent with rtb-vcs clone (https/ssh/git@ forms), length cap. No shell interpolation — rtb-vcs drives the transport, not a spawned git.
  • Derived remote URLs are built from the already-validated project name + a configured host constant; never from raw user free-text.

4. Testing (TDD)

  • Unit: branch-name + remote-URL validation (accept good, reject control/over-length/bad-scheme); remote derivation from name+host; existing-repo detection short-circuits init.
  • E2E (assert_cmd, hermetic — local bare-repo fixture as the remote): generate project --push --remote file://<bare> pushes the initial commit; --git-branch dev lands on dev; generating inside an existing repo skips init (no nested .git); a push to an unreachable remote warns + exits 0 with a valid local repo; --no-git unchanged.

5. Out of scope

  • Creating the remote repository via a forge API (rtb-vcs ReleaseProvider could later create_repo, but this spec only pushes to an existing remote).
  • Multi-remote / upstream-fork setups.