@automattic/vip 2.27.0 → 2.28.0-dev7

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.
Binary file
@@ -17,5 +17,5 @@
17
17
  var _command = _interopRequireDefault(require("../lib/cli/command"));
18
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
19
  (0, _command.default)({
20
- requiredArgs: 2
20
+ requiredArgs: 1
21
21
  }).command('purge-url', 'Purge page cache').argv(process.argv);
@@ -17,5 +17,5 @@
17
17
  var _command = _interopRequireDefault(require("../lib/cli/command"));
18
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
19
  (0, _command.default)({
20
- requiredArgs: 1
20
+ requiredArgs: 0
21
21
  }).command('delete', 'Permanently delete an environment variable').command('get', 'Get the value of an environment variable').command('get-all', 'Get the values of all environment variable').command('list', 'List the names of all environment variables').command('set', 'Add or update an environment variable').argv(process.argv);
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ *
5
+ * @format
6
+ */
7
+
8
+ /**
9
+ * External dependencies
10
+ */
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ "use strict";
16
+
17
+ var _command = _interopRequireDefault(require("../lib/cli/command"));
18
+ var _devEnvironmentCore = require("../lib/dev-environment/dev-environment-core");
19
+ var _devEnvironmentLando = require("../lib/dev-environment/dev-environment-lando");
20
+ var _userError = _interopRequireDefault(require("../lib/user-error"));
21
+ var _devEnvSyncSql = require("../commands/dev-env-sync-sql");
22
+ var _devEnvironment = require("../lib/constants/dev-environment");
23
+ var _tracker = require("../lib/tracker");
24
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
+ const examples = [{
26
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} sync sql @my-test.develop --slug=my_site`,
27
+ description: 'Syncs with the `my-test` site\'s `develop` environment database into `my_site`'
28
+ }];
29
+ const appQuery = `
30
+ id,
31
+ name,
32
+ type,
33
+ organization { id, name },
34
+ environments{
35
+ id
36
+ appId
37
+ type
38
+ name
39
+ primaryDomain { name }
40
+ uniqueLabel
41
+ }
42
+ `;
43
+ (0, _command.default)({
44
+ appContext: true,
45
+ appQuery,
46
+ envContext: true,
47
+ requiredArgs: 0,
48
+ module: 'dev-env-sync-sql'
49
+ }).option('slug', 'Custom name of the dev environment').examples(examples).argv(process.argv, async (arg, {
50
+ app,
51
+ env,
52
+ slug
53
+ }) => {
54
+ const trackerFn = (0, _tracker.makeCommandTracker)('dev_env_sync_sql', {
55
+ app: app.id,
56
+ env: env.uniqueLabel,
57
+ slug
58
+ });
59
+ await trackerFn('execute');
60
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)();
61
+ const envPath = (0, _devEnvironmentCore.getEnvironmentPath)(slug);
62
+ if (!(await (0, _devEnvironmentLando.isEnvUp)(lando, envPath))) {
63
+ await trackerFn('env_not_running_error', {
64
+ errorMessage: 'Environment was not running'
65
+ });
66
+ throw new _userError.default('Environment needs to be started first');
67
+ }
68
+ const cmd = new _devEnvSyncSql.DevEnvSyncSQLCommand(app, env, slug, trackerFn);
69
+ await cmd.run();
70
+ await trackerFn('success');
71
+ });
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ *
5
+ * @format
6
+ */
7
+
8
+ /**
9
+ * External dependencies
10
+ */
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ "use strict";
16
+
17
+ var _command = _interopRequireDefault(require("../lib/cli/command"));
18
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
+ (0, _command.default)({
20
+ requiredArgs: 1
21
+ }).command('sql', 'Sync local database with a production environment').argv(process.argv);
@@ -17,5 +17,5 @@
17
17
  var _command = _interopRequireDefault(require("../lib/cli/command"));
18
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
19
  (0, _command.default)({
20
- requiredArgs: 1
21
- }).command('create', 'Create a new local dev environment').command('update', 'Update an already created local dev environment').command('start', 'Start a local dev environment').command('stop', 'Stop a local dev environment').command('destroy', 'Remove containers, networks, volumes and configuration files of a local dev environment').command('info', 'Provides basic info about one or multiple local dev environments').command('list', 'Provides basic info about all local dev environments').command('exec', 'Execute a WP-CLI command in local dev environment').command('import', 'Import data into a local WordPress environment').command('shell', 'Spawns a shell in a dev environment').command('logs', 'View logs from a local WordPress environment').argv(process.argv);
20
+ requiredArgs: 0
21
+ }).command('create', 'Create a new local dev environment').command('update', 'Update an already created local dev environment').command('start', 'Start a local dev environment').command('stop', 'Stop a local dev environment').command('destroy', 'Remove containers, networks, volumes and configuration files of a local dev environment').command('info', 'Provides basic info about one or multiple local dev environments').command('list', 'Provides basic info about all local dev environments').command('exec', 'Execute a WP-CLI command in local dev environment').command('import', 'Import data into a local WordPress environment').command('shell', 'Spawns a shell in a dev environment').command('logs', 'View logs from a local WordPress environment').command('sync', 'Pull data from production to local development environment').argv(process.argv);
@@ -13,7 +13,7 @@ var _command = _interopRequireDefault(require("../lib/cli/command"));
13
13
  var _tracker = require("../lib/tracker");
14
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
15
  (0, _command.default)({
16
- requiredArgs: 1
16
+ requiredArgs: 0
17
17
  }).command('preflight', 'Runs preflight tests to validate if your application is ready to be deployed').argv(process.argv, async () => {
18
18
  await (0, _tracker.trackEvent)('vip_validate_command_execute');
19
19
  });
@@ -16,6 +16,7 @@ Object.defineProperty(exports, "__esModule", {
16
16
  exports.DevEnvSyncSQLCommand = void 0;
17
17
  var _fs = _interopRequireDefault(require("fs"));
18
18
  var _chalk = _interopRequireDefault(require("chalk"));
19
+ var _url = _interopRequireDefault(require("url"));
19
20
  var _vipSearchReplace = require("@automattic/vip-search-replace");
20
21
  var _clientFileUploader = require("../lib/client-file-uploader");
21
22
  var _exportSql = require("./export-sql");
@@ -39,8 +40,8 @@ function findSiteHomeUrl(sql) {
39
40
  const regex = "'(siteurl|home)',\\s?'(.*?)'";
40
41
  const results = sql.match(regex);
41
42
  if (results) {
42
- const domain = results[2].replace(/https?:\/\//, '');
43
- return domain;
43
+ const url = results[2];
44
+ return _url.default.parse(url).hostname;
44
45
  }
45
46
  return null;
46
47
  }
@@ -55,15 +56,15 @@ function findSiteHomeUrl(sql) {
55
56
  async function extractSiteUrls(sqlFile) {
56
57
  const readInterface = await (0, _lineByLine.getReadInterface)(sqlFile);
57
58
  return new Promise((resolve, reject) => {
58
- const domains = [];
59
+ const domains = new Set();
59
60
  readInterface.on('line', line => {
60
61
  const domain = findSiteHomeUrl(line);
61
62
  if (domain) {
62
- domains.push(domain);
63
+ domains.add('//' + domain);
63
64
  }
64
65
  });
65
66
  readInterface.on('close', () => {
66
- resolve(domains);
67
+ resolve(Array.from(domains));
67
68
  });
68
69
  readInterface.on('error', reject);
69
70
  });
@@ -72,18 +73,20 @@ class DevEnvSyncSQLCommand {
72
73
  /**
73
74
  * Creates a new instance of the command
74
75
  *
75
- * @param {string} app The app object
76
- * @param {string} env The environment object
77
- * @param {string} slug The site slug
76
+ * @param {string} app The app object
77
+ * @param {string} env The environment object
78
+ * @param {string} slug The site slug
79
+ * @param {Function} trackerFn Function to call for tracking
78
80
  */
79
- constructor(app, env, slug) {
81
+ constructor(app, env, slug, trackerFn = () => {}) {
80
82
  this.app = app;
81
83
  this.env = env;
82
84
  this.slug = slug;
85
+ this.track = trackerFn;
83
86
  this.tmpDir = (0, _utils.makeTempDir)();
84
87
  }
85
88
  get landoDomain() {
86
- return `${this.slug}.vipdev.lndo.site`;
89
+ return `//${this.slug}.vipdev.lndo.site`;
87
90
  }
