Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
36 changes: 6 additions & 30 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ Stryker.NET is a mutation testing framework for .NET projects. It allows you to
## Contributing Workflow

### Code Standards

- Follow the repository's `.editorconfig` and [Microsoft C# coding guidelines](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions)
- Create or edit unit tests or integration tests for all changes
- Update documentation when adding features

### Pull Request Title Convention

When creating or updating pull requests, **always** use Angular-style conventional commit format for PR titles:
- Format: `<type>(<scope>): <subject>`
- Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `revert`
Expand All @@ -38,13 +40,15 @@ When creating or updating pull requests, **always** use Angular-style convention
**Why**: The project uses squash merging, so the PR title becomes the commit message in the main branch history.

### Running Tests
- **Unit tests**: Run `dotnet test` in the `/src` directory

- **Unit tests**: Run `dotnet test` in the `/src` directory or use your IDE's test runner
- **Integration tests**:
- On **Windows**: Run `.\integration-tests.ps1` in the root of the repo (PowerShell 7 recommended)
- On **macOS/Linux**: Run `pwsh ./integration-tests.ps1` in the root of the repo (requires [PowerShell 7](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell))
- Always run unit tests and integration tests before committing changes
- Always run unit tests and integration after making a change to ensure nothing is broken
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence is missing a word and reads incorrectly; update 'integration' to 'integration tests' for clarity and grammar.

Suggested change
- Always run unit tests and integration after making a change to ensure nothing is broken
- Always run unit tests and integration tests after making a change to ensure nothing is broken

Copilot uses AI. Check for mistakes.

### Testing Locally

To test Stryker.NET on a project:
1. In `Stryker.CLI`, open `properties > Debug`
2. Create a new Debug profile
Expand All @@ -55,31 +59,3 @@ To test Stryker.NET on a project:

**Note**: Running Stryker on itself doesn't work as assemblies will be in use. To run Stryker on the stryker codebase, use the official nuget release via `dotnet tool install dotnet-stryker` and then `dotnet stryker`.

Comment on lines 55 to 61
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is scoped to adding GitHub release logic, but this change removes the entire “Adding a New Mutator” section from the Copilot instructions. If that removal is unintentional, it should be reverted or moved to a separate PR to keep the release-related changes focused.

Copilot uses AI. Check for mistakes.
## Adding a New Mutator

See the full guide in [adding_a_mutator.md](../adding_a_mutator.md).

### Key Points for Mutators
1. **Purpose**: Generate mutations that look like possible human errors, not just any mutation
2. **Performance**: Keep mutators fast - they're called on every syntax element
3. **Buildable**: Generated mutations should compile in most situations
4. **Killable**: Avoid mutations that always raise exceptions or are semantically equivalent
5. **General**: Mutators should work for all projects, not be framework-specific

### Implementation Steps
1. Create a class inheriting from `MutatorBase<T>` and implementing `IMutator`
2. Specify the expected `SyntaxNode` class you can mutate (e.g., `StatementSyntax`)
3. Override the `MutationLevel` property (typically `Complete` or `Advanced`)
4. Override `ApplyMutation<T>` to generate mutations
5. Add an entry in the `Mutator` enum
6. Create an instance in the `CsharpMutantOrchestrator` constructor
7. Add comprehensive unit tests
8. Update [docs/mutations.md](../docs/mutations.md)

### Mutator Guidelines
- Use Roslyn APIs to work with syntax trees, not text transformations
- Each mutator is called on every syntax element recursively
- Return an empty list or `yield break` if no mutation can be generated
- Mutators must not throw exceptions
- Support various C# syntax versions (expression body vs block statement)
- Invest in unit tests early - look at existing mutator tests for examples
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Change Log

All notable changes to this project will be documented in a package specific changelog file:
All notable changes will be documented in this file.

* [Change log for stryker](/src/Stryker.Core/CHANGELOG.md)
* [Change log for dotnet-stryker](/src/Stryker.CLI/CHANGELOG.md)
For historical release notes prior to the consolidated changelog, see the package-specific files:

* [Historical change log for stryker](/src/Stryker.Core/CHANGELOG.md)
* [Historical change log for dotnet-stryker](/src/Stryker.CLI/CHANGELOG.md)

<!-- changelog -->
4 changes: 2 additions & 2 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
Releasing a new version of the Stryker.NET packages can be done by following these steps:
1. Clone the repo or checkout (and pull) the master branch.
2. Look at the commits since the last release and determine the next version number. If we are on 0.11.0 and there are only bugfixes and refactorings: we go to 0.11.1. If there are also new features or breaking changes, we go to 0.12.0.
3. Run `npm run prepare-release` from the root of the repo and enter the new version number.
4. Verify that the commit is on GitHub and the releases in GitHub have been made.
3. Run `npm run prepare-release` from the root of the repo and enter the new version number. This will automatically create GitHub releases for the new tags with the correct release notes from the changelogs.
4. Verify that the commit is on GitHub and that the GitHub releases for the new version have been created.
Comment on lines +5 to +6
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The text says this will create GitHub releases for “the new tags” (plural) with notes from “the changelogs”, but prepare-release.js currently creates a single dotnet-stryker@... tag/release and updates only the root CHANGELOG.md. Please adjust the wording to match the actual behavior (singular), or update the script to create releases for multiple tags if that’s the intent.

Copilot uses AI. Check for mistakes.
Comment thread
richardwerkman marked this conversation as resolved.
5. Wait for build on master to complete on Azure Pipelines and then start the Production environment on Azure Pipelines.
6. Approve the Production environment on Azure Pipelines (this approval prevents accidental releases).

