From 90b423a2aef1b3c23229e2f2d73cb879b45df017 Mon Sep 17 00:00:00 2001 From: Dimitris Baltas Date: Mon, 23 Apr 2018 10:14:59 +0300 Subject: [PATCH] github create draft release deploy command --- github/github.go | 54 +++++++++++++++ main.go | 170 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 216 insertions(+), 8 deletions(-) create mode 100644 github/github.go diff --git a/github/github.go b/github/github.go new file mode 100644 index 0000000..e5403c7 --- /dev/null +++ b/github/github.go @@ -0,0 +1,54 @@ +package github + +import ( + "context" + "fmt" + + "github.com/dbaltas/ergo/repo" + "github.com/google/go-github/github" + "golang.org/x/oauth2" +) + +//CreateDraftRelease creates a draft release +func CreateDraftRelease(ctx context.Context, accessToken string, organization string, + repo string, name string, tagName string, releaseBody string) (*github.RepositoryRelease, error) { + isDraft := true + release := &github.RepositoryRelease{ + Name: &name, + TagName: &tagName, + Draft: &isDraft, + Body: &releaseBody, + } + client := githubClient(ctx, accessToken) + release, _, err := client.Repositories.CreateRelease(ctx, organization, repo, release) + + return release, err +} + +func githubClient(ctx context.Context, accessToken string) *github.Client { + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: accessToken}, + ) + tc := oauth2.NewClient(ctx, ts) + + return github.NewClient(tc) +} + +//ReleaseBody output needed for github release body +func ReleaseBody(commitDiffBranches []repo.DiffCommitBranch, releaseBodyPrefix string) string { + body := releaseBodyPrefix + + firstLinePrefix := "- [ ] " + nextLinePrefix := " " + lineSeparator := "\r\n" + + for _, diffBranch := range commitDiffBranches { + for _, commit := range diffBranch.Behind { + body = fmt.Sprintf("%s%s%s", + body, + repo.FormatMessage(commit, firstLinePrefix, nextLinePrefix, lineSeparator), + lineSeparator) + } + } + return body +} diff --git a/main.go b/main.go index fa6dfaf..c0e40cc 100644 --- a/main.go +++ b/main.go @@ -1,11 +1,20 @@ package main import ( + "bufio" + "context" "flag" "fmt" + "os" + "os/exec" "strings" + "time" + homedir "github.com/mitchellh/go-homedir" + + "github.com/dbaltas/ergo/github" "github.com/dbaltas/ergo/repo" + "github.com/spf13/viper" "github.com/fatih/color" "github.com/rodaine/table" @@ -15,28 +24,74 @@ import ( type CommitMessageFormat int8 const ( - // Format Commit message for use in github release + // GithubRelease Format Commit message for use in github release GithubRelease CommitMessageFormat = 0 ) func main() { + var cfgFile string var repoURL string var directory string var skipFetch bool var baseBranch string var branchesString string - var inDetail bool + var createReleaseCmd bool + var releaseInterval string + var releaseOffset string + + //command flags + var statusCmd bool + var pendingCmd bool + var deployCmd bool + var err error var diff []repo.DiffCommitBranch + flag.StringVar(&cfgFile, "cfgFile", "", "config file (default is $HOME/.ergo.yaml)") flag.StringVar(&repoURL, "repoUrl", "", "git repo Url. ssh and https supported") - flag.StringVar(&directory, "directory", "", "Location to store or retrieve from the repo") + flag.StringVar(&directory, "directory", ".", "Location to store or retrieve from the repo") flag.BoolVar(&skipFetch, "skipFetch", false, "Skip fetch. When set you may not be up to date with remote") - flag.StringVar(&baseBranch, "baseBranch", "master", "Base branch for the comparison.") + flag.StringVar(&baseBranch, "baseBranch", "", "Base branch for the comparison.") flag.StringVar(&branchesString, "branches", "", "Comma separated list of branches") - flag.BoolVar(&inDetail, "inDetail", false, "When true, display commits difference in detail") + flag.StringVar(&releaseInterval, "releaseInterval", "5m", "Duration to wait between releases. ('5m', '1h25m', '30s')") + flag.StringVar(&releaseOffset, "releaseOffset", "10m", "Duration to wait before the first release ('5m', '1h25m', '30s')") + + // command flags. TODO: move to commands + flag.BoolVar(&statusCmd, "status", false, "CMD: Display status of targetBranches compared to base branch") + flag.BoolVar(&pendingCmd, "pending", false, "CMD: Display commits that branch is behind base branch") + flag.BoolVar(&deployCmd, "deploy", false, "CMD: Deploy base branch to target branches") + flag.BoolVar(&createReleaseCmd, "createRelease", false, "CMD: Create a draft release on github comparing one target branch with the base branch") + flag.Parse() + // viper.AddConfigPath(".") + // err = viper.ReadInConfig() + // if err != nil { + // fmt.Printf("error reading config file: %v\n", err) + // fmt.Println("Proceeding without configuration file") + // return + // } + home, err := homedir.Dir() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + viper.AddConfigPath(home) + viper.SetConfigName(".ergo") + err = viper.ReadInConfig() + if err != nil { + fmt.Printf("error reading config file: %v\n", err) + fmt.Println("Proceeding without configuration file") + } + + if baseBranch == "" { + baseBranch = viper.GetString("generic.base-branch") + } + if branchesString == "" { + branchesString = viper.GetString("generic.status-branches") + } + repository, err := repo.LoadOrClone(repoURL, directory, "origin", skipFetch) if err != nil { fmt.Printf("Error loading repo:%s\n", err) @@ -64,11 +119,39 @@ func main() { diff = append(diff, branchCommitDiff) } - if inDetail { + if statusCmd { + printBranchCompare(diff) + return + + } + + if pendingCmd { printDetail(diff) return } - printBranchCompare(diff) + + if deployCmd { + deployBranches(baseBranch, branches, releaseOffset, releaseInterval, directory) + return + } + + if createReleaseCmd { + tagName := "2018.04.22" + name := "April 22 2018" + + releaseBody := github.ReleaseBody(diff, viper.GetString("github.release-body-prefix")) + + release, err := github.CreateDraftRelease( + context.Background(), + viper.GetString("github.access-token"), + "taxibeat", "rest", + name, tagName, releaseBody) + + if err != nil { + fmt.Println(err) + } + fmt.Println(release) + } } func printBranchCompare(commitDiffBranches []repo.DiffCommitBranch) { @@ -100,7 +183,7 @@ func printDetail(commitDiffBranches []repo.DiffCommitBranch) { firstLinePrefix := "- [ ] " nextLinePrefix := " " - lineSeparator := "\\r\\n" + lineSeparator := "\r\n" for _, diffBranch := range commitDiffBranches { for _, commit := range diffBranch.Behind { @@ -108,3 +191,74 @@ func printDetail(commitDiffBranches []repo.DiffCommitBranch) { } } } + +func deployBranches(baseBranch string, branches []string, releaseOffset string, releaseInterval string, directory string) { + blue := color.New(color.FgCyan) + yellow := color.New(color.FgYellow) + green := color.New(color.FgGreen) + + fmt.Println() + blue.Print("Release from: ") + yellow.Println(baseBranch) + + headerFmt := color.New(color.FgGreen, color.Underline).SprintfFunc() + columnFmt := color.New(color.FgYellow).SprintfFunc() + + tbl := table.New("Branch", "Start Time") + tbl.WithHeaderFormatter(headerFmt).WithFirstColumnFormatter(columnFmt) + + intervalDuration, err := time.ParseDuration(releaseInterval) + if err != nil { + fmt.Printf("error parsing interval %v", err) + return + } + offsetDuration, err := time.ParseDuration(releaseOffset) + if err != nil { + fmt.Printf("error parsing offset %v", err) + return + } + + t := time.Now() + t = t.Add(offsetDuration) + firstRelease := t + for _, branch := range branches { + tbl.AddRow(branch, t.Format("15:04:05")) + t = t.Add(intervalDuration) + } + + tbl.Print() + reader := bufio.NewReader(os.Stdin) + yellow.Printf("Press 'ok' to continue with Deployment:") + input, _ := reader.ReadString('\n') + text := strings.Split(input, "\n")[0] + if text != "ok" { + fmt.Printf("No deployment\n") + return + } + fmt.Println(text) + + if firstRelease.Before(time.Now()) { + yellow.Println("\ndeployment stopped since first released time has passed. Please run again") + return + } + + d := firstRelease.Sub(time.Now()) + green.Printf("Deployment will start in %s\n", d.String()) + time.Sleep(d) + for i, branch := range branches { + if i != 0 { + time.Sleep(intervalDuration) + t = t.Add(intervalDuration) + } + green.Printf("%s Deploying %s\n", time.Now().Format("15:04:05"), branch) + cmd := fmt.Sprintf("cd %s && git push origin origin/%s:%s", directory, baseBranch, branch) + green.Printf("%s Executing %s\n", time.Now().Format("15:04:05"), cmd) + out, err := exec.Command("sh", "-c", cmd).Output() + + if err != nil { + fmt.Printf("error executing command: %s %v\n", cmd, err) + return + } + green.Printf("%s Triggered Successfully %s\n", time.Now().Format("15:04:05"), strings.TrimSpace(string(out))) + } +}