Help us shape the future of Go! Take the 2026 Go Developer Survey

preflight

package
v1.13.0 Latest Latest
Warning

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

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package preflight runs uncommitted local changes through CircleCI: it snapshots the working tree into a commit on a throwaway branch, pushes that branch so the project's push trigger starts a run, watches the run and reports failures as they happen, then deletes the branch.

Index

Constants

View Source
const (
	CleanupDeleted      = "deleted"
	CleanupWouldDelete  = "would_delete"
	CleanupSkippedRun   = "skipped_running"
	CleanupSkippedStart = "skipped_starting"
	CleanupDeleteFailed = "delete_failed"
	CleanupLookupFailed = "lookup_failed"
)

Cleanup statuses, as reported in CleanupEntry.Status.

View Source
const (
	EventBranchPushed = "branch_pushed"
	EventRunStarted   = "run_started"
	EventJobFailed    = "job_failed"
	EventRunSummary   = "run_summary"
)

Event types, as they appear in each event's "type" field.

View Source
const (
	OutcomeSucceeded   = "succeeded"
	OutcomeFailed      = "failed"
	OutcomeCanceled    = "canceled"
	OutcomeFailing     = "failing"
	OutcomeTimedOut    = "timed_out"
	OutcomeInterrupted = "interrupted"
)

Run outcomes reported in RunSummaryEvent.Outcome. The first three are the run's own result; the rest describe a preflight that stopped before the run ended.

View Source
const BranchPrefix = "cci/preflight/"

BranchPrefix is the namespace every preflight branch is pushed under. The UUID that follows it identifies one preflight, so cleanup can target it.

View Source
const DefaultRemote = "origin"

DefaultRemote is the remote preflight branches are pushed to.

View Source
const DefaultRunWait = 2 * time.Minute

DefaultRunWait is how long a preflight waits for its push to start a run.

View Source
const (
	// MaxFailedTests caps the failed tests listed per job, so a job that fails
	// hundreds of tests does not flood an agent's context. The full count is
	// still reported.
	MaxFailedTests = 25
)

Variables

View Source
var ErrNoCommits = errors.New("the repository has no commits yet")

ErrNoCommits is returned by Snapshot when HEAD does not point at a commit yet. The snapshot is a child of HEAD, so a repository with no history has nothing to build it on.

View Source
var ErrRemoteAuth = errors.New("authentication with the remote failed")

ErrRemoteAuth wraps a remote operation that failed to authenticate. go-git authenticates SSH remotes through ssh-agent and does not run git credential helpers, so an HTTPS remote that relies on one cannot be pushed to.

View Source
var ErrTimedOut = errTimedOut

ErrTimedOut is returned when the run did not end within Options.Timeout.

Functions

func BranchName

func BranchName(id uuid.UUID) string

BranchName returns the remote branch name for the preflight with the given ID.

func ParseBranchName

func ParseBranchName(name string) (id uuid.UUID, ok bool)

ParseBranchName extracts the preflight ID from a branch name produced by BranchName. ok is false for any other branch.

Types

type BranchPushedEvent

type BranchPushedEvent struct {
	Type   string    `json:"type"`
	ID     uuid.UUID `json:"preflight_id"`
	Branch string    `json:"branch"`
	Commit string    `json:"commit"`
	// Base is Commit's parent: where HEAD forked from BaseBranch, or HEAD
	// when the default branch could not be determined (BaseBranch empty).
	Base       string `json:"base"`
	BaseBranch string `json:"base_branch,omitempty"`
	// Changes is false when the working tree matched Base, in which case
	// Commit is Base itself.
	Changes bool `json:"changes"`
}

BranchPushedEvent reports the snapshot commit and the branch it was pushed to. The branch outlives the preflight only with --no-cleanup or --no-watch.

type CleanupEntry

type CleanupEntry struct {
	ID     uuid.UUID  `json:"preflight_id"`
	Branch string     `json:"branch"`
	Commit string     `json:"commit"`
	RunID  *uuid.UUID `json:"run_id,omitempty"`
	Status string     `json:"status"`
	Error  string     `json:"error,omitempty"`
}

CleanupEntry is the outcome for one preflight branch.

func Cleanup

func Cleanup(ctx context.Context, opts CleanupOptions) ([]CleanupEntry, error)

Cleanup deletes preflight branches left on the remote — by a preflight run with --no-cleanup or --no-watch, or one that was killed before it could clean up. It returns one entry per branch considered; a branch that could not be deleted, or whose run could not be looked up, is reported in its entry rather than stopping the rest. Without IncludeRunning, a branch whose run is unknown is kept: it may belong to a preflight that is still watching. So is one with no run yet whose commit is recent, as a preflight waiting for its run to start would have. Every branch is looked up before any is deleted, so Confirm sees the whole set.

type CleanupOptions

