scanner

package module
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: May 2, 2026 License: MIT Imports: 9 Imported by: 0

README

Codatus

Codatus scans every repository in a GitHub organization or user account against a set of engineering standards and produces a Markdown scorecard.

It answers one question: how does each repo in your org measure up against the standards you care about?

This repository is a Go library and a CLI. Posting the scorecard (e.g., as a GitHub Issue) is the caller's responsibility - the scanner returns structured results and Markdown, nothing more.


How it works

  1. Codatus receives a GitHub account to scan (organization or user).
  2. It lists the repositories accessible to the token, then filters out archived and forked repos. Both exclusions are reported in the scorecard header so the reader can see the full breakdown (Total repos, Forks excluded, Archived excluded, Repos scanned).
  3. For each remaining repo, it runs 11 rule checks (see below).
  4. It produces a single Markdown scorecard summarizing pass/fail per repo per rule, plus a structured ScanResult value the caller can post-process (e.g. the bulk-scan binary serializes per-rule aggregates to JSON).

The CLI prints the Markdown to stdout. Callers using the library get a ScanResult (org name, scan timestamp, exclusion counts, per-repo results, skipped repos) and can generate the Markdown via GenerateReport(scanResult).


Rules

Each rule produces a pass or fail result per repository. Rules fall into two categories:

  • Scored rules drive the org-level score. The score is the arithmetic mean of pass rates across the 5 scored rules. Per-repo classification (Strong / Moderate / Weak) is also based on what fraction of scored rules a repo passes.
  • Additional checks are informational only. They appear in the report as coverage numbers but do not affect the score. They surface "nice to have" hygiene that's worth seeing but isn't load-bearing for whether a repo's standards are in good shape.
Scored rules (drive the org-level score)
1. Has branch protection

