1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-03-29 21:47:01 +02:00

fix(gitlab): Use start_branch for file creation if branch doesn't exist already (#4792)

Creating files on a new branch is only possible with the API if
`start_branch`
is given otherwise the API returns:

> You can only create or edit files when you are on a branch

Fixes https://github.com/goreleaser/goreleaser/issues/4543

---------

Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
Matthias Baur 2024-04-23 14:01:23 +02:00 committed by GitHub
parent 4015fa32bc
commit fd40f5d772
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 135 additions and 42 deletions

View File

@ -98,6 +98,24 @@ func (c *gitlabClient) getDefaultBranch(_ *context.Context, repo Repo) (string,
return p.DefaultBranch, nil 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. // CloseMilestone closes a given milestone.
func (c *gitlabClient) CloseMilestone(_ *context.Context, repo Repo, title string) error { func (c *gitlabClient) CloseMilestone(_ *context.Context, repo Repo, title string) error {
milestone, err := c.getMilestoneByTitle(repo, title) milestone, err := c.getMilestoneByTitle(repo, title)
@ -135,57 +153,72 @@ func (c *gitlabClient) CreateFile(
commitAuthor config.CommitAuthor, commitAuthor config.CommitAuthor,
repo Repo, repo Repo,
content []byte, // the content of the formula.rb 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 message string, // the commit msg
) error { ) error {
fileName := path
projectID := repo.Name projectID := repo.Name
if repo.Owner != "" { if repo.Owner != "" {
projectID = repo.Owner + "/" + projectID projectID = repo.Owner + "/" + projectID
} }
// Use the project default branch if we can get it...otherwise, just use log.
// 'master' WithField("projectID", projectID).
var branch, ref string Debug("project id")
var branch, defaultBranch string
var branchExists bool
var err error var err error
// Use the branch if given one // Use the branch if given one
if repo.Branch != "" { if repo.Branch != "" {
branch = 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 { } else {
// Try to get the default branch from the Git provider // Try to get the default branch from the Git provider
branch, err = c.getDefaultBranch(ctx, repo) branch, err = c.getDefaultBranch(ctx, repo)
if err != nil { if err != nil {
// Fall back to 'master' 😭 return err
log.
WithField("fileName", fileName).
WithField("projectID", projectID).
WithField("requestedBranch", branch).
WithError(err).
Warn("error checking for default branch, using master")
ref = "master"
branch = "master"
} }
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. // If the branch doesn't exist, we need to check the default branch
WithField("projectID", projectID). // because that's what we use as `start_branch` later if the file needs
WithField("ref", ref). // to be created.
WithField("branch", branch). opts := &gitlab.GetFileOptions{Ref: &defaultBranch}
Debug("projectID at brew") if branchExists {
opts.Ref = &branch
log. }
WithField("projectID", projectID).
Info("pushing")
// Check if the file already exists
_, res, err := c.client.RepositoryFiles.GetFile(projectID, fileName, opts) _, res, err := c.client.RepositoryFiles.GetFile(projectID, fileName, opts)
if err != nil && (res == nil || res.StatusCode != 404) { if err != nil && (res == nil || res.StatusCode != 404) {
log := log. log := log.
WithField("fileName", fileName). WithField("fileName", fileName).
WithField("ref", ref). WithField("branch", branch).
WithField("projectID", projectID) WithField("projectID", projectID)
if res != nil { if res != nil {
log = log.WithField("statusCode", res.StatusCode) log = log.WithField("statusCode", res.StatusCode)
@ -196,24 +229,34 @@ func (c *gitlabClient) CreateFile(
} }
log. log.
WithField("fileName", fileName).
WithField("branch", branch).
WithField("projectID", projectID). 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 { if res.StatusCode == 404 {
// Create a new file because it's not already there
log. log.
WithField("fileName", fileName).
WithField("ref", ref).
WithField("projectID", projectID). WithField("projectID", projectID).
Debug("creating brew formula") WithField("branch", branch).
WithField("fileName", fileName).
Debug("file doesn't exist, creating it")
createOpts := &gitlab.CreateFileOptions{ createOpts := &gitlab.CreateFileOptions{
AuthorName: &commitAuthor.Name, AuthorName: &commitAuthor.Name,
AuthorEmail: &commitAuthor.Email, AuthorEmail: &commitAuthor.Email,
Content: &castedContent, Content: &stringContents,
Branch: &branch, Branch: &branch,
CommitMessage: &message, 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) fileInfo, res, err := c.client.RepositoryFiles.CreateFile(projectID, fileName, createOpts)
if err != nil { if err != nil {
log := log. log := log.
@ -237,19 +280,26 @@ func (c *gitlabClient) CreateFile(
return nil return nil
} }
// Update the existing file
log. log.
WithField("fileName", fileName). WithField("fileName", fileName).
WithField("ref", ref). WithField("branch", branch).
WithField("projectID", projectID). WithField("projectID", projectID).
Debug("updating brew formula") Debug("file exists, updating it")
updateOpts := &gitlab.UpdateFileOptions{ updateOpts := &gitlab.UpdateFileOptions{
AuthorName: &commitAuthor.Name, AuthorName: &commitAuthor.Name,
AuthorEmail: &commitAuthor.Email, AuthorEmail: &commitAuthor.Email,
Content: &castedContent, Content: &stringContents,
Branch: &branch, Branch: &branch,
CommitMessage: &message, 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) updateFileInfo, res, err := c.client.RepositoryFiles.UpdateFile(projectID, fileName, updateOpts)
if err != nil { if err != nil {
log := log. log := log.
@ -260,7 +310,7 @@ func (c *gitlabClient) CreateFile(
log = log.WithField("statusCode", res.StatusCode) log = log.WithField("statusCode", res.StatusCode)
} }
log.WithError(err). log.WithError(err).
Error("error updating brew formula file") Error("error updating file")
return err return err
} }
@ -272,7 +322,7 @@ func (c *gitlabClient) CreateFile(
if res != nil { if res != nil {
log = log.WithField("statusCode", res.StatusCode) log = log.WithField("statusCode", res.StatusCode)
} }
log.Debug("updated brew formula file") log.Debug("updated file")
return nil return nil
} }

View File

@ -488,19 +488,52 @@ func TestGitLabChangelog(t *testing.T) {
func TestGitLabCreateFile(t *testing.T) { func TestGitLabCreateFile(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 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") { 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" }`)) _, err := io.Copy(w, strings.NewReader(`{ "file_path": "newfile.txt", "branch": "somebranch" }`))
require.NoError(t, err) require.NoError(t, err)
return return
} }
// Handle the test where we detect the branch // 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") { 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" }`)) _, err := io.Copy(w, strings.NewReader(`{ "file_path": "newfile.txt", "branch": "main" }`))
require.NoError(t, err) require.NoError(t, err)
return 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 // 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") { 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" }`)) _, err := io.Copy(w, strings.NewReader(`{ "file_path": "newfile-projectID.txt", "branch": "main" }`))
require.NoError(t, err) require.NoError(t, err)
@ -530,7 +563,7 @@ func TestGitLabCreateFile(t *testing.T) {
client, err := newGitLab(ctx, "test-token") client, err := newGitLab(ctx, "test-token")
require.NoError(t, err) require.NoError(t, err)
// Test using an arbitrary branch // Test using an arbitrary existing branch
repo := Repo{ repo := Repo{
Owner: "someone", Owner: "someone",
Name: "something", 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") err = client.CreateFile(ctx, config.CommitAuthor{Name: repo.Owner}, repo, []byte("Hello there"), "newfile-in-default.txt", "test: test commit")
require.NoError(t, err) 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 // Test using projectID
repo = Repo{ repo = Repo{
Name: "123456789", Name: "123456789",