clonenow

package
v0.0.0-...-21ea6c5 Latest Latest
Warning

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

Go to latest
Published: Apr 28, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package clonenow implements the `gitmap clone-now <file>` workflow: re-run `git clone` against the JSON / CSV / text artifacts produced by `gitmap scan`, honoring the recorded folder structure and a user-selected SSH/HTTPS mode.

Why a separate package (not clonefrom or cloner)?

  • clonefrom is plan-driven (user-authored row schema).
  • cloner is the in-memory scan-pipeline cloner that runs as part of the scan command itself.
  • clonenow is a round-trip cloner: the input is gitmap's own scan output, so the schema (RepoName, HTTPSUrl, SSHUrl, Branch, RelativePath, ...) is fixed and we honor RelativePath verbatim so the destination tree is byte-identical to the original.

Splitting keeps each cloner's contract tight and avoids forcing clone-from to grow scan-record-aware fields.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildGitArgs

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

BuildGitArgs returns the argv (excluding the "git" binary) that Execute would pass to exec.Command for the given row. Mirrors the real call site in executeRow exactly — no recomputation.

func DeriveDest

func DeriveDest(url string) string

DeriveDest returns the directory git would create by default for the given URL: the trailing path segment, with a single trailing `.git` removed. Public so executor + parser agree on the fallback.

func KnownScanFields

func KnownScanFields() []string

KnownScanFields returns the alphabetically-sorted list of JSON object keys / CSV header column names accepted by `gitmap clone <file>` (clone-now path). Returned slice is a fresh copy: callers may mutate it freely without affecting validation.

Stable ordering is part of the contract: the JSON-Schema emitter embeds these as enum-of-properties and golden tests pin the resulting bytes.

func Render

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

Render writes the dry-run preview: a header echoing the source + format + mode, then one block per row showing the URL that would be used and the destination path it would land at. Returns any io.Writer error so the CLI can exit 1 on a broken pipe instead of silently truncating.

func RenderSummary

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

RenderSummary writes the end-of-batch tally + the per-row status table. Always prints, even when every row is "ok", so users have a definitive "this is what happened" record without hunting through the per-row progress lines.

func RequiredScanURLFields

func RequiredScanURLFields() []string

RequiredScanURLFields returns the names of the URL-bearing fields at least one of which MUST be present on every input row. Mirrors validateJSONElement / validateCSVBody. Sorted; fresh copy per call.

Types

type BeforeRowHook

type BeforeRowHook func(index, total int, row Row, url, 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 path the plan was read from. Echoed
	// verbatim in the dry-run header.
	Source string
	// Format is "json" | "csv" | "text" -- used by the dry-run
	// header so the user can confirm we parsed the file the way
	// they expected.
	Format string
	// Mode is "https" | "ssh" -- the URL column the executor will
	// prefer for each row. Captured on the Plan (not just on the
	// CLI cfg) so the dry-run renderer and the executor see the
	// same value, with no risk of drift.
	Mode string
	// OnExists is "skip" | "update" | "force" -- the policy applied
	// when the destination already contains a git repository.
	// Captured on the Plan for the same reason as Mode: a single
	// source of truth shared by render + execute prevents the
	// dry-run preview from advertising a behavior different from
	// what the executor would actually do.
	OnExists 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 JSON, CSV, or plain-text scan output and consumed by Render (dry-run) and Execute.

func ParseFile

func ParseFile(path, format, mode, onExists string) (Plan, error)

ParseFile is the package's only public parser entry point. `format` may be "" (auto-detect from extension) or one of constants.CloneNowFormat{JSON,CSV,Text}. `mode` and `onExists` must already be validated by the caller -- ParseFile records them on the Plan but does not interpret them (that's the executor / renderer's job).

type Result

type Result struct {
	Row      Row
	URL      string // the URL actually used (after mode + fallback)
	Dest     string // the destination path actually used (after cwd join)
	Status   string
	Detail   string
	Duration time.Duration
}

Result is one row's outcome. Status is one of "ok" | "skipped" | "failed". Detail carries human-readable context: empty for ok, MsgCloneNowDestExists for skipped, the trimmed git stderr for failed (capped at CloneNowErrTrimLimit chars).

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` -> the process cwd at call time, captured once up-front so a row that shells out into a subprocess (which mutates cwd) doesn't shift where subsequent rows land.

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 {
	// RepoName is shown in progress + summary lines. Derived from
	// the scan record's RepoName when present; falls back to the
	// last URL segment for text-format inputs.
	RepoName string
	// HTTPSUrl is the recorded https-style clone URL (may be empty
	// for text-format rows when the source line was an ssh URL).
	HTTPSUrl string
	// SSHUrl is the recorded ssh-style clone URL (may be empty for
	// text-format rows when the source line was an https URL).
	SSHUrl string
	// Branch optionally pins the initial branch with --branch.
	// Empty -> git uses the remote's HEAD. Honored for JSON / CSV
	// rows; text-format rows always come through with empty Branch
	// because the plain `git clone <url> <dir>` line carries no
	// branch information.
	Branch string
	// RelativePath is the destination directory relative to cwd at
	// execute time. Always non-empty after parsing -- ParseFile
	// fills it in from the URL basename when the source row didn't
	// supply one (text-format with no explicit destination arg).
	RelativePath string
}

Row is one scan-record-shaped clone target. Unlike clonefrom.Row (which carries raw URL + optional branch/depth) this carries the full pair of HTTPS / SSH URLs + the recorded relative folder so the executor can pick a URL based on the mode flag without re- parsing or re-deriving anything.

func (Row) PickURL

func (r Row) PickURL(mode string) string

PickURL returns the URL to clone with for the given mode, falling back to the other mode if the preferred URL is missing on this row. Returns "" only when both URL fields are empty -- a row that reaches Execute with empty PickURL is reported as a failure with MsgCloneNowNoURL rather than silently skipped.

Centralized here (rather than duplicated in execute / render) so the dry-run preview and the actual git invocation always agree on which URL would be used.

Jump to

Keyboard shortcuts

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