-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
170 lines (142 loc) · 4.65 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"strings"
"github.com/tateexon/go-change-delta/cmd"
"github.com/tateexon/go-change-delta/git"
"github.com/tateexon/go-change-delta/golang"
)
type Config struct {
Branch string
ProjectPath string
Excludes []string
Levels int
IncludeTestDeps bool
}
func main() {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
go func() {
<-ctx.Done()
stop() // restore default exit behavior
log.Println("Cancelling... interrupt again to exit")
}()
branch := flag.String("b", "", "The base git branch to compare current changes with. Required.")
projectPath := flag.String("p", "", "The path to the project. Default is the current directory. Useful for subprojects.")
excludes := flag.String("e", "", "The comma separated list of paths to exclude. Useful for repositories with multiple go projects within.")
levels := flag.Int("l", 2, "The number of levels of recursion to search for affected packages. Default is 2. 0 is unlimited.")
includeTestDeps := flag.Bool("t", true, "Should we include test dependencies. Default is true")
flag.Parse()
checkTools()
config := setConfig(branch, projectPath, excludes, levels, includeTestDeps)
goList, gitDiff, gitModDiff := makeExecCalls(config)
run(config, goList, gitDiff, gitModDiff)
}
func setConfig(branch, projectPath, excludes *string, levels *int, includeTestDeps *bool) *Config {
if *branch == "" {
log.Fatalf("Branch is required")
}
parsedExcludes := []string{}
if *excludes != "" {
parsedExcludes = strings.Split(*excludes, ",")
for i, e := range parsedExcludes {
parsedExcludes[i] = strings.TrimSpace(e)
}
}
return &Config{
Branch: *branch,
ProjectPath: *projectPath,
Excludes: parsedExcludes,
Levels: *levels,
IncludeTestDeps: *includeTestDeps,
}
}
// checkTools check if the required tools are installed in the path
func checkTools() {
_, goErr := exec.LookPath("go")
_, gitErr := exec.LookPath("git")
toolsMissing := []string{}
if goErr != nil {
toolsMissing = append(toolsMissing, "go")
}
if gitErr != nil {
toolsMissing = append(toolsMissing, "git")
}
if len(toolsMissing) > 0 {
log.Fatalf("tools are missing from your path and may not be installed that are required to run this tool: %+v", toolsMissing)
}
}
func run(config *Config, goList, gitDiff, gitModDiff *cmd.Output) {
packages, err := golang.ParsePackages(goList.Stdout)
if err != nil {
log.Fatalf("Error parsing packages: %v", err)
}
fileMap := golang.GetGoFileMap(packages, config.IncludeTestDeps)
var changedPackages []string
changedPackages, err = git.GetChangedGoPackagesFromDiff(gitDiff.Stdout, config.ProjectPath, config.Excludes, fileMap)
if err != nil {
log.Fatalf("Error getting changed packages: %v", err)
}
changedModPackages, err := git.GetGoModChangesFromDiff(gitModDiff.Stdout)
if err != nil {
log.Fatalf("Error getting go.mod changes: %v", err)
}
depMap := golang.GetGoDepMap(packages)
affectedPkgs := findAllAffectedPackages(config, changedPackages, changedModPackages, depMap)
printAffectedPackages(affectedPkgs)
}
func findAllAffectedPackages(config *Config, changedPackages, changedModPackages []string, depMap golang.DepMap) []string {
// Find affected packages
// use map to make handling duplicates simpler
affectedPkgs := map[string]bool{}
// loop through packages changed via file changes
for _, pkg := range changedPackages {
p := golang.FindAffectedPackages(pkg, depMap, false, config.Levels)
for _, p := range p {
affectedPkgs[p] = true
}
}
// loop through packages changed via go.mod changes
for _, pkg := range changedModPackages {
p := golang.FindAffectedPackages(pkg, depMap, true, config.Levels)
for _, p := range p {
affectedPkgs[p] = true
}
}
// convert map to array
pkgs := []string{}
for k := range affectedPkgs {
pkgs = append(pkgs, k)
}
return pkgs
}
func makeExecCalls(config *Config) (*cmd.Output, *cmd.Output, *cmd.Output) {
goList, err := golang.GoList()
if err != nil {
log.Fatalf("Error getting go list: %v\nStdErr: %s", err, goList.Stderr.String())
}
gitDiff, err := git.Diff(config.Branch)
if err != nil {
log.Fatalf("Error getting the git diff: %v\nStdErr: %s", err, gitDiff.Stderr.String())
}
gitModDiff, err := git.ModDiff(config.Branch, config.ProjectPath)
if err != nil {
log.Fatalf("Error getting the git mod diff: %v\nStdErr: %s", err, gitModDiff.Stderr.String())
}
return goList, gitDiff, gitModDiff
}
func printAffectedPackages(pkgs []string) {
o := ""
for _, k := range pkgs {
o = fmt.Sprintf("%s %s", o, k)
}
if len(o) > 0 {
fmt.Println(o)
}
}