release

package
v1.4.1 Latest Latest
Warning

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

Go to latest
Published: May 11, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package release owns the end-of-cycle release mechanics for the ATDD pipeline driver: regex-remove `@Disabled`-style markers from in-scope test files, commit, and close the GitHub issue. It replaces the legacy `atdd-release` agent.

The package is deliberately mechanical and small. It exposes three primitives:

  • RemoveDisabledMarkers walks a set of roots, applies one regex per language, and rewrites or deletes lines that match. Each language has a built-in default Pattern; callers can override the full set via RemoveOptions.Patterns.
  • Commit shells out to `git add -A` + `git commit -m <msg>`. It REQUIRES a non-nil Confirmer — there is no way to silently commit. Callers that want non-interactive use must pass a Confirmer that auto-returns true; the explicit handshake makes "skip the gate" visible at the call site rather than buried in a flag.
  • CloseIssue shells out to `gh issue close <N>`.

Scope explicitly excluded in this package: Cobra wiring, auto-detection of in-scope test files from issue commits, and any driver-loop integration. Callers pass Roots explicitly.

Index

Constants

This section is empty.

Variables

View Source
var ErrCommitDeclined = errors.New("release: commit declined by user")

ErrCommitDeclined is returned by Commit when the Confirmer returns false.

View Source
var ErrConfirmerRequired = errors.New("release: Commit requires a Confirmer (no silent commits)")

ErrConfirmerRequired is returned by Commit when CommitOptions.Confirm is nil. This guards the "ask before commit" policy at the type-system level.

Functions

func CloseIssue

func CloseIssue(ctx context.Context, issueNum int, gh GhRunner) error

CloseIssue shells out to `gh issue close <N>`. The Confirmer policy is applied at the Commit boundary, not here — by convention closing an issue happens immediately after an already-approved final commit.

func Commit

func Commit(ctx context.Context, opts CommitOptions) error

Commit stages all working-tree changes (`git add -A`) and creates a commit with the given message — but only after the user explicitly approves via the supplied Confirmer.

The "ask before every commit" gate is firm user policy across the academy; nil Confirm returns ErrConfirmerRequired so a forgetful caller can't accidentally bypass it.

Types

type Changes

type Changes struct {
	Files []FileChange
}

Changes is the return type of RemoveDisabledMarkers.

func RemoveDisabledMarkers

func RemoveDisabledMarkers(ctx context.Context, opts RemoveOptions) (Changes, error)

RemoveDisabledMarkers walks each root, matches files against each pattern's glob, and removes (or rewrites) marker lines per the pattern. Returns a Changes slice describing every file that would change; if opts.DryRun is true, no file is modified.

Ordering: roots are walked in the order given; within each root files are visited in lexical order (the default of filepath.WalkDir). Patterns are applied in the order given; within a single file every pattern that matches the glob runs, in order, so a file matching multiple patterns (rare) accumulates the union of edits.

type CommitOptions

type CommitOptions struct {
	Message   string
	Confirm   Confirmer // mandatory; nil → error.
	GitRunner GitRunner // optional injection point for tests; nil → real exec.
	Stdout    io.Writer // optional; nil → os.Stdout. Diff summary goes here.
}

CommitOptions controls a Commit call.

type Confirmer

type Confirmer func(prompt string) (bool, error)

Confirmer asks the user to approve a destructive action. Implementations return (true, nil) to proceed, (false, nil) to abort cleanly, or (false, err) on I/O failure. A nil Confirmer is rejected by Commit; the "ask before every commit" gate is firm user policy and the only way to opt out is to pass a Confirmer that auto-returns true.

func InteractiveConfirmer

func InteractiveConfirmer(stdin io.Reader, stdout io.Writer) Confirmer

InteractiveConfirmer returns a Confirmer that writes the prompt to stdout and reads one line from stdin. "y", "yes", "Y", "YES" → true; everything else → false. EOF on stdin is treated as a decline (false, nil), matching the "silence = no" rule in shared-commit-confirmation.md.

type FileChange

type FileChange struct {
	Path         string
	LinesRemoved []int // 1-indexed line numbers fully removed
	LinesEdited  []int // 1-indexed line numbers rewritten in place (e.g. .NET Skip drop)
	PatternName  string
}

FileChange records the per-file outcome of a RemoveDisabledMarkers pass.

type GhRunner

type GhRunner interface {
	Run(ctx context.Context, args ...string) ([]byte, error)
}

GhRunner is the test seam for `gh` invocations.

type GitRunner

type GitRunner interface {
	Run(ctx context.Context, args ...string) ([]byte, error)
}

GitRunner is the test seam for `git` invocations. Tests pass a fake; production callers pass nil and Commit uses the real `exec` runner.

type Pattern

type Pattern struct {
	Name        string
	Glob        string
	Line        *regexp.Regexp
	LineRewrite func(string) string
	Imports     *regexp.Regexp
}

Pattern describes how to find and remove a disabled-marker in one language. A file is matched when its path satisfies Glob; once matched, every line matching Line is removed from the file. If LineRewrite is non-nil, matched lines are passed through it instead of removed (used by the .NET `[Fact(Skip = "…")]` rewrite, which keeps the attribute and drops only the Skip parameter).

Imports is optional: if non-nil and the file has no remaining matches after removal, lines matching Imports are also removed (used to clean up `import org.junit.jupiter.api.Disabled;` once the last `@Disabled` is gone).

func DefaultPatterns

func DefaultPatterns() []Pattern

DefaultPatterns returns the built-in marker patterns for Java, .NET, and TypeScript, anchored to the canonical forms in shop/docs/atdd/code/language-equivalents.md:

  • Java: `@Disabled("reason")` standalone line, plus `import org.junit.jupiter.api.Disabled;` cleanup once the last `@Disabled` is gone.
  • .NET (C#): `[Fact(Skip = "reason")]` / `[Theory(Skip = "reason")]` — keep the attribute, drop just the `Skip = "…"` parameter (and the resulting empty `()` or stray leading `, `). The standalone form `[Skip("…")]` is removed entirely.
  • TypeScript: `test.skip(true, "reason")` standalone line — removed entirely.

Callers that need to test a non-default form should construct their own Pattern slice and pass it via RemoveOptions.Patterns.

type RemoveOptions

type RemoveOptions struct {
	// Roots are directories to walk recursively, e.g.
	// ["system-test/java", "system-test/dotnet", "system-test/typescript"].
	// Each root is walked independently; missing roots are skipped (not an
	// error) so callers can pass all three language roots unconditionally.
	Roots []string

	// DryRun, if true, returns the FileChange list without modifying any
	// file on disk.
	DryRun bool

	// Patterns optionally overrides the built-in default patterns. Nil
	// (the common case) uses DefaultPatterns(). Callers passing a custom
	// slice replace the defaults entirely — there is no merge.
	Patterns []Pattern
}

RemoveOptions controls a RemoveDisabledMarkers run.

Jump to

Keyboard shortcuts

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