Skip to content

Add DeployFailed convention hook (cleanup-phase failure handler)

Placeholder ppxd requested to merge feat/calamari-deploy-failed-convention into main

Summary

Closes the deferred convention from G1.5 (#370). Operator drops DeployFailed.sh in the package; Calamari runs it ONLY when the deploy actually failed.

What's in the box

Layer File
New opt-in interface src/Squid.Calamari/Pipeline/IFailureAwareExecutionContext.cs
Pipeline sets the flag src/Squid.Calamari/Pipeline/ExecutionPipeline.cs (catch block)
Context implements interface src/Squid.Calamari/Commands/RunScriptCommandPipeline.cs
Step src/Squid.Calamari/Commands/Conventions/DeployFailedConventionStep.cs (new)
Wire literal ConventionScriptNames.DeployFailed = "DeployFailed"
Pipeline wiring RunScriptCommand.cs (cleanup phase, before CleanupTemporaryFiles)
Tests FailureAwarePipelineTests.cs (4) + DeployFailedConventionStepTests.cs (13)

Predicate truth table

Script exists? ExecutionFailed flag Main exit code Fires?
No — — No
Yes false 0 / null No (pinned)
Yes true — Yes
Yes false non-zero Yes

Key design choices

  • No re-throw on hook failure — if DeployFailed.sh itself fails, log structured warning + swallow. Original execution failure stays the canonical cause for forensics. Matches Octopus.
  • Output vars merge into context.Variables — symmetric with PreDeploy/PostDeploy/main contract.
  • Cleanup-phase order — runs BEFORE CleanupTemporaryFilesStep, so the script can read the extracted+bootstrapped temp files.
  • Opt-in interface pattern — non-implementing contexts unaffected. Same lightweight extension as IPathBasedExecutionContext.

Test plan

  • Squid.Calamari.Tests: 335/335 (was 318; +17 new)
  • Squid.UnitTests: 5625/5625 (unchanged)
  • Solution build: 0 errors
  • Wire literal pinned: ConventionScriptNames.DeployFailed = "DeployFailed"
  • Pre-merge audit GREEN on 6/6 invariants
  • Staging: real .nupkg with DeployFailed.sh + intentionally-failing main script

Non-breaking guarantee

Surface Status
Existing wire literals Unchanged
Standalone-script deploys (no DeployFailed.sh) Zero impact — IsEnabled returns false
Successful deploys with DeployFailed.sh shipped Zero impact — IsEnabled returns false (CRITICAL — pinned by IsEnabled_ScriptPresent_NoFailure_ReturnsFalse)
ExecutionPipeline contract with non-FailureAware contexts Unchanged (pinned by ContextWithoutFailureAware_NotAffected_BackCompat)
SquidWeb Untouched — file-based convention, no UI wire literal

Merge request reports

Loading