Documentation
¶
Overview ¶
Package github fetches workflow files to audit, from a repository, from a local checkout, or from the bundled fixtures.
Index ¶
Constants ¶
const WorkflowsDir = ".github/workflows"
WorkflowsDir is where GitHub Actions looks for workflow definitions.
Variables ¶
var ErrNotFound = errors.New("not found")
ErrNotFound marks a repository or path that does not exist, or that the token cannot see. The two are indistinguishable over the API by design.
Functions ¶
func IsWorkflowFile ¶
IsWorkflowFile reports whether a path names a workflow definition.
Types ¶
type Client ¶
type Client struct {
// Rate carries the limit state observed on the last call.
Rate RateState
// contains filtered or unexported fields
}
Client wraps the GitHub REST API for the handful of calls this tool makes.
func NewClient ¶
func NewClient(opts ClientOptions) (*Client, error)
NewClient builds a client, falling back to GITHUB_TOKEN then GH_TOKEN.
Both names are checked because GITHUB_TOKEN is what Actions injects and GH_TOKEN is what the gh CLI exports; a user who has authenticated with gh should not have to learn a third variable.
func (*Client) Authenticated ¶
Authenticated reports whether a token was supplied.
type ClientOptions ¶
type ClientOptions struct {
// Token authenticates the client. Empty means unauthenticated, which is
// allowed but rate limited to 60 requests an hour.
Token string
// BaseURL points at a GitHub Enterprise instance or a test server.
BaseURL string
// HTTPClient overrides the transport, used by tests.
HTTPClient *http.Client
}
ClientOptions configures a client.
type FixtureSource ¶
type FixtureSource struct {
// contains filtered or unexported fields
}
FixtureSource serves example workflows from a filesystem, normally the one embedded in the binary at build time.
It exists so the tool can be evaluated end to end with no token, no network, and no repository — which is how most people will first run it, and how the test suite runs it every time. Taking an fs.FS rather than embedding here keeps a single copy of the fixtures at the repository root: `go:embed` cannot reach outside its own package directory, and a second copy under internal/github would drift from the first the day someone edited one.
func NewFixtureSource ¶
func NewFixtureSource(fsys fs.FS) *FixtureSource
NewFixtureSource builds a source over a filesystem of workflow files.
func (*FixtureSource) Workflows ¶
func (s *FixtureSource) Workflows(_ context.Context) ([]WorkflowFile, error)
Workflows returns every workflow in the filesystem, sorted by name so two runs report findings in the same order.
type LocalSource ¶
type LocalSource struct {
// contains filtered or unexported fields
}
LocalSource reads workflows from a directory on disk.
This is the source that runs inside the GitHub Action: the repository is already checked out, so re-fetching the same files over the API would spend rate limit to learn nothing.
func NewLocalSource ¶
func NewLocalSource(root string) *LocalSource
NewLocalSource builds a source over a directory. The directory may be a repository root, a `.github/workflows` directory, or a single file.
func (*LocalSource) Workflows ¶
func (s *LocalSource) Workflows(_ context.Context) ([]WorkflowFile, error)
Workflows walks the directory and returns every workflow file, sorted by path so two runs over the same tree report findings in the same order.
type RateLimitError ¶
RateLimitError is returned when the API refuses further calls.
It is a distinct type because the right response differs from every other failure: waiting works, and the message should say how long and — for an unauthenticated caller stuck at 60 requests an hour — that a token raises the ceiling by more than eighty times.
func (*RateLimitError) Error ¶
func (e *RateLimitError) Error() string
Error describes the limit and what to do about it.
func (*RateLimitError) RetryAfter ¶
func (e *RateLimitError) RetryAfter() time.Duration
RetryAfter is how long a caller should wait before trying again.
func (*RateLimitError) Retryable ¶
func (e *RateLimitError) Retryable() bool
Retryable tells utils.Do that waiting is worthwhile.
type RateState ¶
type RateState struct {
Limit int `json:"limit"`
Remaining int `json:"remaining"`
Reset time.Time `json:"reset"`
}
RateState is what the API last told us about the rate limit.
type RepoSource ¶
type RepoSource struct {
// contains filtered or unexported fields
}
RepoSource fetches a repository's workflows over the API.
func NewRepoSource ¶
func NewRepoSource(client *Client, owner, repo, ref string) *RepoSource
NewRepoSource builds a source for owner/repo. An empty ref means the repository's default branch.
func (*RepoSource) Workflows ¶
func (s *RepoSource) Workflows(ctx context.Context) ([]WorkflowFile, error)
Workflows lists and downloads every workflow definition in the repository.
The listing and each download are retried independently: a secondary rate limit part-way through would otherwise throw away the files already fetched and spend the same budget again on the next run.
type Source ¶
type Source interface {
// Name describes the source for report headers.
Name() string
// Workflows returns every workflow file to audit.
Workflows(ctx context.Context) ([]WorkflowFile, error)
}
Source produces the workflows to audit.
The interface is what lets `audit --offline`, `audit ./path`, and `audit owner/repo` run through exactly the same pipeline. That matters beyond tidiness: the offline path is the one used in tests, in CI, and by anyone evaluating the tool without a token, so it must not be a second implementation that drifts from the real one.
type WorkflowFile ¶
type WorkflowFile struct {
// Path is how the file is reported: `.github/workflows/ci.yml` for a
// repository, the real path for a local directory.
Path string
// Repo is "owner/name" when the file came from a repository, empty for a
// local source.
Repo string
Content []byte
// URL links to the file on github.com when one is known.
URL string
}
WorkflowFile is one workflow definition and its contents.