gitlab.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. package gitlab
  2. import (
  3. "net/url"
  4. "strings"
  5. "go-common/app/tool/saga/model"
  6. "go-common/library/log"
  7. "github.com/pkg/errors"
  8. "github.com/xanzy/go-gitlab"
  9. )
  10. // Gitlab def
  11. type Gitlab struct {
  12. url string
  13. token string
  14. client *gitlab.Client
  15. }
  16. // New new gitlab structure
  17. func New(url string, token string) (g *Gitlab) {
  18. g = &Gitlab{
  19. url: url,
  20. token: token,
  21. client: gitlab.NewClient(nil, token),
  22. }
  23. g.client.SetBaseURL(url)
  24. return
  25. }
  26. // HostToken ...
  27. func (g *Gitlab) HostToken() (host string, token string, err error) {
  28. var u *url.URL
  29. if u, err = url.Parse(g.url); err != nil {
  30. return
  31. }
  32. host = u.Host
  33. token = g.token
  34. return
  35. }
  36. // LastGreenCommit get last green pipeline commit
  37. func (g *Gitlab) LastGreenCommit(ciProjectID string, ciCommitRefName string) (commit string, err error) {
  38. var (
  39. status = gitlab.Success
  40. pipelines gitlab.PipelineList
  41. )
  42. opt := &gitlab.ListProjectPipelinesOptions{
  43. Status: &status,
  44. Ref: gitlab.String(ciCommitRefName),
  45. }
  46. if pipelines, _, err = g.client.Pipelines.ListProjectPipelines(ciProjectID, opt); err != nil {
  47. return
  48. }
  49. if len(pipelines) == 0 {
  50. return
  51. }
  52. commit = pipelines[0].Sha
  53. return
  54. }
  55. // AcceptMR accept Merge request
  56. func (g *Gitlab) AcceptMR(projID int, mrIID int, msg string) (state string, err error) {
  57. var (
  58. opt = &gitlab.AcceptMergeRequestOptions{
  59. ShouldRemoveSourceBranch: gitlab.Bool(true),
  60. MergeCommitMessage: gitlab.String(msg),
  61. }
  62. mergeRequest *gitlab.MergeRequest
  63. )
  64. if mergeRequest, _, err = g.client.MergeRequests.AcceptMergeRequest(projID, mrIID, opt); err != nil {
  65. err = errors.Wrapf(err, "AcceptMergeRequest(%d,%d)", projID, mrIID)
  66. return
  67. }
  68. state = mergeRequest.State
  69. return
  70. }
  71. // CloseMR close merge request
  72. func (g *Gitlab) CloseMR(projID int, mrIID int) (err error) {
  73. var (
  74. opt = &gitlab.UpdateMergeRequestOptions{
  75. StateEvent: gitlab.String("close"),
  76. }
  77. )
  78. if _, _, err = g.client.MergeRequests.UpdateMergeRequest(projID, mrIID, opt); err != nil {
  79. err = errors.Wrapf(err, "CloseMR(%d,%d)", projID, mrIID)
  80. }
  81. return
  82. }
  83. // CreateMRNote create note to merge request
  84. func (g *Gitlab) CreateMRNote(projID int, mrIID int, content string) (noteID int, err error) {
  85. var (
  86. opt = &gitlab.CreateMergeRequestNoteOptions{
  87. Body: gitlab.String(content),
  88. }
  89. note *gitlab.Note
  90. )
  91. if note, _, err = g.client.Notes.CreateMergeRequestNote(projID, mrIID, opt); err != nil {
  92. err = errors.Wrapf(err, "CreateMergeRequestNote(%d,%d)", projID, mrIID)
  93. return
  94. }
  95. noteID = note.ID
  96. return
  97. }
  98. // UpdateMRNote update the specified merge request note
  99. func (g *Gitlab) UpdateMRNote(projID int, mrIID int, noteID int, content string) (err error) {
  100. var (
  101. opt = &gitlab.UpdateMergeRequestNoteOptions{
  102. Body: gitlab.String(content),
  103. }
  104. )
  105. if _, _, err = g.client.Notes.UpdateMergeRequestNote(projID, mrIID, noteID, opt); err != nil {
  106. err = errors.Wrapf(err, "UpdateMergeRequestNote(%d,%d,%d)", projID, mrIID, noteID)
  107. return
  108. }
  109. return
  110. }
  111. // DeleteMRNote delete the specified note for merge request
  112. func (g *Gitlab) DeleteMRNote(projID int, mrIID int, noteID int) (err error) {
  113. if _, err = g.client.Notes.DeleteMergeRequestNote(projID, mrIID, noteID); err != nil {
  114. err = errors.Wrapf(err, "DeleteMergeRequestNote(%d,%d,%d)", projID, mrIID, noteID)
  115. }
  116. return
  117. }
  118. // UserName get user name for user id
  119. func (g *Gitlab) UserName(userID int) (userName string, err error) {
  120. var (
  121. user *gitlab.User
  122. )
  123. if user, _, err = g.client.Users.GetUser(userID); err != nil {
  124. err = errors.WithStack(err)
  125. return
  126. }
  127. userName = user.Username
  128. return
  129. }
  130. // Triggers get pipeline triggers
  131. func (g *Gitlab) Triggers(projectID int) (triggers []*gitlab.PipelineTrigger, err error) {
  132. var (
  133. opt = &gitlab.ListPipelineTriggersOptions{}
  134. )
  135. if triggers, _, err = g.client.PipelineTriggers.ListPipelineTriggers(projectID, opt); err != nil {
  136. err = errors.Wrapf(err, "ListPipelineTriggers (projectID: %d)", projectID)
  137. return nil, err
  138. }
  139. return triggers, nil
  140. }
  141. // CreateTrigger create the trigger to trigger pipeline
  142. func (g *Gitlab) CreateTrigger(projectID int) (trigger *gitlab.PipelineTrigger, err error) {
  143. var (
  144. opt = &gitlab.AddPipelineTriggerOptions{
  145. Description: gitlab.String("gitlab-ci-build-on-merge-request"),
  146. }
  147. )
  148. if trigger, _, err = g.client.PipelineTriggers.AddPipelineTrigger(projectID, opt); err != nil {
  149. err = errors.Wrapf(err, "CreateTrigger (projectID: %d)", projectID)
  150. return nil, err
  151. }
  152. return trigger, nil
  153. }
  154. // TakeOwnership take ownership of the trigger
  155. func (g *Gitlab) TakeOwnership(projectID int, triggerID int) (trigger *gitlab.PipelineTrigger, err error) {
  156. if trigger, _, err = g.client.PipelineTriggers.TakeOwnershipOfPipelineTrigger(projectID, triggerID); err != nil {
  157. err = errors.Wrapf(err, "TakeOwnershipOfPipelineTrigger (projectID: %d, triggerID: %d)", projectID, triggerID)
  158. return nil, err
  159. }
  160. return trigger, nil
  161. }
  162. // TriggerPipeline trigger the pipeline
  163. func (g *Gitlab) TriggerPipeline(projectID int, ref string, token string) (pipeline *gitlab.Pipeline, err error) {
  164. var (
  165. opt = &gitlab.RunPipelineTriggerOptions{
  166. Ref: gitlab.String(ref),
  167. Token: gitlab.String(token),
  168. }
  169. )
  170. if pipeline, _, err = g.client.PipelineTriggers.RunPipelineTrigger(projectID, opt); err != nil {
  171. err = errors.Wrapf(err, "RunPipelineTrigger (projectID: %d, ref: %s, token: %s)", projectID, ref, token)
  172. return nil, err
  173. }
  174. return pipeline, nil
  175. }
  176. // AwardEmojiUsernames get the username who gave the emoji
  177. func (g *Gitlab) AwardEmojiUsernames(projID int, mrIID int) (usernames []string, err error) {
  178. var (
  179. opt = &gitlab.ListAwardEmojiOptions{
  180. Page: 1,
  181. PerPage: 100,
  182. }
  183. awards []*gitlab.AwardEmoji
  184. )
  185. if awards, _, err = g.client.AwardEmoji.ListMergeRequestAwardEmoji(projID, mrIID, opt); err != nil {
  186. return
  187. }
  188. for _, a := range awards {
  189. if a.Name == "thumbsup" {
  190. usernames = append(usernames, a.User.Username)
  191. }
  192. }
  193. return
  194. }
  195. // AuditProjects audit all the given projects
  196. func (g *Gitlab) AuditProjects(repos []*model.Repo, hooks []*model.WebHook) (err error) {
  197. var (
  198. repo *model.Repo
  199. projID int
  200. )
  201. log.Info("AuditProjects start")
  202. for _, repo = range repos {
  203. log.Info("AuditProjects project [%s]", repo.Config.URL)
  204. if projID, err = g.ProjectID(repo.Config.URL); err != nil {
  205. log.Error("Failed to get project ID [%s], err: %+v", repo.Config.URL, err)
  206. continue
  207. }
  208. if err = g.AuditProjectHooks(projID, hooks); err != nil {
  209. log.Error("Failed to get hooks [%s], err: %+v", repo.Config.URL, err)
  210. continue
  211. }
  212. log.Info("AuditProjects project [%s]", repo.Config.URL)
  213. }
  214. log.Info("AuditProjects end")
  215. return
  216. }
  217. // AuditProjectHooks check and add or edit the project webhooks
  218. func (g *Gitlab) AuditProjectHooks(projID int, hooks []*model.WebHook) (err error) {
  219. var (
  220. webHooks []*gitlab.ProjectHook
  221. h *model.WebHook
  222. )
  223. if webHooks, err = g.ListProjectHook(projID); err != nil {
  224. return
  225. }
  226. // 配置文件中的webhook是否配置在项目中
  227. inWebHooks := func(h *model.WebHook) (in bool, ph *gitlab.ProjectHook) {
  228. for _, ph = range webHooks {
  229. if h.URL == ph.URL {
  230. in = true
  231. return
  232. }
  233. }
  234. in = false
  235. return
  236. }
  237. // 找到的webhook是否和配置文件中webhook配置相等
  238. equalHook := func(h *model.WebHook, ph *gitlab.ProjectHook) (equal bool) {
  239. if h.PushEvents == ph.PushEvents && h.IssuesEvents == ph.IssuesEvents &&
  240. h.ConfidentialIssuesEvents == ph.ConfidentialIssuesEvents && h.MergeRequestsEvents == ph.MergeRequestsEvents &&
  241. h.TagPushEvents == ph.TagPushEvents && h.NoteEvents == ph.NoteEvents &&
  242. h.JobEvents == ph.JobEvents && h.PipelineEvents == ph.PipelineEvents &&
  243. h.WikiPageEvents == ph.WikiPageEvents && !ph.EnableSSLVerification {
  244. return true
  245. }
  246. return false
  247. }
  248. // 配置的webhook与查询出来的对比,如果存在,则比较属性,属性不同,则修改;不存在,则创建
  249. for _, h = range hooks {
  250. if in, ph := inWebHooks(h); in {
  251. if !equalHook(h, ph) {
  252. if err = g.EditProjectHook(projID, ph.ID, h); err != nil {
  253. return
  254. }
  255. }
  256. } else {
  257. if err = g.AddProjectHook(projID, h); err != nil {
  258. return
  259. }
  260. }
  261. }
  262. return
  263. }
  264. // AddProjectHook add project hook for the project
  265. func (g *Gitlab) AddProjectHook(projID int, hook *model.WebHook) (err error) {
  266. var (
  267. opt = &gitlab.AddProjectHookOptions{
  268. URL: &hook.URL,
  269. PushEvents: &hook.PushEvents,
  270. IssuesEvents: &hook.IssuesEvents,
  271. ConfidentialIssuesEvents: &hook.ConfidentialIssuesEvents,
  272. MergeRequestsEvents: &hook.MergeRequestsEvents,
  273. TagPushEvents: &hook.TagPushEvents,
  274. NoteEvents: &hook.NoteEvents,
  275. JobEvents: &hook.JobEvents,
  276. PipelineEvents: &hook.PipelineEvents,
  277. WikiPageEvents: &hook.WikiPageEvents,
  278. EnableSSLVerification: gitlab.Bool(false),
  279. }
  280. )
  281. if _, _, err = g.client.Projects.AddProjectHook(projID, opt); err != nil {
  282. return
  283. }
  284. return
  285. }
  286. // ListProjectHook list all the webhook for the project
  287. func (g *Gitlab) ListProjectHook(projID int) (hooks []*gitlab.ProjectHook, err error) {
  288. var (
  289. opt = &gitlab.ListProjectHooksOptions{
  290. Page: 1,
  291. PerPage: 100,
  292. }
  293. )
  294. if hooks, _, err = g.client.Projects.ListProjectHooks(projID, opt); err != nil {
  295. return
  296. }
  297. return
  298. }
  299. // EditProjectHook edit the specified webhook for the project
  300. func (g *Gitlab) EditProjectHook(projID int, hookID int, hook *model.WebHook) (err error) {
  301. var (
  302. opt = &gitlab.EditProjectHookOptions{
  303. URL: &hook.URL,
  304. PushEvents: &hook.PushEvents,
  305. IssuesEvents: &hook.IssuesEvents,
  306. ConfidentialIssuesEvents: &hook.ConfidentialIssuesEvents,
  307. MergeRequestsEvents: &hook.MergeRequestsEvents,
  308. TagPushEvents: &hook.TagPushEvents,
  309. NoteEvents: &hook.NoteEvents,
  310. JobEvents: &hook.JobEvents,
  311. PipelineEvents: &hook.PipelineEvents,
  312. WikiPageEvents: &hook.WikiPageEvents,
  313. EnableSSLVerification: gitlab.Bool(false),
  314. }
  315. )
  316. if _, _, err = g.client.Projects.EditProjectHook(projID, hookID, opt); err != nil {
  317. return
  318. }
  319. return
  320. }
  321. // DeletePojectHook delete the specified hook for the project
  322. func (g *Gitlab) DeletePojectHook(projID int, hookID int) (err error) {
  323. if _, err = g.client.Projects.DeleteProjectHook(projID, hookID); err != nil {
  324. return
  325. }
  326. return
  327. }
  328. // ProjectID get project id by project URL-encoded name
  329. func (g *Gitlab) ProjectID(url string) (projID int, err error) {
  330. var (
  331. projectName = url[strings.LastIndex(url, ":")+1 : strings.LastIndex(url, ".git")]
  332. project *gitlab.Project
  333. )
  334. if project, _, err = g.client.Projects.GetProject(projectName); err != nil {
  335. return
  336. }
  337. projID = project.ID
  338. return
  339. }
  340. // MRPipelineStatus query PipelineState for mr
  341. func (g *Gitlab) MRPipelineStatus(projID int, mrIID int) (id int, status string, err error) {
  342. var pipelineList gitlab.PipelineList
  343. if pipelineList, _, err = g.client.MergeRequests.ListMergeRequestPipelines(projID, mrIID); err != nil {
  344. return
  345. }
  346. if len(pipelineList) == 0 {
  347. status = model.PipelineSuccess
  348. return
  349. }
  350. id = pipelineList[0].ID
  351. status = pipelineList[0].Status
  352. return
  353. }
  354. // LastPipeLineState query Last PipeLineState
  355. func (g *Gitlab) LastPipeLineState(projID int, ciCommitRefName string) (state bool, err error) {
  356. var pipelines gitlab.PipelineList
  357. opt := &gitlab.ListProjectPipelinesOptions{
  358. Ref: gitlab.String(ciCommitRefName),
  359. }
  360. state = true
  361. if pipelines, _, err = g.client.Pipelines.ListProjectPipelines(projID, opt); err != nil {
  362. return
  363. }
  364. if len(pipelines) < 2 {
  365. return
  366. }
  367. state = pipelines[1].Status == model.PipelineSuccess
  368. return
  369. }
  370. // MergeStatus query MergeStatus
  371. func (g *Gitlab) MergeStatus(projID int, mrIID int) (wip bool, state string, status string, err error) {
  372. var mergerequest *gitlab.MergeRequest
  373. if mergerequest, _, err = g.client.MergeRequests.GetMergeRequest(projID, mrIID); err != nil {
  374. return
  375. }
  376. state = mergerequest.State
  377. status = mergerequest.MergeStatus
  378. wip = mergerequest.WorkInProgress
  379. return
  380. }
  381. // MergeLabels get Merge request labels
  382. func (g *Gitlab) MergeLabels(projID int, mrIID int) (labels []string, err error) {
  383. var mergerequest *gitlab.MergeRequest
  384. if mergerequest, _, err = g.client.MergeRequests.GetMergeRequest(projID, mrIID); err != nil {
  385. return
  386. }
  387. labels = mergerequest.Labels
  388. return
  389. }
  390. // PlusUsernames get the username who +1
  391. func (g *Gitlab) PlusUsernames(projID int, mrIID int) (usernames []string, err error) {
  392. var (
  393. opt = &gitlab.ListMergeRequestNotesOptions{
  394. Page: 1,
  395. PerPage: 100,
  396. }
  397. notes []*gitlab.Note
  398. exist bool
  399. )
  400. if notes, _, err = g.client.Notes.ListMergeRequestNotes(projID, mrIID, opt); err != nil {
  401. return
  402. }
  403. for _, note := range notes {
  404. if (strings.TrimSpace(note.Body) == model.SagaCommandPlusOne) || (strings.TrimSpace(note.Body) == model.SagaCommandPlusOne1) {
  405. exist = false
  406. for _, user := range usernames {
  407. if user == note.Author.Username {
  408. exist = true
  409. break
  410. }
  411. }
  412. if !exist {
  413. usernames = append(usernames, note.Author.Username)
  414. }
  415. }
  416. }
  417. return
  418. }
  419. // CompareDiff ...
  420. func (g *Gitlab) CompareDiff(projID int, from string, to string) (files []string, err error) {
  421. var (
  422. opt = &gitlab.CompareOptions{
  423. From: &from,
  424. To: &to,
  425. }
  426. compare *gitlab.Compare
  427. )
  428. if compare, _, err = g.client.Repositories.Compare(projID, opt); err != nil {
  429. return
  430. }
  431. for _, diff := range compare.Diffs {
  432. if diff.NewFile {
  433. files = append(files, diff.NewPath)
  434. } else {
  435. files = append(files, diff.OldPath)
  436. }
  437. }
  438. return
  439. }
  440. // CommitDiff ...
  441. func (g *Gitlab) CommitDiff(projID int, sha string) (files []string, err error) {
  442. var (
  443. diffs []*gitlab.Diff
  444. opt = &gitlab.GetCommitDiffOptions{
  445. Page: 1,
  446. PerPage: 100,
  447. }
  448. )
  449. if diffs, _, err = g.client.Commits.GetCommitDiff(projID, sha, opt); err != nil {
  450. return
  451. }
  452. for _, diff := range diffs {
  453. if diff.NewFile {
  454. files = append(files, diff.NewPath)
  455. } else {
  456. files = append(files, diff.OldPath)
  457. }
  458. }
  459. return
  460. }
  461. // MRChanges ...
  462. func (g *Gitlab) MRChanges(projID, mrIID int) (changeFiles []string, deleteFiles []string, err error) {
  463. var (
  464. mr *gitlab.MergeRequest
  465. )
  466. if mr, _, err = g.client.MergeRequests.GetMergeRequestChanges(projID, mrIID); err != nil {
  467. return
  468. }
  469. for _, c := range mr.Changes {
  470. if c.RenamedFile {
  471. deleteFiles = append(deleteFiles, c.OldPath)
  472. changeFiles = append(changeFiles, c.NewPath)
  473. } else if c.DeletedFile {
  474. deleteFiles = append(deleteFiles, c.OldPath)
  475. } else if c.NewFile {
  476. changeFiles = append(changeFiles, c.NewPath)
  477. } else {
  478. changeFiles = append(changeFiles, c.OldPath)
  479. }
  480. }
  481. return
  482. }
  483. // RepoRawFile ...
  484. func (g *Gitlab) RepoRawFile(projID int, branch string, filename string) (content []byte, err error) {
  485. var (
  486. opt = &gitlab.GetRawFileOptions{
  487. Ref: &branch,
  488. }
  489. )
  490. if content, _, err = g.client.RepositoryFiles.GetRawFile(projID, filename, opt); err != nil {
  491. return
  492. }
  493. return
  494. }