From 0dabef9fa3e1a2d0fd9723fb350c19bdffaf81f6 Mon Sep 17 00:00:00 2001 From: Itzamna Date: Thu, 27 Nov 2025 18:31:14 -0600 Subject: [PATCH 1/2] fix(ci): correct branch filtering in ci view command The ci view command was showing the wrong pipeline when viewing different branches. It used commit.LastPipeline which returns the latest pipeline for a commit regardless of branch. This fix uses GetLatestPipeline with the Ref option to correctly filter by branch, with a fallback to MR head pipeline for merged results pipelines. This matches the fix applied to ci status. Closes #7997 --- internal/commands/ci/ciutils/utils.go | 33 ++++++++++++++++++++++ internal/commands/ci/status/status.go | 33 ++-------------------- internal/commands/ci/status/status_test.go | 5 ++-- internal/commands/ci/view/view.go | 21 ++++++++------ internal/commands/ci/view/view_test.go | 19 +++++++++++-- 5 files changed, 68 insertions(+), 43 deletions(-) diff --git a/internal/commands/ci/ciutils/utils.go b/internal/commands/ci/ciutils/utils.go index 84f3a4f6a..3b5d36e19 100644 --- a/internal/commands/ci/ciutils/utils.go +++ b/internal/commands/ci/ciutils/utils.go @@ -17,6 +17,8 @@ import ( gitlab "gitlab.com/gitlab-org/api/client-go" "gitlab.com/gitlab-org/cli/internal/api" + "gitlab.com/gitlab-org/cli/internal/cmdutils" + "gitlab.com/gitlab-org/cli/internal/commands/mr/mrutils" "gitlab.com/gitlab-org/cli/internal/glrepo" "gitlab.com/gitlab-org/cli/internal/iostreams" "gitlab.com/gitlab-org/cli/internal/prompt" @@ -28,6 +30,37 @@ func makeHyperlink(s *iostreams.IOStreams, pipeline *gitlab.PipelineInfo) string return s.Hyperlink(fmt.Sprintf("%d", pipeline.ID), pipeline.WebURL) } +// GetPipelineWithFallback gets the latest pipeline for a branch, falling back to MR head pipeline +// for merged results pipelines where the direct branch lookup may fail. +func GetPipelineWithFallback(client *gitlab.Client, f cmdutils.Factory, repoName, branch string) (*gitlab.Pipeline, error) { + // First try: Get pipeline by branch name + pipeline, _, err := client.Pipelines.GetLatestPipeline(repoName, &gitlab.GetLatestPipelineOptions{Ref: gitlab.Ptr(branch)}) + if err == nil { + return pipeline, nil + } + + // Fallback: Look for MR pipeline (for merged results pipelines) + mr, _, mrErr := mrutils.MRFromArgs(f, []string{}, "any") + if mr == nil || mrErr != nil { + if mrErr != nil { + return nil, fmt.Errorf("no pipeline found for branch %s and failed to find associated merge request: %v", branch, mrErr) + } + return nil, fmt.Errorf("no pipeline found for branch %s and no associated merge request found", branch) + } + + if mr.HeadPipeline == nil { + return nil, fmt.Errorf("no pipeline found. It might not exist yet. Check your pipeline configuration") + } + + // Get the full pipeline details using the MR's head pipeline ID + pipeline, _, pipelineErr := client.Pipelines.GetPipeline(repoName, mr.HeadPipeline.ID) + if pipelineErr != nil { + return nil, pipelineErr + } + + return pipeline, nil +} + func DisplaySchedules(i *iostreams.IOStreams, s []*gitlab.PipelineSchedule, projectID string) string { if len(s) > 0 { table := tableprinter.NewTablePrinter() diff --git a/internal/commands/ci/status/status.go b/internal/commands/ci/status/status.go index 21b0df4d8..7fc053184 100644 --- a/internal/commands/ci/status/status.go +++ b/internal/commands/ci/status/status.go @@ -13,7 +13,6 @@ import ( "gitlab.com/gitlab-org/cli/internal/cmdutils" "gitlab.com/gitlab-org/cli/internal/commands/ci/ciutils" - "gitlab.com/gitlab-org/cli/internal/commands/mr/mrutils" "gitlab.com/gitlab-org/cli/internal/dbg" "gitlab.com/gitlab-org/cli/internal/mcpannotations" "gitlab.com/gitlab-org/cli/internal/utils" @@ -67,7 +66,7 @@ func NewCmdStatus(f cmdutils.Factory) *cobra.Command { dbg.Debug("Using branch:", branch) // Use fallback logic for robust pipeline lookup - runningPipeline, err := getPipelineWithFallback(client, f, repoName, branch) + runningPipeline, err := ciutils.GetPipelineWithFallback(client, f, repoName, branch) if err != nil { redCheck := c.Red("✘") fmt.Fprintf(f.IO().StdOut, "%s %v\n", redCheck, err) @@ -125,7 +124,7 @@ func NewCmdStatus(f cmdutils.Factory) *cobra.Command { if (runningPipeline.Status == "pending" || runningPipeline.Status == "running") && live { // Use fallback logic for live updates - updatedPipeline, err := getPipelineWithFallback(client, f, repoName, branch) + updatedPipeline, err := ciutils.GetPipelineWithFallback(client, f, repoName, branch) if err != nil { // Final fallback: refresh current pipeline by ID updatedPipeline, _, err = client.Pipelines.GetPipeline(repoName, runningPipeline.ID) @@ -156,7 +155,7 @@ func NewCmdStatus(f cmdutils.Factory) *cobra.Command { if err != nil { return err } - updatedPipeline, err := getPipelineWithFallback(client, f, repoName, branch) + updatedPipeline, err := ciutils.GetPipelineWithFallback(client, f, repoName, branch) if err != nil { // Fallback: refresh by pipeline ID if MR lookup fails updatedPipeline, _, err = client.Pipelines.GetPipeline(repoName, runningPipeline.ID) @@ -186,29 +185,3 @@ func NewCmdStatus(f cmdutils.Factory) *cobra.Command { return pipelineStatusCmd } - -func getPipelineWithFallback(client *gitlab.Client, f cmdutils.Factory, repoName, branch string) (*gitlab.Pipeline, error) { - // First try: Get pipeline by branch name - pipeline, _, err := client.Pipelines.GetLatestPipeline(repoName, &gitlab.GetLatestPipelineOptions{Ref: gitlab.Ptr(branch)}) - if err == nil { - return pipeline, nil - } - - // Fallback: Look for MR pipeline - mr, _, mrErr := mrutils.MRFromArgs(f, []string{}, "any") - if mr == nil || mrErr != nil { - return nil, fmt.Errorf("no pipeline found for branch %s and no associated merge request found. Branch may not have an open MR", branch) - } - - if mr.HeadPipeline == nil { - return nil, fmt.Errorf("no pipeline found. It might not exist yet. If this problem continues, check your pipeline configuration.") - } - - // Get the full pipeline details using the MR's head pipeline ID - pipeline, _, pipelineErr := client.Pipelines.GetPipeline(repoName, mr.HeadPipeline.ID) - if pipelineErr != nil { - return nil, pipelineErr - } - - return pipeline, nil -} diff --git a/internal/commands/ci/status/status_test.go b/internal/commands/ci/status/status_test.go index 536e914a7..c653a7d36 100644 --- a/internal/commands/ci/status/status_test.go +++ b/internal/commands/ci/status/status_test.go @@ -13,6 +13,7 @@ import ( gitlab "gitlab.com/gitlab-org/api/client-go" gitlabtesting "gitlab.com/gitlab-org/api/client-go/testing" + "gitlab.com/gitlab-org/cli/internal/commands/ci/ciutils" "gitlab.com/gitlab-org/cli/internal/testing/cmdtest" ) @@ -83,7 +84,7 @@ func Test_getPipelineWithFallback(t *testing.T) { }, wantPipeline: nil, wantErr: true, - expectedErrMsg: "no pipeline found for branch feature and no associated merge request found", + expectedErrMsg: "no pipeline found for branch feature and failed to find associated merge request", }, { name: "returns error when MR has no pipeline", @@ -119,7 +120,7 @@ func Test_getPipelineWithFallback(t *testing.T) { ios, _, _, _ := cmdtest.TestIOStreams(cmdtest.WithTestIOStreamsAsTTY(false)) factory := cmdtest.NewTestFactory(ios, cmdtest.WithGitLabClient(tc.Client)) - pipeline, err := getPipelineWithFallback(tc.Client, factory, "OWNER/REPO", tt.branch) + pipeline, err := ciutils.GetPipelineWithFallback(tc.Client, factory, "OWNER/REPO", tt.branch) if tt.wantErr { assert.Error(t, err) diff --git a/internal/commands/ci/view/view.go b/internal/commands/ci/view/view.go index f440d659d..fd4d0e16d 100644 --- a/internal/commands/ci/view/view.go +++ b/internal/commands/ci/view/view.go @@ -39,6 +39,7 @@ type options struct { gitlabClient func() (*gitlab.Client, error) baseRepo func() (glrepo.Interface, error) config func() config.Config + factory cmdutils.Factory refName string openInBrowser bool @@ -107,6 +108,7 @@ func NewCmdView(f cmdutils.Factory) *cobra.Command { gitlabClient: f.GitLabClient, baseRepo: f.BaseRepo, config: f.Config, + factory: f, } pipelineCIView := &cobra.Command{ Use: "view [branch/tag]", @@ -210,19 +212,22 @@ func (o *options) run() error { return err } } else { - commit, _, err = client.Commits.GetCommit(projectID, o.refName, nil) + // Get pipeline by branch reference (not by commit's LastPipeline) + pipeline, err := ciutils.GetPipelineWithFallback(client, o.factory, projectID, o.refName) if err != nil { return err } - if commit.LastPipeline == nil { - return fmt.Errorf("Can't find pipeline for commit: %s", commit.ID) - } + pipelineID = pipeline.ID + webURL = pipeline.WebURL + pipelineCreatedAt = *pipeline.CreatedAt + commitSHA = pipeline.SHA - pipelineID = commit.LastPipeline.ID - webURL = commit.LastPipeline.WebURL - pipelineCreatedAt = *commit.LastPipeline.CreatedAt - commitSHA = commit.ID + // Get commit details for display purposes + commit, _, err = client.Commits.GetCommit(projectID, commitSHA, nil) + if err != nil { + return err + } } if o.openInBrowser { // open in browser if --web flag is specified diff --git a/internal/commands/ci/view/view_test.go b/internal/commands/ci/view/view_test.go index 8644c8331..0ccb6ac4e 100644 --- a/internal/commands/ci/view/view_test.go +++ b/internal/commands/ci/view/view_test.go @@ -1254,13 +1254,26 @@ func TestCIView(t *testing.T) { httpMocks: []httpMock{ { http.MethodGet, - "https://gitlab.com/api/v4/projects/OWNER%2FREPO/repository/commits/foo", + "https://gitlab.com/api/v4/projects/OWNER%2FREPO/pipelines/latest?ref=foo", http.StatusOK, `{ - "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", + "id": 8, + "ref": "foo", + "sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", + "status": "created", + "web_url": "https://gitlab.com/OWNER/REPO/-/pipelines/225", + "created_at": "2025-10-28T16:52:39.000+01:00" + }`, + }, + { + http.MethodGet, + "https://gitlab.com/api/v4/projects/OWNER%2FREPO/repository/commits/2dc6aa325a317eda67812f05600bdf0fcdc70ab0", + http.StatusOK, + `{ + "id": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", "last_pipeline": { "id": 8, - "ref": "main", + "ref": "foo", "sha": "2dc6aa325a317eda67812f05600bdf0fcdc70ab0", "status": "created", "web_url": "https://gitlab.com/OWNER/REPO/-/pipelines/225", -- GitLab From fa93f7142d732489a8f4a9642763c948662876d7 Mon Sep 17 00:00:00 2001 From: Itzamna Date: Mon, 1 Dec 2025 13:25:52 -0600 Subject: [PATCH 2/2] refactor(ci): remove factory dependency and handle empty pipelines Remove factory parameter from GetPipelineWithFallback in favor of passing only the required iostreams dependency. This aligns with the options struct design pattern where specific dependencies are preferred over the entire factory. Additionally, enhance the fallback logic to check if pipelines have jobs. When GetLatestPipeline returns a pipeline with no jobs (e.g., external pipelines), the function now falls back to the MR head pipeline, which typically has jobs. This fixes an issue where 'glab ci view' would crash with 'No jobs found in the pipeline'. Changes: - Remove factory parameter from GetPipelineWithFallback - Add getMRForBranch helper to replace mrutils.MRFromArgs dependency - Check if pipeline has jobs before returning from direct branch lookup - Update all call sites in ci status and ci view commands - Add test coverage for pipeline with no jobs scenario --- internal/commands/ci/ciutils/utils.go | 99 +++++++++++++++++++--- internal/commands/ci/status/status.go | 6 +- internal/commands/ci/status/status_test.go | 56 +++++++++++- internal/commands/ci/view/view.go | 4 +- 4 files changed, 143 insertions(+), 22 deletions(-) diff --git a/internal/commands/ci/ciutils/utils.go b/internal/commands/ci/ciutils/utils.go index 3b5d36e19..8cca2c152 100644 --- a/internal/commands/ci/ciutils/utils.go +++ b/internal/commands/ci/ciutils/utils.go @@ -17,8 +17,6 @@ import ( gitlab "gitlab.com/gitlab-org/api/client-go" "gitlab.com/gitlab-org/cli/internal/api" - "gitlab.com/gitlab-org/cli/internal/cmdutils" - "gitlab.com/gitlab-org/cli/internal/commands/mr/mrutils" "gitlab.com/gitlab-org/cli/internal/glrepo" "gitlab.com/gitlab-org/cli/internal/iostreams" "gitlab.com/gitlab-org/cli/internal/prompt" @@ -31,34 +29,109 @@ func makeHyperlink(s *iostreams.IOStreams, pipeline *gitlab.PipelineInfo) string } // GetPipelineWithFallback gets the latest pipeline for a branch, falling back to MR head pipeline -// for merged results pipelines where the direct branch lookup may fail. -func GetPipelineWithFallback(client *gitlab.Client, f cmdutils.Factory, repoName, branch string) (*gitlab.Pipeline, error) { +// for merged results pipelines where the direct branch lookup may fail or returns a pipeline with no jobs. +func GetPipelineWithFallback(client *gitlab.Client, repoName, branch string, ios *iostreams.IOStreams) (*gitlab.Pipeline, error) { // First try: Get pipeline by branch name pipeline, _, err := client.Pipelines.GetLatestPipeline(repoName, &gitlab.GetLatestPipelineOptions{Ref: gitlab.Ptr(branch)}) if err == nil { - return pipeline, nil + // Check if the pipeline has jobs - some pipelines (e.g., external pipelines) may have no jobs + jobs, _, jobsErr := client.Jobs.ListPipelineJobs(repoName, pipeline.ID, &gitlab.ListJobsOptions{ + ListOptions: gitlab.ListOptions{PerPage: 1}, + }) + if jobsErr == nil && len(jobs) > 0 { + // Pipeline has jobs, return it + return pipeline, nil + } + // Pipeline has no jobs, try MR fallback below } - // Fallback: Look for MR pipeline (for merged results pipelines) - mr, _, mrErr := mrutils.MRFromArgs(f, []string{}, "any") - if mr == nil || mrErr != nil { - if mrErr != nil { - return nil, fmt.Errorf("no pipeline found for branch %s and failed to find associated merge request: %v", branch, mrErr) + // Fallback: Look for MR pipeline (for merged results pipelines or when branch pipeline has no jobs) + mr, mrErr := getMRForBranch(client, repoName, branch, ios) + if mrErr != nil { + // If we had a pipeline from the branch lookup (even with no jobs), return it + if pipeline != nil { + return pipeline, nil } - return nil, fmt.Errorf("no pipeline found for branch %s and no associated merge request found", branch) + return nil, fmt.Errorf("no pipeline found for branch %s and failed to find associated merge request: %v", branch, mrErr) } if mr.HeadPipeline == nil { + // If we had a pipeline from the branch lookup (even with no jobs), return it + if pipeline != nil { + return pipeline, nil + } return nil, fmt.Errorf("no pipeline found. It might not exist yet. Check your pipeline configuration") } // Get the full pipeline details using the MR's head pipeline ID - pipeline, _, pipelineErr := client.Pipelines.GetPipeline(repoName, mr.HeadPipeline.ID) + mrPipeline, _, pipelineErr := client.Pipelines.GetPipeline(repoName, mr.HeadPipeline.ID) if pipelineErr != nil { + // If we had a pipeline from the branch lookup, return it as fallback + if pipeline != nil { + return pipeline, nil + } return nil, pipelineErr } - return pipeline, nil + return mrPipeline, nil +} + +// getMRForBranch finds a merge request for the given branch +func getMRForBranch(client *gitlab.Client, repoName, branch string, ios *iostreams.IOStreams) (*gitlab.MergeRequest, error) { + opts := &gitlab.ListProjectMergeRequestsOptions{ + SourceBranch: gitlab.Ptr(branch), + } + + mrs, err := api.ListMRs(client, repoName, opts) + if err != nil { + return nil, fmt.Errorf("failed to get merge requests for %q: %w", branch, err) + } + + if len(mrs) == 0 { + return nil, fmt.Errorf("no merge request available for %q", branch) + } + + var selectedMR *gitlab.BasicMergeRequest + + // If exactly one MR, use it + if len(mrs) == 1 { + selectedMR = mrs[0] + } else { + // Multiple MRs exist - need to handle selection + if ios == nil || !ios.PromptEnabled() { + // Build error message with list of possible MRs + var mrNames []string + for _, mr := range mrs { + mrNames = append(mrNames, fmt.Sprintf("!%d (%s) by @%s", mr.IID, branch, mr.Author.Username)) + } + return nil, fmt.Errorf("merge request ID number required. Possible matches:\n\n%s", strings.Join(mrNames, "\n")) + } + + // Prompt user to select + mrMap := map[string]*gitlab.BasicMergeRequest{} + var mrNames []string + for i := range mrs { + t := fmt.Sprintf("!%d (%s) by @%s", mrs[i].IID, branch, mrs[i].Author.Username) + mrMap[t] = mrs[i] + mrNames = append(mrNames, t) + } + + pickedMR := mrNames[0] + err = ios.Select(context.Background(), &pickedMR, "Multiple merge requests exist for this branch. Select one:", mrNames) + if err != nil { + return nil, fmt.Errorf("you must select a merge request: %w", err) + } + + selectedMR = mrMap[pickedMR] + } + + // Fetch the full MR to get HeadPipeline + fullMR, _, err := client.MergeRequests.GetMergeRequest(repoName, selectedMR.IID, &gitlab.GetMergeRequestsOptions{}) + if err != nil { + return nil, fmt.Errorf("failed to get merge request details: %w", err) + } + + return fullMR, nil } func DisplaySchedules(i *iostreams.IOStreams, s []*gitlab.PipelineSchedule, projectID string) string { diff --git a/internal/commands/ci/status/status.go b/internal/commands/ci/status/status.go index 7fc053184..d4d8cff25 100644 --- a/internal/commands/ci/status/status.go +++ b/internal/commands/ci/status/status.go @@ -66,7 +66,7 @@ func NewCmdStatus(f cmdutils.Factory) *cobra.Command { dbg.Debug("Using branch:", branch) // Use fallback logic for robust pipeline lookup - runningPipeline, err := ciutils.GetPipelineWithFallback(client, f, repoName, branch) + runningPipeline, err := ciutils.GetPipelineWithFallback(client, repoName, branch, f.IO()) if err != nil { redCheck := c.Red("✘") fmt.Fprintf(f.IO().StdOut, "%s %v\n", redCheck, err) @@ -124,7 +124,7 @@ func NewCmdStatus(f cmdutils.Factory) *cobra.Command { if (runningPipeline.Status == "pending" || runningPipeline.Status == "running") && live { // Use fallback logic for live updates - updatedPipeline, err := ciutils.GetPipelineWithFallback(client, f, repoName, branch) + updatedPipeline, err := ciutils.GetPipelineWithFallback(client, repoName, branch, f.IO()) if err != nil { // Final fallback: refresh current pipeline by ID updatedPipeline, _, err = client.Pipelines.GetPipeline(repoName, runningPipeline.ID) @@ -155,7 +155,7 @@ func NewCmdStatus(f cmdutils.Factory) *cobra.Command { if err != nil { return err } - updatedPipeline, err := ciutils.GetPipelineWithFallback(client, f, repoName, branch) + updatedPipeline, err := ciutils.GetPipelineWithFallback(client, repoName, branch, f.IO()) if err != nil { // Fallback: refresh by pipeline ID if MR lookup fails updatedPipeline, _, err = client.Pipelines.GetPipeline(repoName, runningPipeline.ID) diff --git a/internal/commands/ci/status/status_test.go b/internal/commands/ci/status/status_test.go index c653a7d36..c8195899d 100644 --- a/internal/commands/ci/status/status_test.go +++ b/internal/commands/ci/status/status_test.go @@ -33,10 +33,51 @@ func Test_getPipelineWithFallback(t *testing.T) { tc.MockPipelines.EXPECT(). GetLatestPipeline("OWNER/REPO", &gitlab.GetLatestPipelineOptions{Ref: gitlab.Ptr("main")}). Return(&gitlab.Pipeline{ID: 1, Status: "success"}, nil, nil) + + // Mock job check to verify pipeline has jobs + tc.MockJobs.EXPECT(). + ListPipelineJobs("OWNER/REPO", int64(1), gomock.Any()). + Return([]*gitlab.Job{{ID: 1, Name: "test"}}, nil, nil) }, wantPipeline: &gitlab.Pipeline{ID: 1, Status: "success"}, wantErr: false, }, + { + name: "falls back to MR pipeline when branch pipeline has no jobs", + branch: "feature", + setupMocks: func(tc *gitlabtesting.TestClient) { + // Latest pipeline found but has no jobs (e.g., external pipeline) + tc.MockPipelines.EXPECT(). + GetLatestPipeline("OWNER/REPO", &gitlab.GetLatestPipelineOptions{Ref: gitlab.Ptr("feature")}). + Return(&gitlab.Pipeline{ID: 1, Status: "success"}, nil, nil) + + // Mock job check returns empty list + tc.MockJobs.EXPECT(). + ListPipelineJobs("OWNER/REPO", int64(1), gomock.Any()). + Return([]*gitlab.Job{}, nil, nil) + + // Find and get MR + tc.MockMergeRequests.EXPECT(). + ListProjectMergeRequests("OWNER/REPO", gomock.Any()). + Return([]*gitlab.BasicMergeRequest{{IID: 1}}, nil, nil) + + tc.MockMergeRequests.EXPECT(). + GetMergeRequest("OWNER/REPO", int64(1), gomock.Any()). + Return(&gitlab.MergeRequest{ + BasicMergeRequest: gitlab.BasicMergeRequest{IID: 1}, + HeadPipeline: &gitlab.Pipeline{ID: 2, Status: "running"}, + }, nil, nil) + + tc.MockPipelines.EXPECT(). + GetPipeline("OWNER/REPO", int64(2), gomock.Any()). + Return(&gitlab.Pipeline{ + ID: 2, + Status: "running", + }, nil, nil) + }, + wantPipeline: &gitlab.Pipeline{ID: 2, Status: "running"}, + wantErr: false, + }, { name: "falls back to MR pipeline when latest not found", branch: "feature", @@ -116,11 +157,10 @@ func Test_getPipelineWithFallback(t *testing.T) { tc := gitlabtesting.NewTestClient(t) tt.setupMocks(tc) - // Create a test factory with test IO streams + // Create test IO streams ios, _, _, _ := cmdtest.TestIOStreams(cmdtest.WithTestIOStreamsAsTTY(false)) - factory := cmdtest.NewTestFactory(ios, cmdtest.WithGitLabClient(tc.Client)) - pipeline, err := ciutils.GetPipelineWithFallback(tc.Client, factory, "OWNER/REPO", tt.branch) + pipeline, err := ciutils.GetPipelineWithFallback(tc.Client, "OWNER/REPO", tt.branch, ios) if tt.wantErr { assert.Error(t, err) @@ -150,6 +190,11 @@ func TestCiStatusCommand_NoPrompt(t *testing.T) { GetLatestPipeline("OWNER/REPO", &gitlab.GetLatestPipelineOptions{Ref: gitlab.Ptr("main")}). Return(&gitlab.Pipeline{ID: 1, Status: "success"}, nil, nil), + // Mock job check in GetPipelineWithFallback + tc.MockJobs.EXPECT(). + ListPipelineJobs("OWNER/REPO", int64(1), gomock.Any()). + Return([]*gitlab.Job{{ID: 1, Name: "test"}}, nil, nil), + // Mock jobs for the pipeline - need to handle pagination tc.MockJobs.EXPECT(). ListPipelineJobs("OWNER/REPO", int64(1), gomock.Any(), gomock.Any()). @@ -184,6 +229,11 @@ func TestCiStatusCommand_WithPromptsEnabled_FinishedPipeline(t *testing.T) { GetLatestPipeline("OWNER/REPO", &gitlab.GetLatestPipelineOptions{Ref: gitlab.Ptr("main")}). Return(&gitlab.Pipeline{ID: 1, Status: "success"}, nil, nil), + // Mock job check in GetPipelineWithFallback + tc.MockJobs.EXPECT(). + ListPipelineJobs("OWNER/REPO", int64(1), gomock.Any()). + Return([]*gitlab.Job{{ID: 1, Name: "test"}}, nil, nil), + // Mock jobs for the pipeline - need to handle pagination tc.MockJobs.EXPECT(). ListPipelineJobs("OWNER/REPO", int64(1), gomock.Any(), gomock.Any()). diff --git a/internal/commands/ci/view/view.go b/internal/commands/ci/view/view.go index fd4d0e16d..836b5c251 100644 --- a/internal/commands/ci/view/view.go +++ b/internal/commands/ci/view/view.go @@ -39,7 +39,6 @@ type options struct { gitlabClient func() (*gitlab.Client, error) baseRepo func() (glrepo.Interface, error) config func() config.Config - factory cmdutils.Factory refName string openInBrowser bool @@ -108,7 +107,6 @@ func NewCmdView(f cmdutils.Factory) *cobra.Command { gitlabClient: f.GitLabClient, baseRepo: f.BaseRepo, config: f.Config, - factory: f, } pipelineCIView := &cobra.Command{ Use: "view [branch/tag]", @@ -213,7 +211,7 @@ func (o *options) run() error { } } else { // Get pipeline by branch reference (not by commit's LastPipeline) - pipeline, err := ciutils.GetPipelineWithFallback(client, o.factory, projectID, o.refName) + pipeline, err := ciutils.GetPipelineWithFallback(client, projectID, o.refName, o.io) if err != nil { return err } -- GitLab