From 5e82f309015dec609df6e39fc5c1f0e4bb20ec13 Mon Sep 17 00:00:00 2001 From: dmvict Date: Thu, 24 Oct 2024 17:39:05 +0300 Subject: [PATCH 1/3] Write test routine `retryWithOptionPreRetryCommand` --- test/Action.test.s | 82 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/test/Action.test.s b/test/Action.test.s index 861db89e..1e135e2e 100644 --- a/test/Action.test.s +++ b/test/Action.test.s @@ -885,6 +885,87 @@ function retryWithOptionRetryCondition( test ) // +function retryWithOptionPreRetryCommand( test ) +{ + const context = this; + const a = test.assetFor( false ); + const actionPath = a.abs( '_action/actions/wretry.action/v1' ); + const testAction = 'dmvict/test.action@v0.0.2'; + const execPath = `node ${ a.path.nativize( a.abs( actionPath, 'src/Main.js' ) ) }`; + const isTestContainer = _.process.insideTestContainer(); + + /* - */ + + a.ready.then( () => + { + test.case = 'enought attempts, executing command between retries'; + core.exportVariable( `INPUT_ACTION`, testAction ); + core.exportVariable( `INPUT_WITH`, 'value : 0' ); + core.exportVariable( `INPUT_ATTEMPT_LIMIT`, '4' ); + core.exportVariable( `INPUT_PRE_RETRY_COMMAND`, 'echo "Executing pre_retry_command"' ); + return null; + }); + + actionSetup(); + + a.shellNonThrowing({ currentPath : actionPath, execPath }); + a.ready.then( ( op ) => + { + test.identical( op.exitCode, 0 ); + if( !isTestContainer ) + test.ge( _.strCount( op.output, '::set-env' ), 3 ); + test.identical( _.strCount( op.output, '::error::Wrong attempt' ), 3 ); + test.identical( _.strCount( op.output, /::error::undefined.*Attempts exhausted, made 4 attempts/ ), 0 ); + test.identical( _.strCount( op.output, /::error::.*Process returned exit code/ ), 0 ); + test.identical( _.strCount( op.output, 'Success' ), 1 ); + test.identical( _.strCount( op.output, 'Executing pre_retry_command' ), 4 ); + return null; + }); + + /* */ + + a.ready.then( () => + { + test.case = 'command between retries trows error, interrupting execution'; + core.exportVariable( `INPUT_ACTION`, testAction ); + core.exportVariable( `INPUT_WITH`, 'value : 0' ); + core.exportVariable( `INPUT_ATTEMPT_LIMIT`, '4' ); + core.exportVariable( `INPUT_PRE_RETRY_COMMAND`, 'exit 1' ); + return null; + }); + + actionSetup(); + + a.shellNonThrowing({ currentPath : actionPath, execPath }); + a.ready.then( ( op ) => + { + test.notIdentical( op.exitCode, 0 ); + if( !isTestContainer ) + test.ge( _.strCount( op.output, '::set-env' ), 2 ); + test.identical( _.strCount( op.output, '::error::Wrong attempt' ), 1 ); + test.identical( _.strCount( op.output, /::error::undefined.*Attempts exhausted, made 4 attempts/ ), 0 ); + test.identical( _.strCount( op.output, /::error::.*Process returned exit code/ ), 1 ); + test.identical( _.strCount( op.output, 'Success' ), 0 ); + return null; + }); + + /* - */ + + return a.ready; + + /* */ + + function actionSetup() + { + a.ready.then( () => { a.fileProvider.filesDelete( a.abs( '.' ) ); return null; } ); + a.ready.then( () => { a.fileProvider.dirMake( actionPath ); return null; } ); + a.shell( `git clone ${ a.path.nativize( context.actionDirPath ) } ${ a.path.nativize( actionPath ) }` ); + return a.ready; + } +} + +// + function retryWithExternalActionOnLocal( test ) { const context = this; @@ -1869,6 +1950,7 @@ const Proto = retryWithOptionAttemptLimit, retryWithOptionAttemptDelay, retryWithOptionRetryCondition, + retryWithOptionPreRetryCommand, retryWithExternalActionOnLocal, retryWithExternalActionOnRemote, From 2fc0a2d1fba74d6f4d2ecfe4c503dea0e840cee4 Mon Sep 17 00:00:00 2001 From: dmvict Date: Tue, 29 Oct 2024 08:01:19 +0200 Subject: [PATCH 2/3] Add option `pre_retry_command`, update logic of routine `retry` --- Readme.md | 4 + action.yml | 3 + .../wdeasync/Release/deasync.node.d | 1 - src/Retry.js | 105 ++++++++++++------ 4 files changed, 81 insertions(+), 32 deletions(-) delete mode 100644 node_modules/wdeasync/build/Release/.deps/home/work/Temp/wretry.action/node_modules/wdeasync/Release/deasync.node.d diff --git a/Readme.md b/Readme.md index 0ee0e62d..7b459d9b 100644 --- a/Readme.md +++ b/Readme.md @@ -52,6 +52,10 @@ The command to run. The action runs the command in the default shell. **Attention**. Action requires defined `action` or `command`. If the fields `action` and `commands` are defined simultaneously, then action will throw error. +### `pre_retry_command` + +Command to run between retries. + ### `with` An options map for Github action. It is a multiline string with pairs `key : value`. diff --git a/action.yml b/action.yml index 23f41dbb..ff671c83 100644 --- a/action.yml +++ b/action.yml @@ -13,6 +13,9 @@ inputs: command: description: 'Command to run. Should be defined action or command, not both.' required: false + pre_retry_command: + description: 'Command to run between retries.' + required: false with: description: 'An options map for Github action' required: false diff --git a/node_modules/wdeasync/build/Release/.deps/home/work/Temp/wretry.action/node_modules/wdeasync/Release/deasync.node.d b/node_modules/wdeasync/build/Release/.deps/home/work/Temp/wretry.action/node_modules/wdeasync/Release/deasync.node.d deleted file mode 100644 index ec79f25f..00000000 --- a/node_modules/wdeasync/build/Release/.deps/home/work/Temp/wretry.action/node_modules/wdeasync/Release/deasync.node.d +++ /dev/null @@ -1 +0,0 @@ -cmd_/home/work/Temp/wretry.action/node_modules/wdeasync/Release/deasync.node := ln -f "Release/deasync.node" "/home/work/Temp/wretry.action/node_modules/wdeasync/Release/deasync.node" 2>/dev/null || (rm -rf "/home/work/Temp/wretry.action/node_modules/wdeasync/Release/deasync.node" && cp -af "Release/deasync.node" "/home/work/Temp/wretry.action/node_modules/wdeasync/Release/deasync.node") diff --git a/src/Retry.js b/src/Retry.js index 817f3d54..a7ce4ba4 100644 --- a/src/Retry.js +++ b/src/Retry.js @@ -8,43 +8,42 @@ const _ = wTools; function retry( scriptType ) { let shouldRetry = core.getInput( 'retry_condition' ) || true; + const actionName = core.getInput( 'action' ); + const command = core.getMultilineInput( 'command' ); + const preRetryCommand = core.getMultilineInput( 'pre_retry_command' ); + + let currentPath = core.getInput( 'current_path' ) || _.path.current(); + if( !_.path.isAbsolute( currentPath ) ) + currentPath = _.path.join( _.path.current(), currentPath ); + + let preRetryExecPath = null; + if( preRetryCommand.length > 0 ) + preRetryExecPath = execPathFromCommandAndNameForm( preRetryCommand, 'pre_script' ); + + let startTime = null; + const timeLimit = _.number.from( core.getInput( 'time_out' ) ) || null; + let timeoutGet = () => null; + if( timeLimit ) + { + startTime = _.time.now(); + timeoutGet = () => + { + let now = _.time.now(); + let spent = now - startTime; + if( spent >= timeLimit ) + shouldRetry = false; + return timeLimit - spent; + }; + } return _.Consequence.Try( () => { - let routine, startTime; + let routine; const con = _.take( null ); - const actionName = core.getInput( 'action' ); - const command = core.getMultilineInput( 'command' ); - - const timeLimit = _.number.from( core.getInput( 'time_out' ) ) || null; - let timeoutGet = () => null; - if( timeLimit ) - { - startTime = _.time.now(); - timeoutGet = () => - { - let now = _.time.now(); - let spent = now - startTime; - if( spent >= timeLimit ) - shouldRetry = false; - return timeLimit - spent; - }; - } if( !actionName ) { - const commands = common.commandsForm( command ); - const commandsScriptPath = _.path.join( __dirname, process.platform === 'win32' ? 'script.ps1' : 'script.sh' ); - _.fileProvider.fileWrite( commandsScriptPath, commands.join( '\n' ) ); - - let currentPath = core.getInput( 'current_path' ) || _.path.current(); - if( !_.path.isAbsolute( currentPath ) ) - currentPath = _.path.join( _.path.current(), currentPath ); - const execPath = - process.platform === 'win32' ? - `pwsh -command ". '${ _.path.nativize( commandsScriptPath ) }'"` : - `bash --noprofile --norc -eo pipefail ${ _.path.nativize( commandsScriptPath ) }`; - + const execPath = execPathFromCommandAndNameForm( command, 'script' ); routine = () => { const o = @@ -167,8 +166,37 @@ function retry( scriptType ) { if( process.env.GITHUB_OUTPUT && _.fileProvider.fileExists( process.env.GITHUB_OUTPUT ) ) _.fileProvider.fileWrite( process.env.GITHUB_OUTPUT, '' ); - return routine(); + + if( preRetryCommand.length > 0 ) + { + const o = + { + currentPath, + execPath : preRetryExecPath, + inputMirroring : 0, + stdio : 'inherit', + mode : 'shell', + }; + o.timeOut = timeoutGet(); + _.process.start( o ); + + return o.ready.catch( ( err ) => + { + _.error.attend( err ); + shouldRetry = false; + return err; + }) + .then( () => + { + return routine(); + }); + } + else + { + return routine(); + } }; + return _.retry ({ routine : githubOutputCleanRoutine, @@ -201,10 +229,25 @@ function retry( scriptType ) function onError( err ) { _.error.attend( err ); + if( _.bool.is( shouldRetry ) ) return shouldRetry; return !!common.evaluateExpression( shouldRetry ); } + + function execPathFromCommandAndNameForm( command, scriptName ) + { + + const commands = common.commandsForm( command ); + const commandsScriptPath = _.path.join( __dirname, process.platform === 'win32' ? `${ scriptName }.ps1` : `${ scriptName }.sh` ); + _.fileProvider.fileWrite( commandsScriptPath, commands.join( '\n' ) ); + + const execPath = process.platform === 'win32' ? + `pwsh -command ". '${ _.path.nativize( commandsScriptPath ) }'"` : + `bash --noprofile --norc -eo pipefail ${ _.path.nativize( commandsScriptPath ) }`; + + return execPath; + } } module.exports = { retry }; From ac8cdbf5465f517fab2a74790d704bca61505a91 Mon Sep 17 00:00:00 2001 From: dmvict Date: Tue, 29 Oct 2024 08:39:22 +0200 Subject: [PATCH 3/3] Fix test routine `retryWithOptionPreRetryCommand`, remove env after execution --- test/Action.test.s | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/Action.test.s b/test/Action.test.s index 1e135e2e..59a230e7 100644 --- a/test/Action.test.s +++ b/test/Action.test.s @@ -949,6 +949,12 @@ function retryWithOptionPreRetryCommand( test ) return null; }); + a.ready.finally( () => + { + delete process.env.INPUT_PRE_RETRY_COMMAND; + return null; + }); + /* - */ return a.ready;