ghasetup

package
v3.98.0 Latest Latest
Warning

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

Go to latest
Published: Aug 30, 2026 License: AGPL-3.0 Imports: 9 Imported by: 0

Documentation

Overview

Package ghasetup wires third-party security scanners into a repository's GitHub Actions workflows.

The catalog is the single source of truth: `vulnetix gha setup` renders workflow jobs from it, and the published documentation examples are generated from it too. A tool documented on the website is therefore, by construction, the same job the command writes and the same job that runs in production.

Index

Constants

View Source
const DefaultJobTimeoutMinutes = 60

DefaultJobTimeoutMinutes caps every scanner job.

Measured over 1,645 jobs on the self-hosted pool: p50 39 seconds, 95.2% under five minutes, p99 29 minutes. Sixty minutes is a little over twice the p99, so it never touches a scan that is merely slow, and it turns a hang from six hours of a held runner — GitHub's own job ceiling, which is what four of these actually hit — into an hour.

View Source
const DefaultScheduleCron = "17 3 * * 1"

DefaultScheduleCron is the schedule used when one is asked for and the repository has no slug to derive a slot from. It is a fallback, not a fleet default: every repository that knows its own name gets its own slot from ScheduleCronFor instead.

View Source
const WorkflowPath = ".github/workflows/vulnetix-scanners.yml"

WorkflowPath is the file `gha setup` manages, relative to the repository root.

It is deliberately separate from any hand-written workflow: the file is regenerated in full on every `gha setup`, so it must not be somewhere a user keeps their own jobs.

Variables

This section is empty.

Functions

func IsManaged

func IsManaged(content string) bool

IsManaged reports whether this file was written by `gha setup`.

func ManualTools added in v3.92.0

func ManualTools(c *Catalog) []string

ManualTools returns the ids --detect will never select, with the reason, so the command can say what it left out rather than silently narrowing the set.

func Render

func Render(c *Catalog, ids []string, opt Options) (string, error)

Render produces the complete workflow for the given tool ids.

The Vulnetix CLI is installed unpinned on purpose. install.sh resolves the latest release, so a workflow written today keeps working without anyone coming back to bump a version — which is exactly how a fleet ends up spread across three different CLI versions. Third-party scanners are pinned, because there the reproducibility of the scan matters more than being current.

func RepoRoot

func RepoRoot(dir string) (string, error)

RepoRoot returns the git working-tree root for dir.

func RepoRootOrCwd added in v3.92.0

func RepoRootOrCwd(root string) string

RepoRootOrCwd is a convenience for callers that already resolved a root.

func ScheduleCronFor added in v3.93.2

func ScheduleCronFor(slug string) string

ScheduleCronFor derives a stable weekly slot from the repository slug.

Deterministic on purpose: the workflow is regenerated in full on every `gha setup`, so a random or clock-derived time would rewrite the schedule (and produce a diff) every single run. Hashing the slug means the same repository always lands in the same slot, and a repository added to the fleet does not move anybody else — which an index-into-a-sorted-list scheme would.

The week is cut into 20-minute slots, three per hour, 504 in all. The CLI runs inside one repository and cannot see the others, so slots are assigned by hash rather than allocated; two repositories colliding is possible and costs only that one of them queues behind the other.

func ToolsInWorkflow

func ToolsInWorkflow(c *Catalog, content string) []string

ToolsInWorkflow returns the catalog tool ids already wired into an existing managed workflow, so `gha setup` adds to the set rather than replacing it.

Types

type Catalog

type Catalog struct {
	Version int    `json:"version"`
	Tools   []Tool `json:"tools"`
}

Catalog is the parsed tool catalog.

func Load

func Load() (*Catalog, error)

Load parses the embedded catalog.

func (*Catalog) Find

func (c *Catalog) Find(id string) (*Tool, bool)

Find returns the tool with this id, matched case-insensitively.

func (*Catalog) IDs

func (c *Catalog) IDs() []string

IDs returns every tool id, sorted.

func (*Catalog) Suggest

func (c *Catalog) Suggest(id string) []string

Suggest returns catalog ids similar to the given one, for a "did you mean" on a typo or on a name the catalog spells differently ("trivy" is split into trivy-fs and trivy-config).

type Detect added in v3.92.0

type Detect struct {
	// Always marks a tool that works on any repository: the SBOM producers,
	// the secret scanners, the multi-language SAST engines.
	Always bool `json:"always,omitempty"`

	// Files are basenames or directory names that must exist somewhere in the
	// tree. A match on any one of them selects the tool.
	Files []string `json:"files,omitempty"`

	// Extensions are file suffixes, matched the same way.
	Extensions []string `json:"extensions,omitempty"`

	// Manual keeps a tool out of --detect entirely. These are the ones that
	// need something the repository cannot supply: a licence key (Snyk), or a
	// live target the operator must nominate (ZAP, Nuclei). Selecting one by
	// name still works.
	Manual bool `json:"manual,omitempty"`
}

Detect says when `gha setup --detect` should pick a tool for a repository.