88
91
  get sqlFile() {
89
92
  return `${this.tmpDir}/sql-export.sql`;
@@ -99,7 +102,7 @@ class DevEnvSyncSQLCommand {
99
102
  * @return {Promise<void>} Promise that resolves when the export is complete
100
103
  */
101
104
  async generateExport() {
102
- const exportCommand = new _exportSql.ExportSQLCommand(this.app, this.env, this.gzFile);
105
+ const exportCommand = new _exportSql.ExportSQLCommand(this.app, this.env, this.gzFile, this.track);
103
106
  await exportCommand.run();
104
107
  }
105
108
 
@@ -114,9 +117,13 @@ class DevEnvSyncSQLCommand {
114
117
  const replacements = this.siteUrls.reduce((acc, url) => [...acc, url, this.landoDomain], []);
115
118
  const readStream = _fs.default.createReadStream(this.sqlFile);
116
119
  const replacedStream = await (0, _vipSearchReplace.replace)(readStream, replacements);
117
- replacedStream.pipe(_fs.default.createWriteStream(this.sqlFile));
120
+ const outputFile = `${this.tmpDir}/sql-export-sr.sql`;
121
+ replacedStream.pipe(_fs.default.createWriteStream(outputFile));
118
122
  return new Promise((resolve, reject) => {
119
- replacedStream.on('finish', resolve);
123
+ replacedStream.on('finish', () => {
124
+ _fs.default.renameSync(outputFile, this.sqlFile);
125
+ resolve();
126
+ });
120
127
  replacedStream.on('error', reject);
121
128
  });
122
129
  }
@@ -129,11 +136,10 @@ class DevEnvSyncSQLCommand {
129
136
  */
130
137
  async runImport() {
131
138
  const importOptions = {
132
- slug: this.slug,
133
139
  inPlace: true,
134
140
  skipValidate: true
135
141
  };
136
- const importCommand = new _devEnvImportSql.DevEnvImportSQLCommand(this.sqlFile, importOptions);
142
+ const importCommand = new _devEnvImportSql.DevEnvImportSQLCommand(this.sqlFile, importOptions, this.slug);
137
143
  await importCommand.run(true);
138
144
  }
139
145
 
@@ -154,6 +160,9 @@ class DevEnvSyncSQLCommand {
154
160
  await (0, _clientFileUploader.unzipFile)(this.gzFile, this.sqlFile);
155
161
  console.log(`${_chalk.default.green('✓')} Extracted to ${this.sqlFile}`);
156
162
  } catch (err) {
163
+ await this.track('archive_extraction_error', {
164
+ errorMessage: err === null || err === void 0 ? void 0 : err.message
165
+ });
157
166
  exit.withError(`Error extracting the SQL export: ${err === null || err === void 0 ? void 0 : err.message}`);
158
167
  }
159
168
  try {
@@ -170,6 +179,9 @@ class DevEnvSyncSQLCommand {
170
179
  await this.runSearchReplace();
171
180
  console.log(`${_chalk.default.green('✓')} Search-replace operation is complete`);
172
181
  } catch (err) {
182
+ await this.track('search_replace_error', {
183
+ errorMessage: err === null || err === void 0 ? void 0 : err.message
184
+ });
173
185
  exit.withError(`Error replacing domains: ${err === null || err === void 0 ? void 0 : err.message}`);
174
186
  }
175
187
  try {
@@ -177,6 +189,9 @@ class DevEnvSyncSQLCommand {
177
189
  await this.runImport();
178
190
  console.log(`${_chalk.default.green('✓')} SQL file imported`);
179
191
  } catch (err) {
192
+ await this.track('import_error', {
193
+ errorMessage: err === null || err === void 0 ? void 0 : err.message
194
+ });
180
195
  exit.withError(`Error importing SQL file: ${err === null || err === void 0 ? void 0 : err.message}`);
181
196
  }
182
197
  }
@@ -8,7 +8,7 @@ var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
8
8
  var _fs = _interopRequireDefault(require("fs"));
9
9
  var _https = _interopRequireDefault(require("https"));
10
10
  var _path = _interopRequireDefault(require("path"));
11
- var _api = _interopRequireDefault(require("../lib/api"));
11
+ var _api = _interopRequireWildcard(require("../lib/api"));
12
12
  var _format = require("../lib/cli/format");
13
13
  var _progress = require("../lib/cli/progress");
14
14
  var exit = _interopRequireWildcard(require("../lib/cli/exit"));
@@ -162,8 +162,10 @@ async function generateDownloadLink(appId, envId, backupId) {
162
162
  * @throws {Error} Throws an error if the job creation fails
163
163
  */
164
164
  async function createExportJob(appId, envId, backupId) {
165
+ // Disable global error handling so that we can handle errors ourselves
166
+ (0, _api.disableGlobalGraphQLErrorHandling)();
165
167
  const api = await (0, _api.default)();
166
- const response = await api.mutate({
168
+ await api.mutate({
167
169
  mutation: CREATE_EXPORT_JOB_MUTATION,
168
170
  variables: {
169
171
  input: {
@@ -173,16 +175,9 @@ async function createExportJob(appId, envId, backupId) {
173
175
  }
174
176
  }
175
177
  });
176
- const {
177
- data: {
178
- startDBBackupCopy: {
179
- success
180
- }
181
- }
182
- } = response;
183
- if (!success) {
184
- throw new Error();
185
- }
178
+
179
+ // Re-enable global error handling
180
+ (0, _api.enableGlobalGraphQLErrorHandling)();
186
181
  }
187
182
 
188
183
  /**
@@ -195,15 +190,15 @@ class ExportSQLCommand {
195
190
  DOWNLOAD_LINK: 'downloadLink',
196
191
  DOWNLOAD: 'download'
197
192
  };
198
-
199
193
  /**
200
194
  * Creates an instance of SQLExportCommand
201
195
  *
202
- * @param {any} app The application object
203
- * @param {any} env The environment object
204
- * @param {string} outputFile The output file path
196
+ * @param {any} app The application object
197
+ * @param {any} env The environment object
198
+ * @param {string} outputFile The output file path
199
+ * @param {Function} trackerFn The progress tracker function
205
200
  */
206
- constructor(app, env, outputFile) {
201
+ constructor(app, env, outputFile, trackerFn = () => {}) {
207
202
  this.app = app;
208
203
  this.env = env;
209
204
  this.outputFile = outputFile;
@@ -220,6 +215,7 @@ class ExportSQLCommand {
220
215
  id: this.steps.DOWNLOAD,
221
216
  name: 'Downloading file'
222
217
  }]);
218
+ this.track = trackerFn;
223
219
  }
224
220
 
225
221
  /**
@@ -319,21 +315,29 @@ class ExportSQLCommand {
319
315
  latestBackup
320
316
  } = await fetchLatestBackupAndJobStatus(this.app.id, this.env.id);
321
317
  if (!latestBackup) {
318
+ await this.track('no_backup_found_error', {
319
+ errorMessage: 'No backup found for the site'
320
+ });
322
321
  exit.withError(`No backup found for site ${this.app.name}`);
323
322
  } else {
324
323
  console.log(`${(0, _format.getGlyphForStatus)('success')} Latest backup found with timestamp ${latestBackup.createdAt}`);
325
324
  }
326
-
327
- // See if there is an existing export job for the latest backup
328
- if (!(await this.getExportJob())) {
325
+ if (await this.getExportJob()) {
326
+ console.log(`Attaching to an existing export for the backup with timestamp ${latestBackup.createdAt}`);
327
+ } else {
329
328
  console.log(`Creating a new export for the backup with timestamp ${latestBackup.createdAt}`);
330
329
  try {
331
330
  await createExportJob(this.app.id, this.env.id, latestBackup.id);
332
331
  } catch (err) {
332
+ // Todo: match error code instead of message substring
333
+ if (err !== null && err !== void 0 && err.message.includes('Backup Copy already in progress')) {
334
+ await this.track('export_job_already_running_error', {
335
+ errorMessage: err === null || err === void 0 ? void 0 : err.message
336
+ });
337
+ exit.withError('There is an export job already running for this site: ' + `https://dashboard.wpvip.com/apps/${this.app.id}/${this.env.uniqueLabel}/data/database/backups\n` + 'Currently, we allow only one export job per site. Please try again later.');
338
+ }
333
339
  exit.withError(`Error creating export job: ${err === null || err === void 0 ? void 0 : err.message}`);
334
340
  }
335
- } else {
336
- console.log(`Attaching to an existing export for the backup with timestamp ${latestBackup.createdAt}`);
337
341
  }
338
342
  this.progressTracker.stepRunning(this.steps.PREPARE);
339
343
  this.progressTracker.startPrinting();
@@ -353,6 +357,9 @@ class ExportSQLCommand {
353
357
  } catch (err) {
354
358
  this.progressTracker.stepFailed(this.steps.DOWNLOAD);
355
359
  this.stopProgressTracker();
360
+ await this.track('download_failed_error', {
361
+ errorMessage: err === null || err === void 0 ? void 0 : err.message
362
+ });
356
363
  exit.withError(`Error downloading exported file: ${err === null || err === void 0 ? void 0 : err.message}`);
357
364
  }
358
365
  }
@@ -125,7 +125,6 @@ async function landoRecovery(lando, instancePath, error) {
125
125
  console.error(_chalk.default.green('Recovery successful, trying to initialize again...'));
126
126
  try {
127
127
  const app = lando.getApp(instancePath);
128
- addHooks(app, lando);
129
128
  await app.init();
130
129
  return app;
131
130
  } catch (initError) {
@@ -143,7 +142,6 @@ async function getLandoApplication(lando, instancePath) {
143
142
  let app;
144
143
  try {
145
144
  app = lando.getApp(instancePath);
146
- await addHooks(app, lando);
147
145
  await app.init();
148
146
  } catch (error) {
149
147
  app = await landoRecovery(lando, instancePath, error);
@@ -153,6 +151,28 @@ async function getLandoApplication(lando, instancePath) {
153
151
  }
154
152
  async function bootstrapLando() {
155
153
  const lando = new _lando.default(await getLandoConfig());
154
+ lando.events.once('pre-engine-build', async data => {
155
+ const instanceData = (0, _devEnvironmentCore.readEnvironmentData)(data.name);
156
+ let registryResolvable = false;
157
+ try {
158
+ registryResolvable = (await _dns.default.promises.lookup('ghcr.io')).address || false;
159
+ debug('Registry ghcr.io is resolvable');
160
+ } catch (err) {
161
+ debug('Registry ghcr.io is not resolvable, image pull might be broken.');
162
+ registryResolvable = false;
163
+ }
164
+ const pull = registryResolvable && (instanceData.pullAfter || 0) < Date.now();
165
+ if (Array.isArray(data.opts.pullable) && Array.isArray(data.opts.local) && data.opts.local.length === 0 && !pull) {
166
+ // Setting `data.opts.pullable` to an empty array prevents Lando from pulling images with `docker pull`.
167
+ // Note that if some of the images are not available, they will still be pulled by `docker-compose`.
168
+ data.opts.local = data.opts.pullable;
169
+ data.opts.pullable = [];
170
+ }
171
+ if (pull || !instanceData.pullAfter) {
172
+ instanceData.pullAfter = Date.now() + 7 * 24 * 60 * 60 * 1000;
173
+ (0, _devEnvironmentCore.writeEnvironmentData)(data.name, instanceData);
174
+ }
175
+ });
156
176
  await lando.bootstrap();
157
177
  return lando;
158
178
  }
@@ -178,85 +198,6 @@ async function landoRebuild(lando, instancePath) {
178
198
  await ensureNoOrphantProxyContainer(lando);
179
199
  await app.rebuild();
180
200
  }
181
- async function addHooks(app, lando) {
182
- app.events.on('post-start', 1, () => healthcheckHook(app, lando));
183
- lando.events.once('pre-engine-build', async data => {
184
- const instanceData = (0, _devEnvironmentCore.readEnvironmentData)(app._name);
185
- let registryResolvable = false;
186
- try {
187
- registryResolvable = (await _dns.default.promises.lookup('ghcr.io')).address || false;
188
- debug('Registry ghcr.io is resolvable');
189
- } catch (err) {
190
- debug('Registry ghcr.io is not resolvable, image pull might be broken.');
191
- registryResolvable = false;
192
- }
193
- const pull = registryResolvable && (instanceData.pullAfter || 0) < Date.now();
194
- if (Array.isArray(data.opts.pullable) && Array.isArray(data.opts.local) && data.opts.local.length === 0 && !pull) {
195
- // Settigs `data.opts.pullable` to an empty array prevents Lando from pulling images with `docker pull`.
196
- // Note that if some of the images are not available, they will still be pulled by `docker-compose`.
197
- data.opts.local = data.opts.pullable;
198
- data.opts.pullable = [];
199
- }
200
- if (pull || !instanceData.pullAfter) {
201
- instanceData.pullAfter = Date.now() + 7 * 24 * 60 * 60 * 1000;
202
- (0, _devEnvironmentCore.writeEnvironmentData)(app._name, instanceData);
203
- }
204
- });
205
- }
206
- const healthChecks = {
207
- database: 'mysql -uroot --silent --execute "SHOW DATABASES;"',
208
- elasticsearch: "curl -s --noproxy '*' -XGET localhost:9200",
209
- php: '[[ -f /wp/wp-includes/pomo/mo.php ]]'
210
- };
211
- async function healthcheckHook(app, lando) {
212
- const now = new Date();
213
- try {
214
- await lando.Promise.retry(async () => {
215
- const list = await lando.engine.list({
216
- project: app.project
217
- });
218
- const notHealthyContainers = [];
219
- const checkPromises = [];
220
- const containerOrder = [];
221
- for (const container of list) {
222
- if (healthChecks[container.service]) {
223
- debug(`Testing ${container.service}: ${healthChecks[container.service]}`);
224
- containerOrder.push(container);
225
- checkPromises.push(app.engine.run({
226
- id: container.id,
227
- cmd: healthChecks[container.service],
228
- compose: app.compose,
229
- project: app.project,
230
- opts: {
231
- silent: true,
232
- noTTY: true,
233
- cstdio: 'pipe',
234
- services: [container.service]
235
- }
236
- }));
237
- }
238
- }
239
- const results = await Promise.allSettled(checkPromises);
240
- results.forEach((result, index) => {
241
- if (result.status === 'rejected') {
242
- debug(`${containerOrder[index].service} Health check failed`);
243
- notHealthyContainers.push(containerOrder[index]);
244
- }
245
- });
246
- if (notHealthyContainers.length) {
247
- notHealthyContainers.forEach(container => console.log(`Waiting for service ${container.service} ...`));
248
- return Promise.reject(notHealthyContainers);
249
- }
250
- }, {
251
- max: 20,
252
- backoff: 1000
253
- });
254
- } catch (containersWithFailingHealthCheck) {
255
- containersWithFailingHealthCheck.forEach(container => console.log(_chalk.default.yellow('WARNING:') + ` Service ${container.service} failed healthcheck`));
256
- }
257
- const duration = new Date().getTime() - now.getTime();
258
- debug(`Healthcheck completed in ${duration}ms`);
259
- }
260
201
  async function landoStop(lando, instancePath) {
261
202
  debug('Will stop lando app on path:', instancePath);
262
203
  const app = await getLandoApplication(lando, instancePath);
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.aliasUser = aliasUser;
7
+ exports.makeCommandTracker = makeCommandTracker;
7
8
  exports.trackEvent = trackEvent;
8
9
  exports.trackEventWithEnv = trackEventWithEnv;
9
10
  var _index = _interopRequireDefault(require("./analytics/index"));
@@ -77,4 +78,13 @@ async function trackEventWithEnv(appId, envId, eventName, eventProps = {}) {
77
78
  app_id: appId,
78
79
  env_id: envId
79
80
  });
81
+ }
82
+ function makeCommandTracker(command, trackingInfo = {}) {
83
+ const trackerFn = async (type, data = {}) => {
84
+ await trackEvent(`${command}_command_${type}`, {
85
+ ...trackingInfo,
86
+ ...data
87
+ });
88
+ };
89
+ return trackerFn;
80
90
  }
@@ -72,6 +72,8 @@
72
72
  "vip-dev-env-shell": "dist/bin/vip-dev-env-shell.js",
73
73
  "vip-dev-env-start": "dist/bin/vip-dev-env-start.js",
74
74
  "vip-dev-env-stop": "dist/bin/vip-dev-env-stop.js",
75
+ "vip-dev-env-sync": "dist/bin/vip-dev-env-sync.js",
76
+ "vip-dev-env-sync-sql": "dist/bin/vip-dev-env-sync-sql.js",
75
77
  "vip-dev-env-update": "dist/bin/vip-dev-env-update.js",
76
78
  "vip-import": "dist/bin/vip-import.js",
77
79
  "vip-import-media": "dist/bin/vip-import-media.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "2.27.0",
3
+ "version": "2.28.0-dev7",
4
4
  "description": "The VIP Javascript library & CLI",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -33,6 +33,8 @@
33
33
  "vip-dev-env-start": "dist/bin/vip-dev-env-start.js",
34
34
  "vip-dev-env-stop": "dist/bin/vip-dev-env-stop.js",
35
35
  "vip-dev-env-logs": "dist/bin/vip-dev-env-logs.js",
36
+ "vip-dev-env-sync": "dist/bin/vip-dev-env-sync.js",
37
+ "vip-dev-env-sync-sql": "dist/bin/vip-dev-env-sync-sql.js",
36
38
  "vip-import": "dist/bin/vip-import.js",
37
39
  "vip-import-media": "dist/bin/vip-import-media.js",
38
40
  "vip-import-media-abort": "dist/bin/vip-import-media-abort.js",