Skip to content

Commit fa9a26b

Browse files
authored
fix/conan-project-key-build-dir-issue (#3370)
1 parent 7219cb1 commit fa9a26b

File tree

3 files changed

+370
-4
lines changed

3 files changed

+370
-4
lines changed

conan_test.go

Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package main
22

33
import (
4+
"crypto/sha256"
5+
"encoding/hex"
46
"os"
57
"os/exec"
68
"path/filepath"
@@ -10,6 +12,7 @@ import (
1012
buildinfo "github.com/jfrog/build-info-go/entities"
1113
biutils "github.com/jfrog/build-info-go/utils"
1214
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
15+
coreBuild "github.com/jfrog/jfrog-cli-core/v2/common/build"
1316
coretests "github.com/jfrog/jfrog-cli-core/v2/utils/tests"
1417
"github.com/jfrog/jfrog-cli/inttestutils"
1518
"github.com/jfrog/jfrog-cli/utils/tests"
@@ -469,6 +472,369 @@ func cleanupConanRemote() {
469472
_ = exec.Command("conan", "remote", "remove", tests.ConanLocalRepo).Run()
470473
}
471474

475+
// TestConanCreateWithProjectKey tests that 'jf conan create --project=<key>' stores build info
476+
// in the correct local build dir (SHA includes the project key).
477+
func TestConanCreateWithProjectKey(t *testing.T) {
478+
initConanTest(t)
479+
buildName := tests.ConanBuildName + "-project"
480+
buildNumber := "1"
481+
projectKey := "testprj"
482+
483+
projectPath := createConanProject(t, "conan-create-project-test")
484+
wd, err := os.Getwd()
485+
require.NoError(t, err)
486+
chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath)
487+
defer chdirCallback()
488+
489+
configureConanRemote(t)
490+
defer cleanupConanRemote()
491+
492+
jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")
493+
createArgs := []string{
494+
"conan", "create", ".",
495+
"--build=missing",
496+
"--build-name=" + buildName,
497+
"--build-number=" + buildNumber,
498+
"--project=" + projectKey,
499+
}
500+
require.NoError(t, jfrogCli.Exec(createArgs...))
501+
502+
// Verify local build info was stored under the project-key-aware directory
503+
builds, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, projectKey)
504+
require.NoError(t, err)
505+
require.Len(t, builds, 1, "Expected 1 build info file stored with project key '%s'", projectKey)
506+
assert.Equal(t, buildName, builds[0].Name)
507+
assert.Equal(t, buildNumber, builds[0].Number)
508+
509+
// Verify that the build is NOT found under the empty-project directory
510+
buildsNoProject, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, "")
511+
assert.NoError(t, err)
512+
assert.Empty(t, buildsNoProject, "Build info should NOT exist under empty project key directory")
513+
514+
// Cleanup local build dir
515+
assert.NoError(t, coreBuild.RemoveBuildDir(buildName, buildNumber, projectKey))
516+
}
517+
518+
// TestConanCreateWithoutProjectKey verifies no regression: 'jf conan create' without --project
519+
// still stores build info in the default (empty project) directory.
520+
func TestConanCreateWithoutProjectKey(t *testing.T) {
521+
initConanTest(t)
522+
buildName := tests.ConanBuildName + "-noproject"
523+
buildNumber := "1"
524+
525+
projectPath := createConanProject(t, "conan-create-noproject-test")
526+
wd, err := os.Getwd()
527+
require.NoError(t, err)
528+
chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath)
529+
defer chdirCallback()
530+
531+
configureConanRemote(t)
532+
defer cleanupConanRemote()
533+
534+
jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")
535+
createArgs := []string{
536+
"conan", "create", ".",
537+
"--build=missing",
538+
"--build-name=" + buildName,
539+
"--build-number=" + buildNumber,
540+
}
541+
require.NoError(t, jfrogCli.Exec(createArgs...))
542+
543+
// Verify local build info was stored under the empty project directory
544+
builds, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, "")
545+
require.NoError(t, err)
546+
require.Len(t, builds, 1, "Expected 1 build info file stored without project key")
547+
assert.Equal(t, buildName, builds[0].Name)
548+
549+
// Cleanup local build dir
550+
assert.NoError(t, coreBuild.RemoveBuildDir(buildName, buildNumber, ""))
551+
}
552+
553+
// TestConanInstallWithProjectKey tests that 'jf conan install --project=<key>' stores
554+
// build info in the project-key-aware directory.
555+
func TestConanInstallWithProjectKey(t *testing.T) {
556+
initConanTest(t)
557+
buildName := tests.ConanBuildName + "-install-project"
558+
buildNumber := "1"
559+
projectKey := "testprj"
560+
561+
projectPath := createConanProject(t, "conan-install-project-test")
562+
wd, err := os.Getwd()
563+
require.NoError(t, err)
564+
chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath)
565+
defer chdirCallback()
566+
567+
configureConanRemote(t)
568+
defer cleanupConanRemote()
569+
570+
jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")
571+
installArgs := []string{
572+
"conan", "install", ".",
573+
"--build=missing",
574+
"-r", tests.ConanVirtualRepo,
575+
"--build-name=" + buildName,
576+
"--build-number=" + buildNumber,
577+
"--project=" + projectKey,
578+
}
579+
require.NoError(t, jfrogCli.Exec(installArgs...))
580+
581+
// Verify local build info was stored with project key
582+
builds, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, projectKey)
583+
require.NoError(t, err)
584+
require.Len(t, builds, 1, "Expected 1 build info file stored with project key '%s'", projectKey)
585+
586+
// Verify NOT stored under empty-project dir
587+
buildsNoProject, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, "")
588+
assert.NoError(t, err)
589+
assert.Empty(t, buildsNoProject, "Build info should NOT exist under empty project key directory")
590+
591+
// Cleanup
592+
assert.NoError(t, coreBuild.RemoveBuildDir(buildName, buildNumber, projectKey))
593+
}
594+
595+
// TestConanUploadWithProjectKey tests that 'jf conan upload --project=<key>' stores
596+
// build info in the project-key-aware directory.
597+
func TestConanUploadWithProjectKey(t *testing.T) {
598+
initConanTest(t)
599+
buildName := tests.ConanBuildName + "-upload-project"
600+
buildNumber := "1"
601+
projectKey := "testprj"
602+
603+
projectPath := createConanProject(t, "conan-upload-project-test")
604+
wd, err := os.Getwd()
605+
require.NoError(t, err)
606+
chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath)
607+
defer chdirCallback()
608+
609+
configureConanRemote(t)
610+
defer cleanupConanRemote()
611+
612+
jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")
613+
614+
// First create the package so it can be uploaded
615+
createArgs := []string{
616+
"conan", "create", ".",
617+
"--build=missing",
618+
"--build-name=" + buildName,
619+
"--build-number=" + buildNumber,
620+
"--project=" + projectKey,
621+
}
622+
require.NoError(t, jfrogCli.Exec(createArgs...))
623+
624+
// Upload with --project
625+
uploadArgs := []string{
626+
"conan", "upload", "cli-test-package/*",
627+
"-r", tests.ConanLocalRepo,
628+
"--confirm",
629+
"--build-name=" + buildName,
630+
"--build-number=" + buildNumber,
631+
"--project=" + projectKey,
632+
}
633+
require.NoError(t, jfrogCli.Exec(uploadArgs...))
634+
635+
// Verify local build info was stored with project key
636+
builds, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, projectKey)
637+
require.NoError(t, err)
638+
require.NotEmpty(t, builds, "Expected build info files stored with project key '%s'", projectKey)
639+
640+
// Cleanup
641+
assert.NoError(t, coreBuild.RemoveBuildDir(buildName, buildNumber, projectKey))
642+
}
643+
644+
// TestConanBuildPublishWithProjectKey verifies that build info stored with --project
645+
// is isolated from builds without --project.
646+
func TestConanBuildPublishWithProjectKey(t *testing.T) {
647+
initConanTest(t)
648+
buildName := tests.ConanBuildName + "-bp-project"
649+
buildNumber := "1"
650+
projectKey := "testprj"
651+
652+
projectPath := createConanProject(t, "conan-bp-project-test")
653+
wd, err := os.Getwd()
654+
require.NoError(t, err)
655+
chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath)
656+
defer chdirCallback()
657+
658+
configureConanRemote(t)
659+
defer cleanupConanRemote()
660+
661+
jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")
662+
663+
// Create with --project so build info is stored with project key in the dir hash
664+
createArgs := []string{
665+
"conan", "create", ".",
666+
"--build=missing",
667+
"--build-name=" + buildName,
668+
"--build-number=" + buildNumber,
669+
"--project=" + projectKey,
670+
}
671+
require.NoError(t, jfrogCli.Exec(createArgs...))
672+
673+
// Verify the project-key-aware build dir has build info files
674+
buildsInfo, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, projectKey)
675+
require.NoError(t, err)
676+
assert.NotEmpty(t, buildsInfo, "Build info should exist in the project-key-aware build dir")
677+
678+
// Verify the build dir WITHOUT project key does NOT have build info files
679+
buildsInfoNoProject, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, "")
680+
require.NoError(t, err)
681+
assert.Empty(t, buildsInfoNoProject, "Build info should NOT exist in the non-project build dir")
682+
683+
// Cleanup
684+
assert.NoError(t, coreBuild.RemoveBuildDir(buildName, buildNumber, projectKey))
685+
assert.NoError(t, coreBuild.RemoveBuildDir(buildName, buildNumber, ""))
686+
}
687+
688+
// TestConanProjectKeyBuildDirHash verifies the SHA256 directory name computation.
689+
// With --project=vsm the local build dir should be SHA256("buildName_buildNumber_vsm"),
690+
func TestConanProjectKeyBuildDirHash(t *testing.T) {
691+
initConanTest(t)
692+
buildName := tests.ConanBuildName + "-hash"
693+
buildNumber := "1"
694+
projectKey := "vsm"
695+
696+
projectPath := createConanProject(t, "conan-hash-test")
697+
wd, err := os.Getwd()
698+
require.NoError(t, err)
699+
chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath)
700+
defer chdirCallback()
701+
702+
configureConanRemote(t)
703+
defer cleanupConanRemote()
704+
705+
jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")
706+
createArgs := []string{
707+
"conan", "create", ".",
708+
"--build=missing",
709+
"--build-name=" + buildName,
710+
"--build-number=" + buildNumber,
711+
"--project=" + projectKey,
712+
}
713+
require.NoError(t, jfrogCli.Exec(createArgs...))
714+
715+
// Compute expected directory names
716+
expectedWithProject := sha256.Sum256([]byte(buildName + "_" + buildNumber + "_" + projectKey))
717+
expectedDirWithProject := hex.EncodeToString(expectedWithProject[:])
718+
buggyNoProject := sha256.Sum256([]byte(buildName + "_" + buildNumber + "_"))
719+
buggyDirNoProject := hex.EncodeToString(buggyNoProject[:])
720+
721+
// Get the actual build dir path (should include project key)
722+
buildDir, err := coreBuild.GetBuildDir(buildName, buildNumber, projectKey)
723+
require.NoError(t, err)
724+
actualDirName := filepath.Base(buildDir)
725+
726+
assert.Equal(t, expectedDirWithProject, actualDirName,
727+
"Build dir should be SHA256 of '%s_%s_%s'", buildName, buildNumber, projectKey)
728+
assert.NotEqual(t, buggyDirNoProject, actualDirName,
729+
"Build dir must NOT be SHA256 of '%s_%s_' (old bug)", buildName, buildNumber)
730+
731+
// Verify build info file exists in that directory
732+
builds, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, projectKey)
733+
require.NoError(t, err)
734+
require.Len(t, builds, 1, "Expected build info to exist in the project-key-aware directory")
735+
736+
assert.NoError(t, coreBuild.RemoveBuildDir(buildName, buildNumber, projectKey))
737+
}
738+
739+
// TestConanMultipleProjectKeys tests that builds with different project keys
740+
// are stored in separate directories and don't interfere with each other.
741+
func TestConanMultipleProjectKeys(t *testing.T) {
742+
initConanTest(t)
743+
buildName := tests.ConanBuildName + "-multiproj"
744+
buildNumber := "1"
745+
projectKeys := []string{"proj1", "proj2", ""}
746+
747+
projectPath := createConanProject(t, "conan-multi-project-test")
748+
wd, err := os.Getwd()
749+
require.NoError(t, err)
750+
chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath)
751+
defer chdirCallback()
752+
753+
configureConanRemote(t)
754+
defer cleanupConanRemote()
755+
756+
jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")
757+
758+
// Run create with different project keys
759+
for _, pk := range projectKeys {
760+
args := []string{
761+
"conan", "create", ".",
762+
"--build=missing",
763+
"--build-name=" + buildName,
764+
"--build-number=" + buildNumber,
765+
}
766+
if pk != "" {
767+
args = append(args, "--project="+pk)
768+
}
769+
require.NoError(t, jfrogCli.Exec(args...), "Failed for project key '%s'", pk)
770+
}
771+
772+
// Verify each project key has its own build info, isolated from the others
773+
for _, pk := range projectKeys {
774+
builds, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, pk)
775+
require.NoError(t, err)
776+
require.Len(t, builds, 1, "Expected exactly 1 build info for project key '%s'", pk)
777+
assert.Equal(t, buildName, builds[0].Name)
778+
}
779+
780+
// Verify all build dirs are distinct (different SHA)
781+
dirs := make(map[string]string)
782+
for _, pk := range projectKeys {
783+
buildDir, err := coreBuild.GetBuildDir(buildName, buildNumber, pk)
784+
require.NoError(t, err)
785+
dirName := filepath.Base(buildDir)
786+
existingPk, exists := dirs[dirName]
787+
assert.False(t, exists, "Project keys '%s' and '%s' produced the same build dir %s", existingPk, pk, dirName)
788+
dirs[dirName] = pk
789+
}
790+
791+
// Cleanup all
792+
for _, pk := range projectKeys {
793+
assert.NoError(t, coreBuild.RemoveBuildDir(buildName, buildNumber, pk))
794+
}
795+
}
796+
797+
// TestConanProjectKeySpaceSeparated tests that --project <value>
798+
// works the same as --project=<value>.
799+
func TestConanProjectKeySpaceSeparated(t *testing.T) {
800+
initConanTest(t)
801+
buildName := tests.ConanBuildName + "-space-project"
802+
buildNumber := "1"
803+
projectKey := "testprj"
804+
805+
projectPath := createConanProject(t, "conan-space-project-test")
806+
wd, err := os.Getwd()
807+
require.NoError(t, err)
808+
chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath)
809+
defer chdirCallback()
810+
811+
configureConanRemote(t)
812+
defer cleanupConanRemote()
813+
814+
jfrogCli := coretests.NewJfrogCli(execMain, "jfrog", "")
815+
// Note: --project testprj (space-separated, not --project=testprj)
816+
createArgs := []string{
817+
"conan", "create", ".",
818+
"--build=missing",
819+
"--build-name=" + buildName,
820+
"--build-number=" + buildNumber,
821+
"--project", projectKey,
822+
}
823+
require.NoError(t, jfrogCli.Exec(createArgs...))
824+
825+
// Verify local build info was stored with project key
826+
builds, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, projectKey)
827+
require.NoError(t, err)
828+
require.Len(t, builds, 1, "Expected 1 build info file stored with space-separated --project")
829+
830+
// Verify NOT stored under empty-project dir
831+
buildsNoProject, err := coreBuild.GetGeneratedBuildsInfo(buildName, buildNumber, "")
832+
assert.NoError(t, err)
833+
assert.Empty(t, buildsNoProject, "Build info should NOT exist under empty project key directory")
834+
835+
assert.NoError(t, coreBuild.RemoveBuildDir(buildName, buildNumber, projectKey))
836+
}
837+
472838
// TestConanBuildPublishWithCIVcsProps tests that CI VCS properties are set on Conan artifacts
473839
// when running build-publish in a CI environment (GitHub Actions).
474840
// Conan packages are published via Conan client; build-publish retrieves artifact paths

0 commit comments

Comments
 (0)