From f699645b8b0d42391dc9ff5c4b0dd00a184a390b Mon Sep 17 00:00:00 2001 From: Sam Beckett Date: Tue, 19 Oct 2021 10:49:06 -0600 Subject: [PATCH 1/7] actions group, listActions command --- plugins/actions/index.js | 96 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 plugins/actions/index.js diff --git a/plugins/actions/index.js b/plugins/actions/index.js new file mode 100644 index 0000000..2c38719 --- /dev/null +++ b/plugins/actions/index.js @@ -0,0 +1,96 @@ +const assert = require('assert'); + +/** + * ListActions - Get a list of actions based on an app + */ +async function listActions(appkit, args) { + // Check to make sure we have passed in an app argument + try { + assert.ok(args.app && args.app !== '', 'An application name was not provided.'); + } catch (err) { + appkit.terminal.error(err); + return; + } + + try { + const actions = await appkit.api.get(`/apps/${args.app}/actions`); + + console.log(appkit.terminal.markdown(`###===### **⬢ ${args.app}** Actions ###===###\n`)); + + if (!actions || actions.length === 0) { + console.log(appkit.terminal.markdown('\nNo actions were found.')); + return; + } + + // Output should look like the following: + /* + === ⬢ app-space Actions === + + [action-name] (269af9dd-b668-4b5a-b9f1-e76e97304c6d) + ------------------------------------------------------- + description Testing for Akkeris Actions + created tsz + updated tsz + + size + command + options + image + environment + + ... + */ + + const ui = require('cliui')(); + + const md = (s) => appkit.terminal.markdown(s); + const label = (s) => ({ text: md(`**${s}**`), width: 20 }); + + actions.forEach((action) => { + ui.div(md(`***${action.name}*** ###(${action.action})###`)); + + // Spit out - for the entire length of header (+ 4 for spaces/parenthesis) + ui.div(md(`###${'-'.repeat(action.name.length + action.action.length + 3)}###`)); + + ui.div(label('Description'), action.description); + ui.div(label('Created At'), (new Date(action.created)).toLocaleString()); + ui.div(label('Updated At'), (new Date(action.updated)).toLocaleString()); + ui.div(label('Image'), action.formation.options.image ? action.formation.options.image : 'Latest app image'); + ui.div(label('Command'), action.formation.command ? action.formation.command : 'Default image command'); + ui.div(); + ui.div(appkit.terminal.italic('Environment Variables')); + if (action.formation.options.env && Object.keys(action.formation.options.env).length > 0) { + Object.keys(action.formation.options.env).forEach((key) => { + ui.div(label(key), action.formation.options.env[key]); + }); + } + }); + + console.log(ui.toString()); + console.log(); + } catch (err) { + appkit.terminal.print(err); + } +} + +module.exports = { + init(appkit) { + const require_app_option = { + app: { + alias: 'a', + demand: true, + string: true, + description: 'The app to act on', + }, + }; + + appkit.args + .command('actions', 'List available actions on an app', require_app_option, listActions.bind(null, appkit)); + }, + update() { + // do nothing. + }, + group: 'actions', + help: 'manage actions (create, destroy)', + primary: true, +}; From 0e410c4f9bdebb820d3c07bf0c827fb10d81dc68 Mon Sep 17 00:00:00 2001 From: Sam Beckett Date: Tue, 19 Oct 2021 11:01:42 -0600 Subject: [PATCH 2/7] add actions:create stub --- plugins/actions/index.js | 71 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/plugins/actions/index.js b/plugins/actions/index.js index 2c38719..f3e88a5 100644 --- a/plugins/actions/index.js +++ b/plugins/actions/index.js @@ -73,6 +73,35 @@ async function listActions(appkit, args) { } } +/** + * createActions - Create a new action on an app + */ +async function createActions(appkit, args) { + // Check to make sure we have passed in required arguments + try { + assert.ok(args.app && args.app !== '', 'An application name was not provided.'); + assert.ok(args.size && args.size !== '', 'An action size was not provided.'); + assert.ok(args.name && args.name !== '', 'A name was not provided.'); + } catch (err) { + appkit.terminal.error(err); + process.exit(1); + } + + // All possible arguments (some of these are optional) + const { + app, description, size, command, image, env, name, + } = args; + + /* + Code goes here + Helpful functions: + appkit.api.post(body, url); -- POST the given body to the given URL + console.log(str); -- Output a message to the console + + Code should create a JSON object with the payload and POST it to the actions endpoint + */ +} + module.exports = { init(appkit) { const require_app_option = { @@ -84,8 +113,48 @@ module.exports = { }, }; + const create_action_option = { + app: { + alias: 'a', + demand: true, + string: true, + description: 'The app to act on', + }, + description: { + alias: 'd', + demand: false, + string: true, + description: 'An optional description of the action', + }, + size: { + alias: 's', + demand: true, + string: true, + description: 'The dyno size to use for the action\'s formation', + }, + command: { + alias: 'c', + demand: false, + string: true, + description: 'An optional command to use for the action\'s image', + }, + image: { + alias: 'i', + demand: false, + string: true, + description: 'An optional image to use for the action instead of the app\'s image', + }, + env: { + alias: 'e', + demand: false, + string: true, + description: 'One or more key-value pairs (KEY=VALUE) to use as additional environment variables', + }, + }; + appkit.args - .command('actions', 'List available actions on an app', require_app_option, listActions.bind(null, appkit)); + .command('actions', 'List available actions on an app', require_app_option, listActions.bind(null, appkit)) + .command('actions:create NAME', 'Create action on an app', create_action_option, createActions.bind(null, appkit)); }, update() { // do nothing. From 5f093d972dad33cc019b3da429a12237c51a6ffa Mon Sep 17 00:00:00 2001 From: Naga Yannam Date: Tue, 2 Nov 2021 11:01:06 -0600 Subject: [PATCH 3/7] create and delete actions for cli --- plugins/actions/index.js | 105 +++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 16 deletions(-) diff --git a/plugins/actions/index.js b/plugins/actions/index.js index f3e88a5..d0052e8 100644 --- a/plugins/actions/index.js +++ b/plugins/actions/index.js @@ -80,26 +80,77 @@ async function createActions(appkit, args) { // Check to make sure we have passed in required arguments try { assert.ok(args.app && args.app !== '', 'An application name was not provided.'); - assert.ok(args.size && args.size !== '', 'An action size was not provided.'); assert.ok(args.name && args.name !== '', 'A name was not provided.'); } catch (err) { appkit.terminal.error(err); process.exit(1); } - // All possible arguments (some of these are optional) - const { - app, description, size, command, image, env, name, - } = args; - - /* - Code goes here - Helpful functions: - appkit.api.post(body, url); -- POST the given body to the given URL - console.log(str); -- Output a message to the console - - Code should create a JSON object with the payload and POST it to the actions endpoint - */ + const requestPayload = { + size: args.size, + options: { + }, + name: args.name, + }; + if (args.description && args.description !== '') { + requestPayload.description = args.description; + } + if (args.image && args.image !== '') { + requestPayload.options.image = args.image; + } + if (args.command && args.command !== '') { + requestPayload.command = args.command; + } + if (args.env && typeof args.env === 'string' && args.env !== '') { + args.env = [args.env]; + } + + if (args.env && Array.isArray(args.env) && args.env.length > 0) { + const values_paired = args.env; + const values = {}; + /* eslint-disable no-restricted-syntax */ + for (const value of values_paired) { + if (value.indexOf('=') !== -1) { + const key = value.substring(0, value.indexOf('=')); + const val = value.substring(value.indexOf('=') + 1); + if (key && val) { + values[key] = val; + } + } + } + requestPayload.options.env = values; + } + appkit.api.post(JSON.stringify(requestPayload), `/apps/${args.app}/actions`) + .then(() => { + console.log(`Action ${args.name} is successfully created`); + }) + .catch((err) => { + appkit.terminal.error(err); + }); +} + +/** + * deleteAction - Delete an existing action on an app + */ +async function deleteAction(appkit, args) { + // Check to make sure we have passed in required arguments + try { + assert.ok(args.app && args.app !== '', 'An application name was not provided.'); + assert.ok(args.action && args.action !== '', 'A action was not provided.'); + } catch (err) { + appkit.terminal.error(err); + process.exit(1); + } + const loader = appkit.terminal.loading(`Deleting action ${args.action} from ${args.app}`); + loader.start(); + appkit.api.delete(`/apps/${args.app}/actions/${args.action}`, (err) => { + loader.end(); + if (err) { + appkit.terminal.error(err); + return; + } + console.log(appkit.terminal.markdown(`###===### **${args.action}** has been succesfully deleted from ##${args.app}##`)); + }); } module.exports = { @@ -128,7 +179,7 @@ module.exports = { }, size: { alias: 's', - demand: true, + demand: false, string: true, description: 'The dyno size to use for the action\'s formation', }, @@ -150,11 +201,33 @@ module.exports = { string: true, description: 'One or more key-value pairs (KEY=VALUE) to use as additional environment variables', }, + name: { + alias: 'n', + demand: true, + string: true, + description: 'The name of the action', + }, + }; + + const delete_action_option = { + app: { + alias: 'a', + demand: true, + string: true, + description: 'The name of the app to act on', + }, + action: { + alias: 'n', + demand: true, + string: true, + description: 'An name of the action', + }, }; appkit.args .command('actions', 'List available actions on an app', require_app_option, listActions.bind(null, appkit)) - .command('actions:create NAME', 'Create action on an app', create_action_option, createActions.bind(null, appkit)); + .command('actions:create', 'Create action on an app', create_action_option, createActions.bind(null, appkit)) + .command('actions:delete', 'Delete action on an app', delete_action_option, deleteAction.bind(null, appkit)); }, update() { // do nothing. From c13db6b76b28c0ec7316435a4d1517992340cc05 Mon Sep 17 00:00:00 2001 From: Naga Yannam Date: Thu, 3 Feb 2022 09:31:23 -0700 Subject: [PATCH 4/7] update actions cli code --- package-lock.json | 4 +- plugins/actions/index.js | 102 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3a7877d..640788e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "akkeris", - "version": "3.1.20", + "version": "3.2.22", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "akkeris", - "version": "3.1.20", + "version": "3.2.22", "license": "ISC", "dependencies": { "chart": "github:jstrace/chart", diff --git a/plugins/actions/index.js b/plugins/actions/index.js index d0052e8..97a0b1b 100644 --- a/plugins/actions/index.js +++ b/plugins/actions/index.js @@ -153,6 +153,60 @@ async function deleteAction(appkit, args) { }); } +/** + * updateAction - update an existing action on an app + */ +async function updateAction(appkit, args) { + try { + assert.ok(args.app && args.app !== '', 'An application name was not provided.'); + assert.ok(args.name && args.name !== '', 'A action was not provided.'); + } catch (err) { + appkit.terminal.error(err); + process.exit(1); + } + const updateRequestPayload = { + size: args.size, + options: { + }, + name: args.name, + }; + if (args.description && args.description !== '') { + updateRequestPayload.description = args.description; + } + if (args.image) { + updateRequestPayload.options.image = args.image; + } + if (args.command && args.command !== '') { + updateRequestPayload.command = args.command; + } + if (args.env && typeof args.env === 'string' && args.env !== '') { + args.env = [args.env]; + } + + if (args.env && Array.isArray(args.env) && args.env.length > 0) { + const values_paired = args.env; + const values = {}; + /* eslint-disable no-restricted-syntax */ + for (const value of values_paired) { + if (value.indexOf('=') !== -1) { + const key = value.substring(0, value.indexOf('=')); + const val = value.substring(value.indexOf('=') + 1); + if (key && val) { + values[key] = val; + } + } + } + updateRequestPayload.options.env = values; + } + appkit.api.patch(JSON.stringify(updateRequestPayload), `/apps/${args.app}/actions/${args.name}`) + .then(() => { + console.log(`Action ${args.name} is successfully updated`); + }) + .catch((err) => { + appkit.terminal.error(err); + }); +} + module.exports = { init(appkit) { const require_app_option = { @@ -224,10 +278,56 @@ module.exports = { }, }; + const update_action_option = { + app: { + alias: 'a', + demand: true, + string: true, + description: 'The app to act on', + }, + description: { + alias: 'd', + demand: false, + string: true, + description: 'An optional description of the action', + }, + size: { + alias: 's', + demand: false, + string: true, + description: 'The dyno size to use for the action\'s formation', + }, + command: { + alias: 'c', + demand: false, + string: true, + description: 'An optional command to use for the action\'s image', + }, + image: { + alias: 'i', + demand: false, + string: true, + description: 'An optional image to use for the action instead of the app\'s image', + }, + env: { + alias: 'e', + demand: false, + string: true, + description: 'One or more key-value pairs (KEY=VALUE) to use as additional environment variables', + }, + name: { + alias: 'n', + demand: true, + string: true, + description: 'The name of the action', + }, + }; + appkit.args .command('actions', 'List available actions on an app', require_app_option, listActions.bind(null, appkit)) .command('actions:create', 'Create action on an app', create_action_option, createActions.bind(null, appkit)) - .command('actions:delete', 'Delete action on an app', delete_action_option, deleteAction.bind(null, appkit)); + .command('actions:delete', 'Delete action on an app', delete_action_option, deleteAction.bind(null, appkit)) + .command('actions:update', 'update existing action on an app', update_action_option, updateAction.bind(null, appkit)); }, update() { // do nothing. From 02b8c149ffc171a0646e676d60f2b506eb13691d Mon Sep 17 00:00:00 2001 From: Sam Beckett Date: Wed, 18 May 2022 23:01:32 -0600 Subject: [PATCH 5/7] add get action, & trigger, list and get runs --- plugins/actions/index.js | 344 +++++++++++++++++++++++++++++++-------- 1 file changed, 275 insertions(+), 69 deletions(-) diff --git a/plugins/actions/index.js b/plugins/actions/index.js index 97a0b1b..3eaac40 100644 --- a/plugins/actions/index.js +++ b/plugins/actions/index.js @@ -22,25 +22,6 @@ async function listActions(appkit, args) { return; } - // Output should look like the following: - /* - === ⬢ app-space Actions === - - [action-name] (269af9dd-b668-4b5a-b9f1-e76e97304c6d) - ------------------------------------------------------- - description Testing for Akkeris Actions - created tsz - updated tsz - - size - command - options - image - environment - - ... - */ - const ui = require('cliui')(); const md = (s) => appkit.terminal.markdown(s); @@ -56,31 +37,189 @@ async function listActions(appkit, args) { ui.div(label('Created At'), (new Date(action.created)).toLocaleString()); ui.div(label('Updated At'), (new Date(action.updated)).toLocaleString()); ui.div(label('Image'), action.formation.options.image ? action.formation.options.image : 'Latest app image'); - ui.div(label('Command'), action.formation.command ? action.formation.command : 'Default image command'); - ui.div(); - ui.div(appkit.terminal.italic('Environment Variables')); + ui.div(label('Command'), action.formation.command ? `'${action.formation.command}'` : 'Default image command'); + ui.div(label('Size'), action.formation.size); + ui.div(label('Events'), action.events || md('###n/a###')); + if (action.formation.options.env && Object.keys(action.formation.options.env).length > 0) { - Object.keys(action.formation.options.env).forEach((key) => { - ui.div(label(key), action.formation.options.env[key]); + Object.keys(action.formation.options.env).forEach((key, idx) => { + ui.div(label(idx > 0 ? ' ' : 'Env Vars'), `'${key}' = '${action.formation.options.env[key]}'`); }); + } else { + ui.div(label('Env Vars'), md('###n/a###')); } + ui.div(); + }); + + console.log(ui.toString()); + } catch (err) { + appkit.terminal.print(err); + } +} + +/** + * DescribeAction - Describe an action + */ +async function describeAction(appkit, args) { + args.action = args.ACTION; + try { + assert.ok(args.app && args.app !== '', 'An application name was not provided.'); + assert.ok(args.action && args.action !== '', 'An action was not provided.'); + } catch (err) { + appkit.terminal.error(err); + return; + } + + try { + const action = await appkit.api.get(`/apps/${args.app}/actions/${args.action}`); + + if (!action) { + console.log(appkit.terminal.markdown('\nNo actions were found.')); + return; + } + + const ui = require('cliui')(); + + const md = (s) => appkit.terminal.markdown(s); + const label = (s) => ({ text: md(`**${s}**`), width: 20 }); + + ui.div(md(`***${action.name}*** ###(${action.action})###`)); + + // Spit out - for the entire length of header (+ 4 for spaces/parenthesis) + ui.div(md(`###${'-'.repeat(action.name.length + action.action.length + 3)}###`)); + + ui.div(label('Description'), action.description); + ui.div(label('Created At'), (new Date(action.created)).toLocaleString()); + ui.div(label('Updated At'), (new Date(action.updated)).toLocaleString()); + ui.div(label('Image'), action.formation.options.image ? action.formation.options.image : 'Latest app image'); + ui.div(label('Command'), action.formation.command ? `'${action.formation.command}'` : 'Default image command'); + ui.div(label('Size'), action.formation.size); + ui.div(label('Events'), action.events || md('###n/a###')); + + if (action.formation.options.env && Object.keys(action.formation.options.env).length > 0) { + Object.keys(action.formation.options.env).forEach((key, idx) => { + ui.div(label(idx > 0 ? ' ' : 'Env Vars'), `'${key}' = '${action.formation.options.env[key]}'`); + }); + } else { + ui.div(label('Env Vars'), md('###n/a###')); + } + ui.div(); + + console.log(ui.toString()); + } catch (err) { + appkit.terminal.print(err); + } +} + +/** + * ListActionRuns - Get a list of action runs for an action + */ +async function listActionRuns(appkit, args) { + args.action = args.ACTION; + try { + assert.ok(args.app && args.app !== '', 'An application name was not provided.'); + assert.ok(args.action && args.action !== '', 'An action was not provided.'); + } catch (err) { + appkit.terminal.error(err); + return; + } + + try { + const runs = await appkit.api.get(`/apps/${args.app}/actions/${args.action}/runs`); + + console.log(appkit.terminal.markdown(`###===### **${args.action}** Action Runs ###===###\n`)); + + if (!runs || runs.length === 0) { + console.log(appkit.terminal.markdown(`\nNo action runs for **${args.action}** were found.`)); + return; + } + + const ui = require('cliui')(); + + const md = (s) => appkit.terminal.markdown(s); + const label = (s) => ({ text: md(`**${s}**`), width: 20 }); + const srt = (a, b) => a.run_number - b.run_number; + + if (runs.length > 10) { + console.log('Showing last 10 runs...\n'); + } + + runs.sort(srt).slice(-10).reverse().forEach((run) => { + ui.div((md(`**Run #${run.run_number}** ###(${run.action_run})###`))); + + // Spit out - for the entire length of header (5 for 'Run #', 3 for ' ()') + ui.div(md(`###${'-'.repeat(run.run_number.toString().length + run.action_run.length + 5 + 3)}###`)); + + ui.div(label('Status'), run.status); + ui.div(label('Started At'), (new Date(run.started_at)).toLocaleString()); + ui.div(label('Finished At'), run.finished_at ? (new Date(run.finished_at)).toLocaleString() : md('###n/a###')); + ui.div(label('Source'), run.source); + ui.div(label('Exit Code'), run.exit_code || md('###n/a###')); + ui.div(); }); console.log(ui.toString()); - console.log(); } catch (err) { appkit.terminal.print(err); } } /** - * createActions - Create a new action on an app + * describeActionRun - Get a list of action runs for an action + */ +async function describeActionRun(appkit, args) { + args.action = args.ACTION; + args.run = args.RUN; + try { + assert.ok(args.app && args.app !== '', 'An application name was not provided.'); + assert.ok(args.action && args.action !== '', 'An action was not provided.'); + assert.ok(args.run && args.run !== '', 'An action run was not provided.'); + } catch (err) { + appkit.terminal.error(err); + return; + } + + try { + const run = await appkit.api.get(`/apps/${args.app}/actions/${args.action}/runs/${args.run}`); + + if (!run) { + console.log(appkit.terminal.markdown(`\nNo action run matching ${args.run} for **${args.action}** was found.`)); + return; + } + + const ui = require('cliui')(); + + const md = (s) => appkit.terminal.markdown(s); + const label = (s) => ({ text: md(`**${s}**`), width: 20 }); + + ui.div((md(`**Run #${run.run_number}** ###(${run.action_run})###`))); + + // Spit out - for the entire length of header (5 for 'Run #', 3 for ' ()') + ui.div(md(`###${'-'.repeat(run.run_number.toString().length + run.action_run.length + 5 + 3)}###`)); + + ui.div(label('Status'), run.status); + ui.div(label('Started At'), (new Date(run.started_at)).toLocaleString()); + ui.div(label('Finished At'), run.finished_at ? (new Date(run.finished_at)).toLocaleString() : md('###n/a###')); + ui.div(label('Source'), run.source); + ui.div(label('Exit Code'), run.exit_code || md('###n/a###')); + ui.div(); + + console.log(ui.toString()); + } catch (err) { + appkit.terminal.print(err); + } +} + +/** + * createAction - Create a new action on an app */ -async function createActions(appkit, args) { +async function createAction(appkit, args) { + args.name = args.NAME; // Check to make sure we have passed in required arguments try { assert.ok(args.app && args.app !== '', 'An application name was not provided.'); assert.ok(args.name && args.name !== '', 'A name was not provided.'); + assert.ok(args.command && args.command !== '', 'A command was not provided.'); } catch (err) { appkit.terminal.error(err); process.exit(1); @@ -105,6 +244,7 @@ async function createActions(appkit, args) { args.env = [args.env]; } + // Make sure that env follows this forrmat: { "key": "value" } if (args.env && Array.isArray(args.env) && args.env.length > 0) { const values_paired = args.env; const values = {}; @@ -120,13 +260,27 @@ async function createActions(appkit, args) { } requestPayload.options.env = values; } - appkit.api.post(JSON.stringify(requestPayload), `/apps/${args.app}/actions`) - .then(() => { - console.log(`Action ${args.name} is successfully created`); - }) - .catch((err) => { - appkit.terminal.error(err); - }); + + // If events are in an array, convert to string + if (args.events && Array.isArray(args.events) && args.events.length > 0) { + args.events = args.events.join(','); + } + + // Make sure events are formatted properly (comma-separated string) + if (args.events && typeof args.events === 'string' && args.events !== '') { + requestPayload.events = args.events; + } + + const task = appkit.terminal.task(`Creating action **${args.name}**`); + task.start(); + try { + await appkit.api.post(JSON.stringify(requestPayload), `/apps/${args.app}/actions`); + } catch (err) { + task.end('error'); + appkit.terminal.error(err); + return; + } + task.end('ok'); } /** @@ -134,6 +288,7 @@ async function createActions(appkit, args) { */ async function deleteAction(appkit, args) { // Check to make sure we have passed in required arguments + args.action = args.ACTION; try { assert.ok(args.app && args.app !== '', 'An application name was not provided.'); assert.ok(args.action && args.action !== '', 'A action was not provided.'); @@ -141,25 +296,27 @@ async function deleteAction(appkit, args) { appkit.terminal.error(err); process.exit(1); } - const loader = appkit.terminal.loading(`Deleting action ${args.action} from ${args.app}`); - loader.start(); - appkit.api.delete(`/apps/${args.app}/actions/${args.action}`, (err) => { - loader.end(); - if (err) { - appkit.terminal.error(err); - return; - } - console.log(appkit.terminal.markdown(`###===### **${args.action}** has been succesfully deleted from ##${args.app}##`)); - }); + + const task = appkit.terminal.task(`Deleting action **${args.action}** from ${args.app}`); + task.start(); + try { + await appkit.api.delete(`/apps/${args.app}/actions/${args.action}`); + } catch (err) { + task.end('error'); + appkit.terminal.error(err); + return; + } + task.end('ok'); } /** * updateAction - update an existing action on an app */ async function updateAction(appkit, args) { + args.action = args.ACTION; try { assert.ok(args.app && args.app !== '', 'An application name was not provided.'); - assert.ok(args.name && args.name !== '', 'A action was not provided.'); + assert.ok(args.action && args.action !== '', 'An action was not provided.'); } catch (err) { appkit.terminal.error(err); process.exit(1); @@ -168,7 +325,7 @@ async function updateAction(appkit, args) { size: args.size, options: { }, - name: args.name, + name: args.action, }; if (args.description && args.description !== '') { updateRequestPayload.description = args.description; @@ -198,13 +355,55 @@ async function updateAction(appkit, args) { } updateRequestPayload.options.env = values; } - appkit.api.patch(JSON.stringify(updateRequestPayload), `/apps/${args.app}/actions/${args.name}`) - .then(() => { - console.log(`Action ${args.name} is successfully updated`); - }) - .catch((err) => { - appkit.terminal.error(err); - }); + + // If events are in an array, convert to string + if (args.events && Array.isArray(args.events) && args.events.length > 0) { + args.events = args.events.join(','); + } + + // Make sure events are formatted properly (comma-separated string) + if (args.events && typeof args.events === 'string' && args.events !== '') { + updateRequestPayload.events = args.events; + } else if (!args.events || (typeof args.events === 'string' && args.events === '')) { + updateRequestPayload.events = ''; + } + + const task = appkit.terminal.task(`Updating action **${args.action}**`); + task.start(); + try { + await appkit.api.patch(JSON.stringify(updateRequestPayload), `/apps/${args.app}/actions/${args.action}`); + } catch (err) { + task.end('error'); + appkit.terminal.error(err); + return; + } + task.end('ok'); +} + +/** + * triggerAction - Trigger an action on an app + */ +async function triggerAction(appkit, args) { + // Check to make sure we have passed in required arguments + args.action = args.ACTION; + try { + assert.ok(args.app && args.app !== '', 'An application name was not provided.'); + assert.ok(args.action && args.action !== '', 'A action was not provided.'); + } catch (err) { + appkit.terminal.error(err); + process.exit(1); + } + + const task = appkit.terminal.task(`Triggering action **${args.action}** on ${args.app}`); + task.start(); + try { + await appkit.api.post(null, `/apps/${args.app}/actions/${args.action}/runs`); + } catch (err) { + task.end('error'); + appkit.terminal.error(err); + return; + } + task.end('ok'); } module.exports = { @@ -239,9 +438,9 @@ module.exports = { }, command: { alias: 'c', - demand: false, + demand: true, string: true, - description: 'An optional command to use for the action\'s image', + description: 'The command to use for the action\'s image', }, image: { alias: 'i', @@ -255,11 +454,11 @@ module.exports = { string: true, description: 'One or more key-value pairs (KEY=VALUE) to use as additional environment variables', }, - name: { - alias: 'n', - demand: true, + events: { + alias: 'v', + demand: false, string: true, - description: 'The name of the action', + description: 'Comma separated list of events that should trigger the action', }, }; @@ -270,11 +469,14 @@ module.exports = { string: true, description: 'The name of the app to act on', }, - action: { - alias: 'n', + }; + + const trigger_action_option = { + app: { + alias: 'a', demand: true, string: true, - description: 'An name of the action', + description: 'The name of the app to act on', }, }; @@ -315,19 +517,23 @@ module.exports = { string: true, description: 'One or more key-value pairs (KEY=VALUE) to use as additional environment variables', }, - name: { - alias: 'n', - demand: true, + events: { + alias: 'v', + demand: false, string: true, - description: 'The name of the action', + description: 'Comma separated list of events that should trigger the action', }, }; appkit.args .command('actions', 'List available actions on an app', require_app_option, listActions.bind(null, appkit)) - .command('actions:create', 'Create action on an app', create_action_option, createActions.bind(null, appkit)) - .command('actions:delete', 'Delete action on an app', delete_action_option, deleteAction.bind(null, appkit)) - .command('actions:update', 'update existing action on an app', update_action_option, updateAction.bind(null, appkit)); + .command('actions:create NAME', 'Create action on an app', create_action_option, createAction.bind(null, appkit)) + .command('actions:info ACTION', 'Get action info on an app', require_app_option, describeAction.bind(null, appkit)) + .command('actions:trigger ACTION', 'Trigger action on an app', trigger_action_option, triggerAction.bind(null, appkit)) + .command('actions:update ACTION', 'Update an action on an app', update_action_option, updateAction.bind(null, appkit)) + .command('actions:delete ACTION', 'Delete an action on an app', delete_action_option, deleteAction.bind(null, appkit)) + .command('actions:runs ACTION', 'List action runs on an app', require_app_option, listActionRuns.bind(null, appkit)) + .command('actions:runs:info ACTION RUN', 'Get info on a specific action run on an app', require_app_option, describeActionRun.bind(null, appkit)); }, update() { // do nothing. From 6402d9c375c8f85df0d7074785e97fab350d290a Mon Sep 17 00:00:00 2001 From: Sam Beckett Date: Wed, 25 May 2022 20:26:57 -0600 Subject: [PATCH 6/7] bump package version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 640788e..897fdca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "akkeris", - "version": "3.2.22", + "version": "3.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "akkeris", - "version": "3.2.22", + "version": "3.2.0", "license": "ISC", "dependencies": { "chart": "github:jstrace/chart", diff --git a/package.json b/package.json index 133c8be..58745ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "akkeris", - "version": "3.1.25", + "version": "3.2.0", "description": "Akkeris CLI", "main": "aka.js", "scripts": { From e681f3491d730247746376a50015e396f7539620 Mon Sep 17 00:00:00 2001 From: Sam Beckett Date: Mon, 12 Dec 2022 22:11:43 -0700 Subject: [PATCH 7/7] fetch logs for action run --- plugins/actions/index.js | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/plugins/actions/index.js b/plugins/actions/index.js index 3eaac40..976de1e 100644 --- a/plugins/actions/index.js +++ b/plugins/actions/index.js @@ -151,10 +151,10 @@ async function listActionRuns(appkit, args) { ui.div(md(`###${'-'.repeat(run.run_number.toString().length + run.action_run.length + 5 + 3)}###`)); ui.div(label('Status'), run.status); - ui.div(label('Started At'), (new Date(run.started_at)).toLocaleString()); + ui.div(label('Started At'), run.started_at ? (new Date(run.started_at)).toLocaleString() : md('###n/a###')); ui.div(label('Finished At'), run.finished_at ? (new Date(run.finished_at)).toLocaleString() : md('###n/a###')); ui.div(label('Source'), run.source); - ui.div(label('Exit Code'), run.exit_code || md('###n/a###')); + ui.div(label('Exit Code'), `${run.exit_code}` || md('###n/a###')); ui.div(); }); @@ -210,6 +210,39 @@ async function describeActionRun(appkit, args) { } } +/** + * getActionRunLogs - Get logs for a specific run + */ +async function getActionRunLogs(appkit, args) { + args.action = args.ACTION; + args.run = args.RUN; + try { + assert.ok(args.app && args.app !== '', 'An application name was not provided.'); + assert.ok(args.action && args.action !== '', 'An action was not provided.'); + assert.ok(args.run && args.run !== '', 'An action run was not provided.'); + } catch (err) { + appkit.terminal.error(err); + return; + } + + try { + const run = await appkit.api.get(`/apps/${args.app}/actions/${args.action}/runs/${args.run}`); + + if (!run) { + console.log(appkit.terminal.markdown(`\nNo action run matching ${args.run} for **${args.action}** was found.`)); + return; + } + + if (run.logs && run.logs.length > 0) { + console.log(run.logs); + } else { + console.log(appkit.terminal.markdown(`\nNo logs for run ${args.run} on **${args.action}** were found.`)); + } + } catch (err) { + appkit.terminal.print(err); + } +} + /** * createAction - Create a new action on an app */ @@ -533,7 +566,8 @@ module.exports = { .command('actions:update ACTION', 'Update an action on an app', update_action_option, updateAction.bind(null, appkit)) .command('actions:delete ACTION', 'Delete an action on an app', delete_action_option, deleteAction.bind(null, appkit)) .command('actions:runs ACTION', 'List action runs on an app', require_app_option, listActionRuns.bind(null, appkit)) - .command('actions:runs:info ACTION RUN', 'Get info on a specific action run on an app', require_app_option, describeActionRun.bind(null, appkit)); + .command('actions:runs:info ACTION RUN', 'Get info on a specific action run on an app', require_app_option, describeActionRun.bind(null, appkit)) + .command('actions:runs:logs ACTION RUN', 'Get logs on a specific action run on an app', require_app_option, getActionRunLogs.bind(null, appkit)); }, update() { // do nothing.