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

Add Plugin CLI Tool #317

Merged
merged 91 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
02e2fca
Add CLI
helenkzhang Apr 18, 2023
9e43cf3
Add push
helenkzhang Apr 18, 2023
2d47881
Update assembly name
helenkzhang Apr 18, 2023
6b79f76
Add json schema doc
helenkzhang Apr 18, 2023
1f27c4c
Add components doc
helenkzhang Apr 19, 2023
aff7fb9
Add CLI doc
helenkzhang Apr 19, 2023
4107670
Fix doc
helenkzhang Apr 19, 2023
3378b92
Fix doc 2
helenkzhang May 9, 2023
63aece0
wip
helenkzhang May 24, 2023
27cfca9
Fix metadata generator
helenkzhang Jun 14, 2023
cf64867
Update package extension to .ptix
helenkzhang Jun 14, 2023
41752b8
Remove old projeact
helenkzhang Jun 14, 2023
ef53cd5
Delete and untrack launchsettings.json
helenkzhang Jun 14, 2023
8dcbf6b
Print errors
helenkzhang Jun 14, 2023
d34076d
Update extension name to ptix
helenkzhang Jun 20, 2023
d670bf4
Require Id and Version
helenkzhang Jun 20, 2023
279ec6b
Put cli and publisher into a folder and don't interfere with plugins …
helenkzhang Jun 20, 2023
d42de6f
Add manifest schema
helenkzhang Jun 21, 2023
4f3d678
Exclude schema folder
helenkzhang Jun 21, 2023
b14f189
Update plugin project template
helenkzhang Jun 21, 2023
0cee77a
Update metadata gen
helenkzhang Jun 22, 2023
279038d
Clean up metadata generation
helenkzhang Jun 23, 2023
a1286a3
Fix schema comma
helenkzhang Jun 23, 2023
4e8b42c
Add manifest schema validation
helenkzhang Jun 23, 2023
04bffe8
Validate using data annotations
helenkzhang Jun 24, 2023
deed3e6
Update pack
helenkzhang Jun 24, 2023
1cc1a58
Pack as dotnet tool
helenkzhang Jun 26, 2023
dd27c97
Change publisher location
helenkzhang Jun 27, 2023
40b1f72
Resolve conflicts
helenkzhang Jul 14, 2023
5c72328
Fix contents metadata
helenkzhang Jul 14, 2023
88252ea
Remove version from schema
helenkzhang Jul 14, 2023
68e5163
Add manifestVersion to required
helenkzhang Jul 14, 2023
bdfca8b
Move package stuff to its own project
helenkzhang Jul 24, 2023
435fb49
Fix issues in cli
helenkzhang Jul 25, 2023
f07dff3
Update plugin template
helenkzhang Jul 25, 2023
97c69f5
Undo change in .gitignore
helenkzhang Jul 25, 2023
24655ae
Untrack launchSettings.json
helenkzhang Jul 25, 2023
623fcdf
Update to use json validation
helenkzhang Jul 25, 2023
df576ab
Update schema location
helenkzhang Jul 25, 2023
3bbd513
Add "bundled manifest" option
helenkzhang Jul 26, 2023
7801f62
Fix shouldinclude
helenkzhang Jul 26, 2023
f84ba94
Update template
helenkzhang Jul 26, 2023
4d9ed52
Update plugin template
helenkzhang Jul 27, 2023
5e6d605
Rename template
helenkzhang Jul 27, 2023
94d385f
Fix path issues
helenkzhang Jul 28, 2023
dbb6fbb
Revert changes in templates and samples
helenkzhang Jul 28, 2023
745a582
Remove publisher project
helenkzhang Jul 28, 2023
11bc340
Remove added files in samples
helenkzhang Jul 28, 2023
39fda35
Remove schema folder
helenkzhang Jul 28, 2023
2c726e2
Remove schema md doc
helenkzhang Jul 28, 2023
e0fce46
Add comments in validator
helenkzhang Aug 2, 2023
fc581c6
Update source directory validation to check SDK
helenkzhang Aug 16, 2023
4d4417c
Refactor options classes
helenkzhang Aug 21, 2023
5ff1b89
Handles destination overwrites
helenkzhang Aug 22, 2023
261631b
Undo package change
helenkzhang Aug 22, 2023
0edd05a
Remove package folder
helenkzhang Aug 22, 2023
f6ad7d1
Undo change in ZipPluginPackage
helenkzhang Aug 22, 2023
a777b56
Add schama loader
helenkzhang Aug 22, 2023
86d075c
Clean up program.cs
helenkzhang Aug 22, 2023
2ca26b9
Add common options
helenkzhang Aug 22, 2023
ed1388a
Add args validation and exception types
helenkzhang Aug 22, 2023
b2c88c9
Clean ups
helenkzhang Aug 23, 2023
6bfcc06
Add eof new lines and copy rights
helenkzhang Aug 23, 2023
4b46c80
Remove .md files
helenkzhang Aug 23, 2023
f6a8f0b
Remove unwanted files
helenkzhang Aug 23, 2023
785b836
Remove generated xml
helenkzhang Aug 24, 2023
a7b567f
Clean up constants.cs
helenkzhang Aug 24, 2023
ec0edbf
Use classes for each options instead of service locator
helenkzhang Aug 25, 2023
56a94e6
Fix property name
helenkzhang Aug 25, 2023
1feda75
Separate options validation
helenkzhang Aug 25, 2023
396ed00
Decoupled commands and console
helenkzhang Aug 27, 2023
1c29bf3
Updated error handling
helenkzhang Aug 28, 2023
a20215b
Clean up
helenkzhang Aug 28, 2023
0fc81f8
Add manifest locators
helenkzhang Aug 29, 2023
1d38f22
Address comments for utils method
helenkzhang Aug 29, 2023
e81c364
Move options validation to separate classes
helenkzhang Aug 29, 2023
081ce35
Address more comments
helenkzhang Aug 29, 2023
0cab583
Added base command
helenkzhang Aug 29, 2023
ecf76ed
Update protected to private
helenkzhang Aug 29, 2023
3a17718
Decorate with NotNullWhen
helenkzhang Aug 29, 2023
f597939
Add docstrings
helenkzhang Aug 29, 2023
0a5a0ca
Remove empty lines and fix warning
helenkzhang Aug 30, 2023
4c43800
Update package name and tool name
helenkzhang Aug 30, 2023
1f49fb2
Update helptext and add basic documentation
helenkzhang Aug 30, 2023
b502125
Merge branch 'main' into feature/pluginToolCli
helenkzhang Aug 30, 2023
911a092
Continue processing if BadImageFormatException is encountered during …
helenkzhang Aug 30, 2023
5a2f453
Adrress more comments
helenkzhang Sep 1, 2023
6e0045c
Use VersionChecker to access sdk versions
helenkzhang Sep 1, 2023
ab6dd06
Track all versions that have been checked and log version check errors
helenkzhang Sep 5, 2023
eb29ed2
Update logging based on pr comments
helenkzhang Sep 6, 2023
eb43a01
Merge branch 'main' into feature/pluginToolCli
helenkzhang Sep 6, 2023
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
28 changes: 28 additions & 0 deletions documentation/Plugins-System-Preview-Only/PluginTool-CLI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## Plugintool CLI
The `plugintool` cli can be used to generate plugin metadata files (`metadata.json` and `metadatacontents.json`) and pack plugin binaries into `.ptix` packages.

