Skip to content
Open
36 changes: 36 additions & 0 deletions buildinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, "--dot-git-path", testDir)

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) {
Expand Down
52 changes: 52 additions & 0 deletions conan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os/exec"
"path/filepath"
"strconv"
"strings"
"testing"

buildinfo "github.com/jfrog/build-info-go/entities"
Expand Down Expand Up @@ -1162,3 +1163,54 @@ 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)

conanfile := filepath.Join(projectPath, "conanfile.py")
data, err := os.ReadFile(conanfile)
require.NoError(t, err)
patched := strings.ReplaceAll(string(data), `name = "cli-test-package"`, `name = "cli-test-package-local-git"`)
require.NoError(t, os.WriteFile(conanfile, []byte(patched), 0o644)) //#nosec G703 -- test code, path built from createConanProject temp dir

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-local-git/*",
"-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)
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,9 @@ require (
sigs.k8s.io/yaml v1.6.0 // indirect
)

// attiasas:expend_vsc_detection
replace github.com/jfrog/jfrog-cli-artifactory => github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260623073101-d9677917e68d

//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
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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-20260623073101-d9677917e68d h1:2GtEL0QARCe/D976gddr8GDr42WfXS9TInvkohoDd5s=
github.com/attiasas/jfrog-cli-artifactory v0.0.0-20260623073101-d9677917e68d/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=
Expand Down Expand Up @@ -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.20260617073349-d68ee3120aa8 h1:FG+SfgPgrIuBHSos4sw4KNZq2MKxebbCZ6KZZRfaYcs=
github.com/jfrog/jfrog-cli-application v1.0.2-0.20260617073349-d68ee3120aa8/go.mod h1:p8yLtbmCxxQucIbLZKnWu0F+EDtj6NLXbRQCEK/nb6o=
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260623043151-6ed368aaea7f h1:NnFf3sTty9i5k22iMNEZWlHJp49/1AlWOCU1qc9VK4A=
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260623043151-6ed368aaea7f/go.mod h1:VqV0Bed11HoBlugAEGa3RumbwnDVslEf0gKocTzLs9s=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260615072209-8ccac4f0072e h1:E3B8OyEkCsdEdGsZifTphBDUPrd00yKoemL9+l25Qj8=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260615072209-8ccac4f0072e/go.mod h1:9R90mhbczGXwW5EGlDs7F08ejQU/xdoDhYHMvzBiqgE=
github.com/jfrog/jfrog-cli-evidence v0.9.5-0.20260601141509-8df6c9a4bc9b h1:V0FxnU3xh29y8yJHWymm6rPr1MrjG1DdPQlr3ckImwk=
Expand Down
69 changes: 60 additions & 9 deletions go_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, 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
}

// Validate VCS properties
assert.Contains(t, props.Properties, "vcs.provider", "Missing vcs.provider on %s", artifact.Name)
Expand All @@ -503,3 +500,57 @@ 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 := 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)
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 := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo,
tests.GoRepo, tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch)
assert.Greater(t, artifactCount, 0)
}
106 changes: 105 additions & 1 deletion gradle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, 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
}

// Validate VCS properties
assert.Contains(t, props.Properties, "vcs.provider", "Missing vcs.provider on %s", artifact.Name)
Expand All @@ -730,3 +733,104 @@ 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.
// 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"
buildNumber := "1"

cleanupEnv := tests.SetupLocalGitVcsEnv(t)
defer cleanupEnv()

_ = os.Unsetenv("JFROG_RUN_NATIVE")

inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)

buildGradlePath := createGradleProject(t, "gradleproject")
projectDir := filepath.Dir(buildGradlePath)
tests.CopyGitFixtureIntoProject(t, projectDir)
require.FileExists(t, filepath.Join(projectDir, ".git", "HEAD"))

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)

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))

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 := tests.ValidateLocalGitVcsPropsOnBuildInfoArtifacts(t, serviceManager, publishedBuildInfo,
tests.GradleRepo, tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch)
assert.Greater(t, artifactCount, 0)

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)
}
47 changes: 47 additions & 0 deletions maven_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -839,3 +839,50 @@ 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)
require.FileExists(t, filepath.Join(pomDir, ".git", "HEAD"))

configFilePath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "buildspecs", tests.MavenConfig)
destPath := filepath.Join(pomDir, ".jfrog", "projects")
createConfigFile(destPath, configFilePath, t)
require.NoError(t, os.Rename(filepath.Join(destPath, tests.MavenConfig), filepath.Join(destPath, "maven.yaml")))

oldHomeDir := changeWD(t, pomDir)
defer clientTestUtils.ChangeDirAndAssert(t, oldHomeDir)

repoLocalSystemProp := localRepoSystemProperty + localRepoDir
args := []string{"mvn", "clean", "install", "-B", repoLocalSystemProp,
"--build-name=" + buildName, "--build-number=" + buildNumber}
require.NoError(t, runJfrogCliWithoutAssertion(args...))

// Must run build-publish from project dir so GetLocalGitVcsInfo finds the fixture .git
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.MvnRepo1,
tests.VcsFixtureMainURL, tests.VcsFixtureMainRevision, tests.VcsFixtureMainBranch)
assert.Greater(t, count, 0)

cleanMavenTest(t)
}
Loading
Loading