Skip to content
Open
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
16 changes: 11 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,16 @@ jobs:
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: '9.0.x'
dotnet-version: |
8.0.x
9.0.x
10.0.x

- name: Set version
id: get_version
shell: pwsh
run: |
$TAG = if ($env:GITHUB_REF -like "refs/tags/v*") {
$TAG = if ($env:GITHUB_REF -like "refs/tags/v*") {
$env:GITHUB_REF -replace 'refs/tags/v', ''
} else {
""
Expand Down Expand Up @@ -88,7 +91,7 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 10
timeout-minutes: 15

steps:
- name: Checkout repository
Expand All @@ -99,7 +102,10 @@ jobs:
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: '9.0.x'
dotnet-version: |
8.0.x
9.0.x
10.0.x

- name: Download NuGetPackage artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
Expand All @@ -120,4 +126,4 @@ jobs:
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: IntegrationTestResults-${{ matrix.os }}
path: test/IntegrationTests/out/
path: test/IntegrationTests/out/
5 changes: 2 additions & 3 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ jobs:
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: '9.0.x'
dotnet-quality: 'preview'
dotnet-version: '10.0.x'

- name: Setup Pages
uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
Expand Down Expand Up @@ -124,4 +123,4 @@ jobs:

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
25 changes: 16 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
**/bin/**
**/obj/**
**/out/
**/.vs/**
*.nupkg
output*.json
_site/
docs/
src/.manifest
**/bin/
**/obj/
**/out/
**/.vs/
.vscode/
*.nupkg
*.snupkg
output*.json
_site/
docs/
src/.manifest
test/packages/
examples/generated/
**/TestResults/
*.user
*.suo
267 changes: 267 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
# Migration Guide: Item-Based Configuration

This guide explains the new item-based configuration approach introduced to make `AggregateConfigBuildTask` work better with MSBuild's build process, similar to how `Azure.Bicep.MSBuild` operates.

## What's New?

### Item-Based Configuration (Recommended)

The package now supports defining configuration files as MSBuild items (`<AggregateConfigInput>`), which allows:

- **Automatic execution during build** - No need to create custom targets
- **Early build phase execution** - Runs by default before the `PrepareForBuild` target so generated files exist before resources are embedded, and the timing is customizable
- **Per-file configuration** - Different output settings per input file
- **Better MSBuild integration** - Proper incremental build support via `Inputs` and `Outputs`

### Backward Compatibility

**All existing projects continue to work without changes.** The legacy direct task invocation approach is fully supported.

## Why the Change?

Many MSBuild tasks need to run very early in the build process. This is especially true when generated files need to be embedded as resources or used as inputs to other build steps. The previous approach required users to:

1. Create custom targets
2. Carefully order them with `BeforeTargets`/`AfterTargets`
3. Manually handle incremental builds

The new item-based approach follows MSBuild best practices and allows the package to handle these concerns automatically. By default, it runs **before** the `Build` target, ensuring generated files are available for embedding as resources.

## How to Migrate

### Before (Legacy Approach)

```xml
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<PackageReference Include="AggregateConfigBuildTask" Version="*" />
</ItemGroup>

<Target Name="AggregateConfigs" BeforeTargets="PrepareForBuild">
<AggregateConfig
InputDirectory="Configs"
OutputFile="$(OutputPath)\output.json"
OutputType="Json"
AddSourceProperty="true" />
</Target>

</Project>
```

### After (Item-Based Approach)

```xml
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<PackageReference Include="AggregateConfigBuildTask" Version="*" />
</ItemGroup>

<ItemGroup>
<AggregateConfigInput Include="Configs\*.yml"
OutputFile="$(OutputPath)output.json"
OutputType="Json"
AddSourceProperty="true" />
</ItemGroup>

</Project>
```

### Benefits of Migrating

1. **Simpler project files** - No need to create targets
2. **Automatic incremental builds** - MSBuild tracks inputs/outputs
3. **Customizable execution timing** - Use properties to control when aggregation runs
4. **Multiple configurations easily** - Define multiple `<AggregateConfigInput>` items

## Running at Different Build Phases

### Default Behavior (Before PrepareForBuild)

By default, the target runs **before** the `PrepareForBuild` target, which is perfect for generating files that need to be embedded as resources:

```xml
<ItemGroup>
<AggregateConfigInput Include="configs\*.yml"
OutputFile="$(OutputPath)config.json"
OutputType="Json" />

<!-- The generated file is available for embedding -->
<EmbeddedResource Include="$(OutputPath)config.json" />
</ItemGroup>
```

### Run Later (Before CoreCompile)

If another target generates the input files after `PrepareForBuild`, move aggregation later so it still happens before compilation starts:

```xml
<PropertyGroup>
<AggregateConfigCompileBeforeTargets>CoreCompile</AggregateConfigCompileBeforeTargets>
</PropertyGroup>

<ItemGroup>
<AggregateConfigInput Include="configs\*.yml"
OutputFile="$(OutputPath)config.json"
OutputType="Json" />
</ItemGroup>
```

### Run After Build (Less Common)

If you want to run after the build completes:

```xml
<PropertyGroup>
<AggregateConfigCompileBeforeTargets></AggregateConfigCompileBeforeTargets>
<AggregateConfigCompileAfterTargets>Build</AggregateConfigCompileAfterTargets>
</PropertyGroup>
```

## Advanced Scenarios

### Multiple Outputs from Same Input

```xml
<ItemGroup>
<!-- Generate JSON -->
<AggregateConfigInput Include="configs\*.yml"
OutputFile="$(OutputPath)config.json"
OutputType="Json" />

<!-- Generate ARM parameters -->
<AggregateConfigInput Include="configs\*.yml"
OutputFile="$(OutputPath)config.parameters.json"
OutputType="Arm" />

<!-- Generate YAML -->
<AggregateConfigInput Include="configs\*.yml"
OutputFile="$(OutputPath)config.yaml"
OutputType="Yaml" />
</ItemGroup>
```

### Preserve Directory Structure

```xml
<ItemGroup>
<AggregateConfigInput Include="configs\**\*.yml"
OutputFile="$(OutputPath)%(RecursiveDir)%(FileName).json"
OutputType="Json" />
</ItemGroup>
```

This recursively finds all YAML files and preserves their directory structure in the output.

### Custom Output Path for All Items

```xml
<PropertyGroup>
<AggregateConfigOutputPath>$(MSBuildProjectDirectory)\generated\</AggregateConfigOutputPath>
</PropertyGroup>

<ItemGroup>
<!-- Will output to generated\config.json by default -->
<AggregateConfigInput Include="configs\*.yml"
OutputType="Json" />
</ItemGroup>
```

## Available Properties

| Property | Default | Description |
|----------|---------|-------------|
| `AggregateConfigCompileAfterTargets` | (empty) | Controls when the target runs (after which target) |
| `AggregateConfigCompileBeforeTargets` | `PrepareForBuild` | Controls when the target runs (before which target) |
| `AggregateConfigCompileDependsOn` | (empty) | Other targets that must run first |
| `AggregateConfigOutputPath` | `$(OutputPath)` | Default output directory for aggregated files |

## Item Metadata

Each `<AggregateConfigInput>` item supports the following metadata:

| Metadata | Required | Default | Description |
|----------|----------|---------|-------------|
| `OutputFile` | No | `$(AggregateConfigOutputPath)%(FileName).json` | Output file path |
| `OutputType` | No | `Json` | Output format (`Json`, `Yaml`, `Arm`, `Xml`) |
| `InputType` | No | `Yaml` | Input format (`Json`, `Yaml`, `Arm`, `Xml`) |
| `AddSourceProperty` | No | `false` | Add source filename to each object |

\* Can be omitted if `AggregateConfigOutputPath` provides a suitable default

## Compatibility Notes

### Both Approaches Work Together

You can use both the legacy and new approaches in the same project:

```xml
<!-- New item-based approach -->
<ItemGroup>
<AggregateConfigInput Include="configs\*.yml"
OutputFile="$(OutputPath)auto.json"
OutputType="Json" />
</ItemGroup>

<!-- Legacy direct invocation still works -->
<Target Name="CustomAggregation" BeforeTargets="PrepareForBuild">
<AggregateConfig
InputDirectory="legacy-configs"
OutputFile="$(OutputPath)legacy.json"
OutputType="Json" />
</Target>
```

### No Breaking Changes

All existing project files continue to work exactly as before. Migration is optional but recommended for new projects.

## Troubleshooting

### Aggregation Running at the Wrong Time

**Problem**: Generated files aren't available when needed.

**Solution**: Adjust `AggregateConfigCompileBeforeTargets` or `AggregateConfigCompileAfterTargets`:

```xml
<PropertyGroup>
<!-- Example: Run before CoreCompile -->
<AggregateConfigCompileAfterTargets></AggregateConfigCompileAfterTargets>
<AggregateConfigCompileBeforeTargets>CoreCompile</AggregateConfigCompileBeforeTargets>
</PropertyGroup>
```

### Multiple Executions

**Problem**: Task runs multiple times.

**Solution**: Ensure you're not mixing item-based and custom target approaches for the same files. The item-based approach handles batching automatically.

### Output Files Not Created

**Problem**: Expected output files don't exist.

**Solution**: Check that:
1. `OutputFile` metadata or property is set correctly
2. `OutputType` metadata or property is set
3. Input files exist and match the `Include` pattern
4. Check build output for error messages

## Getting Help

- **Documentation**: https://docs.richardson.dev/AggregateConfigBuildTask
- **Issues**: https://github.com/richardsondev/AggregateConfigBuildTask/issues
- **Examples**: See `examples/ItemBasedExample.csproj` in the repository

## Summary

The item-based approach provides:
- ✅ Simpler project configuration
- ✅ Better MSBuild integration
- ✅ Automatic incremental builds
- ✅ Flexible execution timing
- ✅ Full backward compatibility

Consider migrating to the new approach for new projects or when refactoring existing ones.
Loading
Loading