Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] file-based configuration format #357

Draft
wants to merge 2 commits into
base: dev
Choose a base branch
from
Draft
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
27 changes: 27 additions & 0 deletions .incrementalist/incrementalist.graphcache.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"Version": "1.0-beta2",
"SolutionPath": "Incrementalist.sln",
"Checksum": "ZKoqWB5rj\u002BRW\u002BmDmmtZBt1p9A1bAn9\u002Bp1wA5CYnWfrQ=",
"Projects": {
"716852be-3403-4f18-867b-04f0b648470f": {
"Id": "716852be-3403-4f18-867b-04f0b648470f",
"Path": "src\\Incrementalist.Tests\\Incrementalist.Tests.csproj",
"Dependencies": [
"60c7319a-07e5-4267-8665-8286637a7466",
"70b66be5-e540-42bc-854a-3129187abf19"
]
},
"60c7319a-07e5-4267-8665-8286637a7466": {
"Id": "60c7319a-07e5-4267-8665-8286637a7466",
"Path": "src\\Incrementalist\\Incrementalist.csproj",
"Dependencies": []
},
"70b66be5-e540-42bc-854a-3129187abf19": {
"Id": "70b66be5-e540-42bc-854a-3129187abf19",
"Path": "src\\Incrementalist.Cmd\\Incrementalist.Cmd.csproj",
"Dependencies": [
"60c7319a-07e5-4267-8665-8286637a7466"
]
}
}
}
43 changes: 36 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,31 @@ Note the command structure when using as a local tool:
- First `--` after `dotnet tool run incrementalist` is for Incrementalist options
- Second `--` (if using `-r`) is for the dotnet command to run on affected projects

## 📄 Configuration Files

Incrementalist supports JSON configuration files to store commonly used settings. This eliminates the need to specify the same command-line arguments repeatedly.

```shell
# Use default configuration file (incrementalist.json)
incrementalist -r -- build

# Specify a custom configuration file
incrementalist -c my-config.json -r -- build
```

Create a configuration file in your repository:

```json
{
"gitBranch": "master",
"solutionFilePath": "src/MySolution.sln",
"verbose": true,
"runInParallel": true
}
```

Command-line arguments take precedence over configuration file settings. See [Configuration Documentation](docs/config.md) for complete details.

## 🚀 Quick Start Examples

