@adobe/aio-cli-plugin-app 8.2.0 → 8.5.0
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/README.md +186 -153
- package/oclif.manifest.json +1 -1
- package/package.json +14 -13
- package/src/AddCommand.js +41 -0
- package/src/BaseCommand.js +9 -0
- package/src/commands/app/add/action.js +9 -15
- package/src/commands/app/add/ci.js +7 -1
- package/src/commands/app/add/event.js +7 -14
- package/src/commands/app/add/extension.js +9 -14
- package/src/commands/app/add/web-assets.js +9 -15
- package/src/commands/app/build.js +1 -1
- package/src/commands/app/config/get/index.js +25 -0
- package/src/commands/app/config/get/log-forwarding/errors.js +46 -0
- package/src/commands/app/config/get/log-forwarding.js +50 -0
- package/src/commands/app/config/index.js +25 -0
- package/src/commands/app/config/set/index.js +25 -0
- package/src/commands/app/config/set/log-forwarding.js +46 -0
- package/src/commands/app/delete/action.js +1 -1
- package/src/commands/app/delete/ci.js +3 -1
- package/src/commands/app/deploy.js +46 -9
- package/src/commands/app/init.js +41 -28
- package/src/commands/app/logs.js +10 -1
- package/src/commands/app/run.js +8 -6
- package/src/commands/app/undeploy.js +8 -6
- package/src/commands/app/use.js +29 -33
- package/src/lib/actions-watcher.js +48 -15
- package/src/lib/build-actions.js +3 -2
- package/src/lib/deploy-actions.js +10 -3
- package/src/lib/log-forwarding.js +267 -0
- package/src/lib/run-dev.js +1 -1
- package/src/lib/vscode.js +3 -1
|
@@ -9,7 +9,7 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA
|
|
|
9
9
|
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
10
|
governing permissions and limitations under the License.
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
const upath = require('upath')
|
|
13
13
|
const chokidar = require('chokidar')
|
|
14
14
|
const aioLogger = require('@adobe/aio-lib-core-logging')('@adobe/aio-cli-plugin-app:actions-watcher', { provider: 'debug' })
|
|
15
15
|
const buildActions = require('./build-actions')
|
|
@@ -58,12 +58,13 @@ module.exports = async (watcherOptions) => {
|
|
|
58
58
|
* Builds and deploy the app.
|
|
59
59
|
*
|
|
60
60
|
* @param {WatcherOptions} watcherOptions the options for the watcher
|
|
61
|
+
* @param {Array<string>} filterActions add filters to deploy only specified OpenWhisk actions
|
|
61
62
|
*/
|
|
62
|
-
async function buildAndDeploy (watcherOptions) {
|
|
63
|
+
async function buildAndDeploy (watcherOptions, filterActions) {
|
|
63
64
|
const { config, isLocal, log } = watcherOptions
|
|
64
65
|
|
|
65
|
-
await buildActions(config)
|
|
66
|
-
await deployActions(config, isLocal, log)
|
|
66
|
+
await buildActions(config, filterActions)
|
|
67
|
+
await deployActions(config, isLocal, log, filterActions)
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
/**
|
|
@@ -75,30 +76,62 @@ async function buildAndDeploy (watcherOptions) {
|
|
|
75
76
|
function createChangeHandler (watcherOptions) {
|
|
76
77
|
const { watcher, log } = watcherOptions
|
|
77
78
|
|
|
78
|
-
let
|
|
79
|
-
let
|
|
79
|
+
let deploymentInProgress = false
|
|
80
|
+
let fileChanged = false
|
|
81
|
+
let undeployedFile = ''
|
|
80
82
|
|
|
81
83
|
return async (filePath) => {
|
|
82
|
-
|
|
84
|
+
aioLogger.debug('Code change triggered...')
|
|
85
|
+
if (deploymentInProgress) {
|
|
83
86
|
aioLogger.debug(`${filePath} has changed. Deploy in progress. This change will be deployed after completion of current deployment.`)
|
|
84
|
-
|
|
87
|
+
undeployedFile = filePath
|
|
88
|
+
fileChanged = true
|
|
85
89
|
return
|
|
86
90
|
}
|
|
87
|
-
|
|
91
|
+
deploymentInProgress = true
|
|
88
92
|
try {
|
|
89
93
|
aioLogger.debug(`${filePath} has changed. Redeploying actions.`)
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
const filterActions = getActionNameFromPath(filePath, watcherOptions)
|
|
95
|
+
if (!filterActions.length) {
|
|
96
|
+
log(' -> A non-action file was changed, restart is required to deploy...')
|
|
97
|
+
} else {
|
|
98
|
+
await buildAndDeploy(watcherOptions, filterActions)
|
|
99
|
+
aioLogger.debug('Deployment successful')
|
|
100
|
+
}
|
|
92
101
|
} catch (err) {
|
|
93
102
|
log(' -> Error encountered while deploying actions. Stopping auto refresh.')
|
|
94
103
|
aioLogger.debug(err)
|
|
95
104
|
await watcher.close()
|
|
96
105
|
}
|
|
97
|
-
if (
|
|
106
|
+
if (fileChanged) {
|
|
98
107
|
aioLogger.debug('Code changed during deployment. Triggering deploy again.')
|
|
99
|
-
|
|
100
|
-
await createChangeHandler(watcherOptions)(
|
|
108
|
+
fileChanged = deploymentInProgress = false
|
|
109
|
+
await createChangeHandler(watcherOptions)(undeployedFile)
|
|
101
110
|
}
|
|
102
|
-
|
|
111
|
+
deploymentInProgress = false
|
|
103
112
|
}
|
|
104
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Util function which returns the actionName from the filePath.
|
|
117
|
+
*
|
|
118
|
+
* @param {string} filePath path of the file
|
|
119
|
+
* @param {WatcherOptions} watcherOptions the options for the watcher
|
|
120
|
+
* @returns {Array<string>} All of the actions which match the modified path
|
|
121
|
+
*/
|
|
122
|
+
function getActionNameFromPath (filePath, watcherOptions) {
|
|
123
|
+
const actionNames = []
|
|
124
|
+
const unixFilePath = upath.toUnix(filePath)
|
|
125
|
+
const { config } = watcherOptions
|
|
126
|
+
Object.entries(config.manifest.full.packages).forEach(([, pkg]) => {
|
|
127
|
+
if (pkg.actions) {
|
|
128
|
+
Object.entries(pkg.actions).forEach(([actionName, action]) => {
|
|
129
|
+
const unixActionFunction = upath.toUnix(action.function)
|
|
130
|
+
if (unixActionFunction.includes(unixFilePath)) {
|
|
131
|
+
actionNames.push(actionName)
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
return actionNames
|
|
137
|
+
}
|
package/src/lib/build-actions.js
CHANGED
|
@@ -17,12 +17,13 @@ const { buildActions } = require('@adobe/aio-lib-runtime')
|
|
|
17
17
|
* Builds actions.
|
|
18
18
|
*
|
|
19
19
|
* @param {object} config see src/lib/config-loader.js
|
|
20
|
+
* @param {Array<string>} filterActions add filters to deploy only specified OpenWhisk actions
|
|
20
21
|
*/
|
|
21
|
-
module.exports = async (config) => {
|
|
22
|
+
module.exports = async (config, filterActions) => {
|
|
22
23
|
utils.runScript(config.hooks['pre-app-build'])
|
|
23
24
|
const script = await utils.runScript(config.hooks['build-actions'])
|
|
24
25
|
if (!script) {
|
|
25
|
-
await buildActions(config)
|
|
26
|
+
await buildActions(config, filterActions)
|
|
26
27
|
}
|
|
27
28
|
utils.runScript(config.hooks['post-app-build'])
|
|
28
29
|
}
|
|
@@ -17,15 +17,22 @@ const { deployActions } = require('@adobe/aio-lib-runtime')
|
|
|
17
17
|
* Deploys actions.
|
|
18
18
|
*
|
|
19
19
|
* @param {object} config see src/lib/config-loader.js
|
|
20
|
-
* @param {boolean} isLocal
|
|
20
|
+
* @param {boolean} isLocal default false, set to true if it's a local deploy
|
|
21
21
|
* @param {Function} [log] a log function
|
|
22
|
+
* @param {boolean} filter true if a filter by built actions is desired.
|
|
22
23
|
*/
|
|
23
24
|
/** @private */
|
|
24
|
-
module.exports = async (config, isLocal = false, log = () => {}) => {
|
|
25
|
+
module.exports = async (config, isLocal = false, log = () => {}, filter = false) => {
|
|
25
26
|
utils.runScript(config.hooks['pre-app-deploy'])
|
|
26
27
|
const script = await utils.runScript(config.hooks['deploy-actions'])
|
|
27
28
|
if (!script) {
|
|
28
|
-
const
|
|
29
|
+
const deployConfig = {
|
|
30
|
+
isLocalDev: isLocal,
|
|
31
|
+
filterEntities: {
|
|
32
|
+
byBuiltActions: filter
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const entities = await deployActions(config, deployConfig, log)
|
|
29
36
|
if (entities.actions) {
|
|
30
37
|
const web = entities.actions.filter(utils.createWebExportFilter(true))
|
|
31
38
|
const nonWeb = entities.actions.filter(utils.createWebExportFilter(false))
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020 Adobe. All rights reserved.
|
|
3
|
+
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
|
|
7
|
+
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const rtLib = require('@adobe/aio-lib-runtime')
|
|
14
|
+
const { writeAio, writeEnv } = require('./import')
|
|
15
|
+
const crypto = require('crypto')
|
|
16
|
+
const fs = require('fs-extra')
|
|
17
|
+
const path = require('path')
|
|
18
|
+
|
|
19
|
+
const SECRET_FIELD_TYPE = 'password'
|
|
20
|
+
const CHECKSUM_DIR = 'dist'
|
|
21
|
+
const CHECKSUM_FILE = 'log-forwarding-config.sha256'
|
|
22
|
+
const IGNORED_REMOTE_SETTINGS = ['updated_at']
|
|
23
|
+
|
|
24
|
+
class LogForwarding {
|
|
25
|
+
constructor (aioConfig) {
|
|
26
|
+
this.aioConfig = aioConfig
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async init () {
|
|
30
|
+
rtLib.utils.checkOpenWhiskCredentials({ ow: this.aioConfig.runtime })
|
|
31
|
+
this.logForwarding = await getRTLogForwarding(this.aioConfig.runtime)
|
|
32
|
+
return this
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getLocalConfig () {
|
|
36
|
+
const config = this.aioConfig.project.workspace.log_forwarding
|
|
37
|
+
try {
|
|
38
|
+
return this.getConfigFromJson(config)
|
|
39
|
+
} catch (e) {
|
|
40
|
+
throw new Error('Incorrect local log forwarding configuration. ' + e.message)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getLocalConfigWithSecrets () {
|
|
45
|
+
const config = this.getLocalConfig()
|
|
46
|
+
const destination = config.getDestination()
|
|
47
|
+
const settings = config.getSettings()
|
|
48
|
+
if (config.isDefined()) {
|
|
49
|
+
const destinationSettings = this.logForwarding.getDestinationSettings(destination)
|
|
50
|
+
const missingSecrets = []
|
|
51
|
+
destinationSettings.forEach(e => {
|
|
52
|
+
if (e.type === SECRET_FIELD_TYPE) {
|
|
53
|
+
const secretVarName = getSecretVarName(destination, e.name)
|
|
54
|
+
if (process.env[secretVarName] !== undefined) {
|
|
55
|
+
settings[e.name] = process.env[secretVarName]
|
|
56
|
+
} else {
|
|
57
|
+
missingSecrets.push(secretVarName)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
if (missingSecrets.length > 0) {
|
|
62
|
+
throw new Error('Required secrets are missing in environment variables: ' + missingSecrets.join(', ') + '. ' +
|
|
63
|
+
'Make sure these variables are set in .env file')
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return new LogForwardingConfig(destination, settings)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async getServerConfig () {
|
|
70
|
+
try {
|
|
71
|
+
return this.getConfigFromJson(await this.logForwarding.get())
|
|
72
|
+
} catch (e) {
|
|
73
|
+
throw new Error('Incorrect log forwarding configuration on server. ' + e.message)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Convert JSON config to Log Forwarding object
|
|
79
|
+
*
|
|
80
|
+
* @param {object} configJson Config in JSON format
|
|
81
|
+
* @returns {LogForwardingConfig} Config
|
|
82
|
+
*/
|
|
83
|
+
getConfigFromJson (configJson) {
|
|
84
|
+
let destination
|
|
85
|
+
let settings
|
|
86
|
+
|
|
87
|
+
if (configJson !== undefined && configJson !== null && !Array.isArray(configJson) && typeof configJson === 'object') {
|
|
88
|
+
const destinations = Object.keys(configJson)
|
|
89
|
+
if (destinations.length === 1) {
|
|
90
|
+
destination = destinations[0]
|
|
91
|
+
settings = configJson[destination]
|
|
92
|
+
} else {
|
|
93
|
+
throw new Error(`Configuration has ${destinations.length} destinations. Exactly one must be defined.`)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return new LogForwardingConfig(destination, settings)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getSupportedDestinations () {
|
|
100
|
+
return this.logForwarding.getSupportedDestinations()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getSettingsConfig (destination) {
|
|
104
|
+
return this.logForwarding.getDestinationSettings(destination)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async updateLocalConfig (lfConfig) {
|
|
108
|
+
const destination = lfConfig.getDestination()
|
|
109
|
+
const destinationSettings = this.logForwarding.getDestinationSettings(destination)
|
|
110
|
+
const projectConfig = {
|
|
111
|
+
project: this.aioConfig.project
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const nonSecretSettings = {}
|
|
115
|
+
const secretSettings = {}
|
|
116
|
+
|
|
117
|
+
const settings = lfConfig.getSettings()
|
|
118
|
+
Object.keys(settings)
|
|
119
|
+
.filter(e => !IGNORED_REMOTE_SETTINGS.includes(e))
|
|
120
|
+
.forEach(k => {
|
|
121
|
+
const destFieldSettings = destinationSettings.find(i => i.name === k)
|
|
122
|
+
if (destFieldSettings.type === SECRET_FIELD_TYPE) {
|
|
123
|
+
secretSettings[getSecretVarName(destination, k)] = settings[k]
|
|
124
|
+
} else {
|
|
125
|
+
nonSecretSettings[k] = settings[k]
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
projectConfig.project.workspace.log_forwarding = {
|
|
130
|
+
[destination]: nonSecretSettings
|
|
131
|
+
}
|
|
132
|
+
const interactive = false
|
|
133
|
+
const merge = true
|
|
134
|
+
await writeAio(projectConfig, '', { interactive, merge })
|
|
135
|
+
await writeEnv({}, '', { interactive, merge }, secretSettings)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
isLocalConfigChanged () {
|
|
139
|
+
if (fs.pathExistsSync(path.join(CHECKSUM_DIR, CHECKSUM_FILE))) {
|
|
140
|
+
const oldChecksum = fs.readFileSync(path.join(CHECKSUM_DIR, CHECKSUM_FILE)).toString()
|
|
141
|
+
const config = this.getLocalConfigWithSecrets()
|
|
142
|
+
const newChecksum = getChecksum(config)
|
|
143
|
+
return oldChecksum !== newChecksum
|
|
144
|
+
} else {
|
|
145
|
+
return true
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async updateServerConfig (lfConfig) {
|
|
150
|
+
const res = await this.logForwarding.setDestination(lfConfig.getDestination(), lfConfig.getSettings())
|
|
151
|
+
const checksum = getChecksum(lfConfig)
|
|
152
|
+
fs.ensureDirSync(CHECKSUM_DIR)
|
|
153
|
+
fs.writeFile(path.join(CHECKSUM_DIR, CHECKSUM_FILE), checksum, { flags: 'w' })
|
|
154
|
+
return res
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
class LogForwardingConfig {
|
|
159
|
+
constructor (destination, settings) {
|
|
160
|
+
this.destination = destination
|
|
161
|
+
this.settings = settings
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
getDestination () {
|
|
165
|
+
return this.destination
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getSettings () {
|
|
169
|
+
return this.settings
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
getMergedConfig (config) {
|
|
173
|
+
const newSettings = {}
|
|
174
|
+
Object.keys(this.settings).forEach(k => {
|
|
175
|
+
newSettings[k] = config.settings[k] !== undefined ? config.settings[k] : this.settings[k]
|
|
176
|
+
})
|
|
177
|
+
return new LogForwardingConfig(this.destination, newSettings)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
isDefined () {
|
|
181
|
+
return this.destination !== undefined
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
isDefault () {
|
|
185
|
+
return !this.isDefined() || this.getDestination() === 'adobe_io_runtime'
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
isEqual (config) {
|
|
189
|
+
return (this.isDefault() && config.isDefault()) ||
|
|
190
|
+
(this.destination === config.getDestination() && shallowEqual(this.settings, config.settings))
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Init Log Forwarding
|
|
196
|
+
*
|
|
197
|
+
* @param {object} aioConfig aio Config
|
|
198
|
+
* @returns {Promise<LogForwarding>} Log Forwarding
|
|
199
|
+
*/
|
|
200
|
+
async function init (aioConfig) {
|
|
201
|
+
const lf = new LogForwarding(aioConfig)
|
|
202
|
+
return await lf.init()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get Runtime Log Forwarding
|
|
207
|
+
*
|
|
208
|
+
* @param {object} rtConfig Runtime config
|
|
209
|
+
* @returns {Promise<LogForwarding>} Log Forwarding
|
|
210
|
+
*/
|
|
211
|
+
async function getRTLogForwarding (rtConfig) {
|
|
212
|
+
const rt = await rtLib.init({
|
|
213
|
+
...rtConfig,
|
|
214
|
+
api_key: rtConfig.auth
|
|
215
|
+
})
|
|
216
|
+
return rt.logForwarding
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Compare to log forwarding configs
|
|
221
|
+
*
|
|
222
|
+
* @param {LogForwardingConfig} config1 Config
|
|
223
|
+
* @param {LogForwardingConfig} config2 Config
|
|
224
|
+
* @returns {boolean} Are configs equal
|
|
225
|
+
*/
|
|
226
|
+
function shallowEqual (config1, config2) {
|
|
227
|
+
// updated_at exists on server only and does not impact actual configuration
|
|
228
|
+
const keys1 = Object.keys(config1).filter(e => !IGNORED_REMOTE_SETTINGS.includes(e))
|
|
229
|
+
const keys2 = Object.keys(config2).filter(e => !IGNORED_REMOTE_SETTINGS.includes(e))
|
|
230
|
+
if (keys1.length !== keys2.length) {
|
|
231
|
+
return false
|
|
232
|
+
}
|
|
233
|
+
for (const key of keys1) {
|
|
234
|
+
if (config1[key] !== config2[key]) {
|
|
235
|
+
return false
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return true
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get secret variable name for the given destination and settings field
|
|
243
|
+
*
|
|
244
|
+
* @param {string} destination Destination
|
|
245
|
+
* @param {string} fieldName Field name
|
|
246
|
+
* @returns {string} Variable name
|
|
247
|
+
*/
|
|
248
|
+
function getSecretVarName (destination, fieldName) {
|
|
249
|
+
return destination.toUpperCase() + '__' + fieldName.toUpperCase()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Generate checksum for the config
|
|
254
|
+
*
|
|
255
|
+
* @param {LogForwardingConfig} config Config
|
|
256
|
+
* @returns {string} Checksum
|
|
257
|
+
*/
|
|
258
|
+
function getChecksum (config) {
|
|
259
|
+
return crypto.createHash('sha256')
|
|
260
|
+
.update(JSON.stringify(config))
|
|
261
|
+
.digest('hex')
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module.exports = {
|
|
265
|
+
init,
|
|
266
|
+
LogForwardingConfig
|
|
267
|
+
}
|
package/src/lib/run-dev.js
CHANGED
|
@@ -122,7 +122,7 @@ async function runDev (config, dataDir, options = {}, log = () => {}) {
|
|
|
122
122
|
// Deploy Phase - deploy actions
|
|
123
123
|
if (withBackend) {
|
|
124
124
|
log('redeploying actions..')
|
|
125
|
-
await deployActions(devConfig, isLocal, log)
|
|
125
|
+
await deployActions(devConfig, isLocal, log, true)
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// Deploy Phase - serve the web UI
|
package/src/lib/vscode.js
CHANGED
|
@@ -47,7 +47,9 @@ function update (config) {
|
|
|
47
47
|
'app-config': config,
|
|
48
48
|
'env-file': config.envFile,
|
|
49
49
|
'frontend-url': props.frontEndUrl,
|
|
50
|
-
'skip-prompt': true
|
|
50
|
+
'skip-prompt': true,
|
|
51
|
+
// by default yeoman runs the install, we control installation from the app plugin
|
|
52
|
+
'skip-install': true
|
|
51
53
|
}
|
|
52
54
|
})
|
|
53
55
|
await env.runGenerator(gen)
|