Skip to content
Merged
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
77 changes: 77 additions & 0 deletions artifactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6885,3 +6885,80 @@ func validateSearchPatternWithAPI(t *testing.T, pattern string, recursive bool)
readerGetErrorAndAssert(t, readerNoDate)
readerCloseAndAssert(t, readerNoDate)
}

// TestUploadMultipleFilesWithCIVcsProps tests that CI VCS properties are set on multiple uploaded files.
// This test:
// - Creates 15 files dynamically during the test
// - Uploads all files using jf rt u
// - Verifies vcs.provider, vcs.org, vcs.repo properties are set on ALL 15 files
// - Fails if any property is missing on any file
func TestUploadMultipleFilesWithCIVcsProps(t *testing.T) {
initArtifactoryTest(t, "")
const numFiles = 15

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract Magic Numbers to Constants

Suggested change
const numFiles = 15
// numFiles tests batch property setting - 15 is sufficient to verify
// properties are set on all files without making the test too slow
const numFiles = 15


// Setup GitHub Actions environment (uses real env vars on CI, mock values locally)
cleanupEnv, actualOrg, actualRepo := tests.SetupGitHubActionsEnv(t)
defer cleanupEnv()

// Create temp directory for test files
tmpDir, createTempDirCallback := coretests.CreateTempDirWithCallbackAndAssert(t)
defer createTempDirCallback()

// Create 15 files dynamically
var createdFiles []string
for i := 1; i <= numFiles; i++ {
fileName := fmt.Sprintf("civcs-test-file-%d.txt", i)
filePath := filepath.Join(tmpDir, fileName)
content := fmt.Sprintf("Test file %d for CI VCS properties verification. Timestamp: %d", i, time.Now().UnixNano())
err := os.WriteFile(filePath, []byte(content), 0644)
require.NoError(t, err, "Failed to create test file: %s", fileName)
createdFiles = append(createdFiles, fileName)
}

// Upload all files to Artifactory
uploadPattern := filepath.Join(tmpDir, "civcs-test-file-*.txt")
targetPath := tests.RtRepo1 + "/civcs-multi-upload/"
runRt(t, "upload", uploadPattern, targetPath, "--flat")

// Create service manager for getting artifact properties
serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false)
require.NoError(t, err, "Failed to create service manager")

// Verify CI VCS properties on each uploaded file
validatedCount := 0
for _, fileName := range createdFiles {
fullPath := tests.RtRepo1 + "/civcs-multi-upload/" + fileName

props, err := serviceManager.GetItemProps(fullPath)
require.NoError(t, err, "Failed to get properties for file: %s", fullPath)
require.NotNil(t, props, "Properties are nil for file: %s", fullPath)

// Validate vcs.provider property
require.Contains(t, props.Properties, "vcs.provider",
"Missing vcs.provider property on file: %s", fileName)
require.Contains(t, props.Properties["vcs.provider"], "github",
"Wrong vcs.provider value on file: %s. Expected 'github', got: %v", fileName, props.Properties["vcs.provider"])

// Validate vcs.org property
require.Contains(t, props.Properties, "vcs.org",
"Missing vcs.org property on file: %s", fileName)
require.Contains(t, props.Properties["vcs.org"], actualOrg,
"Wrong vcs.org value on file: %s. Expected '%s', got: %v", fileName, actualOrg, props.Properties["vcs.org"])

// Validate vcs.repo property
require.Contains(t, props.Properties, "vcs.repo",
"Missing vcs.repo property on file: %s", fileName)
require.Contains(t, props.Properties["vcs.repo"], actualRepo,
"Wrong vcs.repo value on file: %s. Expected '%s', got: %v", fileName, actualRepo, props.Properties["vcs.repo"])

validatedCount++
}

// Ensure we validated exactly the expected number of files
require.Equal(t, numFiles, validatedCount,
"Expected to validate %d files, but validated %d", numFiles, validatedCount)

t.Logf("Successfully validated CI VCS properties on all %d files", validatedCount)

cleanArtifactoryTest()
}
102 changes: 102 additions & 0 deletions conan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,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"
coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests"
"github.com/jfrog/jfrog-cli/inttestutils"
"github.com/jfrog/jfrog-cli/utils/tests"
Expand Down Expand Up @@ -467,3 +468,104 @@ func cleanupConanRemote() {
_ = exec.Command("conan", "remote", "remove", tests.ConanVirtualRepo).Run()
_ = exec.Command("conan", "remote", "remove", tests.ConanLocalRepo).Run()
}

// TestConanBuildPublishWithCIVcsProps tests that CI VCS properties are set on Conan artifacts
// when running build-publish in a CI environment (GitHub Actions).
// Conan packages are published via Conan client; build-publish retrieves artifact paths
// from Build Info and applies properties via batch API.
func TestConanBuildPublishWithCIVcsProps(t *testing.T) {
initConanTest(t)

buildName := tests.ConanBuildName + "-civcs"
buildNumber := "1"

// Setup GitHub Actions environment (uses real env vars on CI, mock values locally)
cleanupEnv, actualOrg, actualRepo := tests.SetupGitHubActionsEnv(t)
defer cleanupEnv()

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

// Prepare project
projectPath := createConanProject(t, "conan-civcs-test")
wd, err := os.Getwd()
require.NoError(t, err)
chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath)
defer chdirCallback()

// Configure Conan remote
configureConanRemote(t)
defer cleanupConanRemote()

jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")

// Run conan create
createArgs := []string{
"conan", "create", ".",
"--build=missing",
"--build-name=" + buildName,
"--build-number=" + buildNumber,
}
assert.NoError(t, jfrogCli.Exec(createArgs...))

