confvis/
├── cmd/confvis/main.go # Entry point, invokes CLI
├── internal/
│ ├── cli/ # Command definitions (cobra)
│ │ ├── root.go # Root command setup
│ │ ├── gauge.go # gauge subcommand (command, deps, orchestration)
│ │ ├── gauge_format.go # gauge output formatting (json, text, markdown, github-comment)
│ │ ├── gauge_badge.go # gauge SVG badge generation (gauge, flat, sparkline)
│ │ ├── gauge_history.go # gauge history loading and storage
│ │ ├── gauge_threshold.go # gauge per-factor threshold parsing
│ │ ├── gate.go # gate subcommand (CI pass/fail gating)
│ │ ├── threshold.go # shared threshold checking (used by gauge and gate)
│ │ ├── baseline_loader.go # shared baseline loading (used by gauge and gate)
│ │ ├── generate.go # generate subcommand
│ │ ├── aggregate.go # aggregate subcommand
│ │ ├── fetch.go # fetch subcommand (external sources)
│ │ ├── baseline.go # baseline save/show subcommands
│ │ ├── check.go # check github subcommand
│ │ ├── comment.go # comment parent command
│ │ ├── comment_github.go # comment github subcommand
│ │ ├── config.go # config file and flag helpers
│ │ ├── filesystem.go # FileSystem interface
│ │ ├── github_env.go # shared GitHub env parsing
│ │ └── report_loader.go # ReportLoader
│ ├── confidence/ # Core data types and parsing
│ │ ├── types.go # Report, Factor structs
│ │ └── parser.go # JSON parsing logic
│ ├── gauge/ # SVG gauge generation
│ │ ├── gauge.go # SVG rendering with svgo
│ │ └── colors.go # Color schemes (light/dark)
│ ├── dashboard/ # HTML dashboard generation
│ │ ├── dashboard.go # Template execution
│ │ └── templates/ # Embedded HTML templates
│ ├── history/ # Score history tracking
│ │ └── history.go # JSON lines format handling
│ ├── baseline/ # Baseline storage for regression detection
│ │ └── baseline.go # Git ref and file storage
│ ├── gitutil/ # Shared git helper functions
│ │ └── gitutil.go # Ref read/write with compare-and-swap
│ ├── checks/ # CI platform check integrations
│ │ └── github.go # GitHub Checks API client
│ └── sources/ # External source modules
│ ├── source.go # Source interface and registry
│ ├── config.go # Configuration resolver helper
│ ├── httpclient/ # Common HTTP client
│ │ ├── client.go # Configurable HTTP client (auth, headers)
│ │ ├── url.go # URL normalization utilities
│ │ └── github.go # GitHub API config helpers
│ ├── repoparse/ # Repository identifier parsing
│ │ └── repoparse.go # Parse "owner/repo" format
│ ├── scoring/ # Shared scoring utilities
│ │ ├── scoring.go # SeverityScore calculation
│ │ ├── factors.go # Factor building helpers
│ │ └── report.go # Report building helper
│ ├── cmdrun/ # CLI command execution
│ │ └── cmdrun.go # Run() and FormatError()
│ ├── sonarqube/ # SonarQube implementation
│ ├── codecov/ # Codecov implementation
│ ├── codeql/ # CodeQL implementation
│ ├── coverage/ # Go coverage implementation
│ ├── coveralls/ # Coveralls implementation
│ ├── ghactions/ # GitHub Actions implementation
│ ├── githubalerts/ # GitHub security alerts implementation
│ ├── gitleaks/ # Gitleaks implementation
│ ├── gosec/ # Gosec implementation
│ ├── snyk/ # Snyk implementation
│ ├── dependabot/ # GitHub Dependabot implementation
│ ├── trivy/ # Trivy implementation
│ ├── grype/ # Grype implementation
│ ├── semgrep/ # Semgrep implementation
│ └── trufflehog/ # TruffleHog implementation
└── testdata/ # Test fixtures
┌─────────────────┐ ┌─────────────────┐
│ confidence.json │ │ External Source │
└────────┬────────┘ │ (SonarQube etc) │
│ └────────┬────────┘
│ Parse │ Fetch
│ ┌────────┘
▼ ▼
┌─────────────────┐
│ confidence. │
│ Report │
└────────┬────────┘
│
┌─────────┼─────────┬──────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌───────┐ ┌───────────┐ ┌────────┐ ┌──────┐
│ gauge │ │ dashboard │ │ checks │ │ gate │
└───┬───┘ └─────┬─────┘ └───┬────┘ └──┬───┘
│ │ │ │
▼ ▼ ▼ ▼
┌───────┐ ┌───────────┐ ┌────────────┐ ┌───────────┐
│ SVG │ │ HTML │ │ GitHub API │ │ exit code │
└───────┘ └───────────┘ └────────────┘ └───────────┘
Entry point only. Delegates to internal/cli.Execute().
Defines CLI structure using Cobra:
- Parses command-line flags
- Orchestrates file I/O
- Calls appropriate generators
Core types and JSON parsing:
Report- Overall report with title, score, thresholdFactor- Individual contributing factorParseFile()- Read and validate JSON
SVG gauge generation:
- Uses svgo for SVG rendering
- Renders semi-circle arc gauge
- Supports light/dark color schemes
- Shows pass/fail status
HTML dashboard generation:
- Uses Go's
html/templatewith embedded templates - Renders full report with factor breakdown
- Embeds the gauge SVG inline
Shared git helper functions used by baseline and history packages:
ReadRef()/ReadRefContent()- Read git ref SHAs and blob contentWriteRef()- Atomic blob write with optional compare-and-swap (CAS)ErrRefConflict/ZeroSHA- CAS error sentinel and create-only constantRefExists()/IsGitRepo()- Basic git state queriesGetCurrentCommit()/GetCurrentBranch()- Current HEAD metadata- Centralizes git plumbing (hash-object, update-ref, rev-parse, cat-file) in one place
Baseline storage for regression detection in CI/CD:
Baseline- Extends Report with save metadata (timestamp, commit, branch)- Git ref storage - Stores baselines in git refs via
gitutil.WriteRef() - File storage - Alternative for non-git environments
- Used by
confvis baseline save/showand--compare-baseline
CI platform check integrations:
GitHubClient- Creates GitHub Check Runs via the Checks API- Supports pass/fail status based on threshold
- Generates markdown summary with factor breakdown
- Auto-detects GitHub Actions environment variables
Modular framework for fetching metrics from external systems:
Sourceinterface defines the contract for all sources- Registry allows auto-registration via
init() - Each source is a sub-package (e.g.,
sources/sonarqube) ConfigResolverhelper consolidates token/URL resolution from options and environment variables
Common HTTP client used by API-based sources:
- Generic
Get[T]()function for type-safe JSON decoding (compile-time type safety via Go generics) - Configurable authentication: Bearer, Token, Basic, or None
- Custom Accept headers and extra headers (e.g., API version)
- Centralized error handling and JSON decoding
- Automatic retry with exponential backoff for transient failures (429, 502, 503, 504, network errors)
- Respects
Retry-Afterheaders; configurable retry count and initial backoff - Optional
ResponseHookcallback for inspecting response headers (e.g., rate-limit awareness) NormalizeBaseURL()helper for consistent URL handlingGitHubConfig()andGitHubConfigWithVersion()for GitHub API clients (includes rate-limit warning hook)- Reduces duplication across source clients
Repository identifier parsing utilities:
Parse()- Split "owner/repo" into separate parts with validationParseDefault()- Like Parse but returns empty strings on error (for URL builders)- Used by GitHub-based sources (ghactions, dependabot) and codecov
Shared scoring utilities used by vulnerability/issue sources:
SeverityScore()- Calculate score from issue count and penaltySeverityCounts- Standard struct for critical/high/medium/low countsVulnSeverityConfigs()- Build severity config arrays for standard vulnerability sourcesBuildSeverityFactors()- Generate confidence factors from severity configsBuildReport()- Create complete report with calculated score
CLI command execution utilities for command-based sources:
Run()- Execute commands with context, handling compound commandsFormatError()- Format errors with stderr output for debugging- Used by trivy, grype, and semgrep sources
SonarQube integration:
- Uses common
httpclientwith Basic auth - Maps SonarQube metrics to confidence factors
- Converts A-E ratings to 0-100 scores
Codecov integration:
- Uses common
httpclientwith Bearer auth - Fetches coverage percentage from Codecov API
- Supports GitHub, GitLab, and Bitbucket providers
GitHub Actions integration:
- Uses common
httpclientwith Bearer auth and GitHub API headers - Calculates workflow success rate from recent runs
- Supports GitHub Enterprise via custom API URL
Snyk integration:
- Uses common
httpclientwith Token auth - Converts vulnerability counts to severity-weighted scores
- Requires organization ID and project ID
Trivy integration:
- Runs local Trivy scanner (not HTTP-based)
- Parses JSON output to extract vulnerability counts
- Supports custom Trivy command (e.g., Docker)
- Create new package under
internal/(e.g.,internal/markdown) - Implement
Generate(w io.Writer, report *confidence.Report, opts Options) error - Add CLI command in
internal/cli/
- Add scheme function in
internal/gauge/colors.go - Update
Optionsto support scheme selection - Add CLI flag if desired
The gauge rendering is self-contained in gauge.go. Modify:
- Arc geometry calculations
- Text positioning
- Stroke widths and styles
- Create new package under
internal/sources/(e.g.,internal/sources/newsource) - Implement the
Sourceinterface:type Source interface { Name() string Fetch(ctx context.Context, opts Options) (*confidence.Report, error) }
- Register in
init():func init() { sources.Register(&Source{}) }
- Import the package in
internal/cli/fetch.gowith blank import:_ "github.com/boinger/confvis/internal/sources/newsource"
The source will automatically be available via confvis fetch newsource.
For API-based sources, use the shared infrastructure:
HTTP Client (internal/sources/httpclient):
import "github.com/boinger/confvis/internal/sources/httpclient"
client := httpclient.New(httpclient.Config{
BaseURL: "https://api.example.com",
Token: token,
AuthType: httpclient.AuthBearer, // or AuthToken, AuthBasic, AuthNone
Accept: "application/json",
Timeout: 30 * time.Second,
// Retry defaults: 3 attempts, 1s initial backoff. Override with:
// MaxRetries: -1, // disable retries
// InitialBackoff: 500*time.Millisecond, // faster backoff
})
result, err := httpclient.Get[ResponseType](client, ctx, endpoint) // retries transient failures automaticallyConfiguration Resolver (internal/sources/config.go):
var configResolver = &sources.ConfigResolver{
SourceName: "newsource",
TokenEnvVar: "NEWSOURCE_TOKEN",
URLEnvVar: "NEWSOURCE_URL",
TokenRequired: true,
URLRequired: false,
DefaultTimeout: 30 * time.Second,
}
func (s *Source) Fetch(ctx context.Context, opts sources.Options) (*confidence.Report, error) {
cfg, err := configResolver.Resolve(opts)
if err != nil {
return nil, err
}
// cfg.Token, cfg.URL, cfg.Timeout are now resolved
client := NewClient(cfg.URL, cfg.Token, cfg.Timeout)
// ...
}For testable source implementations, use the Fetcher interface pattern:
-
Define a
Fetcherinterface that your HTTP client satisfies:type Fetcher interface { FetchData(ctx context.Context, params string) (*Response, error) DataURL(params string) string }
-
Add a
FetchWithClientmethod that accepts the interface:func (s *Source) FetchWithClient(ctx context.Context, fetcher Fetcher, opts sources.Options) (*confidence.Report, error) { // Core logic here - fully testable with mock fetcher }
-
Have
Fetchresolve configuration and delegate:func (s *Source) Fetch(ctx context.Context, opts sources.Options) (*confidence.Report, error) { // Resolve token, URL, etc. from opts and environment client := NewClient(apiURL, token, timeout) return s.FetchWithClient(ctx, client, opts) }
This pattern (similar to *WithFS in internal/cli/) allows injecting mock clients in tests while keeping the public API unchanged. The existing *Client types automatically satisfy their Fetcher interfaces via Go's structural typing.