func (e *Walter) runService() bool { // load .walter-update log.Infof("Loading update file... \"%s\"", e.Engine.Resources.RepoService.GetUpdateFilePath()) update, err := services.LoadLastUpdate(e.Engine.Resources.RepoService.GetUpdateFilePath()) log.Infof("Succeeded loading update file") log.Info("Updating status...") update.Status = "inprogress" result := services.SaveLastUpdate(e.Engine.Resources.RepoService.GetUpdateFilePath(), update) if result == false { log.Error("Failed to save status update") return false } log.Info("Succeeded updating status") // get latest commit and pull requests log.Info("downloading commits and pull requests...") commits, err := e.Engine.Resources.RepoService.GetCommits(update) if err != nil { log.Errorf("Failed getting commits: %s", err) return false } log.Info("Succeeded getting commits") log.Info("Size of commits: " + strconv.Itoa(commits.Len())) hasFailedProcess := false for commit := commits.Front(); commit != nil; commit = commit.Next() { commitType := reflect.TypeOf(commit.Value) if commitType.Name() == "RepositoryCommit" { log.Info("Found new repository commit") trunkCommit := commit.Value.(github.RepositoryCommit) if result := e.processTrunkCommit(trunkCommit); result == false { hasFailedProcess = true } } else if commitType.Name() == "PullRequest" { log.Info("Found new pull request commit") pullreq := commit.Value.(github.PullRequest) if result := e.processPullRequest(pullreq); result == false { hasFailedProcess = true } } else { log.Errorf("Nothing commit type: %s", commitType) hasFailedProcess = true } } // save .walter-update log.Info("Saving update file...") update.Status = "finished" update.Time = time.Now() result = services.SaveLastUpdate(e.Engine.Resources.RepoService.GetUpdateFilePath(), update) if result == false { log.Error("Failed to save update") return false } return !hasFailedProcess }
//Post posts a message to slack func (slack *Slack) Post(message string, c ...string) bool { if slack.Channel[0] != '#' { log.Infof("Add # to channel name: %s", slack.Channel) slack.Channel = "#" + slack.Channel } var color string if len(c) > 0 { color = c[0] } if strings.Contains(message, "[RESULT] Failed") { color = "danger" } else if strings.Contains(message, "[RESULT] Skipped") { color = "warning" } else if strings.Contains(message, "[RESULT] Succeeded") { color = "good" } attachment := map[string]string{ "text": message, "color": color, } attachments := []map[string]string{attachment} params, _ := json.Marshal(struct { FakeSlack Attachments []map[string]string `json:"attachments"` }{ FakeSlack: FakeSlack(*slack), Attachments: attachments, }) resp, err := http.PostForm( slack.IncomingURL, url.Values{"payload": {string(params)}}, ) if err != nil { log.Errorf("Failed post message to Slack...: %s", message) return false } defer resp.Body.Close() if body, err := ioutil.ReadAll(resp.Body); err == nil { log.Infof("Slack post result...: %s", body) return true } log.Errorf("Failed to read result from Slack...") return false }
func (e *Walter) processPullRequest(pullrequest github.PullRequest) bool { // checkout pullrequest num := *pullrequest.Number _, err := exec.Command("git", "fetch", "origin", "refs/pull/"+strconv.Itoa(num)+"/head:pr_"+strconv.Itoa(num)).Output() defer exec.Command("git", "checkout", "master", "-f").Output() // TODO: make trunk branch configurable defer log.Info("returning master branch...") if err != nil { log.Errorf("Failed to fetch pull request: %s", err) return false } _, err = exec.Command("git", "checkout", "pr_"+strconv.Itoa(num)).Output() if err != nil { log.Errorf("Failed to checkout pullrequest branch (\"%s\") : %s", "pr_"+strconv.Itoa(num), err) log.Error("Skip execution...") return false } // run pipeline log.Info("Running pipeline...") w, err := New(e.Opts) if err != nil { log.Errorf("Failed to create Walter object...: %s", err) log.Error("Skip execution...") return false } result := w.Engine.RunOnce() // register the result to hosting service if result.IsSucceeded() { log.Info("succeeded.") e.Engine.Resources.RepoService.RegisterResult( services.Result{ State: "success", Message: "Succeeded running pipeline...", SHA: *pullrequest.Head.SHA}) return true } log.Error("Error reported...") e.Engine.Resources.RepoService.RegisterResult( services.Result{ State: "failure", Message: "Failed running pipleline ...", SHA: *pullrequest.Head.SHA}) return false }
//RegisterResult registers the supplied result func (githubClient *GitHubClient) RegisterResult(result Result) error { ts := oauth2.StaticTokenSource( &oauth2.Token{AccessToken: githubClient.Token}, ) tc := oauth2.NewClient(oauth2.NoContext, ts) client := github.NewClient(tc) if githubClient.BaseUrl != nil { client.BaseURL = githubClient.BaseUrl } log.Info("Submitting result") repositories := client.Repositories status, _, err := repositories.CreateStatus( githubClient.From, githubClient.Repo, result.SHA, &github.RepoStatus{ State: github.String(result.State), TargetURL: github.String(result.Url), Description: github.String(result.Message), Context: github.String("continuous-integraion/walter"), }) log.Infof("Submit status: %s", status) if err != nil { log.Errorf("Failed to register result: %s", err) } return err }
func (e *Walter) processTrunkCommit(commit github.RepositoryCommit) bool { log.Infof("Checkout master branch") _, err := exec.Command("git", "checkout", "master", "-f").Output() if err != nil { log.Errorf("Failed to checkout master branch: %s", err) return false } log.Infof("Downloading new commit from master") _, err = exec.Command("git", "pull", "origin", "master").Output() if err != nil { log.Errorf("Failed to download new commit from master: %s", err) return false } log.Infof("Running the latest commit in master") w, err := New(e.Opts) if err != nil { log.Errorf("Failed to create Walter object...: %s", err) log.Error("Skip execution...") return false } result := w.Engine.RunOnce() // register the result to hosting service if result.IsSucceeded() { log.Info("Succeeded.") e.Engine.Resources.RepoService.RegisterResult( services.Result{ State: "success", Message: "Succeeded running pipeline...", SHA: *commit.SHA}) return true } log.Error("Error reported...") e.Engine.Resources.RepoService.RegisterResult( services.Result{ State: "failure", Message: "Failed running pipleline ...", SHA: *commit.SHA}) return false }
func (hc *HipChat2) newClient() *hipchat.Client { client := hipchat.NewClient(hc.Token) if hc.BaseURL == "" { return client } baseURL, err := url.Parse(hc.BaseURL) if err != nil { log.Errorf("Invalid Hipchat Base URL...: %s", err.Error()) return nil } client.BaseURL = baseURL return client }
//GetCommits get a list of all the commits for the current update func (githubClient *GitHubClient) GetCommits(update Update) (*list.List, error) { log.Info("getting commits\n") commits := list.New() ts := oauth2.StaticTokenSource( &oauth2.Token{AccessToken: githubClient.Token}, ) tc := oauth2.NewClient(oauth2.NoContext, ts) client := github.NewClient(tc) // get a list of pull requests with Pull Request API pullreqs, _, err := client.PullRequests.List( githubClient.From, githubClient.Repo, &github.PullRequestListOptions{}) if err != nil { log.Errorf("Failed to get pull requests") return list.New(), err } re, err := regexp.Compile(githubClient.TargetBranch) if err != nil { log.Error("Failed to compile branch pattern...") return list.New(), err } log.Infof("Size of pull reqests: %d", len(pullreqs)) for _, pullreq := range pullreqs { log.Infof("Branch name is \"%s\"", *pullreq.Head.Ref) if githubClient.TargetBranch != "" { matched := re.Match([]byte(*pullreq.Head.Ref)) if matched != true { log.Infof("Not add a branch, \"%s\" since this branch name is not match the filtering pattern", *pullreq.Head.Ref) continue } } if *pullreq.State == "open" && pullreq.UpdatedAt.After(update.Time) { log.Infof("Adding pullrequest %d", *pullreq.Number) commits.PushBack(pullreq) } } // get the latest commit with Commit API if the commit is newer than last update masterCommits, _, _ := client.Repositories.ListCommits( githubClient.From, githubClient.Repo, &github.CommitsListOptions{}) if masterCommits[0].Commit.Author.Date.After(update.Time) { commits.PushBack(masterCommits[0]) } return commits, nil }
// Run registered commands. func (commandStage *CommandStage) Run() bool { // Check OnlyIf if commandStage.runOnlyIf() == false { log.Warnf("[command] exec: skipped this stage \"%s\", since only_if condition failed", commandStage.BaseStage.StageName) return true } // Run command result := commandStage.runCommand() if result == false { log.Errorf("[command] exec: failed stage \"%s\"", commandStage.BaseStage.StageName) } return result }
//SaveLastUpdate saves the supplied update to the filename func SaveLastUpdate(fname string, update Update) bool { log.Infof("writing down new update: \"%s\"\n", string(fname)) bytes, err := json.Marshal(update) if err != nil { log.Errorf("failed to convert update to string...: %s\n", err) return false } if exist := fileExists(fname); exist == true { log.Infof("file exist: \"%s\"", fname) if err := os.Remove(fname); err != nil { log.Errorf("failed to remove \"%s\" with error: \"%s\"", fname, err) return false } log.Infof("succeeded to remove: \"%s\"", fname) } if err := ioutil.WriteFile(fname, bytes, 0644); err != nil { log.Errorf("failed to write update to file...: \"%s\"\n", err) return false } log.Infof("succeeded to write update file: \"%s\"", fname) return true }
//Validate validates if the command can be executed func (resourceValidator *ResourceValidator) Validate() bool { // check if files exists for file := resourceValidator.files.Front(); file != nil; file = file.Next() { filePath := file.Value.(string) log.Debugf("checking file: %v", filePath) if _, err := os.Stat(filePath); err == nil { log.Debugf("file exists") } else { log.Errorf("file: %v does not exists", filePath) return false } } // check if command exists if len(resourceValidator.command) == 0 { // return true when no command is registrated return true } cmd := exec.Command("which", resourceValidator.command) err := cmd.Run() if err != nil { log.Errorf("command: %v does not exists", resourceValidator.command) return false } return true }
// Post posts a hipchat message // TODO: make hipchat api endpoint configurable for on-premises servers func (hipChat *HipChat) Post(message string) bool { client := hipchat.Client{AuthToken: hipChat.Token} req := hipchat.MessageRequest{ RoomId: hipChat.RoomID, From: hipChat.From, Message: message, Color: hipchat.ColorPurple, MessageFormat: hipchat.FormatText, Notify: true, } if err := client.PostMessage(req); err != nil { log.Errorf("Failed post message...: %s", message) return false } return true }
func validateWaitForCondition(wait *WaitFor) bool { // check duplicate targets if wait.Port != 0 && wait.File != "" { log.Error("[command] Port and File are not able to be specified at the same time.") return false } else if wait.Port != 0 && wait.Delay > 0.0 { log.Error("[command] Port and Delay are not able to be specified at the same time.") return false } else if wait.File != "" && wait.Delay > 0.0 { log.Error("[command] File and Delay are not able to be specified at the same time.") return false } // check illegal conditions if wait.Delay < 0 { log.Error("[command] Delay must be positive.") return false } else if wait.Port < 0 { log.Error("[command] Port must be positive.") return false } else if wait.Port > 0 && wait.Host == "" { log.Error("[command] Host must be specified when port number is specified.") return false } // check illegal states if wait.State != "present" && wait.State != "ready" && wait.State != "absent" && wait.State != "unready" { log.Errorf("[command] \"%s\" is an unsupported state", wait.State) return false } // misc checks if wait.Port > 0 && wait.State == "" { log.Error("[command] State must be specified for port.") return false } else if wait.File != "" && wait.State == "" { log.Error("[command] State must be specified for file.") return false } return true }
// Post sends a new HipChat message using V2 of the API func (hc *HipChat2) Post(message string, color ...string) bool { if hc.client == nil { hc.client = hc.newClient() if hc.client == nil { return false } } msg := &hipchat.NotificationRequest{ Color: "purple", Message: message, Notify: true, MessageFormat: "text", } if _, err := hc.client.Room.Notification(hc.RoomID, msg); err != nil { log.Errorf("Failed post message...: %s", msg.Message) return false } return true }
//RegisterResult registers the supplied result func (githubClient *GitHubClient) RegisterResult(result Result) error { t := &oauth.Transport{ Token: &oauth.Token{AccessToken: githubClient.Token}, } client := github.NewClient(t.Client()) log.Info("Submitting result") repositories := client.Repositories status, _, err := repositories.CreateStatus( githubClient.From, githubClient.Repo, result.SHA, &github.RepoStatus{ State: github.String(result.State), TargetURL: github.String(result.Url), Description: github.String(result.Message), Context: github.String("continuous-integraion/walter"), }) log.Infof("Submit status: %s", status) if err != nil { log.Errorf("Failed to register result: %s", err) } return err }