// Run conan upload
uploadArgs := []string{
"conan", "upload", "cli-test-package/*",
"-r", tests.ConanLocalRepo,
"--confirm",
"--build-name=" + buildName,
"--build-number=" + buildNumber,
}
assert.NoError(t, jfrogCli.Exec(uploadArgs...))

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

// Get the published build info to find artifact paths
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
require.NoError(t, err)
require.True(t, found, "Build info was not found")

// Create service manager for getting artifact properties
serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false)
require.NoError(t, err)

// Verify VCS properties on each artifact from build info
// Note: Conan artifacts may not have OriginalDeploymentRepo set, so we use Path directly as fallback
artifactCount := 0
for _, module := range publishedBuildInfo.BuildInfo.Modules {
for _, artifact := range module.Artifacts {
// Use same fallback logic as CI VCS: OriginalDeploymentRepo + Path, or Path directly
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
}

props, err := serviceManager.GetItemProps(fullPath)
assert.NoError(t, err, "Failed to get properties 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)
assert.Contains(t, props.Properties["vcs.provider"], "github", "Wrong vcs.provider on %s", artifact.Name)

assert.Contains(t, props.Properties, "vcs.org", "Missing vcs.org on %s", artifact.Name)
assert.Contains(t, props.Properties["vcs.org"], actualOrg, "Wrong vcs.org on %s", artifact.Name)

assert.Contains(t, props.Properties, "vcs.repo", "Missing vcs.repo on %s", artifact.Name)
assert.Contains(t, props.Properties["vcs.repo"], actualRepo, "Wrong vcs.repo on %s", artifact.Name)

artifactCount++
}
}

assert.Greater(t, artifactCount, 0, "No artifacts were validated for CI VCS properties")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment says "No artifacts were validated" but message could be more specific about what went wrong.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert.Greater(t, artifactCount, 0, "No artifacts were validated for CI VCS properties")
require.Greater(t, artifactCount, 0,
"No artifacts were validated for CI VCS properties. Build info had %d modules. Check if artifacts were actually uploaded.",
len(publishedBuildInfo.BuildInfo.Modules))```

}
95 changes: 89 additions & 6 deletions go_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package main

import (
"fmt"
"github.com/jfrog/jfrog-client-go/http/httpclient"
"github.com/stretchr/testify/require"
"net/http"
"os"
"os/exec"
Expand All @@ -12,20 +10,21 @@ import (

biutils "github.com/jfrog/build-info-go/utils"

coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests"
clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests"

buildinfo "github.com/jfrog/build-info-go/entities"
"github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/golang"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/common/build"
"github.com/jfrog/jfrog-cli-core/v2/common/commands"
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests"
"github.com/jfrog/jfrog-cli/inttestutils"

"github.com/jfrog/jfrog-cli/utils/tests"
"github.com/jfrog/jfrog-client-go/http/httpclient"
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
"github.com/jfrog/jfrog-client-go/utils/log"
clientTestUtils "github.com/jfrog/jfrog-client-go/utils/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGoConfigWithModuleNameChange(t *testing.T) {
Expand Down Expand Up @@ -420,3 +419,87 @@ func TestSetupGoCommand(t *testing.T) {
assert.Equal(t, http.StatusOK, res.StatusCode)
}
}

// TestGoBuildPublishWithCIVcsProps tests that CI VCS properties are set on Go artifacts
// when running go-publish followed by build-publish in a CI environment (GitHub Actions).
// Go supports direct upload via 'jf gp' which sets CI VCS properties during upload.
func TestGoBuildPublishWithCIVcsProps(t *testing.T) {
_, cleanUpFunc := initGoTest(t)
defer cleanUpFunc()

buildName := tests.GoBuildName + "-civcs"
buildNumber := "1"

// Setup GitHub Actions environment (uses real env vars on CI, mock values locally)
cleanupEnv, actualOrg, actualRepo := tests.SetupGitHubActionsEnv(t)
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, "Failed to get current dir")

// Prepare and build the Go project
prepareGoProject("project1", t, true)
defer clientTestUtils.ChangeDirAndAssert(t, wd)

// Build the Go project with build info collection
jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")
err = execGo(jfrogCli, "go", "build", "--mod=mod", "--build-name="+buildName, "--build-number="+buildNumber)
assert.NoError(t, err)

// Publish the Go module to Artifactory with build info collection
// This is where CI VCS properties should be set during direct upload
err = execGo(jfrogCli, "gp", "--build-name="+buildName, "--build-number="+buildNumber, "v1.0.0")
assert.NoError(t, err)

// Publish build info - should also set CI VCS props via batch update if not already set
err = execGo(artifactoryCli, "bp", buildName, buildNumber)
assert.NoError(t, err)

// Get the published build info to find artifact paths
publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber)
assert.NoError(t, err)
assert.True(t, found, "Build info was not found")

// Create service manager for getting artifact properties
serviceManager, err := utils.CreateServiceManager(serverDetails, 3, 1000, false)
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
}

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)

// Validate VCS properties
assert.Contains(t, props.Properties, "vcs.provider", "Missing vcs.provider on %s", artifact.Name)
assert.Contains(t, props.Properties["vcs.provider"], "github", "Wrong vcs.provider on %s", artifact.Name)

assert.Contains(t, props.Properties, "vcs.org", "Missing vcs.org on %s", artifact.Name)
assert.Contains(t, props.Properties["vcs.org"], actualOrg, "Wrong vcs.org on %s", artifact.Name)

assert.Contains(t, props.Properties, "vcs.repo", "Missing vcs.repo on %s", artifact.Name)
assert.Contains(t, props.Properties["vcs.repo"], actualRepo, "Wrong vcs.repo on %s", artifact.Name)

artifactCount++
}
}

assert.Greater(t, artifactCount, 0, "No artifacts were validated for CI VCS properties")
}
Loading
Loading