Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions buildinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1200,3 +1200,69 @@ func TestBuildInfoPropertiesRemovalInBadAndPublish(t *testing.T) {
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
cleanArtifactoryTest()
}

// TestBuildPublishWithCIVcsProps tests that CI VCS properties are set on artifacts
// when running build-publish in a CI environment (GitHub Actions simulated).
func TestBuildPublishWithCIVcsProps(t *testing.T) {
initArtifactoryTest(t, "")
buildName := tests.RtBuildName1 + "-civcs"
buildNumber := "1"

// Setup mock GitHub Actions environment
cleanupEnv := tests.SetupMockGitHubActionsEnv(t, "jfrog", "jfrog-cli")
defer cleanupEnv()

// Clean old build
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)

// Upload files with build name and number
specFile, err := tests.CreateSpec(tests.UploadFlatNonRecursive)
assert.NoError(t, err)
runRt(t, "upload", "--spec="+specFile, "--build-name="+buildName, "--build-number="+buildNumber)

// Publish build info - should set CI VCS props on artifacts
runRt(t, "build-publish", buildName, buildNumber)

// Search for artifacts
resultItems := getResultItemsFromArtifactory(tests.SearchAllRepo1, t)

// Validate CI VCS properties are set
assert.Greater(t, len(resultItems), 0, "No artifacts found")
tests.ValidateCIVcsPropsOnArtifacts(t, resultItems, "github", "jfrog", "jfrog-cli")

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) {
initArtifactoryTest(t, "")
buildName := tests.RtBuildName1 + "-no-civcs"
buildNumber := "1"

// Ensure CI env vars are NOT set
assert.NoError(t, os.Unsetenv("CI"))
assert.NoError(t, os.Unsetenv("GITHUB_ACTIONS"))

// Clean old build
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)

// Upload files with build name and number
specFile, err := tests.CreateSpec(tests.UploadFlatNonRecursive)
assert.NoError(t, err)
runRt(t, "upload", "--spec="+specFile, "--build-name="+buildName, "--build-number="+buildNumber)

// Publish build info - should NOT set CI VCS props (not in CI)
runRt(t, "build-publish", buildName, buildNumber)

// Search for artifacts
resultItems := getResultItemsFromArtifactory(tests.SearchAllRepo1, t)

// Validate CI VCS properties are NOT set
assert.Greater(t, len(resultItems), 0, "No artifacts found")
tests.ValidateNoCIVcsPropsOnArtifacts(t, resultItems)

cleanArtifactoryTest()
}
72 changes: 72 additions & 0 deletions docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1247,3 +1247,75 @@ func TestContainerFatManifestPullWithSha(t *testing.T) {
}

}

// TestDockerBuildPublishWithCIVcsProps tests that CI VCS properties are set on Docker artifacts
// when running build-publish in a CI environment (GitHub Actions simulated).
func TestDockerBuildPublishWithCIVcsProps(t *testing.T) {
cleanup := initDockerBuildTest(t)
defer cleanup()

buildName := "docker-civcs-test"
buildNumber := "1"

// Setup mock GitHub Actions environment
cleanupEnv := tests.SetupMockGitHubActionsEnv(t, "myorg", "docker-project")
defer cleanupEnv()

// Clean old build
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)

// Extract hostname from ContainerRegistry
registryHost := *tests.ContainerRegistry
if parsedURL, err := url.Parse(registryHost); err == nil && parsedURL.Host != "" {
registryHost = parsedURL.Host
}

// Construct image name
imageName := path.Join(registryHost, tests.OciLocalRepo, "test-civcs-docker")
imageTag := imageName + ":v1"

// Create test workspace
workspace, err := filepath.Abs(tests.Out)
assert.NoError(t, err)
assert.NoError(t, fileutils.CreateDirIfNotExist(workspace))

// Create simple Dockerfile
baseImage := path.Join(registryHost, tests.OciRemoteRepo, "alpine:latest")
dockerfileContent := fmt.Sprintf(`FROM %s
CMD ["echo", "Hello from CI VCS test"]`, baseImage)

dockerfilePath := filepath.Join(workspace, "Dockerfile")
assert.NoError(t, os.WriteFile(dockerfilePath, []byte(dockerfileContent), 0644))

// Clean build before test
runJfrogCli(t, "rt", "bc", buildName, buildNumber)

