clonefrom

package
v0.0.0-...-15a56fc Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 2, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package clonefrom implements the `gitmap clone-from <file>` workflow: read a JSON or CSV plan from disk, validate every row, render a dry-run preview by default, and on `--execute` shell out to `git clone` for each row with a per-row pass/fail summary.

Why a separate package (not gitmap/cloner or gitmap/clonenext)?

  • gitmap/cloner is the scan-driven cloner: it consumes records emitted by the scan workflow and assumes a DB-backed repo model. `clone-from` is plan-driven: input comes from a user- provided file, no scan, no model.CloneRecord round-trip.
  • gitmap/clonenext is the version-bumping cloner for existing local repos (`vN+1` of an already-cloned repo). `clone-from` clones brand-new URLs to user-chosen destinations.

Splitting keeps each cloner's contract tight and lets clone-from evolve (e.g., adding `--depth`, `--single-branch`, parallel fan-out) without touching the more constrained scan/cn paths.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildGitArgs

func BuildGitArgs(r Row, dest string) []string

BuildGitArgs returns the argv (excluding the "git" binary) that Execute would pass to exec.Command for the given row.

func ClassifyScheme

func ClassifyScheme(url string) string

ClassifyScheme picks one bucket for a URL. Exported so tests and any future per-row preview can reuse the rule without re-deriving the prefix table.

func DeriveDest

func DeriveDest(url string) string

DeriveDest computes the directory name `git clone <url>` would pick by default: the last path segment with a trailing `.git` stripped. Public so the executor can use the same logic when the row's Dest field is empty.

func EffectiveCheckout

func EffectiveCheckout(r Row) string

EffectiveCheckout resolves a row's Checkout field to one of the three concrete modes. Empty → CloneFromCheckoutDefault ("auto"). Exported so renderers + faithful-cmd verifiers agree with the executor on the resolved mode without re-importing this file.

func EmitInputSchema

func EmitInputSchema() ([]byte, error)

EmitInputSchema returns the JSON Schema for the clone-now input (top-level array of scan records). Field set is sourced from clonenow.KnownScanFields() so additions auto-propagate.

func EmitReportSchema

func EmitReportSchema() ([]byte, error)

EmitReportSchema returns the JSON Schema for the clone-from JSON report envelope. The schemaVersion `const` is wired to constants.CloneFromReportSchemaVersion so a bump there is auto- reflected here (and the writer emits a matching number).

func EmitSchema

func EmitSchema(kind string) ([]byte, error)

EmitSchema dispatches on kind and returns the pretty-printed JSON Schema bytes. Unknown kinds return an error wrapping the user- facing message so the CLI layer can print and exit 2 verbatim.

func Render

func Render(w io.Writer, p Plan) error

Render writes the legacy human-readable dry-run preview to w. Returns the first write error so callers (CLI) can surface it instead of silently truncating output to a closed pipe.

func RenderSummary

func RenderSummary(w io.Writer, results []Result, reportPath string) error

RenderSummary writes a human-readable summary to w. Format:

gitmap clone-from: 5 ok, 1 skipped, 2 failed (8 total)
report: .gitmap/clone-from-report-1735000000.csv

  ok       https://github.com/a/b.git    (1.2s)
  skipped  https://github.com/c/d.git    dest exists
  failed   https://github.com/e/f.git    fatal: repository not found
  ...

func RenderSummaryTerminal

func RenderSummaryTerminal(w io.Writer, results []Result,
	csvPath, jsonPath string) error

RenderSummaryTerminal writes the enriched terminal-mode summary block. csvPath / jsonPath may each be empty (write skipped or failed); the renderer substitutes a single "(skipped …)" line when BOTH are empty so the report section never disappears entirely. Returns the first write error so a closed pipe surfaces immediately instead of silently truncating.

func RenderTerminal

func RenderTerminal(w io.Writer, p Plan) error

RenderTerminal writes the standardized per-repo block (one block per row) using render.RenderRepoTermBlocks. The block matches the format emitted by scan, clone-next, and probe so users learn one shape regardless of which command produced it.

func TransportTally

func TransportTally(results []Result) (sshCount, httpsCount, otherCount int)

TransportTally collapses ClassifyScheme's seven buckets into the three columns the user-facing summary reports: ssh, https, other. Returned in that order so call sites can format with a single printf without referencing field names.

func WriteReport

func WriteReport(results []Result) (string, error)

