zapier-platform-cli 16.1.0 → 16.2.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.
@@ -898,6 +898,13 @@
898
898
  "multiple": false,
899
899
  "type": "option"
900
900
  },
901
+ "yes": {
902
+ "char": "y",
903
+ "description": "Automatically answer \"yes\" to any prompts. Useful if you want to avoid interactive prompts to run this command in CI.",
904
+ "name": "yes",
905
+ "allowNo": false,
906
+ "type": "boolean"
907
+ },
901
908
  "debug": {
902
909
  "char": "d",
903
910
  "description": "Show extra debugging output.",
@@ -1851,6 +1858,13 @@
1851
1858
  "zapier env:set 1.2.3 SECRET=12345 OTHER=4321"
1852
1859
  ],
1853
1860
  "flags": {
1861
+ "force": {
1862
+ "char": "f",
1863
+ "description": "Force the update of environment variables regardless if the app version is production or not. Use with caution.",
1864
+ "name": "force",
1865
+ "allowNo": false,
1866
+ "type": "boolean"
1867
+ },
1854
1868
  "debug": {
1855
1869
  "char": "d",
1856
1870
  "description": "Show extra debugging output.",
@@ -1901,6 +1915,13 @@
1901
1915
  "zapier env:unset 1.2.3 SECRET OTHER"
1902
1916
  ],
1903
1917
  "flags": {
1918
+ "force": {
1919
+ "char": "f",
1920
+ "description": "Force the update of environment variables regardless if the app version is production or not. Use with caution.",
1921
+ "name": "force",
1922
+ "allowNo": false,
1923
+ "type": "boolean"
1924
+ },
1904
1925
  "debug": {
1905
1926
  "char": "d",
1906
1927
  "description": "Show extra debugging output.",
@@ -2312,5 +2333,5 @@
2312
2333
  ]
2313
2334
  }
2314
2335
  },
2315
- "version": "16.1.0"
2336
+ "version": "16.2.0"
2316
2337
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zapier-platform-cli",
3
- "version": "16.1.0",
3
+ "version": "16.2.0",
4
4
  "description": "The CLI for managing integrations in Zapier Developer Platform.",
5
5
  "repository": "zapier/zapier-platform",
6
6
  "homepage": "https://platform.zapier.com/",
package/src/constants.js CHANGED
@@ -42,7 +42,7 @@ const PACKAGE_VERSION = packageJson.version;
42
42
  const UPDATE_NOTIFICATION_INTERVAL = 1000 * 60 * 60 * 24 * 7; // one week
43
43
 
44
44
  const CHECK_REF_DOC_LINK =
45
- 'https://docs.zapier.com/platform/publish/integration-checks-reference#integration-check-reference';
45
+ 'https://docs.zapier.com/platform/publish/integration-checks-reference';
46
46
 
47
47
  const ISSUES_URL =
48
48
  'https://github.com/zapier/zapier-platform/issues/new/choose';
@@ -1,4 +1,4 @@
1
- const { Args } = require('@oclif/core');
1
+ const { Args, Flags } = require('@oclif/core');
2
2
  const { cyan } = require('colors/safe');
3
3
  const { omit } = require('lodash');
4
4
 
@@ -45,21 +45,30 @@ class SetEnvCommand extends BaseCommand {
45
45
  }
46
46
 
47
47
  const url = `/apps/${app.id}/versions/${version}/multi-environment`;
48
+ const requestOptions = {
49
+ body: payload,
50
+ method: 'POST',
51
+ };
52
+
53
+ if (this.flags.force) {
54
+ requestOptions.extraHeaders = {
55
+ 'X-Force-Env-Var-Update': 'true',
56
+ };
57
+ }
48
58
 
49
59
  try {
50
- // currently, this returns nothing
51
- await callAPI(
52
- url,
53
- {
54
- body: payload,
55
- method: 'POST',
56
- },
57
- true,
58
- );
60
+ await callAPI(url, requestOptions, true);
59
61
 
60
62
  this.log(successMessage(version));
61
63
  this.logJSON(payload);
62
64
  } catch (e) {
65
+ if (e.status === 409) {
66
+ this.error(
67
+ `App version ${version} is the production version. Are you sure you want to set potentially live environment variables?` +
68
+ ` If so, run this command again with the --force flag.`,
69
+ );
70
+ }
71
+
63
72
  // comes back as json: { errors: [ 'The following keys failed to update: 3QER, 4WER' ] },
64
73
  const failedKeys = e.json.errors[0].split('update: ')[1].split(', ');
65
74
  const successfulResult = omit(payload, failedKeys);
@@ -85,7 +94,15 @@ SetEnvCommand.args = {
85
94
  'The key-value pairs to set. Keys are case-insensitive. Each pair should be space separated and pairs should be separated by an `=`. For example: `A=123 B=456`',
86
95
  }),
87
96
  };
88
- SetEnvCommand.flags = buildFlags();
97
+ SetEnvCommand.flags = buildFlags({
98
+ commandFlags: {
99
+ force: Flags.boolean({
100
+ char: 'f',
101
+ description:
102
+ 'Force the update of environment variables regardless if the app version is production or not. Use with caution.',
103
+ }),
104
+ },
105
+ });
89
106
  SetEnvCommand.description = `Set environment variables for a version.`;
