2021-05-29 19:48:52 -04:00

216 lines
6.5 KiB
TypeScript

import * as Sywac from 'sywac';
import * as Chalk from 'chalk';
import { camelize, decamelize, depascalize } from 'xcase';
import * as Gitbeaker from '@gitbeaker/node';
import { getAPIMap } from '@gitbeaker/core';
// Styling settings
const commandStyle = Chalk.hex('#e34329').bold;
const groupStyle = Chalk.hex('#fca325').bold;
const usageStyle = Chalk.hex('#fc6e26').bold;
const optionStyle = Chalk.white.bold;
const descriptionStyle = Chalk.hex('#848484');
const hintStyle = Chalk.hex('#6a5f88');
// Globally configurable arguments
function globalConfig(env = process.env): { [name: string]: Sywac.Options } {
return {
'gb-token': {
alias: 'gl-token',
desc: 'Your Gitlab Personal Token',
type: 'string',
defaultValue: env.GITBEAKER_TOKEN || env.GITLAB_TOKEN,
},
'gb-oauth-token': {
alias: 'gl-oauth-token',
desc: 'Your Gitlab OAuth Token',
type: 'string',
defaultValue: env.GITBEAKER_OAUTH_TOKEN || env.GITLAB_OAUTH_TOKEN,
},
'gb-job-token': {
alias: 'gl-job-token',
desc: 'Your Gitlab Job Token',
type: 'string',
defaultValue: env.GITBEAKER_JOB_TOKEN || env.GITLAB_JOB_TOKEN,
},
'gb-host': {
alias: 'gl-host',
desc: 'Your Gitlab API host (Defaults to https://www.gitlab.com)',
type: 'string',
defaultValue: env.GITBEAKER_HOST || env.GITLAB_HOST,
},
'gb-version': {
alias: 'gl-version',
desc: 'The targetted Gitlab API version (Defaults to 4)',
type: 'number',
defaultValue:
(env.GITBEAKER_VERSION && parseInt(env.GITBEAKER_VERSION, 10)) ||
(env.GITLAB_VERSION && parseInt(env.GITLAB_VERSION, 10)),
},
'gb-sudo': {
alias: 'gl-sudo',
desc: '[Sudo](https://docs.gitlab.com/ee/api/#sudo) query parameter',
type: 'string',
defaultValue: env.GITBEAKER_SUDO || env.GITLAB_SUDO,
},
'gb-camelize': {
alias: 'gl-camelize',
desc: 'Camelizes all response body keys',
type: 'boolean',
defaultValue:
(env.GITBEAKER_CAMELIZE && env.GITBEAKER_CAMELIZE === 'true') ||
(env.GITLAB_CAMELIZE && env.GITLAB_CAMELIZE === 'true'),
},
'gb-request-timeout': {
alias: 'gl-request-timeout',
desc: 'Timeout for API requests. Measured in ms',
type: 'number',
defaultValue:
(env.GITBEAKER_REQUEST_TIMEOUT && parseInt(env.GITBEAKER_REQUEST_TIMEOUT, 10)) ||
(env.GITBEAKER_REQUEST_TIMEOUT && parseInt(env.GITBEAKER_REQUEST_TIMEOUT, 10)),
},
'gb-profile-token': {
alias: 'gl-profile-token',
desc: '[Requests Profiles Token](https://docs.gitlab.com/ee/administration/monitoring/performance/request_profiling.html)',
type: 'string',
defaultValue: env.GITBEAKER_PROFILE_TOKEN || env.GITLAB_PROFILE_TOKEN,
},
'gb-profile-mode': {
alias: 'gl-profile-mode',
desc: '[Requests Profiles Token](https://docs.gitlab.com/ee/administration/monitoring/performance/request_profiling.html)',
type: 'string',
defaultValue: env.GITBEAKER_PROFILE_MODE || env.GITLAB_PROFILE_MODE,
},
};
}
// Options to be ignored when making an API call
const ignoreOptions = ['_', '$0', 'v', 'version', 'h', 'help', 'g', 'global-args'];
// Helper function to param case strings
function param(string) {
let cleaned = string;
// Handle exceptions
if (string.includes('GitLabCI')) cleaned = cleaned.replace('GitLabCI', 'Gitlabci');
if (string.includes('YML')) cleaned = cleaned.replace('YML', 'Yml');
if (string.includes('GPG')) cleaned = cleaned.replace('GPG', 'Gpg');
const attempt = decamelize(cleaned, '-');
return attempt !== cleaned ? attempt : depascalize(cleaned, '-');
}
function setupAPIMethods(setupArgs, methodArgs) {
methodArgs.forEach((name) => {
// if (name === 'options') return; CHECKME: Doesnt seem to be included :/
setupArgs.positional(`[--${param(name)}] <${param(name)}>`, {
group: 'Required Options',
type: 'string',
});
});
return setupArgs;
}
function runAPIMethod(ctx, args, apiName, method) {
const coreArgs = {};
const optionalArgs = {};
const initArgs = {};
Object.entries(args).forEach(([argName, value]) => {
if (ignoreOptions.includes(argName) || value == null) return;
const camelCased = camelize(argName.replace('gb-', '').replace('gl-', ''), '-');
if (globalConfig()[argName.replace('gl-', 'gb-')]) {
initArgs[camelCased] = value;
} else if (method.args.includes(camelCased)) coreArgs[camelCased] = value;
else optionalArgs[camelCased] = value;
});
// Create service
const s = new Gitbeaker[apiName](initArgs);
// Execute function
return s[method.name](...Object.values(coreArgs), optionalArgs)
.then((r) => {
ctx.output = JSON.stringify(r, null, 3);
})
.catch((e) => {
ctx.output = e;
});
}
function setupAPIs(setupArgs, apiName, methods) {
Object.entries(globalConfig()).forEach(([k, v]) => {
setupArgs.option(`${k} <value>`, {
group: 'Base Options',
...v,
});
});
for (let i = 1; i < methods.length; i += 1) {
const method = methods[i];
setupArgs.command(param(method.name), {
setup: (setupMethodArgs) => setupAPIMethods(setupMethodArgs, method.args),
run: (args, ctx) => runAPIMethod(ctx, args, apiName, method),
});
}
return setupArgs;
}
// Add default settings
const cli = Sywac.version('-v, --version')
.help('-h, --help')
.showHelpByDefault()
.epilogue('Copyright 2019')
.style({
usagePrefix: (s) => usageStyle(s),
group: (s) => groupStyle(s),
flags: (s) => optionStyle(s),
usageCommandPlaceholder: (s) => commandStyle(s),
usageOptionsPlaceholder: (s) => optionStyle(s),
desc: (s) => descriptionStyle(s),
hints: (s) => hintStyle(s),
});
// Add Global commands
cli.boolean('-g --global-args', {
desc: 'Show global arguments currently set in the enviroment variables',
});
cli.command('*', (argv, ctx) => {
if (!argv.g) return;
const display = {};
Object.entries(globalConfig()).forEach(([k, v]) => {
if (v.defaultValue == null) return;
display[k] = {
alias: v.alias,
description: v.desc,
value: v.defaultValue,
};
});
ctx.output =
Object.keys(display).length === 0
? 'No global variables have been set!'
: JSON.stringify(display, null, 3);
});
// Add all supported API's
Object.entries(getAPIMap()).forEach(([apiName, methods]) => {
cli.command(param(apiName), {
desc: `The ${apiName} API`,
setup: (setupArgs) => setupAPIs(setupArgs, apiName, methods),
});
});
export { cli };