// Run docker build with build-info
runJfrogCli(t, "docker", "build", "-t", imageTag, "--push", "-f", dockerfilePath, "--build-name="+buildName, "--build-number="+buildNumber, workspace)

// Publish build info - should set CI VCS props on Docker layers
runRt(t, "build-publish", buildName, buildNumber)

// Validate build info was published with artifacts
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
if err != nil {
assert.NoError(t, err)
return
}
if !found {
assert.True(t, found, "build info was expected to be found")
return
}

// Get artifact count from build info
artifactCount := 0
for _, module := range publishedBuildInfo.BuildInfo.Modules {
artifactCount += len(module.Artifacts)
}
assert.Greater(t, artifactCount, 0, "No Docker artifacts in build info")

// Note: Docker layers may have different property validation requirements
// due to the nature of how Docker image manifests and layers work.
// The test validates that build-publish completed without errors with CI VCS env set.
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,8 @@ replace github.com/gfleury/go-bitbucket-v1 => github.com/gfleury/go-bitbucket-v1
// replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security dev

// replace github.com/jfrog/build-info-go => github.com/reshmifrog/build-info-go v1.8.9-0.20260106113011-c7f131cea484

// Local development replaces
replace github.com/jfrog/build-info-go => github.com/bhanurp/build-info-go v1.10.10-0.20260116034640-a49747218450

replace github.com/jfrog/jfrog-cli-artifactory => github.com/bhanurp/jfrog-cli-artifactory v0.1.12-0.20260116041909-2ef5f0919378
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,10 @@ github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=
github.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bhanurp/build-info-go v1.10.10-0.20260116034640-a49747218450 h1:QZ8IJpc3mkZ/zBWB0C1DHm6H+4u/hzBTsE6QL3A41uM=
github.com/bhanurp/build-info-go v1.10.10-0.20260116034640-a49747218450/go.mod h1:+OCtMb22/D+u7Wne5lzkjJjaWr0LRZcHlDwTH86Mpwo=
github.com/bhanurp/jfrog-cli-artifactory v0.1.12-0.20260116041909-2ef5f0919378 h1:+Ns0O1/C8cMi0wiTJMdt3vZHJyYkeExRwGfHGFZlNEM=
github.com/bhanurp/jfrog-cli-artifactory v0.1.12-0.20260116041909-2ef5f0919378/go.mod h1:cIFLe8nP5cv+UUunpusVIZ+QE3pdaXjxpZbblpXUdjQ=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
Expand Down Expand Up @@ -1204,8 +1208,6 @@ github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP
github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
github.com/jfrog/archiver/v3 v3.6.1 h1:LOxnkw9pOn45DzCbZNFV6K0+6dCsQ0L8mR3ZcujO5eI=
github.com/jfrog/archiver/v3 v3.6.1/go.mod h1:VgR+3WZS4N+i9FaDwLZbq+jeU4B4zctXL+gL4EMzfLw=
github.com/jfrog/build-info-go v1.13.1-0.20260107080257-82671efa69a2 h1:I3D+ApaOhHfICPkRlU8WwOY/IlHs4lfBIZC8NNHSDtQ=
github.com/jfrog/build-info-go v1.13.1-0.20260107080257-82671efa69a2/go.mod h1:+OCtMb22/D+u7Wne5lzkjJjaWr0LRZcHlDwTH86Mpwo=
github.com/jfrog/froggit-go v1.20.6 h1:Xp7+LlEh0m1KGrQstb+u0aGfjRUtv1eh9xQBV3571jQ=
github.com/jfrog/froggit-go v1.20.6/go.mod h1:obSG1SlsWjktkuqmKtpq7MNTTL63e0ot+ucTnlOMV88=
github.com/jfrog/go-mockhttp v0.3.1 h1:/wac8v4GMZx62viZmv4wazB5GNKs+GxawuS1u3maJH8=
Expand All @@ -1216,8 +1218,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.20251231144110-a68c3ac11c7a h1:XoJ3w2AFi7zniimALNK3idw9bzY9MwB/FM45TMgxYAY=
github.com/jfrog/jfrog-cli-application v1.0.2-0.20251231144110-a68c3ac11c7a/go.mod h1:xum2HquWO5uExa/A7MQs3TgJJVEeoqTR+6Z4mfBr1Xw=
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260107090044-56a45e5c560e h1:+qB6eWbzeSOh5i6Pc0sC9arG8r5f6GLZm722jDyQ6nI=
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260107090044-56a45e5c560e/go.mod h1:U/1q7jEO0YGSAWZEZiEmo0lZHI48xBorsFuL/F8C1fU=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260106204841-744f3f71817b h1:gGGmYXuYvcNns1BnLQI13lC+pgMxrmenx+ramtolQuA=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260106204841-744f3f71817b/go.mod h1:+Hnaikp/xCSPD/q7txxRy4Zc0wzjW/usrCSf+6uONSQ=
github.com/jfrog/jfrog-cli-evidence v0.8.3-0.20251225153025-9d8ac181d615 h1:y5an0bojHL00ipHP1QuBUrVcP+XK+yZHHOJ/r1I0RUM=
Expand Down
46 changes: 46 additions & 0 deletions gradle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -640,3 +640,49 @@ func prepareGradleSetupTest(t *testing.T) func() {
restoreDir()
}
}