type CleanupOptions struct {
	Client    *apiclient.Client
	Repo      *Repo
	ProjectID string
	// ID limits the cleanup to one preflight. Zero means every preflight
	// branch on the remote.
	ID uuid.UUID
	// IncludeRunning also deletes branches whose run has not ended, or has
	// not started within startGrace of the commit. By default those are
	// skipped: the preflight that pushed them may still be watching or waiting.
	IncludeRunning bool
	DryRun         bool
	// Confirm is called with the branches about to be deleted, once they are
	// known and before any is deleted; an error stops the cleanup with nothing
	// deleted. It is not called for a dry run, or when nothing is to be
	// deleted. Nil means no confirmation.
	Confirm func(toDelete []CleanupEntry) error
}

CleanupOptions configures Cleanup.

type ErrNoRun

type ErrNoRun struct {
	Branch string
	Waited time.Duration
}

ErrNoRun is returned when no run appeared for the pushed branch in time. The branch was pushed, so the project's triggers did not fire for it.

func (*ErrNoRun) Error

func (e *ErrNoRun) Error() string

type FailedStep

type FailedStep struct {
	Execution int    `json:"execution"`
	Num       int    `json:"num"`
	Name      string `json:"name"`
	ExitCode  *int   `json:"exit_code,omitempty"`
}

FailedStep is one failed step of a failed job.

type FailedTest

type FailedTest struct {
	Classname string `json:"classname"`
	Name      string `json:"name"`
	Message   string `json:"message"`
}

FailedTest is one failed test from a job's stored test results.

type GitError

type GitError struct {
	Op  string
	Err error
}

GitError is a failed git operation. Op names what was being done ("push to origin", "snapshot the working tree"); Err is go-git's explanation.

func (*GitError) Error

func (e *GitError) Error() string

func (*GitError) Unwrap

func (e *GitError) Unwrap() error

type JobFailedEvent

type JobFailedEvent struct {
	Type string     `json:"type"`
	Job  JobFailure `json:"job"`
}

JobFailedEvent reports one job that failed, as soon as it is seen.

type JobFailure

type JobFailure struct {
	ID          uuid.UUID    `json:"id"`
	Name        string       `json:"name"`
	WorkflowID  uuid.UUID    `json:"workflow_id"`
	Workflow    string       `json:"workflow"`
	URL         string       `json:"url"`
	FailedSteps []FailedStep `json:"failed_steps"`
	// FailedTests holds at most MaxFailedTests entries; FailedTestCount is
	// the full number.
	FailedTests     []FailedTest `json:"failed_tests"`
	FailedTestCount int          `json:"failed_test_count"`
}

JobFailure describes a failed job: where it ran, which steps failed, and which tests failed when the job stored test results.

type JobRef

type JobRef struct {
	ID       uuid.UUID `json:"id"`
	Name     string    `json:"name"`
	Workflow string    `json:"workflow"`
	URL      string    `json:"url"`
}

JobRef identifies a job in the run summary.

type Observer

type Observer interface {
	BranchPushed(BranchPushedEvent) error
	WaitingForRun(branch string)
	RunStarted(RunStartedEvent) error
	Progress(Progress)
	JobFailed(JobFailedEvent) error
	RunSummary(RunSummaryEvent) error
}

Observer receives everything a preflight reports while it runs. The command layer implements one observer that writes the JSONL event stream and one that writes human-readable lines.

type Options

type Options struct {
	Client      *apiclient.Client
	Repo        *Repo
	ProjectSlug string
	// ID names the preflight and its branch. Zero means generate one.
	ID uuid.UUID
	// Watch follows the run after it starts. Without it, Run returns once the
	// run exists and the branch is left in place.
	Watch bool
	// FailFast stops at the first failed job instead of waiting for the run.
	FailFast bool
	// Cleanup cancels a run the preflight stopped watching early and deletes
	// the branch.
	Cleanup  bool
	Interval time.Duration
	Timeout  time.Duration
	// RunWait is how long to wait for the push to start a run.
	RunWait  time.Duration
	RunURL   func(runID uuid.UUID) string
	JobURL   func(workflowID, jobID uuid.UUID) string
	Observer Observer
}

Options configures Run.

type Progress

type Progress struct {
	Elapsed   time.Duration
	Workflows []WorkflowProgress
}

Progress is a snapshot of the run's workflows, reported each time one of them, or one of their jobs, changes status. It is for human display and is not part of the event stream.

type ProjectError

type ProjectError struct {
	Slug string
	Err  error
}

ProjectError is a failed lookup of the project being preflighted, so the caller can tell it from an API failure later on.

func (*ProjectError) Error

func (e *ProjectError) Error() string

func (*ProjectError) Unwrap

func (e *ProjectError) Unwrap() error

type RemoteBranch