### Usage
#### Metadata Files Generation

`plugintool metadata-gen [-s|--source <SOURCE_DIR>] [-o|--output <OUTPUT_DIR>] [-m|--manifest <MANIFEST_FILE>] [-w|--overwrite]`

- `-s|--source <SOURCE_DIR>` (Required)
Specifies the directory containing the plugin binaries.
- `-o|--output <OUTPUT_DIR>`
Specifies the directory where the metadata files will be created. If not provided, the files will be created in the current directory.
- `-m|--manifest <MANIFEST_FILE>`
Specifies the path to the manifest file. If not provided, the tool will look for a `pluginManifest.json` file in the source directory.
- `-w|--overwrite`
Specifies whether to overwrite existing metadata files if they exist. It's only valid if the `-o|--output` option is specified.

#### Package a Plugin
`plugintool pack [-s|--source <SOURCE_DIR>] [-o|--output <OUTPUT_FILE_PATH>] [-m|--manifest <MANIFEST_FILE>] [-w|--overwrite]` `

- `-s|--source <SOURCE_DIR>` (Required)
Specifies the directory containing the plugin binaries.
- `-o|--output <OUTPUT_FILE_PATH>`
Specifies the path where the `.ptix` package will be created. If not provided, the package will be created in the current directory.
- `-m|--manifest <MANIFEST_FILE>`
Specifies the path to the manifest file. If not provided, the tool will look for a `pluginManifest.json` file in the source directory.
- `-w|--overwrite`
Specifies whether to overwrite existing metadata files if they exist. It's only valid if the `-o|--output` option is specified.
54 changes: 54 additions & 0 deletions src/Microsoft.Performance.SDK.Runtime/TrackingVersionChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Collections.Concurrent;
using System.Collections.Generic;
using NuGet.Versioning;

namespace Microsoft.Performance.SDK.Runtime
{
/// <summary>
/// A <see cref="VersionChecker"/> that keeps track of the versions it has checked and whether they are supported.
/// </summary>
public class TrackingVersionChecker
: VersionChecker
{
private readonly ConcurrentDictionary<SemanticVersion, bool> checkedVersion;

/// <summary>
/// Initializes a new instance of the <see cref="TrackingVersionChecker"/>
/// </summary>
public TrackingVersionChecker()
{
this.checkedVersion = new ConcurrentDictionary<SemanticVersion, bool>();
}

/// <summary>
/// Gets the list of versions that have been checked by this instance.
/// </summary>
public IReadOnlyDictionary<SemanticVersion, bool> CheckedVersions
{
get
{
return this.checkedVersion;
}
}

/// <summary>
/// Overrides the base implementation to keep track of the versions that have been checked.
/// </summary>
/// <param name="candidateVersion">
/// The version to check.
/// </param>
/// <returns>
/// <c>true</c> if the version is supported; <c>false</c> otherwise.
/// </returns>
public override bool IsVersionSupported(SemanticVersion candidateVersion)
{
bool supported = base.IsVersionSupported(candidateVersion);
this.checkedVersion.TryAdd(candidateVersion, supported);

return supported;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Performance.Toolkit.Plugins.Core.Metadata
{
/// <summary>
/// Contains the names of the non-file data sources that are supported by the toolkit
/// </summary>
public static class DataSourceNameConstants
{
public const string DirectoryDataSourceName = "directory";
public const string ExtensionlessDataSourceName = "extensionless";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ public static class PackageConstants
public const string PluginMetadataFileName = "metadata.json";
public const string PluginContentsMetadataFileName = "contentsmetadata.json";
public const string PluginContentFolderName = "plugin/";
public const string PluginPackageExtension = ".ptix";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Performance.Toolkit.Plugins.Cli.Commands
{
/// <summary>
/// Common arguments for packgen commands.
/// </summary>
/// <param name="SourceDirectoryFullPath">
/// The full path to the directory containing the plugin.
/// </param>
/// <param name="ManifestFileFullPath">
/// The full path to the manifest file to use.
/// </param>
/// <param name="Overwrite">
/// Whether or not to overwrite an existing output file.
/// </param>
internal record PackGenCommonArgs(
string SourceDirectoryFullPath,
string? ManifestFileFullPath,
bool Overwrite);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
using Microsoft.Performance.Toolkit.Plugins.Cli.Manifest;
using Microsoft.Performance.Toolkit.Plugins.Cli.Processing;

namespace Microsoft.Performance.Toolkit.Plugins.Cli.Commands.Common
{
/// <summary>
/// Base class for commands that require the plugin to be processed.
/// </summary>
/// <typeparam name="TArgs">
/// The type of arguments for the command.
/// </typeparam>
internal abstract class PackGenCommonCommand<TArgs>
: ICommand<TArgs>
where TArgs : PackGenCommonArgs
{
protected readonly IManifestLocatorFactory manifestLocatorFactory;
protected readonly IPluginArtifactsProcessor artifactsProcessor;
protected readonly ILogger<PackGenCommonCommand<TArgs>> logger;

protected PackGenCommonCommand(
IManifestLocatorFactory manifestLocatorFactory,
IPluginArtifactsProcessor artifactsProcessor,
ILogger<PackGenCommonCommand<TArgs>> logger)
{
this.manifestLocatorFactory = manifestLocatorFactory;
this.artifactsProcessor = artifactsProcessor;
this.logger = logger;
}

/// <inheritdoc />
public int Run(TArgs args)
{
if (!TryGetProcessedPluginResult(args, out ProcessedPluginResult? processedSource))
{
return 1;
helenkzhang marked this conversation as resolved.
Show resolved Hide resolved
}

return RunCore(args, processedSource!);
mslukebo marked this conversation as resolved.
Show resolved Hide resolved
}

protected abstract int RunCore(TArgs args, ProcessedPluginResult processedSource);

private bool TryGetProcessedPluginResult(PackGenCommonArgs args, [NotNullWhen(true)] out ProcessedPluginResult? processedPluginResult)
{
processedPluginResult = null;
IManifestLocator manifestLocator = this.manifestLocatorFactory.Create(args);
if (!manifestLocator.TryLocate(out string? manifestFilePath))
{
this.logger.LogError("Failed to locate manifest file.");
return false;
}

var artifacts = new PluginArtifacts(args.SourceDirectoryFullPath, manifestFilePath);
if (!this.artifactsProcessor.TryProcess(artifacts, out processedPluginResult))
{
this.logger.LogError("Failed to process plugin artifacts.");
return false;
}

return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
using Microsoft.Performance.Toolkit.Plugins.Cli.Console.Verbs;

namespace Microsoft.Performance.Toolkit.Plugins.Cli.Commands.Common
{
/// <summary>
/// Base class for validating common packgen options.
/// </summary>
/// <typeparam name="TOptions">
/// The type of options to validate.
/// </typeparam>
/// <typeparam name="TArgs">
/// The type of arguments to return.
/// </typeparam>
internal abstract class PackGenCommonOptionsValidator<TOptions, TArgs>
: IOptionsValidator<TOptions, TArgs>
where TOptions : PackGenCommonOptions
where TArgs : PackGenCommonArgs
{
protected readonly ILogger<PackGenCommonOptionsValidator<TOptions, TArgs>> logger;

protected PackGenCommonOptionsValidator(ILogger<PackGenCommonOptionsValidator<TOptions, TArgs>> logger)
{
this.logger = logger;
}

/// <inheritdoc />
public bool TryValidate(TOptions cliOptions, [NotNullWhen(true)] out TArgs? validatedAppArgs)
{
if (!TryValidateCommonOptions(cliOptions, out PackGenCommonArgs validatedCommonArgs))
{
validatedAppArgs = null;
return false;
}

return TryValidateCore(cliOptions, validatedCommonArgs, out validatedAppArgs);
}

protected abstract bool TryValidateCore(TOptions options, PackGenCommonArgs validatedCommonAppArgs, out TArgs? validatedAppArgs);

private bool TryValidateCommonOptions(
PackGenCommonOptions rawOptions,
out PackGenCommonArgs validatedAppArgs)
{
validatedAppArgs = null!;
if (string.IsNullOrWhiteSpace(rawOptions.SourceDirectory))
{
this.logger.LogError("Source directory must be specified. Use --source <path> or -s <path>.");
return false;
}

if (!Directory.Exists(rawOptions.SourceDirectory))
{
this.logger.LogError($"Source directory '{rawOptions.SourceDirectory}' does not exist.");
return false;
}

string sourceDirectoryFullPath = Path.GetFullPath(rawOptions.SourceDirectory);

// Validate manifest file path
string? manifestFileFullPath = null;
if (rawOptions.ManifestFilePath != null)
{
if (!File.Exists(rawOptions.ManifestFilePath))
{
this.logger.LogError($"Manifest file '{rawOptions.ManifestFilePath}' does not exist.");
return false;
}

manifestFileFullPath = Path.GetFullPath(rawOptions.ManifestFilePath);
}

validatedAppArgs = new PackGenCommonArgs(sourceDirectoryFullPath, manifestFileFullPath, rawOptions.Overwrite);
return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Performance.Toolkit.Plugins.Cli.Commands
{
/// <summary>
/// Represents a command that can be executed via the CLI.
/// </summary>
/// <typeparam name="TArgs">
/// The type of arguments that the command accepts.
/// </typeparam>
internal interface ICommand<TArgs>
where TArgs : class
{
/// <summary>
/// Executes the command.
/// </summary>
/// <param name="args">
/// The arguments to the command.
/// </param>
/// <returns>
/// The exit code of the command. A value of 0 indicates success. A value of 1 indicates failure.
helenkzhang marked this conversation as resolved.
Show resolved Hide resolved
/// </returns>
int Run(TArgs args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.Performance.Toolkit.Plugins.Cli.Commands
{
/// <summary>
/// Represents the arguments for the <see cref="MetadataGenCommand"/>.
/// </summary>
/// <param name="SourceDirectoryFullPath">
/// The full path to the directory containing the source code for the plugin.
/// </param>
/// <param name="ManifestFileFullPath">
/// The full path to the manifest file for the plugin.
/// </param>
/// <param name="OutputDirectoryFullPath">
/// The full path to the directory to write the generated metadata to.
/// </param>
/// <param name="Overwrite">
/// Whether or not to overwrite existing files in the output directory.
/// </param>
internal record MetadataGenArgs(
string SourceDirectoryFullPath,
string? ManifestFileFullPath,
string? OutputDirectoryFullPath,
bool Overwrite)
: PackGenCommonArgs(
SourceDirectoryFullPath,
ManifestFileFullPath,
Overwrite)
{
/// <summary>
/// Initializes a new instance of the <see cref="MetadataGenArgs"/> class.
/// </summary>
/// <param name="commonArgs">
/// The common arguments.
/// </param>
/// <param name="outputDirectoryFullPath">
/// The full path to the directory to write the generated metadata.
/// </param>
public MetadataGenArgs(PackGenCommonArgs commonArgs, string? outputDirectoryFullPath)
: this(commonArgs.SourceDirectoryFullPath, commonArgs.ManifestFileFullPath, outputDirectoryFullPath, commonArgs.Overwrite)
{
}
}
}
Loading