Documentation
¶
Overview ¶
Package forge is the platform abstraction. It owns the Forge interface, the domain types both implementations speak, the shared HTTP transport carrying the timeout and the bounded retry, and the sentinel errors callers classify on.
Index ¶
Constants ¶
const ( // RequestTimeout bounds a single request, retries excluded. RequestTimeout = 30 * time.Second // MaxAttempts is the total number of attempts, so at most two retries // follow the first try. MaxAttempts = 3 // BaseBackoff is the first pause between attempts; each subsequent pause // doubles it. BaseBackoff = 500 * time.Millisecond )
The bounds every platform call carries (CLI-005, FR-046). They are forgectl's own, applied by one transport both clients are given, so the retry policy never comes from a client library (R5).
Variables ¶
var ( // ErrUnknownHost reports a remote host matching no instance. It is an alias // of the config sentinel so a caller can classify it without importing both // packages (FR-003, R11). ErrUnknownHost = config.ErrUnknownHost // ErrNoCredential reports an instance whose credential environment variable // is unset or empty, raised before any network call (FR-005, R11). ErrNoCredential = config.ErrNoCredential // ErrInsufficientRights reports a credential that may not perform the // operation. It makes the affected check SKIP with that reason rather than // fail, so a token without token-management rights does not turn a healthy // repository into a drifted one (FR-030). ErrInsufficientRights = errors.New("the credential lacks the rights for this operation") // ErrNotSupported reports an operation the platform has no equivalent for. // A generated variable on GitHub raises it, and is skipped with a warning // rather than failing the run (FR-029). ErrNotSupported = errors.New("not supported on this platform") // ErrTokenLifetime reports a requested token lifetime above the instance // maximum. Its message carries the platform's own wording, which states the // permitted maximum (FR-052). ErrTokenLifetime = errors.New("the requested token lifetime exceeds the instance maximum") // ErrMaskRejected reports a value the platform refuses to mask. It drives // the single unmasked retry of FR-043, and its message names the constraint // — never the value (R7). ErrMaskRejected = errors.New("the platform refuses to mask this value") )
Functions ¶
Types ¶
type Forge ¶
Forge is both halves: what apply is given, and what each platform package implements in full.
type ProjectToken ¶
ProjectToken is one project access token as the platform reports it.
func (ProjectToken) DaysRemaining ¶
func (t ProjectToken) DaysRemaining(now time.Time) int
DaysRemaining reports how many whole days are left before the token expires, counted from now. A token that has already expired reports zero.
type ProjectTokenRequest ¶
type ProjectTokenRequest struct {
Name string
Scopes []string
Role config.AccessLevel
ExpiresAt time.Time
}
ProjectTokenRequest is the token forgectl asks the platform to create.
type Protection ¶
type Protection struct {
// Exists is false when the branch carries no protection at all.
Exists bool
// AllowForcePush is GitHub's absent non_fast_forward rule, and GitLab's
// allow_force_push flag.
AllowForcePush bool
// AllowDelete is GitHub's absent deletion rule. On GitLab it is ALWAYS
// false: deleting a protected branch is denied with no toggle (R9), so a
// configured allow_delete: false is satisfied by protection existing at
// all and must never be reported as drift.
AllowDelete bool
// PushAccessLevel is GitLab only. It carries the zero value on GitHub,
// which models no equivalent, and is therefore never compared there
// (FR-026).
PushAccessLevel config.AccessLevel
}
Protection is the state of a protected branch, expressed in the terms both platforms can be mapped onto.
type Reader ¶
type Reader interface {
// DefaultBranch reports the branch the platform serves as default (FR-023).
DefaultBranch(ctx context.Context) (string, error)
// BranchExists reports whether the named branch is on the platform. It is
// what lets the protection check skip with a reason rather than fail when
// the target branch is not there yet (FR-024).
BranchExists(ctx context.Context, name string) (bool, error)
// Protection reads the protection in force on a branch (FR-024).
Protection(ctx context.Context, branch string) (Protection, error)
// TagProtection lists the tag patterns the platform protects (FR-025).
TagProtection(ctx context.Context) ([]string, error)
// Variable reads what the platform reports about a CI variable (FR-026).
Variable(ctx context.Context, name string, secret bool) (VariableState, error)
}
Reader is the read-only half of the platform abstraction: everything the compliance layer needs to judge a repository, and nothing that could change it.
internal/compliance accepts a Reader and never a Forge. FR-031's "check MUST NOT modify any local or platform state" is therefore enforced by the type system: the evaluation layer is handed no method that writes, so no amount of future editing there can make check mutate anything.
Every method takes a context first, so every call is cancellable and bounded (Constitution VI).
type Target ¶
type Target struct {
Instance config.Instance
Owner string
Repo string
// Credential is the token read from the environment variable the instance
// names. It is never logged and never rendered (FR-054, CLI-004).
Credential string
}
Target is the single instance a run operates against, together with the credential that reaches it. One run touches exactly one instance (FR-032).
func Resolve ¶
Resolve matches a remote host against the configured instances, falling back to the built-in definitions, and reads the credential from the environment variable that instance names.
Both failures happen before any network call: an unmatched host wraps ErrUnknownHost (FR-003) and a missing credential wraps ErrNoCredential (FR-005). Neither message ever carries the credential.
type TokenIssuer ¶
type TokenIssuer interface {
// ProjectTokens lists the active tokens carrying the given name (FR-028).
ProjectTokens(ctx context.Context, name string) ([]ProjectToken, error)
// CreateProjectToken creates a token and returns it with its value, which
// the platform discloses exactly once (R8).
//
// The second return is that value. It goes straight into SetVariable and is
// never stored anywhere else (FR-050).
CreateProjectToken(ctx context.Context, req ProjectTokenRequest) (ProjectToken, string, error)
// RevokeProjectToken revokes one token by id (FR-048).
RevokeProjectToken(ctx context.Context, id int) error
}
TokenIssuer is the part of the platform that issues project-scoped access tokens. Only GitLab implements it; a Forge that does not is what makes a generated variable skip with a warning rather than fail (FR-029).
type Transport ¶
type Transport struct {
// Base is the round tripper actually performing the request. A nil Base
// uses http.DefaultTransport.
Base http.RoundTripper
// MaxAttempts overrides the default when set, so a test need not wait.
MaxAttempts int
// BaseBackoff overrides the default when set.
BaseBackoff time.Duration
// Sleep is the pause function, replaced in tests. A nil Sleep uses time.Sleep.
Sleep func(time.Duration)
}
Transport retries a request that the platform could not serve yet — a rate limit or a server-side failure — a bounded number of times, backing off between attempts, and cancels the moment its context does.
A 4xx other than 429 is the platform's considered answer and is never retried: retrying a 404 or a 403 only wastes the maintainer's time.
type VariableState ¶
type VariableState struct {
Exists bool
// Masked is GitLab only.
Masked bool
// Protected is GitLab only.
Protected bool
// Value is GitLab only, and always empty on GitHub, whose Actions
// credentials are write-only.
Value string
// ValueReadable is false wherever the platform cannot disclose a value.
//
// It is an explicit field rather than an inference from the platform, so
// the comparison in internal/compliance reads as a fact about the value
// rather than a special case about one platform (FR-027).
ValueReadable bool
}
VariableState is what the platform reports about a CI variable.
type VariableWrite ¶
type VariableWrite struct {
Name string
Value string
// Secret selects the Actions credential store over the Actions variable
// store on GitHub. It has no effect on GitLab, where every variable is a
// CI variable (FR-026).
Secret bool
// Masked and Protected are GitLab attributes with no GitHub equivalent.
Masked bool
Protected bool
}
VariableWrite is one variable write, carrying the only value that ever crosses this package boundary.
type Writer ¶
type Writer interface {
// SetDefaultBranch makes name the platform default (FR-037, FR-038).
SetDefaultBranch(ctx context.Context, name string) error
// SetProtection puts the wanted protection in force on a branch (FR-037).
SetProtection(ctx context.Context, branch string, want Protection) error
// ProtectTag protects one tag pattern (FR-025).
ProtectTag(ctx context.Context, pattern string) error
// SetVariable creates or updates a CI variable (FR-042).
//
// This is the only method that receives a value. It writes it to the
// platform and nowhere else: the value is never logged, never wrapped into
// an error, and never returned (FR-050, FR-054).
SetVariable(ctx context.Context, write VariableWrite) error
}
Writer is the half that changes things. Only internal/apply is ever given one.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package forgetest provides a scriptable fake implementation of forge.Forge for tests, so the evaluation and execution layers are exercised with no network.
|
Package forgetest provides a scriptable fake implementation of forge.Forge for tests, so the evaluation and execution layers are exercised with no network. |
|
Package github implements forge.Forge against the GitHub REST API through go-github.
|
Package github implements forge.Forge against the GitHub REST API through go-github. |
|
Package gitlab implements forge.Forge against the GitLab REST API through the official client, and adds the project access token lifecycle, which has no equivalent on the other platform.
|
Package gitlab implements forge.Forge against the GitLab REST API through the official client, and adds the project access token lifecycle, which has no equivalent on the other platform. |