github.com/ianfoo/lab@v0.9.5-0.20180123060006-5ed79f2ccfc7/internal/gitlab/gitlab.go (about) 1 // Package gitlab is an internal wrapper for the go-gitlab package 2 // 3 // Most functions serve to expose debug logging if set and accept a project 4 // name string over an ID 5 package gitlab 6 7 import ( 8 "fmt" 9 "io/ioutil" 10 "log" 11 "net/http" 12 "os" 13 "path/filepath" 14 "strings" 15 16 "github.com/pkg/errors" 17 "github.com/xanzy/go-gitlab" 18 "github.com/zaquestion/lab/internal/git" 19 ) 20 21 var ( 22 ErrProjectNotFound = errors.New("gitlab project not found") 23 ) 24 25 var ( 26 lab *gitlab.Client 27 host string 28 user string 29 ) 30 31 // Host exposes the GitLab scheme://hostname used to interact with the API 32 func Host() string { 33 return host 34 } 35 36 // User exposes the configured GitLab user 37 func User() string { 38 return user 39 } 40 41 // Init initializes a gitlab client for use throughout lab. 42 func Init(_host, _user, _token string) { 43 host = _host 44 user = _user 45 lab = gitlab.NewClient(nil, _token) 46 lab.SetBaseURL(host + "/api/v4") 47 } 48 49 // Defines filepath for default GitLab templates 50 const ( 51 TmplMR = "merge_request_templates/default.md" 52 TmplIssue = "issue_templates/default.md" 53 ) 54 55 // LoadGitLabTmpl loads gitlab templates for use in creating Issues and MRs 56 // 57 // https://gitlab.com/help/user/project/description_templates.md#setting-a-default-template-for-issues-and-merge-requests 58 func LoadGitLabTmpl(tmplName string) string { 59 wd, err := git.WorkingDir() 60 if err != nil { 61 log.Fatal(err) 62 } 63 64 tmplFile := filepath.Join(wd, ".gitlab", tmplName) 65 f, err := os.Open(tmplFile) 66 if os.IsNotExist(err) { 67 return "" 68 } else if err != nil { 69 log.Fatal(err) 70 } 71 72 tmpl, err := ioutil.ReadAll(f) 73 if err != nil { 74 log.Fatal(err) 75 } 76 77 return string(tmpl[:len(tmpl)-1]) 78 } 79 80 var ( 81 localProjects map[string]*gitlab.Project = make(map[string]*gitlab.Project) 82 ) 83 84 // FindProject looks up the Gitlab project. If the namespace is not provided in 85 // the project string it will search for projects in the users namespace 86 func FindProject(project string) (*gitlab.Project, error) { 87 if target, ok := localProjects[project]; ok { 88 return target, nil 89 } 90 91 search := project 92 // Assuming that a "/" in the project means its owned by an org 93 if !strings.Contains(project, "/") { 94 search = user + "/" + project 95 } 96 97 target, resp, err := lab.Projects.GetProject(search) 98 if resp.StatusCode == http.StatusNotFound { 99 return nil, ErrProjectNotFound 100 } 101 if err != nil { 102 return nil, err 103 } 104 // fwiw, I feel bad about this 105 localProjects[project] = target 106 107 return target, nil 108 } 109 110 // Fork creates a user fork of a GitLab project 111 func Fork(project string) (string, error) { 112 if !strings.Contains(project, "/") { 113 return "", errors.New("remote must include namespace") 114 } 115 parts := strings.Split(project, "/") 116 117 // See if a fork already exists 118 target, err := FindProject(parts[1]) 119 if err == nil { 120 return target.SSHURLToRepo, nil 121 } else if err != nil && err != ErrProjectNotFound { 122 return "", err 123 } 124 125 target, err = FindProject(project) 126 if err != nil { 127 return "", err 128 } 129 130 fork, _, err := lab.Projects.ForkProject(target.ID) 131 if err != nil { 132 return "", err 133 } 134 135 return fork.SSHURLToRepo, nil 136 } 137 138 // MRCreate opens a merge request on GitLab 139 func MRCreate(project string, opts *gitlab.CreateMergeRequestOptions) (string, error) { 140 p, err := FindProject(project) 141 if err != nil { 142 return "", err 143 } 144 145 mr, _, err := lab.MergeRequests.CreateMergeRequest(p.ID, opts) 146 if err != nil { 147 return "", err 148 } 149 return mr.WebURL, nil 150 } 151 152 // MRGet retrieves the merge request from GitLab project 153 func MRGet(project string, mrNum int) (*gitlab.MergeRequest, error) { 154 p, err := FindProject(project) 155 if err != nil { 156 return nil, err 157 } 158 159 mr, _, err := lab.MergeRequests.GetMergeRequest(p.ID, mrNum) 160 if err != nil { 161 return nil, err 162 } 163 164 return mr, nil 165 } 166 167 // MRList lists the MRs on a GitLab project 168 func MRList(project string, opts *gitlab.ListProjectMergeRequestsOptions) ([]*gitlab.MergeRequest, error) { 169 p, err := FindProject(project) 170 if err != nil { 171 return nil, err 172 } 173 174 list, _, err := lab.MergeRequests.ListProjectMergeRequests(p.ID, opts) 175 if err != nil { 176 return nil, err 177 } 178 return list, nil 179 } 180 181 // MRClose closes an mr on a GitLab project 182 func MRClose(pid interface{}, id int) error { 183 mr, _, err := lab.MergeRequests.GetMergeRequest(pid, id) 184 if err != nil { 185 return err 186 } 187 if mr.State == "closed" { 188 return fmt.Errorf("mr already closed") 189 } 190 _, _, err = lab.MergeRequests.UpdateMergeRequest(pid, int(id), &gitlab.UpdateMergeRequestOptions{ 191 StateEvent: gitlab.String("close"), 192 }) 193 if err != nil { 194 return err 195 } 196 return nil 197 } 198 199 // MRMerge merges an mr on a GitLab project 200 func MRMerge(pid interface{}, id int) error { 201 _, _, err := lab.MergeRequests.AcceptMergeRequest(pid, int(id), &gitlab.AcceptMergeRequestOptions{ 202 MergeWhenPipelineSucceeds: gitlab.Bool(true), 203 }) 204 if err != nil { 205 return err 206 } 207 return nil 208 } 209 210 // IssueCreate opens a new issue on a GitLab Project 211 func IssueCreate(project string, opts *gitlab.CreateIssueOptions) (string, error) { 212 p, err := FindProject(project) 213 if err != nil { 214 return "", err 215 } 216 217 mr, _, err := lab.Issues.CreateIssue(p.ID, opts) 218 if err != nil { 219 return "", err 220 } 221 return mr.WebURL, nil 222 } 223 224 // IssueGet retrieves the issue information from a GitLab project 225 func IssueGet(project string, issueNum int) (*gitlab.Issue, error) { 226 p, err := FindProject(project) 227 if err != nil { 228 return nil, err 229 } 230 231 issue, _, err := lab.Issues.GetIssue(p.ID, issueNum) 232 if err != nil { 233 return nil, err 234 } 235 236 return issue, nil 237 } 238 239 // IssueList gets a list of issues on a GitLab Project 240 func IssueList(project string, opts *gitlab.ListProjectIssuesOptions) ([]*gitlab.Issue, error) { 241 p, err := FindProject(project) 242 if err != nil { 243 return nil, err 244 } 245 246 list, _, err := lab.Issues.ListProjectIssues(p.ID, opts) 247 if err != nil { 248 return nil, err 249 } 250 return list, nil 251 } 252 253 // IssueClose closes an issue on a GitLab project 254 func IssueClose(pid interface{}, id int) error { 255 _, err := lab.Issues.DeleteIssue(pid, int(id)) 256 if err != nil { 257 return err 258 } 259 return nil 260 } 261 262 // BranchPushed checks if a branch exists on a GitLab project 263 func BranchPushed(pid interface{}, branch string) bool { 264 b, _, err := lab.Branches.GetBranch(pid, branch) 265 if err != nil { 266 return false 267 } 268 return b != nil 269 } 270 271 // ProjectSnippetCreate creates a snippet in a project 272 func ProjectSnippetCreate(pid interface{}, opts *gitlab.CreateProjectSnippetOptions) (*gitlab.Snippet, error) { 273 snip, _, err := lab.ProjectSnippets.CreateSnippet(pid, opts) 274 if err != nil { 275 return nil, err 276 } 277 278 return snip, nil 279 } 280 281 // ProjectSnippetDelete deletes a project snippet 282 func ProjectSnippetDelete(pid interface{}, id int) error { 283 _, err := lab.ProjectSnippets.DeleteSnippet(pid, id) 284 return err 285 } 286 287 // ProjectSnippetList lists snippets on a project 288 func ProjectSnippetList(pid interface{}, opts *gitlab.ListProjectSnippetsOptions) ([]*gitlab.Snippet, error) { 289 snips, _, err := lab.ProjectSnippets.ListSnippets(pid, opts) 290 if err != nil { 291 return nil, err 292 } 293 return snips, nil 294 } 295 296 // SnippetCreate creates a personal snippet 297 func SnippetCreate(opts *gitlab.CreateSnippetOptions) (*gitlab.Snippet, error) { 298 snip, _, err := lab.Snippets.CreateSnippet(opts) 299 if err != nil { 300 return nil, err 301 } 302 303 return snip, nil 304 } 305 306 // SnippetDelete deletes a personal snippet 307 func SnippetDelete(id int) error { 308 _, err := lab.Snippets.DeleteSnippet(id) 309 return err 310 } 311 312 // SnippetList lists snippets on a project 313 func SnippetList(opts *gitlab.ListSnippetsOptions) ([]*gitlab.Snippet, error) { 314 snips, _, err := lab.Snippets.ListSnippets(opts) 315 if err != nil { 316 return nil, err 317 } 318 return snips, nil 319 } 320 321 // Lint validates .gitlab-ci.yml contents 322 func Lint(content string) (bool, error) { 323 lint, _, err := lab.Validate.Lint(content) 324 if err != nil { 325 return false, err 326 } 327 if len(lint.Errors) > 0 { 328 return false, errors.New(strings.Join(lint.Errors, " - ")) 329 } 330 return lint.Status == "valid", nil 331 }