Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"sourcePath": "src",
"packageSource": "nuget"
}
}
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ indent_size = 4
trim_trailing_whitespace = true
# end_of_line = crlf # The end_of_line setting is maintained by .gitattributes! Do not set it here!

[*.{json,yml,yaml,xml,md,csproj,props,config}]
indent_style = space
indent_size = 2

[*.cs]
charset = utf-8
max_line_length = 130
Expand Down
6 changes: 3 additions & 3 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ Add the issue number here. e.g. #123

# Checklist

- [ ] I have double checked my own changes
- [ ] I have double-checked my own changes
- [ ] I have read the [Contribution Guidelines](https://github.com/solarwinds/net-changesets/blob/main/CONTRIBUTING.md)
- [ ] Include the issue number (#xxx) in branch name and PR title and description
- [ ] Provide a reasonable description of the PR in the [Changes](#Changes) section
- [ ] Include the issue number (#xxx) in the branch name, PR title, and PR description in the Issue number section above
- [ ] Provide a reasonable description of the PR in the [Changes](#changes) section
- [ ] I have commented on the issue above and discussed the intended changes
- [ ] All newly added code is adequately covered by tests
- [ ] Make sure your PR is passing the CI/CD pipeline
Expand Down
46 changes: 36 additions & 10 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ NET Changesets is a .NET CLI tool for managing versioning and changelogs in mult

## Technology stack

**Language:** C# 12.0 with nullable reference types and implicit usings enabled
**Runtime:** .NET 8.0 (SDK 8.0.406)
**CLI Framework:** Spectre.Console v0.50.0 (interactive prompts, tables, markup rendering)
**Dependency Injection:** Microsoft.Extensions.DependencyInjection v9.0.9
**Testing:** NUnit 4.4.0, Moq 4.20.72, AwesomeAssertions 9.1.0, Spectre.Console.Testing
**Code Analysis:** Microsoft .NET analyzers (all enabled, warnings-as-errors), Spectre.Console.Analyzer
**Package Management:** Central Package Management via `Directory.Packages.props`
**CI/CD:** GitHub Actions (build, test, pack, format verification)
**Language:** C# 12.0 with nullable reference types and implicit usings enabled
**Runtime:** .NET 8.0 (SDK 8.0.406)
**CLI Framework:** Spectre.Console v0.50.0 (interactive prompts, tables, markup rendering)
**Dependency Injection:** Microsoft.Extensions.DependencyInjection v9.0.9
**Testing:** NUnit 4.4.0, Moq 4.20.72, AwesomeAssertions 9.1.0, Spectre.Console.Testing
**Code Analysis:** Microsoft .NET analyzers (all enabled, warnings-as-errors), Spectre.Console.Analyzer
**Package Management:** Central Package Management via `Directory.Packages.props`
**CI/CD:** GitHub Actions (build, test, pack, format verification)
**External Tools:** Git CLI (for diff detection), dotnet CLI (for pack/publish)

## Directory structure
Expand Down Expand Up @@ -44,6 +44,7 @@ SolarWinds.Changesets.sln # Main solution file
**CLI Framework:** Uses Spectre.Console.Cli with `CommandApp<AddChangesetCommand>` (Add is default). Commands registered in `Program.cs` via `config.AddCommand<T>()`. All commands inherit from `ConfigurationCommandBase` which loads `.changeset/config.json`.

**Dependency Injection:** Services registered in `ServiceCollection` and integrated via custom `TypeRegistrar`/`TypeResolver` (required by Spectre.Console.Cli). Main services:

- `IConfigurationService`: Loads/validates `.changeset/config.json`
- `IChangesetsRepository`: Reads/writes/deletes changeset markdown files
- `ICsProjectsRepository`: Parses .csproj files, updates `<VersionPrefix>` elements
Expand All @@ -55,6 +56,7 @@ SolarWinds.Changesets.sln # Main solution file
**Semantic Versioning:** Custom `Semver` class (Major.Minor.Patch) with methods `RaiseMajor()`, `RaiseMinor()`, `RaisePatch()`. Parses version strings from `<VersionPrefix>` in .csproj files. Version bumps cascade dependencies (updating dependent projects).

**Command Flow Example (version command):**

1. `VersionChangesetCommand.ExecuteCommandAsync()` calls `IChangesetsRepository.GetChangesetsAsync()`
2. Reads all `.md` files from `.changeset/` directory
3. Parses YAML front matter (project names, bump types) and markdown content
Expand All @@ -69,28 +71,33 @@ SolarWinds.Changesets.sln # Main solution file
**Prerequisites:** .NET 8.0 SDK (version 8.0.406 or compatible via rollForward in `global.json`)

**Build:**

```bash
dotnet restore --packages ./packages
dotnet build -c Release --no-restore
```

**Test:**

```bash
dotnet test -c Release --no-restore --no-build --verbosity normal
```

**Pack:**

```bash
dotnet pack -c Release --no-restore --no-build
# Output: ./nupkg/SolarWinds.Changesets.0.1.1.nupkg
```

**Code Style Check:**

```bash
dotnet format --no-restore --verify-no-changes
```

**Local Installation for Testing:**

```bash
dotnet tool uninstall solarwinds.changesets --global
dotnet tool install solarwinds.changesets --global --add-source ./nupkg
Expand All @@ -109,6 +116,7 @@ dotnet run --project .\src\SolarWinds.Changesets\SolarWinds.Changesets.csproj
```

**Verification checklist:**

1. ✅ Build succeeds: `dotnet build -c Release --no-restore`
2. ✅ All tests pass: `dotnet test -c Release --no-restore --no-build`
3. ✅ Code formatting: `dotnet format --no-restore --verify-no-changes`
Expand All @@ -118,15 +126,18 @@ dotnet run --project .\src\SolarWinds.Changesets\SolarWinds.Changesets.csproj
## Domain configurations

**Changeset Config (`.changeset/config.json`):**

```json
{
"sourcePath": "src", // Relative path to projects folder
"packageSource": "nuget" // NuGet source name from NuGet.config
"sourcePath": "src", // Relative path to projects folder
"packageSource": "nuget" // NuGet source name from NuGet.config
}
```

Created by `changeset init`. Loaded by `ConfigurationService`. Default `packageSource` is `nuget.org`.

**NuGet.config:** Optional file for custom package sources. Example:

```xml
<packageSources>
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
Expand All @@ -135,6 +146,7 @@ Created by `changeset init`. Loaded by `ConfigurationService`. Default `packageS
```

**Changeset File Format (`.changeset/{randomname}.md`):**

```markdown
---
"ProjectA": minor
Expand All @@ -143,16 +155,19 @@ Created by `changeset init`. Loaded by `ConfigurationService`. Default `packageS

Added new feature X and fixed bug Y in ProjectB
```

Generated by `add` command. Filename: 10 random lowercase letters + `.md`. Projects listed in YAML front matter with bump type (major/minor/patch).

**Project File Requirements:**

- Must contain `<VersionPrefix>` element (e.g., `<VersionPrefix>1.0.0</VersionPrefix>`)
- Supported format: `Major.Minor.Patch` (parsed via `System.Version`)
- `version` command updates this element in-place using XML manipulation

## Conventions

**Code Style:**

- **Implicit usings enabled** (no need for common System namespaces)
- **Nullable reference types enabled** (treat null warnings as errors)
- **File-scoped namespaces** (e.g., `namespace SolarWinds.Changesets.Commands.Add;`)
Expand All @@ -161,54 +176,63 @@ Generated by `add` command. Filename: 10 random lowercase letters + `.md`. Proje
- **IDE rules enforced in CI** via `dotnet format` (not in build)

**Testing:**

- NUnit framework with `[TestFixture]` and `[Test]` attributes
- Global using for `NUnit.Framework` (defined in test .csproj)
- Test data in `TestData/` folders, copied to output directory
- Use `Spectre.Console.Testing.TestConsole` for command output assertions
- Mock external processes using `Moq` on `IProcessExecutor`

**Naming:**

- Commands: `{Action}ChangesetCommand` (e.g., `AddChangesetCommand`)
- Interfaces: Standard `I` prefix (e.g., `IConfigurationService`)
- Internal classes: Most implementation classes are `internal sealed`
- Constants: Defined in `Constants` static class (e.g., `Constants.WorkingDirectoryFullPath`)

**Dependency Constraints:**

- **Only allowed third-party dependency:** Spectre.Console (and related packages)
- Rationale: Minimize external dependencies for a CLI tool
- All other needs met by .NET BCL or Microsoft.Extensions packages

**Git Workflow:**

- Branch naming: `{type}/{issueID}-{description}` (e.g., `feature/123-add-new-command`)
- Commit messages: Start with issue ID (e.g., `123 Implement status command`)
- GPG signing required for commits
- PR title format: `{Type} #{issueID} {Description}`

**Error Handling:**

- `ExceptionHandler.Handle()` registered in Spectre.Console CLI config
- Custom exception: `InitializationException` for config validation errors
- Return codes defined in `ResultCodes` class (not yet fully implemented)

## Integration points

**Git Integration:**

- `GitService.GetDiff()` calls `git diff --name-only {sourcePath}` to detect modified .csproj files
- Used by `publish` command to identify packages needing publication
- Requires git executable in PATH

**Dotnet CLI Integration:**

- `DotnetService.Pack()` calls `dotnet pack {projectPath} --output {Constants.NupkgOutputFullPath}`
- `DotnetService.Publish()` calls `dotnet nuget push {nupkgPath} --source {packageSource}`
- NuGet API key expected in environment or nuget.config (standard dotnet behavior)
- Output directory: `./nupkg/` (created if not exists)

**File System Operations:**

- Changeset files: `.changeset/` directory (created by `init` command)
- CHANGELOG.md: Root of repository
- .csproj files: Located via recursive search from `sourcePath` config
- All paths resolved relative to `Constants.WorkingDirectoryFullPath` (current directory)

**NuGet Sources:**

- Resolved via standard dotnet/NuGet.config mechanisms
- Default: `nuget` source (typically nuget.org)
- Custom sources defined in `NuGet.config` (repository or user-level)
Expand All @@ -219,11 +243,13 @@ Generated by `add` command. Filename: 10 random lowercase letters + `.md`. Proje
**Current State:** MVP stage with manual CLI operations. All 5 commands implemented (`init`, `add`, `version`, `publish`, `status`).

**Known Limitations:**

- `add` command supports only single bump type for all selected projects (npm version allows per-project bumps)
- No command-line options for `add` (interactive mode only)
- Manual execution required (no GitHub Action yet)

**Planned Features:**

- **GitHub Action** (primary roadmap item): Automated PR creation for version bumps, reusing existing codebase. See `.NET GitHub Action` documentation.
- Future improvements open for discussion via GitHub issues

Expand Down
6 changes: 6 additions & 0 deletions .github/instructions/coding.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,39 @@ applyTo: "**/*.go,**/*.cs,**/*.java,**/*.kt,**/*.py,**/*.js,**/*.ts,**/*.tsx"
# Coding Instructions

## General code style and readability

- Write code that is readable, understandable, and maintainable for future readers.
- Aim to create software that is not only functional but also readable, maintainable, and efficient throughout its lifecycle.
- Prioritize clarity to make reading, understanding, and modifying code easier.
- Adhere to established coding standards and write well-structured code to reduce errors.
- Regularly review and refactor code to improve structure, readability, and maintainability. Always leave the codebase cleaner than you found it.

## Naming conventions

- Choose names for variables, functions, and classes that reflect their purpose and behavior.
- A name should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.
- Use specific names that provide a clearer understanding of what the variables represent and how they are used.

## DRY principle

- Follow the DRY (Don't Repeat Yourself) Principle and Avoid Duplicating Code or Logic.
- Avoid writing the same code more than once. Instead, reuse your code using functions, classes, modules, libraries, or other abstractions.
- Modify code in one place if you need to change or update it.

## Function length and responsibility

- Write short functions that only do one thing.
- Follow the single responsibility principle (SRP), which means that a function should have one purpose and perform it effectively.
- If a function becomes too long or complex, consider breaking it into smaller, more manageable functions.

## Comments usage

- Use comments sparingly, and when you do, make them meaningful.
- Don't comment on obvious things. Excessive or unclear comments can clutter the codebase and become outdated.
- Use comments to convey the "why" behind specific actions or explain unusual behavior and potential pitfalls.
- Provide meaningful information about the function's behavior and explain unusual behavior and potential pitfalls.

## Conditional encapsulation

- One way to improve the readability and clarity of functions is to encapsulate nested if/else statements into other functions.
- Encapsulating such logic into a function with a descriptive name clarifies its purpose and simplifies code comprehension.
13 changes: 9 additions & 4 deletions .github/instructions/dotnet.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ applyTo: "**/*.cs"
# Instructions for .NET C# code

C# code (besides unit tests) should adhere to the following guidelines:

- Follow SOLID principles
- Use dependency injection
- Use appropriate configuration formats (JSON, YAML, environment variables)
Expand Down Expand Up @@ -119,7 +120,7 @@ public async Task<ProcessResult> ProcessAsync(UserData userData, CancellationTok

## Repository structure

The following are instructions on how to name folders and files and how the basic repository structure should look like
The following are instructions on how to name folders and files and how the basic repository structure should look like

- Each project (CSPROJ) should be in its own folder.
- Projects with the implementation code should be under "src" folder.
Expand All @@ -130,19 +131,21 @@ The following are instructions on how to name folders and files and how the basi
### Code Examples

#### Async/Await Pattern

```csharp
public async Task<User> GetUserAsync(int userId, CancellationToken cancellationToken = default)
{
using var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.GetAsync($"/api/users/{userId}", cancellationToken);
response.EnsureSuccessStatusCode();

var content = await response.Content.ReadAsStringAsync(cancellationToken);
return JsonSerializer.Deserialize<User>(content);
}
```

#### String Comparison Example

```csharp
// For culture-sensitive comparisons
if (userInput.Equals(expectedValue, StringComparison.InvariantCultureIgnoreCase))
Expand All @@ -152,6 +155,7 @@ if (fileName.EndsWith(".txt", StringComparison.Ordinal))
```

#### StringBuilder Usage

```csharp
// Use StringBuilder for heavy concatenation in loops
var builder = new StringBuilder();
Expand All @@ -163,6 +167,7 @@ return builder.ToString();
```

#### Regex Source Generators

```csharp
using System.Text.RegularExpressions;

Expand All @@ -171,11 +176,11 @@ public partial class MyService
// Use regex source generators for compile-time validation and better performance
[GeneratedRegex(@"\b(query|mutation)\b\s+(\w+)")]
private static partial Regex OperationNameRegex();

// For culture-sensitive patterns, specify options
[GeneratedRegex(@"[a-zA-Z_:][a-zA-Z0-9_:]*", RegexOptions.IgnoreCase)]
private static partial Regex IdentifierRegex();

public string ExtractOperationName(string query)
{
var match = OperationNameRegex().Match(query);
Expand Down
Loading
Loading