fix(gitlab): use project path wherever possible

Turns out that all we need is the path, and not the project id. The path
is way more user friendly, and we can easily get it from a CI variable
or combine it from the namespace & project name.
This commit is contained in:
Julian Tölle 2024-09-08 20:44:34 +02:00
parent 634eac3b76
commit a9080098ee
2 changed files with 22 additions and 28 deletions

View file

@ -62,8 +62,7 @@ func run(cmd *cobra.Command, _ []string) error {
logger.DebugContext(ctx, "using forge GitLab") logger.DebugContext(ctx, "using forge GitLab")
f, err = gitlab.New(logger, &gitlab.Options{ f, err = gitlab.New(logger, &gitlab.Options{
Options: forgeOptions, Options: forgeOptions,
Path: flagOwner, Path: fmt.Sprintf("%s/%s", flagOwner, flagRepo),
Repo: flagRepo,
}) })
if err != nil { if err != nil {
logger.ErrorContext(ctx, "failed to create client", "err", err) logger.ErrorContext(ctx, "failed to create client", "err", err)

View file

@ -6,7 +6,6 @@ import (
"log/slog" "log/slog"
"os" "os"
"slices" "slices"
"strconv"
"strings" "strings"
"github.com/blang/semver/v4" "github.com/blang/semver/v4"
@ -26,7 +25,7 @@ const (
PRStateMerged = "merged" PRStateMerged = "merged"
PRStateEventClose = "close" PRStateEventClose = "close"
EnvAPIToken = "GITLAB_TOKEN" // nolint:gosec // Not actually a hardcoded credential EnvAPIToken = "GITLAB_TOKEN" // nolint:gosec // Not actually a hardcoded credential
EnvProjectID = "CI_PROJECT_ID" EnvProjectPath = "CI_PROJECT_PATH"
) )
type GitLab struct { type GitLab struct {
@ -37,19 +36,19 @@ type GitLab struct {
} }
func (g *GitLab) RepoURL() string { func (g *GitLab) RepoURL() string {
return fmt.Sprintf("https://gitlab.com/%s", g.options.Repository) return fmt.Sprintf("https://gitlab.com/%s", g.options.Path)
} }
func (g *GitLab) CloneURL() string { func (g *GitLab) CloneURL() string {
return fmt.Sprintf("https://gitlab.com/%s/%s.git", g.options.Path, g.options.Repo) return fmt.Sprintf("https://gitlab.com/%s.git", g.options.Path)
} }
func (g *GitLab) ReleaseURL(version string) string { func (g *GitLab) ReleaseURL(version string) string {
return fmt.Sprintf("https://gitlab.com/%s/%s/-/releases/%s", g.options.Path, g.options.Repo, version) return fmt.Sprintf("https://gitlab.com/%s/-/releases/%s", g.options.Path, version)
} }
func (g *GitLab) PullRequestURL(id int) string { func (g *GitLab) PullRequestURL(id int) string {
return fmt.Sprintf("https://gitlab.com/%s/%s/-/merge_requests/%d", g.options.Path, g.options.Repo, id) return fmt.Sprintf("https://gitlab.com/%s/-/merge_requests/%d", g.options.Path, id)
} }
func (g *GitLab) GitAuth() transport.AuthMethod { func (g *GitLab) GitAuth() transport.AuthMethod {
@ -64,7 +63,7 @@ func (g *GitLab) LatestTags(ctx context.Context) (git.Releases, error) {
g.log.DebugContext(ctx, "listing all tags in gitlab repository") g.log.DebugContext(ctx, "listing all tags in gitlab repository")
tags, err := all(func(listOptions gitlab.ListOptions) ([]*gitlab.Tag, *gitlab.Response, error) { tags, err := all(func(listOptions gitlab.ListOptions) ([]*gitlab.Tag, *gitlab.Response, error) {
return g.client.Tags.ListTags(g.options.ProjectID, &gitlab.ListTagsOptions{ return g.client.Tags.ListTags(g.options.Path, &gitlab.ListTagsOptions{
OrderBy: pointer.Pointer("updated"), OrderBy: pointer.Pointer("updated"),
ListOptions: listOptions, ListOptions: listOptions,
}, gitlab.WithContext(ctx)) }, gitlab.WithContext(ctx))
@ -122,7 +121,7 @@ func (g *GitLab) CommitsSince(ctx context.Context, tag *git.Tag) ([]git.Commit,
log.Debug("listing commits", "ref.name", refName) log.Debug("listing commits", "ref.name", refName)
gitLabCommits, err := all(func(listOptions gitlab.ListOptions) ([]*gitlab.Commit, *gitlab.Response, error) { gitLabCommits, err := all(func(listOptions gitlab.ListOptions) ([]*gitlab.Commit, *gitlab.Response, error) {
return g.client.Commits.ListCommits(g.options.ProjectID, &gitlab.ListCommitsOptions{ return g.client.Commits.ListCommits(g.options.Path, &gitlab.ListCommitsOptions{
RefName: &refName, RefName: &refName,
ListOptions: listOptions, ListOptions: listOptions,
}, gitlab.WithContext(ctx)) }, gitlab.WithContext(ctx))
@ -158,7 +157,7 @@ func (g *GitLab) prForCommit(ctx context.Context, commit git.Commit) (*git.PullR
log.Debug("fetching pull requests associated with commit") log.Debug("fetching pull requests associated with commit")
associatedMRs, _, err := g.client.Commits.ListMergeRequestsByCommit( associatedMRs, _, err := g.client.Commits.ListMergeRequestsByCommit(
g.options.ProjectID, commit.Hash, g.options.Path, commit.Hash,
gitlab.WithContext(ctx), gitlab.WithContext(ctx),
) )
if err != nil { if err != nil {
@ -184,7 +183,7 @@ func (g *GitLab) prForCommit(ctx context.Context, commit git.Commit) (*git.PullR
func (g *GitLab) EnsureLabelsExist(ctx context.Context, labels []releasepr.Label) error { func (g *GitLab) EnsureLabelsExist(ctx context.Context, labels []releasepr.Label) error {
g.log.Debug("fetching labels on repo") g.log.Debug("fetching labels on repo")
glLabels, err := all(func(listOptions gitlab.ListOptions) ([]*gitlab.Label, *gitlab.Response, error) { glLabels, err := all(func(listOptions gitlab.ListOptions) ([]*gitlab.Label, *gitlab.Response, error) {
return g.client.Labels.ListLabels(g.options.ProjectID, &gitlab.ListLabelsOptions{ return g.client.Labels.ListLabels(g.options.Path, &gitlab.ListLabelsOptions{
ListOptions: listOptions, ListOptions: listOptions,
}, gitlab.WithContext(ctx)) }, gitlab.WithContext(ctx))
}) })
@ -197,7 +196,7 @@ func (g *GitLab) EnsureLabelsExist(ctx context.Context, labels []releasepr.Label
return glLabel.Name == label.Name return glLabel.Name == label.Name
}) { }) {
g.log.Info("creating label in repository", "label.name", label) g.log.Info("creating label in repository", "label.name", label)
_, _, err := g.client.Labels.CreateLabel(g.options.ProjectID, &gitlab.CreateLabelOptions{ _, _, err := g.client.Labels.CreateLabel(g.options.Path, &gitlab.CreateLabelOptions{
Name: pointer.Pointer(label.Name), Name: pointer.Pointer(label.Name),
Color: pointer.Pointer("#" + label.Color), Color: pointer.Pointer("#" + label.Color),
Description: pointer.Pointer(label.Description), Description: pointer.Pointer(label.Description),
@ -215,7 +214,7 @@ func (g *GitLab) EnsureLabelsExist(ctx context.Context, labels []releasepr.Label
func (g *GitLab) PullRequestForBranch(ctx context.Context, branch string) (*releasepr.ReleasePullRequest, error) { func (g *GitLab) PullRequestForBranch(ctx context.Context, branch string) (*releasepr.ReleasePullRequest, error) {
// There should only be a single open merge request from branch into g.options.BaseBranch at any given moment. // There should only be a single open merge request from branch into g.options.BaseBranch at any given moment.
// We can skip pagination and just return the first result. // We can skip pagination and just return the first result.
mrs, _, err := g.client.MergeRequests.ListProjectMergeRequests(g.options.ProjectID, &gitlab.ListProjectMergeRequestsOptions{ mrs, _, err := g.client.MergeRequests.ListProjectMergeRequests(g.options.Path, &gitlab.ListProjectMergeRequestsOptions{
State: pointer.Pointer(PRStateOpen), State: pointer.Pointer(PRStateOpen),
SourceBranch: pointer.Pointer(branch), SourceBranch: pointer.Pointer(branch),
TargetBranch: pointer.Pointer(g.options.BaseBranch), TargetBranch: pointer.Pointer(g.options.BaseBranch),
@ -241,7 +240,7 @@ func (g *GitLab) CreatePullRequest(ctx context.Context, pr *releasepr.ReleasePul
labels = append(labels, label.Name) labels = append(labels, label.Name)
} }
glMR, _, err := g.client.MergeRequests.CreateMergeRequest(g.options.ProjectID, &gitlab.CreateMergeRequestOptions{ glMR, _, err := g.client.MergeRequests.CreateMergeRequest(g.options.Path, &gitlab.CreateMergeRequestOptions{
Title: &pr.Title, Title: &pr.Title,
Description: &pr.Description, Description: &pr.Description,
SourceBranch: &pr.Head, SourceBranch: &pr.Head,
@ -258,7 +257,7 @@ func (g *GitLab) CreatePullRequest(ctx context.Context, pr *releasepr.ReleasePul
} }
func (g *GitLab) UpdatePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error { func (g *GitLab) UpdatePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error {
_, _, err := g.client.MergeRequests.UpdateMergeRequest(g.options.ProjectID, pr.ID, &gitlab.UpdateMergeRequestOptions{ _, _, err := g.client.MergeRequests.UpdateMergeRequest(g.options.Path, pr.ID, &gitlab.UpdateMergeRequestOptions{
Title: &pr.Title, Title: &pr.Title,
Description: &pr.Description, Description: &pr.Description,
}, gitlab.WithContext(ctx)) }, gitlab.WithContext(ctx))
@ -281,7 +280,7 @@ func (g *GitLab) SetPullRequestLabels(ctx context.Context, pr *releasepr.Release
addLabels = append(addLabels, label.Name) addLabels = append(addLabels, label.Name)
} }
_, _, err := g.client.MergeRequests.UpdateMergeRequest(g.options.ProjectID, pr.ID, &gitlab.UpdateMergeRequestOptions{ _, _, err := g.client.MergeRequests.UpdateMergeRequest(g.options.Path, pr.ID, &gitlab.UpdateMergeRequestOptions{
RemoveLabels: &removeLabels, RemoveLabels: &removeLabels,
AddLabels: &addLabels, AddLabels: &addLabels,
}, gitlab.WithContext(ctx)) }, gitlab.WithContext(ctx))
@ -294,7 +293,7 @@ func (g *GitLab) SetPullRequestLabels(ctx context.Context, pr *releasepr.Release
} }
func (g *GitLab) ClosePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error { func (g *GitLab) ClosePullRequest(ctx context.Context, pr *releasepr.ReleasePullRequest) error {
_, _, err := g.client.MergeRequests.UpdateMergeRequest(g.options.ProjectID, pr.ID, &gitlab.UpdateMergeRequestOptions{ _, _, err := g.client.MergeRequests.UpdateMergeRequest(g.options.Path, pr.ID, &gitlab.UpdateMergeRequestOptions{
StateEvent: pointer.Pointer(PRStateEventClose), StateEvent: pointer.Pointer(PRStateEventClose),
}, gitlab.WithContext(ctx)) }, gitlab.WithContext(ctx))
@ -328,7 +327,7 @@ func (g *GitLab) PendingReleases(ctx context.Context, pendingLabel releasepr.Lab
} }
func (g *GitLab) CreateRelease(ctx context.Context, commit git.Commit, title, changelog string, _, _ bool) error { func (g *GitLab) CreateRelease(ctx context.Context, commit git.Commit, title, changelog string, _, _ bool) error {
_, _, err := g.client.Releases.CreateRelease(g.options.ProjectID, &gitlab.CreateReleaseOptions{ _, _, err := g.client.Releases.CreateRelease(g.options.Path, &gitlab.CreateReleaseOptions{
Name: &title, Name: &title,
TagName: &title, TagName: &title,
Description: &changelog, Description: &changelog,
@ -392,32 +391,28 @@ func gitlabMRToReleasePullRequest(pr *gitlab.MergeRequest) *releasepr.ReleasePul
} }
} }
func (g *Options) autodiscover(log *slog.Logger) { func (g *Options) autodiscover() {
// Read settings from GitLab-CI env vars // Read settings from GitLab-CI env vars
if apiToken := os.Getenv(EnvAPIToken); apiToken != "" { if apiToken := os.Getenv(EnvAPIToken); apiToken != "" {
g.APIToken = apiToken g.APIToken = apiToken
} }
if projectID := os.Getenv(EnvProjectID); projectID != "" { if projectPath := os.Getenv(EnvProjectPath); projectPath != "" {
var err error g.Path = projectPath
g.ProjectID, err = strconv.ParseInt(projectID, 10, 64)
log.Error("failed to parse environment variable as integer", "env.name", EnvProjectID, "env.value", projectID, "err", err)
} }
} }
type Options struct { type Options struct {
forge.Options forge.Options
Path string Path string
Repo string
ProjectID int64
APIToken string APIToken string
} }
func New(log *slog.Logger, options *Options) (*GitLab, error) { func New(log *slog.Logger, options *Options) (*GitLab, error) {
log = log.With("forge", "gitlab") log = log.With("forge", "gitlab")
options.autodiscover(log) options.autodiscover()
client, err := gitlab.NewClient(options.APIToken) client, err := gitlab.NewClient(options.APIToken)
if err != nil { if err != nil {