From 7c3c25075a7e8fdf25e9fd4669e10cca2cdb1f3b Mon Sep 17 00:00:00 2001 From: Reshmi Date: Wed, 1 Jul 2026 00:00:10 +0530 Subject: [PATCH 1/3] feat(cargo): add cargo cli flags and help text --- docs/buildtools/cargo/help.go | 16 ++++++++++++++++ utils/cliutils/commandsflags.go | 4 ++++ utils/cliutils/commandsflags_test.go | 10 ++++++++++ 3 files changed, 30 insertions(+) create mode 100644 docs/buildtools/cargo/help.go create mode 100644 utils/cliutils/commandsflags_test.go diff --git a/docs/buildtools/cargo/help.go b/docs/buildtools/cargo/help.go new file mode 100644 index 000000000..6b63ee168 --- /dev/null +++ b/docs/buildtools/cargo/help.go @@ -0,0 +1,16 @@ +package cargo + +var Usage = []string{"jf cargo [command options]"} + +func GetDescription() string { + return "Run Cargo (Rust) commands with JFrog build-info collection." +} + +func GetArguments() string { + return ` cargo command + The cargo command to run, e.g. build, publish, package, add.` +} + +func GetAIDescription() string { + return "Runs native cargo and collects build-info for Rust projects." +} diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index 4cf45994e..99e70c2d8 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -88,6 +88,7 @@ const ( RubyConfig = "ruby-config" ConanConfig = "conan-config" Conan = "conan" + Cargo = "cargo" Nix = "nix" Ping = "ping" RtCurl = "rt-curl" @@ -2240,6 +2241,9 @@ var commandFlags = map[string][]string{ Conan: { BuildName, BuildNumber, module, Project, }, + Cargo: { + BuildName, BuildNumber, module, Project, + }, Nix: { BuildName, BuildNumber, module, Project, serverId, }, diff --git a/utils/cliutils/commandsflags_test.go b/utils/cliutils/commandsflags_test.go new file mode 100644 index 000000000..5957878ca --- /dev/null +++ b/utils/cliutils/commandsflags_test.go @@ -0,0 +1,10 @@ +package cliutils + +import "testing" + +func TestCargoFlagGroupExists(t *testing.T) { + flags := GetCommandFlags(Cargo) + if len(flags) == 0 { + t.Fatal("expected cargo flag group to be registered") + } +} From d307180f58cd1e047faf23fde75deee56e5d8257 Mon Sep 17 00:00:00 2001 From: Reshmi Date: Wed, 1 Jul 2026 00:03:27 +0530 Subject: [PATCH 2/3] feat(cargo): register jf cargo command Co-Authored-By: Claude Sonnet 4.6 --- buildtools/cli.go | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/buildtools/cli.go b/buildtools/cli.go index 4daff389c..7f046718b 100644 --- a/buildtools/cli.go +++ b/buildtools/cli.go @@ -3,6 +3,7 @@ package buildtools import ( "errors" "fmt" + cargocommand "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/cargo" conancommand "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/conan" nixcommand "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/nix" "io/fs" @@ -53,6 +54,7 @@ import ( terraformdocs "github.com/jfrog/jfrog-cli/docs/artifactory/terraform" "github.com/jfrog/jfrog-cli/docs/artifactory/terraformconfig" twinedocs "github.com/jfrog/jfrog-cli/docs/artifactory/twine" + cargo "github.com/jfrog/jfrog-cli/docs/buildtools/cargo" "github.com/jfrog/jfrog-cli/docs/buildtools/conan" "github.com/jfrog/jfrog-cli/docs/buildtools/conanconfig" "github.com/jfrog/jfrog-cli/docs/buildtools/docker" @@ -417,6 +419,19 @@ func GetCommands() []cli.Command { Category: buildToolsCategory, Action: ConanCmd, }, + { + Name: "cargo", + Hidden: false, + Flags: cliutils.GetCommandFlags(cliutils.Cargo), + Usage: corecommon.ResolveDescription(cargo.GetDescription(), cargo.GetAIDescription()), + HelpName: corecommon.CreateUsage("cargo", corecommon.ResolveDescription(cargo.GetDescription(), cargo.GetAIDescription()), cargo.Usage), + UsageText: cargo.GetArguments(), + ArgsUsage: common.CreateEnvVars(), + SkipFlagParsing: true, + BashComplete: corecommon.CreateBashCompletionFunc(), + Category: buildToolsCategory, + Action: CargoCmd, + }, { Name: "nix", Hidden: false, @@ -2071,6 +2086,39 @@ func ConanCmd(c *cli.Context) error { return commands.ExecWithPackageManager(conanCommand, project.Conan.String()) } +func CargoCmd(c *cli.Context) error { + if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil { + return err + } + if c.NArg() < 1 { + return cliutils.WrongNumberOfArgumentsHandler(c) + } + + args := cliutils.ExtractCommand(c) + + args, serverID, err := coreutils.ExtractServerIdFromCommand(args) + if err != nil { + return fmt.Errorf("failed to extract server ID: %w", err) + } + serverDetails, err := coreConfig.GetSpecificConfig(serverID, true, false) + if err != nil { + return err + } + + filteredArgs, buildConfiguration, err := build.ExtractBuildDetailsFromArgs(args) + if err != nil { + return err + } + + cmdName, cargoArgs := getCommandName(filteredArgs) + + cargoCommand := cargocommand.NewCargoCommand(). + SetCommandName(cmdName).SetArgs(cargoArgs). + SetBuildConfiguration(buildConfiguration).SetServerDetails(serverDetails) + + return commands.ExecWithPackageManager(cargoCommand, "cargo") +} + func NixCmd(c *cli.Context) error { if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil { return err From 0404a139a88c9f5601c6e17b81b5637038fd7342 Mon Sep 17 00:00:00 2001 From: Reshmi Date: Fri, 17 Jul 2026 09:58:09 +0530 Subject: [PATCH 3/3] wired cargo package and add tests for it --- cargo_test.go | 370 +++++++++++++++++++ testdata/cargo/simple/Cargo.toml | 8 + testdata/cargo/simple/src/lib.rs | 16 + testdata/cargo/workspace/Cargo.toml | 3 + testdata/cargo/workspace/member-a/Cargo.toml | 8 + testdata/cargo/workspace/member-a/src/lib.rs | 5 + testdata/cargo/workspace/member-b/Cargo.toml | 8 + testdata/cargo/workspace/member-b/src/lib.rs | 5 + testdata/cargo_local_repository_config.json | 5 + testdata/cargo_remote_repository_config.json | 6 + utils/tests/consts.go | 4 + utils/tests/utils.go | 6 + 12 files changed, 444 insertions(+) create mode 100644 cargo_test.go create mode 100644 testdata/cargo/simple/Cargo.toml create mode 100644 testdata/cargo/simple/src/lib.rs create mode 100644 testdata/cargo/workspace/Cargo.toml create mode 100644 testdata/cargo/workspace/member-a/Cargo.toml create mode 100644 testdata/cargo/workspace/member-a/src/lib.rs create mode 100644 testdata/cargo/workspace/member-b/Cargo.toml create mode 100644 testdata/cargo/workspace/member-b/src/lib.rs create mode 100644 testdata/cargo_local_repository_config.json create mode 100644 testdata/cargo_remote_repository_config.json diff --git a/cargo_test.go b/cargo_test.go new file mode 100644 index 000000000..52b69684f --- /dev/null +++ b/cargo_test.go @@ -0,0 +1,370 @@ +package main + +import ( + "os" + "path/filepath" + "testing" + + biutils "github.com/jfrog/build-info-go/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" + + "github.com/jfrog/jfrog-cli/inttestutils" + "github.com/jfrog/jfrog-cli/utils/tests" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Cargo integration tests. Scenario numbers (#N) refer to CARGO_TEST_PLAN.md. +// +// These tests drive the native `jf cargo` FlexPack command. Build-info collection is +// enabled by JFROG_RUN_NATIVE=true (flexpack.IsFlexPackEnabled). The command buckets are: +// +// deps -> build, install, update, add, fetch, generate-lockfile, run, test, check +// artifacts -> package (scans target/package/*.crate, no deploy) +// publish -> publish (deploys via native cargo publish + collects artifacts) +// +// The build-info module Type is the string "cargo". The published build-info-go in +// jfrog-cli does not export an entities.Cargo constant (it lives in the local +// ../build-info-go used by jfrog-cli-artifactory), so we assert on the string value. +const cargoModuleType = "cargo" + +// ==================== Initialization ==================== + +func initCargoTest(t *testing.T) { + if !*tests.TestCargo { + t.Skip("Skipping Cargo test. To run Cargo test add the '-test.cargo=true' option.") + } + require.True(t, isRepoExist(tests.CargoLocalRepo), "Cargo test local repository doesn't exist.") +} + +// ==================== Project Helpers ==================== + +// createCargoProject copies a fixture from testdata/cargo/ into a fresh +// temp dir and returns its path plus a cleanup callback. +func createCargoProject(t *testing.T, outputFolder, projectName string) (string, func()) { + projectSrc := filepath.Join(filepath.FromSlash(tests.GetTestResourcesPath()), "cargo", projectName) + tmpDir, cleanupCallback := coretests.CreateTempDirWithCallbackAndAssert(t) + + projectPath := filepath.Join(tmpDir, outputFolder) + assert.NoError(t, biutils.CopyDir(projectSrc, projectPath, true, nil)) + return projectPath, cleanupCallback +} + +// runInCargoProject sets JFROG_RUN_NATIVE, prepares an isolated home dir, copies the +// fixture, and chdirs into it. It returns the JfrogCli client and a teardown func that +// restores env/home/cwd. Mirrors the setup boilerplate used by the Nix tests. +func runInCargoProject(t *testing.T, outputFolder, projectName string) (*coretests.JfrogCli, func()) { + nativeCallback := clientTestUtils.SetEnvWithCallbackAndAssert(t, "JFROG_RUN_NATIVE", "true") + + oldHomeDir, newHomeDir := prepareHomeDir(t) + + projectPath, cleanupProject := createCargoProject(t, outputFolder, projectName) + + wd, err := os.Getwd() + assert.NoError(t, err) + chdirCallback := clientTestUtils.ChangeDirWithCallback(t, wd, projectPath) + + teardown := func() { + chdirCallback() + cleanupProject() + clientTestUtils.SetEnvAndAssert(t, coreutils.HomeDir, oldHomeDir) + clientTestUtils.RemoveAllAndAssert(t, newHomeDir) + nativeCallback() + } + return coretests.NewJfrogCli(execMain, "jfrog", ""), teardown +} + +// ==================== Dependency Collection (build bucket) ==================== + +func TestCargoBuild_CollectsDeps(t *testing.T) { + // Scenarios: #14 — cargo build resolves/collects deps; #22 — deps captured in module; + // module Type is "cargo". + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-build-deps", "simple") + defer teardown() + + buildName := "cli-cargo-build-deps-test" + buildNumber := "1" + + err := jfrogCli.Exec("cargo", "build", + "--build-name="+buildName, "--build-number="+buildNumber) + if err != nil { + t.Skipf("cargo build not available or failed: %v", err) + } + + assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + assert.NoError(t, err) + assert.True(t, found, "build-info should be found after cargo build") + if found && len(publishedBuildInfo.BuildInfo.Modules) > 0 { + assert.Equal(t, cargoModuleType, string(publishedBuildInfo.BuildInfo.Modules[0].Type), + "module type should be cargo") + } +} + +// ==================== Artifact Collection (package bucket) ==================== + +func TestCargoPackage_CollectsArtifacts(t *testing.T) { + // Scenarios: #7 — crate path layout //-.crate; + // #35 — artifact carries sha256/sha1/md5; artifact Type is "crate". + // `cargo package` produces target/package/*.crate offline — no registry needed. + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-package", "simple") + defer teardown() + + buildName := "cli-cargo-package-test" + buildNumber := "1" + + err := jfrogCli.Exec("cargo", "package", + "--build-name="+buildName, "--build-number="+buildNumber) + if err != nil { + t.Skipf("cargo package not available or failed: %v", err) + } + + assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + assert.NoError(t, err) + assert.True(t, found) + if found && len(publishedBuildInfo.BuildInfo.Modules) > 0 { + module := publishedBuildInfo.BuildInfo.Modules[0] + assert.Equal(t, cargoModuleType, string(module.Type)) + require.NotEmpty(t, module.Artifacts, "cargo package should capture the .crate artifact") + for _, art := range module.Artifacts { + assert.Equal(t, "crate", art.Type, "artifact type should be crate") + assert.Contains(t, art.Path, "cli-cargo-lib/1.0.0/cli-cargo-lib-1.0.0.crate", + "crate must follow //-.crate layout") + assert.NotEmpty(t, art.Sha256, "crate artifact must have sha256") + assert.NotEmpty(t, art.Sha1, "crate artifact must have sha1") + assert.NotEmpty(t, art.Md5, "crate artifact must have md5") + } + } +} + +// ==================== Publish (publish bucket) ==================== + +func TestCargoPublish(t *testing.T) { + // Scenario: #6 — cargo publish uploads the .crate and collects it as an artifact. + // Publishing needs a registry configured in .cargo/config.toml pointing at the + // Artifactory cargo repo; if that isn't set up in the environment, skip gracefully. + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-publish", "simple") + defer teardown() + + buildName := "cli-cargo-publish-test" + buildNumber := "1" + + err := jfrogCli.Exec("cargo", "publish", + "--registry", "artifactory", "--no-verify", + "--build-name="+buildName, "--build-number="+buildNumber) + if err != nil { + t.Skipf("cargo publish not available or registry not configured: %v", err) + } + + assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + assert.NoError(t, err) + assert.True(t, found) + if found && len(publishedBuildInfo.BuildInfo.Modules) > 0 { + module := publishedBuildInfo.BuildInfo.Modules[0] + assert.Equal(t, cargoModuleType, string(module.Type)) + assert.NotEmpty(t, module.Artifacts, "publish should record the .crate artifact") + } +} + +// ==================== Build-info flags ==================== + +func TestCargoModuleOverride(t *testing.T) { + // Scenario: #31 — --module overrides the auto-detected module ID. + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-module-override", "simple") + defer teardown() + + buildName := "cli-cargo-module-test" + buildNumber := "1" + customModule := "my-custom-cargo-module" + + err := jfrogCli.Exec("cargo", "build", + "--build-name="+buildName, "--build-number="+buildNumber, + "--module="+customModule) + if err != nil { + t.Skipf("cargo build not available: %v", err) + } + + assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + assert.NoError(t, err) + assert.True(t, found) + if found && len(publishedBuildInfo.BuildInfo.Modules) > 0 { + assert.Equal(t, customModule, publishedBuildInfo.BuildInfo.Modules[0].Id, + "module ID should be overridden to the custom name") + } +} + +func TestCargoBuildInfo_ProjectKey(t *testing.T) { + // Scenario: #51 — --project scopes build info to the correct project. The flag must + // pass through; build-publish under a nonexistent project may fail (that's tolerated). + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-project-key", "simple") + defer teardown() + + buildName := "cli-cargo-project-key-test" + buildNumber := "1" + + err := jfrogCli.Exec("cargo", "build", + "--build-name="+buildName, "--build-number="+buildNumber, + "--project=testprj") + if err != nil { + t.Skipf("cargo build not available: %v", err) + } + + if err = artifactoryCli.Exec("bp", buildName, buildNumber, "--project=testprj"); err != nil { + t.Logf("build-publish with --project failed (project may not exist): %v", err) + } + inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) +} + +func TestCargoBuildNameOnly_NoBuildInfo(t *testing.T) { + // Scenario: #27 — --build-name without --build-number → cargo runs, no build-info collected. + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-name-only", "simple") + defer teardown() + + // Only --build-name → collector short-circuits (see collectDeps: empty name check is on + // build-name; a name without a number produces no publishable build). No panic expected. + err := jfrogCli.Exec("cargo", "build", "--build-name=cli-cargo-name-only-test") + if err != nil { + t.Skipf("cargo build not available: %v", err) + } +} + +func TestCargoNoFlags_Passthrough(t *testing.T) { + // Scenario: #29 — no --build-name/--build-number → pure passthrough, no build-info, no crash. + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-no-flags", "simple") + defer teardown() + + if err := jfrogCli.Exec("cargo", "build"); err != nil { + t.Skipf("cargo build not available: %v", err) + } +} + +// ==================== Multi-module / Workspace ==================== + +func TestCargoWorkspace_Modules(t *testing.T) { + // Scenarios: #38/#75 — workspace members build with Artifactory resolution and are + // captured in the build-info. Uses the two-member workspace fixture. + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-workspace", "workspace") + defer teardown() + + buildName := "cli-cargo-workspace-test" + buildNumber := "1" + + err := jfrogCli.Exec("cargo", "build", + "--build-name="+buildName, "--build-number="+buildNumber) + if err != nil { + t.Skipf("cargo build not available or workspace not resolvable: %v", err) + } + + assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + assert.NoError(t, err) + assert.True(t, found) + if found { + require.GreaterOrEqual(t, len(publishedBuildInfo.BuildInfo.Modules), 1, + "workspace build should produce at least one cargo module") + assert.Equal(t, cargoModuleType, string(publishedBuildInfo.BuildInfo.Modules[0].Type)) + } +} + +// ==================== Checksums ==================== + +func TestCargoDepChecksums(t *testing.T) { + // Scenario: #42 — dependencies captured in build info carry checksums when resolvable. + // On a machine with a warm cargo cache some deps may lack AQL checksums; we log the ratio. + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-dep-checksums", "simple") + defer teardown() + + buildName := "cli-cargo-dep-checksums-test" + buildNumber := "1" + + err := jfrogCli.Exec("cargo", "build", + "--build-name="+buildName, "--build-number="+buildNumber) + if err != nil { + t.Skipf("cargo build not available: %v", err) + } + + assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + assert.NoError(t, err) + assert.True(t, found) + if found && len(publishedBuildInfo.BuildInfo.Modules) > 0 { + deps := publishedBuildInfo.BuildInfo.Modules[0].Dependencies + withChecksums := 0 + for _, dep := range deps { + if dep.Sha256 != "" { + withChecksums++ + } + } + t.Logf("deps with sha256: %d/%d", withChecksums, len(deps)) + } +} + +// ==================== Full lifecycle ==================== + +func TestCargoFullLifecycle(t *testing.T) { + // Scenario: #79 — build (collect deps) → package (collect artifacts) → publish build-info. + // Package collects artifacts offline, so this exercises deps + artifacts in one build. + initCargoTest(t) + + jfrogCli, teardown := runInCargoProject(t, "cargo-lifecycle", "simple") + defer teardown() + + buildName := "cli-cargo-lifecycle-test" + buildNumber := "1" + + if err := jfrogCli.Exec("cargo", "build", + "--build-name="+buildName, "--build-number="+buildNumber); err != nil { + t.Skipf("cargo build not available: %v", err) + } + if err := jfrogCli.Exec("cargo", "package", + "--build-name="+buildName, "--build-number="+buildNumber); err != nil { + t.Skipf("cargo package not available: %v", err) + } + + assert.NoError(t, artifactoryCli.Exec("bp", buildName, buildNumber)) + defer inttestutils.DeleteBuild(serverDetails.ArtifactoryUrl, buildName, artHttpDetails) + + publishedBuildInfo, found, err := tests.GetBuildInfo(serverDetails, buildName, buildNumber) + assert.NoError(t, err) + assert.True(t, found) + if found && len(publishedBuildInfo.BuildInfo.Modules) > 0 { + module := publishedBuildInfo.BuildInfo.Modules[0] + assert.Equal(t, cargoModuleType, string(module.Type)) + assert.NotEmpty(t, module.Artifacts, "lifecycle build should carry the .crate artifact") + } +} diff --git a/testdata/cargo/simple/Cargo.toml b/testdata/cargo/simple/Cargo.toml new file mode 100644 index 000000000..de9ffeb13 --- /dev/null +++ b/testdata/cargo/simple/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "cli-cargo-lib" +version = "1.0.0" +edition = "2021" +description = "Fixture crate for JFrog CLI Cargo integration tests" +license = "Apache-2.0" + +[dependencies] diff --git a/testdata/cargo/simple/src/lib.rs b/testdata/cargo/simple/src/lib.rs new file mode 100644 index 000000000..53fe9ff34 --- /dev/null +++ b/testdata/cargo/simple/src/lib.rs @@ -0,0 +1,16 @@ +//! Minimal dependency-free library crate used as a Cargo integration-test fixture. + +/// Adds two numbers. Exists only so the crate compiles and `cargo package` produces a .crate. +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_adds() { + assert_eq!(add(2, 2), 4); + } +} diff --git a/testdata/cargo/workspace/Cargo.toml b/testdata/cargo/workspace/Cargo.toml new file mode 100644 index 000000000..01023d0e1 --- /dev/null +++ b/testdata/cargo/workspace/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +resolver = "2" +members = ["member-a", "member-b"] diff --git a/testdata/cargo/workspace/member-a/Cargo.toml b/testdata/cargo/workspace/member-a/Cargo.toml new file mode 100644 index 000000000..d5eb3a5a9 --- /dev/null +++ b/testdata/cargo/workspace/member-a/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "cli-cargo-member-a" +version = "1.0.0" +edition = "2021" +description = "Workspace member A fixture for JFrog CLI Cargo integration tests" +license = "Apache-2.0" + +[dependencies] diff --git a/testdata/cargo/workspace/member-a/src/lib.rs b/testdata/cargo/workspace/member-a/src/lib.rs new file mode 100644 index 000000000..abd98e28e --- /dev/null +++ b/testdata/cargo/workspace/member-a/src/lib.rs @@ -0,0 +1,5 @@ +//! Workspace member A — dependency-free fixture crate. + +pub fn member_a() -> &'static str { + "member-a" +} diff --git a/testdata/cargo/workspace/member-b/Cargo.toml b/testdata/cargo/workspace/member-b/Cargo.toml new file mode 100644 index 000000000..4e04e8e49 --- /dev/null +++ b/testdata/cargo/workspace/member-b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "cli-cargo-member-b" +version = "1.0.0" +edition = "2021" +description = "Workspace member B fixture for JFrog CLI Cargo integration tests" +license = "Apache-2.0" + +[dependencies] diff --git a/testdata/cargo/workspace/member-b/src/lib.rs b/testdata/cargo/workspace/member-b/src/lib.rs new file mode 100644 index 000000000..1545b665c --- /dev/null +++ b/testdata/cargo/workspace/member-b/src/lib.rs @@ -0,0 +1,5 @@ +//! Workspace member B — dependency-free fixture crate. + +pub fn member_b() -> &'static str { + "member-b" +} diff --git a/testdata/cargo_local_repository_config.json b/testdata/cargo_local_repository_config.json new file mode 100644 index 000000000..916d14a14 --- /dev/null +++ b/testdata/cargo_local_repository_config.json @@ -0,0 +1,5 @@ +{ + "key": "${CARGO_LOCAL_REPO}", + "rclass": "local", + "packageType": "cargo" +} diff --git a/testdata/cargo_remote_repository_config.json b/testdata/cargo_remote_repository_config.json new file mode 100644 index 000000000..824997c82 --- /dev/null +++ b/testdata/cargo_remote_repository_config.json @@ -0,0 +1,6 @@ +{ + "key": "${CARGO_REMOTE_REPO}", + "rclass": "remote", + "packageType": "cargo", + "url": "https://index.crates.io" +} diff --git a/utils/tests/consts.go b/utils/tests/consts.go index aacff5397..ada650b0e 100644 --- a/utils/tests/consts.go +++ b/utils/tests/consts.go @@ -108,6 +108,8 @@ const ( NixLocalRepositoryConfig = "nix_local_repository_config.json" NixRemoteRepositoryConfig = "nix_remote_repository_config.json" NixVirtualRepositoryConfig = "nix_virtual_repository_config.json" + CargoLocalRepositoryConfig = "cargo_local_repository_config.json" + CargoRemoteRepositoryConfig = "cargo_remote_repository_config.json" PoetryLocalRepositoryConfig = "poetry_local_repository_config.json" PoetryRemoteRepositoryConfig = "poetry_remote_repository_config.json" PoetryVirtualRepositoryConfig = "poetry_virtual_repository_config.json" @@ -217,6 +219,8 @@ var ( NixLocalRepo = "cli-nix-local" NixRemoteRepo = "cli-nix-remote" NixVirtualRepo = "cli-nix-virtual" + CargoLocalRepo = "cli-cargo-local" + CargoRemoteRepo = "cli-cargo-remote" PoetryLocalRepo = "cli-poetry-local" PoetryRemoteRepo = "cli-poetry-remote" PoetryVirtualRepo = "cli-poetry-virtual" diff --git a/utils/tests/utils.go b/utils/tests/utils.go index 688b8ec42..d9093156b 100644 --- a/utils/tests/utils.go +++ b/utils/tests/utils.go @@ -71,6 +71,7 @@ var ( TestPoetry *bool TestUv *bool TestNix *bool + TestCargo *bool TestAgentPlugins *bool TestConan *bool TestHelm *bool @@ -139,6 +140,7 @@ func init() { TestPoetry = flag.Bool("test.poetry", false, "Test Poetry") TestUv = flag.Bool("test.uv", false, "Test UV") TestNix = flag.Bool("test.nix", false, "Test Nix") + TestCargo = flag.Bool("test.cargo", false, "Test Cargo") TestAgentPlugins = flag.Bool("test.agentPlugins", false, "Test Agent Plugins") TestConan = flag.Bool("test.conan", false, "Test Conan") TestHelm = flag.Bool("test.helm", false, "Test Helm") @@ -326,6 +328,8 @@ var reposConfigMap = map[*string]string{ &NixLocalRepo: NixLocalRepositoryConfig, &NixRemoteRepo: NixRemoteRepositoryConfig, &NixVirtualRepo: NixVirtualRepositoryConfig, + &CargoLocalRepo: CargoLocalRepositoryConfig, + &CargoRemoteRepo: CargoRemoteRepositoryConfig, &ConanLocalRepo: ConanLocalRepositoryConfig, &ConanRemoteRepo: ConanRemoteRepositoryConfig, &ConanVirtualRepo: ConanVirtualRepositoryConfig, @@ -543,6 +547,8 @@ func getSubstitutionMap() map[string]string { "${NIX_LOCAL_REPO}": NixLocalRepo, "${NIX_REMOTE_REPO}": NixRemoteRepo, "${NIX_VIRTUAL_REPO}": NixVirtualRepo, + "${CARGO_LOCAL_REPO}": CargoLocalRepo, + "${CARGO_REMOTE_REPO}": CargoRemoteRepo, "${CONAN_LOCAL_REPO}": ConanLocalRepo, "${CONAN_REMOTE_REPO}": ConanRemoteRepo, "${CONAN_VIRTUAL_REPO}": ConanVirtualRepo,