Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rust): allow specifying features to cargo metadata #1009

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
11 changes: 9 additions & 2 deletions docs/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Environment variables are sometimes used to control experimental features or adv
## `DisableGoCliScan`

If the environment variable `DisableGoCliScan` is set to "true", we fall back to parsing `go.mod` and `go.sum` ourselves.
Otherwise, the Go detector uses go-cli command: `go list -m all` to discover Go dependencies.
Otherwise, the Go detector uses go-cli command: `go list -m all` to discover Go dependencies. [^1]

## `PyPiMaxCacheEntries`

Expand All @@ -18,4 +18,11 @@ When set to any value, enables detector experiments, a feature to compare the re
same ecosystem. The available experiments are found in the [`Experiments\Config`](../src/Microsoft.ComponentDetection.Orchestrator/Experiments/Configs)
folder.

[1]: https://go.dev/ref/mod#go-mod-graph
## `CD_RUST_CLI_FEATURES`

Specifies the features (comma seperated) to be passed into `cargo metadata`, which tells cargo to build the project with
the features for the workspace/project. By default, `cargo metadata` will run with `--all-features`, instructing `cargo`
to determine the build graph if all features should be built in the project/workspace. [^2]

[^1]: https://go.dev/ref/mod#go-mod-graph
[^2]: https://doc.rust-lang.org/cargo/commands/cargo-metadata.html#feature-selection
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,35 @@ public class RustCliDetector : FileComponentDetector, IExperimentalDetector
@"^(?<packageName>[^ ]+)(?: (?<version>[^ ]+))?(?: \((?<source>[^()]*)\))?$",
RegexOptions.Compiled);

internal const string CustomFeaturesEnvironmentVariable = "CD_RUST_CLI_FEATURES";

private static readonly TomlModelOptions TomlOptions = new TomlModelOptions
{
IgnoreMissingProperties = true,
};

private readonly ICommandLineInvocationService cliService;
private readonly IEnvironmentVariableService environmentVariableService;

/// <summary>
/// Initializes a new instance of the <see cref="RustCliDetector"/> class.
/// </summary>
/// <param name="componentStreamEnumerableFactory">The component stream enumerable factory.</param>
/// <param name="walkerFactory">The walker factory.</param>
/// <param name="cliService">The command line invocation service.</param>
/// <param name="environmentVariableService">The environment variable service.</param>
/// <param name="logger">The logger.</param>
public RustCliDetector(
IComponentStreamEnumerableFactory componentStreamEnumerableFactory,
IObservableDirectoryWalkerFactory walkerFactory,
ICommandLineInvocationService cliService,
IEnvironmentVariableService environmentVariableService,
ILogger<RustCliDetector> logger)
{
this.ComponentStreamEnumerableFactory = componentStreamEnumerableFactory;
this.Scanner = walkerFactory;
this.cliService = cliService;
this.environmentVariableService = environmentVariableService;
this.Logger = logger;
}

Expand Down Expand Up @@ -86,12 +92,15 @@ protected override async Task OnFileFoundAsync(ProcessRequest processRequest, ID
}
else
{
// Use --all-features to ensure that even optional feature dependencies are detected.
// Use --all-features to ensure that even optional feature dependencies are detected if env var isnt set.
var specifiedFeatures =
this.environmentVariableService.GetEnvironmentVariable(CustomFeaturesEnvironmentVariable);

var cliResult = await this.cliService.ExecuteCommandAsync(
"cargo",
null,
"metadata",
"--all-features",
string.IsNullOrEmpty(specifiedFeatures) ? "--all-features" : $"--features={specifiedFeatures}",
"--manifest-path",
componentStream.Location,
"--format-version=1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class RustCliDetectorTests : BaseDetectorTest<RustCliDetector>
{
private Mock<ICommandLineInvocationService> mockCliService;
private Mock<IComponentStreamEnumerableFactory> mockComponentStreamEnumerableFactory;
private Mock<IEnvironmentVariableService> mockEnvVarService;

[TestInitialize]
public void InitCliMock()
Expand All @@ -29,6 +30,8 @@ public void InitCliMock()
this.DetectorTestUtility.AddServiceMock(this.mockCliService);
this.mockComponentStreamEnumerableFactory = new Mock<IComponentStreamEnumerableFactory>();
this.DetectorTestUtility.AddServiceMock(this.mockComponentStreamEnumerableFactory);
this.mockEnvVarService = new Mock<IEnvironmentVariableService>();
this.DetectorTestUtility.AddServiceMock(this.mockEnvVarService);
}

[TestMethod]
Expand Down Expand Up @@ -1137,4 +1140,27 @@ public async Task RustCliDetector_FallBackLogicTriggeredOnFailedProcessingAsync(

return;
}

[TestMethod]
public async Task RustCLiDetector_CustomFeaturesPassedIfPresentAsync()
{
this.mockCliService
.Setup(x => x.CanCommandBeLocatedAsync("cargo", It.IsAny<IEnumerable<string>>()))
.ReturnsAsync(true);

var capturedCargoArgs = Enumerable.Empty<string>();
this.mockCliService.Setup(x =>
x.ExecuteCommandAsync("cargo", It.IsAny<IEnumerable<string>>(), It.IsAny<string[]>()))
.Callback((string _, IEnumerable<string> _, string[] args) => capturedCargoArgs = args);

this.mockEnvVarService
.Setup(x => x.GetEnvironmentVariable(RustCliDetector.CustomFeaturesEnvironmentVariable))
.Returns("a,b");

var (scanResult, componentRecorder) = await this.DetectorTestUtility
.WithFile("Cargo.toml", string.Empty)
.ExecuteDetectorAsync();

capturedCargoArgs.Should().Contain("--features=a,b");
}
}
Loading