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

Add when' overload to accept a stage and treat stage result as a when condition for the enclosing context #76

Merged
merged 5 commits into from
Sep 11, 2024
Merged
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
90 changes: 89 additions & 1 deletion Fun.Build.Tests/ConditionsBuilderTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,94 @@ let ``whenEnvVar should work`` () =
)


[<Fact>]
let ``when' stage should use stage execution result as when' condition for stage`` () =
shouldNotBeCalled (fun call ->
pipeline "" {
stage "" {
when' (stage "" {run (fun ctx -> 1)})
run call
}
runImmediate
}
)

shouldBeCalled (fun call ->
pipeline "" {
stage "" {
when' (stage "" {run (fun ctx -> 0)})
run call
}
runImmediate
}
)

[<Fact>]
let ``when' stage should have parent context in execution mode`` () =
shouldBeCalled (fun call ->
pipeline "" {
envVars [ "ENV", "0" ]
stage "" {
when' (stage "" {run (fun ctx -> ctx.GetEnvVar("ENV") |> int)})
run call
}
runImmediate
}
)

[<Fact>]
let ``when' stage should use stage execution result as when' condition for nested stage`` () =
shouldNotBeCalled (fun call ->
pipeline "" {
stage "" {
stage "nested" {
when' (stage "" {run (fun ctx -> 1)})
run call
}
}
runImmediate
}
)

shouldBeCalled (fun call ->
pipeline "" {
stage "" {
stage "nested" {
when' (stage "" {run (fun ctx -> 0)})
run call
}
}
runImmediate
}
)

[<Fact>]
let ``when' stage should use stage execution result as when' condition in composed when`` () =
shouldNotBeCalled (fun call ->
pipeline "" {
stage "" {
whenAll {
when' (stage "" {run (fun ctx -> 1)})
}
run call
}
runImmediate
}
)

shouldBeCalled (fun call ->
pipeline "" {
stage "" {
whenAll {
when' (stage "" {run (fun ctx -> 0)})
}
run call
}
runImmediate
}
)


[<Fact>]
let ``whenAny should work`` () =
let condition = whenAny {
Expand Down Expand Up @@ -541,4 +629,4 @@ let ``for top level condition of stage or pipeline it should combine all conditi

runImmediate
}
)
)
30 changes: 30 additions & 0 deletions Fun.Build.Tests/PipelineBuilderTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,36 @@ let ``Verification should work`` () =
}
)

[<Fact>]
let ``when' stage should use stage execution result as verification condition for pipeline`` () =
let mutable numChecks = 0
Assert.Throws<PipelineFailedException>(fun _ ->
shouldNotBeCalled (fun call ->
pipeline "" {
when' (stage "whenStageShouldFail" {run (fun ctx ->
numChecks <- numChecks + 1
1)})
stage "" {
run call
}
runImmediate
}
)
)
|> ignore
Assert.Equal(1, numChecks)

shouldBeCalled (fun call ->
pipeline "" {
when' (stage "whenStageShouldPass" {run (fun ctx -> 0)})
stage "" {
run call
}
runImmediate
}
)



[<Fact>]
let ``Should fail if stage is ignored`` () =
Expand Down
22 changes: 22 additions & 0 deletions Fun.Build/ConditionsBuilder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ module Internal =
false
| Mode.Execution -> isTrue

member ctx.WhenStage(stage: StageContext) =
match ctx.GetMode() with
| Mode.Execution ->
let result, exns =
{ stage with ParentContext = ctx.ParentContext }
.Run(StageIndex.Condition, System.Threading.CancellationToken.None)
result
| Mode.Verification ->
AnsiConsole.MarkupLineInterpolated($"[yellow]? [/]{ctx.BuildIndent().Substring(2)}check results of stage [yellow]{stage.Name}[/]")
false
| _ ->
false

member ctx.WhenEnvArg(info: EnvArg) =
if info.Name |> String.IsNullOrEmpty then
failwith "ENV variable name cannot be empty"
Expand Down Expand Up @@ -212,6 +225,9 @@ type ConditionsBuilder() =
[<CustomOperation("when'")>]
member inline _.when'([<InlineIfLambda>] builder: BuildConditions, arg: bool) = buildConditions builder (fun ctx -> ctx.When'(arg))

[<CustomOperation("when'")>]
member inline _.when'([<InlineIfLambda>] builder: BuildConditions, whenStage: StageContext) = buildConditions builder (fun ctx -> ctx.WhenStage whenStage)


[<CustomOperation("envVar")>]
member inline _.envVar([<InlineIfLambda>] builder: BuildConditions, arg: EnvArg) = buildConditions builder (fun ctx -> ctx.WhenEnvArg(arg))
Expand Down Expand Up @@ -287,6 +303,9 @@ type StageBuilder with
[<CustomOperation("when'")>]
member inline _.when'([<InlineIfLambda>] build: BuildStage, value: bool) = buildStageIsActive build (fun ctx -> ctx.When' value)

