Documentation
¶
Index ¶
- Variables
- func GenerateReport(sr ScanResult) string
- func IsRateLimitError(err error) bool
- func Score(sr ScanResult) (score int, defined bool)
- type Auth
- type BranchProtection
- type Bucket
- type FileEntry
- type GitHubClient
- type HasActivity
- type HasBranchProtection
- type HasCIWorkflow
- type HasCodeowners
- type HasLicense
- type HasReadme
- type HasRepoDescription
- type HasRequiredReviewers
- type HasRequiredStatusChecks
- type HasSecurityMd
- type InstallationAuth
- type Option
- type PATAuth
- type Repo
- type RepoResult
- type Rule
- type RuleCategory
- type RuleResult
- type ScanResult
Constants ¶
This section is empty.
Variables ¶
var ( ErrEmptyRepo = errors.New("repository is empty") ErrTruncatedTree = errors.New("tree truncated by GitHub API") )
Sentinel errors for per-repo scan failures.
Functions ¶
func GenerateReport ¶
func GenerateReport(sr ScanResult) string
GenerateReport produces a Markdown engineering-standards scorecard from a ScanResult. The structure is fixed and meaningful for prospects landing from a cold-email link:
- Header with totals and exclusion counts
- ## Scored rules table (importance order, drives the score)
- **Score: N/100** inline callout (or **Score: N/A** when no repos)
- ## Additional checks table (importance order, "Coverage" column)
- ## Repository details with three score-based buckets
- ## Rule reference (collapsed <details>, split by category)
- ## ⚠️ Skipped (only if any repos couldn't be scanned)
func IsRateLimitError ¶ added in v0.6.0
IsRateLimitError reports whether an error is a GitHub rate limit error (primary or secondary). Rate limit errors must never be swallowed - they indicate a global problem that affects all subsequent API calls. Exported so callers (e.g., bulk-scan) can decide whether to abort a multi-org run on the first rate-limited org rather than continue and fail every subsequent call.
func Score ¶ added in v0.7.0
func Score(sr ScanResult) (score int, defined bool)
Score computes the org-level score: the arithmetic mean of pass rates across the scored rules in sr.Results. Returns the score (0-100) and a flag indicating whether it's defined. When sr has no scanned repos, defined=false and the caller should render "N/A". Result is rounded to the nearest integer for display.
Types ¶
type Auth ¶ added in v0.3.0
type Auth interface {
// contains filtered or unexported methods
}
Auth identifies how the scanner authenticates to GitHub. It is a sealed interface — only PATAuth and InstallationAuth in this package satisfy it. New auth types are added by defining a struct with an isAuth() method.
type BranchProtection ¶
BranchProtection holds the branch protection settings the scanner needs.
type Bucket ¶ added in v0.7.0
type Bucket struct {
Name string // "Strong", "Moderate", "Weak"
MinPct int // inclusive lower bound (0..100)
MaxPct int // inclusive upper bound (0..100)
}
Bucket classifies a repo by what fraction of the scored rules it passes. Each bucket covers an integer percentage range; the full bucket set returned by Buckets() covers [0, 100] without gaps or overlaps. Display labels are derived from MinPct/MaxPct at render time (see report.go).
func BucketOf ¶ added in v0.7.0
func BucketOf(rr RepoResult) (b Bucket, scoredPassing, scoredTotal, scorePct int)
BucketOf classifies a single repo by the percentage of scored rules it passes. Returns the matching Bucket plus the underlying counts so callers don't re-derive them. If there are zero scored rules the result is the last-defined bucket (i.e. Weak) with zero counts; this only happens in test fixtures with no scored rules registered.
func Buckets ¶ added in v0.7.0
func Buckets() []Bucket
Buckets returns the score-range buckets in display order (highest range first). Adding/removing buckets, renaming them, or shifting thresholds is a one-place edit here - report and stats output both derive from this list and need no separate updates.
type FileEntry ¶
type FileEntry struct {
Path string // full path relative to repo root (e.g., ".github/workflows/ci.yml")
Size int
Type string // "blob" (file) or "tree" (directory)
}
FileEntry represents a file or directory in a repo.
type GitHubClient ¶
type GitHubClient interface {
// ListReposByAccount lists repos for a named org (falls back to user on 404).
// Used by PAT auth.
ListReposByAccount(ctx context.Context, name string) ([]Repo, error)
// ListReposByInstallation lists the repos the current GitHub App installation
// was granted access to. Used by installation-token auth.
ListReposByInstallation(ctx context.Context) ([]Repo, error)
GetTree(ctx context.Context, owner, repo, branch string) ([]FileEntry, error)
GetBranchProtection(ctx context.Context, owner, repo, branch string) (*BranchProtection, error)
GetRulesets(ctx context.Context, owner, repo, branch string) (*BranchProtection, error)
}
GitHubClient is the interface for all GitHub API interactions. The scanner depends only on this interface, making it testable via mocks.
func NewGitHubClient ¶
func NewGitHubClient(token string) GitHubClient
NewGitHubClient creates a GitHubClient that calls the public GitHub REST API.
type HasActivity ¶ added in v0.5.1
HasActivity checks that the repo has had a commit (push) within the last 12 months. Set Now to a fixed time for deterministic testing; the zero value means time.Now() is used at check time.
func (HasActivity) Category ¶ added in v0.7.0
func (r HasActivity) Category() RuleCategory
func (HasActivity) Check ¶ added in v0.5.1
func (r HasActivity) Check(repo Repo) bool
func (HasActivity) Description ¶ added in v0.5.1
func (r HasActivity) Description() string
func (HasActivity) HowToFix ¶ added in v0.5.1
func (r HasActivity) HowToFix() string
func (HasActivity) Name ¶ added in v0.5.1
func (r HasActivity) Name() string
type HasBranchProtection ¶
type HasBranchProtection struct{}
HasBranchProtection checks that the default branch has protection rules enabled.
func (HasBranchProtection) Category ¶ added in v0.7.0
func (r HasBranchProtection) Category() RuleCategory
func (HasBranchProtection) Check ¶
func (r HasBranchProtection) Check(repo Repo) bool
func (HasBranchProtection) Description ¶ added in v0.4.0
func (r HasBranchProtection) Description() string
func (HasBranchProtection) HowToFix ¶ added in v0.4.0
func (r HasBranchProtection) HowToFix() string
func (HasBranchProtection) Name ¶
func (r HasBranchProtection) Name() string
type HasCIWorkflow ¶
type HasCIWorkflow struct{}
HasCIWorkflow checks that at least one .yml or .yaml file exists under .github/workflows/.
func (HasCIWorkflow) Category ¶ added in v0.7.0
func (r HasCIWorkflow) Category() RuleCategory
func (HasCIWorkflow) Check ¶
func (r HasCIWorkflow) Check(repo Repo) bool
func (HasCIWorkflow) Description ¶ added in v0.4.0
func (r HasCIWorkflow) Description() string
func (HasCIWorkflow) HowToFix ¶ added in v0.4.0
func (r HasCIWorkflow) HowToFix() string
func (HasCIWorkflow) Name ¶
func (r HasCIWorkflow) Name() string
type HasCodeowners ¶
type HasCodeowners struct{}
HasCodeowners checks that a CODEOWNERS file exists in root, docs/, or .github/.
func (HasCodeowners) Category ¶ added in v0.7.0
func (r HasCodeowners) Category() RuleCategory
func (HasCodeowners) Check ¶
func (r HasCodeowners) Check(repo Repo) bool
func (HasCodeowners) Description ¶ added in v0.4.0
func (r HasCodeowners) Description() string
func (HasCodeowners) HowToFix ¶ added in v0.4.0
func (r HasCodeowners) HowToFix() string
func (HasCodeowners) Name ¶
func (r HasCodeowners) Name() string
type HasLicense ¶
type HasLicense struct{}
HasLicense checks that a LICENSE.md or LICENSE file exists in the repo root.
func (HasLicense) Category ¶ added in v0.7.0
func (r HasLicense) Category() RuleCategory
func (HasLicense) Check ¶
func (r HasLicense) Check(repo Repo) bool
func (HasLicense) Description ¶ added in v0.4.0
func (r HasLicense) Description() string
func (HasLicense) HowToFix ¶ added in v0.4.0
func (r HasLicense) HowToFix() string
func (HasLicense) Name ¶
func (r HasLicense) Name() string
type HasReadme ¶ added in v0.7.0
type HasReadme struct{}
HasReadme checks that a README.md or README file exists at the repo root. (No size threshold - the previous "substantial" variant was dropped because 2 KB is too low to discriminate quality and too high to reward minimal but useful READMEs.)
func (HasReadme) Category ¶ added in v0.7.0
func (r HasReadme) Category() RuleCategory
func (HasReadme) Description ¶ added in v0.7.0
type HasRepoDescription ¶
type HasRepoDescription struct{}
HasRepoDescription checks that the repo description field is not blank.
func (HasRepoDescription) Category ¶ added in v0.7.0
func (r HasRepoDescription) Category() RuleCategory
func (HasRepoDescription) Check ¶
func (r HasRepoDescription) Check(repo Repo) bool
func (HasRepoDescription) Description ¶ added in v0.4.0
func (r HasRepoDescription) Description() string
func (HasRepoDescription) HowToFix ¶ added in v0.4.0
func (r HasRepoDescription) HowToFix() string
func (HasRepoDescription) Name ¶
func (r HasRepoDescription) Name() string
type HasRequiredReviewers ¶
type HasRequiredReviewers struct{}
HasRequiredReviewers checks that at least one approving review is required.
func (HasRequiredReviewers) Category ¶ added in v0.7.0
func (r HasRequiredReviewers) Category() RuleCategory
func (HasRequiredReviewers) Check ¶
func (r HasRequiredReviewers) Check(repo Repo) bool
func (HasRequiredReviewers) Description ¶ added in v0.4.0
func (r HasRequiredReviewers) Description() string
func (HasRequiredReviewers) HowToFix ¶ added in v0.4.0
func (r HasRequiredReviewers) HowToFix() string
func (HasRequiredReviewers) Name ¶
func (r HasRequiredReviewers) Name() string
type HasRequiredStatusChecks ¶
type HasRequiredStatusChecks struct{}
HasRequiredStatusChecks checks that at least one status check is required before merging.
func (HasRequiredStatusChecks) Category ¶ added in v0.7.0
func (r HasRequiredStatusChecks) Category() RuleCategory
func (HasRequiredStatusChecks) Check ¶
func (r HasRequiredStatusChecks) Check(repo Repo) bool
func (HasRequiredStatusChecks) Description ¶ added in v0.4.0
func (r HasRequiredStatusChecks) Description() string
func (HasRequiredStatusChecks) HowToFix ¶ added in v0.4.0
func (r HasRequiredStatusChecks) HowToFix() string
func (HasRequiredStatusChecks) Name ¶
func (r HasRequiredStatusChecks) Name() string
type HasSecurityMd ¶
type HasSecurityMd struct{}
HasSecurityMd checks that SECURITY.md exists in the repo root or .github/.
func (HasSecurityMd) Category ¶ added in v0.7.0
func (r HasSecurityMd) Category() RuleCategory
func (HasSecurityMd) Check ¶
func (r HasSecurityMd) Check(repo Repo) bool
func (HasSecurityMd) Description ¶ added in v0.4.0
func (r HasSecurityMd) Description() string
func (HasSecurityMd) HowToFix ¶ added in v0.4.0
func (r HasSecurityMd) HowToFix() string
func (HasSecurityMd) Name ¶
func (r HasSecurityMd) Name() string
type InstallationAuth ¶ added in v0.3.0
type InstallationAuth struct {
Token string
Name string // org or user login the app is installed on (used in repo URLs)
}
InstallationAuth uses a GitHub App installation access token. Scanner lists repositories via /installation/repositories, which returns exactly the repos the installation was granted access to (no public-repo leak on "Selected repositories" installs).
type Option ¶ added in v0.2.0
type Option func(*scanOptions)
Option configures optional scan behavior.
func WithBaseURL ¶ added in v0.2.0
WithBaseURL sets a custom GitHub API base URL. Defaults to the public GitHub API when unset. Useful for testing against a mock server or pointing at a GitHub Enterprise instance.
type PATAuth ¶ added in v0.3.0
PATAuth uses a Personal Access Token targeting a named account. Scanner lists repositories via /orgs/{Name}/repos and falls back to /users/{Name}/repos on 404, so it works for both org and user accounts.
type Repo ¶
type Repo struct {
Name string
Description string
DefaultBranch string
Archived bool
Fork bool
PushedAt time.Time // most recent push to any branch (from list-repos)
Files []FileEntry // all files and directories in the repo
BranchProtection *BranchProtection // nil if no protection configured
}
Repo represents a GitHub repository with the fields the scanner needs.
type RepoResult ¶
type RepoResult struct {
RepoName string
MostRecentCommit time.Time // PushedAt from the listing; zero if unknown
Results []RuleResult
KnownSkipReason string
UnknownSkipError string
}
RepoResult holds all rule results for a single repository. KnownSkipReason and UnknownSkipError are mutually exclusive.
func (RepoResult) Skipped ¶ added in v0.2.0
func (rr RepoResult) Skipped() bool
type Rule ¶
type Rule interface {
Name() string
Category() RuleCategory
Check(repo Repo) bool
Description() string
HowToFix() string
}
Rule defines a named check that produces a pass/fail result for a repo. Description and HowToFix supply the per-rule text used by the Markdown scorecard's Rule reference section. Category determines whether the rule feeds into the org-level score or appears in the informational-only "Additional checks" section.
func AdditionalRules ¶ added in v0.7.0
func AdditionalRules() []Rule
AdditionalRules returns just the rules with CategoryAdditional, in AllRules order.
func AllRules ¶
func AllRules() []Rule
AllRules returns the ordered list of rules the scanner evaluates. The order is fixed and meaningful: scored rules first (by importance), then additional checks (by importance). Callers that want only one category can use ScoredRules or AdditionalRules.
func ScoredRules ¶ added in v0.7.0
func ScoredRules() []Rule
ScoredRules returns just the rules with CategoryScored, in AllRules order.
type RuleCategory ¶ added in v0.7.0
type RuleCategory string
RuleCategory classifies a rule as either a *scored* rule (contributes to the org-level score) or an *additional* check (informational only).
const ( CategoryScored RuleCategory = "scored" CategoryAdditional RuleCategory = "additional" )
type RuleResult ¶
RuleResult holds the outcome of a single rule check for a single repo.
type ScanResult ¶ added in v0.6.0
type ScanResult struct {
Org string
ScannedAt time.Time
TotalRepos int // total repos returned by GitHub before any filtering
ArchivedExcluded int // archived repos filtered out at listing time
ForksExcluded int // forked repos filtered out at listing time
Skipped []RepoResult // empty repos, truncated trees, or unexpected errors during the scan
Results []RepoResult // repos that finished scanning (success or fail per-rule)
}
ScanResult bundles the scan outcome with the listing-time exclusion counts the scanner accumulates while filtering archived and forked repos. The counts let callers report a full breakdown ("32 total, 4 forks excluded, 2 archived excluded, 26 scanned") without re-querying GitHub.
The library does not expose a precomputed "most recent commit across the org" — each RepoResult carries its own MostRecentCommit and consumers aggregate as needed.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
bulk-scan
command
bulk-scan reads a list of GitHub orgs/users from a file, runs the scanner against each one, and writes per-org output files (scorecard.md + stats.json) into a destination folder.
|
bulk-scan reads a list of GitHub orgs/users from a file, runs the scanner against each one, and writes per-org output files (scorecard.md + stats.json) into a destination folder. |
|
generate-sample
command
generate-sample renders samples.Fixture() through scanner.GenerateReport and writes the resulting Markdown to stdout (or to a file via --out).
|
generate-sample renders samples.Fixture() through scanner.GenerateReport and writes the resulting Markdown to stdout (or to a file via --out). |
|
scanner
command
|
|
|
Package samples provides the canonical sample scorecard used to drive the landing page hero and the app's dev-seed data.
|
Package samples provides the canonical sample scorecard used to drive the landing page hero and the app's dev-seed data. |