// TestGradleBuildPublishWithCIVcsProps tests that CI VCS properties are set on Gradle artifacts
// when running build-publish in a CI environment (GitHub Actions simulated).
func TestGradleBuildPublishWithCIVcsProps(t *testing.T) {
initGradleTest(t)
buildName := "gradle-civcs-test"
buildNumber := "1"

// Setup mock GitHub Actions environment
cleanupEnv := tests.SetupMockGitHubActionsEnv(t, "myorg", "gradle-project")
defer cleanupEnv()

// Clean old build
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)

// Create Gradle project and config
buildGradlePath := createGradleProject(t, "gradleproject")
configFilePath := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "buildspecs", tests.GradleConfig)
destPath := filepath.Join(filepath.Dir(buildGradlePath), ".jfrog", "projects")
createConfigFile(destPath, configFilePath, t)

oldHomeDir := changeWD(t, filepath.Dir(buildGradlePath))
defer clientTestUtils.ChangeDirAndAssert(t, oldHomeDir)

// Windows compatibility
buildGradlePath = strings.ReplaceAll(buildGradlePath, `\`, "/")

// Run Gradle build with build info collection
runJfrogCli(t, "gradle", "clean", "artifactoryPublish", "-b"+buildGradlePath, "--build-name="+buildName, "--build-number="+buildNumber)

// Publish build info - should set CI VCS props on artifacts
assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber))

// Search for deployed Gradle artifacts
searchSpec, err := tests.CreateSpec(tests.SearchAllGradle)
assert.NoError(t, err)
resultItems := getResultItemsFromArtifactory(searchSpec, t)

// Validate CI VCS properties are set on Gradle artifacts
if len(resultItems) > 0 {
tests.ValidateCIVcsPropsOnArtifacts(t, resultItems, "github", "myorg", "gradle-project")
}

cleanGradleTest(t)
}
35 changes: 35 additions & 0 deletions maven_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,3 +668,38 @@ func readConfigFileCreated(t *testing.T) commands.ConfigFile {
assert.NoError(t, err)
return configFile
}

// TestMavenBuildPublishWithCIVcsProps tests that CI VCS properties are set on Maven artifacts
// when running build-publish in a CI environment (GitHub Actions simulated).
func TestMavenBuildPublishWithCIVcsProps(t *testing.T) {
initMavenTest(t, false)
buildName := tests.MvnBuildName + "-civcs"
buildNumber := "1"

// Setup mock GitHub Actions environment
cleanupEnv := tests.SetupMockGitHubActionsEnv(t, "myorg", "maven-project")
defer cleanupEnv()

// Clean old build
inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)
defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails)

// Run Maven build with build info collection
err := runMaven(t, createSimpleMavenProject, tests.MavenConfig, "install", "--build-name="+buildName, "--build-number="+buildNumber)
assert.NoError(t, err)

// Publish build info - should set CI VCS props on artifacts
runRt(t, "build-publish", buildName, buildNumber)

// Search for deployed Maven artifacts
searchSpec, err := tests.CreateSpec(tests.SearchAllMaven)
assert.NoError(t, err)
resultItems := getResultItemsFromArtifactory(searchSpec, t)

// Validate CI VCS properties are set on Maven artifacts
if len(resultItems) > 0 {
tests.ValidateCIVcsPropsOnArtifacts(t, resultItems, "github", "myorg", "maven-project")
}

cleanMavenTest(t)
}
42 changes: 42 additions & 0 deletions npm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1272,3 +1272,45 @@ func containsTarName(tarName string, expectedTars []string) bool {
}
return isTarPresent
}

// TestNpmBuildPublishWithCIVcsProps tests that CI VCS properties are set on npm artifacts
// when running build-publish in a CI environment (GitHub Actions simulated).
func TestNpmBuildPublishWithCIVcsProps(t *testing.T) {
initNpmTest(t)
defer cleanNpmTest(t)

buildName := "npm-civcs-test"
buildNumber := "1"

// Setup mock GitHub Actions environment
cleanupEnv := tests.SetupMockGitHubActionsEnv(t, "myorg", "npm-project")
defer cleanupEnv()

// Clean old build
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)

// Setup npm project
npmProjectPath := initNpmProjectTest(t)
clientTestUtils.ChangeDirAndAssert(t, npmProjectPath)

// Run npm publish with build info collection
runJfrogCli(t, "npm", "publish", "--build-name="+buildName, "--build-number="+buildNumber)

// Publish build info - should set CI VCS props on artifacts
assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber))

// Search for published npm package
searchSpec, err := tests.CreateSpec(tests.SearchAllNpm)
assert.NoError(t, err)
resultItems := getResultItemsFromArtifactory(searchSpec, t)

// Validate CI VCS properties are set on npm artifacts
if len(resultItems) > 0 {
tests.ValidateCIVcsPropsOnArtifacts(t, resultItems, "github", "myorg", "npm-project")
}
}
72 changes: 72 additions & 0 deletions utils/tests/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -770,3 +770,75 @@ func SkipTest(reason string) {
log.Info(reason)
os.Exit(0)
}

// SetupMockGitHubActionsEnv sets up mock GitHub Actions CI environment variables.
// Returns a cleanup function to restore original env vars.
func SetupMockGitHubActionsEnv(t *testing.T, org, repo string) func() {
callbacks := []func(){}

// CI=true (required for all CI providers)
callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "CI", "true"))

// GitHub Actions specific vars
callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_ACTIONS", "true"))
callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_WORKFLOW", "test-workflow"))
callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_RUN_ID", "12345"))
callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_REPOSITORY_OWNER", org))
callbacks = append(callbacks, tests.SetEnvWithCallbackAndAssert(t, "GITHUB_REPOSITORY", org+"/"+repo))

return func() {
for _, cb := range callbacks {
cb()
}
}
}

// 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 {
propertiesMap := ConvertPropertiesToMap(item.Properties)

// Validate vcs.provider
if expectedProvider != "" {
vals, ok := propertiesMap["vcs.provider"]
assert.True(t, ok, "Missing vcs.provider on %s", item.Name)
assert.Contains(t, vals, expectedProvider, "Wrong vcs.provider on %s", item.Name)
}

// Validate vcs.org
if expectedOrg != "" {
vals, ok := propertiesMap["vcs.org"]
assert.True(t, ok, "Missing vcs.org on %s", item.Name)
assert.Contains(t, vals, expectedOrg, "Wrong vcs.org on %s", item.Name)
}

// Validate vcs.repo
if expectedRepo != "" {
vals, ok := propertiesMap["vcs.repo"]
assert.True(t, ok, "Missing vcs.repo on %s", item.Name)
assert.Contains(t, vals, expectedRepo, "Wrong vcs.repo on %s", item.Name)
}
}
}

// ConvertPropertiesToMap converts a slice of Property to a map for easier lookup.
func ConvertPropertiesToMap(properties []utils.Property) map[string][]string {
propsMap := make(map[string][]string)
for _, prop := range properties {
propsMap[prop.Key] = append(propsMap[prop.Key], prop.Value)
}
return propsMap
}

// ValidateNoCIVcsPropsOnArtifacts validates that CI VCS properties are NOT set on artifacts.
func ValidateNoCIVcsPropsOnArtifacts(t *testing.T, resultItems []utils.ResultItem) {
for _, item := range resultItems {
propertiesMap := ConvertPropertiesToMap(item.Properties)
_, hasProvider := propertiesMap["vcs.provider"]
_, hasOrg := propertiesMap["vcs.org"]
_, hasRepo := propertiesMap["vcs.repo"]
assert.False(t, hasProvider, "vcs.provider should not be set when not in CI on %s", item.Name)
assert.False(t, hasOrg, "vcs.org should not be set when not in CI on %s", item.Name)
assert.False(t, hasRepo, "vcs.repo should not be set when not in CI on %s", item.Name)
}
}
Loading