Skip to content

Commit

Permalink
Support continueOnStepFailure for stage
Browse files Browse the repository at this point in the history
  • Loading branch information
albertwoo committed Jan 31, 2024
1 parent 8541db3 commit 290c0ae
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 23 deletions.
7 changes: 4 additions & 3 deletions Fun.Build.Cli/Pipeline.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ type Pipeline with
static member Parse(str: string) =
let lines = str.Split(Environment.NewLine)

if lines.Length = 0 then []
if lines.Length = 0 then
[]

else if lines[0].StartsWith("Description:") then
let name = lines[1].Split(" ") |> Seq.map (fun x -> x.Trim()) |> Seq.filter (String.IsNullOrEmpty >> not) |> Seq.item 1
let name = lines[1].Split(" ") |> Seq.map (fun x -> x.Trim()) |> Seq.filter (String.IsNullOrEmpty >> not) |> Seq.item 1
let description = lines[2].Trim()
[ name, description ]

Expand Down Expand Up @@ -77,7 +78,7 @@ type Pipeline with
psInfo.FileName <- Diagnostics.Process.GetQualifiedFileName "dotnet"
psInfo.Arguments <- $"fsi \"{f}\" -- -h"

let! result =
let! result =
Async.StartChild(
Diagnostics.Process.StartAsync(psInfo, "", "", printOutput = false, captureOutput = true),
millisecondsTimeout = 60_000
Expand Down
127 changes: 127 additions & 0 deletions Fun.Build.Tests/SequenceTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,130 @@ let ``for loop or yield! should work`` () =
}

Assert.Equal<int>([ 1..6 ], list)



[<Fact>]
let ``continueOnStepFailure should work`` () =
let list = System.Collections.Generic.List()
pipeline "" {
stage "" {
continueOnStepFailure
run (fun _ -> list.Add(1))
run (fun _ ->
list.Add(2)
Error ""
)
run (fun _ -> list.Add(3))
}
stage "" { run (fun _ -> list.Add(4)) }
runImmediate
}
Assert.Equal<int>([ 1; 2; 3; 4 ], list)

shouldBeCalled (fun fn ->
pipeline "" {
stage "" {
paralle
continueOnStepFailure
run (fun _ -> Ok())
run (fun _ -> Error "")
run (fun _ -> Ok())
}
stage "" { run fn }
runImmediate
}
)

list.Clear()
Assert.Throws<PipelineFailedException>(fun _ ->
shouldNotBeCalled (fun fn ->
pipeline "" {
stage "" {
continueOnStepFailure false
run (fun _ -> list.Add(1))
run (fun _ ->
list.Add(2)
Error ""
)
run (fun _ -> Ok())
}
stage "" { run fn }
runImmediate
}
)
)
|> ignore
Assert.Equal<int>([ 1; 2 ], list)

Assert.Throws<PipelineFailedException>(fun _ ->
shouldNotBeCalled (fun fn ->
pipeline "" {
stage "" {
paralle
continueOnStepFailure false
run (fun _ -> Ok())
run (fun _ -> Error "")
run (fun _ -> Ok())
}
stage "" { run fn }
runImmediate
}
)
)
|> ignore

list.Clear()
Assert.Throws<PipelineFailedException>(fun _ ->
shouldNotBeCalled (fun fn ->
pipeline "" {
stage "" {
run (fun _ -> list.Add(1))
run (fun _ ->
list.Add(2)
Error ""
)
run (fun _ -> Ok())
}
stage "" { run fn }
runImmediate
}
)
)
|> ignore
Assert.Equal<int>([ 1; 2 ], list)

Assert.Throws<PipelineFailedException>(fun _ ->
shouldNotBeCalled (fun fn ->
pipeline "" {
stage "" {
paralle
run (fun _ -> Ok())
run (fun _ -> Error "")
run (fun _ -> Ok())
}
stage "" { run fn }
runImmediate
}
)
)
|> ignore

list.Clear()
pipeline "" {
stage "" {
continueOnStepFailure
run (fun _ -> list.Add(1))
stage "" {
run (fun _ ->
list.Add(2)
Error ""
)
run (fun _ -> list.Add(5))
}
run (fun _ -> list.Add(3))
}
stage "" { run (fun _ -> list.Add(4)) }
runImmediate
}
Assert.Equal<int>([ 1; 2; 3; 4 ], list)
4 changes: 4 additions & 0 deletions Fun.Build/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

## [1.1.0] - 2024-01-31

- Support continueOnStepFailure for stage

## [1.0.9] - 2024-01-22

- Add when' for ConditionsBuilder
Expand Down
6 changes: 2 additions & 4 deletions Fun.Build/ConditionsBuilder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@ module Internal =
type StageContext with

member ctx.When'(isTrue: bool) =
let getPrintInfo (prefix: string) =
makeCommandOption prefix (if isTrue then "always true" else "always false") ""
let getPrintInfo (prefix: string) = makeCommandOption prefix (if isTrue then "always true" else "always false") ""

match ctx.GetMode() with
| Mode.CommandHelp { Verbose = true } ->
AnsiConsole.WriteLine(getPrintInfo (ctx.BuildIndent()))
false
| Mode.CommandHelp _ ->
false
| Mode.CommandHelp _ -> false
| Mode.Verification ->
if isTrue then
AnsiConsole.MarkupLineInterpolated($"[green]✓ always true[/]")
Expand Down
9 changes: 9 additions & 0 deletions Fun.Build/StageBuilder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ type StageBuilder(name: string) =
member inline this.failIfNoActiveSubStage([<InlineIfLambda>] build: BuildStage) = this.failIfNoActiveSubStage (build, true)


