mirror of
https://github.com/therootcompany/golib.git
synced 2026-03-13 12:27:59 +00:00
WIP: feat(cmd/monorelease): better table
This commit is contained in:
parent
c8c2b63e93
commit
3abf0838c2
@ -25,12 +25,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var verbose = flag.Bool("verbose", false, "")
|
||||||
verbose = flag.Bool("verbose", false, "")
|
var ignoreDirty = flag.String("ignore-dirty", "", "ignore dirty states [u n m d]")
|
||||||
ignoreDirty = flag.String("ignore-dirty", "", "ignore dirty states [u n m d]")
|
var useCSV = flag.Bool("csv", false, "output CSV instead of table")
|
||||||
csvOut = flag.Bool("csv", false, "output real CSV")
|
var csvComma = flag.String("comma", ",", "CSV field separator")
|
||||||
comma = flag.String("comma", ",", "CSV field delimiter")
|
|
||||||
)
|
|
||||||
|
|
||||||
func runGit(args ...string) (string, error) {
|
func runGit(args ...string) (string, error) {
|
||||||
cmd := exec.Command("git", args...)
|
cmd := exec.Command("git", args...)
|
||||||
@ -45,15 +43,35 @@ func isVersion(s string) bool {
|
|||||||
return re.MatchString(s)
|
return re.MatchString(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Module struct {
|
||||||
|
Path string
|
||||||
|
Type string
|
||||||
|
Untracked bool
|
||||||
|
Name string
|
||||||
|
Bins map[string]string
|
||||||
|
HasRootMain bool
|
||||||
|
}
|
||||||
|
|
||||||
type Row struct {
|
type Row struct {
|
||||||
Status string
|
Status string
|
||||||
Type string
|
Typ string
|
||||||
Name string
|
Name string
|
||||||
Version string
|
Version string
|
||||||
CurrentTag string
|
Tag string
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func manifestPath(m Module) string {
|
||||||
|
f := "go.mod"
|
||||||
|
if m.Type == "node" {
|
||||||
|
f = "package.json"
|
||||||
|
}
|
||||||
|
if m.Path == "" {
|
||||||
|
return "./" + f
|
||||||
|
}
|
||||||
|
return "./" + m.Path + "/" + f
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -62,21 +80,16 @@ func main() {
|
|||||||
ignoreSet[c] = true
|
ignoreSet[c] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
repoDir, _ := runGit("rev-parse", "--show-toplevel")
|
rootB, _ := exec.Command("git", "rev-parse", "--show-toplevel").Output()
|
||||||
_ = os.Chdir(strings.TrimSpace(repoDir))
|
root := strings.TrimSpace(string(rootB))
|
||||||
|
_ = os.Chdir(root)
|
||||||
|
repoName := filepath.Base(root)
|
||||||
|
|
||||||
repoName := filepath.Base(repoDir)
|
ls, _ := runGit("ls-files")
|
||||||
|
committed := strings.Split(ls, "\n")
|
||||||
gitFiles, _ := runGit("ls-files")
|
|
||||||
committed := strings.Split(gitFiles, "\n")
|
|
||||||
|
|
||||||
|
modMap := make(map[string]Module)
|
||||||
modTypes := map[string]string{"go.mod": "go", "package.json": "node"}
|
modTypes := map[string]string{"go.mod": "go", "package.json": "node"}
|
||||||
modMap := make(map[string]struct {
|
|
||||||
Path string
|
|
||||||
Type string
|
|
||||||
Untracked bool
|
|
||||||
Bins map[string]string
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, f := range committed {
|
for _, f := range committed {
|
||||||
for suf, typ := range modTypes {
|
for suf, typ := range modTypes {
|
||||||
@ -85,19 +98,13 @@ func main() {
|
|||||||
if p == "." {
|
if p == "." {
|
||||||
p = ""
|
p = ""
|
||||||
}
|
}
|
||||||
modMap[p] = struct {
|
modMap[p] = Module{Path: p, Type: typ, Untracked: false}
|
||||||
Path string
|
|
||||||
Type string
|
|
||||||
Untracked bool
|
|
||||||
Bins map[string]string
|
|
||||||
}{p, typ, false, make(map[string]string)}
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
untrackedS, _ := runGit("ls-files", "--others", "--exclude-standard")
|
||||||
moreFiles, _ := runGit("ls-files", "--others", "--exclude-standard")
|
for _, f := range strings.Split(untrackedS, "\n") {
|
||||||
for _, f := range strings.Split(moreFiles, "\n") {
|
|
||||||
if f == "" {
|
if f == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -108,333 +115,442 @@ func main() {
|
|||||||
p = ""
|
p = ""
|
||||||
}
|
}
|
||||||
if _, ok := modMap[p]; !ok {
|
if _, ok := modMap[p]; !ok {
|
||||||
modMap[p] = struct {
|
modMap[p] = Module{Path: p, Type: typ, Untracked: true}
|
||||||
Path string
|
|
||||||
Type string
|
|
||||||
Untracked bool
|
|
||||||
Bins map[string]string
|
|
||||||
}{p, typ, true, make(map[string]string)}
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
goFiles, _ := runGit("ls-files", "*.go")
|
goCommS, _ := runGit("ls-files", "--", "*.go")
|
||||||
moreGoFiles, _ := runGit("ls-files", "--others", "--exclude-standard", "*.go")
|
goUntrS, _ := runGit("ls-files", "--others", "--exclude-standard", "--", "*.go")
|
||||||
allGoFiles := append(
|
allGoFiles := []string{}
|
||||||
strings.Split(goFiles, "\n"),
|
for _, s := range []string{goCommS, goUntrS} {
|
||||||
strings.Split(moreGoFiles, "\n")...,
|
for _, line := range strings.Split(s, "\n") {
|
||||||
)
|
if line != "" {
|
||||||
|
allGoFiles = append(allGoFiles, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// go module prefix matching
|
modules := make([]Module, 0, len(modMap))
|
||||||
goPrefixes := []string{}
|
for _, m := range modMap {
|
||||||
for p := range modMap {
|
modules = append(modules, m)
|
||||||
if modMap[p].Type == "go" {
|
|
||||||
goPrefixes = append(goPrefixes, p+"/")
|
|
||||||
}
|
}
|
||||||
}
|
sort.Slice(modules, func(i, j int) bool { return modules[i].Path < modules[j].Path })
|
||||||
if _, ok := modMap[""]; ok && modMap[""].Type == "go" {
|
|
||||||
goPrefixes = append(goPrefixes, "")
|
|
||||||
}
|
|
||||||
sort.Slice(goPrefixes, func(i, j int) bool { return len(goPrefixes[i]) > len(goPrefixes[j]) })
|
|
||||||
|
|
||||||
goModuleOf := make(map[string]string)
|
goModulePrefixes := []string{}
|
||||||
for _, f := range allGoFiles {
|
for _, m := range modules {
|
||||||
if f == "" {
|
if m.Type == "go" {
|
||||||
continue
|
pre := m.Path
|
||||||
|
if pre != "" {
|
||||||
|
pre += "/"
|
||||||
}
|
}
|
||||||
dir := filepath.Dir(f)
|
goModulePrefixes = append(goModulePrefixes, pre)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(goModulePrefixes, func(i, j int) bool { return len(goModulePrefixes[i]) > len(goModulePrefixes[j]) })
|
||||||
|
|
||||||
|
goToModule := make(map[string]string)
|
||||||
|
for _, gf := range allGoFiles {
|
||||||
|
dir := filepath.Dir(gf)
|
||||||
if dir == "." {
|
if dir == "." {
|
||||||
dir = ""
|
dir = ""
|
||||||
}
|
}
|
||||||
for _, pre := range goPrefixes {
|
matchP := ""
|
||||||
if pre == "" || strings.HasPrefix(dir+"/", pre) {
|
for _, pre := range goModulePrefixes {
|
||||||
goModuleOf[f] = strings.TrimSuffix(pre, "/")
|
if pre != "" && strings.HasPrefix(dir+"/", pre) {
|
||||||
|
matchP = strings.TrimSuffix(pre, "/")
|
||||||
|
break
|
||||||
|
} else if pre == "" {
|
||||||
|
matchP = ""
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if matchP != "" || goToModule[gf] == "" {
|
||||||
|
goToModule[gf] = matchP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate bins
|
for i := range modules {
|
||||||
for p, m := range modMap {
|
m := &modules[i]
|
||||||
|
m.Bins = make(map[string]string)
|
||||||
|
m.Name = ""
|
||||||
if m.Type == "node" {
|
if m.Type == "node" {
|
||||||
pkgPath := filepath.Join(p, "package.json")
|
pkgFile := "package.json"
|
||||||
if p == "" {
|
if m.Path != "" {
|
||||||
pkgPath = "package.json"
|
pkgFile = filepath.Join(m.Path, "package.json")
|
||||||
}
|
}
|
||||||
data, _ := os.ReadFile(pkgPath)
|
data, err := os.ReadFile(pkgFile)
|
||||||
var pkg struct {
|
if err == nil {
|
||||||
|
var pi struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Bin map[string]any `json:"bin"`
|
Bin any `json:"bin"`
|
||||||
|
}
|
||||||
|
if json.Unmarshal(data, &pi) == nil {
|
||||||
|
if pi.Name != "" {
|
||||||
|
name := pi.Name
|
||||||
|
if idx := strings.LastIndex(name, "/"); idx != -1 {
|
||||||
|
name = name[idx+1:]
|
||||||
|
}
|
||||||
|
m.Name = name
|
||||||
|
}
|
||||||
|
switch v := pi.Bin.(type) {
|
||||||
|
case string:
|
||||||
|
if v != "" {
|
||||||
|
rel := filepath.Clean(filepath.Join(m.Path, v))
|
||||||
|
n := m.Name
|
||||||
|
if n == "" {
|
||||||
|
n = strings.TrimSuffix(filepath.Base(v), filepath.Ext(v))
|
||||||
|
}
|
||||||
|
if n == "" || n == "." {
|
||||||
|
n = "bin"
|
||||||
|
}
|
||||||
|
m.Bins[n] = rel
|
||||||
|
}
|
||||||
|
case map[string]any:
|
||||||
|
for k, vv := range v {
|
||||||
|
if s, ok := vv.(string); ok && s != "" {
|
||||||
|
rel := filepath.Clean(filepath.Join(m.Path, s))
|
||||||
|
m.Bins[k] = rel
|
||||||
|
}
|
||||||
}
|
}
|
||||||
json.Unmarshal(data, &pkg)
|
|
||||||
if pkg.Bin == nil {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
for name, v := range pkg.Bin {
|
|
||||||
if s, ok := v.(string); ok && s != "" {
|
|
||||||
rel := filepath.Clean(filepath.Join(p, s))
|
|
||||||
m.Bins[name] = rel
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if m.Type == "go" {
|
} else if m.Type == "go" {
|
||||||
mainDirs := make(map[string]struct{})
|
goModPath := "go.mod"
|
||||||
for _, f := range allGoFiles {
|
if m.Path != "" {
|
||||||
if f == "" || goModuleOf[f] != p || strings.HasSuffix(f, "_test.go") {
|
goModPath = filepath.Join(m.Path, "go.mod")
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
data, _ := os.ReadFile(f)
|
if data, err := os.ReadFile(goModPath); err == nil {
|
||||||
if strings.Contains(string(data), "\npackage main\n") || strings.HasPrefix(string(data), "package main\n") {
|
for _, line := range strings.Split(string(data), "\n") {
|
||||||
d := filepath.Dir(f)
|
line = strings.TrimSpace(line)
|
||||||
if d == "." {
|
if strings.HasPrefix(line, "module ") {
|
||||||
d = ""
|
full := strings.TrimSpace(strings.TrimPrefix(line, "module "))
|
||||||
}
|
m.Name = filepath.Base(full)
|
||||||
mainDirs[d] = struct{}{}
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for d := range mainDirs {
|
|
||||||
name := repoName
|
|
||||||
if d != "" {
|
|
||||||
name = filepath.Base(d)
|
|
||||||
}
|
}
|
||||||
m.Bins[name] = d
|
if m.Name == "" {
|
||||||
|
if m.Path == "" {
|
||||||
|
m.Name = repoName
|
||||||
|
} else {
|
||||||
|
m.Name = filepath.Base(m.Path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
modMap[p] = m
|
|
||||||
}
|
|
||||||
|
|
||||||
// dirty
|
mainDirs := make(map[string]bool)
|
||||||
statusLines, _ := runGit("status", "--porcelain", ".")
|
for _, gf := range allGoFiles {
|
||||||
porLines := strings.Split(statusLines, "\n")
|
if goToModule[gf] != m.Path {
|
||||||
dirty := make(map[string]bool)
|
continue
|
||||||
modPrefixes := make([]string, 0, len(modMap))
|
}
|
||||||
for p := range modMap {
|
if strings.HasSuffix(gf, "_test.go") {
|
||||||
if p != "" {
|
continue
|
||||||
modPrefixes = append(modPrefixes, p+"/")
|
}
|
||||||
|
data, err := os.ReadFile(gf)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hasMain := false
|
||||||
|
for _, line := range strings.Split(string(data), "\n") {
|
||||||
|
if strings.TrimSpace(line) == "package main" {
|
||||||
|
hasMain = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasMain {
|
||||||
|
dir := filepath.Dir(gf)
|
||||||
|
if dir == "." {
|
||||||
|
dir = ""
|
||||||
|
}
|
||||||
|
mainDirs[dir] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for dir := range mainDirs {
|
||||||
|
var name, fullP string
|
||||||
|
if dir == "" || dir == "." {
|
||||||
|
name = repoName
|
||||||
|
fullP = "."
|
||||||
|
} else {
|
||||||
|
name = filepath.Base(dir)
|
||||||
|
fullP = dir
|
||||||
|
}
|
||||||
|
m.Bins[name] = fullP
|
||||||
|
if (dir == "" && m.Path == "") || dir == m.Path {
|
||||||
|
m.HasRootMain = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m.Name == "" {
|
||||||
|
if m.Path == "" {
|
||||||
|
m.Name = repoName
|
||||||
|
} else {
|
||||||
|
m.Name = filepath.Base(m.Path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dirtyReasons := make(map[string]map[rune]bool)
|
||||||
|
por, _ := runGit("status", "--porcelain", ".")
|
||||||
|
for _, line := range strings.Split(por, "\n") {
|
||||||
|
if len(line) < 3 || line[2] != ' ' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
status := line[0:2]
|
||||||
|
fileP := line[3:]
|
||||||
|
|
||||||
|
types := []rune{}
|
||||||
|
if status == "??" {
|
||||||
|
types = append(types, 'u')
|
||||||
|
}
|
||||||
|
if strings.Contains(status, "A") {
|
||||||
|
types = append(types, 'n')
|
||||||
|
}
|
||||||
|
if strings.Contains(status, "M") || strings.Contains(status, "R") || strings.Contains(status, "C") {
|
||||||
|
types = append(types, 'm')
|
||||||
|
}
|
||||||
|
if strings.Contains(status, "D") {
|
||||||
|
types = append(types, 'd')
|
||||||
|
}
|
||||||
|
|
||||||
|
thisDirty := false
|
||||||
|
for _, t := range types {
|
||||||
|
if !ignoreSet[t] {
|
||||||
|
thisDirty = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !thisDirty {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
matched := false
|
||||||
|
modPrefixes := make([]string, 0, len(modules))
|
||||||
|
for _, m := range modules {
|
||||||
|
if m.Path != "" {
|
||||||
|
modPrefixes = append(modPrefixes, m.Path+"/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sort.Slice(modPrefixes, func(i, j int) bool { return len(modPrefixes[i]) > len(modPrefixes[j]) })
|
sort.Slice(modPrefixes, func(i, j int) bool { return len(modPrefixes[i]) > len(modPrefixes[j]) })
|
||||||
|
|
||||||
for _, line := range porLines {
|
|
||||||
if len(line) < 4 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
st := line[:2]
|
|
||||||
file := line[3:]
|
|
||||||
if st == " " || st == "!!" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
states := []rune{}
|
|
||||||
if st == "??" {
|
|
||||||
states = append(states, 'u')
|
|
||||||
}
|
|
||||||
if strings.Contains(st, "A") || strings.Contains(st, "?") {
|
|
||||||
states = append(states, 'n')
|
|
||||||
}
|
|
||||||
if strings.Contains(st, "M") || strings.Contains(st, "R") || strings.Contains(st, "C") {
|
|
||||||
states = append(states, 'm')
|
|
||||||
}
|
|
||||||
if strings.Contains(st, "D") {
|
|
||||||
states = append(states, 'd')
|
|
||||||
}
|
|
||||||
|
|
||||||
isDirty := false
|
|
||||||
for _, r := range states {
|
|
||||||
if !ignoreSet[r] {
|
|
||||||
isDirty = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !isDirty {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
found := false
|
|
||||||
for _, pre := range modPrefixes {
|
for _, pre := range modPrefixes {
|
||||||
if strings.HasPrefix(file, pre) {
|
if strings.HasPrefix(fileP, pre) {
|
||||||
dirty[strings.TrimSuffix(pre, "/")] = true
|
modP := strings.TrimSuffix(pre, "/")
|
||||||
found = true
|
if dirtyReasons[modP] == nil {
|
||||||
|
dirtyReasons[modP] = make(map[rune]bool)
|
||||||
|
}
|
||||||
|
for _, t := range types {
|
||||||
|
dirtyReasons[modP][t] = true
|
||||||
|
}
|
||||||
|
matched = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !found {
|
if !matched {
|
||||||
dirty[""] = true
|
if dirtyReasons[""] == nil {
|
||||||
|
dirtyReasons[""] = make(map[rune]bool)
|
||||||
|
}
|
||||||
|
for _, t := range types {
|
||||||
|
dirtyReasons[""][t] = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tags
|
tagsS, _ := runGit("tag", "--list", "--sort=-version:refname")
|
||||||
tagLines, _ := runGit("tag", "--list", "--sort=-version:refname")
|
tags := strings.Split(tagsS, "\n")
|
||||||
tags := strings.Split(tagLines, "\n")
|
modLatest := make(map[string]string)
|
||||||
latestTag := make(map[string]string)
|
|
||||||
for _, t := range tags {
|
for _, t := range tags {
|
||||||
if t == "" {
|
if t == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for p := range modMap {
|
matched := false
|
||||||
if p == "" {
|
for _, m := range modules {
|
||||||
|
match := false
|
||||||
|
if m.Path == "" {
|
||||||
if !strings.Contains(t, "/") && isVersion(t) {
|
if !strings.Contains(t, "/") && isVersion(t) {
|
||||||
if _, ok := latestTag[p]; !ok {
|
match = true
|
||||||
latestTag[p] = t
|
|
||||||
}
|
}
|
||||||
break
|
} else if strings.HasPrefix(t, m.Path+"/") {
|
||||||
|
suf := t[len(m.Path)+1:]
|
||||||
|
if isVersion(suf) {
|
||||||
|
match = true
|
||||||
}
|
}
|
||||||
} else if strings.HasPrefix(t, p+"/") && isVersion(strings.TrimPrefix(t, p+"/")) {
|
|
||||||
if _, ok := latestTag[p]; !ok {
|
|
||||||
latestTag[p] = t
|
|
||||||
}
|
}
|
||||||
|
if match {
|
||||||
|
if _, ok := modLatest[m.Path]; !ok {
|
||||||
|
modLatest[m.Path] = t
|
||||||
|
}
|
||||||
|
matched = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !matched && *verbose {
|
||||||
|
fmt.Printf("unmatched tag: %s\n", t)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// rows
|
rows := []Row{}
|
||||||
var rows []Row
|
for _, m := range modules {
|
||||||
for p, m := range modMap {
|
latest := modLatest[m.Path]
|
||||||
tag := latestTag[p]
|
tagStr := "-"
|
||||||
suf := tag
|
if latest != "" {
|
||||||
if p != "" {
|
tagStr = latest
|
||||||
suf = strings.TrimPrefix(tag, p+"/")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pArg := "."
|
||||||
|
if m.Path != "" {
|
||||||
|
pArg = "./" + m.Path
|
||||||
|
}
|
||||||
|
var commits int
|
||||||
ver := "v0.0.0"
|
ver := "v0.0.0"
|
||||||
if tag != "" {
|
if latest != "" {
|
||||||
|
suf := latest
|
||||||
|
if m.Path != "" {
|
||||||
|
suf = strings.TrimPrefix(latest, m.Path+"/")
|
||||||
|
}
|
||||||
ver = suf
|
ver = suf
|
||||||
}
|
cS, _ := runGit("rev-list", "--count", latest+"..", "--", pArg)
|
||||||
commits := 0
|
commits, _ = strconv.Atoi(cS)
|
||||||
if tag != "" {
|
|
||||||
scope := "."
|
|
||||||
if p != "" {
|
|
||||||
scope = "./" + p
|
|
||||||
}
|
|
||||||
n, _ := runGit("rev-list", "--count", tag+"..", "--", scope)
|
|
||||||
c, _ := strconv.Atoi(n)
|
|
||||||
commits = c
|
|
||||||
if commits > 0 {
|
if commits > 0 {
|
||||||
ver += fmt.Sprintf("-%d", commits)
|
ver += fmt.Sprintf("-%d", commits)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ver += "-1"
|
cS, _ := runGit("rev-list", "--count", "--", pArg)
|
||||||
}
|
commits, _ = strconv.Atoi(cS)
|
||||||
if dirty[p] {
|
|
||||||
ver += "-dirty"
|
|
||||||
}
|
|
||||||
|
|
||||||
pathShow := "."
|
|
||||||
if p != "" {
|
|
||||||
pathShow = "./" + p
|
|
||||||
}
|
|
||||||
|
|
||||||
statusParts := []string{}
|
|
||||||
if m.Untracked {
|
|
||||||
statusParts = append(statusParts, "untracked")
|
|
||||||
}
|
|
||||||
if dirty[p] {
|
|
||||||
var ds []string
|
|
||||||
s, _ := runGit("status", "--porcelain", pathShow)
|
|
||||||
if strings.Contains(s, " M") {
|
|
||||||
ds = append(ds, "m")
|
|
||||||
}
|
|
||||||
s, _ = runGit("status", "--porcelain", pathShow)
|
|
||||||
if strings.Contains(s, "??") {
|
|
||||||
ds = append(ds, "u")
|
|
||||||
}
|
|
||||||
// simplified; can expand later
|
|
||||||
if len(ds) > 0 {
|
|
||||||
statusParts = append(statusParts, fmt.Sprintf("dirty (%s)", strings.Join(ds, "")))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if commits > 0 {
|
if commits > 0 {
|
||||||
statusParts = append(statusParts, "new commits")
|
ver = fmt.Sprintf("v0.0.0-%d", commits)
|
||||||
}
|
}
|
||||||
if len(statusParts) == 0 {
|
|
||||||
statusParts = append(statusParts, "current")
|
|
||||||
}
|
}
|
||||||
status := strings.Join(statusParts, ", ")
|
|
||||||
|
|
||||||
// module row
|
dirtyStr := ""
|
||||||
|
if dr, ok := dirtyReasons[m.Path]; ok && len(dr) > 0 {
|
||||||
|
order := []rune{'u', 'n', 'm', 'd'}
|
||||||
|
for _, c := range order {
|
||||||
|
if dr[c] {
|
||||||
|
dirtyStr += string(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status := "current"
|
||||||
|
if m.Untracked {
|
||||||
|
status = "untracked"
|
||||||
|
} else if dirtyStr != "" {
|
||||||
|
status = "dirty (" + dirtyStr + ")"
|
||||||
|
} else if commits > 0 {
|
||||||
|
status = "new commits"
|
||||||
|
}
|
||||||
|
|
||||||
|
showModRow := true
|
||||||
|
if (m.Type == "go" && m.HasRootMain) || (m.Type == "node" && len(m.Bins) > 0) {
|
||||||
|
showModRow = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if showModRow {
|
||||||
rows = append(rows, Row{
|
rows = append(rows, Row{
|
||||||
Status: status,
|
Status: status,
|
||||||
Type: "mod",
|
Typ: "mod",
|
||||||
Name: filepath.Base(pathShow),
|
Name: m.Name,
|
||||||
Version: ver,
|
Version: ver,
|
||||||
CurrentTag: tag,
|
Tag: tagStr,
|
||||||
Path: filepath.Join(pathShow, m.Type+".mod"),
|
Path: manifestPath(m),
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// bin rows
|
if len(m.Bins) > 0 {
|
||||||
binNames := make([]string, 0, len(m.Bins))
|
names := make([]string, 0, len(m.Bins))
|
||||||
for n := range m.Bins {
|
for n := range m.Bins {
|
||||||
binNames = append(binNames, n)
|
names = append(names, n)
|
||||||
}
|
}
|
||||||
sort.Strings(binNames)
|
sort.Strings(names)
|
||||||
|
for _, n := range names {
|
||||||
for _, name := range binNames {
|
binLoc := m.Bins[n]
|
||||||
binPath := m.Bins[name]
|
var binShow string
|
||||||
binShow := "."
|
if m.Type == "go" {
|
||||||
if binPath != "" {
|
if binLoc == "" || binLoc == "." {
|
||||||
binShow = "./" + binPath
|
binLoc = m.Path
|
||||||
|
if binLoc == "" {
|
||||||
|
binLoc = "."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if binLoc == "." {
|
||||||
|
binShow = "."
|
||||||
|
} else {
|
||||||
|
binShow = binLoc + "/"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binShow = binLoc
|
||||||
}
|
}
|
||||||
rows = append(rows, Row{
|
rows = append(rows, Row{
|
||||||
Status: status,
|
Status: status,
|
||||||
Type: "bin",
|
Typ: "bin",
|
||||||
Name: name,
|
Name: n,
|
||||||
Version: ver,
|
Version: ver,
|
||||||
CurrentTag: tag,
|
Tag: tagStr,
|
||||||
Path: binShow + "/",
|
Path: "./" + binShow,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// output
|
if *useCSV {
|
||||||
if *csvOut {
|
c := ','
|
||||||
|
if *csvComma != "" && len(*csvComma) > 0 {
|
||||||
|
c = rune((*csvComma)[0])
|
||||||
|
}
|
||||||
w := csv.NewWriter(os.Stdout)
|
w := csv.NewWriter(os.Stdout)
|
||||||
w.Comma = rune((*comma)[0])
|
w.Comma = c
|
||||||
w.Write([]string{"status", "type", "name", "version", "current tag", "path"})
|
_ = w.Write([]string{"status", "type", "name", "version", "current tag", "path"})
|
||||||
for _, r := range rows {
|
for _, r := range rows {
|
||||||
w.Write([]string{r.Status, r.Type, r.Name, r.Version, r.CurrentTag, r.Path})
|
_ = w.Write([]string{r.Status, r.Typ, r.Name, r.Version, r.Tag, r.Path})
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
return
|
} else {
|
||||||
|
headers := []string{"status", "type", "name", "version", "current tag", "path"}
|
||||||
|
colWidths := make([]int, len(headers))
|
||||||
|
for i, h := range headers {
|
||||||
|
colWidths[i] = len(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
// aligned table
|
|
||||||
var max [6]int
|
|
||||||
for _, r := range rows {
|
for _, r := range rows {
|
||||||
l := []string{r.Status, r.Type, r.Name, r.Version, r.CurrentTag, r.Path}
|
if len(r.Status) > colWidths[0] {
|
||||||
for i, s := range l {
|
colWidths[0] = len(r.Status)
|
||||||
if len(s) > max[i] {
|
}
|
||||||
max[i] = len(s)
|
if len(r.Typ) > colWidths[1] {
|
||||||
|
colWidths[1] = len(r.Typ)
|
||||||
|
}
|
||||||
|
if len(r.Name) > colWidths[2] {
|
||||||
|
colWidths[2] = len(r.Name)
|
||||||
|
}
|
||||||
|
if len(r.Version) > colWidths[3] {
|
||||||
|
colWidths[3] = len(r.Version)
|
||||||
|
}
|
||||||
|
if len(r.Tag) > colWidths[4] {
|
||||||
|
colWidths[4] = len(r.Tag)
|
||||||
|
}
|
||||||
|
if len(r.Path) > colWidths[5] {
|
||||||
|
colWidths[5] = len(r.Path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fmt.Print("|")
|
||||||
|
for i, h := range headers {
|
||||||
|
fmt.Printf(" %-*s |", colWidths[i], h)
|
||||||
}
|
}
|
||||||
|
fmt.Println()
|
||||||
fmt.Printf("%-*s | %-*s | %-*s | %-*s | %-*s | %s\n",
|
fmt.Print("|")
|
||||||
max[0], "status",
|
for _, w := range colWidths {
|
||||||
max[1], "type",
|
fmt.Printf(" %s |", strings.Repeat("-", w))
|
||||||
max[2], "name",
|
}
|
||||||
max[3], "version",
|
fmt.Println()
|
||||||
max[4], "current tag",
|
|
||||||
"path",
|
|
||||||
)
|
|
||||||
fmt.Printf("%s-+-%s-+-%s-+-%s-+-%s-+-%s\n",
|
|
||||||
strings.Repeat("-", max[0]),
|
|
||||||
strings.Repeat("-", max[1]),
|
|
||||||
strings.Repeat("-", max[2]),
|
|
||||||
strings.Repeat("-", max[3]),
|
|
||||||
strings.Repeat("-", max[4]),
|
|
||||||
strings.Repeat("-", max[5]),
|
|
||||||
)
|
|
||||||
|
|
||||||
for _, r := range rows {
|
for _, r := range rows {
|
||||||
fmt.Printf("%-*s | %-*s | %-*s | %-*s | %-*s | %s\n",
|
fmt.Print("|")
|
||||||
max[0], r.Status,
|
fmt.Printf(" %-*s |", colWidths[0], r.Status)
|
||||||
max[1], r.Type,
|
fmt.Printf(" %-*s |", colWidths[1], r.Typ)
|
||||||
max[2], r.Name,
|
fmt.Printf(" %-*s |", colWidths[2], r.Name)
|
||||||
max[3], r.Version,
|
fmt.Printf(" %-*s |", colWidths[3], r.Version)
|
||||||
max[4], r.CurrentTag,
|
fmt.Printf(" %-*s |", colWidths[4], r.Tag)
|
||||||
r.Path,
|
fmt.Printf(" %-*s |", colWidths[5], r.Path)
|
||||||
)
|
fmt.Println()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user