mirror of
https://github.com/therootcompany/golib.git
synced 2026-03-02 23:57:59 +00:00
feat(monorel): -A flag, dot/underscore skip, same-commit guard, -force bump
- findMainPackages: skip dot/underscore-prefixed dirs by default; warn (not error) on ReadDir failures when -A is set - expandPaths, runRelease, runBump, runInit: thread -A flag through - bumpModuleTag: refuse to tag a commit already tagged by the previous stable release; -force creates an empty bump commit instead
This commit is contained in:
parent
0706c53de4
commit
f39d35d57c
@ -102,8 +102,9 @@ func usage() {
|
|||||||
|
|
||||||
func runRelease(args []string) {
|
func runRelease(args []string) {
|
||||||
fs := flag.NewFlagSet("monorel release", flag.ExitOnError)
|
fs := flag.NewFlagSet("monorel release", flag.ExitOnError)
|
||||||
var recursive bool
|
var recursive, all bool
|
||||||
fs.BoolVar(&recursive, "recursive", false, "find all main packages recursively under each path")
|
fs.BoolVar(&recursive, "recursive", false, "find all main packages recursively under each path")
|
||||||
|
fs.BoolVar(&all, "A", false, "include dot/underscore-prefixed directories; warn rather than error on failures")
|
||||||
fs.Usage = func() {
|
fs.Usage = func() {
|
||||||
fmt.Fprintln(os.Stderr, "usage: monorel release [options] <binary-path>...")
|
fmt.Fprintln(os.Stderr, "usage: monorel release [options] <binary-path>...")
|
||||||
fmt.Fprintln(os.Stderr, "")
|
fmt.Fprintln(os.Stderr, "")
|
||||||
@ -125,7 +126,7 @@ func runRelease(args []string) {
|
|||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
allPaths, err := expandPaths(binPaths, recursive)
|
allPaths, err := expandPaths(binPaths, recursive, all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf("%v", err)
|
fatalf("%v", err)
|
||||||
}
|
}
|
||||||
@ -156,9 +157,11 @@ func runRelease(args []string) {
|
|||||||
func runBump(args []string) {
|
func runBump(args []string) {
|
||||||
fs := flag.NewFlagSet("monorel bump", flag.ExitOnError)
|
fs := flag.NewFlagSet("monorel bump", flag.ExitOnError)
|
||||||
var component string
|
var component string
|
||||||
var recursive bool
|
var recursive, all, force bool
|
||||||
fs.StringVar(&component, "r", "patch", "version component to bump: major, minor, or patch")
|
fs.StringVar(&component, "r", "patch", "version component to bump: major, minor, or patch")
|
||||||
fs.BoolVar(&recursive, "recursive", false, "find all main packages recursively under each path")
|
fs.BoolVar(&recursive, "recursive", false, "find all main packages recursively under each path")
|
||||||
|
fs.BoolVar(&all, "A", false, "include dot/underscore-prefixed directories; warn rather than error on failures")
|
||||||
|
fs.BoolVar(&force, "force", false, "if no new commits, create an empty bump commit and tag it")
|
||||||
fs.Usage = func() {
|
fs.Usage = func() {
|
||||||
fmt.Fprintln(os.Stderr, "usage: monorel bump [options] <binary-path>...")
|
fmt.Fprintln(os.Stderr, "usage: monorel bump [options] <binary-path>...")
|
||||||
fmt.Fprintln(os.Stderr, "")
|
fmt.Fprintln(os.Stderr, "")
|
||||||
@ -170,6 +173,7 @@ func runBump(args []string) {
|
|||||||
fmt.Fprintln(os.Stderr, " monorel bump -r minor ./cmd/csvauth # bump minor")
|
fmt.Fprintln(os.Stderr, " monorel bump -r minor ./cmd/csvauth # bump minor")
|
||||||
fmt.Fprintln(os.Stderr, " monorel bump -r major ./cmd/csvauth # bump major")
|
fmt.Fprintln(os.Stderr, " monorel bump -r major ./cmd/csvauth # bump major")
|
||||||
fmt.Fprintln(os.Stderr, " monorel bump -recursive . # bump patch for all modules")
|
fmt.Fprintln(os.Stderr, " monorel bump -recursive . # bump patch for all modules")
|
||||||
|
fmt.Fprintln(os.Stderr, " monorel bump -force ./cmd/csvauth # bump even with no new commits")
|
||||||
fmt.Fprintln(os.Stderr, "")
|
fmt.Fprintln(os.Stderr, "")
|
||||||
fs.PrintDefaults()
|
fs.PrintDefaults()
|
||||||
}
|
}
|
||||||
@ -189,7 +193,7 @@ func runBump(args []string) {
|
|||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
allPaths, err := expandPaths(binPaths, recursive)
|
allPaths, err := expandPaths(binPaths, recursive, all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf("%v", err)
|
fatalf("%v", err)
|
||||||
}
|
}
|
||||||
@ -201,7 +205,7 @@ func runBump(args []string) {
|
|||||||
fatalf("%v", err)
|
fatalf("%v", err)
|
||||||
}
|
}
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
newTag := bumpModuleTag(group, component)
|
newTag := bumpModuleTag(group, component, force)
|
||||||
fmt.Fprintf(os.Stderr, "created tag: %s\n", newTag)
|
fmt.Fprintf(os.Stderr, "created tag: %s\n", newTag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,8 +214,9 @@ func runBump(args []string) {
|
|||||||
|
|
||||||
func runInit(args []string) {
|
func runInit(args []string) {
|
||||||
fs := flag.NewFlagSet("monorel init", flag.ExitOnError)
|
fs := flag.NewFlagSet("monorel init", flag.ExitOnError)
|
||||||
var recursive bool
|
var recursive, all bool
|
||||||
fs.BoolVar(&recursive, "recursive", false, "find all main packages recursively under each path")
|
fs.BoolVar(&recursive, "recursive", false, "find all main packages recursively under each path")
|
||||||
|
fs.BoolVar(&all, "A", false, "include dot/underscore-prefixed directories; warn rather than error on failures")
|
||||||
fs.Usage = func() {
|
fs.Usage = func() {
|
||||||
fmt.Fprintln(os.Stderr, "usage: monorel init [options] <binary-path>...")
|
fmt.Fprintln(os.Stderr, "usage: monorel init [options] <binary-path>...")
|
||||||
fmt.Fprintln(os.Stderr, "")
|
fmt.Fprintln(os.Stderr, "")
|
||||||
@ -233,7 +238,7 @@ func runInit(args []string) {
|
|||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
allPaths, err := expandPaths(binPaths, recursive)
|
allPaths, err := expandPaths(binPaths, recursive, all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatalf("%v", err)
|
fatalf("%v", err)
|
||||||
}
|
}
|
||||||
@ -282,7 +287,7 @@ func initModuleGroup(group *moduleGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. Bump patch.
|
// 3. Bump patch.
|
||||||
newTag := bumpModuleTag(group, "patch")
|
newTag := bumpModuleTag(group, "patch", false)
|
||||||
fmt.Fprintf(os.Stderr, "created tag: %s\n", newTag)
|
fmt.Fprintf(os.Stderr, "created tag: %s\n", newTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,8 +295,13 @@ func initModuleGroup(group *moduleGroup) {
|
|||||||
|
|
||||||
// bumpModuleTag finds the latest stable tag for the module, computes the next
|
// bumpModuleTag finds the latest stable tag for the module, computes the next
|
||||||
// version by bumping the given component (major, minor, or patch), creates the
|
// version by bumping the given component (major, minor, or patch), creates the
|
||||||
// git tag at HEAD, and returns the new tag name.
|
// git tag at the module's latest commit, and returns the new tag name.
|
||||||
func bumpModuleTag(group *moduleGroup, component string) string {
|
//
|
||||||
|
// If the module's latest commit is the same as the one already tagged by the
|
||||||
|
// previous stable tag, bumpModuleTag refuses to create a duplicate tag — unless
|
||||||
|
// force is true, in which case it creates an empty commit
|
||||||
|
// ("chore(release): bump to <version>") and tags that instead.
|
||||||
|
func bumpModuleTag(group *moduleGroup, component string, force bool) string {
|
||||||
modRoot := group.root
|
modRoot := group.root
|
||||||
|
|
||||||
prefix := mustRunIn(modRoot, "git", "rev-parse", "--show-prefix")
|
prefix := mustRunIn(modRoot, "git", "rev-parse", "--show-prefix")
|
||||||
@ -325,12 +335,31 @@ func bumpModuleTag(group *moduleGroup, component string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
newTag := computeBumpTag(prefix, latestStable, component)
|
newTag := computeBumpTag(prefix, latestStable, component)
|
||||||
|
newVersion := strings.TrimPrefix(newTag, prefix+"/")
|
||||||
|
|
||||||
// Tag the most recent commit that touched this module's directory, which
|
// Tag the most recent commit that touched this module's directory, which
|
||||||
// may be behind HEAD if other modules have been updated more recently.
|
// may be behind HEAD if other modules have been updated more recently.
|
||||||
commitSHA := mustRunIn(modRoot, "git", "log", "--format=%H", "-1", "--", ".")
|
commitSHA := mustRunIn(modRoot, "git", "log", "--format=%H", "-1", "--", ".")
|
||||||
if commitSHA == "" {
|
if commitSHA == "" {
|
||||||
fatalf("no commits found in %s", modRoot)
|
fatalf("no commits found in %s", modRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Guard: refuse to tag the same commit twice.
|
||||||
|
if latestStable != "" {
|
||||||
|
prevCommit := mustRunIn(modRoot, "git", "rev-list", "-n", "1", latestStable)
|
||||||
|
if prevCommit == commitSHA {
|
||||||
|
if !force {
|
||||||
|
fatalf("no new commits in %s since %s; nothing to tag\n"+
|
||||||
|
" (use -force to create an empty bump commit)", prefix, latestStable)
|
||||||
|
}
|
||||||
|
// Create an empty commit so we have something new to tag.
|
||||||
|
commitMsg := "chore(release): bump to " + newVersion
|
||||||
|
mustRunIn(modRoot, "git", "commit", "--allow-empty", "-m", commitMsg)
|
||||||
|
fmt.Fprintf(os.Stderr, "created empty commit: %s\n", commitMsg)
|
||||||
|
commitSHA = mustRunIn(modRoot, "git", "rev-parse", "HEAD")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mustRunIn(modRoot, "git", "tag", newTag, commitSHA)
|
mustRunIn(modRoot, "git", "tag", newTag, commitSHA)
|
||||||
return newTag
|
return newTag
|
||||||
}
|
}
|
||||||
@ -373,26 +402,33 @@ func computeBumpTag(prefix, latestStableTag, component string) string {
|
|||||||
|
|
||||||
// expandPaths returns paths unchanged when recursive is false. When true, it
|
// expandPaths returns paths unchanged when recursive is false. When true, it
|
||||||
// replaces each path with all main-package directories found beneath it.
|
// replaces each path with all main-package directories found beneath it.
|
||||||
func expandPaths(paths []string, recursive bool) ([]string, error) {
|
// all mirrors the -A flag: include dot/underscore-prefixed directories and
|
||||||
|
// warn on errors instead of failing.
|
||||||
|
func expandPaths(paths []string, recursive, all bool) ([]string, error) {
|
||||||
if !recursive {
|
if !recursive {
|
||||||
return paths, nil
|
return paths, nil
|
||||||
}
|
}
|
||||||
var all []string
|
var result []string
|
||||||
for _, p := range paths {
|
for _, p := range paths {
|
||||||
found, err := findMainPackages(p)
|
found, err := findMainPackages(p, all)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("searching %s: %w", p, err)
|
return nil, fmt.Errorf("searching %s: %w", p, err)
|
||||||
}
|
}
|
||||||
all = append(all, found...)
|
result = append(result, found...)
|
||||||
}
|
}
|
||||||
return all, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// findMainPackages recursively walks root and returns the absolute path of
|
// findMainPackages recursively walks root and returns the absolute path of
|
||||||
// every directory that contains a Go main package. It stops descending into
|
// every directory that contains a Go main package. It stops descending into
|
||||||
// any directory listed in stopMarkers (e.g. .git directories), preventing
|
// any directory listed in stopMarkers (e.g. .git directories), preventing
|
||||||
// the walk from crossing into a parent repository.
|
// the walk from crossing into a parent repository.
|
||||||
func findMainPackages(root string) ([]string, error) {
|
//
|
||||||
|
// By default directories whose names begin with '.' or '_' are skipped (they
|
||||||
|
// are conventionally hidden or disabled). Pass all=true (the -A flag) to
|
||||||
|
// include them; in that mode ReadDir failures are downgraded to warnings so
|
||||||
|
// that a single unreadable directory doesn't abort the whole walk.
|
||||||
|
func findMainPackages(root string, all bool) ([]string, error) {
|
||||||
abs, err := filepath.Abs(root)
|
abs, err := filepath.Abs(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("resolving %s: %w", root, err)
|
return nil, fmt.Errorf("resolving %s: %w", root, err)
|
||||||
@ -402,6 +438,10 @@ func findMainPackages(root string) ([]string, error) {
|
|||||||
walk = func(dir string) error {
|
walk = func(dir string) error {
|
||||||
entries, err := os.ReadDir(dir)
|
entries, err := os.ReadDir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if all {
|
||||||
|
fmt.Fprintf(os.Stderr, "warning: skipping %s: %v\n", dir, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if checkPackageMain(dir) == nil {
|
if checkPackageMain(dir) == nil {
|
||||||
@ -411,13 +451,14 @@ func findMainPackages(root string) ([]string, error) {
|
|||||||
if !e.IsDir() {
|
if !e.IsDir() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
name := e.Name()
|
||||||
// Honour stopMarkers: skip .git directories (repo boundary).
|
// Honour stopMarkers: skip .git directories (repo boundary).
|
||||||
// A .git FILE (submodule pointer) is not a directory, so it is
|
// A .git FILE (submodule pointer) is not a directory, so it is
|
||||||
// not matched here and we keep descending — consistent with
|
// not matched here and we keep descending — consistent with
|
||||||
// findModuleRoot's behaviour.
|
// findModuleRoot's behaviour.
|
||||||
skip := false
|
skip := false
|
||||||
for _, stop := range stopMarkers {
|
for _, stop := range stopMarkers {
|
||||||
if e.Name() == stop {
|
if name == stop {
|
||||||
skip = true
|
skip = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -425,7 +466,11 @@ func findMainPackages(root string) ([]string, error) {
|
|||||||
if skip {
|
if skip {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := walk(filepath.Join(dir, e.Name())); err != nil {
|
// Skip dot- and underscore-prefixed directories unless -A is set.
|
||||||
|
if !all && len(name) > 0 && (name[0] == '.' || name[0] == '_') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := walk(filepath.Join(dir, name)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user