Expand Down
32 changes: 26 additions & 6 deletions prepare-release.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,32 @@
console.log(`Updating version numbers in ${pckg.csproj}`);
replaceVersionNumber(pckg.csproj, `<VersionPrefix>${oldVersionPrefix}</VersionPrefix>`, `<VersionPrefix>${versionPrefix}</VersionPrefix>`);
replaceVersionNumber(pckg.csproj, `<VersionSuffix>${oldVersionSuffix}</VersionSuffix>`, `<VersionSuffix>${versionSuffix}</VersionSuffix>`);
});

if (!versionSuffix) {
console.log(`Updating changelog for ${pckg.name}`);
commitMessageLines.push(`- ${pckg.name}@${newVersionNumber}`);
exec(`npx conventional-changelog-cli -p angular --infile "${pckg.path}/CHANGELOG.md" --same-file --commit-path ${pckg.path} --tag-prefix "${pckg.name}@"`);
let releaseNotes = '';
if (!versionSuffix) {
console.log(`Updating changelog`);
commitMessageLines.push(`- dotnet-stryker@${newVersionNumber}`);
releaseNotes = execSync(`npx conventional-changelog-cli -p angular --tag-prefix "dotnet-stryker@"`, { encoding: 'utf8' }).trim();
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

releaseNotes is captured by running conventional-changelog-cli without limiting the output, which typically prints the entire changelog history to stdout. That means the annotated tag message (and therefore gh release ... --notes-from-tag) can become huge/incorrect (entire history instead of just this release) and may even fail due to execSync maxBuffer. Consider generating only the latest release section (e.g., --release-count 1 / -r 1) and ensure the options match whatever you write into CHANGELOG.md so the tag notes and file contents stay consistent.

Suggested change
releaseNotes = execSync(`npx conventional-changelog-cli -p angular --tag-prefix "dotnet-stryker@"`, { encoding: 'utf8' }).trim();
const changelogCommand = 'npx conventional-changelog-cli -p angular --tag-prefix "dotnet-stryker@" --release-count 1';
releaseNotes = execSync(changelogCommand, { encoding: 'utf8' }).trim();

Copilot uses AI. Check for mistakes.
const changelogPath = './CHANGELOG.md';
const changelog = fs.readFileSync(changelogPath, { encoding: 'UTF-8' });
const marker = '<!-- changelog -->';
if (!changelog.includes(marker)) {
throw new Error(`${changelogPath} is missing the '${marker}' insertion marker`);
}
});
fs.writeFileSync(changelogPath, changelog.replace(marker, `${marker}\n\n${releaseNotes}`), { encoding: 'UTF-8' });
}

console.log('Updating azure-pipelines.yml');
replaceVersionNumber('./azure-pipelines.yml', `VersionBuildNumber: $[counter('${oldVersion}', 1)]`, `VersionBuildNumber: $[counter('${versionPrefix}', 1)]`);
replaceVersionNumber('./azure-pipelines.yml', `PackageVersion: '${oldVersion}'`, `PackageVersion: '${versionPrefix}'`);

Comment on lines 85 to 88
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The temp file .release-notes.md is deleted only if git tag succeeds; if git tag throws, the file is left behind in the repo root. Using a try/finally cleanup (and ideally a unique temp file location such as os.tmpdir()/fs.mkdtemp) would prevent leftover files and avoid collisions if the script is re-run.

Copilot uses AI. Check for mistakes.
if (!versionSuffix) {
console.log('Tagging commit');
packages.forEach(pckg => exec(`git tag -a ${pckg.name}@${newVersionNumber} -m "${pckg.name}@${newVersionNumber}"`));
const tmpTagFile = '.release-notes.md';
fs.writeFileSync(tmpTagFile, releaseNotes);
exec(`git tag -a dotnet-stryker@${newVersionNumber} -F ${tmpTagFile}`);
fs.unlinkSync(tmpTagFile);
}

console.log(`Creating commit`);
Expand All @@ -71,6 +82,15 @@

console.log(`Pushing commit ${versionSuffix?'':' and tags'}`);
Comment on lines 97 to 101
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

newVersionNumber is interpolated into shell commands (git tag ...${newVersionNumber}... and gh release create ...${newVersionNumber}...) executed via execSync with a string, which runs through a shell. Even though this is a maintainer script, it’s safer to validate the version input (e.g., strict semver) and/or use execFileSync/spawnSync with an argument array to avoid shell injection and quoting issues.

Copilot uses AI. Check for mistakes.
exec('git push --follow-tags');
if (!versionSuffix) {
try {
const execSync = require('child_process').execSync;

Check warning on line 87 in prepare-release.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `node:child_process` over `child_process`.

See more on https://sonarcloud.io/project/issues?id=stryker-net&issues=AZ1ZCz9Vmm9_Jq1qJrTV&open=AZ1ZCz9Vmm9_Jq1qJrTV&pullRequest=3523
execSync(`gh release create dotnet-stryker@${newVersionNumber} --title "dotnet-stryker@${newVersionNumber}" --notes-from-tag`);
console.log(`Created GitHub release for dotnet-stryker@${newVersionNumber}`);
} catch (e) {
console.warn('Failed to create GitHub release:', e.message);
}
}
rl.close();
});

2 changes: 1 addition & 1 deletion stryker-net.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@
"ms-vscode.powershell"
]
}
}
}
Loading