90
107
  SetEnvCommand.examples = [`zapier env:set 1.2.3 SECRET=12345 OTHER=4321`];
91
108
  SetEnvCommand.strict = false;
@@ -1,4 +1,4 @@
1
- const { Args } = require('@oclif/core');
1
+ const { Args, Flags } = require('@oclif/core');
2
2
  const { cyan } = require('colors/safe');
3
3
 
4
4
  const BaseCommand = require('../../ZapierBaseCommand');
@@ -42,13 +42,29 @@ class UnsetEnvCommand extends BaseCommand {
42
42
  }
43
43
 
44
44
  const url = `/apps/${app.id}/versions/${version}/multi-environment`;
45
-
46
- // currently, this returns nothing
47
- // also, no need to cath errors here, since invalid keys don't get tripped over if the env var didn't exist in the first place
48
- await callAPI(url, {
45
+ const requestOptions = {
49
46
  body: payload,
50
47
  method: 'POST',
51
- });
48
+ };
49
+
50
+ if (this.flags.force) {
51
+ requestOptions.extraHeaders = {
52
+ 'X-Force-Env-Var-Update': 'true',
53
+ };
54
+ }
55
+
56
+ try {
57
+ await callAPI(url, requestOptions, true);
58
+ } catch (e) {
59
+ if (e.status === 409) {
60
+ this.error(
61
+ `App version ${version} is the production version. Are you sure you want to unset potentially live environment variables?` +
62
+ ` If so, run this command again with the --force flag.`,
63
+ );
64
+ } else {
65
+ throw e;
66
+ }
67
+ }
52
68
 
53
69
  this.log(successMessage(version));
54
70
  this.logJSON(keysToUnset);
@@ -64,7 +80,15 @@ UnsetEnvCommand.args = {
64
80
  description: 'The keys to unset. Keys are case-insensitive.',
65
81
  }),
66
82
  };
67
- UnsetEnvCommand.flags = buildFlags();
83
+ UnsetEnvCommand.flags = buildFlags({
84
+ commandFlags: {
85
+ force: Flags.boolean({
86
+ char: 'f',
87
+ description:
88
+ 'Force the update of environment variables regardless if the app version is production or not. Use with caution.',
89
+ }),
90
+ },
91
+ });
68
92
  UnsetEnvCommand.description = `Unset environment variables for a version.`;
69
93
  UnsetEnvCommand.examples = [`zapier env:unset 1.2.3 SECRET OTHER`];
70
94
  UnsetEnvCommand.strict = false;
@@ -1,3 +1,5 @@
1
+ const _ = require('lodash');
2
+ const debug = require('debug')('zapier:migrate');
1
3
  const { Args, Flags } = require('@oclif/core');
2
4
 
3
5
  const BaseCommand = require('../ZapierBaseCommand');
@@ -6,6 +8,51 @@ const { callAPI } = require('../../utils/api');
6
8
  const { buildFlags } = require('../buildFlags');
7
9
 
