Swapped tslint to eslint and fixed files
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module",
|
||||||
|
"ecmaVersion": 2017
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"extends": ["prettier"],
|
||||||
|
"plugins": ["@typescript-eslint", "prettier"],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": ["error"],
|
||||||
|
"no-return-await": "error"
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-5
@@ -9,11 +9,10 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"format": "prettier --write **/*.{js,ts,tsx,json}",
|
"format": "prettier --write **/*.{js,ts,tsx,json}",
|
||||||
"lint": "tslint -p .",
|
|
||||||
"clean-build": "rm -rf ./build",
|
"clean-build": "rm -rf ./build",
|
||||||
"compile": "tsc -p .",
|
"compile": "tsc -p .",
|
||||||
"copy-templates": "if [ -e ./src/templates ]; then cp -a ./src/templates ./build/; fi",
|
"copy-templates": "if [ -e ./src/templates ]; then cp -a ./src/templates ./build/; fi",
|
||||||
"build": "yarn format && yarn lint && yarn clean-build && yarn compile && yarn copy-templates",
|
"build": "yarn format && yarn clean-build && yarn compile && yarn copy-templates",
|
||||||
"prepublishOnly": "yarn build",
|
"prepublishOnly": "yarn build",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"watch": "jest --watch",
|
"watch": "jest --watch",
|
||||||
@@ -48,9 +47,11 @@
|
|||||||
"prettier": "^1.12.1",
|
"prettier": "^1.12.1",
|
||||||
"ts-jest": "^24.1.0",
|
"ts-jest": "^24.1.0",
|
||||||
"ts-node": "^8.4.1",
|
"ts-node": "^8.4.1",
|
||||||
"tslint": "^5.12.0",
|
"@typescript-eslint/eslint-plugin": "^5.44.0",
|
||||||
"tslint-config-prettier": "^1.17.0",
|
"@typescript-eslint/parser": "^5.44.0",
|
||||||
"tslint-config-standard": "^8.0.1",
|
"eslint": "^8.28.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"typescript": "^3.6.3"
|
"typescript": "^3.6.3"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu'
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
import chalk = require('chalk');
|
import chalk = require('chalk');
|
||||||
import { confirmMessage, defaultMenuSettings } from '../../../globals';
|
import { confirmMessage, defaultMenuSettings } from '../../../globals';
|
||||||
const { AutoComplete } = require('enquirer');
|
const { AutoComplete } = require('enquirer');
|
||||||
@@ -10,10 +10,11 @@ module.exports = {
|
|||||||
description: 'Tail a log in cloudwatch (l) (WIP)',
|
description: 'Tail a log in cloudwatch (l) (WIP)',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
run: async (toolbox: GluegunMenuToolbox) => {
|
run: async (toolbox: GluegunMenuToolbox) => {
|
||||||
|
const { system, strings } = toolbox;
|
||||||
|
|
||||||
const { system, strings } = toolbox
|
const groups = JSON.parse(
|
||||||
|
strings.trim(await system.run(`aws logs describe-log-groups`))
|
||||||
const groups = JSON.parse(strings.trim(await system.run(`aws logs describe-log-groups`)));
|
);
|
||||||
|
|
||||||
const options = groups.logGroups.map(item => {
|
const options = groups.logGroups.map(item => {
|
||||||
return item.logGroupName;
|
return item.logGroupName;
|
||||||
@@ -29,16 +30,23 @@ module.exports = {
|
|||||||
|
|
||||||
const logGroup = await prompt.run();
|
const logGroup = await prompt.run();
|
||||||
if (await confirmMessage(`Tail logs from ${chalk.yellow(logGroup)}?`)) {
|
if (await confirmMessage(`Tail logs from ${chalk.yellow(logGroup)}?`)) {
|
||||||
const child = spawn('aws', ['logs', 'tail', `${logGroup}`, '--follow', '--format', 'short']);
|
const child = spawn('aws', [
|
||||||
|
'logs',
|
||||||
|
'tail',
|
||||||
|
`${logGroup}`,
|
||||||
|
'--follow',
|
||||||
|
'--format',
|
||||||
|
'short'
|
||||||
|
]);
|
||||||
child.stdout.setEncoding('utf8');
|
child.stdout.setEncoding('utf8');
|
||||||
child.stdout.on('data', function(data) {
|
child.stdout.on('data', function(data) {
|
||||||
// Here is where the output goes
|
// Here is where the output goes
|
||||||
console.log(data);
|
console.log(data);
|
||||||
});
|
});
|
||||||
child.stderr.on('data', (data) => {
|
child.stderr.on('data', data => {
|
||||||
console.error(`stderr: ${data}`);
|
console.error(`stderr: ${data}`);
|
||||||
});
|
});
|
||||||
child.on('close', (code) => {
|
child.on('close', code => {
|
||||||
console.log(`child process exited with code ${code}`);
|
console.log(`child process exited with code ${code}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -47,4 +55,4 @@ module.exports = {
|
|||||||
await toolbox.menu.showMenu('aws', defaultMenuSettings);
|
await toolbox.menu.showMenu('aws', defaultMenuSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu'
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
import chalk = require('chalk');
|
import chalk = require('chalk');
|
||||||
import { defaultMenuSettings, sleep } from '../../../globals';
|
import { defaultMenuSettings, sleep } from '../../../globals';
|
||||||
const { AutoComplete } = require('enquirer');
|
const { AutoComplete } = require('enquirer');
|
||||||
@@ -9,7 +9,6 @@ module.exports = {
|
|||||||
description: 'Parameter store functions (ps)',
|
description: 'Parameter store functions (ps)',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
run: async (toolbox: GluegunMenuToolbox) => {
|
run: async (toolbox: GluegunMenuToolbox) => {
|
||||||
|
|
||||||
const { system, strings, print, filesystem } = toolbox;
|
const { system, strings, print, filesystem } = toolbox;
|
||||||
|
|
||||||
// Check for cache in /tmp folder
|
// Check for cache in /tmp folder
|
||||||
@@ -19,21 +18,38 @@ module.exports = {
|
|||||||
|
|
||||||
let nextToken = '';
|
let nextToken = '';
|
||||||
if (valueNames.length === 0) {
|
if (valueNames.length === 0) {
|
||||||
const spinner = print.spin(`Downloading parameter names (${chalk.red('0')})`);
|
const spinner = print.spin(
|
||||||
|
`Downloading parameter names (${chalk.red('0')})`
|
||||||
|
);
|
||||||
while (nextToken !== null) {
|
while (nextToken !== null) {
|
||||||
const values = JSON.parse(
|
const values = JSON.parse(
|
||||||
strings.trim(
|
strings.trim(
|
||||||
await system.run(`aws ssm describe-parameters --output json --max-items 50 ${(nextToken !== '') ? `--starting-token ${nextToken}` : ''}`)));
|
await system.run(
|
||||||
|
`aws ssm describe-parameters --output json --max-items 50 ${
|
||||||
|
nextToken !== '' ? `--starting-token ${nextToken}` : ''
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
await sleep(500);
|
await sleep(500);
|
||||||
valueNames.push(...values.Parameters.map(({Name}) => { return Name; }));
|
valueNames.push(
|
||||||
|
...values.Parameters.map(({ Name }) => {
|
||||||
|
return Name;
|
||||||
|
})
|
||||||
|
);
|
||||||
nextToken = values.NextToken || null;
|
nextToken = values.NextToken || null;
|
||||||
spinner.text = `Downloading parameter names (${chalk.red(valueNames.length)})`
|
spinner.text = `Downloading parameter names (${chalk.red(
|
||||||
|
valueNames.length
|
||||||
|
)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner.text = 'Saving parameter names to /tmp/.pwcli_parametercache';
|
spinner.text = 'Saving parameter names to /tmp/.pwcli_parametercache';
|
||||||
|
|
||||||
filesystem.file('/tmp/.pwcli_parametercache', {mode: '600', content: JSON.stringify(valueNames)});
|
filesystem.file('/tmp/.pwcli_parametercache', {
|
||||||
|
mode: '600',
|
||||||
|
content: JSON.stringify(valueNames)
|
||||||
|
});
|
||||||
await sleep(2000);
|
await sleep(2000);
|
||||||
|
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
@@ -51,8 +67,18 @@ module.exports = {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const parameterName = await prompt.run();
|
const parameterName = await prompt.run();
|
||||||
const parameterValue = JSON.parse(strings.trim(await system.run(`aws ssm get-parameter --name ${parameterName} --with-decryption`)));
|
const parameterValue = JSON.parse(
|
||||||
print.success(`${chalk.green(parameterName)} value is [${chalk.yellow(parameterValue.Parameter.Value)}]`);
|
strings.trim(
|
||||||
|
await system.run(
|
||||||
|
`aws ssm get-parameter --name ${parameterName} --with-decryption`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
print.success(
|
||||||
|
`${chalk.green(parameterName)} value is [${chalk.yellow(
|
||||||
|
parameterValue.Parameter.Value
|
||||||
|
)}]`
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
parameterName = 'exit';
|
parameterName = 'exit';
|
||||||
}
|
}
|
||||||
@@ -62,4 +88,4 @@ module.exports = {
|
|||||||
await toolbox.menu.showMenu('aws', defaultMenuSettings);
|
await toolbox.menu.showMenu('aws', defaultMenuSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu'
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
import { exit } from 'process';
|
import { exit } from 'process';
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const { AutoComplete, Input, Confirm } = require('enquirer');
|
const { AutoComplete, Input, Confirm } = require('enquirer');
|
||||||
@@ -6,18 +6,20 @@ import { defaultMenuSettings, getSettings } from '../../../../globals';
|
|||||||
import { getRepoBranches } from '../../../../services/github_rest';
|
import { getRepoBranches } from '../../../../services/github_rest';
|
||||||
|
|
||||||
type Stack = {
|
type Stack = {
|
||||||
name: string
|
name: string;
|
||||||
status: string
|
status: string;
|
||||||
drift_status: string
|
drift_status: string;
|
||||||
branch: string
|
branch: string;
|
||||||
updated: string
|
updated: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getConfirmation = async (branchName: string, stackName: string) => {
|
const getConfirmation = async (branchName: string, stackName: string) => {
|
||||||
const url = stackName.replace('photowall-', '');
|
const url = stackName.replace('photowall-', '');
|
||||||
const confirm = new Confirm({
|
const confirm = new Confirm({
|
||||||
name: 'confirm',
|
name: 'confirm',
|
||||||
message: `Will create ${chalk.yellow(url + '.photowall-test.xx')} and publish ${chalk.green(branchName)} to it, continue?`
|
message: `Will create ${chalk.yellow(
|
||||||
|
url + '.photowall-test.xx'
|
||||||
|
)} and publish ${chalk.green(branchName)} to it, continue?`
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -26,7 +28,7 @@ const getConfirmation = async (branchName: string, stackName: string) => {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'create',
|
name: 'create',
|
||||||
@@ -38,7 +40,7 @@ module.exports = {
|
|||||||
|
|
||||||
// Load settings
|
// Load settings
|
||||||
const config = await getSettings(toolbox);
|
const config = await getSettings(toolbox);
|
||||||
if (!config || (!config.photowall_repo || config.photowall_repo === "")) {
|
if (!config || !config.photowall_repo || config.photowall_repo === '') {
|
||||||
print.error(`
|
print.error(`
|
||||||
|
|
||||||
The create command requires that you set an absolute path to your local photowall repository.
|
The create command requires that you set an absolute path to your local photowall repository.
|
||||||
@@ -48,7 +50,12 @@ module.exports = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const branches = await getRepoBranches(toolbox, config.gh_token, 'Photowall', 'photowall');
|
const branches = await getRepoBranches(
|
||||||
|
toolbox,
|
||||||
|
config.gh_token,
|
||||||
|
'Photowall',
|
||||||
|
'photowall'
|
||||||
|
);
|
||||||
const branchOptions = branches.map(item => item.name);
|
const branchOptions = branches.map(item => item.name);
|
||||||
|
|
||||||
const promptBranch = new AutoComplete({
|
const promptBranch = new AutoComplete({
|
||||||
@@ -62,7 +69,7 @@ module.exports = {
|
|||||||
|
|
||||||
const stacks = JSON.parse(
|
const stacks = JSON.parse(
|
||||||
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
||||||
)
|
);
|
||||||
const teststacks: Stack[] = await Promise.all(
|
const teststacks: Stack[] = await Promise.all(
|
||||||
stacks.StackSummaries.map(async item => {
|
stacks.StackSummaries.map(async item => {
|
||||||
if (
|
if (
|
||||||
@@ -82,24 +89,24 @@ module.exports = {
|
|||||||
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
const branch = pipeline.pipeline.stages.filter(stage => {
|
const branch = pipeline.pipeline.stages.filter(stage => {
|
||||||
return stage.name === 'Source'
|
return stage.name === 'Source';
|
||||||
})[0].actions[0].configuration.Branch
|
})[0].actions[0].configuration.Branch;
|
||||||
const lastUpdated = pipeline.metadata.updated
|
const lastUpdated = pipeline.metadata.updated;
|
||||||
return {
|
return {
|
||||||
name: item.StackName,
|
name: item.StackName,
|
||||||
status: item.StackStatus,
|
status: item.StackStatus,
|
||||||
drift_status: item.DriftInformation.StackDriftStatus,
|
drift_status: item.DriftInformation.StackDriftStatus,
|
||||||
branch: branch,
|
branch: branch,
|
||||||
updated: lastUpdated
|
updated: lastUpdated
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
return Promise.resolve(null);
|
||||||
return Promise.resolve(null)
|
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
|
|
||||||
const curratedStacks: Stack[] = teststacks.filter(Boolean)
|
const curratedStacks: Stack[] = teststacks.filter(Boolean);
|
||||||
const options = curratedStacks.map(item => {
|
const options = curratedStacks.map(item => {
|
||||||
return item.name;
|
return item.name;
|
||||||
});
|
});
|
||||||
@@ -110,12 +117,14 @@ module.exports = {
|
|||||||
message: `Subdomain of new server (${chalk.yellow('test-xxx')})`
|
message: `Subdomain of new server (${chalk.yellow('test-xxx')})`
|
||||||
});
|
});
|
||||||
const stacksuffix = await input.run();
|
const stacksuffix = await input.run();
|
||||||
const stackName = `photowall-${stacksuffix}`
|
const stackName = `photowall-${stacksuffix}`;
|
||||||
|
|
||||||
if (options.includes(stackName)) {
|
if (options.includes(stackName)) {
|
||||||
print.error(`
|
print.error(`
|
||||||
|
|
||||||
The stack ${chalk.yellow(stackName)} is already taken, try the update command instead.
|
The stack ${chalk.yellow(
|
||||||
|
stackName
|
||||||
|
)} is already taken, try the update command instead.
|
||||||
|
|
||||||
`);
|
`);
|
||||||
if (toolbox.fromMenu()) {
|
if (toolbox.fromMenu()) {
|
||||||
@@ -126,11 +135,16 @@ module.exports = {
|
|||||||
} else if (stackName.indexOf('photowall-test-') !== 0) {
|
} else if (stackName.indexOf('photowall-test-') !== 0) {
|
||||||
print.error(`
|
print.error(`
|
||||||
|
|
||||||
The stack must begin with ${chalk.yellow('test-')} and then something after like ${chalk.yellow('test-yourname-1')}.
|
The stack must begin with ${chalk.yellow(
|
||||||
|
'test-'
|
||||||
|
)} and then something after like ${chalk.yellow('test-yourname-1')}.
|
||||||
|
|
||||||
`);
|
`);
|
||||||
if (toolbox.fromMenu()) {
|
if (toolbox.fromMenu()) {
|
||||||
await toolbox.menu.showMenu('deployment testservers', defaultMenuSettings);
|
await toolbox.menu.showMenu(
|
||||||
|
'deployment testservers',
|
||||||
|
defaultMenuSettings
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
@@ -141,8 +155,15 @@ module.exports = {
|
|||||||
|
|
||||||
if (shouldRun) {
|
if (shouldRun) {
|
||||||
const cmd = `make create-stack STACK_ENVIRONMENT_NAME=${stacksuffix} GITHUB_BRANCH=${branchName}`;
|
const cmd = `make create-stack STACK_ENVIRONMENT_NAME=${stacksuffix} GITHUB_BRANCH=${branchName}`;
|
||||||
print.fancy(`Running [${chalk.yellow(cmd)}] in folder [${`${config.photowall_repo}/cloudformation/`}]`);
|
print.fancy(
|
||||||
const output = await system.run(cmd, {cwd: `${config.photowall_repo}/cloudformation/`, trim: true});
|
`Running [${chalk.yellow(
|
||||||
|
cmd
|
||||||
|
)}] in folder [${`${config.photowall_repo}/cloudformation/`}]`
|
||||||
|
);
|
||||||
|
const output = await system.run(cmd, {
|
||||||
|
cwd: `${config.photowall_repo}/cloudformation/`,
|
||||||
|
trim: true
|
||||||
|
});
|
||||||
print.fancy(output);
|
print.fancy(output);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -150,7 +171,10 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (toolbox.fromMenu()) {
|
if (toolbox.fromMenu()) {
|
||||||
await toolbox.menu.showMenu('deployment testservers', defaultMenuSettings);
|
await toolbox.menu.showMenu(
|
||||||
}
|
'deployment testservers',
|
||||||
|
defaultMenuSettings
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu'
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
import { GluegunSystem } from 'gluegun';
|
import { GluegunSystem } from 'gluegun';
|
||||||
import { defaultMenuSettings } from '../../../../globals';
|
import { defaultMenuSettings } from '../../../../globals';
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const { AutoComplete, Input, Confirm } = require('enquirer');
|
const { AutoComplete, Input, Confirm } = require('enquirer');
|
||||||
|
|
||||||
type Stack = {
|
type Stack = {
|
||||||
name: string,
|
name: string;
|
||||||
status: string,
|
status: string;
|
||||||
drift_status: string,
|
drift_status: string;
|
||||||
branch: string,
|
branch: string;
|
||||||
updated: string,
|
updated: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getConfirmation = async (stackName: string) => {
|
const getConfirmation = async (stackName: string) => {
|
||||||
const confirm = new Confirm({
|
const confirm = new Confirm({
|
||||||
name: 'confirm',
|
name: 'confirm',
|
||||||
message: `The stack ${chalk.yellow(stackName)} will be ${chalk.red.bold('deleted')}`
|
message: `The stack ${chalk.yellow(stackName)} will be ${chalk.red.bold(
|
||||||
|
'deleted'
|
||||||
|
)}`
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -23,12 +25,12 @@ const getConfirmation = async (stackName: string) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const confirmCommand = async (cmd: string) => {
|
const confirmCommand = async (cmd: string) => {
|
||||||
const confirm = new Confirm({
|
const confirm = new Confirm({
|
||||||
name: 'confirm',
|
name: 'confirm',
|
||||||
message: `RUN COMMAND > [${chalk.yellow(cmd)}] ?`,
|
message: `RUN COMMAND > [${chalk.yellow(cmd)}] ?`
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -40,7 +42,7 @@ const confirmCommand = async (cmd: string) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const runCommand = async (system: GluegunSystem, cmd: string | null) => {
|
const runCommand = async (system: GluegunSystem, cmd: string | null) => {
|
||||||
if (cmd) {
|
if (cmd) {
|
||||||
@@ -55,12 +57,11 @@ module.exports = {
|
|||||||
description: 'Choose a testserver to delete (d)',
|
description: 'Choose a testserver to delete (d)',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
run: async (toolbox: GluegunMenuToolbox) => {
|
run: async (toolbox: GluegunMenuToolbox) => {
|
||||||
|
|
||||||
const { system, strings, print } = toolbox;
|
const { system, strings, print } = toolbox;
|
||||||
|
|
||||||
const stacks = JSON.parse(
|
const stacks = JSON.parse(
|
||||||
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
||||||
)
|
);
|
||||||
const teststacks: Stack[] = await Promise.all(
|
const teststacks: Stack[] = await Promise.all(
|
||||||
stacks.StackSummaries.map(async item => {
|
stacks.StackSummaries.map(async item => {
|
||||||
if (
|
if (
|
||||||
@@ -80,27 +81,27 @@ module.exports = {
|
|||||||
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
const branch = pipeline.pipeline.stages.filter(stage => {
|
const branch = pipeline.pipeline.stages.filter(stage => {
|
||||||
return stage.name === 'Source'
|
return stage.name === 'Source';
|
||||||
})[0].actions[0].configuration.Branch
|
})[0].actions[0].configuration.Branch;
|
||||||
const lastUpdated = pipeline.metadata.updated
|
const lastUpdated = pipeline.metadata.updated;
|
||||||
return {
|
return {
|
||||||
name: item.StackName,
|
name: item.StackName,
|
||||||
status: item.StackStatus,
|
status: item.StackStatus,
|
||||||
drift_status: item.DriftInformation.StackDriftStatus,
|
drift_status: item.DriftInformation.StackDriftStatus,
|
||||||
branch: branch,
|
branch: branch,
|
||||||
updated: lastUpdated
|
updated: lastUpdated
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
return Promise.resolve(null);
|
||||||
return Promise.resolve(null)
|
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
|
|
||||||
const curratedStacks: Stack[] = teststacks.filter(Boolean)
|
const curratedStacks: Stack[] = teststacks.filter(Boolean);
|
||||||
const options = curratedStacks.map(item => {
|
const options = curratedStacks.map(item => {
|
||||||
return item.name
|
return item.name;
|
||||||
})
|
});
|
||||||
|
|
||||||
const prompt = new AutoComplete({
|
const prompt = new AutoComplete({
|
||||||
name: 'testserver',
|
name: 'testserver',
|
||||||
@@ -115,20 +116,50 @@ module.exports = {
|
|||||||
const shouldRun = await getConfirmation(stackName);
|
const shouldRun = await getConfirmation(stackName);
|
||||||
|
|
||||||
if (shouldRun) {
|
if (shouldRun) {
|
||||||
await runCommand(system, await confirmCommand(`aws cloudformation delete-stack --stack-name ${stackName}`));
|
await runCommand(
|
||||||
await runCommand(system, await confirmCommand(`aws ecr delete-repository --force --repository-name ${stackName}`));
|
system,
|
||||||
await runCommand(system, await confirmCommand(`aws logs delete-log-group --log-group-name /aws/codebuild/${stackName}`));
|
await confirmCommand(
|
||||||
await runCommand(system, await confirmCommand(`aws logs delete-log-group --log-group-name ${stackName}`));
|
`aws cloudformation delete-stack --stack-name ${stackName}`
|
||||||
const artifacts = strings.trim(
|
)
|
||||||
await system.run(`aws s3 ls | grep ${stackName}-artifactbucket | awk '{print $3}'`)
|
);
|
||||||
).split('\n');
|
await runCommand(
|
||||||
|
system,
|
||||||
|
await confirmCommand(
|
||||||
|
`aws ecr delete-repository --force --repository-name ${stackName}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
await runCommand(
|
||||||
|
system,
|
||||||
|
await confirmCommand(
|
||||||
|
`aws logs delete-log-group --log-group-name /aws/codebuild/${stackName}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
await runCommand(
|
||||||
|
system,
|
||||||
|
await confirmCommand(
|
||||||
|
`aws logs delete-log-group --log-group-name ${stackName}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const artifacts = strings
|
||||||
|
.trim(
|
||||||
|
await system.run(
|
||||||
|
`aws s3 ls | grep ${stackName}-artifactbucket | awk '{print $3}'`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.split('\n');
|
||||||
for (const artifact of artifacts) {
|
for (const artifact of artifacts) {
|
||||||
await runCommand(system, await confirmCommand(`aws s3 rb --force s3://${artifact}`));
|
await runCommand(
|
||||||
|
system,
|
||||||
|
await confirmCommand(`aws s3 rb --force s3://${artifact}`)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbox.fromMenu()) {
|
if (toolbox.fromMenu()) {
|
||||||
await toolbox.menu.showMenu('deployment testservers', defaultMenuSettings);
|
await toolbox.menu.showMenu(
|
||||||
}
|
'deployment testservers',
|
||||||
|
defaultMenuSettings
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu'
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
import chalk = require('chalk');
|
import chalk = require('chalk');
|
||||||
import { defaultMenuSettings } from '../../../../globals';
|
import { defaultMenuSettings } from '../../../../globals';
|
||||||
const tto = require('terminal-table-output').create();
|
const tto = require('terminal-table-output').create();
|
||||||
|
|
||||||
type Stack = {
|
type Stack = {
|
||||||
name: string,
|
name: string;
|
||||||
status: string,
|
status: string;
|
||||||
drift_status: string,
|
drift_status: string;
|
||||||
branch: string,
|
branch: string;
|
||||||
updated: string,
|
updated: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -17,39 +17,59 @@ module.exports = {
|
|||||||
description: 'Show status for available testservers (s)',
|
description: 'Show status for available testservers (s)',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
run: async (toolbox: GluegunMenuToolbox) => {
|
run: async (toolbox: GluegunMenuToolbox) => {
|
||||||
|
const { system, strings } = toolbox;
|
||||||
|
|
||||||
const { system, strings } = toolbox
|
const stacks = JSON.parse(
|
||||||
|
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
||||||
const stacks = JSON.parse(strings.trim(await system.run(`aws cloudformation list-stacks`)));
|
);
|
||||||
const teststacks: Stack[] = await Promise.all(stacks.StackSummaries.map(async (item) => {
|
const teststacks: Stack[] = await Promise.all(
|
||||||
if (item.StackName.indexOf('photowall-test-') > -1 && ['CREATE_COMPLETE', 'UPDATE_COMPLETE', 'UPDATE_IN_PROGRESS', 'UPDATE_FAILED', 'CREATE_FAILED', 'CREATE_IN_PROGRESS'].includes(item.StackStatus)) {
|
stacks.StackSummaries.map(async item => {
|
||||||
|
if (
|
||||||
|
item.StackName.indexOf('photowall-test-') > -1 &&
|
||||||
|
[
|
||||||
|
'CREATE_COMPLETE',
|
||||||
|
'UPDATE_COMPLETE',
|
||||||
|
'UPDATE_IN_PROGRESS',
|
||||||
|
'UPDATE_FAILED',
|
||||||
|
'CREATE_FAILED',
|
||||||
|
'CREATE_IN_PROGRESS'
|
||||||
|
].includes(item.StackStatus)
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const pipeline = JSON.parse(strings.trim(await system.run(`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`)));
|
const pipeline = JSON.parse(
|
||||||
|
strings.trim(
|
||||||
|
await system.run(
|
||||||
|
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
const sourceStage = pipeline.pipeline.stages.filter(stage => {
|
const sourceStage = pipeline.pipeline.stages.filter(stage => {
|
||||||
return stage.name === 'Source';
|
return stage.name === 'Source';
|
||||||
});
|
});
|
||||||
const branch = sourceStage[0].actions[0].configuration.Branch ?? sourceStage[0].actions[0].configuration.BranchName;
|
const branch =
|
||||||
|
sourceStage[0].actions[0].configuration.Branch ??
|
||||||
|
sourceStage[0].actions[0].configuration.BranchName;
|
||||||
const lastUpdated = pipeline.metadata.updated;
|
const lastUpdated = pipeline.metadata.updated;
|
||||||
return {
|
return {
|
||||||
name: item.StackName,
|
name: item.StackName,
|
||||||
status: item.StackStatus,
|
status: item.StackStatus,
|
||||||
drift_status: item.DriftInformation.StackDriftStatus,
|
drift_status: item.DriftInformation.StackDriftStatus,
|
||||||
branch: branch,
|
branch: branch,
|
||||||
updated: lastUpdated,
|
updated: lastUpdated
|
||||||
}
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {
|
return {
|
||||||
name: item.StackName,
|
name: item.StackName,
|
||||||
status: 'NO_STATUS',
|
status: 'NO_STATUS',
|
||||||
drift_status: '',
|
drift_status: '',
|
||||||
branch: '',
|
branch: '',
|
||||||
updated: '',
|
updated: ''
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
return Promise.resolve(null);
|
return Promise.resolve(null);
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const curratedStacks: Stack[] = teststacks.filter(Boolean);
|
const curratedStacks: Stack[] = teststacks.filter(Boolean);
|
||||||
|
|
||||||
@@ -67,10 +87,12 @@ module.exports = {
|
|||||||
case 'CREATE_COMPLETE':
|
case 'CREATE_COMPLETE':
|
||||||
return `(${chalk.greenBright('OK')})`;
|
return `(${chalk.greenBright('OK')})`;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const stackUrl = (stackname: string) => {
|
const stackUrl = (stackname: string) => {
|
||||||
return `https://test-${stackname.split('photowall-test-')[1]}.photowall-test.com/us`;
|
return `https://test-${
|
||||||
|
stackname.split('photowall-test-')[1]
|
||||||
|
}.photowall-test.com/us`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ellipsis = (a: string, max: number) => {
|
const ellipsis = (a: string, max: number) => {
|
||||||
@@ -88,30 +110,31 @@ module.exports = {
|
|||||||
let interval = seconds / 31536000;
|
let interval = seconds / 31536000;
|
||||||
|
|
||||||
if (interval > 1) {
|
if (interval > 1) {
|
||||||
return Math.floor(interval) + " years";
|
return Math.floor(interval) + ' years';
|
||||||
}
|
}
|
||||||
interval = seconds / 2592000;
|
interval = seconds / 2592000;
|
||||||
if (interval > 1) {
|
if (interval > 1) {
|
||||||
return `${chalk.red(Math.floor(interval) + " months")}`;
|
return `${chalk.red(Math.floor(interval) + ' months')}`;
|
||||||
}
|
}
|
||||||
interval = seconds / 86400;
|
interval = seconds / 86400;
|
||||||
if (interval > 1) {
|
if (interval > 1) {
|
||||||
const days = Math.floor(interval);
|
const days = Math.floor(interval);
|
||||||
return `${chalk[days >= 14 ? 'yellow' : 'green'](days + " days")}`;
|
return `${chalk[days >= 14 ? 'yellow' : 'green'](days + ' days')}`;
|
||||||
}
|
}
|
||||||
interval = seconds / 3600;
|
interval = seconds / 3600;
|
||||||
if (interval > 1) {
|
if (interval > 1) {
|
||||||
return `${chalk.green(Math.floor(interval) + " hours")}`;
|
return `${chalk.green(Math.floor(interval) + ' hours')}`;
|
||||||
}
|
}
|
||||||
interval = seconds / 60;
|
interval = seconds / 60;
|
||||||
if (interval > 1) {
|
if (interval > 1) {
|
||||||
return `${chalk.green(Math.floor(interval) + " minutes")}`;
|
return `${chalk.green(Math.floor(interval) + ' minutes')}`;
|
||||||
}
|
|
||||||
return Math.floor(seconds) + " seconds";
|
|
||||||
}
|
}
|
||||||
|
return Math.floor(seconds) + ' seconds';
|
||||||
|
};
|
||||||
|
|
||||||
tto.line('-');
|
tto.line('-');
|
||||||
tto.pushrow(['| status', 'stack', 'branch', 'time since update', 'url'])
|
tto
|
||||||
|
.pushrow(['| status', 'stack', 'branch', 'time since update', 'url'])
|
||||||
.line('-');
|
.line('-');
|
||||||
// Sort on stackname
|
// Sort on stackname
|
||||||
curratedStacks.sort((a, b) => {
|
curratedStacks.sort((a, b) => {
|
||||||
@@ -121,12 +144,21 @@ module.exports = {
|
|||||||
});
|
});
|
||||||
for (const stack of curratedStacks) {
|
for (const stack of curratedStacks) {
|
||||||
const t = new Date(stack.updated);
|
const t = new Date(stack.updated);
|
||||||
tto.pushrow([status(stack.status), stack.name, ellipsis(stack.branch, 24), timeSince(t.getTime()), stackUrl(stack.name)]);
|
tto.pushrow([
|
||||||
|
status(stack.status),
|
||||||
|
stack.name,
|
||||||
|
ellipsis(stack.branch, 24),
|
||||||
|
timeSince(t.getTime()),
|
||||||
|
stackUrl(stack.name)
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
tto.print(true);
|
tto.print(true);
|
||||||
|
|
||||||
if (toolbox.fromMenu()) {
|
if (toolbox.fromMenu()) {
|
||||||
await toolbox.menu.showMenu('deployment testservers', defaultMenuSettings);
|
await toolbox.menu.showMenu(
|
||||||
}
|
'deployment testservers',
|
||||||
|
defaultMenuSettings
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,26 +1,35 @@
|
|||||||
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu'
|
import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
||||||
const chalk = require('chalk');
|
const chalk = require('chalk');
|
||||||
const { AutoComplete, Input, Confirm } = require('enquirer');
|
const { AutoComplete, Input, Confirm } = require('enquirer');
|
||||||
import { Octokit } from "@octokit/core";
|
import { Octokit } from '@octokit/core';
|
||||||
import { exit } from 'process';
|
import { exit } from 'process';
|
||||||
import { defaultMenuSettings, getCurrentBranch, getSettings, sleep } from '../../../../globals';
|
import {
|
||||||
|
defaultMenuSettings,
|
||||||
|
getCurrentBranch,
|
||||||
|
getSettings,
|
||||||
|
sleep
|
||||||
|
} from '../../../../globals';
|
||||||
|
|
||||||
type Stack = {
|
type Stack = {
|
||||||
name: string
|
name: string;
|
||||||
status: string
|
status: string;
|
||||||
drift_status: string
|
drift_status: string;
|
||||||
branch: string
|
branch: string;
|
||||||
updated: string
|
updated: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
const getBranchName = async (toolbox: GluegunMenuToolbox, token: string, repoPath: string) => {
|
const getBranchName = async (
|
||||||
|
toolbox: GluegunMenuToolbox,
|
||||||
|
token: string,
|
||||||
|
repoPath: string
|
||||||
|
) => {
|
||||||
const { print } = toolbox;
|
const { print } = toolbox;
|
||||||
const currentBranch = await getCurrentBranch(repoPath, toolbox);
|
const currentBranch = await getCurrentBranch(repoPath, toolbox);
|
||||||
const input = new Input({
|
const input = new Input({
|
||||||
type: 'input',
|
type: 'input',
|
||||||
name: 'branch',
|
name: 'branch',
|
||||||
message: 'What github branch should be deployed?',
|
message: 'What github branch should be deployed?',
|
||||||
initial: currentBranch,
|
initial: currentBranch
|
||||||
});
|
});
|
||||||
const responseBranch = await input.run();
|
const responseBranch = await input.run();
|
||||||
|
|
||||||
@@ -28,11 +37,14 @@ const getBranchName = async (toolbox: GluegunMenuToolbox, token: string, repoPat
|
|||||||
const octokit = new Octokit({ auth: token });
|
const octokit = new Octokit({ auth: token });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await octokit.request("GET /repos/{owner}/{repo}/branches/{branch}", {
|
const response = await octokit.request(
|
||||||
owner: "photowall",
|
'GET /repos/{owner}/{repo}/branches/{branch}',
|
||||||
repo: "photowall",
|
{
|
||||||
|
owner: 'photowall',
|
||||||
|
repo: 'photowall',
|
||||||
branch: responseBranch
|
branch: responseBranch
|
||||||
});
|
}
|
||||||
|
);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return responseBranch;
|
return responseBranch;
|
||||||
}
|
}
|
||||||
@@ -45,7 +57,9 @@ const getBranchName = async (toolbox: GluegunMenuToolbox, token: string, repoPat
|
|||||||
const getConfirmation = async (branchName: string, stackName: string) => {
|
const getConfirmation = async (branchName: string, stackName: string) => {
|
||||||
const confirm = new Confirm({
|
const confirm = new Confirm({
|
||||||
name: 'confirm',
|
name: 'confirm',
|
||||||
message: `The branch ${chalk.green(branchName)} will deploy to ${chalk.yellow(stackName)}`
|
message: `The branch ${chalk.green(
|
||||||
|
branchName
|
||||||
|
)} will deploy to ${chalk.yellow(stackName)}`
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -54,7 +68,7 @@ const getConfirmation = async (branchName: string, stackName: string) => {
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'update',
|
name: 'update',
|
||||||
@@ -66,7 +80,7 @@ module.exports = {
|
|||||||
|
|
||||||
// Load settings
|
// Load settings
|
||||||
const config = await getSettings(toolbox);
|
const config = await getSettings(toolbox);
|
||||||
if (!config || (!config.photowall_repo || config.photowall_repo === "")) {
|
if (!config || !config.photowall_repo || config.photowall_repo === '') {
|
||||||
print.error(`
|
print.error(`
|
||||||
|
|
||||||
The create command requires that you set an absolute path to your local photowall repository.
|
The create command requires that you set an absolute path to your local photowall repository.
|
||||||
@@ -78,12 +92,16 @@ module.exports = {
|
|||||||
|
|
||||||
let branchName = null;
|
let branchName = null;
|
||||||
while (!branchName) {
|
while (!branchName) {
|
||||||
branchName = await getBranchName(toolbox, config.gh_token, config.photowall_repo);
|
branchName = await getBranchName(
|
||||||
|
toolbox,
|
||||||
|
config.gh_token,
|
||||||
|
config.photowall_repo
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stacks = JSON.parse(
|
const stacks = JSON.parse(
|
||||||
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
strings.trim(await system.run(`aws cloudformation list-stacks`))
|
||||||
)
|
);
|
||||||
const teststacks: Stack[] = await Promise.all(
|
const teststacks: Stack[] = await Promise.all(
|
||||||
stacks.StackSummaries.map(async item => {
|
stacks.StackSummaries.map(async item => {
|
||||||
if (
|
if (
|
||||||
@@ -103,24 +121,24 @@ module.exports = {
|
|||||||
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
`aws codepipeline get-pipeline --name ${item.StackName}Pipeline`
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
const branch = pipeline.pipeline.stages.filter(stage => {
|
const branch = pipeline.pipeline.stages.filter(stage => {
|
||||||
return stage.name === 'Source'
|
return stage.name === 'Source';
|
||||||
})[0].actions[0].configuration.Branch
|
})[0].actions[0].configuration.Branch;
|
||||||
const lastUpdated = pipeline.metadata.updated
|
const lastUpdated = pipeline.metadata.updated;
|
||||||
return {
|
return {
|
||||||
name: item.StackName,
|
name: item.StackName,
|
||||||
status: item.StackStatus,
|
status: item.StackStatus,
|
||||||
drift_status: item.DriftInformation.StackDriftStatus,
|
drift_status: item.DriftInformation.StackDriftStatus,
|
||||||
branch: branch,
|
branch: branch,
|
||||||
updated: lastUpdated
|
updated: lastUpdated
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
return Promise.resolve(null);
|
||||||
return Promise.resolve(null)
|
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
|
|
||||||
const curratedStacks: Stack[] = teststacks.filter(Boolean)
|
const curratedStacks: Stack[] = teststacks.filter(Boolean);
|
||||||
const options = curratedStacks.map(item => {
|
const options = curratedStacks.map(item => {
|
||||||
return item.name;
|
return item.name;
|
||||||
});
|
});
|
||||||
@@ -141,48 +159,69 @@ module.exports = {
|
|||||||
|
|
||||||
if (shouldRun) {
|
if (shouldRun) {
|
||||||
const cmd = `make update-stack STACK_ENVIRONMENT_NAME=${stacksuffix} GITHUB_BRANCH=${branchName}`;
|
const cmd = `make update-stack STACK_ENVIRONMENT_NAME=${stacksuffix} GITHUB_BRANCH=${branchName}`;
|
||||||
print.fancy(`Running [${chalk.yellow(cmd)}] in folder [${`${config.photowall_repo}/cloudformation/`}]`);
|
print.fancy(
|
||||||
const output = await system.run(cmd, {cwd: `${config.photowall_repo}/cloudformation/`, trim: true});
|
`Running [${chalk.yellow(
|
||||||
|
cmd
|
||||||
|
)}] in folder [${`${config.photowall_repo}/cloudformation/`}]`
|
||||||
|
);
|
||||||
|
const output = await system.run(cmd, {
|
||||||
|
cwd: `${config.photowall_repo}/cloudformation/`,
|
||||||
|
trim: true
|
||||||
|
});
|
||||||
print.fancy(output);
|
print.fancy(output);
|
||||||
|
|
||||||
// Check the status
|
// Check the status
|
||||||
const spinner = print.spin(`Waiting for stack to update`);
|
const spinner = print.spin(`Waiting for stack to update`);
|
||||||
let currentStackStatus = "";
|
let currentStackStatus = '';
|
||||||
while (currentStackStatus !== "UPDATE_COMPLETE") {
|
while (currentStackStatus !== 'UPDATE_COMPLETE') {
|
||||||
const stackCmd = `aws cloudformation describe-stacks --stack-name photowall-${stacksuffix}`;
|
const stackCmd = `aws cloudformation describe-stacks --stack-name photowall-${stacksuffix}`;
|
||||||
const response = await system.run(stackCmd, { trim: true });
|
const response = await system.run(stackCmd, { trim: true });
|
||||||
const stackData = JSON.parse(response);
|
const stackData = JSON.parse(response);
|
||||||
currentStackStatus = stackData.Stacks[0].StackStatus;
|
currentStackStatus = stackData.Stacks[0].StackStatus;
|
||||||
|
let statusOutput = '';
|
||||||
switch (currentStackStatus) {
|
switch (currentStackStatus) {
|
||||||
case "UPDATE_IN_PROGRESS":
|
case 'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS':
|
||||||
spinner.text = `[${chalk.red(currentStackStatus)}]: Updating the stack`;
|
statusOutput = `[${chalk.yellow(
|
||||||
case "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS":
|
currentStackStatus
|
||||||
spinner.text = `[${chalk.yellow(currentStackStatus)}]: Almost done, cleanup in progress`;
|
)}]: Almost done, cleanup in progress`;
|
||||||
case "UPDATE_COMPLETE":
|
break;
|
||||||
spinner.text = `[${chalk.green(currentStackStatus)}]: Update complete`;
|
case 'UPDATE_COMPLETE':
|
||||||
|
statusOutput = `[${chalk.green(
|
||||||
|
currentStackStatus
|
||||||
|
)}]: Update complete`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
statusOutput = `[${chalk.red(
|
||||||
|
currentStackStatus
|
||||||
|
)}]: Updating the stack`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
spinner.text = statusOutput;
|
||||||
if (currentStackStatus !== 'UPDATE_COMPLETE') {
|
if (currentStackStatus !== 'UPDATE_COMPLETE') {
|
||||||
await sleep(1000);
|
await sleep(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner.text = `Will start the pipeline now [${chalk.yellow(`photowall-${stacksuffix}Pipeline`)}]`;
|
spinner.text = `Will start the pipeline now [${chalk.yellow(
|
||||||
|
`photowall-${stacksuffix}Pipeline`
|
||||||
|
)}]`;
|
||||||
const runPipelineCmd = `aws codepipeline start-pipeline-execution --name photowall-${stacksuffix}Pipeline`;
|
const runPipelineCmd = `aws codepipeline start-pipeline-execution --name photowall-${stacksuffix}Pipeline`;
|
||||||
await system.run(runPipelineCmd);
|
await system.run(runPipelineCmd);
|
||||||
await sleep(2000);
|
await sleep(2000);
|
||||||
spinner.stop();
|
spinner.stop();
|
||||||
print.fancy('Pipeline started, bye! 🚀');
|
print.fancy('Pipeline started, bye! 🚀');
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`e`, e)
|
console.log(`e`, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbox.fromMenu()) {
|
if (toolbox.fromMenu()) {
|
||||||
await toolbox.menu.showMenu('deployment testservers', defaultMenuSettings)
|
await toolbox.menu.showMenu(
|
||||||
|
'deployment testservers',
|
||||||
|
defaultMenuSettings
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const checkCLIProgram = async (toolbox: GluegunMenuToolbox, cmd: string) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'settings',
|
name: 'settings',
|
||||||
@@ -20,10 +20,10 @@ module.exports = {
|
|||||||
description: 'Setup settings for pwcli (se)',
|
description: 'Setup settings for pwcli (se)',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
run: async (toolbox: GluegunMenuToolbox) => {
|
run: async (toolbox: GluegunMenuToolbox) => {
|
||||||
|
|
||||||
const { print, system, strings } = toolbox;
|
const { print, system, strings } = toolbox;
|
||||||
|
|
||||||
print.info(chalk.yellow(`
|
print.info(
|
||||||
|
chalk.yellow(`
|
||||||
|
|
||||||
This will help you setup everything needed to run pwcli on a mac (maby linux).
|
This will help you setup everything needed to run pwcli on a mac (maby linux).
|
||||||
All settings will be saved to ${chalk.cyan('~/.pwcli_settings')}
|
All settings will be saved to ${chalk.cyan('~/.pwcli_settings')}
|
||||||
@@ -31,7 +31,8 @@ module.exports = {
|
|||||||
First step is to install aws cli [https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html]
|
First step is to install aws cli [https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html]
|
||||||
and configuring it. Install it either with brew or do as specified in the link.
|
and configuring it. Install it either with brew or do as specified in the link.
|
||||||
|
|
||||||
`));
|
`)
|
||||||
|
);
|
||||||
|
|
||||||
let config = await getSettings(toolbox);
|
let config = await getSettings(toolbox);
|
||||||
|
|
||||||
@@ -101,38 +102,48 @@ module.exports = {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
await system.run('touch ~/.pwcli_settings');
|
await system.run('touch ~/.pwcli_settings');
|
||||||
print.info(`Created configuration file ${chalk.yellow(`~/.pwcli_settings`)}`);
|
print.info(
|
||||||
|
`Created configuration file ${chalk.yellow(`~/.pwcli_settings`)}`
|
||||||
|
);
|
||||||
if (!config) {
|
if (!config) {
|
||||||
config = {
|
config = {
|
||||||
gh_token: '',
|
gh_token: '',
|
||||||
photowall_repo: '',
|
photowall_repo: ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
config.gh_token = await (new Input({
|
config.gh_token = await new Input({
|
||||||
message: `A github accesstoken is required with ${chalk.yellow('Full control of private repositories')}, please insert the key >`,
|
message: `A github accesstoken is required with ${chalk.yellow(
|
||||||
initial: config.gh_token,
|
'Full control of private repositories'
|
||||||
})).run();
|
)}, please insert the key >`,
|
||||||
|
initial: config.gh_token
|
||||||
|
}).run();
|
||||||
|
|
||||||
config.photowall_repo = await (new Input({
|
config.photowall_repo = await new Input({
|
||||||
message: `If you want to be able to use current branch in your photowall repository\nor the create and update commands for testservers, please insert\nthe absolute path to the project or leave blank >`,
|
message: `If you want to be able to use current branch in your photowall repository\nor the create and update commands for testservers, please insert\nthe absolute path to the project or leave blank >`,
|
||||||
initial: config.photowall_repo,
|
initial: config.photowall_repo
|
||||||
})).run();
|
}).run();
|
||||||
|
|
||||||
print.info(`
|
print.info(`
|
||||||
|
|
||||||
${JSON.stringify(config)}
|
${JSON.stringify(config)}
|
||||||
|
|
||||||
`);
|
`);
|
||||||
if (await (new Confirm({
|
if (
|
||||||
message: `Ok to save the these settings to ${chalk.yellow(`~/.pwcli_settings`)}?`
|
await new Confirm({
|
||||||
})).run()) {
|
message: `Ok to save the these settings to ${chalk.yellow(
|
||||||
|
`~/.pwcli_settings`
|
||||||
|
)}?`
|
||||||
|
}).run()
|
||||||
|
) {
|
||||||
await system.run(`echo '${JSON.stringify(config)}' > ~/.pwcli_settings`);
|
await system.run(`echo '${JSON.stringify(config)}' > ~/.pwcli_settings`);
|
||||||
} else {
|
} else {
|
||||||
print.info('Ok then.');
|
print.info('Ok then.');
|
||||||
if (toolbox.fromMenu()) await toolbox.menu.showMenu(null, defaultMenuSettings);
|
if (toolbox.fromMenu())
|
||||||
|
await toolbox.menu.showMenu(null, defaultMenuSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbox.fromMenu()) await toolbox.menu.showMenu(null, defaultMenuSettings);
|
if (toolbox.fromMenu())
|
||||||
|
await toolbox.menu.showMenu(null, defaultMenuSettings);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,9 +2,14 @@ import { GluegunMenuToolbox } from '@lenne.tech/gluegun-menu';
|
|||||||
import { Octokit } from '@octokit/core';
|
import { Octokit } from '@octokit/core';
|
||||||
import chalk = require('chalk');
|
import chalk = require('chalk');
|
||||||
import { system } from 'gluegun';
|
import { system } from 'gluegun';
|
||||||
import { confirmMessage, defaultMenuSettings, getSettings, issueToBranchName } from '../../../globals';
|
import {
|
||||||
import { createPullRequest, getIssueFromRepo, getOpenIssuesFromRepo, getOrgIssues, getOrgRepos } from '../../../services/github_rest';
|
confirmMessage,
|
||||||
const { AutoComplete, Confirm, Input } = require('enquirer');
|
defaultMenuSettings,
|
||||||
|
getSettings,
|
||||||
|
issueToBranchName
|
||||||
|
} from '../../../globals';
|
||||||
|
import { getIssueFromRepo, getOrgIssues } from '../../../services/github_rest';
|
||||||
|
const { AutoComplete } = require('enquirer');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
name: 'SetupIssue',
|
name: 'SetupIssue',
|
||||||
@@ -12,7 +17,6 @@ module.exports = {
|
|||||||
description: 'Setup everything for a issue (si)',
|
description: 'Setup everything for a issue (si)',
|
||||||
hidden: false,
|
hidden: false,
|
||||||
run: async (toolbox: GluegunMenuToolbox) => {
|
run: async (toolbox: GluegunMenuToolbox) => {
|
||||||
|
|
||||||
const { print, system, strings } = toolbox;
|
const { print, system, strings } = toolbox;
|
||||||
|
|
||||||
// Load settings
|
// Load settings
|
||||||
@@ -24,8 +28,10 @@ module.exports = {
|
|||||||
|
|
||||||
const repos = await getOrgIssues(toolbox, config.gh_token);
|
const repos = await getOrgIssues(toolbox, config.gh_token);
|
||||||
const issueOptions = repos.flatMap(repo => {
|
const issueOptions = repos.flatMap(repo => {
|
||||||
return repo.issues.map(issue => `${issue.number}-${repo.repo}-${issue.title}`);
|
return repo.issues.map(
|
||||||
})
|
issue => `${issue.number}-${repo.repo}-${issue.title}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const promptIssues = new AutoComplete({
|
const promptIssues = new AutoComplete({
|
||||||
name: 'issues',
|
name: 'issues',
|
||||||
@@ -38,14 +44,30 @@ module.exports = {
|
|||||||
const issueNumber = issueResponse.split('-')[0];
|
const issueNumber = issueResponse.split('-')[0];
|
||||||
const repo = issueResponse.split('-')[1];
|
const repo = issueResponse.split('-')[1];
|
||||||
|
|
||||||
const issueData = await getIssueFromRepo(toolbox, config.gh_token, repo, issueNumber);
|
const issueData = await getIssueFromRepo(
|
||||||
|
toolbox,
|
||||||
|
config.gh_token,
|
||||||
|
repo,
|
||||||
|
issueNumber
|
||||||
|
);
|
||||||
const branchName = issueToBranchName(issueData);
|
const branchName = issueToBranchName(issueData);
|
||||||
|
|
||||||
if (await confirmMessage(`Create new branch ${chalk.yellow(branchName)} from origin/master?`)) {
|
if (
|
||||||
await system.run(`git checkout -b ${branchName} origin/master`, {cwd: config.photowall_repo, trim: true});
|
await confirmMessage(
|
||||||
await system.run(`git push -u origin ${branchName}`, {cwd: config.photowall_repo, trim: true});
|
`Create new branch ${chalk.yellow(branchName)} from origin/master?`
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
await system.run(`git checkout -b ${branchName} origin/master`, {
|
||||||
|
cwd: config.photowall_repo,
|
||||||
|
trim: true
|
||||||
|
});
|
||||||
|
await system.run(`git push -u origin ${branchName}`, {
|
||||||
|
cwd: config.photowall_repo,
|
||||||
|
trim: true
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toolbox.fromMenu()) await toolbox.menu.showMenu('workflow', defaultMenuSettings);
|
if (toolbox.fromMenu())
|
||||||
|
await toolbox.menu.showMenu('workflow', defaultMenuSettings);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
|
import { GluegunToolbox } from 'gluegun';
|
||||||
import { GluegunToolbox } from 'gluegun'
|
|
||||||
|
|
||||||
|
|
||||||
// add your CLI-specific functionality here, which will then be accessible
|
// add your CLI-specific functionality here, which will then be accessible
|
||||||
// to your commands
|
// to your commands
|
||||||
module.exports = (toolbox: GluegunToolbox) => {
|
module.exports = (toolbox: GluegunToolbox) => {
|
||||||
toolbox.foo = () => {
|
toolbox.foo = () => {
|
||||||
toolbox.print.info('called foo extension')
|
toolbox.print.info('called foo extension');
|
||||||
}
|
};
|
||||||
|
|
||||||
// enable this if you want to read configuration in from
|
// enable this if you want to read configuration in from
|
||||||
// the current folder's package.json (in a "pwcli" property),
|
// the current folder's package.json (in a "pwcli" property),
|
||||||
@@ -16,4 +14,4 @@ module.exports = (toolbox: GluegunToolbox) => {
|
|||||||
// ...toolbox.config,
|
// ...toolbox.config,
|
||||||
// ...toolbox.config.loadConfig("pwcli", process.cwd())
|
// ...toolbox.config.loadConfig("pwcli", process.cwd())
|
||||||
// }
|
// }
|
||||||
}
|
};
|
||||||
|
|||||||
+3
-3
@@ -66,6 +66,6 @@ export const confirmMessage = async (message: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sleep = (milliseconds) => {
|
export const sleep = milliseconds => {
|
||||||
return new Promise(resolve => setTimeout(resolve, milliseconds))
|
return new Promise(resolve => setTimeout(resolve, milliseconds));
|
||||||
}
|
};
|
||||||
|
|||||||
+37
-26
@@ -1,12 +1,12 @@
|
|||||||
import { Octokit } from "@octokit/core";
|
import { Octokit } from '@octokit/core';
|
||||||
|
|
||||||
export const getOrgRepos = async (toolbox, token, org) => {
|
export const getOrgRepos = async (toolbox, token, org) => {
|
||||||
const { print } = toolbox;
|
const { print } = toolbox;
|
||||||
const octokit = new Octokit({ auth: token });
|
const octokit = new Octokit({ auth: token });
|
||||||
try {
|
try {
|
||||||
const response = await octokit.request("GET /orgs/{org}/repos", {
|
const response = await octokit.request('GET /orgs/{org}/repos', {
|
||||||
org: org,
|
org: org,
|
||||||
per_page: 100,
|
per_page: 100
|
||||||
});
|
});
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
@@ -21,9 +21,9 @@ export const getRepo = async (toolbox, token, owner, repo) => {
|
|||||||
const { print } = toolbox;
|
const { print } = toolbox;
|
||||||
const octokit = new Octokit({ auth: token });
|
const octokit = new Octokit({ auth: token });
|
||||||
try {
|
try {
|
||||||
const response = await octokit.request("GET /repos/{owner}/{repo}", {
|
const response = await octokit.request('GET /repos/{owner}/{repo}', {
|
||||||
owner: owner,
|
owner: owner,
|
||||||
repo: repo,
|
repo: repo
|
||||||
});
|
});
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
@@ -32,19 +32,22 @@ export const getRepo = async (toolbox, token, owner, repo) => {
|
|||||||
print.error('Not a valid organisation for your token access');
|
print.error('Not a valid organisation for your token access');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getRepoBranches = async (toolbox, token, owner, repo) => {
|
export const getRepoBranches = async (toolbox, token, owner, repo) => {
|
||||||
const { print } = toolbox;
|
const { print } = toolbox;
|
||||||
const octokit = new Octokit({ auth: token });
|
const octokit = new Octokit({ auth: token });
|
||||||
|
|
||||||
const getBranches = async (toolbox, token, owner, repo, page) => {
|
const getBranches = async (toolbox, token, owner, repo, page) => {
|
||||||
const response = await octokit.request("GET /repos/{owner}/{repo}/branches", {
|
const response = await octokit.request(
|
||||||
|
'GET /repos/{owner}/{repo}/branches',
|
||||||
|
{
|
||||||
owner: owner,
|
owner: owner,
|
||||||
repo: repo,
|
repo: repo,
|
||||||
page: page,
|
page: page,
|
||||||
per_page: 100,
|
per_page: 100
|
||||||
});
|
}
|
||||||
|
);
|
||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,29 +65,31 @@ export const getRepoBranches = async (toolbox, token, owner, repo) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return allBranches;
|
return allBranches;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getOrgIssues = async (toolbox, token) => {
|
export const getOrgIssues = async (toolbox, token) => {
|
||||||
// const { print } = toolbox;
|
// const { print } = toolbox;
|
||||||
const repos = await getOrgRepos(toolbox, token, 'Photowall');
|
const repos = await getOrgRepos(toolbox, token, 'Photowall');
|
||||||
const repoNames = repos.map(item => item.name);
|
const repoNames = repos.map(item => item.name);
|
||||||
const allIssues = await Promise.all(repoNames.map(async (repoName) => {
|
const allIssues = await Promise.all(
|
||||||
|
repoNames.map(async repoName => {
|
||||||
const issues = await getOpenIssuesFromRepo(toolbox, token, repoName);
|
const issues = await getOpenIssuesFromRepo(toolbox, token, repoName);
|
||||||
// console.log(`${repoName} - issues`, issues.length);
|
// console.log(`${repoName} - issues`, issues.length);
|
||||||
// return issues.map(item => `${item.number}-${item.title.substring(0, 10)}`);
|
// return issues.map(item => `${item.number}-${item.title.substring(0, 10)}`);
|
||||||
return { repo: repoName, issues: issues };
|
return { repo: repoName, issues: issues };
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
return allIssues.flat();
|
return allIssues.flat();
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getOpenIssuesFromRepo = async (toolbox, token, repo) => {
|
export const getOpenIssuesFromRepo = async (toolbox, token, repo) => {
|
||||||
const { print } = toolbox;
|
const { print } = toolbox;
|
||||||
const octokit = new Octokit({ auth: token });
|
const octokit = new Octokit({ auth: token });
|
||||||
try {
|
try {
|
||||||
const response = await octokit.request("GET /repos/{owner}/{repo}/issues", {
|
const response = await octokit.request('GET /repos/{owner}/{repo}/issues', {
|
||||||
owner: "Photowall",
|
owner: 'Photowall',
|
||||||
repo: repo,
|
repo: repo,
|
||||||
per_page: 100,
|
per_page: 100
|
||||||
});
|
});
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
@@ -93,17 +98,20 @@ export const getOpenIssuesFromRepo = async (toolbox, token, repo) => {
|
|||||||
print.error('Not a valid issuenumber for this repository');
|
print.error('Not a valid issuenumber for this repository');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getIssueFromRepo = async (toolbox, token, repo, issueNumber) => {
|
export const getIssueFromRepo = async (toolbox, token, repo, issueNumber) => {
|
||||||
const { print } = toolbox;
|
const { print } = toolbox;
|
||||||
const octokit = new Octokit({ auth: token });
|
const octokit = new Octokit({ auth: token });
|
||||||
try {
|
try {
|
||||||
const response = await octokit.request("GET /repos/{owner}/{repo}/issues/{issue_number}", {
|
const response = await octokit.request(
|
||||||
owner: "Photowall",
|
'GET /repos/{owner}/{repo}/issues/{issue_number}',
|
||||||
|
{
|
||||||
|
owner: 'Photowall',
|
||||||
repo: repo,
|
repo: repo,
|
||||||
issue_number: issueNumber,
|
issue_number: issueNumber
|
||||||
});
|
}
|
||||||
|
);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
@@ -111,9 +119,14 @@ export const getIssueFromRepo = async (toolbox, token, repo, issueNumber) => {
|
|||||||
print.error('Not a valid issuenumber for this repository');
|
print.error('Not a valid issuenumber for this repository');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const createPullRequest = async (toolbox, token: string, repo: string, branch: string) => {
|
export const createPullRequest = async (
|
||||||
|
toolbox,
|
||||||
|
token: string,
|
||||||
|
repo: string,
|
||||||
|
branch: string
|
||||||
|
) => {
|
||||||
const { print } = toolbox;
|
const { print } = toolbox;
|
||||||
const octokit = new Octokit({ auth: token });
|
const octokit = new Octokit({ auth: token });
|
||||||
try {
|
try {
|
||||||
@@ -131,6 +144,4 @@ export const createPullRequest = async (toolbox, token: string, repo: string, br
|
|||||||
print.error('Not a valid branch');
|
print.error('Not a valid branch');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
+4
-1
@@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"strictPropertyInitialization": false,
|
||||||
"allowSyntheticDefaultImports": false,
|
"allowSyntheticDefaultImports": false,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
@@ -19,7 +22,7 @@
|
|||||||
"inlineSourceMap": true,
|
"inlineSourceMap": true,
|
||||||
"outDir": "build",
|
"outDir": "build",
|
||||||
"strict": false,
|
"strict": false,
|
||||||
"target": "es5",
|
"target": "es2020",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationDir": "build/types"
|
"declarationDir": "build/types"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user