Skip to content

Commit

Permalink
Make an explicit update command
Browse files Browse the repository at this point in the history
  • Loading branch information
marcomorain committed Jul 4, 2018
1 parent 7c97b58 commit ca4d7cd
Showing 1 changed file with 79 additions and 45 deletions.
124 changes: 79 additions & 45 deletions cmd/build.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package cmd

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"regexp"
"strings"
"syscall"

"github.com/CircleCI-Public/circleci-cli/settings"
Expand All @@ -17,92 +17,126 @@ import (

func newBuildCommand() *cobra.Command {

return &cobra.Command{
updateCommand := &cobra.Command{
Use: "update",
Short: "Update the build agent to the latest version",
RunE: updateBuildAgentToLatest,
}

buildCommand := &cobra.Command{
Use: "build",
Short: "Run a build",
RunE: runBuild,
DisableFlagParsing: true,
}

buildCommand.AddCommand(updateCommand)

return buildCommand
}

var picardRepo = "circleci/picard"
var circleCiDir = path.Join(settings.UserHomeDir(), ".circleci")
var currentDigestFile = path.Join(circleCiDir, "current_picard_digest")
var latestDigestFile = path.Join(circleCiDir, "latest_picard_digest")
var buildAgentSettingsPath = path.Join(circleCiDir, "build_agent_settings.json")

func getDigest(file string) string {
type buildAgentSettings struct {
LatestSha256 string
}

if _, err := os.Stat(file); !os.IsNotExist(err) {
digest, err := ioutil.ReadFile(file)
func storeBuildAgentSha(sha256 string) error {
settings := buildAgentSettings{
LatestSha256: sha256,
}

if err != nil {
Logger.Error("Could not load digest file", err)
} else {
return strings.TrimSpace(string(digest))
}
settingsJSON, err := json.Marshal(settings)

if err != nil {
return errors.Wrap(err, "Failed to serialize build agent settings")
}

return "" // Unknown digest
}
err = ioutil.WriteFile(buildAgentSettingsPath, settingsJSON, 0644)

func newPullLatestImageCommand() *exec.Cmd {
return exec.Command("docker", "pull", picardRepo)
if err != nil {
return errors.Wrap(err, "Failed to write build agent settings file")
}

return nil
}

func getLatestImageSha() (string, error) {
func updateBuildAgentToLatest(cmd *cobra.Command, args []string) error {

cmd := newPullLatestImageCommand()
Logger.Info("Pulling latest build image")
bytes, err := cmd.CombinedOutput()
latestSha256, err := findLatestPicardSha()

if err != nil {
return "", errors.Wrap(err, "failed to pull latest image")
return err
}

output := string(bytes)
Logger.Infof("Latest build agent is version %s", latestSha256)

sha256 := regexp.MustCompile("(?m)sha256.*$")
return storeBuildAgentSha(latestSha256)

//latest_image=$(docker pull picardRepo | grep -o "sha256.*$")
//echo $latest_image > $LATEST_DIGEST_FILE
}

func findLatestPicardSha() (string, error) {

outputBytes, err := exec.Command("docker", "pull", picardRepo).CombinedOutput()

if err != nil {
return "", errors.Wrap(err, "failed to pull latest docker image")
}

output := string(outputBytes)
sha256 := regexp.MustCompile("(?m)sha256.*$")
latest := sha256.FindString(output)

if latest == "" {
return latest, errors.New("Failed to find latest image")
return "", errors.New("failed to parse sha256 from docker pull output")
}

return latest, nil

}

func picardImageSha1() (string, error) {
currentDigest := getDigest(currentDigestFile)
func loadCurrentBuildAgentSha() string {
if _, err := os.Stat(buildAgentSettingsPath); os.IsNotExist(err) {
return ""
}

if currentDigest == "" {
Logger.Debug("no current digest stored - downloading latest image of picard")
Logger.Info("Downloading latest CircleCI build agent...")
settingsJSON, err := ioutil.ReadFile(buildAgentSettingsPath)

// TODO - write the digest to a file so that we can
// use it again.
return getLatestImageSha()
if err != nil {
Logger.Error("Faild to load build agent settings JSON", err)
return ""
}

// TODO - this should write to a file to record
// the fact that we have the latest image.
Logger.Debug("Pulling latest picard image in background")
// We don't wait for this command, we just `Start` it and run in the background.
if err := newPullLatestImageCommand().Start(); err != nil {
return "", errors.Wrap(err, "Fails to start background update of build image")
}
var settings buildAgentSettings

return currentDigest, nil
err = json.Unmarshal(settingsJSON, &settings)

if err != nil {
Logger.Error("Faild to parse build agent settings JSON", err)
return ""
}

return settings.LatestSha256
}

func picardImage() (string, error) {
sha, err := picardImageSha1()

if err != nil {
return "", err
sha := loadCurrentBuildAgentSha()

if sha == "" {

Logger.Info("Downloading latest CircleCI build agent...")

var err error

sha, err = findLatestPicardSha()

if err != nil {
return "", err
}
storeBuildAgentSha(sha)
}
Logger.Infof("Docker image digest: %s", sha)
return fmt.Sprintf("%s@%s", picardRepo, sha), nil
Expand Down

0 comments on commit ca4d7cd

Please sign in to comment.