From 4d186cccaed4a2ec26ddc700b083bad6b5f79595 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Jul 2026 00:42:05 +0000 Subject: [PATCH 1/2] Initial plan From d860d072e2a743c990b3d3f085a58661c4bc8787 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Jul 2026 00:44:41 +0000 Subject: [PATCH 2/2] fix: report missing build parameters --- src/Invex.Atom.Build/BuildResolver.cs | 11 +++++++--- .../ClassTests/Build/BuildResolverTests.cs | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Invex.Atom.Build/BuildResolver.cs b/src/Invex.Atom.Build/BuildResolver.cs index 65520744..ec8cce6c 100644 --- a/src/Invex.Atom.Build/BuildResolver.cs +++ b/src/Invex.Atom.Build/BuildResolver.cs @@ -99,7 +99,7 @@ public BuildModel Resolve() var usedParams = new List(); foreach (var param in x.Params) - AddParamAndChildren(param.Param, param.Required, usedParams, paramModels, []); + AddParamAndChildren(x.Name, param.Param, param.Required, usedParams, paramModels, []); return new TargetModel(x.Name, x.Description, x.Hidden) { @@ -261,12 +261,14 @@ void Visit(TargetModel target) /// /// Recursively adds a parameter and its chained dependencies to the list of used parameters for a target. /// + /// The name of the target that references the parameter. /// The name of the parameter to add. /// A value indicating whether the parameter is required. /// The list of used parameters to add to. /// A dictionary of all available parameter models. /// A set of already-visited parameter names to detect and prevent circular chains. private static void AddParamAndChildren( + string target, string param, bool required, List usedParams, @@ -276,10 +278,13 @@ private static void AddParamAndChildren( if (!visited.Add(param)) return; - var model = paramModels[param]; + if (!paramModels.TryGetValue(param, out var model)) + throw new BuildConfigurationException( + $"Target '{target}' references parameter '{param}' which is not defined in the build."); + usedParams.Add(new(model, required)); foreach (var chainedParam in model.ChainedParams) - AddParamAndChildren(chainedParam, required, usedParams, paramModels, visited); + AddParamAndChildren(target, chainedParam, required, usedParams, paramModels, visited); } } diff --git a/tests/Invex.Atom.Build.Tests/ClassTests/Build/BuildResolverTests.cs b/tests/Invex.Atom.Build.Tests/ClassTests/Build/BuildResolverTests.cs index f362857a..94f97758 100644 --- a/tests/Invex.Atom.Build.Tests/ClassTests/Build/BuildResolverTests.cs +++ b/tests/Invex.Atom.Build.Tests/ClassTests/Build/BuildResolverTests.cs @@ -240,6 +240,28 @@ public void Resolve_WithMissingDependency_ThrowsException() ex.Message.ShouldContain("depends on target 'Target2' which does not exist"); } + [Test] + public void Resolve_WithMissingParam_ThrowsException() + { + // Arrange + var buildDefinition = new TestBuildDefinition(_services) + { + ManualTargetDefinitions = new Dictionary + { + ["Target1"] = t => t.RequiresParam("MissingParam"), + }, + }; + + var commandLineArgs = new CommandLineArgs(true, [new CommandArg("Target1")]); + var paramService = A.Fake(); + var logger = A.Fake>(); + var buildResolver = new BuildResolver(buildDefinition, paramService, commandLineArgs, logger); + + // Act & Assert + var ex = Should.Throw(buildResolver.Resolve); + ex.Message.ShouldBe("Target 'Target1' references parameter 'MissingParam' which is not defined in the build."); + } + [Test] public void Resolve_WithComplexCircularDependency_ThrowsException() {