diff --git a/internal/client/gitlab.go b/internal/client/gitlab.go index a1ab6b956..2d17216b0 100644 --- a/internal/client/gitlab.go +++ b/internal/client/gitlab.go @@ -98,6 +98,24 @@ func (c *gitlabClient) getDefaultBranch(_ *context.Context, repo Repo) (string, return p.DefaultBranch, nil } +// checkBranchExists checks if a branch exists +func (c *gitlabClient) checkBranchExists(_ *context.Context, repo Repo, branch string) (bool, error) { + projectID := repo.Name + if repo.Owner != "" { + projectID = repo.Owner + "/" + projectID + } + + // Verify if branch exists + _, res, err := c.client.Branches.GetBranch(projectID, branch) + if err != nil && res.StatusCode != 404 { + log.WithError(err). + Error("error verify branch existence") + return false, err + } + + return res.StatusCode != 404, nil +} + // CloseMilestone closes a given milestone. func (c *gitlabClient) CloseMilestone(_ *context.Context, repo Repo, title string) error { milestone, err := c.getMilestoneByTitle(repo, title) @@ -135,57 +153,72 @@ func (c *gitlabClient) CreateFile( commitAuthor config.CommitAuthor, repo Repo, content []byte, // the content of the formula.rb - path, // the path to the formula.rb + fileName, // the path to the formula.rb message string, // the commit msg ) error { - fileName := path - projectID := repo.Name if repo.Owner != "" { projectID = repo.Owner + "/" + projectID } - // Use the project default branch if we can get it...otherwise, just use - // 'master' - var branch, ref string + log. + WithField("projectID", projectID). + Debug("project id") + + var branch, defaultBranch string + var branchExists bool var err error // Use the branch if given one if repo.Branch != "" { branch = repo.Branch + branchExists, err = c.checkBranchExists(ctx, repo, branch) + if err != nil { + return err + } + + // Retrieving default branch because we need it for `start_branch` + if !branchExists { + defaultBranch, err = c.getDefaultBranch(ctx, repo) + if err != nil { + return err + } + } + + log. + WithField("projectID", projectID). + WithField("branch", branch). + WithField("branchExists", branchExists). + Debug("using given branch") } else { // Try to get the default branch from the Git provider branch, err = c.getDefaultBranch(ctx, repo) if err != nil { - // Fall back to 'master' 😭 - log. - WithField("fileName", fileName). - WithField("projectID", projectID). - WithField("requestedBranch", branch). - WithError(err). - Warn("error checking for default branch, using master") - ref = "master" - branch = "master" + return err } + + defaultBranch = branch + branchExists = true + + log. + WithField("projectID", projectID). + WithField("branch", branch). + Debug("no branch given, using default branch") } - ref = branch - opts := &gitlab.GetFileOptions{Ref: &ref} - castedContent := string(content) - log. - WithField("projectID", projectID). - WithField("ref", ref). - WithField("branch", branch). - Debug("projectID at brew") - - log. - WithField("projectID", projectID). - Info("pushing") + // If the branch doesn't exist, we need to check the default branch + // because that's what we use as `start_branch` later if the file needs + // to be created. + opts := &gitlab.GetFileOptions{Ref: &defaultBranch} + if branchExists { + opts.Ref = &branch + } + // Check if the file already exists _, res, err := c.client.RepositoryFiles.GetFile(projectID, fileName, opts) if err != nil && (res == nil || res.StatusCode != 404) { log := log. WithField("fileName", fileName). - WithField("ref", ref). + WithField("branch", branch). WithField("projectID", projectID) if res != nil { log = log.WithField("statusCode", res.StatusCode) @@ -196,24 +229,34 @@ func (c *gitlabClient) CreateFile( } log. - WithField("fileName", fileName). - WithField("branch", branch). WithField("projectID", projectID). - Debug("found already existing brew formula file") + WithField("branch", branch). + WithField("fileName", fileName). + Info("pushing file") + + stringContents := string(content) if res.StatusCode == 404 { + // Create a new file because it's not already there log. - WithField("fileName", fileName). - WithField("ref", ref). WithField("projectID", projectID). - Debug("creating brew formula") + WithField("branch", branch). + WithField("fileName", fileName). + Debug("file doesn't exist, creating it") + createOpts := &gitlab.CreateFileOptions{ AuthorName: &commitAuthor.Name, AuthorEmail: &commitAuthor.Email, - Content: &castedContent, + Content: &stringContents, Branch: &branch, CommitMessage: &message, } + + // Branch not found, thus Gitlab requires a "start branch" to create the file + if !branchExists { + createOpts.StartBranch = &defaultBranch + } + fileInfo, res, err := c.client.RepositoryFiles.CreateFile(projectID, fileName, createOpts) if err != nil { log := log. @@ -237,19 +280,26 @@ func (c *gitlabClient) CreateFile( return nil } + // Update the existing file log. WithField("fileName", fileName). - WithField("ref", ref). + WithField("branch", branch). WithField("projectID", projectID). - Debug("updating brew formula") + Debug("file exists, updating it") + updateOpts := &gitlab.UpdateFileOptions{ AuthorName: &commitAuthor.Name, AuthorEmail: &commitAuthor.Email, - Content: &castedContent, + Content: &stringContents, Branch: &branch, CommitMessage: &message, } + // Branch not found, thus Gitlab requires a "start branch" to update the file + if !branchExists { + updateOpts.StartBranch = &defaultBranch + } + updateFileInfo, res, err := c.client.RepositoryFiles.UpdateFile(projectID, fileName, updateOpts) if err != nil { log := log. @@ -260,7 +310,7 @@ func (c *gitlabClient) CreateFile( log = log.WithField("statusCode", res.StatusCode) } log.WithError(err). - Error("error updating brew formula file") + Error("error updating file") return err } @@ -272,7 +322,7 @@ func (c *gitlabClient) CreateFile( if res != nil { log = log.WithField("statusCode", res.StatusCode) } - log.Debug("updated brew formula file") + log.Debug("updated file") return nil } diff --git a/internal/client/gitlab_test.go b/internal/client/gitlab_test.go index a96c357fc..f2053a5c7 100644 --- a/internal/client/gitlab_test.go +++ b/internal/client/gitlab_test.go @@ -488,19 +488,52 @@ func TestGitLabChangelog(t *testing.T) { func TestGitLabCreateFile(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Handle the test where we know the branch + // Handle the test where we know the branch and it exists + if strings.HasSuffix(r.URL.Path, "projects/someone/something/repository/branches/somebranch") { + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, "{}") + return + } if strings.HasSuffix(r.URL.Path, "projects/someone/something/repository/files/newfile.txt") { _, err := io.Copy(w, strings.NewReader(`{ "file_path": "newfile.txt", "branch": "somebranch" }`)) require.NoError(t, err) return } + // Handle the test where we detect the branch + if strings.HasSuffix(r.URL.Path, "projects/someone/something") { + _, err := io.Copy(w, strings.NewReader(`{ "default_branch": "main" }`)) + require.NoError(t, err) + return + } if strings.HasSuffix(r.URL.Path, "projects/someone/something/repository/files/newfile-in-default.txt") { _, err := io.Copy(w, strings.NewReader(`{ "file_path": "newfile.txt", "branch": "main" }`)) require.NoError(t, err) return } + + // Handle the test where the branch doesn't exist already + if strings.HasSuffix(r.URL.Path, "projects/someone/something/repository/branches/non-existing-branch") { + w.WriteHeader(http.StatusNotFound) + return + } + if strings.HasSuffix(r.URL.Path, "projects/someone/something/repository/files/newfile-on-new-branch.txt") { + if r.Method == "POST" { + var resBody map[string]string + require.NoError(t, json.NewDecoder(r.Body).Decode(&resBody)) + require.Equal(t, "master", resBody["start_branch"]) + } + _, err := io.Copy(w, strings.NewReader(`{"file_path":"newfile-on-new-branch.txt","branch":"non-existing-branch"}`)) + require.NoError(t, err) + return + } + // Handle the case with a projectID + if strings.HasSuffix(r.URL.Path, "projects/123456789/repository/branches/main") { + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, "{}") + return + } if strings.HasSuffix(r.URL.Path, "projects/123456789/repository/files/newfile-projectID.txt") { _, err := io.Copy(w, strings.NewReader(`{ "file_path": "newfile-projectID.txt", "branch": "main" }`)) require.NoError(t, err) @@ -530,7 +563,7 @@ func TestGitLabCreateFile(t *testing.T) { client, err := newGitLab(ctx, "test-token") require.NoError(t, err) - // Test using an arbitrary branch + // Test using an arbitrary existing branch repo := Repo{ Owner: "someone", Name: "something", @@ -550,6 +583,16 @@ func TestGitLabCreateFile(t *testing.T) { err = client.CreateFile(ctx, config.CommitAuthor{Name: repo.Owner}, repo, []byte("Hello there"), "newfile-in-default.txt", "test: test commit") require.NoError(t, err) + // Test creating a new branch + repo = Repo{ + Owner: "someone", + Name: "something", + Branch: "non-existing-branch", + } + + err = client.CreateFile(ctx, config.CommitAuthor{Name: repo.Owner}, repo, []byte("Hello there"), "newfile-on-new-branch.txt", "test: test commit") + require.NoError(t, err) + // Test using projectID repo = Repo{ Name: "123456789",