G1.2: ConfigurationTransformsStep — apply XDT to *.config files
Summary
Phase G1.2 of the Calamari Refactor Roadmap. Closes the IIS deploy handler's currently-degraded "Configuration transforms" toggle by giving Calamari a real XDT engine for the Bash deploy path.
What's new
Two components under Squid.Calamari.Commands.Configuration:
| Component | Lines | Tests | Purpose |
|---|---|---|---|
XdtTransformer |
~80 | 7 | Pure-function wrapper over Microsoft.Web.XmlTransform with typed TransformResult, atomic write, XML-decl preservation |
ConfigurationTransformsStep |
~140 | 13 | Pipeline step: auto-pair *.{Env}.config + *.Release.config, operator-supplied transform => base pairs, malformed-line tolerance |
Plus ConfigurationTransformsVariableNames public constants for cross-project wire-contract pinning.
Dependency add
-
Microsoft.Web.Xdt3.1.0 (~150 KB) — Microsoft's official XDT engine, used by .NET Frameworkweb.configtransform tooling. Per the Calamari Architecture Decision, heavy-lifting work like this belongs in Calamari (fork-isolated child process, has access to extracted files on the agent).
Pipeline order
ResolveWorkingDirectory
→ LoadVariablesFromFiles
→ SubstituteInFiles (G1.1 — #{Token} replacement; runs first so transform files have concrete values)
→ ConfigurationTransforms (G1.2 — XDT; runs on already-substituted files)
→ WriteBootstrappedBashScript
→ ExecuteScriptWithEngine
→ BuildRunScriptCommandResult
→ CleanupTemporaryFiles
Matches Octopus's Octopus.Features.ConfigurationTransforms ordering — SubstituteInFiles → ConfigurationTransforms is critical: if the order were reversed, transform files containing #{Token} references would be parsed by the XDT engine BEFORE the tokens were resolved, producing garbage transforms.
Auto-pairing semantic (Octopus parity)
For each *.config file in the working directory:
-
Always applied (if it exists):
*.Release.config— matches .NET FX build-time XDT default -
Conditionally applied:
*.{EnvironmentName}.config— driven bySquid.Action.IISWebSite.ConfigurationTransforms.EnvironmentNamevariable
A "base config" is any *.config whose stem has no . (e.g. web.config ✓, web.Production.config ✗). Heuristic avoids treating transforms as their own targets.
Operator-supplied pairs
ConfigurationTransforms.AdditionalTransforms (newline-separated):
custom-overrides.config => app.config
overrides/prod.config => web.config
Malformed line (no =>) → skip with stderr warning, continue with the rest. Don't fail the whole deploy on a typo.
Wire-contract pinning
Cross-project drift detector in Squid.UnitTests.IISConfigurationTransformsWireContractTests asserts all three variable literals match between server (IISDeployProperties) and agent (ConfigurationTransformsVariableNames). Same pattern as G1.1.
Test plan
-
7 unit tests on XdtTransformer— SetAttributes/Insert directives, missing files, malformed XML, identity transform, XML-declaration preservation -
13 unit tests on ConfigurationTransformsStep— enable-gate × 5, auto-pair × 3, operator-pair × 3, edge cases × 2 -
4 cross-project drift tests in Squid.UnitTestspinning server↔️ agent contract -
5611/5611 Squid.UnitTests green (+4) -
203/203 Squid.Calamari.Tests green (+24 net new)
What's NOT in this PR
- G1.3 (StructuredConfigVariablesStep) — JSON path edits on appsettings.json; next PR
- G1.4 (ExtractPackageStep) — package archive extraction; separate PR
- G1.5 (Convention hooks) — PreDeploy/Deploy/PostDeploy scripts; separate PR
Operator-visible improvement
Per the failed-deploy log the operator shared earlier:
ConfigurationTransforms: processing *.config under '.'
警告: ConfigurationTransforms: Microsoft.Web.XmlTransform not available on this agent.
For Bash deploys, the Calamari child now has the assembly bundled — transforms apply cleanly. For Windows IIS deploys (which use the PowerShell path per PR #353), the PS script still has its own inline XDT check; making that path also benefit from this is a future PR.
Refs
- Notion:
🦑 Squid — Calamari Refactor Roadmap (Post 1.8.1) — phase plan - PR #363 (G1.1) — the precedent for the wire-contract pinning + pipeline integration shape