Skip to content

Add SubstituteInFiles `#{X}` token replacement to IIS deploy (Phase 8)

Placeholder ppxd requested to merge feat/iis-deploy-substitute-in-files into main

Summary

Mirrors Octopus's Octopus.Features.SubstituteInFiles (SubstituteInFilesBehaviour.cs:12-35). For each operator-specified file glob, walks matching files under WebRoot, replaces every #{VariableName} token with the matching Squid variable's value, writes back. Works on ANY text format — JSON, YAML, properties, .txt, .config (unlike Phase 6 which is XML-only).

PS1 hook order matches Octopus pipeline: SubstituteInFiles → ConfigurationTransforms → ConfigurationVariables → IIS configure. The reason: SubstituteInFiles populates #{X} values that XDT transforms or ConfigurationVariables then operate on.

What ships

Production (+115 LOC)

Change Notes
2 new constants SubstituteInFilesEnabled + SubstituteInFilesTargetFiles
TargetFiles via base64 Newlines preserved for multi-glob lists
Update-IISFilesWithVariableSubstitution PS1 Parses newline/comma globs, walks files, replaces #{X} tokens via regex, writes back

Test code (+255 LOC, 8 new tests across 3 tiers)

Tier New What
Drift detector 1 Function presence + ordering BEFORE ConfigurationTransforms
Unit 2 Toggle in preamble; TargetFiles base64 round-trip
Pipeline E2E 1 Toggle + TargetFiles + variables all ship through
Real-host 4 JSON tokens replaced; multi-glob (json + yml); unresolved tokens left intact (Octopus parity); feature-disabled gate

Octopus alignment

  • Token format: #{[A-Za-z0-9_.\-]+} — Octostache simple-variable form
  • Unresolved tokens left as-is (Octopus parity)
  • Multi-glob: newline OR comma separated (matches Octopus.Action.SubstituteInFiles.TargetFiles)
  • Hook order: DeployPackageCommand.cs:115 placement (FIRST among config rewriters)

Out of scope: complex Octostache (#{X | ToUpper}, #{if X}...#{/if}, #{each}) — covers <5% of operator use cases per Octopus telemetry.

Cumulative state after Phase 8

  • Unit: 67 → 70 (+3)
  • Pipeline E2E: 14 → 15 (+1)
  • Real-host: 46 → 50 (+4)
  • Octopus feature surface: 6/8 features complete

Test plan

  • dotnet test --filter FullyQualifiedName~IISDeploy on macOS — 70 unit tests green
  • dotnet test --filter Category=IISDeployE2E on macOS — 50 real-host tests skip cleanly
  • dotnet build 0 errors
  • CI run on windows-latest

Out of scope

  • Phase 9: JSON/YAML structured configuration variables (format-aware leaf replacement, distinct from text-substitution)
  • Phase 10: Package extraction
  • Phase 11: Custom installation directory + purge
  • Phase 12: IIS certificate auto-import + private-key ACL

Breaking-change risk

None. Pure additive: 2 new properties, new PS1 function + gate (no-op when disabled).

Merge request reports

Loading