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
- Variables
- func BranchName(id uuid.UUID) string
- func ParseBranchName(name string) (id uuid.UUID, ok bool)
- type BranchPushedEvent
- type CleanupEntry
- type CleanupOptions
- type ErrNoRun
- type FailedStep
- type FailedTest
- type GitError
- type JobFailedEvent
- type JobFailure
- type JobRef
- type Observer
- type Options
- type Progress
- type ProjectError
- type RemoteBranch
- type Repo
- func (r *Repo) Close() error
- func (r *Repo) CommitTime(sha string) (time.Time, bool)
- func (r *Repo) DeleteBranch(ctx context.Context, branch string) error
- func (r *Repo) ListBranches(ctx context.Context) ([]RemoteBranch, error)
- func (r *Repo) Push(ctx context.Context, sha, branch string) error
- func (r *Repo) Snapshot(ctx context.Context, message string) (Snapshot, error)
- type RunError
- type RunStartedEvent
- type RunSummaryEvent
- type Snapshot
- type WorkflowProgress
Constants ¶
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.
const ( EventBranchPushed = "branch_pushed" EventRunStarted = "run_started" EventJobFailed = "job_failed" EventRunSummary = "run_summary" )
Event types, as they appear in each event's "type" field.
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.
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.
const DefaultRemote = "origin"
DefaultRemote is the remote preflight branches are pushed to.
const DefaultRunWait = 2 * time.Minute
DefaultRunWait is how long a preflight waits for its push to start a run.
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 ¶
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.
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.
var ErrTimedOut = errTimedOut
ErrTimedOut is returned when the run did not end within Options.Timeout.
Functions ¶
func BranchName ¶
BranchName returns the remote branch name for the preflight with the given ID.
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 ¶
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.
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 ¶
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.
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 ¶
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 ¶
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 ¶
OpenRepo returns the repository containing dir, pushing to remote. Callers must Close it to release its file handles.
func (*Repo) CommitTime ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.