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 .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x

- name: Restore dependencies
run: dotnet restore
Expand Down
12 changes: 8 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x

- name: Build Binaries
run: |
Expand All @@ -29,9 +29,13 @@ jobs:
- name: Build Debian Package
run: |
chmod +x build_deb.sh
VERSION=${{ github.ref_name }}
VERSION=${VERSION#V.}
./build_deb.sh $VERSION
# Accepts v1.1.0, V1.1.0, V.1.1.0 or 1.1.0. Debian versions must start
# with a digit, so the leading tag prefix has to go.
VERSION="${{ github.ref_name }}"
VERSION="${VERSION#[vV]}"
VERSION="${VERSION#.}"
echo "Packaging version: $VERSION"
./build_deb.sh "$VERSION"

- name: Pack NuGet Tool
run: dotnet pack MTC/MTC.csproj -c Release -o nupkg
Expand Down
131 changes: 131 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Changelog

All notable changes to this project are documented here.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0] — unreleased

Maintenance release after an eight-month pause. Everything now targets .NET 10, the
template engine vulnerability is closed, and the code MTC generates compiles without
warnings.

### Security

- Updated Scriban from 6.5.2 to 7.2.6, closing 14 known advisories — one critical
and eight high severity. Scriban renders every template, so this affected all
generated output.
- Generated Web/API projects now pin `Microsoft.OpenApi` 2.11.0. The version pulled in
transitively by `Microsoft.AspNetCore.OpenApi` carries a known advisory
(GHSA-v5pm-xwqc-g5wc).
- Rendered file and directory names can no longer escape the output directory. A name
such as `../Escaped` previously wrote files into the parent directory; it is now
refused with a clear error.

### Added

- **Generate commands no longer overwrite existing files.** Every command now checks its
targets before writing anything. If a file it would produce already exists, nothing is
written, the conflicting paths are listed, and the command exits with code 1. Pass
`--force` to replace them. Generation is all-or-nothing, so a conflict can no longer
leave a half-written feature behind. Applies to `new`, `add feature`,
`add value-object` and `add dto`, including the Clean Architecture path that renders
through a temporary directory.
- `mtc new <template> <name>` now accepts the project name as a positional argument.
`--name`/`-n` still works and takes precedence when both are given.
- Reference-typed properties are generated with the `required` modifier, so scaffolded
projects compile clean under `<Nullable>enable</Nullable>`.
- New field type aliases: `long`, `short`, `byte`, `float`, `double`, `char`,
`dateonly` and `timeonly`.
- `CONTRIBUTING.md` and this changelog.
- Thirteen new unit tests covering type mapping, malformed input and value-type
detection (7 tests to 20).

### Changed

- Target framework moved from `net9.0` to `net10.0`, in MTC itself, both test projects
and all seven generated project templates. .NET 9 has reached end of support.
- Templates render with controlled whitespace, so generated files are indented like
hand-written code instead of carrying stray blank lines and doubled indentation.
- Dependencies updated: Spectre.Console 0.54.0 to 0.57.2, `Microsoft.Extensions.*`
10.0.0 to 10.0.10.
- Generated projects now reference ASP.NET Core and EF Core 10.0.10. MediatR is
pinned at 12.4.1 across all templates, which previously disagreed with each other.
- Failure messages point at the command that helps: a missing template suggests
`mtc list`, undetected architecture suggests `mtc debug-context`.
- `add value-object` and `add dto` were near-identical copies and now share one
implementation.
- Integration tests locate the built binary by searching, instead of hardcoding a
target framework directory.

### Fixed

- `mtc new ConsoleApp MyApp` — the form documented in the README — failed with an
unrecognized argument error. The project name was only accepted via `--name`.
- The default project name resolved to a literal `"."` when neither `--name` nor an
output directory was given.
- Commands exited with code 0 after reporting an error, so scripts and CI could not
detect failures.
- `add feature`, `add value-object` and `add dto` shared single `Argument`/`Option`
instances, making parsing dependent on registration order.
- Debug output leaked into normal runs, from both `mtc new` and the scaffolding service.
- Generated entities, DTOs and value objects raised CS8618 nullability warnings.
- The release workflow only stripped a literal `V.` tag prefix, while the workflow
triggers on `v*` and `V*`. A `v1.1.0` tag produced a Debian version string starting
with a letter, which `dpkg-deb` rejects. All four tag spellings are now handled.

### Removed

- Two empty `UnitTest1.cs` placeholder files.

## [1.0.2] — 2025-12-07

### Added

- README is now included in the NuGet package.

### Changed

- Removed the PPA publishing steps from the release workflow. Debian users are served
by the `.deb` artifact instead.

## [1.0.1] — 2025-12-07

### Added

