Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions cli/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cli

import (
"fmt"
"log"
"time"

"github.com/bubunyo/buildgraph/pkg/diff"
Expand All @@ -21,7 +20,7 @@ and outputs which services are affected by the detected changes.`,
}

func init() {
analyzeCmd.Flags().StringP("format", "f", "json", "Output format: json, text")
analyzeCmd.Flags().StringP("format", "f", "text", "Output format: text, json")

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README.md states that JSON is the default output format (# JSON output (default)) for buildgraph analyze, but this PR changes the default from "json" to "text". The README documentation is now incorrect and should be updated to reflect that text is the new default (i.e., swap the examples so text is the default and json requires --format json).

Copilot uses AI. Check for mistakes.
analyzeCmd.Flags().StringP("output", "o", "", "Output file (default: stdout)")
analyzeCmd.Flags().BoolP("verbose", "v", false, "Include debug info in output")
analyzeCmd.Flags().Bool("no-cache", false, "Ignore baseline, treat everything as new")
Expand All @@ -41,8 +40,6 @@ func runAnalyze(cmd *cobra.Command, _ []string) error {
return fmt.Errorf("detecting root module: %w", err)
}

log.Printf("Analyzing project: %s", rootModule)

// Load baseline before parsing so unchanged functions can reuse stored hashes.
var previousBaseline *types.Baseline
noCache, _ := cmd.Flags().GetBool("no-cache")
Expand Down Expand Up @@ -91,11 +88,5 @@ func runAnalyze(cmd *cobra.Command, _ []string) error {
output, _ := cmd.Flags().GetString("output")
writeOutput(result, format, output)

if len(changes) > 0 {
log.Printf("Changes detected: %d", len(changes))
log.Printf("Services to build: %v", impactResult.ServicesToBuild)
} else {
log.Printf("No changes detected")
}
return nil
}
9 changes: 5 additions & 4 deletions cli/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cli
import (
"encoding/json"
"fmt"
"log"
"os"
"sort"
"strings"
Expand All @@ -22,13 +21,15 @@ func writeOutput(result *types.Result, format, outputPath string) {
var marshalErr error
output, marshalErr = json.MarshalIndent(result, "", " ")
if marshalErr != nil {
log.Fatalf("failed to marshal result to JSON: %v", marshalErr)
fmt.Fprintf(os.Stderr, "failed to marshal result to JSON: %v\n", marshalErr)
os.Exit(1)
}
}

if outputPath != "" {
if err := os.WriteFile(outputPath, output, 0644); err != nil {
log.Fatalf("failed to write output: %v", err)
fmt.Fprintf(os.Stderr, "failed to write output: %v\n", err)
os.Exit(1)
}
return
}
Expand All @@ -45,7 +46,7 @@ func formatText(result *types.Result) string {
if len(result.Changes) > 0 {
fmt.Fprintf(sb, "Changes (%d):\n", len(result.Changes))
for _, c := range result.Changes {
fmt.Fprintf(sb, " [%-20s] %s\n", c.Type, c.Function)
fmt.Fprintf(sb, " [%s] %s\n", c.Type, c.Function)
if c.Reason != "" {
fmt.Fprintf(sb, " reason : %s\n", c.Reason)
}
Expand Down
11 changes: 5 additions & 6 deletions cli/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cli

import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -30,12 +29,12 @@ func parseProject(
) {
a := analyzer.New(cfg, rootModule, rootPath)

log.Printf("Loading packages…")
fmt.Fprintln(os.Stderr, "Loading packages…")
if err = a.Load(); err != nil {
return
}

log.Printf("Building call graph…")
fmt.Fprintln(os.Stderr, "Building call graph…")
functions, graph, err = a.BuildGraph()
if err != nil {
return
Expand All @@ -48,22 +47,22 @@ func parseProject(
prevFuncHashes = prevBaseline.FunctionHashes
}

log.Printf("Computing hashes for %d functions…", len(functions))
fmt.Fprintf(os.Stderr, "Computing hashes for %d functions…\n", len(functions))
if err = a.ComputeHashes(functions, prevSrcHashes, prevFuncHashes); err != nil {
return
}

extDeps, extHash, err = a.ExtractExternalDeps()
if err != nil {
log.Printf("Warning: could not extract external deps: %v", err)
fmt.Fprintf(os.Stderr, "Warning: could not extract external deps: %v\n", err)
extDeps = map[string]string{}
extHash = ""
err = nil //nolint:ineffassign
}

sourceHashes, err = a.ComputeSourceHashes()
if err != nil {
log.Printf("Warning: could not compute source hashes: %v", err)
fmt.Fprintf(os.Stderr, "Warning: could not compute source hashes: %v\n", err)
sourceHashes = map[string]string{}
err = nil //nolint:ineffassign
}
Expand Down
8 changes: 5 additions & 3 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package cli

import (
"errors"
"log"
"fmt"
"os"
"strings"

Expand Down Expand Up @@ -71,7 +71,8 @@ func initConfig() {
// errors.Is(err, os.ErrNotExist) to correctly catch the missing-file
// case and only fatal on genuine read errors (bad permissions, etc.).
if !errors.Is(err, os.ErrNotExist) {
log.Fatalf("error reading config file %s: %v", cfgFile, err)
fmt.Fprintf(os.Stderr, "error reading config file %s: %v\n", cfgFile, err)
os.Exit(1)
}
}
}
Expand All @@ -80,7 +81,8 @@ func initConfig() {
func loadConfig() *config.Config {
cfg := config.Default()
if err := viper.Unmarshal(cfg); err != nil {
log.Fatalf("invalid configuration: %v", err)
fmt.Fprintf(os.Stderr, "invalid configuration: %v\n", err)
os.Exit(1)
}
if len(cfg.Services) == 0 {
cfg.Services = config.Default().Services
Expand Down
1 change: 0 additions & 1 deletion pkg/impact/impact.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ func (a *Analyzer) ComputeImpact(changes []types.Change) types.Impact {
AffectedFunctions: make(map[string][]string),
AffectReasons: make(map[string][]string),
ServicesToBuild: []string{},
Changes: changes,
}

// Get initial changed functions
Expand Down
5 changes: 2 additions & 3 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ type Change struct {
}

type Impact struct {
AffectedFunctions map[string][]string `json:"affected_functions"`
AffectReasons map[string][]string `json:"affect_reasons"`
AffectedFunctions map[string][]string `json:"-"`
AffectReasons map[string][]string `json:"-"`
Comment on lines +73 to +74

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AffectedFunctions and AffectReasons fields of the Impact struct are now tagged with json:"-", excluding them from the JSON output. However, the README.md JSON output example still shows "affected_functions" as part of the impact object, and SPEC.md documents both affected_functions and affect_reasons with their original JSON tags. These documentation artifacts are now incorrect and should be updated to match the new JSON output, which only includes "services_to_build" under "impact".

Copilot uses AI. Check for mistakes.
ServicesToBuild []string `json:"services_to_build"`
Changes []Change `json:"changes"`
}

type DebugInfo struct {
Expand Down
Loading