Check: the default branch has branch protection rules enabled (via the GitHub API's branch protection endpoint).

Pass: branch protection is enabled on the default branch. Fail: branch protection is not enabled, or the API returns 404 (no protection configured).

2. Has required reviewers

Check: the default branch's branch protection rules require at least one approving review before merging (via the GitHub API - required_pull_request_reviews.required_approving_review_count >= 1).

Pass: required reviewers is set to 1 or more. Fail: required reviewers is not configured, or set to 0, or branch protection is not enabled.

3. Requires status checks before merging

Check: the default branch's branch protection rules require at least one status check to pass before merging (via the GitHub API - required_status_checks is configured with one or more contexts).

Pass: at least one required status check is configured. Fail: required status checks are not configured, or the list of required contexts is empty, or branch protection is not enabled.

4. Has CODEOWNERS

Check: a CODEOWNERS file exists in one of the three standard locations: root (/CODEOWNERS), docs/CODEOWNERS, or .github/CODEOWNERS.

Pass: file found in any of the three locations. Fail: file not found in any location.

5. Has CI workflow

Check: at least one file exists under .github/workflows/ with a .yml or .yaml extension.

Pass: one or more workflow files found. Fail: .github/workflows/ is missing or empty.

Additional checks (informational only)
6. Has README

Check: a README.md or README file exists in the repo root.

Pass: file found. Fail: file not found.

There is no size threshold - any README counts. The previous "substantial" variant required >2 KB, which discriminated poorly (too low to weed out stubs, too high to reward minimal but useful READMEs).

7. Has LICENSE

Check: a LICENSE or LICENSE.md file exists in the repo root.

Pass: file found. Fail: file not found.

8. Has repo description

Check: the GitHub repository description field is not blank.

Pass: description is set and non-empty. Fail: description is blank or not set.

9. Has activity

Check: the most recent push to any branch is within the last 12 months (via the GitHub API's pushed_at field on the repository).

Pass: the repository was pushed within the last 12 months. Fail: the repository has not been pushed in the last 12 months, or has never been pushed. Archived repositories are filtered out before scanning, so they never reach this rule.

10. Has SECURITY.md

Check: a SECURITY.md file exists in the repo root or .github/SECURITY.md.

Pass: file found in either location. Fail: file not found.

Score and bucketing

The org-level score is the arithmetic mean of pass rates across the 5 scored rules:

score = (pass_rate_branch_protection
       + pass_rate_required_reviewers
       + pass_rate_status_checks
       + pass_rate_codeowners
       + pass_rate_ci_workflow) / 5

Each repo also gets a percentage based on how many of the 5 scored rules it passes (0%, 20%, 40%, 60%, 80%, 100%) and is bucketed:

  • Strong (≥80%): 4 or 5 scored rules passing
  • Moderate (40-79%): 2 or 3 scored rules passing
  • Weak (≤39%): 0 or 1 scored rules passing

Additional checks do not affect either the org score or the per-repo bucket.


Scorecard format

The scorecard is a single Markdown document. Structure:

# Codatus - Engineering Standards Scorecard

**Org:** {org_name}
**Scanned:** {timestamp}
**Total repos:** {count}              <-- only if > 0
**Forks excluded:** {count}           <-- only if > 0
**Archived excluded:** {count}        <-- only if > 0
**Repos scanned:** {count}
**Skipped:** {count}                  <-- only if any repos were skipped

## Scored rules

| Rule | Passing | Failing | Pass rate |
|------|---------|---------|----------|
| Has branch protection | 11 | 42 | 20% |
| Has required reviewers | 4 | 49 | 7% |
| Requires status checks before merging | 4 | 49 | 7% |
| Has CODEOWNERS | 3 | 50 | 5% |
| Has CI workflow | 27 | 26 | 50% |

**Score: 18/100** (average pass rate across the scored rules above)

## Additional checks

| Check | Passing | Failing | Coverage |
|------|---------|---------|----------|
| Has README | 50 | 3 | 94% |
| Has LICENSE | 38 | 15 | 71% |
| Has repo description | 46 | 7 | 86% |
| Has activity | 43 | 10 | 81% |
| Has SECURITY.md | 3 | 50 | 5% |

## Repository details

### Strong (≥80%)

<details>
<summary><a href="https://github.com/{org}/repo-a">repo-a</a> - 100% (5/5 scored rules passing)</summary>

</details>

### Moderate (40-79%)

<details>
<summary><a href="https://github.com/{org}/repo-b">repo-b</a> - 60% (3/5 scored rules passing)</summary>

**Failing scored rules:**
- Has CODEOWNERS
- Requires status checks before merging

**Additional check failures:**
- Has SECURITY.md

</details>

### Weak (≤39%)

<details>
<summary><a href="https://github.com/{org}/repo-c">repo-c</a> - 0% (0/5 scored rules passing)</summary>

**Failing scored rules:**
- Has branch protection
- Has required reviewers
- Requires status checks before merging
- Has CODEOWNERS
- Has CI workflow

</details>

## Rule reference

<details>
<summary>What each rule checks and how to fix it</summary>

### Scored rules

#### Has branch protection
- **What it checks:** ...
- **How to fix:** ...

---

#### Has required reviewers
...

### Additional checks

#### Has README
...

</details>

## ⚠️ Skipped ({n} repos)          <-- only if any repos were skipped

- [empty-repo](https://github.com/{org}/empty-repo) - repository is empty
- [huge-repo](https://github.com/{org}/huge-repo) - file tree too large (truncated by GitHub API)

Tables render in fixed importance order (not sorted by pass rate). The Rule reference section is collapsed by default and lists, for every rule actually present in the scan results, the "what it checks" / "how to fix" text - split into Scored rules and Additional checks subsections. Sections (and entire buckets) are omitted when empty. Skipped repos are those that could not be scanned (empty repos, truncated file trees, API errors) - they are excluded from the score and bucket counts, and appear at the very end.

When repos_scanned is 0, the Score line reads **Score: N/A** (no repos available to score).

Canonical sample fixture

samples.Fixture() returns a deterministic ScanResult for the fictional acme-corp org. It's the single source of truth for the sample scorecard shown on the landing page and used as dev-seed data in the app, replacing what used to be hand-typed Markdown in each downstream repo.

Go consumers render it in process:

md := scanner.GenerateReport(samples.Fixture())

Non-Go consumers use the generator binary, which writes Markdown to stdout (or to --out):

go run github.com/CodatusHQ/scanner/cmd/generate-sample > sample-scorecard.md

No rendered .md is committed here - downstream copies are refreshed on demand by re-running the generator.


Scanner configuration

scanner.Scan(ctx, auth, opts...) takes an Auth - a sealed interface implemented by two concrete types. Pick the one that matches your token.

PATAuth - personal access token

For scanning with a user-generated token (classic or fine-grained PAT). Scanner calls /orgs/{Name}/repos and falls back to /users/{Name}/repos on 404, so it works for both org and user accounts.

results, err := scanner.Scan(ctx, scanner.PATAuth{
    Token: "ghp_...",
    Name:  "my-org",        // or a user login like "octocat"
})
Field Type Required Description
Token string Yes Personal access token
Name string Yes GitHub organization or user login to scan
InstallationAuth - GitHub App installation

For scanning as a GitHub App. Scanner calls /installation/repositories, which returns exactly the repos the installation was granted access to. Works identically for org and user installs, and respects "Selected repositories" mode (no leak of other public repos).

results, err := scanner.Scan(ctx, scanner.InstallationAuth{
    Token: "ghs_...",       // short-lived installation access token
    Name:  "my-org",        // the account the app is installed on
})
Field Type Required Description
Token string Yes Installation access token (from /app/installations/{id}/access_tokens)
Name string Yes Account the app is installed on; used for per-repo URL construction
Options
Option Description
WithBaseURL(url string) Override the GitHub API base URL. Defaults to the public GitHub API. Useful for testing against a mock server or targeting GitHub Enterprise.
Required token permissions

Classic PAT:

  • repo — read repo contents and branch protection
  • read:org — required when Name is an organization

Fine-grained PAT: scoped to the target account, with Repository permissions:

  • Metadata: Read
  • Contents: Read
  • Administration: Read (for branch protection)

Installation token: permissions come from the GitHub App's configured repository permissions (not PAT scopes). At minimum the app needs Contents (read) and Metadata (read); Administration (read) is required for branch protection rules to resolve.

How these values are sourced (env vars, CLI flags, config file) is the responsibility of the caller, not the scanner module.

CLI

The codatus binary reads CODATUS_ORG and CODATUS_TOKEN from the environment, wraps them in PATAuth, runs a scan, and prints the Markdown scorecard to stdout. Log output (scan summary, errors) goes to stderr so stdout stays clean for piping.

Despite the name, CODATUS_ORG accepts either an organization slug or a user login - the library dispatches automatically.

# Organization
CODATUS_ORG=myorg CODATUS_TOKEN=ghp_... codatus > scorecard.md

# User account
CODATUS_ORG=my-username CODATUS_TOKEN=ghp_... codatus > scorecard.md
Bulk scan (many orgs at once)

The bulk-scan binary reads a list of orgs/users from a file (one slug per line, blank lines skipped, no comment handling) and scans them sequentially. For each org it writes a scorecard.md and a stats.json into a per-org subfolder, so partial runs preserve completed work even if a later scan aborts.

# orgs.txt
acme-corp
wayne-enterprises
stark-industries

# Run
bulk-scan --orgs orgs.txt --out ./scans --token ghp_...
# or with the token in env:
CODATUS_TOKEN=ghp_... bulk-scan --orgs orgs.txt --out ./scans

Output layout:

scans/
├── acme-corp/
│   ├── scorecard.md     # same Markdown the single-org CLI produces
│   └── stats.json       # structured aggregates: per-rule pass rates, totals, exclusion counts
├── wayne-enterprises/
│   ├── scorecard.md
│   └── stats.json
└── ...

Progress prints to stderr per org ([2/3] wayne-enterprises ... ok (42 scanned, 18/42 compliant = 42%)); a final summary lists succeeded / failed / not-attempted counts.

Failure handling:

  • Per-org errors (404, 403, "user not found", etc.) - logged, run continues to the next org.
  • Global errors (429 rate limit, 401 auth) - run aborts immediately. Files for orgs that already completed remain on disk; the un-scanned tail is reported in the summary as "not attempted."

Exit code is non-zero if any org failed or was not attempted.


What Codatus is not

  • Not a velocity/DORA metrics tool. It does not measure cycle time, deployment frequency, or review speed. That's a different product category (Swarmia, LinearB, CodePulse).
  • Not a security scanner. It checks whether SECURITY.md exists and whether branch protection is on, but it does not scan code for vulnerabilities (use Snyk, Dependabot, or OpenSSF Scorecard).
  • Not a developer portal. There is no service catalog, no scaffolding, no self-service actions (Backstage, Cortex, OpsLevel cover that). Just standards.

Documentation

Index

Constants

This section is empty.

Variables

View Source
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:

  1. Header with totals and exclusion counts
  2. ## Scored rules table (importance order, drives the score)
  3. **Score: N/100** inline callout (or **Score: N/A** when no repos)
  4. ## Additional checks table (importance order, "Coverage" column)
  5. ## Repository details with three score-based buckets
  6. ## Rule reference (collapsed <details>, split by category)
  7. ## ⚠️ Skipped (only if any repos couldn't be scanned)

func IsRateLimitError added in v0.6.0

func IsRateLimitError(err error) bool

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

type BranchProtection struct {
	RequiredReviewers    int
	RequiredStatusChecks []string
}

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

type HasActivity struct {
	Now time.Time
}

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) Check added in v0.7.0

func (r HasReadme) Check(repo Repo) bool

func (HasReadme) Description added in v0.7.0

func (r HasReadme) Description() string

func (HasReadme) HowToFix added in v0.7.0

func (r HasReadme) HowToFix() string

func (HasReadme) Name added in v0.7.0

func (r HasReadme) Name() string

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 (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

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

func WithBaseURL(url string) Option

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

type PATAuth struct {
	Token string
	Name  string // org or user login to scan
}

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

type RuleResult struct {
	RuleName string
	Passed   bool
}

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.

func Scan

func Scan(ctx context.Context, auth Auth, opts ...Option) (ScanResult, error)

Scan lists repositories accessible to auth and evaluates every rule against each non-archived, non-forked repo. Forks and archived repos are excluded at listing time and surface in the returned ScanResult's ForksExcluded / ArchivedExcluded counts.

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.

Jump to

Keyboard shortcuts

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