// Set if stage is active or should run depending on the results of the whenStage
[<CustomOperation("when'")>]
member inline _.when'([<InlineIfLambda>] build: BuildStage, whenStage: StageContext) = buildStageIsActive build (fun ctx -> ctx.WhenStage whenStage)

/// Set if stage is active or should run by check the environment variable.
[<CustomOperation("whenEnvVar")>]
Expand Down Expand Up @@ -368,6 +387,9 @@ type PipelineBuilder with
[<CustomOperation("when'")>]
member inline _.when'([<InlineIfLambda>] build: BuildPipeline, value: bool) = buildPipelineVerification build (fun ctx -> ctx.When' value)

// Set if pipeline can run depending on the results of the whenStage
[<CustomOperation("when'")>]
member inline _.when'([<InlineIfLambda>] build: BuildPipeline, whenStage: StageContext) = buildPipelineVerification build (fun ctx -> ctx.WhenStage whenStage)

/// Set if pipeline can run by check the environment variable.
[<CustomOperation("whenEnvVar")>]
Expand Down
9 changes: 9 additions & 0 deletions Fun.Build/StageContextExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ module StageContextExtensionsInternal =

let extraInfo = $"timeout: {timeoutForStage}ms. step timeout: {timeoutForStep}ms."
match index with
| StageIndex.Condition ->
AnsiConsole.Write(Rule($"[grey50]CONDITION STAGE [bold turquoise4]{namePath}[/] started. {extraInfo}[/]").LeftJustified())
| StageIndex.Stage i ->
AnsiConsole.Write(Rule($"[grey50]STAGE #{i} [bold turquoise4]{namePath}[/] started. {extraInfo}[/]").LeftJustified())
| StageIndex.Step _ ->
Expand All @@ -230,6 +232,7 @@ module StageContextExtensionsInternal =
ParentContext = ValueSome(StageParent.Stage stage)
}
subStage.BuildCurrentStepPrefix() + ">"


let exns = ResizeArray<Exception>()
try
Expand Down Expand Up @@ -343,6 +346,11 @@ module StageContextExtensionsInternal =

let color = if isSuccess then "turquoise4" else "red"
match index with
| StageIndex.Condition ->
AnsiConsole.Write(
Rule($"""[grey50]CONDITION STAGE [bold {color}]{namePath}[/] finished. {stageSW.ElapsedMilliseconds}ms.[/]""")
.LeftJustified()
)
| StageIndex.Stage i ->
AnsiConsole.Write(
Rule($"""[grey50]STAGE #{i} [bold {color}]{namePath}[/] finished. {stageSW.ElapsedMilliseconds}ms.[/]""")
Expand All @@ -359,6 +367,7 @@ module StageContextExtensionsInternal =
AnsiConsole.WriteLine()

match index with
| StageIndex.Condition -> AnsiConsole.Write(Rule($"[grey50]CONDITION STAGE {namePath} is [yellow]inactive[/][/]").LeftJustified())
| StageIndex.Stage i -> AnsiConsole.Write(Rule($"[grey50]STAGE #{i} {namePath} is [yellow]inactive[/][/]").LeftJustified())
| StageIndex.Step _ ->
AnsiConsole.MarkupLineInterpolated($"[grey50]{stage.BuildCurrentStepPrefix()}> sub-stage is [yellow]inactive[/][/]")
Expand Down
1 change: 1 addition & 0 deletions Fun.Build/Types.Internal.fs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type StageParent =
type StageIndex =
| Step of step: int
| Stage of stage: int
| Condition


type CommandHelpContext = {
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ pipeline "Fun.Build" {
cmdArg "cmdKey" "" "Check has cmd arg"
cmdArg "cmdKey" "cmdValue" "Check has cmd arg value which should be behind the cmdKey"
whenNot { cmdArg "--not-demo" }
when' (stage "Check" {run (fun ctx -> Ok())}) // Check result of a stage, useful for dynamic checks
}
shuffleExecuteSequence // It can shuffle the sequence of steps executing sequence
run "dotnet --version"
Expand Down
1 change: 1 addition & 0 deletions demo.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ pipeline "Fun.Build" {
cmdArg "cmdKey" "" "Check has cmd arg"
cmdArg "cmdKey" "cmdValue" "Check has cmd arg value which should be behind the cmdKey"
whenNot { cmdArg "--not-demo" }
when' (stage "Check" {run (fun ctx -> Ok())}) // Check result of a stage, useful for dynamic checks
}
shuffleExecuteSequence // It can shuffle the sequence of steps executing sequence
run "dotnet --version"
Expand Down
Loading