/// Continue pipeline execution (consider this stage as success) even if the stage's step is failed, default is true
[<CustomOperation("continueOnStepFailure")>]
member inline _.continueOnStepFailure([<InlineIfLambda>] build: BuildStage, ?flag) =
BuildStage(fun ctx ->
let ctx = build.Invoke ctx
{ ctx with ContinueOnStepFailure = defaultArg flag true }
)


/// Set timeout for every step under the current stage.
/// Unit is second.
[<CustomOperation("timeout")>]
Expand Down
11 changes: 6 additions & 5 deletions Fun.Build/StageContextExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module StageContextExtensionsInternal =
Name = name
IsActive = fun _ -> true
IsParallel = fun _ -> false
ContinueOnStepFailure = false
Timeout = ValueNone
TimeoutForStep = ValueNone
WorkingDir = ValueNone
Expand Down Expand Up @@ -249,7 +250,7 @@ module StageContextExtensionsInternal =
)
if i = stage.Steps.Length - 1 then AnsiConsole.WriteLine()

if not isSuccess then stepErrorCTS.Cancel()
if not isSuccess && not stage.ContinueOnStepFailure then stepErrorCTS.Cancel()
return isSuccess

with
Expand All @@ -265,7 +266,7 @@ module StageContextExtensionsInternal =
AnsiConsole.MarkupLineInterpolated $"[red]{prefix} exception hanppened.[/]"
AnsiConsole.WriteException ex
stepExns.Add ex
stepErrorCTS.Cancel()
if not stage.ContinueOnStepFailure then stepErrorCTS.Cancel()
return false
})

Expand All @@ -280,7 +281,7 @@ module StageContextExtensionsInternal =
completers.Add completer

let mutable i = 0
while i < completers.Count && isSuccess do
while i < completers.Count && (stage.ContinueOnStepFailure || isSuccess) do
let! result = completers[i]
i <- i + 1
isSuccess <- isSuccess && result
Expand All @@ -289,7 +290,7 @@ module StageContextExtensionsInternal =
async {
let mutable i = 0
let length = Seq.length steps
while i < length && isSuccess do
while i < length && (stage.ContinueOnStepFailure || isSuccess) do
let! completer = Async.StartChild(Seq.item i steps, timeoutForStep)
let! result = completer
i <- i + 1
Expand Down Expand Up @@ -338,7 +339,7 @@ module StageContextExtensionsInternal =
finally
pipeline |> Option.iter (fun x -> x.RunAfterEachStage stage)

isSuccess, stepExns
stage.ContinueOnStepFailure || isSuccess, stepExns


let inline buildStageIsActive ([<InlineIfLambda>] build: BuildStage) ([<InlineIfLambda>] conditionFn) =
Expand Down
2 changes: 2 additions & 0 deletions Fun.Build/Types.Internal.fs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ type StageContext = {
Name: string
IsActive: StageContext -> bool
IsParallel: StageContext -> bool
/// When this is ture, the stage's final execution result will also be considered as true, so the pipeline can continue too.
ContinueOnStepFailure: bool
Timeout: TimeSpan voption
TimeoutForStep: TimeSpan voption
WorkingDir: string voption
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Every **step** is just a **async<Result<unit, string>>**, string is for the erro
## Minimal example and conventions

```fsharp
#r "nuget: Fun.Build, 1.0.9"
#r "nuget: Fun.Build, 1.1.0"
open Fun.Build
pipeline "demo" {
Expand Down Expand Up @@ -67,7 +67,7 @@ dotnet fsi build.fsx -- -p your_pipeline -h
Below example covered most of the apis and usage example, take it as the documents😊:

```fsharp
#r "nuget: Fun.Build, 1.0.9"
#r "nuget: Fun.Build, 1.1.0"
open Fun.Result
open Fun.Build
Expand Down Expand Up @@ -172,6 +172,7 @@ pipeline "Fun.Build" {
}
stage "FailIfIgnored" {
failIfIgnored // When set this, the stage cannot be ignored
continueOnStepFailure // When set this, the stage will be considered as success even if it's step is failed
whenCmdArg "arg2"
echo "Got here!"
}
Expand Down Expand Up @@ -259,6 +260,8 @@ pipeline "cmd-info" {
}
echo "here we are"
run "dotnet --list-sdks"
// You can get the cmd from the context
run (fun ctx -> printfn "%A" (ctx.GetCmdArg("--build")))
}
runIfOnlySpecified
}
Expand Down
12 changes: 3 additions & 9 deletions build.fsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#r "nuget: Fun.Build, 1.0.8"
#r "nuget: Fun.Build"

open System.IO
open Fun.Result
Expand All @@ -14,17 +14,11 @@ let options = {|
|}


let stage_checkEnv =
stage "Check environment" {
run "dotnet tool restore"
}
let stage_checkEnv = stage "Check environment" { run "dotnet tool restore" }

let stage_lint =
stage "Lint" {
stage "Format" {
whenGithubAction
run "dotnet fantomas . -r"
}
stage "Format" { run "dotnet fantomas . -r" }
stage "Check" {
whenGithubAction
run "dotnet fantomas . -r --check"
Expand Down

0 comments on commit 290c0ae

Please sign in to comment.