```shell
Expand Down Expand Up @@ -128,7 +153,7 @@ These files can be used in build scripts, CI/CD pipelines, or other automation t

-l, --folders-only Optional. List affected folders instead of projects.

-b, --branch Required. (Default: dev) Git branch to compare against
-b, --branch Optional. (Default: dev) Git branch to compare against
(e.g., 'dev' or 'master').

-d, --dir Optional. Working directory. Defaults to current directory.
Expand All @@ -137,18 +162,21 @@ These files can be used in build scripts, CI/CD pipelines, or other automation t

-t, --timeout Optional. (Default: 2) Solution load timeout in minutes.

-r, --run Optional. Run dotnet CLI command against affected projects.
All arguments after -- are passed to dotnet.
-r, --run Optional. Run dotnet CLI command against affected projects.
All arguments after -- are passed to dotnet.

--continue-on-error Optional. (Default: true) Continue executing commands even
if some fail.
if some fail.

--parallel Optional. (Default: false) Execute commands in parallel.
--parallel Optional. (Default: false) Execute commands in parallel.

--fail-on-no-projects Optional. (Default: false) Fail if no projects are affected.

--no-cache Optional. (Default: false) Ignore any existing cache file
and perform a full Roslyn analysis.
--no-cache Optional. (Default: false) Ignore any existing cache file
and perform a full Roslyn analysis.

-c, --config Optional. Path to the configuration file. Defaults to
incrementalist.json in the current directory.

--help Display help screen.

Expand Down Expand Up @@ -178,6 +206,7 @@ incrementalist -b dev -r --continue-on-error=false -- build -c Release --nologo
- 🔍 [How It Works](docs/how-it-works.md) - Technical details and architecture
- 🏗️ [Building from Source](docs/building.md) - Build instructions and development setup
- ⚡ [Dependency Graph Caching](docs/caching.md) - Cache system explanation and best practices
- ⚙️ [Configuration Files](docs/config.md) - Using JSON configuration files

## 📜 License

Expand Down
83 changes: 83 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Incrementalist Configuration Files

Incrementalist now supports configuration files to store commonly used settings. This eliminates the need to specify the same command-line arguments repeatedly.

## Configuration File Format

Incrementalist uses a JSON-based configuration file format. By default, Incrementalist looks for a file named `incrementalist.json` in the current directory, but you can specify a different file using the `-c` or `--config` command-line option.

## Available Settings

The following settings can be specified in the configuration file:

| Setting | Type | Description | CLI Equivalent |
|---------|------|-------------|----------------|
| `gitBranch` | string | The branch to compare against (e.g. "dev", "master") | `-b`, `--branch` |
| `solutionFilePath` | string | Path to the solution file to analyze | `-s`, `--sln` |
| `outputFile` | string | Path where affected projects will be written | `-f`, `--file` |
| `listFolders` | boolean | List affected folders instead of projects | `-l`, `--folders-only` |
| `workingDirectory` | string | Working directory for the analysis | `-d`, `--dir` |
| `verbose` | boolean | Enable verbose logging | `--verbose` |
| `timeoutMinutes` | number | Timeout for solution loading in minutes | `-t`, `--timeout` |
| `continueOnError` | boolean | Continue when command execution fails | `--continue-on-error` |
| `runInParallel` | boolean | Run commands in parallel | `--parallel` |
| `failOnNoProjects` | boolean | Fail if no projects are affected | `--fail-on-no-projects` |
| `noCache` | boolean | Ignore existing cache file | `--no-cache` |

## Sample Configuration File

Here's an example configuration file with all available settings:

```json
{
"gitBranch": "master",
"solutionFilePath": "MySolution.sln",
"outputFile": "affected-projects.txt",
"listFolders": false,
"workingDirectory": null,
"verbose": false,
"timeoutMinutes": 2,
"continueOnError": true,
"runInParallel": false,
"failOnNoProjects": false,
"noCache": false
}
```

## Command-Line Override

Command-line arguments take precedence over configuration file settings. For example, if your configuration file specifies `"gitBranch": "master"` but you run `incrementalist --branch dev`, the `dev` branch will be used.

## Usage Examples

### Basic Usage

1. Create an `incrementalist.json` file in your project root:

```json
{
"gitBranch": "master",
"solutionFilePath": "MySolution.sln",
"verbose": true
}
```

2. Run Incrementalist without specifying these options on the command line:

```bash
dotnet run -- --run -- build
```

### Using a Different Configuration File

```bash
dotnet run -- --config my-custom-config.json --run -- build
```

### Overriding Configuration Values

```bash
dotnet run -- --branch dev --verbose false --run -- build
```

This will use the `dev` branch and disable verbose logging, overriding any values in the configuration file.
11 changes: 11 additions & 0 deletions incrementalist.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"solutionFilePath": "Incrementalist.sln",
"listFolders": false,
"gitBranch": "master",
"verbose": true,
"timeoutMinutes": 2,
"continueOnError": true,
"runInParallel": false,
"failOnNoProjects": false,
"noCache": false
}
13 changes: 13 additions & 0 deletions incrementalist.sample.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"gitBranch": "master",
"solutionFilePath": "MySolution.sln",
"outputFile": "affected-projects.txt",
"listFolders": false,
"workingDirectory": null,
"verbose": false,
"timeoutMinutes": 2,
"continueOnError": true,
"runInParallel": false,
"failOnNoProjects": false,
"noCache": false
}
95 changes: 95 additions & 0 deletions src/Incrementalist.Cmd/Commands/CreateConfigFileTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// -----------------------------------------------------------------------
// <copyright file="CreateConfigFileTask.cs" company="Petabridge, LLC">
// Copyright (C) 2015 - 2024 Petabridge, LLC <https://petabridge.com>
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Incrementalist.Caching;
using Incrementalist.Cmd.Config;
using Microsoft.Extensions.Logging;

namespace Incrementalist.Cmd.Commands
{
/// <summary>
/// Command to create a configuration file from the current options
/// </summary>
public sealed class CreateConfigFileTask
{
private readonly SlnOptions _options;
private readonly ILogger _logger;

public CreateConfigFileTask(SlnOptions options, ILogger logger)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}

/// <summary>
/// Creates a configuration file from the current options
/// </summary>
/// <returns>A task representing the asynchronous operation. Returns 0 on success, non-zero on failure.</returns>
public async Task<int> Run()
{
try
{
// Create the config object from the current options
var config = new IncrementalistConfig
{
GitBranch = _options.GitBranch,
SolutionFilePath = _options.SolutionFilePath,
OutputFile = _options.OutputFile,
ListFolders = _options.ListFolders,
WorkingDirectory = _options.WorkingDirectory,
Verbose = _options.Verbose,
TimeoutMinutes = _options.TimeoutMinutes,
ContinueOnError = _options.ContinueOnError,
RunInParallel = _options.RunInParallel,
FailOnNoProjects = _options.FailOnNoProjects,
NoCache = _options.NoCache
};

// Determine the output file path
string configFilePath = _options.ConfigFile;
if (string.IsNullOrEmpty(configFilePath))
{
// Use default filename in the same directory as the dependency cache
var workingDir = _options.WorkingDirectory ?? Directory.GetCurrentDirectory();
var incrementalistDir = Path.Combine(workingDir, IncrementalistFileConstants.IncrementalistDirectory);

// Create the directory if it doesn't exist
if (!Directory.Exists(incrementalistDir))
{
Directory.CreateDirectory(incrementalistDir);
}

configFilePath = Path.Combine(incrementalistDir, IncrementalistConfig.DefaultConfigFileName);
}

// Create the serialization options
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

// Serialize and write the config to file
string json = JsonSerializer.Serialize(config, options);
await File.WriteAllTextAsync(configFilePath, json);

_logger.LogInformation("Configuration file created: {FilePath}", configFilePath);
return 0;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating configuration file");
return -1;
}
}
}
}
Loading