From 76ff6a2fd183ee2ba0cb30dce6d07d3ae3e515cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20H=C3=A9bert?= Date: Mon, 28 Aug 2023 17:54:56 +0200 Subject: [PATCH] feat(deploy): add same-commit-policy option --- bin/clever.js | 5 ++-- src/command-options.js | 42 +++++++++++++++++++++++++++++++++ src/commands/deploy.js | 28 ++++++++++++++++++---- src/get-output-format-option.js | 23 ------------------ 4 files changed, 68 insertions(+), 30 deletions(-) create mode 100644 src/command-options.js delete mode 100644 src/get-output-format-option.js diff --git a/bin/clever.js b/bin/clever.js index cc2f4e10..816adb3f 100755 --- a/bin/clever.js +++ b/bin/clever.js @@ -21,8 +21,8 @@ const git = require('../src/models/git.js'); const Parsers = require('../src/parsers.js'); const handleCommandPromise = require('../src/command-promise-handler.js'); const Formatter = require('../src/models/format-string.js'); -const { getOutputFormatOption } = require('../src/get-output-format-option.js'); const { AVAILABLE_ZONES } = require('../src/models/application.js'); +const { getOutputFormatOption, getSameCommitPolicyOption } = require('../src/command-options.js'); // Exit cleanly if the program we pipe to exits abruptly process.stdout.on('error', (error) => { @@ -216,6 +216,7 @@ function run () { aliases: ['f'], description: 'Force deploy even if it\'s not fast-forwardable', }), + sameCommitPolicy: getSameCommitPolicyOption(), webhookFormat: cliparse.option('format', { metavar: 'format', default: 'raw', @@ -642,7 +643,7 @@ function run () { const deploy = lazyRequirePromiseModule('../src/commands/deploy.js'); const deployCommand = cliparse.command('deploy', { description: 'Deploy an application', - options: [opts.alias, opts.branch, opts.quiet, opts.forceDeploy, opts.followDeployLogs], + options: [opts.alias, opts.branch, opts.quiet, opts.forceDeploy, opts.followDeployLogs, opts.sameCommitPolicy], }, deploy('deploy')); // DIAG COMMAND diff --git a/src/command-options.js b/src/command-options.js new file mode 100644 index 00000000..2e802251 --- /dev/null +++ b/src/command-options.js @@ -0,0 +1,42 @@ +const cliparse = require('cliparse'); + +function getOutputFormatOption (formats = []) { + const availableFormats = ['human', 'json', ...formats]; + return cliparse.option('format', { + aliases: ['F'], + metavar: 'format', + parser: (format) => { + return availableFormats.includes(format) + ? cliparse.parsers.success(format) + : cliparse.parsers.error('The output format must be one of ' + availableFormats.join(', ')); + }, + default: 'human', + description: `Output format (${availableFormats.join(', ')})`, + complete () { + return cliparse.autocomplete.words(availableFormats); + }, + }); +} + +function getSameCommitPolicyOption () { + const availablePolicies = ['error', 'ignore', 'restart', 'rebuild']; + return cliparse.option('same-commit-policy', { + aliases: ['p'], + metavar: 'same-commit-policy', + parser: (policy) => { + return availablePolicies.includes(policy) + ? cliparse.parsers.success(policy) + : cliparse.parsers.error(`The output policy must be one of ${availablePolicies.join(', ')}`); + }, + default: 'error', + description: `Which policy to apply when the local commit is the same as the remote one. Available policies are (${availablePolicies.join(', ')})`, + complete () { + return cliparse.autocomplete.words(availablePolicies); + }, + }); +} + +module.exports = { + getOutputFormatOption, + getSameCommitPolicyOption, +}; diff --git a/src/commands/deploy.js b/src/commands/deploy.js index c8e438fe..ec4bc1c0 100644 --- a/src/commands/deploy.js +++ b/src/commands/deploy.js @@ -13,7 +13,7 @@ const { sendToApi } = require('../models/send-to-api.js'); // Once the API call to redeploy() has been triggered successfully, // the rest (waiting for deployment state to evolve and displaying logs) is done with auto retry (resilient to network failures) async function deploy (params) { - const { alias, branch: branchName, quiet, force, follow } = params.options; + const { alias, branch: branchName, quiet, force, follow, 'same-commit-policy': sameCommitPolicy } = params.options; const appData = await AppConfig.getAppDetails({ alias }); const { ownerId, appId } = appData; @@ -31,11 +31,24 @@ async function deploy (params) { Logger.println(colors.bold.blue(`Remote application belongs to ${ownerId}`)); if (commitIdToPush === remoteHeadCommitId) { - const upToDateMessage = `The clever-cloud application is up-to-date (${remoteHeadCommitId}). Try this command to restart the application:`; - if (commitIdToPush !== deployedCommitId) { - throw new Error(`${upToDateMessage}\nclever restart --commit ${commitIdToPush}`); + switch (sameCommitPolicy) { + case 'ignore': + Logger.println(`The clever-cloud application is up-to-date (${colors.green(remoteHeadCommitId)})`); + return; + case 'restart': + return restartOnSameCommit(ownerId, appId, commitIdToPush, quiet, follow, false); + case 'rebuild': + return restartOnSameCommit(ownerId, appId, commitIdToPush, quiet, follow, true); + case 'error': + default: { + const upToDateMessage = `The clever-cloud application is up-to-date (${colors.green(remoteHeadCommitId)}).\nYou can set a policy with 'same-commit-policy' to handle differently when remote HEAD has the same commit as the one to push.\nOr try this command to restart the application:`; + if (commitIdToPush !== deployedCommitId) { + const restartWithId = `clever restart --commit ${commitIdToPush}` + throw new Error(`${upToDateMessage}\n${colors.yellow(restartWithId)}`); + } + throw new Error(`${upToDateMessage}\n${colors.yellow('clever restart')}`); + } } - throw new Error(`${upToDateMessage}\nclever restart`); } if (remoteHeadCommitId == null || deployedCommitId == null) { @@ -68,4 +81,9 @@ async function deploy (params) { return Log.watchDeploymentAndDisplayLogs({ ownerId, appId, commitId: commitIdToPush, knownDeployments, quiet, follow }); } +async function restartOnSameCommit (ownerId, appId, commitIdToPush, quiet, follow, withoutCache) { + const restart = await Application.redeploy(ownerId, appId, commitIdToPush, withoutCache); + return Log.watchDeploymentAndDisplayLogs({ ownerId, appId, deploymentId: restart.deploymentId, quiet, follow }); +} + module.exports = { deploy }; diff --git a/src/get-output-format-option.js b/src/get-output-format-option.js deleted file mode 100644 index 11830a79..00000000 --- a/src/get-output-format-option.js +++ /dev/null @@ -1,23 +0,0 @@ -const cliparse = require('cliparse'); - -function getOutputFormatOption (formats = []) { - const availableFormats = ['human', 'json', ...formats]; - return cliparse.option('format', { - aliases: ['F'], - metavar: 'format', - parser: (format) => { - return availableFormats.includes(format) - ? cliparse.parsers.success(format) - : cliparse.parsers.error('The output format must be one of ' + availableFormats.join(', ')); - }, - default: 'human', - description: `Output format (${availableFormats.join(', ')})`, - complete () { - return cliparse.autocomplete.words(availableFormats); - }, - }); -} - -module.exports = { - getOutputFormatOption, -};