ref(cmd/monorelease): fix tag and version output, start flagset

This commit is contained in:
AJ ONeal 2026-02-28 22:55:16 -07:00
parent ac6a039698
commit 4146c62f37
No known key found for this signature in database
2 changed files with 161 additions and 102 deletions

View File

@ -12,7 +12,6 @@ package main
import ( import (
"bytes" "bytes"
"encoding/csv"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
@ -28,11 +27,6 @@ import (
"sync" "sync"
) )
// var verbose = flag.Bool("verbose", false, "")
var ignoreDirty = flag.String("ignore-dirty", "", "ignore dirty states [u n m d]")
var useCSV = flag.Bool("csv", false, "output CSV instead of table")
var csvComma = flag.String("comma", ",", "CSV field separator")
func runGit(args ...string) (string, error) { func runGit(args ...string) (string, error) {
cmd := exec.Command("git", args...) cmd := exec.Command("git", args...)
var b bytes.Buffer var b bytes.Buffer
@ -41,6 +35,18 @@ func runGit(args ...string) (string, error) {
return strings.TrimSpace(b.String()), err return strings.TrimSpace(b.String()), err
} }
func RunGoFrom(chdir string, args ...string) (string, error) {
cmd := exec.Command("go", args...)
if chdir != "" {
cmd.Dir = chdir
}
var b bytes.Buffer
cmd.Stdout = &b
err := cmd.Run()
return strings.TrimSpace(b.String()), err
}
func isVersion(s string) bool { func isVersion(s string) bool {
re := regexp.MustCompile(`^v?\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$`) re := regexp.MustCompile(`^v?\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$`)
return re.MatchString(s) return re.MatchString(s)
@ -92,7 +98,8 @@ type Releaser struct {
} }
type GoModule struct { type GoModule struct {
Path string PackageName string
Path string
} }
type NodePackage struct { type NodePackage struct {
@ -197,16 +204,24 @@ func (r *Releaser) LatestTag(modPath string) string {
func getDirtyTypes(xy string) []rune { func getDirtyTypes(xy string) []rune {
types := []rune{} types := []rune{}
if xy == "??" { if xy == "??" {
types = append(types, 'u') types = append(types, '?')
return types
} }
if strings.Contains(xy, "A") { if strings.Contains(xy, "A") {
types = append(types, 'n') types = append(types, 'A')
} }
if strings.ContainsAny(xy, "MRC") { if strings.ContainsAny(xy, "M") {
types = append(types, 'm') types = append(types, 'M')
}
if strings.ContainsAny(xy, "R") {
types = append(types, 'R')
}
if strings.ContainsAny(xy, "C") {
types = append(types, 'C')
} }
if strings.Contains(xy, "D") { if strings.Contains(xy, "D") {
types = append(types, 'd') types = append(types, 'D')
} }
return types return types
} }
@ -273,7 +288,7 @@ func getVersionStatus(r *Releaser, modPath, manifestRel string) (ver string, com
} }
dirtyMap := r.DirtyStates(modPath) dirtyMap := r.DirtyStates(modPath)
dirtyStr := "" dirtyStr := ""
for _, c := range []rune{'u', 'n', 'm', 'd'} { for _, c := range []rune{'?', 'A', 'M', 'R', 'C', 'D'} {
if dirtyMap[c] && !r.Ignore[c] { if dirtyMap[c] && !r.Ignore[c] {
dirtyStr += string(c) dirtyStr += string(c)
} }
@ -288,6 +303,9 @@ func getVersionStatus(r *Releaser, modPath, manifestRel string) (ver string, com
if untrackedMod { if untrackedMod {
status = "-" // untracked status = "-" // untracked
ver = "-" ver = "-"
if tagStr == "-" {
tagStr = ""
}
} else if dirtyStr != "" { } else if dirtyStr != "" {
if ver == "" { if ver == "" {
// ver = "v0.0.0" // ver = "v0.0.0"
@ -302,7 +320,15 @@ func getVersionStatus(r *Releaser, modPath, manifestRel string) (ver string, com
} }
func (r *Releaser) DiscoverGoModules() []GoModule { func (r *Releaser) DiscoverGoModules() []GoModule {
seen := make(map[string]bool) modCh := make(chan GoModule, 10)
var wg sync.WaitGroup
var mods []GoModule
go func() {
for mod := range modCh {
mods = append(mods, mod)
}
}()
for _, f := range append(r.Committed, r.Untracked...) { for _, f := range append(r.Committed, r.Untracked...) {
if f == "" { if f == "" {
continue continue
@ -312,13 +338,17 @@ func (r *Releaser) DiscoverGoModules() []GoModule {
if p == "." { if p == "." {
p = "" p = ""
} }
seen[p] = true mods = append(mods, GoModule{PackageName: "", Path: p})
// TODO for when we need the real package name
wg.Go(func() {
// pkg, _ := RunGoFrom(p, "list", "-f", "{{.Name}}", ".")
// modCh <- GoModule{PackageName: pkg, Path: p}
})
} }
} }
var mods []GoModule wg.Wait()
for p := range seen { close(modCh)
mods = append(mods, GoModule{Path: p})
}
sort.Slice(mods, func(i, j int) bool { return mods[i].Path < mods[j].Path }) sort.Slice(mods, func(i, j int) bool { return mods[i].Path < mods[j].Path })
r.GoModulePrefixes = make([]string, 0, len(mods)) r.GoModulePrefixes = make([]string, 0, len(mods))
@ -381,10 +411,9 @@ func (m GoModule) Process(r *Releaser) []Releasable {
name := "" name := ""
goModFile := manifestRel goModFile := manifestRel
if data, err := os.ReadFile(goModFile); err == nil { if data, err := os.ReadFile(goModFile); err == nil {
for _, line := range strings.Split(string(data), "\n") { for line := range strings.SplitSeq(string(data), "\n") {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if strings.HasPrefix(line, "module ") { if full, ok := strings.CutPrefix(line, "module "); ok {
full := strings.TrimSpace(strings.TrimPrefix(line, "module "))
name = filepath.Base(full) name = filepath.Base(full)
break break
} }
@ -582,14 +611,33 @@ func (p NodePackage) Process(r *Releaser) []Releasable {
return rows return rows
} }
func main() { type MainConfig struct {
flag.Parse() ignoreDirty string
useCSV bool
csvComma string
rows []Releasable
}
func main() {
cli := &MainConfig{}
fs := flag.NewFlagSet("rerelease status", flag.ExitOnError)
// var verbose = flag.Bool("verbose", false, "")
fs.StringVar(&cli.ignoreDirty, "ignore-dirty", "", "ignore dirty states [? A M R C D]")
fs.BoolVar(&cli.useCSV, "csv", false, "output CSV instead of table")
fs.StringVar(&cli.csvComma, "comma", ",", "CSV field separator")
_ = fs.Parse(os.Args[1:])
cli.init()
cli.status()
}
func (cli *MainConfig) init() {
r := &Releaser{} r := &Releaser{}
r.Init() r.Init()
r.Ignore = make(map[rune]bool) r.Ignore = make(map[rune]bool)
for _, c := range *ignoreDirty { for _, c := range cli.ignoreDirty {
r.Ignore[c] = true r.Ignore[c] = true
} }
@ -615,85 +663,11 @@ func main() {
return len(r.ModulePrefixes[i]) > len(r.ModulePrefixes[j]) return len(r.ModulePrefixes[i]) > len(r.ModulePrefixes[j])
}) })
rows := []Releasable{} cli.rows = []Releasable{}
for _, m := range goMods { for _, m := range goMods {
rows = append(rows, m.Process(r)...) cli.rows = append(cli.rows, m.Process(r)...)
} }
for _, p := range nodePkgs { for _, p := range nodePkgs {
rows = append(rows, p.Process(r)...) cli.rows = append(cli.rows, p.Process(r)...)
}
if *useCSV {
c := ','
if len(*csvComma) > 0 {
c = rune((*csvComma)[0])
}
w := csv.NewWriter(os.Stdout)
w.Comma = c
_ = w.Write([]string{"type", "name", "next_version", "current_tag", "status"})
for _, rr := range rows {
_ = w.Write([]string{rr.releasable, rr.Status, rr.Version, rr.CurrentTag, rr.Path})
}
w.Flush()
} else {
headers := []string{ /*"t",*/ "name", "next_version", "current_tag", "status"}
colWidths := make([]int, len(headers))
for i, h := range headers {
colWidths[i] = len(h)
}
// var typeIdx = 0
var nameIdx = 0
var versionIdx = 1
var tagIdx = 2
var statusIdx = 3
for _, rr := range rows {
// colWidths[typeIdx] = 0
// if len(rr.Type) > colWidths[typeIdx] {
// colWidths[typeIdx] = len(rr.Type)
// }
if len(rr.releasable) > colWidths[nameIdx] {
colWidths[nameIdx] = len(rr.releasable)
}
if len(rr.Version) > colWidths[versionIdx] {
colWidths[versionIdx] = len(rr.Version)
}
if len(rr.CurrentTag) > colWidths[tagIdx] {
colWidths[tagIdx] = len(rr.CurrentTag)
}
if len(rr.Status) > colWidths[statusIdx] {
colWidths[statusIdx] = len(rr.Status)
}
// if len(rr.Path) > colWidths[5] {
// colWidths[5] = len(rr.Path)
// }
}
sep := ""
fmt.Print(sep)
{
fmt.Printf("%-*s %s", colWidths[nameIdx], headers[nameIdx], sep)
fmt.Printf(" %-*s %s", colWidths[versionIdx], headers[versionIdx], sep)
fmt.Printf(" %-*s %s", colWidths[tagIdx], headers[tagIdx], sep)
fmt.Printf(" %-*s %s", colWidths[statusIdx], headers[statusIdx], sep)
}
fmt.Println()
fmt.Print(sep)
for i, w := range colWidths {
if i == 0 {
fmt.Printf("%s %s", strings.Repeat("-", w), sep)
continue
}
fmt.Printf(" %s %s", strings.Repeat("-", w), sep)
}
fmt.Println()
for _, rr := range rows {
fmt.Print(sep)
// fmt.Printf(" %-*s %s", colWidths[typeIdx], rr.Type, sep)
fmt.Printf("%-*s %s", colWidths[nameIdx], rr.releasable, sep)
fmt.Printf(" %-*s %s", colWidths[versionIdx], rr.Version, sep)
fmt.Printf(" %-*s %s", colWidths[tagIdx], rr.CurrentTag, sep)
fmt.Printf(" %-*s %s", colWidths[statusIdx], rr.Status, sep)
// fmt.Printf(" %-*s %s", colWidths[5], rr.Path, sep)
fmt.Println()
}
} }
} }

85
cmd/monorelease/status.go Normal file
View File

@ -0,0 +1,85 @@
package main
import (
"encoding/csv"
"fmt"
"os"
"strings"
)
func (cli *MainConfig) status() {
if cli.useCSV {
c := '\t'
if len(cli.csvComma) > 0 {
c = rune((cli.csvComma)[0])
}
w := csv.NewWriter(os.Stdout)
w.Comma = c
_ = w.Write([]string{"type", "name", "next_version", "current_tag", "status"})
for _, rr := range cli.rows {
_ = w.Write([]string{rr.releasable, rr.Status, rr.Version, rr.CurrentTag, rr.Path})
}
w.Flush()
return
}
headers := []string{ /*"t",*/ "name", "next_version", "current_tag", "status"}
colWidths := make([]int, len(headers))
for i, h := range headers {
colWidths[i] = len(h)
}
// var typeIdx = 0
var nameIdx = 0
var versionIdx = 1
var tagIdx = 2
var statusIdx = 3
for _, rr := range cli.rows {
// colWidths[typeIdx] = 0
// if len(rr.Type) > colWidths[typeIdx] {
// colWidths[typeIdx] = len(rr.Type)
// }
if len(rr.releasable) > colWidths[nameIdx] {
colWidths[nameIdx] = len(rr.releasable)
}
if len(rr.Version) > colWidths[versionIdx] {
colWidths[versionIdx] = len(rr.Version)
}
if len(rr.CurrentTag) > colWidths[tagIdx] {
colWidths[tagIdx] = len(rr.CurrentTag)
}
if len(rr.Status) > colWidths[statusIdx] {
colWidths[statusIdx] = len(rr.Status)
}
// if len(rr.Path) > colWidths[5] {
// colWidths[5] = len(rr.Path)
// }
}
sep := ""
fmt.Print(sep)
{
fmt.Printf("%-*s %s", colWidths[nameIdx], headers[nameIdx], sep)
fmt.Printf(" %-*s %s", colWidths[versionIdx], headers[versionIdx], sep)
fmt.Printf(" %-*s %s", colWidths[tagIdx], headers[tagIdx], sep)
fmt.Printf(" %-*s %s", colWidths[statusIdx], headers[statusIdx], sep)
}
fmt.Println()
fmt.Print(sep)
for i, w := range colWidths {
if i == 0 {
fmt.Printf("%s %s", strings.Repeat("-", w), sep)
continue
}
fmt.Printf(" %s %s", strings.Repeat("-", w), sep)
}
fmt.Println()
for _, rr := range cli.rows {
fmt.Print(sep)
// fmt.Printf(" %-*s %s", colWidths[typeIdx], rr.Type, sep)
fmt.Printf("%-*s %s", colWidths[nameIdx], rr.releasable, sep)
fmt.Printf(" %-*s %s", colWidths[versionIdx], rr.Version, sep)
fmt.Printf(" %-*s %s", colWidths[tagIdx], rr.CurrentTag, sep)
fmt.Printf(" %-*s %s", colWidths[statusIdx], rr.Status, sep)
// fmt.Printf(" %-*s %s", colWidths[5], rr.Path, sep)
fmt.Println()
}
}