github.com/section/sectionctl@v1.12.3/commands/gitService.go (about) 1 package commands 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "path/filepath" 8 "strings" 9 "time" 10 11 "github.com/alecthomas/kong" 12 "github.com/go-git/go-git/v5" 13 "github.com/go-git/go-git/v5/plumbing" 14 "github.com/go-git/go-git/v5/plumbing/object" 15 gitHTTP "github.com/go-git/go-git/v5/plumbing/transport/http" 16 "github.com/rs/zerolog/log" 17 "github.com/section/sectionctl/api" 18 ) 19 20 // GitService interface provides a way to interact with Git 21 type GitService interface { 22 UpdateGitViaGit(ctx *kong.Context, c *DeployCmd, response UploadResponse, logWriters *LogWriters) error 23 } 24 25 // GS ... 26 type GS struct{} 27 28 // This is far less then ideal, however Kong does not seem to provide a way to inject dependencies into its commands so we must use this for testing 29 var globalGitService GitService = &GS{} 30 31 // UpdateGitViaGit clones the application repository to a temporary directory then updates it with the latest payload id and pushes a new commit 32 func (g *GS) UpdateGitViaGit(ctx *kong.Context, c *DeployCmd, response UploadResponse,logWriters *LogWriters) error { 33 app, err := api.Application(c.AccountID, c.AppID) 34 if err != nil { 35 return err 36 } 37 appName := strings.ReplaceAll(app.ApplicationName, "/", "") 38 cloneDir := fmt.Sprintf("https://aperture.section.io/account/%d/application/%d/%s.git", c.AccountID, c.AppID, appName) 39 log.Debug().Msg(fmt.Sprintf(" Begin updating hash in .section-external-source.json:\n\tsection-configmap-tars/%v/%s.tar.gz\n",c.AccountID,response.PayloadID)) 40 tempDir, err := ioutil.TempDir("", "sectionctl-*") 41 if err != nil { 42 return err 43 } 44 log.Debug().Msg(fmt.Sprintln("tempDir: ", tempDir)) 45 // Git objects storer based on memory 46 gitAuth := &gitHTTP.BasicAuth{ 47 Username: "section-token", // yes, this can be anything except an empty string 48 Password: api.Token, 49 } 50 payload := PayloadValue{ID: response.PayloadID} 51 branchRef := fmt.Sprintf("refs/heads/%s",c.Environment) 52 var r *git.Repository 53 progressOutput := logWriters.CarriageReturnWriter 54 log.Info().Msg(fmt.Sprintln("Cloning section config repo for your application to ",tempDir)) 55 r, err = git.PlainClone(tempDir, false, &git.CloneOptions{ 56 URL: cloneDir, 57 Auth: gitAuth, 58 Progress: progressOutput, 59 ReferenceName: plumbing.ReferenceName(branchRef), 60 }) 61 62 if err != nil { 63 log.Error().Err(err).Msg("error cloning") 64 return err 65 } 66 // ... retrieving the branch being pointed by HEAD 67 ref, err := r.Head() 68 if err != nil { 69 log.Error().Err(err).Msg("error retrieving the git HEAD") 70 return err 71 } 72 // ... retrieving the commit object 73 commit, err := r.CommitObject(ref.Hash()) 74 if err != nil { 75 log.Error().Err(err).Msg("error retrieving the commit hash") 76 return err 77 } 78 log.Debug().Msg(fmt.Sprintln("HEAD commit: ", commit)) 79 // ... retrieve the tree from the commit 80 tree, err := commit.Tree() 81 if err != nil { 82 return err 83 } 84 w, err := r.Worktree() 85 if err != nil { 86 return err 87 } 88 f, err := tree.File(c.AppPath + "/.section-external-source.json") 89 if err != nil { 90 return err 91 } 92 srcContent := PayloadValue{} 93 content, err := f.Contents() 94 if err != nil { 95 return fmt.Errorf("couldn't open contents of file: %w", err) 96 } 97 err = json.Unmarshal([]byte(content), &srcContent) 98 if err != nil { 99 return fmt.Errorf("failed to unmarshal json: %w", err) 100 } 101 log.Debug().Str("Old tarball UUID", content); 102 log.Debug().Str("New tarball UUID", response.PayloadID) 103 srcContent.ID = payload.ID 104 pl, err := json.MarshalIndent(srcContent, "", "\t") 105 if err != nil { 106 return err 107 } 108 err = ioutil.WriteFile(filepath.Join(tempDir, c.AppPath+"/.section-external-source.json"), pl, 0644) 109 if err != nil { 110 return err 111 } 112 _, err = w.Add(c.AppPath + "/.section-external-source.json") 113 if err != nil { 114 return err 115 } 116 117 status, err := w.Status() 118 if err != nil { 119 return err 120 } 121 log.Debug().Msg(fmt.Sprintln("git status: ", status)) 122 _, err = w.Add(c.AppPath + "/.section-external-source.json") 123 if err != nil { 124 return err 125 } 126 commitHash, err := w.Commit("[sectionctl] updated nodejs/.section-external-source.json with new deployment.", &git.CommitOptions{Author: &object.Signature{ 127 Name: "sectionctl", 128 Email: "noreply@section.io", 129 When: time.Now(), 130 }}) 131 if err != nil { 132 return fmt.Errorf("failed to make a commit on the temporary repository: %w", err) 133 } 134 cmt, err := r.CommitObject(commitHash) 135 if err != nil { 136 return fmt.Errorf("failed to get commit object: %w", err) 137 } 138 log.Debug().Msg(fmt.Sprintln("New Commit: ", cmt.String())) 139 newTree, err := cmt.Tree() 140 if err != nil { 141 return err 142 } 143 newF, err := newTree.File(c.AppPath + "/.section-external-source.json") 144 if err != nil { 145 return err 146 } 147 148 ctt, err := newF.Contents() 149 if err != nil { 150 return fmt.Errorf("could not open contents of new file in git: %w", err) 151 } 152 log.Debug().Msg(fmt.Sprintln("contents in new commit: ", ctt)) 153 154 configFile, err := tree.File("section.config.json") 155 if err != nil { 156 log.Error().Err(err).Msg("unable to open section.config.json which is used to log the image name and version") 157 } 158 sectionConfigContents, err := configFile.Contents() 159 if err != nil { 160 log.Error().Err(err).Msg("unable to open section.config.json which is used to log the image name and version") 161 } 162 sectionConfig, err := ParseSectionConfig(sectionConfigContents) 163 if err != nil{ 164 log.Error().Err(err).Msg("There was an issue reading the section.config.json") 165 } 166 // if err := json.Unmarshal(sectionConfigContent.Bytes(), §ionConfig); err != nil { 167 // log.Error().Err(err).Msg("unable to decode the json for section.config.json which is used to log the image name and version") 168 // } 169 moduleVersion := "unknown" 170 for _,v := range sectionConfig.Proxychain{ 171 if(v.Name == c.AppPath){ 172 moduleVersion = v.Image 173 } 174 } 175 if moduleVersion == "unknown"{ 176 log.Debug().Msg("failed to pair app path (aka proxy name) with image (version)") 177 } 178 // for proxy, _ := range sectionConfig["proxychain"]{ 179 180 // } 181 log.Info().Str("Git Remote",cloneDir).Msg("") 182 log.Info().Str("Tarball Source",fmt.Sprintf("%v/%s.tar.gz",c.AccountID,response.PayloadID)).Msg("") 183 log.Info().Str("Module Name",c.AppPath).Msg("") 184 log.Info().Str("Module Version",moduleVersion).Msg("") 185 log.Info().Msg("Validating your app...") 186 err = r.Push(&git.PushOptions{Auth: gitAuth, Progress: progressOutput}) 187 188 if err != nil { 189 return fmt.Errorf("failed to push git changes: %w", err) 190 } 191 192 return nil 193 }