WriteReport persists the result set as CSV under .gitmap/. The timestamp suffix (Unix seconds) lets users keep a history of runs without one overwriting another. Returns the absolute path for the caller to surface in the summary header. On directory- create failure, returns "" + the error so the caller can decide whether the failure is fatal (it isn't — clones already happened, the report is bonus).

func WriteReportJSON

func WriteReportJSON(results []Result) (string, error)

WriteReportJSON persists the result set as a versioned JSON envelope under .gitmap/. Mirrors WriteReport's contract: same dir, same timestamp suffix (so the CSV and JSON for one run sort adjacently), absolute path on success, ("", err) on failure so callers can decide whether to surface the failure (they should: the JSON path is shown in the terminal summary).

Types

type BeforeRowHook

type BeforeRowHook func(index, total int, row Row, dest string)

BeforeRowHook is invoked once per row, just before the row's git clone runs. See file header for the parameter contract.

type Plan

type Plan struct {
	// Source is the absolute or user-supplied path the plan was
	// read from. Echoed verbatim in the dry-run header.
	Source string
	// Format is "json" or "csv" — used by the dry-run header so the
	// user can confirm we parsed the file the way they expected.
	Format string
	// Rows is the deduplicated, validated list of clones to perform.
	// Order matches the on-disk order so dry-run output is stable
	// across runs of the same file.
	Rows []Row
}

Plan is the validated, in-memory representation of one input file. Built by ParseFile from either JSON or CSV; consumed by Render (dry-run) and Execute. The Source field carries the on-disk path so the dry-run header can echo it back to the user without the caller having to thread the original argument through.

func ParseFile

func ParseFile(path string) (Plan, error)

ParseFile is the package's only public parser entry point. Returns a fully-validated Plan or a wrapped error explaining where parsing failed (file open, format mismatch, row N invalid).

type Result

type Result struct {
	Row      Row
	Dest     string // resolved (after DeriveDest fallback)
	Status   string
	Detail   string
	Duration time.Duration
}

Result is one row's outcome. Status is one of "ok" | "skipped" | "failed". Detail is human-readable context: for "ok" the empty string; for "skipped" the reason ("dest exists"); for "failed" the trimmed git stderr (capped at GitErrorTrimLimit chars to keep the summary table readable).

func Execute

func Execute(plan Plan, cwd string, progress io.Writer) []Result

Execute runs every row sequentially and writes per-row progress lines to progress (typically os.Stderr; pass io.Discard to silence). Returns the full result slice — callers render the summary AFTER Execute returns so a write failure on progress doesn't truncate the result set.

Working directory: each clone runs in `cwd`. Empty cwd → use the current process cwd at call time.

func ExecuteWithHooks

func ExecuteWithHooks(plan Plan, cwd string, progress io.Writer,
	beforeRow BeforeRowHook) []Result

ExecuteWithHooks is Execute + a per-row BeforeRow callback. The body is a copy of Execute's loop with the hook insertion point; kept as a separate function (rather than refactoring Execute to accept an optional hook) so the existing Execute signature — and every test that calls it — stays untouched.

func ExecuteWithHooksConcurrent

func ExecuteWithHooksConcurrent(plan Plan, cwd string, progress io.Writer,
	beforeRow BeforeRowHook, workers int) []Result

ExecuteWithHooksConcurrent is the parallel sibling of ExecuteWithHooks. See file header for the contract.

type Row

type Row struct {
	// URL is the clone source. Required. Validated for non-empty
	// and rough HTTPS/SSH/scp-style shape — we do NOT round-trip
	// through net/url.Parse because git accepts forms (scp-style
	// `user@host:path`) that net/url rejects.
	URL string
	// Dest is the target directory relative to cwd at execute
	// time. Empty → derived from the last URL segment (matches
	// `git clone <url>` default).
	Dest string
	// Branch optionally pins the initial branch with --branch.
	// Empty → git uses the remote's HEAD.
	Branch string
	// Depth optionally enables a shallow clone with --depth=N.
	// Zero → full history.
	Depth int
	// Checkout controls post-clone working-tree behavior. One of
	// "" / "auto" / "skip" / "force" (validated at parse time).
	// Empty string means "inherit the global default" (set via
	// --checkout on the CLI; defaults to "auto"). Per-row value
	// always wins over the global default. See
	// constants.FlagDescCloneFromCheckout for the semantics of each
	// mode.
	Checkout string
}

Row is one git-clone target. Every field except URL is optional; zero values translate to "use git's default" (HEAD branch, full history, dest derived from URL basename).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL