mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
Refactor
This commit is contained in:
293
releasing/releasing/gitrunner.go
Normal file
293
releasing/releasing/gitrunner.go
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type gitRunner struct {
|
||||||
|
// Original git repo path, which should be current working directory
|
||||||
|
originalGitPath string
|
||||||
|
// A temporary path for worktree
|
||||||
|
worktreePath string
|
||||||
|
// Does this have worktree
|
||||||
|
hasWorktree bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGitRunner(worktree bool) (gitRunner, error) {
|
||||||
|
gr := gitRunner{}
|
||||||
|
pwd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return gr, err
|
||||||
|
}
|
||||||
|
gr.originalGitPath = pwd
|
||||||
|
gr.hasWorktree = worktree
|
||||||
|
if worktree {
|
||||||
|
err = gr.CreateWorktreeDir()
|
||||||
|
if err != nil {
|
||||||
|
return gr, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) Close() error {
|
||||||
|
if !gr.hasWorktree {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := gr.DeleteWorktreeDir()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = gr.PruneWorktree()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) DeleteWorktreeDir() error {
|
||||||
|
logDebug("Deleting git worktree dir: %s", gr.worktreePath)
|
||||||
|
err := os.RemoveAll(gr.worktreePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logDebug("Deleting done")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) WorktreePath() (string, error) {
|
||||||
|
if gr.worktreePath == "" {
|
||||||
|
return "", fmt.Errorf("Empty worktree path")
|
||||||
|
}
|
||||||
|
return gr.worktreePath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) OriginalGitPath() (string, error) {
|
||||||
|
if gr.originalGitPath == "" {
|
||||||
|
return "", fmt.Errorf("Empty git path")
|
||||||
|
}
|
||||||
|
return gr.originalGitPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) CreateWorktreeDir() error {
|
||||||
|
// Create temporary directory
|
||||||
|
temp, err := ioutil.TempDir("", "kustomize-releases")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gr.worktreePath = filepath.Join(temp, "sigs.k8s.io/kustomize")
|
||||||
|
err = os.MkdirAll(gr.worktreePath, 0700)
|
||||||
|
logDebug("Created git worktree dir: %s", gr.worktreePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) CheckRemoteExistence(remote string) error {
|
||||||
|
path, err := gr.OriginalGitPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logDebug("Checking remote %s in %s", remote, path)
|
||||||
|
cmd := exec.Command("git", "remote")
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
logDebug("Remotes:\n%s", string(stdoutStderr))
|
||||||
|
|
||||||
|
regString := fmt.Sprintf("(?m)^%s$", remote)
|
||||||
|
reg := regexp.MustCompile(regString)
|
||||||
|
if !reg.MatchString(string(stdoutStderr)) {
|
||||||
|
return fmt.Errorf("Cannot find remote named %s", remote)
|
||||||
|
}
|
||||||
|
logDebug("Remote %s exists", remote)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) FetchTags(remote string) error {
|
||||||
|
logDebug("Fetching latest tags")
|
||||||
|
path, err := gr.OriginalGitPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd := exec.Command("git", "fetch", "-t", remote)
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
logDebug("Finished fetching")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) GetTags() (string, error) {
|
||||||
|
path, err := gr.OriginalGitPath()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
logDebug("Getting latest tag in repo %s", path)
|
||||||
|
cmd := exec.Command("git", "tag", "-l")
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
logDebug("Finished getting tags")
|
||||||
|
return string(stdoutStderr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) CheckBranchExistence(name string) (bool, error) {
|
||||||
|
logDebug("Checking branch %s existence", name)
|
||||||
|
path, err := gr.OriginalGitPath()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
cmd := exec.Command("git", "branch", "-a")
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
branches := strings.Split(string(stdoutStderr), "\n")
|
||||||
|
for _, branch := range branches {
|
||||||
|
if strings.Trim(branch, " ") == "remotes/"+name {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) NewBranch(name string) error {
|
||||||
|
logInfo("Creating new branch %s", name)
|
||||||
|
path, err := gr.OriginalGitPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
upstreamBranch := "upstream/" + name
|
||||||
|
cmd := exec.Command("git", "branch", name, upstreamBranch)
|
||||||
|
exist, err := gr.CheckBranchExistence(upstreamBranch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
logInfo("Remote branch %s doesn't exist", upstreamBranch)
|
||||||
|
cmd = exec.Command("git", "branch", name)
|
||||||
|
}
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) DeleteBranch(name string) error {
|
||||||
|
logDebug("Deleting branch %s", name)
|
||||||
|
path, err := gr.OriginalGitPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd := exec.Command("git", "branch", "-D", name)
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
logDebug("Finished deleting branch")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) AddWorktree(branch string) error {
|
||||||
|
path, err := gr.OriginalGitPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tempDir, err := gr.WorktreePath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logInfo("Adding worktree %s for branch %s", tempDir, branch)
|
||||||
|
cmd := exec.Command("git", "worktree", "add", tempDir, branch)
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) PruneWorktree() error {
|
||||||
|
path, err := gr.OriginalGitPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logDebug("Pruning worktree for repo %s", path)
|
||||||
|
cmd := exec.Command("git", "worktree", "prune")
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
logDebug("Finished pruning worktree")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) Merge(branch string) error {
|
||||||
|
logInfo("Merging %s", branch)
|
||||||
|
path, err := gr.WorktreePath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logDebug("Working dir: %s", path)
|
||||||
|
cmd := exec.Command("git", "merge", branch)
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gr *gitRunner) PushRelease(branch string, mod module) error {
|
||||||
|
logInfo("Pushing branch %s", branch)
|
||||||
|
path, err := gr.WorktreePath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cmd := exec.Command("git", "push", "upstream", branch)
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo("Creating tag %s", mod.Tag())
|
||||||
|
cmd = exec.Command(
|
||||||
|
"git", "tag",
|
||||||
|
"-a", mod.Tag(),
|
||||||
|
"-m", fmt.Sprintf("Release %s on branch %s", mod.Tag(), branch),
|
||||||
|
)
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo("Pushing tag %s", mod.Tag())
|
||||||
|
cmd = exec.Command("git", "push", "upstream", mod.Tag())
|
||||||
|
cmd.Dir = path
|
||||||
|
stdoutStderr, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s %s", err.Error(), stdoutStderr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
271
releasing/releasing/main.go
Normal file
271
releasing/releasing/main.go
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var modules = [...]string{
|
||||||
|
"kyaml", "api", "kstatus", "cmd/config",
|
||||||
|
"cmd/resource", "cmd/kubectl", "pluginator", "kustomize",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable verbose or not
|
||||||
|
var verbose bool
|
||||||
|
|
||||||
|
// Disable dry run
|
||||||
|
var noDryRun bool
|
||||||
|
|
||||||
|
// Enable module tests
|
||||||
|
var doTest bool
|
||||||
|
|
||||||
|
// === Log helper functions ===
|
||||||
|
|
||||||
|
func logDebug(format string, v ...interface{}) {
|
||||||
|
if verbose {
|
||||||
|
log.Printf("DEBUG "+format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func logInfo(format string, v ...interface{}) {
|
||||||
|
log.Printf("INFO "+format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logWarn(format string, v ...interface{}) {
|
||||||
|
log.Printf("WARN "+format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logFatal(format string, v ...interface{}) {
|
||||||
|
log.Fatalf("FATAL "+format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logFatalE(e error) {
|
||||||
|
log.Fatalf("FATAL %s", e.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Command line commands ===
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "releasing",
|
||||||
|
Short: "This go program is used to improve the modules releasing process in Kustomize repository.",
|
||||||
|
}
|
||||||
|
|
||||||
|
func listCmdImpl() error {
|
||||||
|
gr, err := newGitRunner(false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logDebug("Working directory: %s", gr.originalGitPath)
|
||||||
|
remote := "upstream"
|
||||||
|
|
||||||
|
err = gr.CheckRemoteExistence(remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = gr.FetchTags(remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tags, err := gr.GetTags()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := []string{} // Store result strings
|
||||||
|
for _, modName := range modules {
|
||||||
|
mod := module{
|
||||||
|
name: modName,
|
||||||
|
}
|
||||||
|
err = mod.UpdateVersion(tags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res = append(res, fmt.Sprintf("%s/%s", mod.name, mod.version.String()))
|
||||||
|
}
|
||||||
|
err = gr.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, l := range res {
|
||||||
|
fmt.Println(l)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var listSubCmd = &cobra.Command{
|
||||||
|
Use: "list",
|
||||||
|
Short: "List current version of all covered modules",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := listCmdImpl()
|
||||||
|
if err != nil {
|
||||||
|
logFatalE(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkReleaseArgs(args []string) error {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return errors.New("2 arguments are required")
|
||||||
|
}
|
||||||
|
found := false
|
||||||
|
for _, mod := range modules {
|
||||||
|
if mod == args[0] {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("%s is not a valid module. Valid modules are %s", args[0], modules)
|
||||||
|
}
|
||||||
|
types := []string{"major", "minor", "patch"}
|
||||||
|
found = false
|
||||||
|
for _, t := range types {
|
||||||
|
if t == args[1] {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("%s is not a valid version type. Valid types are %s", args[1], types)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func releaseCmdImpl(args []string) error {
|
||||||
|
modName := args[0]
|
||||||
|
versionType := args[1]
|
||||||
|
gr, err := newGitRunner(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logInfo("Creating tag for module %s", modName)
|
||||||
|
logDebug("Working directory: %s", gr.originalGitPath)
|
||||||
|
remote := "upstream"
|
||||||
|
|
||||||
|
err = gr.CheckRemoteExistence(remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = gr.FetchTags(remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tags, err := gr.GetTags()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
gitPath, err := gr.OriginalGitPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mod := module{
|
||||||
|
name: modName,
|
||||||
|
path: gitPath,
|
||||||
|
}
|
||||||
|
err = mod.UpdateVersion(tags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldVersion := mod.version.String()
|
||||||
|
err = mod.version.Bump(versionType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newVersion := mod.version.String()
|
||||||
|
logInfo("Bumping version: %s => %s", oldVersion, newVersion)
|
||||||
|
|
||||||
|
// Create branch
|
||||||
|
branch := fmt.Sprintf("release-%s-v%d.%d", mod.name, mod.version.major, mod.version.minor)
|
||||||
|
err = gr.NewBranch(branch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gr.AddWorktree(branch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gr.Merge("upstream/master")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Update module path
|
||||||
|
worktreePath, err := gr.WorktreePath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mod.path = worktreePath
|
||||||
|
|
||||||
|
logInfo(
|
||||||
|
"Releasing summary:\nDir:\t%s\nModule:\t%s %s\nBranch:\t%s\nTag:\t%s",
|
||||||
|
worktreePath,
|
||||||
|
mod.name,
|
||||||
|
mod.version.String(),
|
||||||
|
branch,
|
||||||
|
mod.Tag(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run module tests
|
||||||
|
output, err := mod.RunTest()
|
||||||
|
if err != nil {
|
||||||
|
logWarn(output)
|
||||||
|
} else if !noDryRun {
|
||||||
|
logInfo("Skipping push module %s. Run with --no-dry-run to push the release.", mod.name)
|
||||||
|
} else {
|
||||||
|
err = gr.PushRelease(branch, mod)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clean
|
||||||
|
err = gr.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = gr.DeleteBranch(branch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logInfo("Releasing for module %s completes", mod.name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var release = &cobra.Command{
|
||||||
|
Use: "release [module name] [version type]",
|
||||||
|
Short: "Release a new version of specified module",
|
||||||
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return checkReleaseArgs(args)
|
||||||
|
},
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
err := releaseCmdImpl(args)
|
||||||
|
if err != nil {
|
||||||
|
logFatalE(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var subCmds = [...]*cobra.Command{
|
||||||
|
listSubCmd,
|
||||||
|
release,
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Main function ===
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
for _, cmd := range subCmds {
|
||||||
|
rootCmd.AddCommand(cmd)
|
||||||
|
}
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
|
||||||
|
release.Flags().BoolVarP(&noDryRun, "no-dry-run", "", false, "disable dry-run")
|
||||||
|
release.Flags().BoolVarP(&doTest, "do-test", "", false, "run module tests before releasing")
|
||||||
|
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
logFatal(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
20
releasing/releasing/main_test.go
Normal file
20
releasing/releasing/main_test.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestList(t *testing.T) {
|
||||||
|
err := listCmdImpl()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRelease(t *testing.T) {
|
||||||
|
args := []string{"api", "minor"}
|
||||||
|
err := releaseCmdImpl(args)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
42
releasing/releasing/modulemeta.go
Normal file
42
releasing/releasing/modulemeta.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type module struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
version moduleVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) UpdateVersion(tags string) error {
|
||||||
|
v, err := newModuleVersionFromGitTags(tags, m.name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.version = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) Tag() string {
|
||||||
|
return m.name + "/" + m.version.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *module) RunTest() (string, error) {
|
||||||
|
if !doTest {
|
||||||
|
logInfo("Tests disabled.")
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
testPath := filepath.Join(m.path, m.name)
|
||||||
|
logInfo("Running tests in %s...", testPath)
|
||||||
|
cmd := exec.Command("go", "test", "./...")
|
||||||
|
cmd.Dir = testPath
|
||||||
|
stdoutStderr, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return string(stdoutStderr), err
|
||||||
|
}
|
||||||
|
logInfo("Tests are successfully finished")
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
93
releasing/releasing/moduleversion.go
Normal file
93
releasing/releasing/moduleversion.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type moduleVersion struct {
|
||||||
|
major int
|
||||||
|
minor int
|
||||||
|
patch int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *moduleVersion) String() string {
|
||||||
|
return fmt.Sprintf("v%d.%d.%d", v.major, v.minor, v.patch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *moduleVersion) Bump(t string) error {
|
||||||
|
if t == "major" {
|
||||||
|
v.major++
|
||||||
|
v.minor = 0
|
||||||
|
v.patch = 0
|
||||||
|
} else if t == "minor" {
|
||||||
|
v.minor++
|
||||||
|
v.patch = 0
|
||||||
|
} else if t == "patch" {
|
||||||
|
v.patch++
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Invalid version type: %s", t)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newModuleVersionFromString(vs string) (*moduleVersion, error) {
|
||||||
|
if len(vs) < 1 {
|
||||||
|
return nil, fmt.Errorf("Invalid version string %s", vs)
|
||||||
|
}
|
||||||
|
if vs[0] == 'v' {
|
||||||
|
vs = vs[1:]
|
||||||
|
}
|
||||||
|
versions := strings.Split(vs, ".")
|
||||||
|
major, err := strconv.Atoi(versions[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
minor, err := strconv.Atoi(versions[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
patch, err := strconv.Atoi(versions[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v := moduleVersion{
|
||||||
|
major: major,
|
||||||
|
minor: minor,
|
||||||
|
patch: patch,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newModuleVersionFromGitTags(tags, modName string) (moduleVersion, error) {
|
||||||
|
// Search for module tag
|
||||||
|
regString := fmt.Sprintf("(?m)^%s/v(\\d+\\.){2}\\d+$", modName)
|
||||||
|
reg := regexp.MustCompile(regString)
|
||||||
|
modTagsString := reg.FindAllString(tags, -1)
|
||||||
|
logDebug("Tags for module %s:\n%s", modName, modTagsString)
|
||||||
|
var versions []moduleVersion
|
||||||
|
for _, tag := range modTagsString {
|
||||||
|
tag = tag[len(modName)+2:]
|
||||||
|
v, err := newModuleVersionFromString(tag)
|
||||||
|
if err != nil {
|
||||||
|
return moduleVersion{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
versions = append(versions, *v)
|
||||||
|
}
|
||||||
|
// Sort to find latest tag
|
||||||
|
sort.Slice(versions, func(i, j int) bool {
|
||||||
|
if versions[i].major == versions[j].major && versions[i].minor == versions[j].minor {
|
||||||
|
return versions[i].patch > versions[j].patch
|
||||||
|
} else if versions[i].major == versions[j].major {
|
||||||
|
return versions[i].minor > versions[j].minor
|
||||||
|
} else {
|
||||||
|
return versions[i].major > versions[j].major
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return versions[0], nil
|
||||||
|
}
|
||||||
@@ -1,478 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var modules = [...]string{
|
|
||||||
"kyaml", "api", "kstatus", "cmd/config",
|
|
||||||
"cmd/resource", "cmd/kubectl", "pluginator", "kustomize",
|
|
||||||
}
|
|
||||||
var verbose bool // Enable verbose or not
|
|
||||||
var noDryRun bool // Disable dry run
|
|
||||||
var doTest bool // Enable module tests
|
|
||||||
var tempDir string // Temporary directory path for git worktree
|
|
||||||
|
|
||||||
// === Log helper functions ===
|
|
||||||
|
|
||||||
func logDebug(format string, v ...interface{}) {
|
|
||||||
if verbose {
|
|
||||||
log.Printf("DEBUG "+format, v...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func logInfo(format string, v ...interface{}) {
|
|
||||||
log.Printf("INFO "+format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func logWarn(format string, v ...interface{}) {
|
|
||||||
log.Printf("WARN "+format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func logFatal(format string, v ...interface{}) {
|
|
||||||
log.Fatalf("FATAL "+format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Command line commands ===
|
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
|
||||||
Use: "releasing",
|
|
||||||
Short: "This go program is used to improve the modules releasing process in Kustomize repository.",
|
|
||||||
}
|
|
||||||
|
|
||||||
var listSubCmd = &cobra.Command{
|
|
||||||
Use: "list",
|
|
||||||
Short: "List current version of all covered modules",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
pwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
logDebug("Working directory: %s", pwd)
|
|
||||||
remote := "upstream"
|
|
||||||
// Check remotes
|
|
||||||
checkRemoteExistence(pwd, remote)
|
|
||||||
// Fetch latest tags from remote
|
|
||||||
fetchTags(pwd, remote)
|
|
||||||
res := []string{} // Store result strings
|
|
||||||
for _, mod := range modules {
|
|
||||||
res = append(res, fmt.Sprintf("%s/%s", mod, getModuleCurrentVersion(mod, pwd)))
|
|
||||||
}
|
|
||||||
for _, l := range res {
|
|
||||||
fmt.Println(l)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var release = &cobra.Command{
|
|
||||||
Use: "release [module name] [version type]",
|
|
||||||
Short: "Release a new version of specified module",
|
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) != 2 {
|
|
||||||
return errors.New("2 arguments are required")
|
|
||||||
}
|
|
||||||
found := false
|
|
||||||
for _, mod := range modules {
|
|
||||||
if mod == args[0] {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return fmt.Errorf("%s is not a valid module. Valid modules are %s", args[0], modules)
|
|
||||||
}
|
|
||||||
types := []string{"major", "minor", "patch"}
|
|
||||||
found = false
|
|
||||||
for _, t := range types {
|
|
||||||
if t == args[1] {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return fmt.Errorf("%s is not a valid version type. Valid types are %s", args[1], types)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
modName := args[0]
|
|
||||||
versionType := args[1]
|
|
||||||
createTempDir()
|
|
||||||
logInfo("Creating tag for module %s", modName)
|
|
||||||
pwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
logDebug("Working directory: %s", pwd)
|
|
||||||
remote := "upstream"
|
|
||||||
// Check remotes
|
|
||||||
checkRemoteExistence(pwd, remote)
|
|
||||||
// Fetch latest tags from remote
|
|
||||||
fetchTags(pwd, remote)
|
|
||||||
|
|
||||||
mod := module{
|
|
||||||
name: modName,
|
|
||||||
path: pwd,
|
|
||||||
}
|
|
||||||
mod.UpdateCurrentVersion()
|
|
||||||
|
|
||||||
oldVersion := mod.version.String()
|
|
||||||
mod.version.Bump(versionType)
|
|
||||||
newVersion := mod.version.String()
|
|
||||||
logInfo("Bumping version: %s => %s", oldVersion, newVersion)
|
|
||||||
|
|
||||||
// Create branch
|
|
||||||
branch := fmt.Sprintf("release-%s-v%d.%d", mod.name, mod.version.major, mod.version.minor)
|
|
||||||
newBranch(pwd, branch)
|
|
||||||
|
|
||||||
addWorktree(pwd, tempDir, branch)
|
|
||||||
|
|
||||||
merge(tempDir, "upstream/master")
|
|
||||||
// Update module path
|
|
||||||
mod.path = tempDir
|
|
||||||
|
|
||||||
logInfo(
|
|
||||||
"Releasing summary:\nDir:\t%s\nModule:\t%s %s\nBranch:\t%s\nTag:\t%s",
|
|
||||||
tempDir,
|
|
||||||
mod.name,
|
|
||||||
mod.version.String(),
|
|
||||||
branch,
|
|
||||||
mod.Tag(),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Run module tests
|
|
||||||
output, err := mod.RunTest()
|
|
||||||
if err != nil {
|
|
||||||
logWarn(output)
|
|
||||||
} else if !noDryRun {
|
|
||||||
logInfo("Skipping push module %s. Run with --no-dry-run to push the release.", mod.name)
|
|
||||||
} else {
|
|
||||||
pushRelease(tempDir, branch, mod)
|
|
||||||
}
|
|
||||||
// Clean
|
|
||||||
removeTempDir()
|
|
||||||
pruneWorktree(pwd)
|
|
||||||
deleteBranch(pwd, branch)
|
|
||||||
logInfo("Releasing for module %s completes", mod.name)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var subCmds = [...]*cobra.Command{
|
|
||||||
listSubCmd,
|
|
||||||
release,
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Main function ===
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
for _, cmd := range subCmds {
|
|
||||||
rootCmd.AddCommand(cmd)
|
|
||||||
}
|
|
||||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
|
|
||||||
release.Flags().BoolVarP(&noDryRun, "no-dry-run", "", false, "disable dry-run")
|
|
||||||
release.Flags().BoolVarP(&doTest, "do-test", "", false, "run module tests before releasing")
|
|
||||||
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getModuleCurrentVersion(modName, path string) string {
|
|
||||||
mod := module{
|
|
||||||
name: modName,
|
|
||||||
path: path,
|
|
||||||
}
|
|
||||||
mod.UpdateCurrentVersion()
|
|
||||||
v := mod.version.String()
|
|
||||||
logDebug("module %s version.toString => %s", mod.name, v)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// === module version struct and functions definition ===
|
|
||||||
|
|
||||||
type moduleVersion struct {
|
|
||||||
major int
|
|
||||||
minor int
|
|
||||||
patch int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v moduleVersion) String() string {
|
|
||||||
return fmt.Sprintf("v%d.%d.%d", v.major, v.minor, v.patch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *moduleVersion) Set(major, minor, patch int) {
|
|
||||||
v.major = major
|
|
||||||
v.minor = minor
|
|
||||||
v.patch = patch
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *moduleVersion) FromString(vs string) {
|
|
||||||
versions := strings.Split(vs, ".")
|
|
||||||
major, err := strconv.Atoi(versions[0])
|
|
||||||
if err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
minor, err := strconv.Atoi(versions[1])
|
|
||||||
if err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
patch, err := strconv.Atoi(versions[2])
|
|
||||||
if err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
v.Set(major, minor, patch)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *moduleVersion) Bump(t string) {
|
|
||||||
if t == "major" {
|
|
||||||
v.major++
|
|
||||||
v.minor = 0
|
|
||||||
v.patch = 0
|
|
||||||
} else if t == "minor" {
|
|
||||||
v.minor++
|
|
||||||
v.patch = 0
|
|
||||||
} else if t == "patch" {
|
|
||||||
v.patch++
|
|
||||||
} else {
|
|
||||||
logFatal("Invalid version type: %s", t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// === module struct and functions definition ===
|
|
||||||
|
|
||||||
type module struct {
|
|
||||||
name string
|
|
||||||
path string
|
|
||||||
version moduleVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *module) UpdateCurrentVersion() {
|
|
||||||
logDebug("Getting latest tag for %s", m.name)
|
|
||||||
cmd := exec.Command("git", "tag", "-l")
|
|
||||||
var out bytes.Buffer
|
|
||||||
cmd.Stdout = &out
|
|
||||||
cmd.Dir = m.path
|
|
||||||
err := cmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for module tag
|
|
||||||
regString := fmt.Sprintf("(?m)^%s/v(\\d+\\.){2}\\d+$", m.name)
|
|
||||||
reg := regexp.MustCompile(regString)
|
|
||||||
tagsString := reg.FindAllString(out.String(), -1)
|
|
||||||
logDebug("Tags for module %s:\n%s", m.name, tagsString)
|
|
||||||
var versions []moduleVersion
|
|
||||||
for _, tag := range tagsString {
|
|
||||||
tag = tag[len(m.name)+2:]
|
|
||||||
v := moduleVersion{}
|
|
||||||
v.FromString(tag)
|
|
||||||
|
|
||||||
versions = append(versions, v)
|
|
||||||
}
|
|
||||||
// Sort to find latest tag
|
|
||||||
sort.Slice(versions, func(i, j int) bool {
|
|
||||||
if versions[i].major == versions[j].major && versions[i].minor == versions[j].minor {
|
|
||||||
return versions[i].patch > versions[j].patch
|
|
||||||
} else if versions[i].major == versions[j].major {
|
|
||||||
return versions[i].minor > versions[j].minor
|
|
||||||
} else {
|
|
||||||
return versions[i].major > versions[j].major
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
m.version = versions[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *module) Tag() string {
|
|
||||||
return m.name + "/" + m.version.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m module) RunTest() (string, error) {
|
|
||||||
if !doTest {
|
|
||||||
logInfo("Tests disabled.")
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
testPath := path.Join(m.path, m.name)
|
|
||||||
logInfo("Running tests in %s...", testPath)
|
|
||||||
cmd := exec.Command("go", "test", "./...")
|
|
||||||
cmd.Dir = testPath
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return string(stdoutStderr), err
|
|
||||||
}
|
|
||||||
logInfo("Tests are successfully finished")
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Git environment functions ===
|
|
||||||
|
|
||||||
func createTempDir() {
|
|
||||||
// Create temporary directory
|
|
||||||
temp, err := ioutil.TempDir("", "kustomize-releases")
|
|
||||||
if err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
logDebug("Created git temp dir: " + tempDir)
|
|
||||||
tempDir = path.Join(temp, "sigs.k8s.io/kustomize")
|
|
||||||
err = os.MkdirAll(tempDir, 0700)
|
|
||||||
if err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeTempDir() {
|
|
||||||
logDebug("Deleting git temp dir: " + tempDir)
|
|
||||||
err := os.RemoveAll(tempDir)
|
|
||||||
if err != nil {
|
|
||||||
logFatal(err.Error())
|
|
||||||
}
|
|
||||||
logDebug("Deleting done")
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRemoteExistence(path, remote string) {
|
|
||||||
logDebug("Checking remote %s in %s", remote, path)
|
|
||||||
cmd := exec.Command("git", "remote")
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
logDebug("Remotes:\n%s", string(stdoutStderr))
|
|
||||||
|
|
||||||
regString := fmt.Sprintf("(?m)^%s$", remote)
|
|
||||||
reg := regexp.MustCompile(regString)
|
|
||||||
if !reg.MatchString(string(stdoutStderr)) {
|
|
||||||
logFatal("Cannot find remote named %s", remote)
|
|
||||||
}
|
|
||||||
logDebug("Remote %s exists", remote)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchTags(path, remote string) {
|
|
||||||
logDebug("Fetching latest tags")
|
|
||||||
cmd := exec.Command("git", "fetch", "-t", remote)
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
logDebug("Finished fetching")
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkBranchExistence(path, name string) bool {
|
|
||||||
logDebug("Checking branch %s existence", name)
|
|
||||||
cmd := exec.Command("git", "branch", "-a")
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
branches := strings.Split(string(stdoutStderr), "\n")
|
|
||||||
for _, branch := range branches {
|
|
||||||
if strings.Trim(branch, " ") == "remotes/"+name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func newBranch(path, name string) {
|
|
||||||
logInfo("Creating new branch %s", name)
|
|
||||||
upstreamBranch := "upstream/" + name
|
|
||||||
cmd := exec.Command("git", "branch", name, upstreamBranch)
|
|
||||||
if !checkBranchExistence(path, upstreamBranch) {
|
|
||||||
logInfo("Remote branch %s doesn't exist", upstreamBranch)
|
|
||||||
cmd = exec.Command("git", "branch", name)
|
|
||||||
}
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteBranch(path, name string) {
|
|
||||||
logDebug("Deleting branch %s", name)
|
|
||||||
cmd := exec.Command("git", "branch", "-D", name)
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
logDebug("Finished deleting branch")
|
|
||||||
}
|
|
||||||
|
|
||||||
func addWorktree(path, tempDir, branch string) {
|
|
||||||
logInfo("Adding worktree %s for branch %s", tempDir, branch)
|
|
||||||
cmd := exec.Command("git", "worktree", "add", tempDir, branch)
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pruneWorktree(path string) {
|
|
||||||
logDebug("Pruning worktree for repo %s", path)
|
|
||||||
cmd := exec.Command("git", "worktree", "prune")
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
logDebug("Finished pruning worktree")
|
|
||||||
}
|
|
||||||
|
|
||||||
func merge(path, branch string) {
|
|
||||||
logInfo("Merging %s", branch)
|
|
||||||
logDebug("Working dir: %s", path)
|
|
||||||
cmd := exec.Command("git", "merge", branch)
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func pushRelease(path, branch string, mod module) {
|
|
||||||
logInfo("Pushing branch %s", branch)
|
|
||||||
cmd := exec.Command("git", "push", "upstream", branch)
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
|
|
||||||
logInfo("Creating tag %s", mod.Tag())
|
|
||||||
cmd = exec.Command(
|
|
||||||
"git", "tag",
|
|
||||||
"-a", mod.Tag(),
|
|
||||||
"-m", fmt.Sprintf("Release %s on branch %s", mod.Tag(), branch),
|
|
||||||
)
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err = cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
|
|
||||||
logInfo("Pushing tag %s", mod.Tag())
|
|
||||||
cmd = exec.Command("git", "push", "upstream", mod.Tag())
|
|
||||||
cmd.Dir = path
|
|
||||||
stdoutStderr, err = cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
logFatal(string(stdoutStderr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
|
||||||
pwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
remote := "upstream"
|
|
||||||
// Check remotes
|
|
||||||
checkRemoteExistence(pwd, remote)
|
|
||||||
// Fetch latest tags from remote
|
|
||||||
fetchTags(pwd, remote)
|
|
||||||
for _, mod := range modules {
|
|
||||||
v := getModuleCurrentVersion(mod, pwd)
|
|
||||||
valid, err := regexp.MatchString("^v(\\d+\\.){2}\\d+$", v)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
if !valid {
|
|
||||||
t.Errorf("Returned version %s is not valid", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRelease(t *testing.T) {
|
|
||||||
createTempDir()
|
|
||||||
modName := "api"
|
|
||||||
versionType := "patch"
|
|
||||||
pwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
remote := "upstream"
|
|
||||||
// Check remotes
|
|
||||||
checkRemoteExistence(pwd, remote)
|
|
||||||
// Fetch latest tags from remote
|
|
||||||
fetchTags(pwd, remote)
|
|
||||||
mod := module{
|
|
||||||
name: modName,
|
|
||||||
path: pwd,
|
|
||||||
}
|
|
||||||
mod.UpdateCurrentVersion()
|
|
||||||
|
|
||||||
oldVersion := mod.version.String()
|
|
||||||
mod.version.Bump(versionType)
|
|
||||||
newVersion := mod.version.String()
|
|
||||||
logInfo("Bumping version: %s => %s", oldVersion, newVersion)
|
|
||||||
|
|
||||||
// Create branch
|
|
||||||
branch := fmt.Sprintf("release-%s-v%d.%d", mod.name, mod.version.major, mod.version.minor)
|
|
||||||
newBranch(pwd, branch)
|
|
||||||
|
|
||||||
addWorktree(pwd, tempDir, branch)
|
|
||||||
|
|
||||||
merge(tempDir, "upstream/master")
|
|
||||||
// Update module path
|
|
||||||
mod.path = tempDir
|
|
||||||
|
|
||||||
// Clean
|
|
||||||
removeTempDir()
|
|
||||||
pruneWorktree(pwd)
|
|
||||||
deleteBranch(pwd, branch)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user