From cccbb74c157e34c9732013e398f726d6dff7d298 Mon Sep 17 00:00:00 2001 From: Shubh Bapna <38372682+shubhbapna@users.noreply.github.com> Date: Tue, 30 May 2023 08:34:35 -0400 Subject: [PATCH] implement fail at end (#438) * implement fail at end * bump version * updated readme --- README.md | 4 +++ package-lock.json | 4 +-- package.json | 2 +- src/domain/inputs.ts | 1 + .../cli/build/build-subcommand-factory.ts | 1 + .../command/execute-command-service.ts | 13 +++++++ src/service/config/configuration-service.ts | 4 +++ .../command/execute-command-service.test.ts | 34 +++++++++++++++++++ 8 files changed, 60 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 56e6101e..30561798 100644 --- a/README.md +++ b/README.md @@ -613,6 +613,7 @@ Options: to replace with the ReplacementEx --skipProjectCheckout A list of projects to skip checkout. --skipCheckout skip checkout for all projects. Overrides skipProjectCheckout (default: false) + -fae, --fail-at-end Only fail the build afterwards; allow all non-impacted builds to continue (default: false) -h, --help display help for command ``` @@ -649,6 +650,7 @@ Options: to replace with the ReplacementEx --skipProjectCheckout A list of projects to skip checkout. --skipCheckout skip checkout for all projects. Overrides skipProjectCheckout (default: false) + -fae, --fail-at-end Only fail the build afterwards; allow all non-impacted builds to continue (default: false) -h, --help display help for command ``` @@ -685,6 +687,7 @@ Options: to replace with the ReplacementEx --skipProjectCheckout A list of projects to skip checkout. --skipCheckout skip checkout for all projects. Overrides skipProjectCheckout (default: false) + -fae, --fail-at-end Only fail the build afterwards; allow all non-impacted builds to continue (default: false) -h, --help display help for command ``` @@ -725,6 +728,7 @@ Options: to replace with the ReplacementEx --skipProjectCheckout A list of projects to skip checkout. --skipCheckout skip checkout for all projects. Overrides skipProjectCheckout (default: false) + -fae, --fail-at-end Only fail the build afterwards; allow all non-impacted builds to continue (default: false) -h, --help display help for command ``` diff --git a/package-lock.json b/package-lock.json index 32d58333..ad7cdb91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@kie/build-chain-action", - "version": "3.1.0", + "version": "3.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@kie/build-chain-action", - "version": "3.1.0", + "version": "3.1.1", "license": "ISC", "dependencies": { "@actions/artifact": "^1.1.0", diff --git a/package.json b/package.json index f87b9e3d..e11eb217 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kie/build-chain-action", - "version": "3.1.0", + "version": "3.1.1", "description": "Library to execute commands based on github projects dependencies.", "main": "dist/index.js", "author": "", diff --git a/src/domain/inputs.ts b/src/domain/inputs.ts index 89cb4edc..7d07adb2 100644 --- a/src/domain/inputs.ts +++ b/src/domain/inputs.ts @@ -41,6 +41,7 @@ export interface InputValues extends OptionValues { url?: string; branch?: string; group?: string; + failAtEnd?: boolean; } /** diff --git a/src/service/arguments/cli/build/build-subcommand-factory.ts b/src/service/arguments/cli/build/build-subcommand-factory.ts index c6721a68..d5491e04 100644 --- a/src/service/arguments/cli/build/build-subcommand-factory.ts +++ b/src/service/arguments/cli/build/build-subcommand-factory.ts @@ -51,6 +51,7 @@ export class BuildSubCommandFactory { .option("-t, --customCommandTreatment ", "Each exp must be of the form . Regex defines the regular expression for what you want to replace with the ReplacementEx") .option("--skipProjectCheckout ", "A list of projects to skip checkout.") .option("--skipCheckout", "skip checkout for all projects. Overrides skipProjectCheckout", false) + .option("-fae, --fail-at-end", "Only fail the build afterwards; allow all non-impacted builds to continue", false) .action((options) => { const parsedInputs = Container.get(InputService); if (options.debug) options.loggerLevel = LoggerLevel.DEBUG; diff --git a/src/service/command/execute-command-service.ts b/src/service/command/execute-command-service.ts index 8512054e..31a7543c 100644 --- a/src/service/command/execute-command-service.ts +++ b/src/service/command/execute-command-service.ts @@ -80,6 +80,10 @@ export class ExecuteCommandService { return result; } + private nodeExecutionFailed(result: ExecuteNodeResult[]): boolean { + return !!result.find(res => res.executeCommandResults.find(r => r.result === ExecutionResult.NOT_OK)); + } + private async executeNodeChainSequential(chain: NodeExecution[], printResults?: (node: ExecuteNodeResult[]) => void) { const result: ExecuteNodeResult[][] = []; for (const node of chain) { @@ -93,6 +97,11 @@ export class ExecuteCommandService { } this.logger.endGroup(); + + if (!this._configurationService.failAtEnd() && this.nodeExecutionFailed(currentNodeResult)) { + this.logger.info(`${node.node.project} failed. Won't execute remaining projects`); + return result; + } } return result; } @@ -158,6 +167,10 @@ export class ExecuteCommandService { printResults(currentResults[project]); } this.logger.endGroup(); + if (!this._configurationService.failAtEnd() && this.nodeExecutionFailed(currentResults[project])) { + this.logger.info(`${project} failed. Won't execute remaining projects`); + return result; + } } } return result; diff --git a/src/service/config/configuration-service.ts b/src/service/config/configuration-service.ts index aec1984e..bb1e7f84 100644 --- a/src/service/config/configuration-service.ts +++ b/src/service/config/configuration-service.ts @@ -259,4 +259,8 @@ export class ConfigurationService { isToolsCommand(): boolean { return this.configuration.parsedInputs.CLICommand === CLIActionType.TOOLS; } + + failAtEnd(): boolean { + return this.configuration.parsedInputs.failAtEnd ?? false; + } } diff --git a/test/unitary/service/command/execute-command-service.test.ts b/test/unitary/service/command/execute-command-service.test.ts index b9d4d9d3..83f58c1c 100644 --- a/test/unitary/service/command/execute-command-service.test.ts +++ b/test/unitary/service/command/execute-command-service.test.ts @@ -313,4 +313,38 @@ describe("executeNodeChain", () => { // last call to execSpy must be for the last node expect(execSpy.mock.calls[3][0]).toStrictEqual(nodeChain[3]); }); + + test.each([ + ["sequential: fail fast", false, false, 1], + ["sequential: fail at end", false, true, 2], + ["parallel: fail fast", true, true, 2], + ["parallel: fail at end", true, true, 2], + ])("%p", async (_title, isParallel, failAtEnd, numExecCalls) => { + jest.spyOn(ConfigurationService.prototype, "isParallelExecutionEnabled").mockReturnValueOnce(isParallel); + jest.spyOn(ConfigurationService.prototype, "failAtEnd").mockReturnValueOnce(failAtEnd); + const execSpy = jest.spyOn(executeCommandService, "executeNodeCommands"); + execSpy.mockResolvedValueOnce([{executeCommandResults: [{result: ExecutionResult.NOT_OK}]} as unknown as ExecuteNodeResult]); + execSpy.mockResolvedValueOnce([{executeCommandResults: [{result: ExecutionResult.OK}]} as unknown as ExecuteNodeResult]); + + const nodeChain: NodeExecution[] = [ + { + node: { + ...defaultNodeValue, + project: "project1", + depth: 0 + } + }, + { + node: { + ...defaultNodeValue, + project: "project2", + depth: 1 + } + } + ]; + + await executeCommandService.executeNodeChain(nodeChain, undefined); + expect(execSpy).toHaveBeenCalledTimes(numExecCalls); + + }); }); \ No newline at end of file