From 0ba6bc6a01bba0c38200ca043d7574e26f0cf5c0 Mon Sep 17 00:00:00 2001 From: andres-portainer <91705312+andres-portainer@users.noreply.github.com> Date: Wed, 17 Jun 2026 09:08:30 -0300 Subject: [PATCH] fix(gitops/sources): fix the handling of the newer structure BE-12919 (#2931) --- api/datastore/migrator/migrate_2_43_0.go | 6 ++- api/datastore/migrator/migrate_2_43_0_test.go | 3 +- api/http/handler/gitops/sources/fetch.go | 41 +++++++++++++++---- api/http/handler/gitops/sources/get.go | 2 - api/http/handler/gitops/sources/get_test.go | 3 +- .../handler/gitops/sources/helpers_test.go | 6 ++- api/http/handler/gitops/sources/utils_test.go | 2 - 7 files changed, 48 insertions(+), 15 deletions(-) diff --git a/api/datastore/migrator/migrate_2_43_0.go b/api/datastore/migrator/migrate_2_43_0.go index f45dc722d..05dc98d7c 100644 --- a/api/datastore/migrator/migrate_2_43_0.go +++ b/api/datastore/migrator/migrate_2_43_0.go @@ -128,7 +128,11 @@ func (m *Migrator) migrateGitConfigToSources_2_43_0() error { src := &portainer.Source{ Name: gittypes.RepoName(cfg.URL), Type: portainer.SourceTypeGit, - Git: cfg, + Git: &gittypes.RepoConfig{ + URL: cfg.URL, + Authentication: cfg.Authentication, + TLSSkipVerify: cfg.TLSSkipVerify, + }, } if err := m.sourceService.Tx(tx).Create(src); err != nil { return fmt.Errorf("failed to create source for stack %d: %w", ls.ID, err) diff --git a/api/datastore/migrator/migrate_2_43_0_test.go b/api/datastore/migrator/migrate_2_43_0_test.go index 4c333e478..6d5095a58 100644 --- a/api/datastore/migrator/migrate_2_43_0_test.go +++ b/api/datastore/migrator/migrate_2_43_0_test.go @@ -65,7 +65,8 @@ func TestMigrateGitConfigToSources_2_43_0_GitStackMigrated(t *testing.T) { require.NoError(t, err) require.Equal(t, portainer.SourceTypeGit, src.Type) require.Equal(t, gitStack.GitConfig.URL, src.Git.URL) - require.Equal(t, gitStack.GitConfig.ReferenceName, src.Git.ReferenceName) + require.Empty(t, src.Git.ReferenceName) + require.Equal(t, gitStack.GitConfig.ReferenceName, wf.Artifacts[0].Files[0].Ref) } func TestMigrateGitConfigToSources_2_43_0_NonGitStackUntouched(t *testing.T) { diff --git a/api/http/handler/gitops/sources/fetch.go b/api/http/handler/gitops/sources/fetch.go index c97af8f26..bc52f85b4 100644 --- a/api/http/handler/gitops/sources/fetch.go +++ b/api/http/handler/gitops/sources/fetch.go @@ -5,9 +5,9 @@ import ( portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/dataservices" + gittypes "github.com/portainer/portainer/api/git/types" ce "github.com/portainer/portainer/api/gitops/workflows" "github.com/portainer/portainer/api/set" - "github.com/portainer/portainer/api/slicesx" ) // FetchSourceWorkflows returns the workflows and stats for a single source. @@ -27,7 +27,18 @@ func FetchSourceWorkflows(tx dataservices.DataStoreTx, src *portainer.Source) ([ return nil, ce.SourceStats{}, nil } - wfIDSet := set.ToSet(slicesx.Map(wfs, func(wf portainer.Workflow) portainer.WorkflowID { return wf.ID })) + wfIDSet := make(map[portainer.WorkflowID]struct{}, len(wfs)) + artifactByStack := make(map[portainer.StackID]portainer.ArtifactFile) + for _, wf := range wfs { + wfIDSet[wf.ID] = struct{}{} + for _, art := range wf.Artifacts { + for _, f := range art.Files { + if f.SourceID == src.ID { + artifactByStack[art.StackID] = f + } + } + } + } stacks, err := tx.Stack().ReadAll(func(s portainer.Stack) bool { _, ok := wfIDSet[s.WorkflowID] @@ -41,16 +52,32 @@ func FetchSourceWorkflows(tx dataservices.DataStoreTx, src *portainer.Source) ([ items := make([]ce.Workflow, 0, len(stacks)) stats := ce.SourceStats{EndpointIDs: set.Set[portainer.EndpointID]{}} - for _, stacks := range stacks { - items = append(items, ce.MapStackToWorkflow(stacks, src.Git, unknown, unknown)) + for _, s := range stacks { + gitCfg := gitConfigForArtifact(src.Git, artifactByStack[s.ID]) + items = append(items, ce.MapStackToWorkflow(s, gitCfg, unknown, unknown)) stats.WorkflowCount++ - if stacks.EndpointID != 0 { - stats.EndpointIDs.Add(stacks.EndpointID) + if s.EndpointID != 0 { + stats.EndpointIDs.Add(s.EndpointID) } - if lastSync := ce.StackLastSyncDate(stacks); lastSync > stats.LastSync { + if lastSync := ce.StackLastSyncDate(s); lastSync > stats.LastSync { stats.LastSync = lastSync } } return items, stats, nil } + +func gitConfigForArtifact(src *gittypes.RepoConfig, af portainer.ArtifactFile) *gittypes.RepoConfig { + if src == nil { + return nil + } + + return &gittypes.RepoConfig{ + URL: src.URL, + Authentication: src.Authentication, + TLSSkipVerify: src.TLSSkipVerify, + ReferenceName: af.Ref, + ConfigFilePath: af.Path, + ConfigHash: af.Hash, + } +} diff --git a/api/http/handler/gitops/sources/get.go b/api/http/handler/gitops/sources/get.go index eb5258147..8af706be6 100644 --- a/api/http/handler/gitops/sources/get.go +++ b/api/http/handler/gitops/sources/get.go @@ -17,7 +17,6 @@ type gitAuthInfo struct { } type connectionInfo struct { - ConfigFilePath string `json:"configFilePath"` TLSSkipVerify bool `json:"tlsSkipVerify"` Authentication *gitAuthInfo `json:"authentication,omitempty"` } @@ -102,7 +101,6 @@ func buildConnectionInfo(cfg *gittypes.RepoConfig) connectionInfo { return connectionInfo{} } return connectionInfo{ - ConfigFilePath: cfg.ConfigFilePath, TLSSkipVerify: cfg.TLSSkipVerify, Authentication: buildGitAuthInfo(cfg.Authentication), } diff --git a/api/http/handler/gitops/sources/get_test.go b/api/http/handler/gitops/sources/get_test.go index 90bd307e2..589419863 100644 --- a/api/http/handler/gitops/sources/get_test.go +++ b/api/http/handler/gitops/sources/get_test.go @@ -56,10 +56,11 @@ func TestGetSource_ReturnsDetail(t *testing.T) { assert.Equal(t, srcID, detail.ID) assert.Equal(t, "repo", detail.Name) assert.Equal(t, 1, detail.UsedBy) - assert.Equal(t, "docker-compose.yml", detail.Connection.ConfigFilePath) assert.True(t, detail.Connection.TLSSkipVerify) require.Len(t, detail.Workflows, 1) assert.Equal(t, "my-stack", detail.Workflows[0].Name) + require.NotNil(t, detail.Workflows[0].GitConfig) + assert.Equal(t, "docker-compose.yml", detail.Workflows[0].GitConfig.ConfigFilePath) } func TestGetSource_RedactsCredentials(t *testing.T) { diff --git a/api/http/handler/gitops/sources/helpers_test.go b/api/http/handler/gitops/sources/helpers_test.go index 5a0780cd1..028716ad3 100644 --- a/api/http/handler/gitops/sources/helpers_test.go +++ b/api/http/handler/gitops/sources/helpers_test.go @@ -25,7 +25,11 @@ func createGitWorkflow(t *testing.T, tx dataservices.DataStoreTx, stack *portain src := &portainer.Source{ Name: gittypes.RepoName(cfg.URL), Type: portainer.SourceTypeGit, - Git: cfg, + Git: &gittypes.RepoConfig{ + URL: cfg.URL, + Authentication: cfg.Authentication, + TLSSkipVerify: cfg.TLSSkipVerify, + }, } require.NoError(t, tx.Source().Create(src)) diff --git a/api/http/handler/gitops/sources/utils_test.go b/api/http/handler/gitops/sources/utils_test.go index 0b155d848..85d834bfa 100644 --- a/api/http/handler/gitops/sources/utils_test.go +++ b/api/http/handler/gitops/sources/utils_test.go @@ -69,12 +69,10 @@ func TestBuildConnectionInfo(t *testing.T) { assert.Equal(t, connectionInfo{}, buildConnectionInfo(nil)) cfg := &gittypes.RepoConfig{ - ConfigFilePath: "docker-compose.yml", TLSSkipVerify: true, Authentication: &gittypes.GitAuthentication{Username: "user"}, } got := buildConnectionInfo(cfg) - assert.Equal(t, "docker-compose.yml", got.ConfigFilePath) assert.True(t, got.TLSSkipVerify) require.NotNil(t, got.Authentication) assert.Equal(t, "user", got.Authentication.Username)