type RemoteBranch struct {
	ID     uuid.UUID
	Name   string
	Commit string
}

RemoteBranch is a preflight branch that exists on the remote.

type Repo

type Repo struct {
	// Dir is the top level of the working tree.
	Dir string
	// Remote is the remote branches are pushed to and deleted from.
	Remote string
	// contains filtered or unexported fields
}

Repo is a git checkout preflight operates on.

func OpenRepo

func OpenRepo(dir, remote string) (*Repo, error)

OpenRepo returns the repository containing dir, pushing to remote. Callers must Close it to release its file handles.

func (*Repo) Close

func (r *Repo) Close() error

Close releases the repository's file handles.

func (*Repo) CommitTime

func (r *Repo) CommitTime(sha string) (time.Time, bool)

CommitTime returns when the commit sha was made, and false when it is not in the local repository — as for a preflight pushed from another clone.

func (*Repo) DeleteBranch

func (r *Repo) DeleteBranch(ctx context.Context, branch string) error

DeleteBranch deletes branch from the remote.

func (*Repo) ListBranches

func (r *Repo) ListBranches(ctx context.Context) ([]RemoteBranch, error)

ListBranches returns every preflight branch on the remote. Branches under BranchPrefix whose name is not a UUID were not made by preflight and are left out.

func (*Repo) Push

func (r *Repo) Push(ctx context.Context, sha, branch string) error

Push pushes sha to branch on the remote. Hooks are never run: go-git does not run them, and the point of a preflight is to let CI judge work in progress that a pre-push hook might reject.

func (*Repo) Snapshot

func (r *Repo) Snapshot(ctx context.Context, message string) (Snapshot, error)

Snapshot records the whole working tree — staged, unstaged and untracked files, minus anything .gitignore excludes — as one commit.

The commit's parent is where HEAD forked from the remote's default branch, not HEAD, so its diff shows everything being changed: the branch's own commits and the uncommitted work together. The tree built is the working tree either way. When the working tree matches the base there is nothing to show, and the base itself is returned rather than an empty commit.

Nothing the user can see changes: the working tree is staged into an in-memory copy of the index, and the snapshot's objects are written without moving any ref, so HEAD, the real index, the working tree and the stash are all left as they were.

type RunError

type RunError struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

RunError is an error the run itself carries, such as a config that would not compile.

type RunStartedEvent

type RunStartedEvent struct {
	Type    string    `json:"type"`
	RunID   uuid.UUID `json:"run_id"`
	Project string    `json:"project"`
	Branch  string    `json:"branch"`
	Commit  string    `json:"commit"`
	URL     string    `json:"url"`
}

RunStartedEvent reports the run the push triggered.

type RunSummaryEvent

type RunSummaryEvent struct {
	Type    string    `json:"type"`
	RunID   uuid.UUID `json:"run_id"`
	Outcome string    `json:"outcome"`
	// Ended is false when the preflight stopped before the run did: at the
	// first failure, on timeout or on Ctrl+C.
	Ended      bool       `json:"ended"`
	DurationMS int64      `json:"duration_ms"`
	URL        string     `json:"url"`
	Errors     []RunError `json:"errors"`
	FailedJobs []JobRef   `json:"failed_jobs"`
	// RunCanceled is true when the preflight canceled what was left of the
	// run on its way out.
	RunCanceled   bool   `json:"run_canceled"`
	BranchDeleted bool   `json:"branch_deleted"`
	CleanupError  string `json:"cleanup_error,omitempty"`
}

RunSummaryEvent is the last event of every watched preflight.

func Run

func Run(ctx context.Context, opts Options) (*RunSummaryEvent, error)

Run snapshots the working tree (see Repo.Snapshot), pushes it to a preflight branch, waits for the push to start a run and, with Options.Watch, follows that run to the end (or to the first failure), then cleans up.

The returned summary is nil only when no run was watched. A run that failed is not an error: the summary's Outcome says how it ended, and the caller decides the exit status. The error reports what stopped the preflight itself — a ProjectError, a git or API failure, ErrNoRun, ErrTimedOut, or the context being canceled.

type Snapshot

type Snapshot struct {
	// Commit is the SHA to push.
	Commit string
	// Base is Commit's parent: where HEAD forked from the remote's default
	// branch, or HEAD itself when that cannot be determined.
	Base string
	// BaseBranch is the default branch Base was found on, or "" when Base
	// fell back to HEAD.
	BaseBranch string
	// Changed is false when the working tree matched Base, in which case
	// Commit is Base itself.
	Changed bool
}

Snapshot is the commit a preflight pushes.

type WorkflowProgress

type WorkflowProgress struct {
	Name   string
	Status string
	Jobs   int
	Done   int
}

WorkflowProgress is one workflow's status in a Progress snapshot.

Jump to

Keyboard shortcuts

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