- MIT License file.
- Manual workflow dispatch trigger for releases, and support for uppercase version tags.
- AUR installation instructions and a complete CLI reference in the README.
- OS-specific instructions for installing the standalone binaries.

### Changed

- Publish trimming disabled, which was corrupting the shipped template directory.
- Template directory is copied cleanly on publish.
- Template service error handling refined.

### Fixed

- `ls` invocation in the publish script.

## [1.0.0] — 2025-12-06

First public release.

### Added

- `mtc new` with four project templates: `ConsoleApp`, `MvcMonolith`, `CleanArch` and
`VerticalSlice`.
- `mtc add feature`, which detects whether the current solution uses Clean Architecture,
MVC or Vertical Slice and writes each generated file into the matching layer.
- `mtc add value-object` and `mtc add dto`.
- `mtc list`, `mtc config` (`set`/`get`/`list`) and `mtc debug-context`.
- Field definitions via `--fields "Name:string Price:decimal"`.
- Distribution as a .NET global tool, an AUR package, a `.deb`, and standalone binaries
for Windows, Linux and macOS.

[1.1.0]: https://github.com/JuansesDev/MTC/compare/V.1.0.2...HEAD
[1.0.2]: https://github.com/JuansesDev/MTC/compare/V.1.0.1...V.1.0.2
[1.0.1]: https://github.com/JuansesDev/MTC/compare/V.1.0.0...V.1.0.1
[1.0.0]: https://github.com/JuansesDev/MTC/releases/tag/V.1.0.0
175 changes: 175 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Contributing to MTC

Thanks for taking the time. MTC is a small project, so the process is light — the only
hard rule is that `dotnet build` and `dotnet test` must be clean before a PR.

## Getting set up

You need the [.NET 10 SDK](https://dotnet.microsoft.com/download). Nothing else.

```bash
git clone https://github.com/JuansesDev/MTC.git
cd MTC
dotnet build
dotnet test
```

The build should finish with **0 warnings**. If you see warnings that were not there
before your change, they count as breakage.

### Running your build

`dotnet run` swallows CLI arguments in confusing ways, so drive the binary directly:

```bash
dotnet build
./MTC/bin/Debug/net10.0/MTC list
```

A good scratch loop is to generate into a throwaway directory and inspect the result:

```bash
mkdir -p /tmp/mtc-scratch && cd /tmp/mtc-scratch
/path/to/MTC/MTC/bin/Debug/net10.0/MTC new CleanArch MyShop
/path/to/MTC/MTC/bin/Debug/net10.0/MTC add feature Product --fields "Name:string Price:decimal"
dotnet build MyShop.sln # generated projects must compile clean too
```

## How the code is laid out

| Path | What lives there |
|---|---|
| `MTC/Program.cs` | CLI command definitions and handlers |
| `MTC/Services/` | The real logic — one interface + implementation per concern |
| `MTC/Models/` | Plain data types (`Template`, `Property`, `ProjectContext`) |
| `Templates/` | The templates themselves, copied to the build output |
| `MTC.Tests/` | Unit tests |
| `MTC.IntegrationTests/` | Tests that run the compiled binary end to end |

The services worth knowing:

- **`ContextService`** — walks up from the current directory to find a `.sln`, then decides
whether the solution is Clean Architecture, Vertical Slice or MVC.
- **`TemplateService`** — discovers templates by scanning recursively for `manifest.json`.
- **`ScribanTemplateRenderer`** — renders template content *and* file/directory names.
- **`ScaffoldingService`** — walks a template directory and writes the rendered result.
- **`FieldParser`** — turns `"Name:string Price:decimal"` into `Property` objects.

## Adding or editing a template

A template is just a directory with a `manifest.json` and the files to render. Both file
**contents** and file/directory **names** go through Scriban, which is why you see paths
like `Templates/Common/Dto/{{Name}}.cs`.

```json
{
"Name": "MyTemplate",
"Description": "What it generates",
"Version": "1.0.0",
"Author": "You",
"Tags": ["Tag"],
"Variables": { "Name": "string" }
}
```

`Name` is what users type (`mtc new MyTemplate ...`), so it must be unique.

### Variables available in templates

| Variable | Available in | Notes |
|---|---|---|
| `Name` | everywhere | the entity/project name the user typed |
| `ProjectName` | `add feature` | taken from the `.sln` file name |
| `Namespace` | `add value-object`, `add dto` | resolved from the detected architecture |
| `Properties` | `add *` | list of `{ Name, Type, IsValueType }` |

Member names are case-sensitive: write `{{ prop.Name }}`, not `{{ prop.name }}`.

### Two conventions that matter

**1. Control whitespace, or the output looks generated.** Use `{{~` and `~}}` around
loops so the rendered file is indented like hand-written code:

```
public class {{Name}}
{
{{~ for prop in Properties ~}}
public {{ prop.Type }} {{ prop.Name }} { get; set; }
{{~ end ~}}
}
```

**2. Emit `required` for reference types.** Generated projects have
`<Nullable>enable</Nullable>`, so a plain `public string Name { get; set; }` raises CS8618.
That is what `IsValueType` is for:

```
public {{ if !prop.IsValueType }}required {{ end }}{{ prop.Type }} {{ prop.Name }} { get; set; }
```

**Anything MTC generates must compile with zero warnings.** Always build the generated
solution before opening a PR, not just MTC itself.

## Tests

- Put unit tests in `MTC.Tests`, next to the service they cover.
- `MTC.IntegrationTests` runs the compiled binary, so it needs a build first. It locates
the executable by searching `MTC/bin/Debug/` — don't hardcode a framework folder.
- New behaviour needs a test. Bug fixes should come with a test that fails without the fix.

## Style

Follow the surrounding code; there is no separate style config.

- 4 spaces, file-scoped namespaces, nullable enabled.
- Services go behind an interface and get registered in `Program.cs`.
- Commands report failures with `AnsiConsole.MarkupLine("[red]Error:[/] ...")` **and** set
`Environment.ExitCode = 1`. Silent failures with exit code 0 break scripted use.
- Prefer a short explanatory comment over a long one. Don't commit commented-out code or
notes to yourself.

## Pull requests

1. Branch off `main`.
2. Keep the change focused — one concern per PR.
3. Make sure `dotnet build` and `dotnet test` are clean.
4. Add an entry under `## [Unreleased]` in [CHANGELOG.md](CHANGELOG.md). Describe the
effect on users, not the diff.
5. Say what you changed and why. If it changes CLI behaviour, update the README too.

CI runs build and tests on every push and PR to `main`.

## Releasing (maintainers)

Version numbers live in **two** places and must match:

- `MTC/MTC.csproj` → `<Version>`
- `publish.sh` → `VERSION`

Rename the `## [Unreleased]` heading in `CHANGELOG.md` to the version and date, then tag
and push:

```bash
git tag v1.1.0
git push origin v1.1.0
```

Tags may be written `v1.1.0`, `V1.1.0`, `V.1.1.0` or `1.1.0` — the workflow strips the
prefix either way. Releases up to 1.0.2 used the `V.` form.

The release workflow builds the cross-platform binaries and the `.deb`, publishes the
GitHub release, and pushes the package to NuGet. AUR is published separately with
`./publish_aur.sh`.

## Reporting bugs

Open an issue with the MTC version, your OS, the exact command, what you expected, and
what happened. For anything about architecture detection, include the output of:

```bash
mtc debug-context
```

## License

Contributions are accepted under the [MIT License](LICENSE).
27 changes: 22 additions & 5 deletions MTC.IntegrationTests/CliTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,30 @@ public CliTests()
throw new DirectoryNotFoundException("Could not find MTC.sln in parent directories.");
}