Every recipe already refuses to run against a tree it has nothing to say about ("no go.mod found; skipping gosec"), so a wrong guess here costs a no-op job rather than a bad result. What it buys is a workflow that names only the scanners this repository can actually feed, which is the difference between reading a run and scrolling past forty skipped jobs.

func (Detect) Matches added in v3.92.0

func (d Detect) Matches(sig *RepoSignals) bool

Matches reports whether a tool applies to a repository with these signals.

type Options

type Options struct {
	// SelfHostedLabels, when set, replaces `ubuntu-latest` on every job.
	SelfHostedLabels []string
	// OrgID is written into the publish job's env as a literal only when the
	// caller explicitly asks; otherwise the secret reference is used, which is
	// what almost every repository wants.
	OrgIDLiteral string

	// Triggers are the workflow's `on:` events. Empty means push +
	// workflow_dispatch, which is what this command has always written.
	//
	// It is worth choosing deliberately once a repository runs more than a
	// handful of scanners: a full detected set is twenty-odd jobs, and on a
	// small runner pool "on every push" turns a two-minute build into an hour
	// of queue for everyone else.
	Triggers []string

	// ScheduleCron is the five-field expression used when Triggers includes
	// "schedule". Leave it empty and the expression is derived from RepoSlug,
	// which is what staggers the fleet; set it only to pin one repository to a
	// time somebody actually cares about.
	ScheduleCron string

	// RepoSlug is "owner/repo", used to derive the schedule when ScheduleCron
	// is empty. Empty falls back to DefaultScheduleCron.
	RepoSlug string
}

Options controls how the workflow is rendered.

type Remote

type Remote struct {
	// URL is the raw origin URL, empty when there is no origin.
	URL string
	// Host is the parsed host ("github.com", "gitlab.com", …), empty when the
	// URL could not be parsed.
	Host string
	// Slug is "owner/repo" when the URL could be parsed.
	Slug string
	// IsGitHub reports whether this is github.com or a GitHub Enterprise host.
	IsGitHub bool
}

Remote describes the repository's origin.

func DetectRemote

func DetectRemote(dir string) Remote

DetectRemote inspects the origin remote of the repository at dir.

A workflow file is only meaningful on GitHub, so the caller warns when this is something else. It is a warning rather than an error: mirrors, forks about to be pushed to GitHub, and Enterprise hosts with unusual names are all legitimate reasons to write the file anyway.

type RepoSignals added in v3.92.0

type RepoSignals struct {
	Files      map[string]bool
	Extensions map[string]bool
}

RepoSignals is what a repository contains, reduced to the two questions the catalog asks: which files are present, and which extensions appear.

func DetectTools added in v3.92.0

func DetectTools(c *Catalog, root string) ([]string, *RepoSignals, error)

DetectTools returns the catalog ids that apply to the repository at root, sorted, along with the signals they were chosen from.

func ScanRepo added in v3.92.0

func ScanRepo(root string) (*RepoSignals, error)

ScanRepo walks a repository and records the signals the catalog matches on.

type Step

type Step struct {
	Name string `json:"name,omitempty"`
	ID   string `json:"id,omitempty"`
	// If is a GitHub expression written verbatim into the step's `if:`. A step
	// that cannot run everywhere is gated on a preceding detection step rather
	// than left to hang the job on a runner that cannot support it.
	If   string            `json:"if,omitempty"`
	Uses string            `json:"uses,omitempty"`
	Run  string            `json:"run,omitempty"`
	With map[string]any    `json:"with,omitempty"`
	Env  map[string]string `json:"env,omitempty"`
	// TimeoutMinutes caps one step. The job cap cannot rescue a step that
	// blocks on a prompt that never arrives: the job spends its whole budget
	// inside setup and dies having run no scanner at all.
	TimeoutMinutes int `json:"timeoutMinutes,omitempty"`
}

Step is one workflow step. Exactly one of Uses or Run is set.

type Tool

type Tool struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	JobName     string   `json:"jobName"`
	Category    string   `json:"category"`
	Description string   `json:"description"`
	Artifact    string   `json:"artifact"`
	Paths       []string `json:"paths"`
	Note        string   `json:"note,omitempty"`

	// TimeoutMinutes overrides DefaultJobTimeoutMinutes for this tool.
	//
	// Set it only where the measured runtime says the default is wrong: too
	// tight and a legitimate slow scan is killed, too loose and a hung one
	// holds a runner until GitHub's six-hour ceiling. Zero means "use the
	// default", which is the right answer for almost every scanner.
	TimeoutMinutes int `json:"timeoutMinutes,omitempty"`

	Detect Detect `json:"detect"`
	Steps  []Step `json:"steps"`
}

Tool is one scanner: the job that runs it and the artifact it leaves behind.

Directories

Path Synopsis
Command ghasetupgen renders the third-party scanner documentation from the single source of truth: the embedded catalog (internal/ghasetup/catalog).
Command ghasetupgen renders the third-party scanner documentation from the single source of truth: the embedded catalog (internal/ghasetup/catalog).

Jump to

Keyboard shortcuts

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