8
10
  class MigrateCommand extends BaseCommand {
11
+ async run_require_confirmation_pre_checks(app, requestBody) {
12
+ const assumeYes = 'yes' in this.flags;
13
+ const url = `/apps/${app.id}/pre-migration-require-confirmation-checks`;
14
+
15
+ this.startSpinner(`Running pre-checks before migration...`);
16
+
17
+ try {
18
+ await callAPI(
19
+ url,
20
+ {
21
+ method: 'POST',
22
+ body: requestBody,
23
+ },
24
+ true,
25
+ );
26
+ } catch (response) {
27
+ this.stopSpinner({ success: false });
28
+
29
+ // 409 from the backend specifically signals pre-checks failed
30
+ if (response.status === 409) {
31
+ const softCheckErrors = _.get(response, 'json.errors', []);
32
+ const formattedErrors = softCheckErrors.map((e) => `* ${e}`).join('\n');
33
+
34
+ this.log();
35
+ this.log('Non-blocking checks prior to migration returned warnings:');
36
+ this.log(formattedErrors);
37
+ this.log();
38
+
39
+ const shouldContinuePreChecks =
40
+ assumeYes ||
41
+ (await this.confirm(
42
+ 'Would you like to continue with the migration regardless?',
43
+ ));
44
+
45
+ if (!shouldContinuePreChecks) {
46
+ this.error('Cancelled migration.');
47
+ }
48
+ } else {
49
+ debug('Soft pre-checks before migration failed:', response.errText);
50
+ }
51
+ } finally {
52
+ this.stopSpinner();
53
+ }
54
+ }
55
+
9
56
  async perform() {
10
57
  const percent = this.args.percent;
11
58
  if (isNaN(percent) || percent < 1 || percent > 100) {
@@ -14,6 +61,7 @@ class MigrateCommand extends BaseCommand {
14
61
 
15
62
  const account = this.flags.account;
16
63
  const user = this.flags.user;
64
+
17
65
  const fromVersion = this.args.fromVersion;
18
66
  const toVersion = this.args.toVersion;
19
67
  let flagType;
@@ -67,6 +115,9 @@ class MigrateCommand extends BaseCommand {
67
115
  email_type: flagType,
68
116
  },
69
117
  };
118
+
119
+ await this.run_require_confirmation_pre_checks(app, body);
120
+
70
121
  if (user || account) {
71
122
  this.startSpinner(
72
123
  `Starting migration from ${fromVersion} to ${toVersion} for ${
@@ -106,6 +157,11 @@ MigrateCommand.flags = buildFlags({
106
157
  description:
107
158
  "Migrates all of a users' Zaps, Private & Shared, within all accounts for which the specified user is a member",
108
159
  }),
160
+ yes: Flags.boolean({
161
+ char: 'y',
162
+ description:
163
+ 'Automatically answer "yes" to any prompts. Useful if you want to avoid interactive prompts to run this command in CI.',
164
+ }),
109
165
  },
110
166
  });
111
167
 
@@ -1,4 +1,5 @@
1
1
  const _ = require('lodash');
2
+ const debug = require('debug')('zapier:promote');
2
3
  const colors = require('colors/safe');
3
4
  const { Args, Flags } = require('@oclif/core');
4
5
 
@@ -43,6 +44,52 @@ const hasAppChangeType = (metadata, changeType) => {
43
44
  };
44
45
 
45
46
  class PromoteCommand extends BaseCommand {
47
+ async run_require_confirmation_pre_checks(app, requestBody) {
48
+ const assumeYes = 'yes' in this.flags;
49
+ const url = `/apps/${app.id}/pre-migration-require-confirmation-checks`;
50
+
51
+ this.startSpinner(`Running pre-checks before promoting...`);
52
+
53
+ try {
54
+ await callAPI(
55
+ url,
56
+ {
57
+ method: 'POST',
58
+ body: requestBody,
59
+ },
60
+ true,
61
+ );
62
+ } catch (response) {
63
+ this.stopSpinner({ success: false });
64
+ // 409 from the backend specifically signals pre-checks failed
65
+ if (response.status === 409) {
66
+ const softCheckErrors = _.get(response, 'json.errors', []);
67
+ const formattedErrors = softCheckErrors.map((e) => `* ${e}`).join('\n');
68
+
69
+ this.log();
70
+ this.log(
71
+ 'Non-blocking checks prior to promoting the integration returned warnings:',
72
+ );
73
+ this.log(formattedErrors);
74
+ this.log();
75
+
76
+ const shouldContinuePreChecks =
77
+ assumeYes ||
78
+ (await this.confirm(
79
+ 'Would you like to continue with the promotion process regardless?',
80
+ ));
81
+
82
+ if (!shouldContinuePreChecks) {
83
+ this.error('Cancelled promote.');
84
+ }
85
+ } else {
86
+ debug('Soft pre-checks before promotion failed:', response.errText);
87
+ }
88
+ } finally {
89
+ this.stopSpinner();
90
+ }
91
+ }
92
+
46
93
  async perform() {
47
94
  const app = await this.getWritableApp();
48
95
 
@@ -51,7 +98,8 @@ class PromoteCommand extends BaseCommand {
51
98
  const version = this.args.version;
52
99
  const assumeYes = 'yes' in this.flags;
53
100
 
54
- let shouldContinue;
101
+ let shouldContinueChangelog;
102
+
55
103
  const { changelog, appMetadata, issueMetadata } =
56
104
  await getVersionChangelog(version);
57
105
 
@@ -133,15 +181,15 @@ ${metadataPromptHelper}`);
133
181
  this.log();
134
182
  /* eslint-enable camelcase */
135
183
 
136
- shouldContinue =
184
+ shouldContinueChangelog =
137
185
  assumeYes ||
138
186
  (await this.confirm(
139
187
  'Would you like to continue promoting with this changelog?',
140
188
  ));
141
- }
142
189
 
143
- if (!shouldContinue) {
144
- this.error('Cancelled promote.');
190
+ if (!shouldContinueChangelog) {
191
+ this.error('Cancelled promote.');
192
+ }
145
193
  }
146
194
 
147
195
  this.log(
@@ -167,6 +215,8 @@ ${metadataPromptHelper}`);
167
215
  },
168
216
  };
169
217
 
218
+ await this.run_require_confirmation_pre_checks(app, body);
219
+
170
220
  this.startSpinner(`Verifying and promoting ${version}`);
171
221
 
172
222
  const url = `/apps/${app.id}/migrations`;
package/src/utils/api.js CHANGED
@@ -68,6 +68,7 @@ const callAPI = async (
68
68
  method: options.method || 'GET',
69
69
  body: options.body ? JSON.stringify(options.body) : null,
70
70
  headers: {
71
+ ...options.extraHeaders, // extra headers first so they don't override defaults
71
72
  Accept: 'application/json',
72
73
  'Content-Type': 'application/json; charset=utf-8',
73
74
  'User-Agent': `${constants.PACKAGE_NAME}/${constants.PACKAGE_VERSION}`,