Skip to content

Commit d5a05c2

Browse files
Merge pull request #140 from microsoft/users/jstatia/implement_indirectsignature_plugin
Implement IndirectSignatures through CoseSignTool Plugin and iterate plugin architecture.
2 parents 30d66c2 + 7f15c28 commit d5a05c2

File tree

84 files changed

+7890
-1146
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+7890
-1146
lines changed

.github/copilot-instructions.md

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# CoseSignTool Coding Standards for GitHub Copilot
2+
3+
This file ensures GitHub Copilot follows the repository's coding standards as defined in .editorconfig and established patterns.
4+
5+
## Code Generation Preferences
6+
7+
### File Headers
8+
- Always include the Microsoft copyright header at the top of all C# files:
9+
```csharp
10+
// Copyright (c) Microsoft Corporation.
11+
// Licensed under the MIT License.
12+
```
13+
14+
### Namespace and Using Directives
15+
- Use file-scoped namespaces (C# 10+ feature): `namespace MyNamespace;`
16+
- Place using directives inside the namespace
17+
- Sort System directives first, then others alphabetically
18+
- Do not separate import directive groups with blank lines
19+
- Follow namespace-to-folder structure matching
20+
21+
### Naming Conventions
22+
- **Constants**: PascalCase (e.g., `DefaultStoreName`)
23+
- **Static private fields**: PascalCase (e.g. `StaticStoreName`)
24+
- **Private/internal instance fields**: camelCase (e.g., `commands`)
25+
- **Public properties/methods**: PascalCase
26+
- **Local variables**: camelCase
27+
- **Parameters**: camelCase
28+
29+
### Code Style Preferences
30+
- **Braces**: Always use braces for control statements (enforced as error)
31+
- **var usage**: Avoid `var` - use explicit types for clarity
32+
- **this qualifier**: useh `this.` so that it's clear when member parameters or state is being modified
33+
- **Predefined types**: Use predefined types (`int`, `string`) over .NET types (`Int32`, `String`)
34+
- **Null checking**: Prefer `is null` over `== null`
35+
- **Object initialization**: Prefer object and collection initializers
36+
- **String interpolation**: Use simplified interpolation when possible
37+
38+
### Expression Preferences
39+
- **Expression-bodied members**:
40+
- Use for properties, indexers, and accessors when appropriate
41+
- Avoid for methods, constructors, and operators (prefer block bodies)
42+
- **Pattern matching**: Prefer pattern matching over `is` with cast checks
43+
- **Target-typed new**: Use when type is apparent (e.g., `List<string> items = new();`)
44+
- **Using statements**: Prefer simple using statements over using blocks
45+
46+
### Formatting Rules
47+
- **Indentation**: 4 spaces (no tabs)
48+
- **End of line**: CRLF (Windows line endings)
49+
- **Final newline**: Do not insert final newline
50+
- **Trim whitespace**: Always trim trailing whitespace
51+
- **New lines**:
52+
- Opening braces on new line for all constructs
53+
- `else`, `catch`, `finally` on new lines
54+
- Members in object initializers on new lines
55+
56+
### Space Preferences
57+
- No space after casts: `(int)value`
58+
- Space after keywords: `if (condition)`
59+
- Space around binary operators: `a + b`
60+
- Space after commas: `Method(a, b, c)`
61+
- Space around colons in inheritance: `class Derived : Base`
62+
- No space before dots: `object.Property`
63+
- No space in empty parentheses: `Method()`
64+
65+
### Modifier Order
66+
Follow this order: `public`, `private`, `protected`, `internal`, `static`, `extern`, `new`, `virtual`, `abstract`, `sealed`, `override`, `readonly`, `unsafe`, `volatile`, `async`
67+
68+
### Error Handling and Diagnostics
69+
- Null reference warnings are suggestions, not errors
70+
- Unused parameters should be flagged
71+
- Platform compatibility should be validated
72+
- File headers are required (enforced as error)
73+
- Missing braces are errors
74+
- Unused private members are errors
75+
76+
### Plugin Project-Specific Patterns
77+
- **Plugin naming**: Use `.Plugin.csproj` suffix for auto-packaging
78+
- **Plugin Assembly naming**: Use `.Plugin.dll` suffix for runtime discovery
79+
- **Plugin Exit codes**: Use the `PluginExitCode` enum for plugin commands
80+
- **Async patterns**: Always use `CancellationToken` parameters in async methods
81+
- **Plugin Interface implementation**: Implement plugin interfaces (`ICoseSignToolPlugin`, `IPluginCommand`)
82+
- **Plugin Error handling**: Use appropriate exit codes and console error output
83+
84+
### Documentation
85+
- Use XML documentation comments for public APIs
86+
- Include parameter descriptions and return value documentation
87+
- Use `<summary>`, `<param>`, `<returns>` tags appropriately
88+
- Where possible provide appropriate `<example>` tags for different behaviors
89+
- Document exceptions with `<exception>` tags
90+
91+
### Testing Patterns
92+
- Use descriptive test method names
93+
- Follow Arrange-Act-Assert pattern
94+
- Use meaningful assertions with clear error messages
95+
- Include both positive and negative test cases
96+
- Cover as close to 100% of blocks as posible without overgenerating test cases
97+
- Prefer NUnit tests with the new `Assert.That` syntax when writing new tests
98+
- Prefer data-driven test cases when possible
99+
- Prefer test local state over shared state to enable parallel execution
100+
- Leverage maintainability patterns in test code by creating shared functions for common code
101+
102+
### Plugin Development Guidelines
103+
- Implement `PluginCommandBase` for command implementations
104+
- Use proper dependency injection patterns
105+
- Handle configuration through `IConfiguration`
106+
- Implement proper cancellation token support
107+
- Follow security best practices for plugin loading
108+
109+
## Example Code Structure
110+
111+
```csharp
112+
// Copyright (c) Microsoft Corporation.
113+
// Licensed under the MIT License.
114+
115+
using Microsoft.Extensions.Configuration;
116+
using CoseSignTool.Abstractions;
117+
118+
namespace CoseSignTool.MyPlugin;
119+
120+
/// <summary>
121+
/// Example plugin implementation following repository standards.
122+
/// </summary>
123+
public class ExamplePlugin : ICoseSignToolPlugin
124+
{
125+
private readonly List<IPluginCommand> commands;
126+
private static readonly object LockObject = new();
127+
128+
public ExamplePlugin()
129+
{
130+
this.commands = new List<IPluginCommand>
131+
{
132+
new ExampleCommand()
133+
};
134+
}
135+
136+
public string Name => "Example Plugin";
137+
public string Version => "1.0.0";
138+
public string Description => "An example plugin demonstrating coding standards.";
139+
public IEnumerable<IPluginCommand> Commands => _commands;
140+
141+
public void Initialize(IConfiguration? configuration = null)
142+
{
143+
// Initialization logic here
144+
}
145+
}
146+
147+
/// <summary>
148+
/// Example command implementation.
149+
/// </summary>
150+
public class ExampleCommand : PluginCommandBase
151+
{
152+
public override string Name => "example";
153+
public override string Description => "Example command for demonstration.";
154+
public override string Usage => "example --input <file> [--output <file>]";
155+
156+
public override IDictionary<string, string> Options => new Dictionary<string, string>
157+
{
158+
["--input"] = "input",
159+
["--output"] = "output"
160+
};
161+
162+
public override async Task<PluginExitCode> ExecuteAsync(
163+
IConfiguration configuration,
164+
CancellationToken cancellationToken = default)
165+
{
166+
try
167+
{
168+
string inputFile = base.GetRequiredValue(configuration, "input");
169+
string? outputFile = base.GetOptionalValue(configuration, "output");
170+
171+
if (!File.Exists(inputFile))
172+
{
173+
Console.Error.WriteLine($"Input file not found: {inputFile}");
174+
return PluginExitCode.UserSpecifiedFileNotFound;
175+
}
176+
177+
// Process the file
178+
await this.ProcessFileAsync(inputFile, outputFile, cancellationToken);
179+
180+
return PluginExitCode.Success;
181+
}
182+
catch (OperationCanceledException)
183+
{
184+
Console.Error.WriteLine("Operation was cancelled");
185+
return PluginExitCode.UnknownError;
186+
}
187+
catch (ArgumentNullException ex)
188+
{
189+
Console.Error.WriteLine($"Missing required option: {ex.ParamName}");
190+
return PluginExitCode.MissingRequiredOption;
191+
}
192+
catch (Exception ex)
193+
{
194+
Console.Error.WriteLine($"Unexpected error: {ex.Message}");
195+
return PluginExitCode.UnknownError;
196+
}
197+
}
198+
199+
private static async Task ProcessFileAsync(
200+
string inputFile,
201+
string? outputFile,
202+
CancellationToken cancellationToken)
203+
{
204+
// Implementation here
205+
await Task.CompletedTask;
206+
}
207+
}
208+
```
209+
210+
## Summary
211+
When generating code for this repository, always:
212+
1. Include the Microsoft copyright header
213+
2. Use file-scoped namespaces
214+
3. Follow the specified naming conventions
215+
4. Use explicit types instead of var
216+
5. Include proper error handling with appropriate exit codes
217+
6. Implement cancellation token support in async methods
218+
7. If creating a plug-in, use the established plugin patterns for extensibility
219+
8. Follow the formatting and spacing rules exactly as specified
220+
9. Include comprehensive XML documentation for public APIs
221+
10. Ensure all generated code follows the .editorconfig rules

0 commit comments

Comments
 (0)