@toptal/davinci-workflow 1.8.3 → 1.8.4-alpha-fx-2755-interactive-workflow-generation.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -7
- package/src/commands/new-workflow.js +14 -6
- package/src/constants.js +2 -2
- package/src/index.js +1 -1
- package/src/post-build/alpha-package.js +1 -1
- package/src/post-build/deploy-temploy.js +1 -1
- package/src/post-build/deploy.js +2 -2
- package/src/post-build/integration-tests.js +108 -2
- package/src/utils/filter-workspaces-by-command.js +34 -0
- package/src/utils/generate-command-message.js +1 -1
- package/src/utils/get-program-config.js +42 -0
- package/src/utils/prompts.js +86 -0
- package/src/utils/read-secrets.js +1 -1
- package/src/utils/set-root-env.js +17 -0
- package/src/utils/skeleton.js +1 -1
- package/src/utils/workflows.js +1 -1
- package/dist-package/package.json +0 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toptal/davinci-workflow",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.4-alpha-fx-2755-interactive-workflow-generation.4+46405792",
|
|
4
4
|
"description": "GH Workflow generator package for frontend applications",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -30,12 +30,17 @@
|
|
|
30
30
|
"url": "https://github.com/toptal/davinci/issues"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@toptal/davinci-cli-shared": "1.5.
|
|
34
|
-
"@toptal/davinci-
|
|
33
|
+
"@toptal/davinci-cli-shared": "1.5.3-alpha-fx-2755-interactive-workflow-generation.4+46405792",
|
|
34
|
+
"@toptal/davinci-monorepo": "6.5.2-alpha-fx-2755-interactive-workflow-generation.4+46405792",
|
|
35
|
+
"@toptal/davinci-skeleton": "7.0.1-alpha-fx-2755-interactive-workflow-generation.23+46405792",
|
|
36
|
+
"chalk": "^4.1.2",
|
|
37
|
+
"cosmiconfig": "^7.0.1",
|
|
35
38
|
"fs-extra": "^9.0.1",
|
|
39
|
+
"inquirer": "^8.2.4",
|
|
40
|
+
"inquirer-table-prompt": "^0.2.1",
|
|
41
|
+
"js-yaml": "^4.1.0",
|
|
36
42
|
"lodash.kebabcase": "^4.1.1",
|
|
37
|
-
"ora": "^5.4.1"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
43
|
+
"ora": "^5.4.1"
|
|
44
|
+
},
|
|
45
|
+
"gitHead": "46405792bfc49fdb4b954b2ca9ddbb9ce8660801"
|
|
41
46
|
}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
const { print } = require('@toptal/davinci-cli-shared')
|
|
2
|
+
const {
|
|
3
|
+
utils: { checkIfMonorepo }
|
|
4
|
+
} = require('@toptal/davinci-monorepo')
|
|
2
5
|
const path = require('path')
|
|
3
6
|
const fs = require('fs-extra')
|
|
4
7
|
const ora = require('ora')
|
|
5
8
|
const kebabCase = require('lodash.kebabcase')
|
|
6
9
|
const {
|
|
7
|
-
prettifyCommands
|
|
10
|
+
prettifyCommands,
|
|
8
11
|
} = require('@toptal/davinci-cli-shared/src/utils/print')
|
|
9
12
|
|
|
10
13
|
const { WORKFLOWS_DIR, GITHUB_DIR } = require('../constants')
|
|
11
14
|
const {
|
|
12
15
|
copyWorkflowFromSkeleton,
|
|
13
|
-
getWorkflowSourceFilepath
|
|
16
|
+
getWorkflowSourceFilepath,
|
|
14
17
|
} = require('../utils/skeleton')
|
|
15
18
|
const { readYMLSecrets } = require('../utils/read-secrets')
|
|
16
19
|
const generateMessageFromSecrets = require('../utils/generate-command-message')
|
|
@@ -77,7 +80,7 @@ const getWorkflowPostBuildFilename = workflowName => {
|
|
|
77
80
|
return kebabCase(workflowName) + '.js'
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
const performPostBuildProcedure = async (filename, destDir) => {
|
|
83
|
+
const performPostBuildProcedure = async (filename, destDir, workflowName) => {
|
|
81
84
|
const postBuildFilePathname = path.join(
|
|
82
85
|
require.resolve('@toptal/davinci-workflow'),
|
|
83
86
|
'../',
|
|
@@ -96,9 +99,13 @@ const performPostBuildProcedure = async (filename, destDir) => {
|
|
|
96
99
|
throw new Error('post build file must export a function')
|
|
97
100
|
}
|
|
98
101
|
|
|
102
|
+
const isMonorepo = checkIfMonorepo()
|
|
103
|
+
|
|
99
104
|
await executePostBuild({
|
|
100
105
|
print,
|
|
101
|
-
destDir
|
|
106
|
+
destDir,
|
|
107
|
+
workflowName,
|
|
108
|
+
isMonorepo,
|
|
102
109
|
})
|
|
103
110
|
}
|
|
104
111
|
|
|
@@ -129,7 +136,8 @@ const createCommand = async workflowName => {
|
|
|
129
136
|
// execute a possible post-build procedure
|
|
130
137
|
await performPostBuildProcedure(
|
|
131
138
|
getWorkflowPostBuildFilename(workflowName),
|
|
132
|
-
workflowsDir
|
|
139
|
+
workflowsDir,
|
|
140
|
+
fileName
|
|
133
141
|
)
|
|
134
142
|
// read and print mandatory ENV secrets that must be installed beforehand
|
|
135
143
|
await printMandatorySecretsList(fileName)
|
|
@@ -165,7 +173,7 @@ const newWorkflowCommandCreator = {
|
|
|
165
173
|
|
|
166
174
|
return createCommand(workflowName)
|
|
167
175
|
},
|
|
168
|
-
help
|
|
176
|
+
help,
|
|
169
177
|
}
|
|
170
178
|
|
|
171
179
|
module.exports = newWorkflowCommandCreator
|
package/src/constants.js
CHANGED
|
@@ -21,11 +21,11 @@ const SECRET_DEFINITION = {
|
|
|
21
21
|
'Jenkins Build Token. Necessary to execute Jenkins job',
|
|
22
22
|
// alpha-package
|
|
23
23
|
NPM_TOKEN_PUBLISH: 'NPM publish access token',
|
|
24
|
-
NPM_TOKEN_READ_ONLY: 'NPM read token'
|
|
24
|
+
NPM_TOKEN_READ_ONLY: 'NPM read token',
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
module.exports = {
|
|
28
28
|
GITHUB_DIR,
|
|
29
29
|
WORKFLOWS_DIR,
|
|
30
|
-
SECRET_DEFINITION
|
|
30
|
+
SECRET_DEFINITION,
|
|
31
31
|
}
|
package/src/index.js
CHANGED
package/src/post-build/deploy.js
CHANGED
|
@@ -6,7 +6,7 @@ const logText = chalk.red.bold
|
|
|
6
6
|
const yellowBoldText = chalk.yellow.bold
|
|
7
7
|
const deployWorkflows = [
|
|
8
8
|
'davinci-deploy-production.yml',
|
|
9
|
-
'davinci-deploy-staging.yml'
|
|
9
|
+
'davinci-deploy-staging.yml',
|
|
10
10
|
]
|
|
11
11
|
|
|
12
12
|
const executePostBuild = async ({ destDir }) => {
|
|
@@ -53,7 +53,7 @@ const executePostBuild = async ({ destDir }) => {
|
|
|
53
53
|
' — it performs build & deploy to staging for the latest commit in the PR branch \n',
|
|
54
54
|
!isScriptExist
|
|
55
55
|
? logText('New "build" script was added in package.json! \n')
|
|
56
|
-
: ''
|
|
56
|
+
: '',
|
|
57
57
|
].join('')
|
|
58
58
|
)
|
|
59
59
|
}
|
|
@@ -1,10 +1,116 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
1
2
|
const chalk = require('chalk')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const {
|
|
5
|
+
utils: { getWorkspacesInfo },
|
|
6
|
+
} = require('@toptal/davinci-monorepo')
|
|
7
|
+
|
|
8
|
+
const filterWorkspacesByCommand = require('../utils/filter-workspaces-by-command')
|
|
9
|
+
const setRootEnv = require('../utils/set-root-env')
|
|
10
|
+
const getProgramConfig = require('../utils/get-program-config')
|
|
11
|
+
const { readPackageJson } = require('../utils/skeleton')
|
|
12
|
+
const {
|
|
13
|
+
promptParallelWorkersForMonorepo,
|
|
14
|
+
promptNumber,
|
|
15
|
+
promptConfirm,
|
|
16
|
+
} = require('../utils/prompts')
|
|
2
17
|
|
|
3
18
|
const CONFLUENCE_PAGE =
|
|
4
19
|
'https://toptal-core.atlassian.net/l/c/qtXKBzvs#Generated-IT-Workflow'
|
|
5
20
|
const yellowBoldText = chalk.yellow.bold
|
|
6
21
|
|
|
7
|
-
const
|
|
22
|
+
const getCoverageReportDir = config => config?.['report-dir'] ?? 'coverage'
|
|
23
|
+
|
|
24
|
+
const getEnvForMonorepo = async (destDir, commands) => {
|
|
25
|
+
const rootDir = path.resolve(destDir, '../../')
|
|
26
|
+
const workspaces = await getWorkspacesInfo()
|
|
27
|
+
|
|
28
|
+
const filteredWorkspaces = filterWorkspacesByCommand(
|
|
29
|
+
rootDir,
|
|
30
|
+
workspaces,
|
|
31
|
+
commands
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
if (filteredWorkspaces.length === 0) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`There are no packages to run integration tests in. Following commands were not found: ${JSON.stringify(
|
|
37
|
+
commands
|
|
38
|
+
)}`
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { command, location } = filteredWorkspaces[0]
|
|
43
|
+
|
|
44
|
+
const coverageConfig = await getProgramConfig(
|
|
45
|
+
'nyc',
|
|
46
|
+
path.join(rootDir, location)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
PARALLEL_MATRIX: JSON.stringify(
|
|
51
|
+
await promptParallelWorkersForMonorepo(filteredWorkspaces)
|
|
52
|
+
),
|
|
53
|
+
COMMAND: command,
|
|
54
|
+
COVERAGE_REPORT_DIR: getCoverageReportDir(coverageConfig),
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const getEnvForSPA = async (destDir, commands) => {
|
|
59
|
+
const rootDir = path.resolve(destDir, '../../')
|
|
60
|
+
|
|
61
|
+
const pkg = readPackageJson(rootDir)
|
|
62
|
+
const command = Object.keys(pkg.scripts).find(script =>
|
|
63
|
+
commands.includes(script)
|
|
64
|
+
)
|
|
65
|
+
const coverageConfig = await getProgramConfig('nyc', path.join(rootDir))
|
|
66
|
+
|
|
67
|
+
if (!command) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`No command for integration tests was found. Searched for: ${JSON.stringify(
|
|
70
|
+
commands
|
|
71
|
+
)}`
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
PARALLEL_GROUPS: await promptNumber(
|
|
77
|
+
'Input how many parallel workers you want for integration tests'
|
|
78
|
+
),
|
|
79
|
+
COMMAND: command,
|
|
80
|
+
COVERAGE_REPORT_DIR: getCoverageReportDir(coverageConfig),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const executePostBuild = async ({ destDir, workflowName, isMonorepo }) => {
|
|
85
|
+
const workflowPath = path.join(destDir, workflowName)
|
|
86
|
+
// to support backward compatibility
|
|
87
|
+
const supportedCommands = ['test:integration:ci', 'test:ui:ci', 'test:e2e:ci']
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const dynamicEnv = isMonorepo
|
|
91
|
+
? await getEnvForMonorepo(destDir, supportedCommands)
|
|
92
|
+
: await getEnvForSPA(destDir, supportedCommands)
|
|
93
|
+
|
|
94
|
+
const printCoverage = await promptConfirm(
|
|
95
|
+
'Do you want to print code coverage info in your PR? \nYour project must support code-coverage generation',
|
|
96
|
+
false
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
setRootEnv(
|
|
100
|
+
{
|
|
101
|
+
...dynamicEnv,
|
|
102
|
+
PRINT_COVERAGE: printCoverage,
|
|
103
|
+
},
|
|
104
|
+
workflowPath
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
console.log(chalk.green('\nWorkflow has been updated.'))
|
|
108
|
+
} catch (e) {
|
|
109
|
+
fs.unlinkSync(workflowPath)
|
|
110
|
+
console.error(e.stack)
|
|
111
|
+
throw new Error(`Workflow failed to be generated: ${e.message}`)
|
|
112
|
+
}
|
|
113
|
+
|
|
8
114
|
console.log(
|
|
9
115
|
[
|
|
10
116
|
chalk.green(
|
|
@@ -18,7 +124,7 @@ const executePostBuild = () => {
|
|
|
18
124
|
' to make it work in your CI\n',
|
|
19
125
|
'Read more about customization here ',
|
|
20
126
|
chalk.grey.bold(CONFLUENCE_PAGE),
|
|
21
|
-
'\n'
|
|
127
|
+
'\n',
|
|
22
128
|
].join('')
|
|
23
129
|
)
|
|
24
130
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Return workspace packages that have specified
|
|
6
|
+
* command(s) in their package.json scripts
|
|
7
|
+
* @param {string} rootDir
|
|
8
|
+
* @param {Object<string, { location: string }>} workspaces
|
|
9
|
+
* @param {Array<string>} commands
|
|
10
|
+
* @return {Array<{ name: string, location: string, command: string }>}
|
|
11
|
+
*/
|
|
12
|
+
const filterWorkspacesByCommand = (rootDir, workspaces, commands) => {
|
|
13
|
+
return Object.entries(workspaces)
|
|
14
|
+
.map(([name, { location }]) => {
|
|
15
|
+
const packageJsonPath = path.join(rootDir, location, 'package.json')
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const pkg = require(packageJsonPath)
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
name,
|
|
25
|
+
location,
|
|
26
|
+
command: Object.keys(pkg.scripts).find(script =>
|
|
27
|
+
commands.includes(script)
|
|
28
|
+
),
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
.filter(({ command }) => command)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = filterWorkspacesByCommand
|
|
@@ -16,7 +16,7 @@ const generateMessageFromSecrets = secretsList => {
|
|
|
16
16
|
'————————————————————————————————————————————————————————————————————————————————————————————————\n',
|
|
17
17
|
'The following GH secrets',
|
|
18
18
|
logText('must'),
|
|
19
|
-
'be specified in the repo to properly execute the command: \n\n'
|
|
19
|
+
'be specified in the repo to properly execute the command: \n\n',
|
|
20
20
|
].join(' ')
|
|
21
21
|
|
|
22
22
|
message += secretsList.reduce((messageItem, secret) => {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const { cosmiconfig } = require('cosmiconfig')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Searches for and loads configuration for your program
|
|
6
|
+
* Looks for:
|
|
7
|
+
* - a package.json property
|
|
8
|
+
* - a JSON or YAML, extensionless "rc file"
|
|
9
|
+
* - an "rc file" with the extensions .json, .yaml, .yml, .js, or .cjs
|
|
10
|
+
* - a .config.js or .config.cjs CommonJS module
|
|
11
|
+
* @param program
|
|
12
|
+
* @param [defaultDir] - search up the directory tree from this folder
|
|
13
|
+
* @return {Promise<any|{extends}>}
|
|
14
|
+
*/
|
|
15
|
+
const getProgramConfig = async (program, defaultDir) => {
|
|
16
|
+
const explorer = cosmiconfig(program)
|
|
17
|
+
|
|
18
|
+
const defaultConfig = await explorer.search(defaultDir)
|
|
19
|
+
|
|
20
|
+
if (!defaultConfig) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!defaultConfig.config.extends) {
|
|
25
|
+
return defaultConfig.config
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const extendedConfig = await explorer.load(
|
|
29
|
+
path.join(defaultDir, defaultConfig.config.extends)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if (!extendedConfig) {
|
|
33
|
+
return defaultConfig.config
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
...defaultConfig.config,
|
|
38
|
+
...extendedConfig.config
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = getProgramConfig
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const inquirer = require('inquirer')
|
|
2
|
+
|
|
3
|
+
inquirer.registerPrompt('table', require('inquirer-table-prompt'))
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Show table prompt with selection of parallel workers for each package
|
|
7
|
+
* @param {Array<{ name: string, location: string, command: string }>} packages
|
|
8
|
+
* @param {string} [message]
|
|
9
|
+
* @return {Promise<Array<{ pkg: string, location: string, parallelGroups: number }>>}
|
|
10
|
+
*/
|
|
11
|
+
const promptParallelWorkersForMonorepo = async (packages, message = '') => {
|
|
12
|
+
const { parallelWorkers } = await inquirer.prompt([
|
|
13
|
+
{
|
|
14
|
+
type: 'table',
|
|
15
|
+
name: 'parallelWorkers',
|
|
16
|
+
message: `${
|
|
17
|
+
message ? message + '\n' : ''
|
|
18
|
+
}Here are packages that support integration tests. Select how many parallel workers you want in which package.\nIf you don't choose anything, package won't be included in integration testing`,
|
|
19
|
+
columns: Array.from({ length: 10 }, (_, index) => ({
|
|
20
|
+
name: index + 1,
|
|
21
|
+
})),
|
|
22
|
+
rows: packages.map(({ name }) => ({
|
|
23
|
+
name,
|
|
24
|
+
})),
|
|
25
|
+
},
|
|
26
|
+
])
|
|
27
|
+
|
|
28
|
+
// At least one package needs to be selected for integration tests
|
|
29
|
+
// so if none is selected, show the prompt again
|
|
30
|
+
if (parallelWorkers.filter(Boolean).length === 0) {
|
|
31
|
+
return promptParallelWorkersForMonorepo(
|
|
32
|
+
packages,
|
|
33
|
+
'At least one package need to be selected for integration tests!'
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return packages
|
|
38
|
+
.map(({ name, location }, index) => ({
|
|
39
|
+
pkg: name,
|
|
40
|
+
location,
|
|
41
|
+
parallelGroups: parallelWorkers[index],
|
|
42
|
+
}))
|
|
43
|
+
.filter(({ parallelGroups }) => parallelGroups)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Show number prompts
|
|
48
|
+
* @param {string} message
|
|
49
|
+
* @return {Promise<number>}
|
|
50
|
+
*/
|
|
51
|
+
const promptNumber = async message => {
|
|
52
|
+
const { numberValue } = await inquirer.prompt([
|
|
53
|
+
{
|
|
54
|
+
type: 'number',
|
|
55
|
+
name: 'numberValue',
|
|
56
|
+
message,
|
|
57
|
+
},
|
|
58
|
+
])
|
|
59
|
+
|
|
60
|
+
return numberValue
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Show confirmation prompt
|
|
65
|
+
* @param {string} message
|
|
66
|
+
* @param {boolean} [defaultValue]
|
|
67
|
+
* @return {Promise<boolean>}
|
|
68
|
+
*/
|
|
69
|
+
const promptConfirm = async (message, defaultValue) => {
|
|
70
|
+
const { confirmValue } = await inquirer.prompt([
|
|
71
|
+
{
|
|
72
|
+
type: 'confirm',
|
|
73
|
+
name: 'confirmValue',
|
|
74
|
+
message,
|
|
75
|
+
default: defaultValue,
|
|
76
|
+
},
|
|
77
|
+
])
|
|
78
|
+
|
|
79
|
+
return confirmValue
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
promptParallelWorkersForMonorepo,
|
|
84
|
+
promptNumber,
|
|
85
|
+
promptConfirm,
|
|
86
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const yaml = require('js-yaml')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Set/replace root ENV variables in the workflow
|
|
6
|
+
* @param {Object<string, any>} env
|
|
7
|
+
* @param {string} workflowPath
|
|
8
|
+
*/
|
|
9
|
+
const setRootEnv = (env, workflowPath) => {
|
|
10
|
+
const workflow = yaml.load(fs.readFileSync(workflowPath, 'utf8'))
|
|
11
|
+
|
|
12
|
+
Object.assign(workflow.env, env)
|
|
13
|
+
|
|
14
|
+
fs.writeFileSync(workflowPath, yaml.dump(workflow))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = setRootEnv
|
package/src/utils/skeleton.js
CHANGED
package/src/utils/workflows.js
CHANGED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@toptal/davinci-workflow",
|
|
3
|
-
"version": "1.8.3",
|
|
4
|
-
"description": "GH Workflow generator package for frontend applications",
|
|
5
|
-
"publishConfig": {
|
|
6
|
-
"access": "public"
|
|
7
|
-
},
|
|
8
|
-
"keywords": [
|
|
9
|
-
"Github Actions",
|
|
10
|
-
"GH Workflows",
|
|
11
|
-
"generator"
|
|
12
|
-
],
|
|
13
|
-
"author": "Toptal",
|
|
14
|
-
"homepage": "https://github.com/toptal/davinci/tree/master/packages/workflow#readme",
|
|
15
|
-
"license": "ISC",
|
|
16
|
-
"bin": {
|
|
17
|
-
"davinci-workflow": "./bin/davinci-workflow.js"
|
|
18
|
-
},
|
|
19
|
-
"main": "./src/index.js",
|
|
20
|
-
"repository": {
|
|
21
|
-
"type": "git",
|
|
22
|
-
"url": "git+https://github.com/toptal/davinci.git"
|
|
23
|
-
},
|
|
24
|
-
"scripts": {
|
|
25
|
-
"build:package": "../../bin/build-package.js",
|
|
26
|
-
"prepublishOnly": "../../bin/prepublish.js",
|
|
27
|
-
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
28
|
-
},
|
|
29
|
-
"bugs": {
|
|
30
|
-
"url": "https://github.com/toptal/davinci/issues"
|
|
31
|
-
},
|
|
32
|
-
"dependencies": {
|
|
33
|
-
"@toptal/davinci-cli-shared": "1.5.2",
|
|
34
|
-
"@toptal/davinci-skeleton": "7.0.0",
|
|
35
|
-
"fs-extra": "^9.0.1",
|
|
36
|
-
"lodash.kebabcase": "^4.1.1",
|
|
37
|
-
"ora": "^5.4.1",
|
|
38
|
-
"chalk": "^4.1.2",
|
|
39
|
-
"js-yaml": "^4.1.0"
|
|
40
|
-
}
|
|
41
|
-
}
|