Function bodies 88 total
cmd.init function · go · L36-L41 (6 LOC)cli/cmd/check.go
func init() {
rootCmd.AddCommand(checkCmd)
checkCmd.Flags().StringVar(&formatFlag, "format", "auto", "output format (auto, json, text)")
checkCmd.Flags().BoolVar(&githubCommentFlag, "github-comment", false, "post results as PR comment (auto-enabled in GitHub Actions PR)")
checkCmd.Flags().BoolVar(&noGithubComment, "no-github-comment", false, "disable PR comment even in GitHub Actions")
}cmd.runCheck function · go · L43-L137 (95 LOC)cli/cmd/check.go
func runCheck(cmd *cobra.Command, args []string) error {
// Load config
configPath := cfgFile
if configPath == "" {
var err error
configPath, err = config.FindConfigFile()
if err != nil {
return fmt.Errorf("failed to find config file: %w", err)
}
}
cfg, err := config.Load(configPath)
if err != nil {
return fmt.Errorf("failed to load config: %w", err)
}
// Determine output format
format := formatFlag
if format == "auto" {
if cfg.Output.Format != "" && cfg.Output.Format != "auto" {
format = cfg.Output.Format
} else {
format = "text"
}
}
// Create formatter
formatter := output.NewFormatter(format, os.Stdout)
// Create runner
r := runner.New(cfg)
// Run checks
var results *output.Results
if len(args) == 0 {
// Run all enabled checks
checks := getEnabledCheckNames(cfg)
if len(checks) == 0 {
fmt.Fprintln(os.Stderr, "No checks enabled in configuration")
return nil
}
if err := formatter.PrintProgress(checks); err != nil {
retucmd.maybePostGitHubComment function · go · L140-L179 (40 LOC)cli/cmd/check.go
func maybePostGitHubComment(results *output.Results) error {
// Check if explicitly disabled
if noGithubComment {
return nil
}
// Detect GitHub Actions environment
ghCtx := github.DetectGitHub()
// Determine if we should post a comment
shouldComment := githubCommentFlag // Explicit flag
if !shouldComment && ghCtx != nil {
// Auto-enable in GitHub Actions PR context with token
shouldComment = ghCtx.EventName == "pull_request" &&
ghCtx.Token != "" &&
ghCtx.PRNumber > 0
}
if !shouldComment {
return nil
}
// Validate we have what we need
if ghCtx == nil {
return fmt.Errorf("--github-comment requires GitHub Actions environment")
}
if ghCtx.Token == "" {
return fmt.Errorf("GITHUB_TOKEN not set")
}
if ghCtx.PRNumber == 0 {
return fmt.Errorf("not a pull request event")
}
// Format and post comment
client := github.NewClient(ghCtx)
commentBody := github.FormatComment(results)
fmt.Fprintln(os.Stderr, "Posting results to PR comment...")
return cliecmd.getEnabledCheckNames function · go · L181-L198 (18 LOC)cli/cmd/check.go
func getEnabledCheckNames(cfg *config.Config) []string {
var checks []string
if cfg.CI.Test.Enabled != nil && *cfg.CI.Test.Enabled {
checks = append(checks, "test")
}
if cfg.CI.Lint.Enabled {
checks = append(checks, "lint")
}
if cfg.CI.Security.Enabled {
checks = append(checks, "security")
}
if cfg.CI.Benchmark.Enabled {
checks = append(checks, "benchmark")
}
return checks
}cmd.runInit function · go · L25-L60 (36 LOC)cli/cmd/init.go
func runInit(cmd *cobra.Command, args []string) error {
// Check if config already exists
configPath := ".go-actions.yaml"
if _, err := os.Stat(configPath); err == nil {
return fmt.Errorf("config file already exists: %s", configPath)
}
// Create default config
cfg := config.DefaultConfig()
// Detect project settings
if err := detectProjectSettings(cfg); err != nil {
fmt.Fprintf(os.Stderr, "Warning: %v\n", err)
}
// Save config
if err := config.Save(cfg, configPath); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
fmt.Println("Created .go-actions.yaml with recommended settings")
// Print what was detected
if cfg.CI.Go.VersionFile != "" {
fmt.Printf("Detected: Go version file: %s\n", cfg.CI.Go.VersionFile)
}
// Check for golangci-lint config
if _, err := os.Stat(".golangci.yml"); err == nil {
fmt.Println("Detected: golangci-lint config present")
} else if _, err := os.Stat(".golangci.yaml"); err == nil {
fmt.Println("Detected: golancmd.detectProjectSettings function · go · L62-L100 (39 LOC)cli/cmd/init.go
func detectProjectSettings(cfg *config.Config) error {
// Check for go.mod
if _, err := os.Stat("go.mod"); err == nil {
cfg.CI.Go.VersionFile = "go.mod"
}
// Check for .go-version
if _, err := os.Stat(".go-version"); err == nil {
cfg.CI.Go.VersionFile = ".go-version"
}
// Note: golangci-lint config detection is handled by the linter itself
// Lint is enabled by default in the config
// Look for test files to determine if testing is viable
hasTests := false
err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil // Skip errors
}
if info.IsDir() && (info.Name() == "vendor" || info.Name() == ".git") {
return filepath.SkipDir
}
if filepath.Ext(path) == ".go" && filepath.Base(path) != "go.mod" {
// Check if it's a test file
if len(filepath.Base(path)) > 8 && filepath.Base(path)[len(filepath.Base(path))-8:] == "_test.go" {
hasTests = true
return filepath.SkipAll
}
}
return nil
})
if cmd.Execute function · go · L25-L30 (6 LOC)cli/cmd/root.go
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}Repobility · severity-and-effort ranking · https://repobility.com
cmd.init function · go · L34-L39 (6 LOC)cli/cmd/validate.go
func init() {
rootCmd.AddCommand(validateCmd)
validateCmd.Flags().BoolVar(&validateFixFlag, "fix", false, "auto-fix issues where possible")
validateCmd.Flags().StringVar(&validateFormatFlag, "format", "text", "output format (text, json)")
validateCmd.Flags().BoolVarP(&validateQuietFlag, "quiet", "q", false, "suppress progress output")
}cmd.runValidate function · go · L41-L85 (45 LOC)cli/cmd/validate.go
func runValidate(cmd *cobra.Command, args []string) error {
v := validate.New(validate.Options{
WorkingDir: ".",
Fix: validateFixFlag,
Quiet: validateQuietFlag || validateFormatFlag == "json",
})
result := v.Validate()
// JSON output
if validateFormatFlag == "json" {
output := map[string]interface{}{
"valid": result.IsValid,
"errors": result.Errors,
"warnings": result.Warnings,
}
data, _ := json.Marshal(output)
fmt.Println(string(data))
if !result.IsValid {
os.Exit(1)
}
return nil
}
// Text output
if result.IsValid {
fmt.Println("✅ All validations passed!")
return nil
}
fmt.Printf("❌ Validation failed with %d error(s):\n", len(result.Errors))
for _, err := range result.Errors {
fmt.Printf(" - %s\n", err)
}
if len(result.Warnings) > 0 {
fmt.Printf("\n⚠️ %d warning(s):\n", len(result.Warnings))
for _, warn := range result.Warnings {
fmt.Printf(" - %s\n", warn)
}
}
os.Exit(1)
return nil
}benchmark.ParseBenchmarkOutput function · go · L21-L51 (31 LOC)cli/internal/benchmark/parser.go
func ParseBenchmarkOutput(output string) ([]Result, error) {
var results []Result
scanner := bufio.NewScanner(strings.NewReader(output))
for scanner.Scan() {
line := scanner.Text()
// Skip lines that don't match benchmark output format
if !strings.HasPrefix(line, "Benchmark") {
continue
}
result, err := parseBenchmarkLine(line)
if err != nil {
// Log error but continue parsing other lines
continue
}
results = append(results, result)
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error reading benchmark output: %w", err)
}
if len(results) == 0 {
return nil, fmt.Errorf("no benchmark results found in output")
}
return results, nil
}benchmark.parseBenchmarkLine function · go · L54-L95 (42 LOC)cli/internal/benchmark/parser.go
func parseBenchmarkLine(line string) (Result, error) {
matches := benchRegex.FindStringSubmatch(line)
if len(matches) < 4 {
return Result{}, fmt.Errorf("invalid benchmark line format: %s", line)
}
result := Result{
Name: matches[1],
}
// Parse iterations
iterations, err := strconv.Atoi(matches[2])
if err != nil {
return Result{}, fmt.Errorf("invalid iterations: %w", err)
}
result.Iterations = iterations
// Parse ns/op
nsPerOp, err := strconv.ParseFloat(matches[3], 64)
if err != nil {
return Result{}, fmt.Errorf("invalid ns/op: %w", err)
}
result.NsPerOp = nsPerOp
// Parse optional B/op (bytes per operation)
if len(matches) > 4 && matches[4] != "" {
bytesPerOp, err := strconv.ParseFloat(matches[4], 64)
if err == nil {
result.BytesPerOp = int64(bytesPerOp)
}
}
// Parse optional allocs/op (allocations per operation)
if len(matches) > 5 && matches[5] != "" {
allocsPerOp, err := strconv.ParseFloat(matches[5], 64)
if err == nil {
result.Allocsbenchmark.NewRunner function · go · L35-L47 (13 LOC)cli/internal/benchmark/runner.go
func NewRunner(opts Options) (*Runner, error) {
if err := validateBenchmarkArgs(opts.BenchmarkArgs); err != nil {
return nil, err
}
args := parseBenchmarkArgs(opts.BenchmarkArgs)
return &Runner{
workingDir: opts.WorkingDirectory,
args: args,
count: opts.BenchmarkCount,
}, nil
}benchmark.Runner.Run method · go · L50-L82 (33 LOC)cli/internal/benchmark/runner.go
func (r *Runner) Run() (*Output, error) {
start := time.Now()
output := &Output{
RunCount: r.count,
Success: true,
}
// Collect all benchmark results across runs
allResults := make(map[string][]Result)
for i := 1; i <= r.count; i++ {
results, err := r.runSingleBenchmark()
if err != nil {
output.Success = false
output.Error = fmt.Sprintf("benchmark run %d/%d failed: %v", i, r.count, err)
return output, err
}
// Group results by benchmark name
for _, result := range results {
allResults[result.Name] = append(allResults[result.Name], result)
}
}
// Calculate statistics for each benchmark
for name, results := range allResults {
stats := calculateStats(name, results)
output.Results = append(output.Results, stats)
}
output.Duration = time.Since(start)
return output, nil
}benchmark.Runner.runSingleBenchmark method · go · L85-L111 (27 LOC)cli/internal/benchmark/runner.go
func (r *Runner) runSingleBenchmark() ([]Result, error) {
// Build command: go test -bench <args> ./...
cmdArgs := append([]string{"test"}, r.args...)
cmdArgs = append(cmdArgs, "./...")
cmd := exec.Command("go", cmdArgs...)
if r.workingDir != "" {
cmd.Dir = r.workingDir
}
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
if err != nil {
return nil, fmt.Errorf("go test failed: %w\nstderr: %s", err, stderr.String())
}
// Parse benchmark output
results, err := ParseBenchmarkOutput(stdout.String())
if err != nil {
return nil, fmt.Errorf("failed to parse benchmark output: %w", err)
}
return results, nil
}benchmark.validateBenchmarkArgs function · go · L114-L121 (8 LOC)cli/internal/benchmark/runner.go
func validateBenchmarkArgs(args string) error {
for _, pattern := range dangerousPatterns {
if pattern.MatchString(args) {
return fmt.Errorf("invalid benchmark arguments: contains potentially dangerous characters: %s", args)
}
}
return nil
}If a scraper extracted this row, it came from Repobility (https://repobility.com)
benchmark.parseBenchmarkArgs function · go · L125-L153 (29 LOC)cli/internal/benchmark/runner.go
func parseBenchmarkArgs(argsString string) []string {
var args []string
var current strings.Builder
inQuote := false
var quoteChar rune
for _, char := range argsString {
if (char == '"' || char == '\'') && !inQuote {
inQuote = true
quoteChar = char
} else if char == quoteChar && inQuote {
inQuote = false
quoteChar = 0
} else if char == ' ' && !inQuote {
if current.Len() > 0 {
args = append(args, current.String())
current.Reset()
}
} else {
current.WriteRune(char)
}
}
if current.Len() > 0 {
args = append(args, current.String())
}
return args
}benchmark.calculateStats function · go · L156-L198 (43 LOC)cli/internal/benchmark/runner.go
func calculateStats(name string, results []Result) Stats {
stats := Stats{
Name: name,
Runs: results,
RunCount: len(results),
}
if len(results) == 0 {
return stats
}
// Calculate min, max, and mean
sum := 0.0
stats.Min = results[0].NsPerOp
stats.Max = results[0].NsPerOp
for _, result := range results {
nsPerOp := result.NsPerOp
sum += nsPerOp
if nsPerOp < stats.Min {
stats.Min = nsPerOp
}
if nsPerOp > stats.Max {
stats.Max = nsPerOp
}
}
stats.Mean = sum / float64(len(results))
// Calculate standard deviation
if len(results) > 1 {
varianceSum := 0.0
for _, result := range results {
diff := result.NsPerOp - stats.Mean
varianceSum += diff * diff
}
variance := varianceSum / float64(len(results))
stats.StdDev = math.Sqrt(variance)
}
return stats
}benchmark.RunBenchmarks function · go · L201-L212 (12 LOC)cli/internal/benchmark/runner.go
func RunBenchmarks(dir string, args string, count int) (*Output, error) {
runner, err := NewRunner(Options{
WorkingDirectory: dir,
BenchmarkArgs: args,
BenchmarkCount: count,
})
if err != nil {
return nil, err
}
return runner.Run()
}config.Load function · go · L12-L48 (37 LOC)cli/internal/config/loader.go
func Load(path string) (*Config, error) {
// Start with defaults
cfg := DefaultConfig()
// If no config file exists, return defaults
if _, err := os.Stat(path); os.IsNotExist(err) {
return cfg, nil
}
// Read the file
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}
// Parse YAML
if err := yaml.Unmarshal(data, cfg); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}
// Validate version
if cfg.Version != 1 {
return nil, fmt.Errorf("unsupported config version: %d (expected 1)", cfg.Version)
}
// Apply defaults for unset boolean pointers
if cfg.CI.Test.Enabled == nil {
trueVal := true
cfg.CI.Test.Enabled = &trueVal
}
if cfg.CI.Test.Coverage.Enabled == nil {
trueVal := true
cfg.CI.Test.Coverage.Enabled = &trueVal
}
return cfg, nil
}config.FindConfigFile function · go · L51-L75 (25 LOC)cli/internal/config/loader.go
func FindConfigFile() (string, error) {
cwd, err := os.Getwd()
if err != nil {
return "", fmt.Errorf("failed to get current directory: %w", err)
}
// Search up the directory tree
dir := cwd
for {
configPath := filepath.Join(dir, ".go-actions.yaml")
if _, err := os.Stat(configPath); err == nil {
return configPath, nil
}
parent := filepath.Dir(dir)
if parent == dir {
// Reached root without finding config
break
}
dir = parent
}
// No config found, return default path
return filepath.Join(cwd, ".go-actions.yaml"), nil
}config.Save function · go · L78-L89 (12 LOC)cli/internal/config/loader.go
func Save(cfg *Config, path string) error {
data, err := yaml.Marshal(cfg)
if err != nil {
return fmt.Errorf("failed to marshal config: %w", err)
}
if err := os.WriteFile(path, data, 0644); err != nil {
return fmt.Errorf("failed to write config file: %w", err)
}
return nil
}config.DefaultConfig function · go · L105-L140 (36 LOC)cli/internal/config/types.go
func DefaultConfig() *Config {
trueVal := true
return &Config{
Version: 1,
CI: CIConfig{
Test: TestConfig{
Enabled: &trueVal,
Args: "-v -race ./...",
Coverage: CoverageConfig{
Enabled: &trueVal,
Threshold: 80.0,
},
},
Lint: LintConfig{
Enabled: true,
Version: "v2.0.2",
Args: "run ./...",
},
Security: SecurityConfig{
Enabled: true,
Version: "latest",
Args: "./...",
FailOn: "high",
},
Benchmark: BenchmarkConfig{
Enabled: false,
Args: "-bench=. -benchmem",
Count: 5,
},
},
Output: OutputConfig{
Format: "auto",
Verbosity: "normal",
},
}
}coverage.validateCoverageFileName function · go · L24-L38 (15 LOC)cli/internal/coverage/extractor.go
func validateCoverageFileName(fileName string) error {
// Only allow alphanumeric, dots, hyphens, and underscores
safePattern := regexp.MustCompile(`^[\w.-]+$`)
// Prevent path traversal
if strings.Contains(fileName, "..") || strings.Contains(fileName, "/") || strings.Contains(fileName, "\\") {
return fmt.Errorf("invalid coverage file name: %s. Path separators not allowed", fileName)
}
if !safePattern.MatchString(fileName) {
return fmt.Errorf("invalid coverage file name: %s. Only alphanumeric characters, dots, hyphens, and underscores are allowed", fileName)
}
return nil
}About: code-quality intelligence by Repobility · https://repobility.com
coverage.ExtractCoverage function · go · L41-L107 (67 LOC)cli/internal/coverage/extractor.go
func ExtractCoverage(coverageFile string, threshold float64) (*CoverageResult, error) {
// Validate coverage file name
fileName := filepath.Base(coverageFile)
if err := validateCoverageFileName(fileName); err != nil {
return &CoverageResult{
Percentage: 0,
Threshold: threshold,
MeetsThreshold: false,
Error: err.Error(),
}, err
}
// Check if coverage file exists
if _, err := os.Stat(coverageFile); os.IsNotExist(err) {
return &CoverageResult{
Percentage: 0,
Threshold: threshold,
MeetsThreshold: false,
Error: "coverage file not found",
}, fmt.Errorf("coverage file not found: %s", coverageFile)
}
// Get absolute path for working directory
workingDir := filepath.Dir(coverageFile)
// Execute go tool cover with safe arguments
cmd := exec.Command("go", "tool", "cover", fmt.Sprintf("-func=%s", fileName))
cmd.Dir = workingDir
output, err := cmd.CombinedOutput()
if err != nil {
errMsg := fmt.Sprintf("go tool cover failed: %v", err)
coverage.extractCoverageFromOutput function · go · L111-L139 (29 LOC)cli/internal/coverage/extractor.go
func extractCoverageFromOutput(output string) (float64, error) {
if output == "" {
return 0, fmt.Errorf("empty coverage output")
}
scanner := bufio.NewScanner(strings.NewReader(output))
for scanner.Scan() {
line := scanner.Text()
// Look for the line containing "total:"
if strings.Contains(line, "total:") {
// Extract the last field which should be the percentage
fields := strings.Fields(line)
if len(fields) == 0 {
continue
}
// Get the last field (percentage)
percentageStr := fields[len(fields)-1]
return parseCoveragePercentage(percentageStr)
}
}
if err := scanner.Err(); err != nil {
return 0, fmt.Errorf("error reading coverage output: %w", err)
}
return 0, fmt.Errorf("total coverage line not found in output")
}coverage.parseCoveragePercentage function · go · L142-L159 (18 LOC)cli/internal/coverage/extractor.go
func parseCoveragePercentage(coverage string) (float64, error) {
// Remove the % sign if present
coverage = strings.TrimSpace(coverage)
coverage = strings.TrimSuffix(coverage, "%")
// Parse as float
percentage, err := strconv.ParseFloat(coverage, 64)
if err != nil {
return 0, fmt.Errorf("failed to parse coverage percentage '%s': %w", coverage, err)
}
// Validate range
if percentage < 0 || percentage > 100 {
return 0, fmt.Errorf("invalid coverage percentage: %.2f (must be between 0 and 100)", percentage)
}
return percentage, nil
}github.Client.ListComments method · go · L44-L71 (28 LOC)cli/internal/github/client.go
func (c *Client) ListComments(owner, repo string, prNumber int) ([]Comment, error) {
url := fmt.Sprintf("%s/repos/%s/%s/issues/%d/comments", c.apiURL, owner, repo, prNumber)
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
c.setHeaders(req)
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to list comments: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("GitHub API returned status %d: %s", resp.StatusCode, string(body))
}
var comments []Comment
if err := json.NewDecoder(resp.Body).Decode(&comments); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}
return comments, nil
}github.Client.CreateComment method · go · L74-L102 (29 LOC)cli/internal/github/client.go
func (c *Client) CreateComment(owner, repo string, prNumber int, body string) error {
url := fmt.Sprintf("%s/repos/%s/%s/issues/%d/comments", c.apiURL, owner, repo, prNumber)
payload := map[string]string{"body": body}
jsonData, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("failed to marshal payload: %w", err)
}
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, url, bytes.NewReader(jsonData))
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
c.setHeaders(req)
resp, err := c.client.Do(req)
if err != nil {
return fmt.Errorf("failed to create comment: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("GitHub API returned status %d: %s", resp.StatusCode, string(body))
}
return nil
}github.Client.UpdateComment method · go · L105-L133 (29 LOC)cli/internal/github/client.go
func (c *Client) UpdateComment(owner, repo string, commentID int64, body string) error {
url := fmt.Sprintf("%s/repos/%s/%s/issues/comments/%d", c.apiURL, owner, repo, commentID)
payload := map[string]string{"body": body}
jsonData, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("failed to marshal payload: %w", err)
}
req, err := http.NewRequestWithContext(context.Background(), http.MethodPatch, url, bytes.NewReader(jsonData))
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
c.setHeaders(req)
resp, err := c.client.Do(req)
if err != nil {
return fmt.Errorf("failed to update comment: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("GitHub API returned status %d: %s", resp.StatusCode, string(body))
}
return nil
}github.Client.UpsertComment method · go · L136-L151 (16 LOC)cli/internal/github/client.go
func (c *Client) UpsertComment(owner, repo string, prNumber int, body, marker string) error {
comments, err := c.ListComments(owner, repo, prNumber)
if err != nil {
return fmt.Errorf("failed to list comments: %w", err)
}
// Find existing bot comment with marker
for _, comment := range comments {
if comment.User.Type == "Bot" && strings.Contains(comment.Body, marker) {
return c.UpdateComment(owner, repo, comment.ID, body)
}
}
// No existing comment found, create new
return c.CreateComment(owner, repo, prNumber, body)
}github.Client.setHeaders method · go · L154-L160 (7 LOC)cli/internal/github/client.go
func (c *Client) setHeaders(req *http.Request) {
req.Header.Set("Accept", "application/vnd.github.v3+json")
req.Header.Set("Content-Type", "application/json")
if c.token != "" {
req.Header.Set("Authorization", "Bearer "+c.token)
}
}Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
github.titleCase function · go · L21-L26 (6 LOC)cli/internal/github/comment.go
func titleCase(s string) string {
if s == "" {
return s
}
return strings.ToUpper(s[:1]) + s[1:]
}github.formatStatusLine function · go · L62-L122 (61 LOC)cli/internal/github/comment.go
func formatStatusLine(check output.CheckResult) string {
var line string
switch check.Name {
case "test":
icon := "✅"
if check.Status == "fail" || check.Status == "error" {
icon = "❌"
}
line = fmt.Sprintf("%s **Tests**", icon)
if check.Coverage > 0 {
line += fmt.Sprintf(" (%.1f%% coverage)", check.Coverage)
} else if check.Status == "fail" {
line += " (failed)"
}
case "lint":
icon := "✅"
if check.Status == "fail" || check.Status == "error" {
icon = "🚨"
}
line = fmt.Sprintf("%s **Lint**", icon)
if check.Status == "fail" || check.Status == "error" {
line += " **- Issues Found!**"
}
case "security":
icon := "✅"
if check.Status == "fail" || check.Status == "error" {
icon = "🚨"
}
line = fmt.Sprintf("%s **Security**", icon)
if check.Status == "fail" || check.Status == "error" {
count := check.Vulnerabilities
plural := "ies"
if count == 1 {
plural = "y"
}
line += fmt.Sprintf(" **- %d Vulnerabilit%s Found!**", cougithub.formatDetailsSection function · go · L125-L140 (16 LOC)cli/internal/github/comment.go
func formatDetailsSection(check output.CheckResult) string {
var section strings.Builder
switch check.Name {
case "test":
section.WriteString(formatTestDetails(check))
case "lint":
section.WriteString(formatLintDetails(check))
case "security":
section.WriteString(formatSecurityDetails(check))
case "benchmark":
section.WriteString(formatBenchmarkDetails(check))
}
return section.String()
}github.formatTestDetails function · go · L143-L172 (30 LOC)cli/internal/github/comment.go
func formatTestDetails(check output.CheckResult) string {
var details strings.Builder
if check.Status == "pass" {
if check.Coverage > 0 {
emoji := getCoverageEmoji(check.Coverage)
coverageMsg := getCoverageMessage(check.Coverage)
details.WriteString("<details><summary>Test Details</summary>\n\n")
details.WriteString(fmt.Sprintf("**Coverage: %.1f%%**\n\n", check.Coverage))
details.WriteString(fmt.Sprintf("%s %s\n\n", emoji, coverageMsg))
details.WriteString("</details>\n\n")
} else {
details.WriteString("<details><summary>Test Details</summary>\n\n")
details.WriteString("All tests passed successfully!\n\n")
details.WriteString("</details>\n\n")
}
} else {
details.WriteString("<details open><summary>Test Issues</summary>\n\n")
details.WriteString("**Tests failed!**\n\n")
if check.Message != "" {
details.WriteString(fmt.Sprintf("**Error:** %s\n\n", check.Message))
} else {
details.WriteString("Please check the test logs for details.\n\n"github.formatLintDetails function · go · L175-L201 (27 LOC)cli/internal/github/comment.go
func formatLintDetails(check output.CheckResult) string {
var details strings.Builder
if check.Status == "pass" {
details.WriteString("<details><summary>Lint Details</summary>\n\n")
details.WriteString("Code quality checks passed!\n\n")
details.WriteString("</details>\n\n")
} else {
details.WriteString("<details open><summary>🚨 Lint Issues Found</summary>\n\n")
details.WriteString("**Code quality checks failed!**")
if check.Message != "" {
issuesOutput := check.Message
// Truncate if too long (GitHub comment limit considerations)
if len(issuesOutput) > MaxLintOutputLength {
issuesOutput = issuesOutput[:MaxLintOutputLength] + "\n\n... (truncated, see workflow logs for full output)"
}
details.WriteString(fmt.Sprintf("\n\n```\n%s\n```\n", issuesOutput))
} else {
details.WriteString("\n\nPlease check the workflow logs for details.")
}
details.WriteString("\n\n</details>\n\n")
}
return details.String()
}github.formatSecurityDetails function · go · L204-L232 (29 LOC)cli/internal/github/comment.go
func formatSecurityDetails(check output.CheckResult) string {
var details strings.Builder
if check.Status == "pass" {
details.WriteString("<details><summary>Security Details</summary>\n\n")
details.WriteString("🛡️ No known vulnerabilities found in dependencies!\n\n")
details.WriteString("**Tool:** govulncheck (Go Vulnerability Database)\n\n")
details.WriteString("</details>\n\n")
} else {
count := check.Vulnerabilities
plural := "ies"
if count == 1 {
plural = "y"
}
details.WriteString(fmt.Sprintf("<details open><summary>🚨 %d Vulnerabilit%s Found</summary>\n\n", count, plural))
details.WriteString("**Security scan detected known CVEs in your dependencies!**")
if check.Message != "" {
details.WriteString(fmt.Sprintf("\n\n%s\n", check.Message))
} else {
details.WriteString("\n\nPlease check the workflow logs for details.")
}
details.WriteString("\n\n</details>\n\n")
}
return details.String()
}github.formatBenchmarkDetails function · go · L235-L252 (18 LOC)cli/internal/github/comment.go
func formatBenchmarkDetails(check output.CheckResult) string {
var details strings.Builder
if check.Status == "pass" {
details.WriteString("<details><summary>Benchmark Details</summary>\n\n")
details.WriteString("Benchmarks completed successfully!\n\n")
details.WriteString("</details>\n\n")
} else {
details.WriteString("<details open><summary>Benchmark Issues</summary>\n\n")
details.WriteString("**Benchmarks failed!**\n\n")
if check.Message != "" {
details.WriteString(fmt.Sprintf("**Error:** %s\n\n", check.Message))
}
details.WriteString("</details>\n\n")
}
return details.String()
}github.getCoverageEmoji function · go · L255-L262 (8 LOC)cli/internal/github/comment.go
func getCoverageEmoji(coverage float64) string {
if coverage >= ExcellentCoverageThreshold {
return "🎉"
} else if coverage >= GoodCoverageThreshold {
return "⚠️"
}
return "🚨"
}Repobility · severity-and-effort ranking · https://repobility.com
github.getCoverageMessage function · go · L265-L272 (8 LOC)cli/internal/github/comment.go
func getCoverageMessage(coverage float64) string {
if coverage >= ExcellentCoverageThreshold {
return "Excellent test coverage!"
} else if coverage >= GoodCoverageThreshold {
return "Good coverage, consider adding more tests."
}
return "Low test coverage detected. Please add more tests."
}github.formatEmptyComment function · go · L275-L300 (26 LOC)cli/internal/github/comment.go
func formatEmptyComment() string {
return `# Go Actions Report
⏳ **Pending**
<details><summary>Details</summary>
No CI jobs have run yet. Results will appear here as jobs complete.
</details>
*🤖 This comment will update automatically as you push changes.*
*Generated by [go-actions](https://github.com/jrschumacher/go-actions)*`
}
// FormatProcessingComment returns a comment for when jobs are running
func FormatProcessingComment() string {
return `# Go Actions Report
🔄 **Running...**
Validation is in progress. Results will appear here shortly.
*🤖 This comment will update automatically as jobs complete.*
*Generated by [go-actions](https://github.com/jrschumacher/go-actions)*`
}github.DetectGitHub function · go · L27-L66 (40 LOC)cli/internal/github/env.go
func DetectGitHub() *GitHubContext {
// Check if running in GitHub Actions
if os.Getenv("GITHUB_ACTIONS") != "true" {
return nil
}
ctx := &GitHubContext{
IsGitHubActions: true,
EventName: os.Getenv("GITHUB_EVENT_NAME"),
Token: os.Getenv("GITHUB_TOKEN"),
Repository: os.Getenv("GITHUB_REPOSITORY"),
ServerURL: getEnvOrDefault("GITHUB_SERVER_URL", "https://github.com"),
APIURL: getEnvOrDefault("GITHUB_API_URL", "https://api.github.com"),
}
// Parse repository into owner and repo
if ctx.Repository != "" {
parts := strings.SplitN(ctx.Repository, "/", 2)
if len(parts) == 2 {
ctx.Owner = parts[0]
ctx.Repo = parts[1]
}
}
// Parse PR number from event file
if eventPath := os.Getenv("GITHUB_EVENT_PATH"); eventPath != "" {
if prNum, err := parsePRNumberFromEvent(eventPath); err == nil {
ctx.PRNumber = prNum
}
}
// Parse run ID
if runIDStr := os.Getenv("GITHUB_RUN_ID"); runIDStr != "" {
if runID, err := strconv.github.getEnvOrDefault function · go · L69-L74 (6 LOC)cli/internal/github/env.go
func getEnvOrDefault(key, defaultValue string) string {
if val := os.Getenv(key); val != "" {
return val
}
return defaultValue
}github.parsePRNumberFromEvent function · go · L77-L105 (29 LOC)cli/internal/github/env.go
func parsePRNumberFromEvent(eventPath string) (int, error) {
data, err := os.ReadFile(eventPath)
if err != nil {
return 0, fmt.Errorf("failed to read event file: %w", err)
}
var event struct {
Number int `json:"number"`
PullRequest *struct {
Number int `json:"number"`
} `json:"pull_request"`
}
if err := json.Unmarshal(data, &event); err != nil {
return 0, fmt.Errorf("failed to parse event JSON: %w", err)
}
// Try .pull_request.number first (for push events referencing a PR)
if event.PullRequest != nil && event.PullRequest.Number > 0 {
return event.PullRequest.Number, nil
}
// Fall back to .number (for pull_request events)
if event.Number > 0 {
return event.Number, nil
}
return 0, fmt.Errorf("no PR number found in event")
}lint.ParseLintOutput function · go · L12-L32 (21 LOC)cli/internal/lint/formatter.go
func ParseLintOutput(jsonFilePath string) (*Report, error) {
data, err := os.ReadFile(jsonFilePath)
if err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("lint output file not found: %s", jsonFilePath)
}
return nil, fmt.Errorf("failed to read lint output file: %w", err)
}
// Handle empty file
if len(strings.TrimSpace(string(data))) == 0 {
return &Report{Issues: []Issue{}}, nil
}
var report Report
if err := json.Unmarshal(data, &report); err != nil {
return nil, fmt.Errorf("failed to parse lint output JSON: %w", err)
}
return &report, nil
}lint.GroupIssuesByLinter function · go · L35-L81 (47 LOC)cli/internal/lint/formatter.go
func GroupIssuesByLinter(issues []Issue) []LinterGroup {
grouped := make(map[string]*LinterGroup)
for _, issue := range issues {
linterName := issue.FromLinter
if linterName == "" {
linterName = "unknown"
}
if _, exists := grouped[linterName]; !exists {
grouped[linterName] = &LinterGroup{
Name: linterName,
Count: 0,
Issues: []GroupedIssue{},
}
}
group := grouped[linterName]
group.Count++
group.Issues = append(group.Issues, GroupedIssue{
File: issue.Pos.Filename,
Line: issue.Pos.Line,
Column: issue.Pos.Column,
Message: issue.Text,
})
}
// Convert map to slice
var groups []LinterGroup
for _, group := range grouped {
// Sort issues within each group by file, then line
sort.Slice(group.Issues, func(i, j int) bool {
if group.Issues[i].File != group.Issues[j].File {
return group.Issues[i].File < group.Issues[j].File
}
return group.Issues[i].Line < group.Issues[j].Line
})
groups = append(groups, *grouplint.FormatGroupedIssues function · go · L84-L154 (71 LOC)cli/internal/lint/formatter.go
func FormatGroupedIssues(groups []LinterGroup, options FormatOptions) string {
if len(groups) == 0 {
return ""
}
var output strings.Builder
issuesShown := 0
lintersShown := 0
totalLinters := len(groups)
// Calculate total issues
totalIssues := 0
for _, group := range groups {
totalIssues += group.Count
}
for _, group := range groups {
// Check if we've hit the total issues limit
if issuesShown >= options.MaxTotalIssues {
remaining := totalIssues - issuesShown
remainingLinters := totalLinters - lintersShown
output.WriteString(fmt.Sprintf("\n*...and %d more issue%s from %d linter%s*\n",
remaining,
pluralize(remaining),
remainingLinters,
pluralize(remainingLinters)))
break
}
lintersShown++
// Format linter section
linterTitle := fmt.Sprintf("**%s** (%d issue%s)", group.Name, group.Count, pluralize(group.Count))
if options.UseCollapsible {
// Use collapsible section for each linter
isOpen := lintersShown == 1 // First linIf a scraper extracted this row, it came from Repobility (https://repobility.com)
lint.FormatLintOutput function · go · L157-L204 (48 LOC)cli/internal/lint/formatter.go
func FormatLintOutput(jsonFilePath string, options FormatOptions) (*FormattedOutput, error) {
report, err := ParseLintOutput(jsonFilePath)
if err != nil {
return nil, err
}
if len(report.Issues) == 0 {
return nil, nil
}
groups := GroupIssuesByLinter(report.Issues)
formattedIssues := FormatGroupedIssues(groups, options)
if formattedIssues == "" {
return nil, nil
}
// Build header with total count
totalIssues := len(report.Issues)
var header strings.Builder
header.WriteString(fmt.Sprintf("### 🚨 Lint Issues Found (%d issue%s)\n\n",
totalIssues, pluralize(totalIssues)))
// Add workflow logs link if provided
if options.WorkflowLogsURL != "" {
header.WriteString(fmt.Sprintf("[📋 View full logs](%s)\n\n", options.WorkflowLogsURL))
}
markdown := header.String() + formattedIssues
// Determine if output was truncated
truncated := false
issueCount := 0
for _, group := range groups {
issueCount += min(len(group.Issues), options.MaxIssuesPerLinter)
if issueClint.GetWorkflowLogsURL function · go · L207-L217 (11 LOC)cli/internal/lint/formatter.go
func GetWorkflowLogsURL() string {
serverURL := os.Getenv("GITHUB_SERVER_URL")
repository := os.Getenv("GITHUB_REPOSITORY")
runID := os.Getenv("GITHUB_RUN_ID")
if serverURL != "" && repository != "" && runID != "" {
return fmt.Sprintf("%s/%s/actions/runs/%s", serverURL, repository, runID)
}
return ""
}lint.pluralize function · go · L220-L225 (6 LOC)cli/internal/lint/formatter.go
func pluralize(count int) string {
if count == 1 {
return ""
}
return "s"
}page 1 / 2next ›