From 8ef998ef8ea00c5ea52e85d7c3c2359a8de3e813 Mon Sep 17 00:00:00 2001 From: attiasas Date: Sun, 14 Jun 2026 13:46:39 +0300 Subject: [PATCH 01/27] chore: bump jfrog-cli-artifactory for local git VCS fallback Co-authored-by: Cursor --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 92ffe89a6..a28ce27ca 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/jfrog/build-info-go v1.13.1-0.20260610071651-260ad6720e0d github.com/jfrog/gofrog v1.7.6 github.com/jfrog/jfrog-cli-application v1.0.2-0.20260608074325-4de652aef752 - github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260610074911-82ce7d90edbd + github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260610130201-b879889dc96a github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260609101026-df3091b39d06 github.com/jfrog/jfrog-cli-evidence v0.9.5-0.20260601141509-8df6c9a4bc9b github.com/jfrog/jfrog-cli-platform-services v1.10.1-0.20260601140139-4cefb6add7b7 diff --git a/go.sum b/go.sum index fc437d77c..e9122bb42 100644 --- a/go.sum +++ b/go.sum @@ -406,8 +406,8 @@ github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYL github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= github.com/jfrog/jfrog-cli-application v1.0.2-0.20260608074325-4de652aef752 h1:Fcb54+kmZjEuBbGstzerLz37Lk6SutfMF7CxLqgXRlE= github.com/jfrog/jfrog-cli-application v1.0.2-0.20260608074325-4de652aef752/go.mod h1:EeLKgLeJfKcS7671H52bfCgj1xK8wyJJGBRD5a+vJMc= -github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260610074911-82ce7d90edbd h1:1AKWhJehl5Z8X69ibX7Azjt70x2L4PnBdnrGiVzdWxY= -github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260610074911-82ce7d90edbd/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= +github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260610130201-b879889dc96a h1:vvVwLa4OCA6W87AaBQtjQU8wBZ1fM1dZD3ZzRouUBBk= +github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260610130201-b879889dc96a/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260609101026-df3091b39d06 h1:A8hWKHyvqzGXfWmh+8lXv3waAkim4xiucBfGhl7ZOeQ= github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260609101026-df3091b39d06/go.mod h1:9R90mhbczGXwW5EGlDs7F08ejQU/xdoDhYHMvzBiqgE= github.com/jfrog/jfrog-cli-evidence v0.9.5-0.20260601141509-8df6c9a4bc9b h1:V0FxnU3xh29y8yJHWymm6rPr1MrjG1DdPQlr3ckImwk= From edabc01e7776f54bc14aac08cbdd6aa7300f5d9e Mon Sep 17 00:00:00 2001 From: attiasas Date: Sun, 14 Jun 2026 13:48:50 +0300 Subject: [PATCH 02/27] test: add local git VCS e2e helpers and fixtures --- utils/tests/utils.go | 72 +++++++++++++++++++++++++++++++++++++ utils/tests/vcs_fixtures.go | 52 +++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 utils/tests/vcs_fixtures.go diff --git a/utils/tests/utils.go b/utils/tests/utils.go index 4bacf7ffc..c5a383751 100644 --- a/utils/tests/utils.go +++ b/utils/tests/utils.go @@ -960,3 +960,75 @@ func ValidateCIVcsPropsIfPresent(t *testing.T, resultItems []utils.ResultItem, e } } } + +// SetupLocalGitVcsEnv enables VCS property collection and clears CI detection +// so only local git fallback is exercised. +func SetupLocalGitVcsEnv(t *testing.T) (cleanup func()) { + t.Helper() + var callbacks []func() + + callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "JFROG_CLI_CI_VCS_PROPS_DISABLED", "")) + callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "CI", "")) + callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_ACTIONS", "")) + callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_WORKFLOW", "")) + callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_RUN_ID", "")) + callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_REPOSITORY", "")) + callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_REPOSITORY_OWNER", "")) + + return func() { + for _, cb := range callbacks { + cb() + } + } +} + +// ValidateLocalGitVcsPropsOnArtifacts asserts vcs.url, vcs.revision, vcs.branch on every item. +func ValidateLocalGitVcsPropsOnArtifacts(t *testing.T, resultItems []utils.ResultItem, expectedURL, expectedRevision, expectedBranch string) { + t.Helper() + for _, item := range resultItems { + propertiesMap := ConvertPropertiesToMap(item.Properties) + assertLocalGitProp(t, item.Name, propertiesMap, "vcs.url", expectedURL) + assertLocalGitProp(t, item.Name, propertiesMap, "vcs.revision", expectedRevision) + if expectedBranch != "" { + assertLocalGitProp(t, item.Name, propertiesMap, "vcs.branch", expectedBranch) + } + } +} + +func assertLocalGitProp(t *testing.T, itemName string, props map[string][]string, key, expected string) { + t.Helper() + vals, ok := props[key] + assert.True(t, ok, "Missing %s on %s", key, itemName) + assert.Contains(t, vals, expected, "Wrong %s on %s", key, itemName) +} + +// ValidateNoLocalGitVcsPropsOnArtifacts asserts url/revision/branch are absent. +func ValidateNoLocalGitVcsPropsOnArtifacts(t *testing.T, resultItems []utils.ResultItem) { + t.Helper() + for _, item := range resultItems { + propertiesMap := ConvertPropertiesToMap(item.Properties) + _, hasURL := propertiesMap["vcs.url"] + _, hasRev := propertiesMap["vcs.revision"] + _, hasBranch := propertiesMap["vcs.branch"] + assert.False(t, hasURL, "vcs.url should not be set on %s", item.Name) + assert.False(t, hasRev, "vcs.revision should not be set on %s", item.Name) + assert.False(t, hasBranch, "vcs.branch should not be set on %s", item.Name) + } +} + +// ValidateCIAndLocalGitVcsPropsOnArtifacts asserts CI props plus local git props coexist. +func ValidateCIAndLocalGitVcsPropsOnArtifacts(t *testing.T, resultItems []utils.ResultItem, + expectedProvider, expectedOrg, expectedRepo, expectedURL, expectedRevision, expectedBranch string) { + t.Helper() + for _, item := range resultItems { + propertiesMap := ConvertPropertiesToMap(item.Properties) + assertLocalGitProp(t, item.Name, propertiesMap, "vcs.provider", expectedProvider) + assertLocalGitProp(t, item.Name, propertiesMap, "vcs.org", expectedOrg) + assertLocalGitProp(t, item.Name, propertiesMap, "vcs.repo", expectedRepo) + assertLocalGitProp(t, item.Name, propertiesMap, "vcs.url", expectedURL) + assertLocalGitProp(t, item.Name, propertiesMap, "vcs.revision", expectedRevision) + if expectedBranch != "" { + assertLocalGitProp(t, item.Name, propertiesMap, "vcs.branch", expectedBranch) + } + } +} diff --git a/utils/tests/vcs_fixtures.go b/utils/tests/vcs_fixtures.go new file mode 100644 index 000000000..9952252cf --- /dev/null +++ b/utils/tests/vcs_fixtures.go @@ -0,0 +1,52 @@ +package tests + +import ( + "path/filepath" + "testing" + + biutils "github.com/jfrog/build-info-go/utils" + coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" + "github.com/jfrog/jfrog-client-go/utils/io/fileutils" + "github.com/stretchr/testify/assert" +) + +const ( + VcsFixtureMainURL = "https://github.com/jfrog/jfrog-cli.git" + VcsFixtureMainRevision = "d63c5957ad6819f4c02a817abe757f210d35ff92" + VcsFixtureMainBranch = "master" + + VcsFixtureOtherURL = "https://github.com/jfrog/jfrog-client-go.git" + VcsFixtureOtherRevision = "ad99b6c068283878fde4d49423728f0bdc00544a" + VcsFixtureOtherBranch = "InnerGit" +) + +// CopyVcsGitFixture copies testdata/vcs into destDir and renames gitdata -> .git. +// Returns the absolute path to destDir. +func CopyVcsGitFixture(t *testing.T, destDir string) string { + t.Helper() + src := filepath.Join(filepath.FromSlash(GetTestResourcesPath()), "vcs") + assert.NoError(t, biutils.CopyDir(src, destDir, true, nil)) + if found, err := fileutils.IsDirExists(filepath.Join(destDir, "gitdata"), false); found { + assert.NoError(t, err) + coretests.RenamePath(filepath.Join(destDir, "gitdata"), filepath.Join(destDir, ".git"), t) + } + if found, err := fileutils.IsDirExists(filepath.Join(destDir, "OtherGit", "gitdata"), false); found { + assert.NoError(t, err) + coretests.RenamePath( + filepath.Join(destDir, "OtherGit", "gitdata"), + filepath.Join(destDir, "OtherGit", ".git"), + t, + ) + } + abs, err := filepath.Abs(destDir) + assert.NoError(t, err) + return abs +} + +// CopyGitFixtureIntoProject copies only gitdata from testdata/vcs/gitdata into projectDir/.git. +func CopyGitFixtureIntoProject(t *testing.T, projectDir string) { + t.Helper() + src := filepath.Join(filepath.FromSlash(GetTestResourcesPath()), "vcs", "gitdata") + dest := filepath.Join(projectDir, ".git") + assert.NoError(t, biutils.CopyDir(src, dest, true, nil)) +} From 25ba2195e48eaad3928cc0a4915fe296afae6bc3 Mon Sep 17 00:00:00 2001 From: attiasas Date: Sun, 14 Jun 2026 13:50:04 +0300 Subject: [PATCH 03/27] test: e2e local git VCS props on rt upload flows Co-authored-by: Cursor --- artifactory_test.go | 141 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/artifactory_test.go b/artifactory_test.go index 9d5bd245b..35433c4c6 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -7181,3 +7181,144 @@ func TestUploadMultipleFilesWithCIVcsProps(t *testing.T) { cleanArtifactoryTest() } + +// TestUploadWithLocalGitVcsProps verifies civcs local git fallback on rt upload +// when CI VCS env vars are absent but VCS collection is enabled. +func TestUploadWithLocalGitVcsProps(t *testing.T) { + initArtifactoryTest(t, "") + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + testDir := tests.CopyVcsGitFixture(t, tests.Temp) + targetPath := tests.RtRepo1 + "/local-git-vcs/" + + runRt(t, "upload", filepath.Join(testDir, "a*.in"), targetPath, "--flat=true") + + resultItems := searchItemsInArtifactory(t, tests.SearchRepo1ByInSuffix) + assert.NotZero(t, len(resultItems)) + + var uploaded []rtutils.ResultItem + for _, item := range resultItems { + if item.Name == "a1.in" || item.Name == "a2.in" { + uploaded = append(uploaded, item) + } + } + assert.Len(t, uploaded, 2) + + tests.ValidateLocalGitVcsPropsOnArtifacts(t, uploaded, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + + cleanArtifactoryTest() +} + +// TestUploadWithLocalGitVcsPropsNestedRepo verifies upload from a subdirectory +// resolves the nearest .git (OtherGit), not the parent repo. +func TestUploadWithLocalGitVcsPropsNestedRepo(t *testing.T) { + initArtifactoryTest(t, "") + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + testDir := tests.CopyVcsGitFixture(t, tests.Temp) + targetPath := tests.RtRepo1 + "/local-git-vcs-nested/" + + runRt(t, "upload", filepath.Join(testDir, "OtherGit", "b*.in"), targetPath, "--flat=true") + + resultItems := searchItemsInArtifactory(t, tests.SearchRepo1ByInSuffix) + var uploaded []rtutils.ResultItem + for _, item := range resultItems { + if item.Name == "b1.in" || item.Name == "b2.in" { + uploaded = append(uploaded, item) + } + } + assert.Len(t, uploaded, 2) + + tests.ValidateLocalGitVcsPropsOnArtifacts(t, uploaded, + tests.VcsFixtureOtherURL, tests.VcsFixtureOtherRevision, tests.VcsFixtureOtherBranch) + + cleanArtifactoryTest() +} + +func TestUploadUserPropsOverrideLocalGitVcs(t *testing.T) { + initArtifactoryTest(t, "") + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + testDir := tests.CopyVcsGitFixture(t, tests.Temp) + targetPath := tests.RtRepo1 + "/local-git-vcs-user-override/" + userProps := "vcs.url=https://example.com/custom.git;vcs.revision=deadbeef;vcs.branch=feature-x" + + runRt(t, "upload", filepath.Join(testDir, "a1.in"), targetPath, "--flat=true", "--props="+userProps) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + props, err := serviceManager.GetItemProps(targetPath + "a1.in") + require.NoError(t, err) + require.Contains(t, props.Properties["vcs.url"], "https://example.com/custom.git") + require.Contains(t, props.Properties["vcs.revision"], "deadbeef") + require.Contains(t, props.Properties["vcs.branch"], "feature-x") + require.NotContains(t, props.Properties["vcs.url"], tests.VcsFixtureMainURL) + require.NotContains(t, props.Properties["vcs.revision"], tests.VcsFixtureMainRevision) + + cleanArtifactoryTest() +} + +// TestUploadCIPlusLocalGitVcsProps verifies CI provides provider/org/repo +// while local git fills url/revision/branch when CI env lacks them. +func TestUploadCIPlusLocalGitVcsProps(t *testing.T) { + initArtifactoryTest(t, "") + + cleanupEnv, actualOrg, actualRepo := tests.SetupGitHubActionsEnv(t) + defer cleanupEnv() + + testDir := tests.CopyVcsGitFixture(t, tests.Temp) + targetPath := tests.RtRepo1 + "/local-git-vcs-ci-merge/" + + runRt(t, "upload", filepath.Join(testDir, "a1.in"), targetPath, "--flat=true") + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + props, err := serviceManager.GetItemProps(targetPath + "a1.in") + require.NoError(t, err) + + require.Contains(t, props.Properties["vcs.provider"], "github") + require.Contains(t, props.Properties["vcs.org"], actualOrg) + require.Contains(t, props.Properties["vcs.repo"], actualRepo) + require.Contains(t, props.Properties["vcs.url"], tests.VcsFixtureMainURL) + require.Contains(t, props.Properties["vcs.revision"], tests.VcsFixtureMainRevision) + require.Contains(t, props.Properties["vcs.branch"], tests.VcsFixtureMainBranch) + + cleanArtifactoryTest() +} + +func TestUploadNoLocalGitVcsWhenNoGitRepo(t *testing.T) { + initArtifactoryTest(t, "") + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + tmpDir, cleanupTmp := coretests.CreateTempDirWithCallbackAndAssert(t) + defer cleanupTmp() + + filePath := filepath.Join(tmpDir, "no-git.txt") + require.NoError(t, os.WriteFile(filePath, []byte("no git here"), 0644)) + + targetPath := tests.RtRepo1 + "/local-git-vcs-none/" + runRt(t, "upload", filePath, targetPath, "--flat=true") + + resultItems := searchItemsInArtifactory(t, tests.SearchAllRepo1) + var uploaded []rtutils.ResultItem + for _, item := range resultItems { + if item.Name == "no-git.txt" { + uploaded = append(uploaded, item) + } + } + require.Len(t, uploaded, 1) + tests.ValidateNoLocalGitVcsPropsOnArtifacts(t, uploaded) + + cleanArtifactoryTest() +} From 1e1ef7e74cab45fa0bec2bb54851c0d617459489 Mon Sep 17 00:00:00 2001 From: attiasas Date: Sun, 14 Jun 2026 13:51:02 +0300 Subject: [PATCH 04/27] test: e2e local git VCS props on go and gradle publish Co-authored-by: Cursor --- go_test.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ gradle_test.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) diff --git a/go_test.go b/go_test.go index 1c9a2e304..c77320177 100644 --- a/go_test.go +++ b/go_test.go @@ -503,3 +503,75 @@ func TestGoBuildPublishWithCIVcsProps(t *testing.T) { assert.Greater(t, artifactCount, 0, "No artifacts were validated for CI VCS properties") } + +// TestGoPublishWithLocalGitVcsProps tests that local git VCS properties are set on Go artifacts +// when running go-publish followed by build-publish with VCS collection enabled and no CI env. +func TestGoPublishWithLocalGitVcsProps(t *testing.T) { + _, cleanUpFunc := initGoTest(t) + defer cleanUpFunc() + + buildName := tests.GoBuildName + "-local-git" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + wd, err := os.Getwd() + assert.NoError(t, err, "Failed to get current dir") + + projectPath := prepareGoProject("project1", t, true) + tests.CopyGitFixtureIntoProject(t, projectPath) + defer clientTestUtils.ChangeDirAndAssert(t, wd) + + jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "") + err = execGo(jfrogCli, "go", "build", "--mod=mod", "--build-name="+buildName, "--build-number="+buildNumber) + assert.NoError(t, err) + + err = execGo(jfrogCli, "gp", "--build-name="+buildName, "--build-number="+buildNumber, "v1.0.0") + assert.NoError(t, err) + + err = execGo(artifactoryCli, "bp", buildName, buildNumber) + assert.NoError(t, err) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + assert.NoError(t, err) + assert.True(t, found, "Build info was not found") + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + assert.NoError(t, err) + + artifactCount := 0 + for _, module := range publishedBuildInfo.BuildInfo.Modules { + for _, artifact := range module.Artifacts { + var fullPath string + switch { + case artifact.OriginalDeploymentRepo != "": + fullPath = artifact.OriginalDeploymentRepo + "/" + artifact.Path + case artifact.Path != "": + fullPath = artifact.Path + default: + continue + } + + props, err := serviceManager.GetItemProps(fullPath) + assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) + assert.NotNil(t, props, "Properties are nil for artifact: %s", fullPath) + + assert.Contains(t, props.Properties, "vcs.url", "Missing vcs.url on %s", artifact.Name) + assert.Contains(t, props.Properties["vcs.url"], tests.VcsFixtureMainURL, "Wrong vcs.url on %s", artifact.Name) + + assert.Contains(t, props.Properties, "vcs.revision", "Missing vcs.revision on %s", artifact.Name) + assert.Contains(t, props.Properties["vcs.revision"], tests.VcsFixtureMainRevision, "Wrong vcs.revision on %s", artifact.Name) + + assert.Contains(t, props.Properties, "vcs.branch", "Missing vcs.branch on %s", artifact.Name) + assert.Contains(t, props.Properties["vcs.branch"], tests.VcsFixtureMainBranch, "Wrong vcs.branch on %s", artifact.Name) + + artifactCount++ + } + } + + assert.Greater(t, artifactCount, 0, "No artifacts were validated for local git VCS properties") +} diff --git a/gradle_test.go b/gradle_test.go index 74c0a60a8..954840f5d 100644 --- a/gradle_test.go +++ b/gradle_test.go @@ -730,3 +730,72 @@ func TestGradleBuildPublishWithCIVcsProps(t *testing.T) { cleanGradleTest(t) } + +// TestGradleBuildPublishWithLocalGitVcsProps tests that local git VCS properties are set on Gradle artifacts +// when running build-publish with VCS collection enabled and no CI env. +// This test uses FlexPack mode (JFROG_RUN_NATIVE=true) with native Gradle publish task. +func TestGradleBuildPublishWithLocalGitVcsProps(t *testing.T) { + initGradleTest(t) + buildName := "gradle-local-git-test" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + setEnvCallBack := clientTestUtils.SetEnvWithCallbackAndAssert(t, "JFROG_RUN_NATIVE", "true") + defer setEnvCallBack() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + buildGradlePath := createGradleProject(t, "civcsproject") + tests.CopyGitFixtureIntoProject(t, filepath.Dir(buildGradlePath)) + + oldHomeDir := changeWD(t, filepath.Dir(buildGradlePath)) + defer clientTestUtils.ChangeDirAndAssert(t, oldHomeDir) + + runJfrogCli(t, "gradle", "clean", "publish", "--build-name="+buildName, "--build-number="+buildNumber) + + assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + clientTestUtils.ChangeDirAndAssert(t, oldHomeDir) + + var publishedBuildInfo *buildinfo.PublishedBuildInfo + var found bool + assert.Eventuallyf(t, func() bool { + var biErr error + publishedBuildInfo, found, biErr = tests.GetBuildInfo(serverDetails, buildName, buildNumber) + return biErr == nil && found + }, 30*time.Second, 2*time.Second, "Build info was not found for %s/%s", buildName, buildNumber) + if !found || publishedBuildInfo == nil { + return + } + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + assert.NoError(t, err) + + artifactCount := 0 + for _, module := range publishedBuildInfo.BuildInfo.Modules { + for _, artifact := range module.Artifacts { + fullPath := artifact.OriginalDeploymentRepo + "/" + artifact.Path + + props, err := serviceManager.GetItemProps(fullPath) + assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) + assert.NotNil(t, props, "Properties are nil for artifact: %s", fullPath) + + assert.Contains(t, props.Properties, "vcs.url", "Missing vcs.url on %s", artifact.Name) + assert.Contains(t, props.Properties["vcs.url"], tests.VcsFixtureMainURL, "Wrong vcs.url on %s", artifact.Name) + + assert.Contains(t, props.Properties, "vcs.revision", "Missing vcs.revision on %s", artifact.Name) + assert.Contains(t, props.Properties["vcs.revision"], tests.VcsFixtureMainRevision, "Wrong vcs.revision on %s", artifact.Name) + + assert.Contains(t, props.Properties, "vcs.branch", "Missing vcs.branch on %s", artifact.Name) + assert.Contains(t, props.Properties["vcs.branch"], tests.VcsFixtureMainBranch, "Wrong vcs.branch on %s", artifact.Name) + + artifactCount++ + } + } + assert.Greater(t, artifactCount, 0, "No artifacts in build info") + + cleanGradleTest(t) +} From 7d5ab4328e447a977864ebb65efbd641e62f5a4e Mon Sep 17 00:00:00 2001 From: attiasas Date: Sun, 14 Jun 2026 13:51:15 +0300 Subject: [PATCH 05/27] test: e2e local git VCS props on build-publish Co-authored-by: Cursor --- buildinfo_test.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/buildinfo_test.go b/buildinfo_test.go index 6c95699c2..c94d4c273 100644 --- a/buildinfo_test.go +++ b/buildinfo_test.go @@ -1234,6 +1234,42 @@ func TestBuildPublishWithCIVcsProps(t *testing.T) { cleanArtifactoryTest() } +// TestBuildPublishWithLocalGitVcsProps verifies build-publish sets local git VCS props +// when CI env is absent but VCS collection is enabled. +func TestBuildPublishWithLocalGitVcsProps(t *testing.T) { + initArtifactoryTest(t, "") + buildName := tests.RtBuildName1 + "-local-git" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + testDir := tests.CopyVcsGitFixture(t, tests.Temp) + runRt(t, "upload", filepath.Join(testDir, "a1.in"), tests.RtRepo1+"/local-git-bp/", "--flat=true", + "--build-name="+buildName, "--build-number="+buildNumber) + + runRt(t, "build-publish", buildName, buildNumber) + + resultItems := getResultItemsFromArtifactory(tests.SearchAllRepo1, t) + require.Greater(t, len(resultItems), 0) + + var uploaded []rtutils.ResultItem + for _, item := range resultItems { + if item.Name == "a1.in" { + uploaded = append(uploaded, item) + } + } + require.NotEmpty(t, uploaded) + + tests.ValidateLocalGitVcsPropsOnArtifacts(t, uploaded, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + + cleanArtifactoryTest() +} + // TestBuildPublishWithoutCI tests that CI VCS properties are NOT set on artifacts // when running build-publish outside of a CI environment. func TestBuildPublishWithoutCI(t *testing.T) { From 91b95ec17d21021fdd0d528bba96871efabae56d Mon Sep 17 00:00:00 2001 From: attiasas Date: Sun, 14 Jun 2026 14:06:14 +0300 Subject: [PATCH 06/27] format, fix static --- go.mod | 3 +++ go.sum | 4 ++-- utils/tests/utils.go | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index a28ce27ca..55a490d01 100644 --- a/go.mod +++ b/go.mod @@ -242,6 +242,9 @@ require ( sigs.k8s.io/yaml v1.6.0 // indirect ) +// attiasas:local_git_detection +replace github.com/jfrog/jfrog-cli-artifactory => github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260610130201-b879889dc96a + //replace github.com/gfleury/go-bitbucket-v1 => github.com/gfleury/go-bitbucket-v1 v0.0.0-20230825095122-9bc1711434ab //replace github.com/ktrysmt/go-bitbucket => github.com/ktrysmt/go-bitbucket v0.9.80 diff --git a/go.sum b/go.sum index e9122bb42..84f7b98a7 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260610130201-b879889dc96a h1:E+bKUwaVu/F/dvV7RP8na06TuoZ+gDOMJzUDDsc07ik= +github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260610130201-b879889dc96a/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= @@ -406,8 +408,6 @@ github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYL github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= github.com/jfrog/jfrog-cli-application v1.0.2-0.20260608074325-4de652aef752 h1:Fcb54+kmZjEuBbGstzerLz37Lk6SutfMF7CxLqgXRlE= github.com/jfrog/jfrog-cli-application v1.0.2-0.20260608074325-4de652aef752/go.mod h1:EeLKgLeJfKcS7671H52bfCgj1xK8wyJJGBRD5a+vJMc= -github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260610130201-b879889dc96a h1:vvVwLa4OCA6W87AaBQtjQU8wBZ1fM1dZD3ZzRouUBBk= -github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260610130201-b879889dc96a/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260609101026-df3091b39d06 h1:A8hWKHyvqzGXfWmh+8lXv3waAkim4xiucBfGhl7ZOeQ= github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260609101026-df3091b39d06/go.mod h1:9R90mhbczGXwW5EGlDs7F08ejQU/xdoDhYHMvzBiqgE= github.com/jfrog/jfrog-cli-evidence v0.9.5-0.20260601141509-8df6c9a4bc9b h1:V0FxnU3xh29y8yJHWymm6rPr1MrjG1DdPQlr3ckImwk= diff --git a/utils/tests/utils.go b/utils/tests/utils.go index c5a383751..6c61a59d2 100644 --- a/utils/tests/utils.go +++ b/utils/tests/utils.go @@ -114,8 +114,8 @@ func init() { TestPip = flag.Bool("test.pip", false, "Test Pip") TestPipenv = flag.Bool("test.pipenv", false, "Test Pipenv") TestPoetry = flag.Bool("test.poetry", false, "Test Poetry") - TestUv = flag.Bool("test.uv", false, "Test UV") - TestNix = flag.Bool("test.nix", false, "Test Nix") + TestUv = flag.Bool("test.uv", false, "Test UV") + TestNix = flag.Bool("test.nix", false, "Test Nix") TestConan = flag.Bool("test.conan", false, "Test Conan") TestHelm = flag.Bool("test.helm", false, "Test Helm") TestHuggingFace = flag.Bool("test.huggingface", false, "Test HuggingFace") From b18e7a94a0160f8bf9a0278ea92f49933b05fdc6 Mon Sep 17 00:00:00 2001 From: attiasas Date: Sun, 14 Jun 2026 14:47:09 +0300 Subject: [PATCH 07/27] fix tests --- artifactory_test.go | 6 +++--- go_test.go | 2 ++ gradle_test.go | 20 +++++++++++++------- utils/tests/vcs_fixtures.go | 19 ++++++++++++++++--- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/artifactory_test.go b/artifactory_test.go index 35433c4c6..1031799d7 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -7193,7 +7193,7 @@ func TestUploadWithLocalGitVcsProps(t *testing.T) { testDir := tests.CopyVcsGitFixture(t, tests.Temp) targetPath := tests.RtRepo1 + "/local-git-vcs/" - runRt(t, "upload", filepath.Join(testDir, "a*.in"), targetPath, "--flat=true") + runRt(t, "upload", filepath.Join(testDir, "*.in"), targetPath, "--flat=true") resultItems := searchItemsInArtifactory(t, tests.SearchRepo1ByInSuffix) assert.NotZero(t, len(resultItems)) @@ -7223,7 +7223,7 @@ func TestUploadWithLocalGitVcsPropsNestedRepo(t *testing.T) { testDir := tests.CopyVcsGitFixture(t, tests.Temp) targetPath := tests.RtRepo1 + "/local-git-vcs-nested/" - runRt(t, "upload", filepath.Join(testDir, "OtherGit", "b*.in"), targetPath, "--flat=true") + runRt(t, "upload", filepath.Join(testDir, "OtherGit", "*.in"), targetPath, "--flat=true") resultItems := searchItemsInArtifactory(t, tests.SearchRepo1ByInSuffix) var uploaded []rtutils.ResultItem @@ -7250,7 +7250,7 @@ func TestUploadUserPropsOverrideLocalGitVcs(t *testing.T) { targetPath := tests.RtRepo1 + "/local-git-vcs-user-override/" userProps := "vcs.url=https://example.com/custom.git;vcs.revision=deadbeef;vcs.branch=feature-x" - runRt(t, "upload", filepath.Join(testDir, "a1.in"), targetPath, "--flat=true", "--props="+userProps) + runRt(t, "upload", filepath.Join(testDir, "a1.in"), targetPath, "--flat=true", "--target-props="+userProps) serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) require.NoError(t, err) diff --git a/go_test.go b/go_test.go index c77320177..3d3a4a798 100644 --- a/go_test.go +++ b/go_test.go @@ -524,6 +524,8 @@ func TestGoPublishWithLocalGitVcsProps(t *testing.T) { projectPath := prepareGoProject("project1", t, true) tests.CopyGitFixtureIntoProject(t, projectPath) + require.FileExists(t, filepath.Join(projectPath, ".git", "HEAD")) + clientTestUtils.ChangeDirAndAssert(t, projectPath) defer clientTestUtils.ChangeDirAndAssert(t, wd) jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "") diff --git a/gradle_test.go b/gradle_test.go index 954840f5d..f2c12533c 100644 --- a/gradle_test.go +++ b/gradle_test.go @@ -733,7 +733,8 @@ func TestGradleBuildPublishWithCIVcsProps(t *testing.T) { // TestGradleBuildPublishWithLocalGitVcsProps tests that local git VCS properties are set on Gradle artifacts // when running build-publish with VCS collection enabled and no CI env. -// This test uses FlexPack mode (JFROG_RUN_NATIVE=true) with native Gradle publish task. +// Uses the traditional Gradle extractor path (not FlexPack) because SetCIVcsPropsToConfig +// injects local git props into the extractor config; FlexPack only sets build.* props on publish. func TestGradleBuildPublishWithLocalGitVcsProps(t *testing.T) { initGradleTest(t) buildName := "gradle-local-git-test" @@ -742,19 +743,24 @@ func TestGradleBuildPublishWithLocalGitVcsProps(t *testing.T) { cleanupEnv := tests.SetupLocalGitVcsEnv(t) defer cleanupEnv() - setEnvCallBack := clientTestUtils.SetEnvWithCallbackAndAssert(t, "JFROG_RUN_NATIVE", "true") - defer setEnvCallBack() + _ = os.Unsetenv("JFROG_RUN_NATIVE") inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) - buildGradlePath := createGradleProject(t, "civcsproject") - tests.CopyGitFixtureIntoProject(t, filepath.Dir(buildGradlePath)) + buildGradlePath := createGradleProject(t, "gradleproject") + projectDir := filepath.Dir(buildGradlePath) + tests.CopyGitFixtureIntoProject(t, projectDir) + require.FileExists(t, filepath.Join(projectDir, ".git", "HEAD")) - oldHomeDir := changeWD(t, filepath.Dir(buildGradlePath)) + configFilePath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "buildspecs", tests.GradleConfig) + createConfigFile(filepath.Join(projectDir, ".jfrog", "projects"), configFilePath, t) + + oldHomeDir := changeWD(t, projectDir) defer clientTestUtils.ChangeDirAndAssert(t, oldHomeDir) - runJfrogCli(t, "gradle", "clean", "publish", "--build-name="+buildName, "--build-number="+buildNumber) + buildGradlePath = strings.ReplaceAll(buildGradlePath, `\`, "/") + runJfrogCli(t, "gradle", "clean", "artifactoryPublish", "-b"+buildGradlePath, "--build-name="+buildName, "--build-number="+buildNumber) assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) diff --git a/utils/tests/vcs_fixtures.go b/utils/tests/vcs_fixtures.go index 9952252cf..3b73ecbfe 100644 --- a/utils/tests/vcs_fixtures.go +++ b/utils/tests/vcs_fixtures.go @@ -1,6 +1,7 @@ package tests import ( + "os" "path/filepath" "testing" @@ -8,6 +9,7 @@ import ( coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" "github.com/jfrog/jfrog-client-go/utils/io/fileutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -43,10 +45,21 @@ func CopyVcsGitFixture(t *testing.T, destDir string) string { return abs } -// CopyGitFixtureIntoProject copies only gitdata from testdata/vcs/gitdata into projectDir/.git. +// CopyGitFixtureIntoProject installs testdata/vcs/gitdata as projectDir/.git. func CopyGitFixtureIntoProject(t *testing.T, projectDir string) { t.Helper() src := filepath.Join(filepath.FromSlash(GetTestResourcesPath()), "vcs", "gitdata") - dest := filepath.Join(projectDir, ".git") - assert.NoError(t, biutils.CopyDir(src, dest, true, nil)) + gitDir := filepath.Join(projectDir, ".git") + stagingDir := filepath.Join(projectDir, "gitdata-staging") + + if fileutils.IsPathExists(gitDir, false) { + require.NoError(t, os.RemoveAll(gitDir)) + } + require.NoError(t, os.RemoveAll(stagingDir)) + + require.NoError(t, biutils.CopyDir(src, stagingDir, true, nil)) + coretests.RenamePath(stagingDir, gitDir, t) + + require.FileExists(t, filepath.Join(gitDir, "HEAD")) + require.FileExists(t, filepath.Join(gitDir, "config")) } From 2242fc6566fa319a61bcd956c3ee8fa1311c5e35 Mon Sep 17 00:00:00 2001 From: attiasas Date: Sun, 14 Jun 2026 15:37:14 +0300 Subject: [PATCH 08/27] update deps --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 55a490d01..dc5bf9290 100644 --- a/go.mod +++ b/go.mod @@ -243,7 +243,7 @@ require ( ) // attiasas:local_git_detection -replace github.com/jfrog/jfrog-cli-artifactory => github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260610130201-b879889dc96a +replace github.com/jfrog/jfrog-cli-artifactory => github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260614123517-168b70f9ddb3 //replace github.com/gfleury/go-bitbucket-v1 => github.com/gfleury/go-bitbucket-v1 v0.0.0-20230825095122-9bc1711434ab diff --git a/go.sum b/go.sum index 84f7b98a7..a26e4cce8 100644 --- a/go.sum +++ b/go.sum @@ -67,8 +67,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260610130201-b879889dc96a h1:E+bKUwaVu/F/dvV7RP8na06TuoZ+gDOMJzUDDsc07ik= -github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260610130201-b879889dc96a/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= +github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260614123517-168b70f9ddb3 h1:0heTK0r0dtJz9eHaVndgYTmf2wUouLNLxfoKwceDKsA= +github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260614123517-168b70f9ddb3/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= From dfe10210320c7bb4703c4b787a3f5196b7d56340 Mon Sep 17 00:00:00 2001 From: attiasas Date: Mon, 15 Jun 2026 09:51:58 +0300 Subject: [PATCH 09/27] update dep --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index dc5bf9290..eff965693 100644 --- a/go.mod +++ b/go.mod @@ -243,7 +243,7 @@ require ( ) // attiasas:local_git_detection -replace github.com/jfrog/jfrog-cli-artifactory => github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260614123517-168b70f9ddb3 +replace github.com/jfrog/jfrog-cli-artifactory => github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260615065015-5c7cac0b0a98 //replace github.com/gfleury/go-bitbucket-v1 => github.com/gfleury/go-bitbucket-v1 v0.0.0-20230825095122-9bc1711434ab diff --git a/go.sum b/go.sum index a26e4cce8..b11080e7e 100644 --- a/go.sum +++ b/go.sum @@ -67,8 +67,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260614123517-168b70f9ddb3 h1:0heTK0r0dtJz9eHaVndgYTmf2wUouLNLxfoKwceDKsA= -github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260614123517-168b70f9ddb3/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= +github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260615065015-5c7cac0b0a98 h1:DaAcgwo8oImmU/n5qFLXnXfHLE2Li2Maa6Bt2oiHsyM= +github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260615065015-5c7cac0b0a98/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= From 0946bdce4cbc4f94f68f5329d602ad3cb4b62112 Mon Sep 17 00:00:00 2001 From: attiasas Date: Mon, 15 Jun 2026 12:20:16 +0300 Subject: [PATCH 10/27] fix tests --- artifactory_test.go | 2 +- go_test.go | 37 ++++++++++++++------------- gradle_test.go | 10 ++++++-- utils/tests/artifact_props.go | 15 +++++++++++ utils/tests/artifact_props_test.go | 25 +++++++++++++++++++ utils/tests/utils.go | 40 ++++++++++++++++++++++++------ utils/tests/utils_test.go | 31 +++++++++++++++++++++++ utils/tests/vcs_fixtures.go | 31 +++++++++++++++++++++-- utils/tests/vcs_fixtures_test.go | 27 ++++++++++++++++++++ 9 files changed, 189 insertions(+), 29 deletions(-) create mode 100644 utils/tests/artifact_props.go create mode 100644 utils/tests/artifact_props_test.go create mode 100644 utils/tests/utils_test.go create mode 100644 utils/tests/vcs_fixtures_test.go diff --git a/artifactory_test.go b/artifactory_test.go index 1031799d7..9de331c31 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -7271,7 +7271,7 @@ func TestUploadUserPropsOverrideLocalGitVcs(t *testing.T) { func TestUploadCIPlusLocalGitVcsProps(t *testing.T) { initArtifactoryTest(t, "") - cleanupEnv, actualOrg, actualRepo := tests.SetupGitHubActionsEnv(t) + cleanupEnv, actualOrg, actualRepo := tests.SetupGitHubActionsEnvForLocalGitMerge(t) defer cleanupEnv() testDir := tests.CopyVcsGitFixture(t, tests.Temp) diff --git a/go_test.go b/go_test.go index 3d3a4a798..3b12f9be9 100644 --- a/go_test.go +++ b/go_test.go @@ -469,23 +469,20 @@ func TestGoBuildPublishWithCIVcsProps(t *testing.T) { assert.NoError(t, err) // Verify VCS properties on each artifact from build info - // Use same fallback logic as CI VCS: OriginalDeploymentRepo + Path, or Path directly artifactCount := 0 for _, module := range publishedBuildInfo.BuildInfo.Modules { for _, artifact := range module.Artifacts { - var fullPath string - switch { - case artifact.OriginalDeploymentRepo != "": - fullPath = artifact.OriginalDeploymentRepo + "/" + artifact.Path - case artifact.Path != "": - fullPath = artifact.Path - default: - continue // Skip artifacts without any path info + fullPath := tests.ArtifactFullPath(artifact) + if fullPath == "" { + continue } props, err := serviceManager.GetItemProps(fullPath) assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) assert.NotNil(t, props, "Properties are nil for artifact: %s", fullPath) + if props == nil { + continue + } // Validate VCS properties assert.Contains(t, props.Properties, "vcs.provider", "Missing vcs.provider on %s", artifact.Name) @@ -522,11 +519,19 @@ func TestGoPublishWithLocalGitVcsProps(t *testing.T) { wd, err := os.Getwd() assert.NoError(t, err, "Failed to get current dir") - projectPath := prepareGoProject("project1", t, true) + projectPath := createGoProject(t, "project1", true) + testdataTarget := filepath.Join(tests.Out, "testdata") + testdataSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "go", "testdata") + require.NoError(t, biutils.CopyDir(testdataSrc, testdataTarget, true, nil)) + configFileDir := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "go", "project1", ".jfrog", "projects") + _, err = tests.ReplaceTemplateVariables(filepath.Join(configFileDir, "go.yaml"), filepath.Join(projectPath, ".jfrog", "projects")) + require.NoError(t, err) + tests.CopyGitFixtureIntoProject(t, projectPath) require.FileExists(t, filepath.Join(projectPath, ".git", "HEAD")) clientTestUtils.ChangeDirAndAssert(t, projectPath) defer clientTestUtils.ChangeDirAndAssert(t, wd) + log.Info("Using Go project located at", projectPath) jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "") err = execGo(jfrogCli, "go", "build", "--mod=mod", "--build-name="+buildName, "--build-number="+buildNumber) @@ -548,19 +553,17 @@ func TestGoPublishWithLocalGitVcsProps(t *testing.T) { artifactCount := 0 for _, module := range publishedBuildInfo.BuildInfo.Modules { for _, artifact := range module.Artifacts { - var fullPath string - switch { - case artifact.OriginalDeploymentRepo != "": - fullPath = artifact.OriginalDeploymentRepo + "/" + artifact.Path - case artifact.Path != "": - fullPath = artifact.Path - default: + fullPath := tests.ArtifactFullPath(artifact) + if fullPath == "" { continue } props, err := serviceManager.GetItemProps(fullPath) assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) assert.NotNil(t, props, "Properties are nil for artifact: %s", fullPath) + if props == nil { + continue + } assert.Contains(t, props.Properties, "vcs.url", "Missing vcs.url on %s", artifact.Name) assert.Contains(t, props.Properties["vcs.url"], tests.VcsFixtureMainURL, "Wrong vcs.url on %s", artifact.Name) diff --git a/gradle_test.go b/gradle_test.go index f2c12533c..36a117f29 100644 --- a/gradle_test.go +++ b/gradle_test.go @@ -707,11 +707,14 @@ func TestGradleBuildPublishWithCIVcsProps(t *testing.T) { artifactCount := 0 for _, module := range publishedBuildInfo.BuildInfo.Modules { for _, artifact := range module.Artifacts { - fullPath := artifact.OriginalDeploymentRepo + "/" + artifact.Path + fullPath := tests.ArtifactFullPath(artifact) props, err := serviceManager.GetItemProps(fullPath) assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) assert.NotNil(t, props, "Properties are nil for artifact: %s", fullPath) + if props == nil { + continue + } // Validate VCS properties assert.Contains(t, props.Properties, "vcs.provider", "Missing vcs.provider on %s", artifact.Name) @@ -783,11 +786,14 @@ func TestGradleBuildPublishWithLocalGitVcsProps(t *testing.T) { artifactCount := 0 for _, module := range publishedBuildInfo.BuildInfo.Modules { for _, artifact := range module.Artifacts { - fullPath := artifact.OriginalDeploymentRepo + "/" + artifact.Path + fullPath := tests.ArtifactFullPath(artifact) props, err := serviceManager.GetItemProps(fullPath) assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) assert.NotNil(t, props, "Properties are nil for artifact: %s", fullPath) + if props == nil { + continue + } assert.Contains(t, props.Properties, "vcs.url", "Missing vcs.url on %s", artifact.Name) assert.Contains(t, props.Properties["vcs.url"], tests.VcsFixtureMainURL, "Wrong vcs.url on %s", artifact.Name) diff --git a/utils/tests/artifact_props.go b/utils/tests/artifact_props.go new file mode 100644 index 000000000..7a5ad7d08 --- /dev/null +++ b/utils/tests/artifact_props.go @@ -0,0 +1,15 @@ +package tests + +import ( + "strings" + + buildinfo "github.com/jfrog/build-info-go/entities" +) + +func ArtifactFullPath(a buildinfo.Artifact) string { + path := strings.TrimPrefix(a.Path, "/") + if a.OriginalDeploymentRepo != "" { + return a.OriginalDeploymentRepo + "/" + path + } + return path +} diff --git a/utils/tests/artifact_props_test.go b/utils/tests/artifact_props_test.go new file mode 100644 index 000000000..9667882e6 --- /dev/null +++ b/utils/tests/artifact_props_test.go @@ -0,0 +1,25 @@ +package tests + +import ( + "testing" + + buildinfo "github.com/jfrog/build-info-go/entities" + "github.com/stretchr/testify/assert" +) + +func TestArtifactFullPath(t *testing.T) { + t.Run("uses OriginalDeploymentRepo when set", func(t *testing.T) { + a := buildinfo.Artifact{OriginalDeploymentRepo: "cli-gradle-123", Path: "com/foo/1.0/foo.jar"} + assert.Equal(t, "cli-gradle-123/com/foo/1.0/foo.jar", ArtifactFullPath(a)) + }) + + t.Run("falls back to Path when repo empty", func(t *testing.T) { + a := buildinfo.Artifact{Path: "com/foo/1.0/foo.jar"} + assert.Equal(t, "com/foo/1.0/foo.jar", ArtifactFullPath(a)) + }) + + t.Run("strips leading slash from Path", func(t *testing.T) { + a := buildinfo.Artifact{Path: "/minimal-example/1.0/minimal-example-1.0.jar"} + assert.Equal(t, "minimal-example/1.0/minimal-example-1.0.jar", ArtifactFullPath(a)) + }) +} diff --git a/utils/tests/utils.go b/utils/tests/utils.go index 6c61a59d2..e8653d1cf 100644 --- a/utils/tests/utils.go +++ b/utils/tests/utils.go @@ -862,6 +862,31 @@ func SetupGitHubActionsEnv(t *testing.T) (cleanup func(), actualOrg, actualRepo return cleanup, actualOrg, actualRepo } +// SetupGitHubActionsEnvForLocalGitMerge enables CI VCS collection with provider/org/repo +// but clears url/revision/branch CI env vars so local git fallback is exercised. +func SetupGitHubActionsEnvForLocalGitMerge(t *testing.T) (cleanup func(), actualOrg, actualRepo string) { + t.Helper() + cleanupBase, actualOrg, actualRepo := SetupGitHubActionsEnv(t) + + var callbacks []func() + for _, key := range []string{ + "GITHUB_SERVER_URL", + "GITHUB_SHA", + "GITHUB_REF", + "GITHUB_REF_NAME", + "GITHUB_HEAD_REF", + } { + callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, key, "")) + } + + return func() { + for _, cb := range callbacks { + cb() + } + cleanupBase() + }, actualOrg, actualRepo +} + // ValidateCIVcsPropsOnArtifacts validates that CI VCS properties are set on artifacts. func ValidateCIVcsPropsOnArtifacts(t *testing.T, resultItems []utils.ResultItem, expectedProvider, expectedOrg, expectedRepo string) { for _, item := range resultItems { @@ -967,13 +992,14 @@ func SetupLocalGitVcsEnv(t *testing.T) (cleanup func()) { t.Helper() var callbacks []func() - callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "JFROG_CLI_CI_VCS_PROPS_DISABLED", "")) - callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "CI", "")) - callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_ACTIONS", "")) - callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_WORKFLOW", "")) - callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_RUN_ID", "")) - callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_REPOSITORY", "")) - callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_REPOSITORY_OWNER", "")) + for _, key := range []string{ + "JFROG_CLI_CI_VCS_PROPS_DISABLED", // set to "" to enable + "CI", "GITHUB_ACTIONS", "GITHUB_WORKFLOW", "GITHUB_RUN_ID", + "GITHUB_REPOSITORY", "GITHUB_REPOSITORY_OWNER", + "GITHUB_SERVER_URL", "GITHUB_SHA", "GITHUB_REF", "GITHUB_REF_NAME", "GITHUB_HEAD_REF", + } { + callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, key, "")) + } return func() { for _, cb := range callbacks { diff --git a/utils/tests/utils_test.go b/utils/tests/utils_test.go new file mode 100644 index 000000000..d384b9e8c --- /dev/null +++ b/utils/tests/utils_test.go @@ -0,0 +1,31 @@ +package tests + +import ( + "testing" + + "github.com/jfrog/build-info-go/utils/cienv" + "github.com/stretchr/testify/assert" +) + +func TestSetupGitHubActionsEnvForLocalGitMerge_ClearsUrlRevisionBranch(t *testing.T) { + t.Setenv("CI", "true") + t.Setenv("GITHUB_ACTIONS", "true") + t.Setenv("GITHUB_WORKFLOW", "wf") + t.Setenv("GITHUB_RUN_ID", "99") + t.Setenv("GITHUB_REPOSITORY_OWNER", "jfrog") + t.Setenv("GITHUB_REPOSITORY", "jfrog/jfrog-cli") + t.Setenv("GITHUB_SERVER_URL", "https://github.com") + t.Setenv("GITHUB_SHA", "abc123") + t.Setenv("GITHUB_REF", "refs/heads/feature") + + cleanup, _, _ := SetupGitHubActionsEnvForLocalGitMerge(t) + defer cleanup() + + info := cienv.GetCIVcsInfo() + assert.Equal(t, "github", info.Provider) + assert.Equal(t, "jfrog", info.Org) + assert.Equal(t, "jfrog-cli", info.Repo) + assert.Empty(t, info.Url) + assert.Empty(t, info.Revision) + assert.Empty(t, info.Branch) +} diff --git a/utils/tests/vcs_fixtures.go b/utils/tests/vcs_fixtures.go index 3b73ecbfe..2b94d5cbf 100644 --- a/utils/tests/vcs_fixtures.go +++ b/utils/tests/vcs_fixtures.go @@ -3,6 +3,7 @@ package tests import ( "os" "path/filepath" + "runtime" "testing" biutils "github.com/jfrog/build-info-go/utils" @@ -22,11 +23,37 @@ const ( VcsFixtureOtherBranch = "InnerGit" ) +// testResourcesDir returns the absolute path to the repo's testdata/ directory. +// It is resolved from this source file's location, not os.Getwd(). +func testResourcesDir() string { + _, filename, _, ok := runtime.Caller(0) + if !ok { + abs, err := filepath.Abs(filepath.FromSlash(GetTestResourcesPath())) + if err != nil { + return filepath.FromSlash(GetTestResourcesPath()) + } + return abs + } + abs, err := filepath.Abs(filepath.Join(filepath.Dir(filename), "..", "..", "testdata")) + if err != nil { + return filepath.Join(filepath.Dir(filename), "..", "..", "testdata") + } + return abs +} + +func vcsFixtureSrcDir() string { + return filepath.Join(testResourcesDir(), "vcs") +} + +func vcsGitdataSrcDir() string { + return filepath.Join(vcsFixtureSrcDir(), "gitdata") +} + // CopyVcsGitFixture copies testdata/vcs into destDir and renames gitdata -> .git. // Returns the absolute path to destDir. func CopyVcsGitFixture(t *testing.T, destDir string) string { t.Helper() - src := filepath.Join(filepath.FromSlash(GetTestResourcesPath()), "vcs") + src := vcsFixtureSrcDir() assert.NoError(t, biutils.CopyDir(src, destDir, true, nil)) if found, err := fileutils.IsDirExists(filepath.Join(destDir, "gitdata"), false); found { assert.NoError(t, err) @@ -48,7 +75,7 @@ func CopyVcsGitFixture(t *testing.T, destDir string) string { // CopyGitFixtureIntoProject installs testdata/vcs/gitdata as projectDir/.git. func CopyGitFixtureIntoProject(t *testing.T, projectDir string) { t.Helper() - src := filepath.Join(filepath.FromSlash(GetTestResourcesPath()), "vcs", "gitdata") + src := vcsGitdataSrcDir() gitDir := filepath.Join(projectDir, ".git") stagingDir := filepath.Join(projectDir, "gitdata-staging") diff --git a/utils/tests/vcs_fixtures_test.go b/utils/tests/vcs_fixtures_test.go new file mode 100644 index 000000000..a9968101a --- /dev/null +++ b/utils/tests/vcs_fixtures_test.go @@ -0,0 +1,27 @@ +package tests + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCopyGitFixtureIntoProject_WorksAfterChdir(t *testing.T) { + repoRoot, err := os.Getwd() + require.NoError(t, err) + + projectDir := t.TempDir() + subDir := filepath.Join(projectDir, "nested") + require.NoError(t, os.MkdirAll(subDir, 0o755)) + + // Simulate prepareGoProject leaving cwd inside the project tree. + require.NoError(t, os.Chdir(subDir)) + t.Cleanup(func() { _ = os.Chdir(repoRoot) }) + + CopyGitFixtureIntoProject(t, projectDir) + + require.FileExists(t, filepath.Join(projectDir, ".git", "HEAD")) + require.FileExists(t, filepath.Join(projectDir, ".git", "config")) +} From 585283688ab2bfea09e732484d83537e6bc19124 Mon Sep 17 00:00:00 2001 From: attiasas Date: Mon, 15 Jun 2026 13:31:10 +0300 Subject: [PATCH 11/27] fix tests for gradle fix --- go_test.go | 4 ++-- gradle_test.go | 4 ++-- utils/tests/artifact_props.go | 13 ++++++++++--- utils/tests/artifact_props_test.go | 13 +++++++++---- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/go_test.go b/go_test.go index 3b12f9be9..249505f35 100644 --- a/go_test.go +++ b/go_test.go @@ -472,7 +472,7 @@ func TestGoBuildPublishWithCIVcsProps(t *testing.T) { artifactCount := 0 for _, module := range publishedBuildInfo.BuildInfo.Modules { for _, artifact := range module.Artifacts { - fullPath := tests.ArtifactFullPath(artifact) + fullPath := tests.ArtifactFullPath(artifact, tests.GoRepo) if fullPath == "" { continue } @@ -553,7 +553,7 @@ func TestGoPublishWithLocalGitVcsProps(t *testing.T) { artifactCount := 0 for _, module := range publishedBuildInfo.BuildInfo.Modules { for _, artifact := range module.Artifacts { - fullPath := tests.ArtifactFullPath(artifact) + fullPath := tests.ArtifactFullPath(artifact, tests.GoRepo) if fullPath == "" { continue } diff --git a/gradle_test.go b/gradle_test.go index 36a117f29..b6e1faafd 100644 --- a/gradle_test.go +++ b/gradle_test.go @@ -707,7 +707,7 @@ func TestGradleBuildPublishWithCIVcsProps(t *testing.T) { artifactCount := 0 for _, module := range publishedBuildInfo.BuildInfo.Modules { for _, artifact := range module.Artifacts { - fullPath := tests.ArtifactFullPath(artifact) + fullPath := tests.ArtifactFullPath(artifact, tests.GradleRepo) props, err := serviceManager.GetItemProps(fullPath) assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) @@ -786,7 +786,7 @@ func TestGradleBuildPublishWithLocalGitVcsProps(t *testing.T) { artifactCount := 0 for _, module := range publishedBuildInfo.BuildInfo.Modules { for _, artifact := range module.Artifacts { - fullPath := tests.ArtifactFullPath(artifact) + fullPath := tests.ArtifactFullPath(artifact, tests.GradleRepo) props, err := serviceManager.GetItemProps(fullPath) assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) diff --git a/utils/tests/artifact_props.go b/utils/tests/artifact_props.go index 7a5ad7d08..88a032b32 100644 --- a/utils/tests/artifact_props.go +++ b/utils/tests/artifact_props.go @@ -6,10 +6,17 @@ import ( buildinfo "github.com/jfrog/build-info-go/entities" ) -func ArtifactFullPath(a buildinfo.Artifact) string { +// ArtifactFullPath builds the Artifactory item path for GetItemProps. +// When OriginalDeploymentRepo is empty (common with Gradle extractor build-info), +// defaultRepo is used as the repository prefix. +func ArtifactFullPath(a buildinfo.Artifact, defaultRepo string) string { path := strings.TrimPrefix(a.Path, "/") - if a.OriginalDeploymentRepo != "" { - return a.OriginalDeploymentRepo + "/" + path + repo := a.OriginalDeploymentRepo + if repo == "" { + repo = defaultRepo + } + if repo != "" { + return repo + "/" + path } return path } diff --git a/utils/tests/artifact_props_test.go b/utils/tests/artifact_props_test.go index 9667882e6..f77d4930d 100644 --- a/utils/tests/artifact_props_test.go +++ b/utils/tests/artifact_props_test.go @@ -10,16 +10,21 @@ import ( func TestArtifactFullPath(t *testing.T) { t.Run("uses OriginalDeploymentRepo when set", func(t *testing.T) { a := buildinfo.Artifact{OriginalDeploymentRepo: "cli-gradle-123", Path: "com/foo/1.0/foo.jar"} - assert.Equal(t, "cli-gradle-123/com/foo/1.0/foo.jar", ArtifactFullPath(a)) + assert.Equal(t, "cli-gradle-123/com/foo/1.0/foo.jar", ArtifactFullPath(a, "fallback-repo")) }) - t.Run("falls back to Path when repo empty", func(t *testing.T) { + t.Run("falls back to defaultRepo when OriginalDeploymentRepo empty", func(t *testing.T) { a := buildinfo.Artifact{Path: "com/foo/1.0/foo.jar"} - assert.Equal(t, "com/foo/1.0/foo.jar", ArtifactFullPath(a)) + assert.Equal(t, "cli-gradle-123/com/foo/1.0/foo.jar", ArtifactFullPath(a, "cli-gradle-123")) + }) + + t.Run("falls back to Path when repo empty and no default", func(t *testing.T) { + a := buildinfo.Artifact{Path: "com/foo/1.0/foo.jar"} + assert.Equal(t, "com/foo/1.0/foo.jar", ArtifactFullPath(a, "")) }) t.Run("strips leading slash from Path", func(t *testing.T) { a := buildinfo.Artifact{Path: "/minimal-example/1.0/minimal-example-1.0.jar"} - assert.Equal(t, "minimal-example/1.0/minimal-example-1.0.jar", ArtifactFullPath(a)) + assert.Equal(t, "cli-gradle-123/minimal-example/1.0/minimal-example-1.0.jar", ArtifactFullPath(a, "cli-gradle-123")) }) } From 722af119adef5c4060c6af7d88716d5edbeee75f Mon Sep 17 00:00:00 2001 From: attiasas Date: Mon, 15 Jun 2026 15:12:51 +0300 Subject: [PATCH 12/27] update dep to expend_vsc_detection --- go.mod | 3 ++- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index eff965693..0bcf5e5cb 100644 --- a/go.mod +++ b/go.mod @@ -243,7 +243,8 @@ require ( ) // attiasas:local_git_detection -replace github.com/jfrog/jfrog-cli-artifactory => github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260615065015-5c7cac0b0a98 +// attiasas:expend_vsc_detection +replace github.com/jfrog/jfrog-cli-artifactory => github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260615121012-628b7b6f896e //replace github.com/gfleury/go-bitbucket-v1 => github.com/gfleury/go-bitbucket-v1 v0.0.0-20230825095122-9bc1711434ab diff --git a/go.sum b/go.sum index b11080e7e..0214ad1f2 100644 --- a/go.sum +++ b/go.sum @@ -67,8 +67,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260615065015-5c7cac0b0a98 h1:DaAcgwo8oImmU/n5qFLXnXfHLE2Li2Maa6Bt2oiHsyM= -github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260615065015-5c7cac0b0a98/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= +github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260615121012-628b7b6f896e h1:LuzFnMNs6Sp1f757MFqKXtyz43YZkteWtZKPKKZJZgo= +github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260615121012-628b7b6f896e/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s= github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= From 8cd321d5dd9c394e952eb03e77654e64dab5ae99 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 09:36:34 +0300 Subject: [PATCH 13/27] fix tests in CI --- buildinfo_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildinfo_test.go b/buildinfo_test.go index c94d4c273..5fb686c25 100644 --- a/buildinfo_test.go +++ b/buildinfo_test.go @@ -1251,7 +1251,7 @@ func TestBuildPublishWithLocalGitVcsProps(t *testing.T) { runRt(t, "upload", filepath.Join(testDir, "a1.in"), tests.RtRepo1+"/local-git-bp/", "--flat=true", "--build-name="+buildName, "--build-number="+buildNumber) - runRt(t, "build-publish", buildName, buildNumber) + runRt(t, "build-publish", buildName, buildNumber, "--dot-git-path", testDir) resultItems := getResultItemsFromArtifactory(tests.SearchAllRepo1, t) require.Greater(t, len(resultItems), 0) From b0a82a6f2e3e1cc5983916b35887f82082d6026e Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 09:59:07 +0300 Subject: [PATCH 14/27] test: add shared local-git VCS validation for build-info artifacts Co-authored-by: Cursor --- go_test.go | 32 ++---------------- gradle_test.go | 28 ++-------------- utils/tests/artifact_props.go | 52 ++++++++++++++++++++++++++++++ utils/tests/artifact_props_test.go | 9 ++++++ 4 files changed, 67 insertions(+), 54 deletions(-) diff --git a/go_test.go b/go_test.go index 249505f35..79a7a7520 100644 --- a/go_test.go +++ b/go_test.go @@ -550,33 +550,7 @@ func TestGoPublishWithLocalGitVcsProps(t *testing.T) { serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) assert.NoError(t, err) - artifactCount := 0 - for _, module := range publishedBuildInfo.BuildInfo.Modules { - for _, artifact := range module.Artifacts { - fullPath := tests.ArtifactFullPath(artifact, tests.GoRepo) - if fullPath == "" { - continue - } - - props, err := serviceManager.GetItemProps(fullPath) - assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) - assert.NotNil(t, props, "Properties are nil for artifact: %s", fullPath) - if props == nil { - continue - } - - assert.Contains(t, props.Properties, "vcs.url", "Missing vcs.url on %s", artifact.Name) - assert.Contains(t, props.Properties["vcs.url"], tests.VcsFixtureMainURL, "Wrong vcs.url on %s", artifact.Name) - - assert.Contains(t, props.Properties, "vcs.revision", "Missing vcs.revision on %s", artifact.Name) - assert.Contains(t, props.Properties["vcs.revision"], tests.VcsFixtureMainRevision, "Wrong vcs.revision on %s", artifact.Name) - - assert.Contains(t, props.Properties, "vcs.branch", "Missing vcs.branch on %s", artifact.Name) - assert.Contains(t, props.Properties["vcs.branch"], tests.VcsFixtureMainBranch, "Wrong vcs.branch on %s", artifact.Name) - - artifactCount++ - } - } - - assert.Greater(t, artifactCount, 0, "No artifacts were validated for local git VCS properties") + artifactCount := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, + tests.GoRepo, tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, artifactCount, 0) } diff --git a/gradle_test.go b/gradle_test.go index b6e1faafd..2b0d65c33 100644 --- a/gradle_test.go +++ b/gradle_test.go @@ -783,31 +783,9 @@ func TestGradleBuildPublishWithLocalGitVcsProps(t *testing.T) { serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) assert.NoError(t, err) - artifactCount := 0 - for _, module := range publishedBuildInfo.BuildInfo.Modules { - for _, artifact := range module.Artifacts { - fullPath := tests.ArtifactFullPath(artifact, tests.GradleRepo) - - props, err := serviceManager.GetItemProps(fullPath) - assert.NoError(t, err, "Failed to get properties for artifact: %s", fullPath) - assert.NotNil(t, props, "Properties are nil for artifact: %s", fullPath) - if props == nil { - continue - } - - assert.Contains(t, props.Properties, "vcs.url", "Missing vcs.url on %s", artifact.Name) - assert.Contains(t, props.Properties["vcs.url"], tests.VcsFixtureMainURL, "Wrong vcs.url on %s", artifact.Name) - - assert.Contains(t, props.Properties, "vcs.revision", "Missing vcs.revision on %s", artifact.Name) - assert.Contains(t, props.Properties["vcs.revision"], tests.VcsFixtureMainRevision, "Wrong vcs.revision on %s", artifact.Name) - - assert.Contains(t, props.Properties, "vcs.branch", "Missing vcs.branch on %s", artifact.Name) - assert.Contains(t, props.Properties["vcs.branch"], tests.VcsFixtureMainBranch, "Wrong vcs.branch on %s", artifact.Name) - - artifactCount++ - } - } - assert.Greater(t, artifactCount, 0, "No artifacts in build info") + artifactCount := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, + tests.GradleRepo, tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, artifactCount, 0) cleanGradleTest(t) } diff --git a/utils/tests/artifact_props.go b/utils/tests/artifact_props.go index 88a032b32..87959ac02 100644 --- a/utils/tests/artifact_props.go +++ b/utils/tests/artifact_props.go @@ -2,8 +2,12 @@ package tests import ( "strings" + "testing" buildinfo "github.com/jfrog/build-info-go/entities" + "github.com/jfrog/jfrog-client-go/artifactory" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // ArtifactFullPath builds the Artifactory item path for GetItemProps. @@ -20,3 +24,51 @@ func ArtifactFullPath(a buildinfo.Artifact, defaultRepo string) string { } return path } + +// ValidateLocalGitVcsPropsOnBuildInfoArtifacts fetches props for each build-info artifact +// and asserts local-git VCS fields. Returns the number of artifacts validated. +func ValidateLocalGitVcsPropsOnBuildInfoArtifacts( + t *testing.T, + serviceManager artifactory.ArtifactoryServicesManager, + publishedBuildInfo *buildinfo.PublishedBuildInfo, + defaultRepo string, + expectedURL, expectedRevision, expectedBranch string, +) int { + t.Helper() + require.NotNil(t, publishedBuildInfo) + + count := 0 + for _, module := range publishedBuildInfo.BuildInfo.Modules { + for _, artifact := range module.Artifacts { + fullPath := ArtifactFullPath(artifact, defaultRepo) + if fullPath == "" { + continue + } + + props, err := serviceManager.GetItemProps(fullPath) + require.NoError(t, err, "GetItemProps failed for %s", fullPath) + if props == nil { + assert.Fail(t, "Properties are nil for artifact: %s", fullPath) + continue + } + + assert.Contains(t, props.Properties, "vcs.url", "Missing vcs.url on %s", artifact.Name) + assert.Contains(t, props.Properties["vcs.url"], expectedURL, "Wrong vcs.url on %s", artifact.Name) + + assert.Contains(t, props.Properties, "vcs.revision", "Missing vcs.revision on %s", artifact.Name) + assert.Contains(t, props.Properties["vcs.revision"], expectedRevision, "Wrong vcs.revision on %s", artifact.Name) + + if expectedBranch != "" { + assert.Contains(t, props.Properties, "vcs.branch", "Missing vcs.branch on %s", artifact.Name) + assert.Contains(t, props.Properties["vcs.branch"], expectedBranch, "Wrong vcs.branch on %s", artifact.Name) + } + + // Local-git-only: provider/org/repo must NOT appear when CI is cleared + _, hasProvider := props.Properties["vcs.provider"] + assert.False(t, hasProvider, "vcs.provider should not be set on %s in local-git-only mode", artifact.Name) + + count++ + } + } + return count +} diff --git a/utils/tests/artifact_props_test.go b/utils/tests/artifact_props_test.go index f77d4930d..58d717002 100644 --- a/utils/tests/artifact_props_test.go +++ b/utils/tests/artifact_props_test.go @@ -28,3 +28,12 @@ func TestArtifactFullPath(t *testing.T) { assert.Equal(t, "cli-gradle-123/minimal-example/1.0/minimal-example-1.0.jar", ArtifactFullPath(a, "cli-gradle-123")) }) } + +func TestValidateLocalGitVcsPropsOnBuildInfoArtifacts_UsesArtifactFullPath(t *testing.T) { + // Smoke-test ArtifactFullPath integration used by the helper (no Artifactory call). + a := buildinfo.Artifact{ + OriginalDeploymentRepo: "", + Path: "/com/foo/1.0/foo.jar", + } + assert.Equal(t, "my-repo/com/foo/1.0/foo.jar", ArtifactFullPath(a, "my-repo")) +} From 9b97e2248efa37771035cd4ca12ebdde898ca104 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:00:37 +0300 Subject: [PATCH 15/27] test: e2e local git VCS props on conan upload Co-authored-by: Cursor --- conan_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/conan_test.go b/conan_test.go index a7334b185..6d4e4d7eb 100644 --- a/conan_test.go +++ b/conan_test.go @@ -1162,3 +1162,48 @@ func TestConanBuildPublishWithCIVcsProps(t *testing.T) { assert.Greater(t, artifactCount, 0, "No artifacts were validated for CI VCS properties") } + +// TestConanUploadWithLocalGitVcsProps verifies civcs local git fallback on conan upload. +func TestConanUploadWithLocalGitVcsProps(t *testing.T) { + initConanTest(t) + + buildName := tests.ConanBuildName + "-local-git" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + projectPath := createConanProject(t, "conan-local-git") + tests.CopyGitFixtureIntoProject(t, projectPath) + + wd, err := os.Getwd() + require.NoError(t, err) + chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath) + defer chdirCallback() + + configureConanRemote(t) + defer cleanupConanRemote() + + jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "") + require.NoError(t, jfrogCli.Exec("conan", "create", ".", "--build=missing", + "--build-name="+buildName, "--build-number="+buildNumber)) + require.NoError(t, jfrogCli.Exec("conan", "upload", "cli-test-package/*", + "-r", tests.ConanLocalRepo, "--confirm", + "--build-name="+buildName, "--build-number="+buildNumber)) + + require.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.ConanLocalRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} From 16767d2806a8a0d11b82a7ede542c448f7749494 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:03:32 +0300 Subject: [PATCH 16/27] test: e2e local git VCS props on maven build-publish Co-authored-by: Cursor --- maven_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/maven_test.go b/maven_test.go index 54a55ee51..2fac48ecc 100644 --- a/maven_test.go +++ b/maven_test.go @@ -839,3 +839,42 @@ func TestMavenBuildPublishWithCIVcsProps(t *testing.T) { cleanMavenTest(t) } + +// TestMavenBuildPublishWithLocalGitVcsProps verifies local git VCS props on Maven artifacts +// when running build-publish with VCS collection enabled and no CI env. +func TestMavenBuildPublishWithLocalGitVcsProps(t *testing.T) { + initMavenTest(t, false) + buildName := tests.MvnBuildName + "-local-git" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + pomDir := createSimpleMavenProject(t) + tests.CopyGitFixtureIntoProject(t, pomDir) + + oldHomeDir := changeWD(t, pomDir) + defer clientTestUtils.ChangeDirAndAssert(t, oldHomeDir) + + err := runMaven(t, func(t *testing.T) string { return pomDir }, tests.MavenConfig, + "install", "--build-name="+buildName, "--build-number="+buildNumber) + require.NoError(t, err) + + runRt(t, "build-publish", buildName, buildNumber) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found, "Build info was not found") + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.MvnRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) + + cleanMavenTest(t) +} From f7dc24ff29d2fb49c1713ba71632ca2243772579 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:04:32 +0300 Subject: [PATCH 17/27] test: e2e local git VCS props on Gradle FlexPack publish Co-authored-by: Cursor --- gradle_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/gradle_test.go b/gradle_test.go index 2b0d65c33..725c2641d 100644 --- a/gradle_test.go +++ b/gradle_test.go @@ -789,3 +789,48 @@ func TestGradleBuildPublishWithLocalGitVcsProps(t *testing.T) { cleanGradleTest(t) } + +// TestGradleFlexPackPublishWithLocalGitVcsProps verifies local git VCS on FlexPack publish path. +func TestGradleFlexPackPublishWithLocalGitVcsProps(t *testing.T) { + initGradleTest(t) + buildName := "gradle-flexpack-local-git" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + setEnvCallBack := clientTestUtils.SetEnvWithCallbackAndAssert(t, "JFROG_RUN_NATIVE", "true") + defer setEnvCallBack() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + buildGradlePath := createGradleProject(t, "civcsproject") + projectDir := filepath.Dir(buildGradlePath) + tests.CopyGitFixtureIntoProject(t, projectDir) + + oldHomeDir := changeWD(t, projectDir) + defer clientTestUtils.ChangeDirAndAssert(t, oldHomeDir) + + runJfrogCli(t, "gradle", "clean", "publish", "--build-name="+buildName, "--build-number="+buildNumber) + require.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + clientTestUtils.ChangeDirAndAssert(t, oldHomeDir) + + var publishedBuildInfo *buildinfo.PublishedBuildInfo + var found bool + require.Eventually(t, func() bool { + var biErr error + publishedBuildInfo, found, biErr = tests.GetBuildInfo(serverDetails, buildName, buildNumber) + return biErr == nil && found + }, 30*time.Second, 2*time.Second) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.GradleRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) + + cleanGradleTest(t) +} From 447593b130369ea2e6ea181e4c946774b933def8 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:05:26 +0300 Subject: [PATCH 18/27] test: e2e local git VCS props on npm publish Co-authored-by: Cursor --- npm_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/npm_test.go b/npm_test.go index c5e679664..70df1a14d 100644 --- a/npm_test.go +++ b/npm_test.go @@ -1648,3 +1648,43 @@ func TestNpmBuildPublishWithCIVcsProps(t *testing.T) { } assert.Greater(t, artifactCount, 0, "No artifacts in build info") } + +// TestNpmPublishWithLocalGitVcsProps verifies local git VCS props on npm artifacts +// when running publish followed by build-publish with VCS collection enabled and no CI env. +func TestNpmPublishWithLocalGitVcsProps(t *testing.T) { + initNpmTest(t) + defer cleanNpmTest(t) + + buildName := "npm-local-git-test" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + wd, err := os.Getwd() + require.NoError(t, err) + + npmPath := initNpmProjectTest(t) + tests.CopyGitFixtureIntoProject(t, npmPath) + chdirCallBack := clientTestUtils.ChangeDirWithCallback(t, wd, npmPath) + defer chdirCallBack() + + runJfrogCli(t, "npm", "publish", "--build-name="+buildName, "--build-number="+buildNumber) + require.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + clientTestUtils.ChangeDirAndAssert(t, wd) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.NpmRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} From 093068eeb837833827321efe096c3ba24616630e Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:05:28 +0300 Subject: [PATCH 19/27] test: e2e local git VCS props on pnpm publish Co-authored-by: Cursor --- pnpm_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pnpm_test.go b/pnpm_test.go index b471a894d..f87da7f69 100644 --- a/pnpm_test.go +++ b/pnpm_test.go @@ -891,6 +891,52 @@ func TestPnpmBuildPublishWithCIVcsProps(t *testing.T) { assert.Greater(t, artifactCount, 0, "No artifacts in build info") } +// TestPnpmPublishWithLocalGitVcsProps verifies local git VCS props on pnpm artifacts +// when running publish followed by build-publish with VCS collection enabled and no CI env. +func TestPnpmPublishWithLocalGitVcsProps(t *testing.T) { + initPnpmTest(t) + defer cleanPnpmTest(t) + + buildName := "pnpm-local-git-test" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + wd, err := os.Getwd() + assert.NoError(t, err) + defer clientTestUtils.ChangeDirAndAssert(t, wd) + + pnpmProjectPath := createPnpmProject(t, "pnpm-local-git") + projectDir := filepath.Dir(pnpmProjectPath) + tests.CopyGitFixtureIntoProject(t, projectDir) + prepareArtifactoryForPnpmBuild(t, projectDir) + clientTestUtils.ChangeDirAndAssert(t, projectDir) + + cleanupAuth := setupPnpmPublishAuth(t, tests.NpmRepo) + defer cleanupAuth() + + runJfrogCli(t, "pnpm", "publish", "--no-git-checks", + "--build-name="+buildName, "--build-number="+buildNumber) + assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + clientTestUtils.ChangeDirAndAssert(t, wd) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + assert.NoError(t, err) + assert.True(t, found, "Build info was not found") + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + assert.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.NpmRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} + // TestPnpmInstallAndPublishWithProject verifies that pnpm install and publish work correctly // when targeting a non-default Artifactory project (RTECO-924). // The test uses --project flag with install, publish, and build-publish to verify that From d95694453d3a27ee72bd59b767fc3219d7461dd7 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:09:00 +0300 Subject: [PATCH 20/27] test: e2e local git VCS props on twine publish Co-authored-by: Cursor --- pip_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/pip_test.go b/pip_test.go index 4ee8ac9d6..3a39bd1ab 100644 --- a/pip_test.go +++ b/pip_test.go @@ -777,3 +777,50 @@ func TestTwineBuildPublishWithCIVcsProps(t *testing.T) { assert.Greater(t, artifactCount, 0, "No artifacts were validated for CI VCS properties") } + +func TestTwinePublishWithLocalGitVcsProps(t *testing.T) { + initPipTest(t) + + buildName := tests.PipBuildName + "-local-git" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + oldHomeDir, newHomeDir := prepareHomeDir(t) + defer func() { + clientTestUtils.SetEnvAndAssert(t, coreutils.HomeDir, oldHomeDir) + clientTestUtils.RemoveAllAndAssert(t, newHomeDir) + }() + + cleanVirtualEnv, err := prepareVirtualEnv(t) + require.NoError(t, err) + defer cleanVirtualEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + projectPath := createPypiProject(t, "twine-local-git", "pyproject", "twine") + tests.CopyGitFixtureIntoProject(t, projectPath) + + wd, err := os.Getwd() + require.NoError(t, err) + chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath) + defer chdirCallback() + + jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "") + require.NoError(t, jfrogCli.Exec("twine", "upload", "dist/*", + "--build-name="+buildName, "--build-number="+buildNumber)) + require.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.PypiVirtualRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} From eee0ded32f85bd19ac84acf90b927f00431e1246 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:09:00 +0300 Subject: [PATCH 21/27] test: e2e local git VCS props on uv publish Co-authored-by: Cursor --- uv_test.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/uv_test.go b/uv_test.go index afe08fffa..94132fe67 100644 --- a/uv_test.go +++ b/uv_test.go @@ -1490,6 +1490,39 @@ func TestUvBuildPublishWithCIVcsProps(t *testing.T) { assert.Greater(t, artifactCount, 0, "no artifacts were validated for CI VCS properties") } +func TestUvPublishWithLocalGitVcsProps(t *testing.T) { + initUvTest(t) + defer cleanUvTest(t) + + buildName := tests.UvBuildName + "-local-git" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + projectPath := createUvProject(t, "uv-local-git", "uvproject") + tests.CopyGitFixtureIntoProject(t, projectPath) + + require.NoError(t, runUvCmd(t, projectPath, "build")) + require.NoError(t, runUvCmd(t, projectPath, "publish", + "--build-name="+buildName, "--build-number="+buildNumber)) + require.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found) + + serviceManager, err := artUtils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.UvLocalRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} + // --------------------------------------------------------------------------- // P0 — Artifact sha256 not "untrusted" in Artifactory (#Cat7 NEW requirement) // --------------------------------------------------------------------------- From c3b15bcb2c25717af7318fc9e99484a2810f3be8 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:11:27 +0300 Subject: [PATCH 22/27] test: e2e local git VCS props on docker push Co-authored-by: Cursor --- docker_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docker_test.go b/docker_test.go index f0ac37a0a..2b9752f66 100644 --- a/docker_test.go +++ b/docker_test.go @@ -1574,6 +1574,55 @@ CMD ["echo", "Hello from CI VCS test"]`, baseImage) assert.Greater(t, artifactCount, 0, "No artifacts in build info") } +// TestDockerPushWithLocalGitVcsProps verifies local git VCS props on Docker artifacts +// when running build-publish with VCS collection enabled and no CI env. +func TestDockerPushWithLocalGitVcsProps(t *testing.T) { + cleanup := initDockerBuildTest(t) + defer cleanup() + + buildName := "docker-local-git-test" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + registryHost := *tests.ContainerRegistry + if parsedURL, err := url.Parse(registryHost); err == nil && parsedURL.Host != "" { + registryHost = parsedURL.Host + } + imageName := path.Join(registryHost, tests.OciLocalRepo, "test-local-git-docker") + imageTag := imageName + ":v1" + + workspace, err := filepath.Abs(tests.Out) + require.NoError(t, err) + require.NoError(t, fileutils.CreateDirIfNotExist(workspace)) + tests.CopyGitFixtureIntoProject(t, workspace) + + baseImage := path.Join(registryHost, tests.OciRemoteRepo, "alpine:latest") + dockerfileContent := fmt.Sprintf("FROM %s\nCMD [\"echo\", \"local git vcs test\"]", baseImage) + dockerfilePath := filepath.Join(workspace, "Dockerfile") + require.NoError(t, os.WriteFile(dockerfilePath, []byte(dockerfileContent), 0o644)) //#nosec G703 -- test code, path built from test workspace + + runJfrogCli(t, "rt", "bc", buildName, buildNumber) + runJfrogCli(t, "docker", "build", "-t", imageTag, "--push", "-f", dockerfilePath, + "--build-name="+buildName, "--build-number="+buildNumber, workspace) + runRt(t, "build-publish", buildName, buildNumber) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.OciLocalRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} + // TestSetupDockerCommand verifies `jf setup docker --url ...` end-to-end. // // Guards RTECO-1352: configureContainer (in jfrog-cli-artifactory) used to read From d582695c0ab9a1a3188f471755d56ad29c584482 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:11:27 +0300 Subject: [PATCH 23/27] test: e2e local git VCS props on helm push Co-authored-by: Cursor --- helm_test.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/helm_test.go b/helm_test.go index a472b2967..aa051ba33 100644 --- a/helm_test.go +++ b/helm_test.go @@ -958,6 +958,99 @@ func TestHelmBuildPublishWithCIVcsProps(t *testing.T) { assert.Greater(t, artifactCount, 0, "No artifacts were validated for CI VCS properties") } +// TestHelmPushWithLocalGitVcsProps verifies local git VCS props on Helm artifacts +// when running build-publish with VCS collection enabled and no CI env. +func TestHelmPushWithLocalGitVcsProps(t *testing.T) { + initHelmTest(t) + defer cleanHelmTest(t) + + buildName := tests.HelmBuildName + "-local-git" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + chartDir := createTestHelmChartWithDependencies(t, "test-chart-local-git", "0.2.0") + defer func() { + if err := os.RemoveAll(chartDir); err != nil { + t.Logf("Warning: Failed to remove test chart directory %s: %v", chartDir, err) + } + }() + tests.CopyGitFixtureIntoProject(t, chartDir) + + originalDir, err := os.Getwd() + require.NoError(t, err) + defer func() { + if err := os.Chdir(originalDir); err != nil { + t.Logf("Warning: Failed to change back to original directory: %v", err) + } + }() + require.NoError(t, os.Chdir(chartDir)) + + helmCmd := exec.Command("helm", "dependency", "update") + helmCmd.Dir = chartDir + require.NoError(t, helmCmd.Run(), "helm dependency update should succeed") + + jfrogCli := coreTests.NewJfrogCli(execMain, "jfrog", "") + require.NoError(t, jfrogCli.Exec("helm", "package", ".", + "--build-name="+buildName, "--build-number="+buildNumber), "helm package should succeed") + + chartFiles, err := filepath.Glob(filepath.Join(chartDir, "*.tgz")) + require.NoError(t, err) + require.NotEmpty(t, chartFiles, "Chart package file should be created") + chartFile := filepath.Base(chartFiles[0]) + + parsedURL, err := url.Parse(serverDetails.ArtifactoryUrl) + require.NoError(t, err) + registryHost := parsedURL.Host + registryURL := fmt.Sprintf("oci://%s/%s", registryHost, tests.HelmLocalRepo) + + if !isRepoExist(tests.HelmLocalRepo) { + t.Skipf("Repository %s does not exist. Skipping test.", tests.HelmLocalRepo) + } + + err = loginHelmRegistry(t, registryHost) + if err != nil { + errorMsg := strings.ToLower(err.Error()) + if strings.Contains(errorMsg, "account temporarily locked") { + t.Skip("Artifactory account is temporarily locked. Skipping test.") + } + if strings.Contains(errorMsg, "http response to https") || + strings.Contains(errorMsg, "tls: first record does not look like a tls handshake") { + t.Skip("Helm registry login failed due to HTTPS/HTTP mismatch. Skipping test.") + } + } + require.NoError(t, err, "helm registry login should succeed") + + err = jfrogCli.Exec("helm", "push", chartFile, registryURL, + "--build-name="+buildName, "--build-number="+buildNumber) + if err != nil { + errorMsg := strings.ToLower(err.Error()) + if strings.Contains(errorMsg, "404") || + strings.Contains(errorMsg, "not found") || + strings.Contains(errorMsg, "exit status 1") { + t.Skip("OCI registry API not accessible (404). Skipping test.") + } + } + require.NoError(t, err, "helm push should succeed") + + require.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.HelmLocalRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} + // InitHelmTests initializes Helm tests func InitHelmTests() { initArtifactoryCli() From 5ba23a31802f98aa4b9e643ec441952a75fbbc1f Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:13:12 +0300 Subject: [PATCH 24/27] test: e2e local git VCS props on nix copy Co-authored-by: Cursor --- nix_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/nix_test.go b/nix_test.go index 9eac5a39d..4380ab43e 100644 --- a/nix_test.go +++ b/nix_test.go @@ -10,6 +10,7 @@ import ( buildinfo "github.com/jfrog/build-info-go/entities" biutils "github.com/jfrog/build-info-go/utils" + "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" @@ -495,6 +496,60 @@ func TestNixCopy_VirtualToLocalResolution(t *testing.T) { inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) } +func TestNixCopyWithLocalGitVcsProps(t *testing.T) { + initNixTest(t) + + oldHomeDir, newHomeDir := prepareHomeDir(t) + defer func() { + clientTestUtils.SetEnvAndAssert(t, coreutils.HomeDir, oldHomeDir) + clientTestUtils.RemoveAllAndAssert(t, newHomeDir) + }() + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + buildName := "nix-copy-local-git" + buildNumber := "1" + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + projectDir, cleanupProject := createNixProject(t, "nix-local-git", "channelproject") + defer cleanupProject() + tests.CopyGitFixtureIntoProject(t, projectDir) + + wd, err := os.Getwd() + require.NoError(t, err) + chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectDir) + defer chdirCallback() + + jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "") + err = jfrogCli.Exec("nix", "nix-build", "", "-A", "hello", + "--build-name="+buildName, "--build-number="+buildNumber) + if err != nil { + t.Skipf("nix-build not available: %v", err) + } + + toURL := fmt.Sprintf("https://%s:%s@%s/api/nix/%s/", + *tests.JfrogUser, *tests.JfrogPassword, + strings.TrimPrefix(strings.TrimPrefix(*tests.JfrogUrl, "https://"), "http://"), + tests.NixLocalRepo) + require.NoError(t, jfrogCli.Exec("nix", "copy", "--to", toURL, "./result", + "--build-name="+buildName, "--build-number="+buildNumber)) + + require.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.NixLocalRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} + func TestNixBuild_BuildOnlyNoCopy(t *testing.T) { initNixTest(t) From 295cefde4fc1c7b73d28da9232996bf0fec29a17 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:13:22 +0300 Subject: [PATCH 25/27] test: e2e local git VCS props on huggingface upload Co-authored-by: Cursor --- huggingface_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/huggingface_test.go b/huggingface_test.go index f71ad8ac9..2007143aa 100644 --- a/huggingface_test.go +++ b/huggingface_test.go @@ -10,9 +10,11 @@ import ( "strings" "testing" + "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" "github.com/jfrog/jfrog-cli-core/v2/utils/config" "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" coreTests "github.com/jfrog/jfrog-cli-core/v2/utils/tests" + "github.com/jfrog/jfrog-cli/inttestutils" "github.com/jfrog/jfrog-cli/utils/tests" clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests" "github.com/stretchr/testify/assert" @@ -867,6 +869,53 @@ func InitHuggingFaceTests() { createRequiredRepos() } +func TestHuggingFaceUploadWithLocalGitVcsProps(t *testing.T) { + initHuggingFaceTest(t) + defer cleanHuggingFaceTest(t) + checkHuggingFaceHubAvailable(t) + + buildName := tests.HuggingFaceBuildName + "-local-git" + buildNumber := "1" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + tempDir, err := os.MkdirTemp("", "hf-local-git-*") + require.NoError(t, err) + t.Cleanup(func() { _ = os.RemoveAll(tempDir) }) + tests.CopyGitFixtureIntoProject(t, tempDir) + + require.NoError(t, os.WriteFile(filepath.Join(tempDir, "config.json"), + []byte(`{"model_type": "local-git-vcs"}`), 0o644)) + require.NoError(t, os.WriteFile(filepath.Join(tempDir, "model.bin"), + []byte("model"), 0o644)) + + jfrogCli := coreTests.NewJfrogCli(execMain, "jfrog", "") + args := []string{ + "hf", "u", tempDir, "test-org/test-local-git-model", + "--repo-type=model", + "--build-name=" + buildName, + "--build-number=" + buildNumber, + "--repo-key=" + tests.HuggingFaceLocalRepo, + } + require.NoError(t, jfrogCli.Exec(args...)) + require.NoError(t, jfrogCli.Exec("rt", "bp", buildName, buildNumber)) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.HuggingFaceLocalRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} + // CleanHuggingFaceTests cleans up after HuggingFace tests func CleanHuggingFaceTests() { deleteCreatedRepos() From 91ca7854b5a5e26ce61c7f998079b3ee9b4c30d5 Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:13:22 +0300 Subject: [PATCH 26/27] test: e2e local git VCS props on terraform publish Co-authored-by: Cursor --- artifactory_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/artifactory_test.go b/artifactory_test.go index 9de331c31..18184cc7d 100644 --- a/artifactory_test.go +++ b/artifactory_test.go @@ -6922,6 +6922,51 @@ func terraformPublishModulesAndBuildInfo(t *testing.T, trPublishArgs []string) { assert.Len(t, buildInfo.Modules[0].Artifacts, 3) } +func TestTerraformPublishWithLocalGitVcsProps(t *testing.T) { + initArtifactoryTest(t, terraformMinArtifactoryVersion) + defer cleanArtifactoryTest() + createJfrogHomeConfig(t, true) + + buildNumber := "local-git-1" + buildName := tests.RtBuildName1 + "-local-git" + + cleanupEnv := tests.SetupLocalGitVcsEnv(t) + defer cleanupEnv() + + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + projectPath := prepareTerraformProject("terraformproject", t, true) + tests.CopyGitFixtureIntoProject(t, projectPath) + + wd, err := os.Getwd() + require.NoError(t, err) + awsDir := filepath.Join(projectPath, "aws") + chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, awsDir) + defer chdirCallback() + + trPublishArgs := []string{ + "terraform", "publish", + "--namespace=namespace", "--provider=provider", "--tag=tag", + "--exclusions=*test*", + "--build-name=" + buildName, "--build-number=" + buildNumber, + "--module=my-tr-module-local-git", + } + require.NoError(t, platformCli.WithoutCredentials().Exec(trPublishArgs...)) + require.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + require.NoError(t, err) + require.True(t, found) + + serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) + require.NoError(t, err) + + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.TerraformRepo, + tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) + assert.Greater(t, count, 0) +} + func prepareTerraformProject(projectName string, t *testing.T, copyDirs bool) string { projectPath := filepath.Join(tests.GetTestResourcesPath(), "terraform", projectName) testdataTarget := filepath.Join(tests.Out, "terraformProject") From 2da7cddd6fddeb3fe3ff6b2577289fe99b5d468f Mon Sep 17 00:00:00 2001 From: attiasas Date: Tue, 16 Jun 2026 10:17:41 +0300 Subject: [PATCH 27/27] fix(tests): correct maven repo constant and UV artifact path resolution Use MvnRepo1 for maven local-git VCS test. Append artifact.Name in ArtifactItemPath when Path is a directory (UV layout). Co-authored-by: Cursor --- maven_test.go | 2 +- utils/tests/artifact_props.go | 16 +++++++++++++++- utils/tests/artifact_props_test.go | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/maven_test.go b/maven_test.go index 2fac48ecc..8417cfe47 100644 --- a/maven_test.go +++ b/maven_test.go @@ -872,7 +872,7 @@ func TestMavenBuildPublishWithLocalGitVcsProps(t *testing.T) { serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false) require.NoError(t, err) - count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.MvnRepo, + count := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo, tests.MvnRepo1, tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch) assert.Greater(t, count, 0) diff --git a/utils/tests/artifact_props.go b/utils/tests/artifact_props.go index 87959ac02..172e2aadf 100644 --- a/utils/tests/artifact_props.go +++ b/utils/tests/artifact_props.go @@ -25,6 +25,20 @@ func ArtifactFullPath(a buildinfo.Artifact, defaultRepo string) string { return path } +// ArtifactItemPath returns the Artifactory item path for GetItemProps. +// When Name is set and not already part of Path (e.g. UV stores Path as a directory), +// Name is appended as the filename segment. +func ArtifactItemPath(a buildinfo.Artifact, defaultRepo string) string { + fullPath := ArtifactFullPath(a, defaultRepo) + if a.Name == "" { + return fullPath + } + if strings.HasSuffix(fullPath, "/"+a.Name) || strings.HasSuffix(fullPath, a.Name) { + return fullPath + } + return fullPath + "/" + a.Name +} + // ValidateLocalGitVcsPropsOnBuildInfoArtifacts fetches props for each build-info artifact // and asserts local-git VCS fields. Returns the number of artifacts validated. func ValidateLocalGitVcsPropsOnBuildInfoArtifacts( @@ -40,7 +54,7 @@ func ValidateLocalGitVcsPropsOnBuildInfoArtifacts( count := 0 for _, module := range publishedBuildInfo.BuildInfo.Modules { for _, artifact := range module.Artifacts { - fullPath := ArtifactFullPath(artifact, defaultRepo) + fullPath := ArtifactItemPath(artifact, defaultRepo) if fullPath == "" { continue } diff --git a/utils/tests/artifact_props_test.go b/utils/tests/artifact_props_test.go index 58d717002..aaf8bbe01 100644 --- a/utils/tests/artifact_props_test.go +++ b/utils/tests/artifact_props_test.go @@ -37,3 +37,21 @@ func TestValidateLocalGitVcsPropsOnBuildInfoArtifacts_UsesArtifactFullPath(t *te } assert.Equal(t, "my-repo/com/foo/1.0/foo.jar", ArtifactFullPath(a, "my-repo")) } + +func TestArtifactItemPath_AppendsNameForDirectoryPath(t *testing.T) { + a := buildinfo.Artifact{ + OriginalDeploymentRepo: "uv-local", + Path: "my-pkg/0.1.0", + Name: "my_pkg-0.1.0-py3-none-any.whl", + } + assert.Equal(t, "uv-local/my-pkg/0.1.0/my_pkg-0.1.0-py3-none-any.whl", ArtifactItemPath(a, "")) +} + +func TestArtifactItemPath_DoesNotDoubleAppendName(t *testing.T) { + a := buildinfo.Artifact{ + OriginalDeploymentRepo: "mvn-local", + Path: "com/foo/1.0/foo.jar", + Name: "foo.jar", + } + assert.Equal(t, "mvn-local/com/foo/1.0/foo.jar", ArtifactItemPath(a, "")) +}