Files
atlantis/e2e/gitlab.go
2025-04-10 13:36:44 -07:00

182 lines
5.3 KiB
Go

// Copyright 2017 HootSuite Media Inc.
//
// Licensed under the Apache License, Version 2.0 (the License);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an AS IS BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Modified hereafter by contributors to runatlantis/atlantis.
package main
import (
"context"
"fmt"
"log"
"os"
"os/exec"
gitlab "gitlab.com/gitlab-org/api/client-go"
)
type GitlabClient struct {
client *gitlab.Client
username string
ownerName string
repoName string
token string
projectId int
// A mapping from branch names to MR IDs
branchToMR map[string]int
}
func NewGitlabClient() *GitlabClient {
gitlabUsername := os.Getenv("ATLANTIS_GITLAB_USER")
if gitlabUsername == "" {
log.Fatalf("ATLANTIS_GITLAB_USER cannot be empty")
}
gitlabToken := os.Getenv("ATLANTIS_GITLAB_TOKEN")
if gitlabToken == "" {
log.Fatalf("ATLANTIS_GITLAB_TOKEN cannot be empty")
}
ownerName := os.Getenv("GITLAB_REPO_OWNER_NAME")
if ownerName == "" {
ownerName = "runatlantis"
}
repoName := os.Getenv("GITLAB_REPO_NAME")
if repoName == "" {
repoName = "atlantis-tests"
}
gitlabClient, err := gitlab.NewClient(gitlabToken)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
project, _, err := gitlabClient.Projects.GetProject(fmt.Sprintf("%s/%s", ownerName, repoName), &gitlab.GetProjectOptions{})
if err != nil {
log.Fatalf("Failed to find project: %v", err)
}
return &GitlabClient{
client: gitlabClient,
username: gitlabUsername,
ownerName: ownerName,
repoName: repoName,
token: gitlabToken,
projectId: project.ID,
branchToMR: make(map[string]int),
}
}
func (g GitlabClient) Clone(cloneDir string) error {
repoURL := fmt.Sprintf("https://%s:%s@gitlab.com/%s/%s.git", g.username, g.token, g.ownerName, g.repoName)
cloneCmd := exec.Command("git", "clone", repoURL, cloneDir)
// git clone the repo
log.Printf("git cloning into %q", cloneDir)
if output, err := cloneCmd.CombinedOutput(); err != nil {
return fmt.Errorf("failed to clone repository: %v: %s", err, string(output))
}
return nil
}
func (g GitlabClient) CreateAtlantisWebhook(ctx context.Context, hookURL string) (int64, error) {
hook, _, err := g.client.Projects.AddProjectHook(g.projectId, &gitlab.AddProjectHookOptions{
URL: &hookURL,
IssuesEvents: gitlab.Ptr(true),
MergeRequestsEvents: gitlab.Ptr(true),
PushEvents: gitlab.Ptr(true),
})
if err != nil {
return 0, err
}
log.Printf("created webhook for %s", hook.URL)
return int64(hook.ID), err
}
func (g GitlabClient) DeleteAtlantisHook(ctx context.Context, hookID int64) error {
_, err := g.client.Projects.DeleteProjectHook(g.projectId, int(hookID))
if err != nil {
return err
}
log.Printf("deleted webhook id %d", hookID)
return nil
}
func (g GitlabClient) CreatePullRequest(ctx context.Context, title, branchName string) (string, int, error) {
mr, _, err := g.client.MergeRequests.CreateMergeRequest(g.projectId, &gitlab.CreateMergeRequestOptions{
Title: gitlab.Ptr(title),
SourceBranch: gitlab.Ptr(branchName),
TargetBranch: gitlab.Ptr("main"),
})
if err != nil {
return "", 0, fmt.Errorf("error while creating new pull request: %v", err)
}
g.branchToMR[branchName] = mr.IID
return mr.WebURL, mr.IID, nil
}
func (g GitlabClient) GetAtlantisStatus(ctx context.Context, branchName string) (string, error) {
pipelineInfos, _, err := g.client.MergeRequests.ListMergeRequestPipelines(g.projectId, g.branchToMR[branchName])
if err != nil {
return "", err
}
// Possible todo: determine which status in the pipeline we care about?
if len(pipelineInfos) != 1 {
return "", fmt.Errorf("unexpected pipelines: %d", len(pipelineInfos))
}
pipelineInfo := pipelineInfos[0]
pipeline, _, err := g.client.Pipelines.GetPipeline(g.projectId, pipelineInfo.ID)
if err != nil {
return "", err
}
return pipeline.Status, nil
}
func (g GitlabClient) ClosePullRequest(ctx context.Context, pullRequestNumber int) error {
// clean up
_, _, err := g.client.MergeRequests.UpdateMergeRequest(g.projectId, pullRequestNumber, &gitlab.UpdateMergeRequestOptions{
StateEvent: gitlab.Ptr("close"),
})
if err != nil {
return fmt.Errorf("error while closing new pull request: %v", err)
}
return nil
}
func (g GitlabClient) DeleteBranch(ctx context.Context, branchName string) error {
_, err := g.client.Branches.DeleteBranch(g.projectId, branchName)
if err != nil {
return fmt.Errorf("error while deleting branch %s: %v", branchName, err)
}
return nil
}
func (g GitlabClient) IsAtlantisInProgress(state string) bool {
// From https://docs.gitlab.com/api/pipelines/
// created, waiting_for_resource, preparing, pending, running, success, failed, canceled, skipped, manual, scheduled
for _, s := range []string{"success", "failed", "canceled", "skipped"} {
if state == s {
return false
}
}
return true
}
func (g GitlabClient) DidAtlantisSucceed(state string) bool {
return state == "success"
}