var solutionDir = currentDir.FullName;
_mtcPath = Path.Combine(solutionDir, "MTC/bin/Debug/net9.0/MTC");

if (!File.Exists(_mtcPath))
_mtcPath = ResolveMtcBinary(currentDir.FullName);
}

/// <summary>
/// Locates the built MTC executable without hardcoding a target framework,
/// so the tests keep working across .NET upgrades.
/// </summary>
private static string ResolveMtcBinary(string solutionDir)
{
var binDir = Path.Combine(solutionDir, "MTC", "bin", "Debug");
var executableName = OperatingSystem.IsWindows() ? "MTC.exe" : "MTC";

var candidates = Directory.Exists(binDir)
? Directory.GetFiles(binDir, executableName, SearchOption.AllDirectories)
: Array.Empty<string>();

if (candidates.Length == 0)
{
throw new FileNotFoundException($"MTC binary not found at {_mtcPath}. Please build the solution first.");
throw new FileNotFoundException(
$"MTC binary '{executableName}' not found under {binDir}. Build the solution first (dotnet build).");
}

// Most recent build wins, so the tests follow the framework currently targeted.
return candidates.OrderByDescending(File.GetLastWriteTimeUtc).First();
}

public void Dispose()
Expand Down
8 changes: 7 additions & 1 deletion MTC.IntegrationTests/MTC.IntegrationTests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
Expand All @@ -18,4 +18,10 @@
<Using Include="Xunit" />
</ItemGroup>

<!-- The tests drive the built executable rather than the library, but the reference
guarantees MTC is built before this project. -->
<ItemGroup>
<ProjectReference Include="..\MTC\MTC.csproj" />
</ItemGroup>

</Project>
Loading
Loading