@commercetools-frontend/deployment-cli 2.0.2 → 2.0.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/bin/cli.js CHANGED
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ process.env.NODE_ENV = 'production';
4
+
3
5
  const { exit } = require('node:process');
4
6
  const { run } = require('@commercetools-frontend/deployment-cli/cli');
5
7
 
@@ -2,43 +2,56 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var cac = require('cac');
6
- var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
5
+ var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys');
6
+ var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols');
7
+ var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter');
8
+ var _Object$getOwnPropertyDescriptor = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor');
7
9
  var _forEachInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/for-each');
10
+ var _Object$getOwnPropertyDescriptors = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors');
11
+ var _Object$defineProperties = require('@babel/runtime-corejs3/core-js-stable/object/define-properties');
12
+ var _Object$defineProperty = require('@babel/runtime-corejs3/core-js-stable/object/define-property');
13
+ var _defineProperty = require('@babel/runtime-corejs3/helpers/defineProperty');
14
+ var commander = require('commander');
15
+ var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
16
+ var _URLSearchParams = require('@babel/runtime-corejs3/core-js-stable/url-search-params');
17
+ var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries');
18
+ var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise');
19
+ var _JSON$stringify = require('@babel/runtime-corejs3/core-js-stable/json/stringify');
20
+ var node_process = require('node:process');
21
+ var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
8
22
  var _mapInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/map');
9
- var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter');
10
23
  var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with');
11
24
  var _concatInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/concat');
12
25
  var cosmiconfig = require('cosmiconfig');
13
26
  var merge = require('lodash/merge');
14
27
  var prompts = require('prompts');
15
28
  var pRetry = require('p-retry');
16
- var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
17
- var _URLSearchParams = require('@babel/runtime-corejs3/core-js-stable/url-search-params');
18
- var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries');
19
- var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise');
20
- var _JSON$stringify = require('@babel/runtime-corejs3/core-js-stable/json/stringify');
21
- var node_process = require('node:process');
22
29
 
23
30
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
24
31
 
25
- var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
32
+ var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
33
+ var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols);
34
+ var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
35
+ var _Object$getOwnPropertyDescriptor__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptor);
26
36
  var _forEachInstanceProperty__default = /*#__PURE__*/_interopDefault(_forEachInstanceProperty);
37
+ var _Object$getOwnPropertyDescriptors__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptors);
38
+ var _Object$defineProperties__default = /*#__PURE__*/_interopDefault(_Object$defineProperties);
39
+ var _Object$defineProperty__default = /*#__PURE__*/_interopDefault(_Object$defineProperty);
40
+ var _URLSearchParams__default = /*#__PURE__*/_interopDefault(_URLSearchParams);
41
+ var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
42
+ var _Promise__default = /*#__PURE__*/_interopDefault(_Promise);
43
+ var _JSON$stringify__default = /*#__PURE__*/_interopDefault(_JSON$stringify);
44
+ var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
27
45
  var _mapInstanceProperty__default = /*#__PURE__*/_interopDefault(_mapInstanceProperty);
28
- var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
29
46
  var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
30
47
  var _concatInstanceProperty__default = /*#__PURE__*/_interopDefault(_concatInstanceProperty);
31
48
  var merge__default = /*#__PURE__*/_interopDefault(merge);
32
49
  var prompts__default = /*#__PURE__*/_interopDefault(prompts);
33
50
  var pRetry__default = /*#__PURE__*/_interopDefault(pRetry);
34
- var _URLSearchParams__default = /*#__PURE__*/_interopDefault(_URLSearchParams);
35
- var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
36
- var _Promise__default = /*#__PURE__*/_interopDefault(_Promise);
37
- var _JSON$stringify__default = /*#__PURE__*/_interopDefault(_JSON$stringify);
38
51
 
39
52
  var pkgJson = {
40
53
  name: "@commercetools-frontend/deployment-cli",
41
- version: "2.0.2",
54
+ version: "2.0.4",
42
55
  description: "CLI to manage Custom Applications deployments in Google Storage.",
43
56
  keywords: [
44
57
  "commercetools",
@@ -62,9 +75,8 @@ var pkgJson = {
62
75
  },
63
76
  dependencies: {
64
77
  "@babel/core": "^7.22.11",
65
- "@babel/runtime": "^7.21.0",
66
78
  "@babel/runtime-corejs3": "^7.21.0",
67
- cac: "^6.7.14",
79
+ commander: "^13.1.0",
68
80
  cosmiconfig: "9.0.0",
69
81
  lodash: "4.17.21",
70
82
  "p-retry": "4.6.2",
@@ -73,7 +85,7 @@ var pkgJson = {
73
85
  devDependencies: {
74
86
  "@tsconfig/node20": "20.1.4",
75
87
  "@types/lodash": "^4.14.198",
76
- "@types/node": "20.16.10",
88
+ "@types/node": "22.13.9",
77
89
  "@types/prompts": "2.4.9",
78
90
  msw: "1.3.5",
79
91
  typescript: "5.2.2"
@@ -93,6 +105,152 @@ var pkgJson = {
93
105
  }
94
106
  };
95
107
 
108
+ async function processCircleCiResponse(response) {
109
+ if (!response.ok) {
110
+ /**
111
+ * NOTE:
112
+ * Trying to handle known but undocumented responses of the CircleCI API.
113
+ *
114
+ * 1. Message: Already approved job
115
+ * Deployment was already triggered manually or by train the day before.
116
+ */
117
+
118
+ // Response data is a stream so text OR json can only be read once, so
119
+ // we read it as text first and then try to parse it as json to handle
120
+ // known error responses.
121
+ const error = await response.text();
122
+ try {
123
+ // The CircleCI API always uses content-type text/plain, so we always
124
+ // try parsing JSON to see if the message property is present.
125
+ const _JSON$parse = JSON.parse(error),
126
+ message = _JSON$parse.message;
127
+ if (message.match(/job already approved/i)) {
128
+ console.log('ℹ️ Deployment job is already approved.');
129
+ // TODO: can we return instead of force exiting?
130
+ node_process.exit(0);
131
+ }
132
+ } catch {
133
+ // Ignore JSON parsing errors
134
+ }
135
+ throw new Error(`${response.status}: Network response was not ok.\n
136
+ Status text is ${response.statusText} and text is ${error}.`);
137
+ }
138
+ return response.json();
139
+ }
140
+ function createCircleCiClient(_ref) {
141
+ let projectName = _ref.projectName,
142
+ apiBaseUrl = _ref.apiBaseUrl;
143
+ async function execute(api) {
144
+ let _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
145
+ skip = _ref2.skip,
146
+ debug = _ref2.debug;
147
+ let url = `${apiBaseUrl}${api.url}`;
148
+ if (api.params && api.method === 'GET') {
149
+ const urlSearchParams = new _URLSearchParams__default["default"]();
150
+ for (const _ref3 of _Object$entries__default["default"](api.params)) {
151
+ var _ref4 = _slicedToArray(_ref3, 2);
152
+ const key = _ref4[0];
153
+ const value = _ref4[1];
154
+ if (value !== null && value !== undefined) {
155
+ urlSearchParams.append(key, value);
156
+ }
157
+ }
158
+ url += `?${urlSearchParams.toString()}`;
159
+ }
160
+ if (skip) {
161
+ if (debug) {
162
+ console.log(`🏭 Skipping CircleCI call API at: ${url}.`);
163
+ }
164
+
165
+ // @ts-expect-error
166
+ return _Promise__default["default"].resolve();
167
+ }
168
+ if (debug) {
169
+ console.log(`🏭 Calling CircleCI API at: ${url}.`);
170
+ }
171
+ try {
172
+ const response = await fetch(url, {
173
+ headers: api.headers,
174
+ method: api.method,
175
+ body: api.method === 'POST' ? _JSON$stringify__default["default"](api.params) : undefined
176
+ });
177
+ const processedCircleCiResponse = await processCircleCiResponse(response);
178
+ return processedCircleCiResponse;
179
+ } catch (error) {
180
+ console.log(`⚠️ Calling CircleCI API at: ${url} failed.`);
181
+ throw error;
182
+ }
183
+ }
184
+ return {
185
+ pipelines: function () {
186
+ let _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
187
+ pageToken = _ref5.pageToken,
188
+ _ref5$projectSlug = _ref5.projectSlug,
189
+ projectSlug = _ref5$projectSlug === void 0 ? `gh/commercetools/${projectName}` : _ref5$projectSlug,
190
+ _ref5$branch = _ref5.branch,
191
+ branch = _ref5$branch === void 0 ? 'main' : _ref5$branch;
192
+ return {
193
+ execute: options => execute({
194
+ url: `/project/${projectSlug}/pipeline`,
195
+ headers: {
196
+ 'Content-Type': 'application/json',
197
+ // The CLI throws if this is not present on environment
198
+ 'Circle-Token': process.env.CIRCLE_TOKEN
199
+ },
200
+ method: 'GET',
201
+ params: {
202
+ branch,
203
+ 'page-token': pageToken
204
+ }
205
+ }, options)
206
+ };
207
+ },
208
+ workflows: _ref6 => {
209
+ let pipelineId = _ref6.pipelineId;
210
+ return {
211
+ execute: options => execute({
212
+ url: `/pipeline/${pipelineId}/workflow`,
213
+ headers: {
214
+ 'Content-Type': 'application/json',
215
+ // The CLI throws if this is not present on environment
216
+ 'Circle-Token': process.env.CIRCLE_TOKEN
217
+ },
218
+ method: 'GET'
219
+ }, options)
220
+ };
221
+ },
222
+ jobs: _ref7 => {
223
+ let workflowId = _ref7.workflowId;
224
+ return {
225
+ execute: options => execute({
226
+ url: `/workflow/${workflowId}/job`,
227
+ headers: {
228
+ 'Content-Type': 'application/json',
229
+ // The CLI throws if this is not present on environment
230
+ 'Circle-Token': process.env.CIRCLE_TOKEN
231
+ },
232
+ method: 'GET'
233
+ }, options)
234
+ };
235
+ },
236
+ approve: _ref8 => {
237
+ let workflowId = _ref8.workflowId,
238
+ approvalRequestId = _ref8.approvalRequestId;
239
+ return {
240
+ execute: options => execute({
241
+ url: `/workflow/${workflowId}/approve/${approvalRequestId} `,
242
+ headers: {
243
+ 'Content-Type': 'application/json',
244
+ // The CLI throws if this is not present on environment
245
+ 'Circle-Token': process.env.CIRCLE_TOKEN
246
+ },
247
+ method: 'POST'
248
+ }, options)
249
+ };
250
+ }
251
+ };
252
+ }
253
+
96
254
  async function loadConfig() {
97
255
  const deploymentConfigExplorer = cosmiconfig.cosmiconfig('deployment', {
98
256
  searchStrategy: 'project'
@@ -412,153 +570,9 @@ async function approve(cliFlags, config, circleCiApis) {
412
570
  }
413
571
  }
414
572
 
415
- async function processCircleCiResponse(response) {
416
- if (!response.ok) {
417
- /**
418
- * NOTE:
419
- * Trying to handle known but undocumented responses of the CircleCI API.
420
- *
421
- * 1. Message: Already approved job
422
- * Deployment was already triggered manually or by train the day before.
423
- */
424
-
425
- // Response data is a stream so text OR json can only be read once, so
426
- // we read it as text first and then try to parse it as json to handle
427
- // known error responses.
428
- const error = await response.text();
429
- try {
430
- // The CircleCI API always uses content-type text/plain, so we always
431
- // try parsing JSON to see if the message property is present.
432
- const _JSON$parse = JSON.parse(error),
433
- message = _JSON$parse.message;
434
- if (message.match(/job already approved/i)) {
435
- console.log('ℹ️ Deployment job is already approved.');
436
- // TODO: can we return instead of force exiting?
437
- node_process.exit(0);
438
- }
439
- } catch {
440
- // Ignore JSON parsing errors
441
- }
442
- throw new Error(`${response.status}: Network response was not ok.\n
443
- Status text is ${response.statusText} and text is ${error}.`);
444
- }
445
- return response.json();
446
- }
447
- function createCircleCiClient(_ref) {
448
- let projectName = _ref.projectName,
449
- apiBaseUrl = _ref.apiBaseUrl;
450
- async function execute(api) {
451
- let _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
452
- skip = _ref2.skip,
453
- debug = _ref2.debug;
454
- let url = `${apiBaseUrl}${api.url}`;
455
- if (api.params && api.method === 'GET') {
456
- const urlSearchParams = new _URLSearchParams__default["default"]();
457
- for (const _ref3 of _Object$entries__default["default"](api.params)) {
458
- var _ref4 = _slicedToArray(_ref3, 2);
459
- const key = _ref4[0];
460
- const value = _ref4[1];
461
- if (value !== null && value !== undefined) {
462
- urlSearchParams.append(key, value);
463
- }
464
- }
465
- url += `?${urlSearchParams.toString()}`;
466
- }
467
- if (skip) {
468
- if (debug) {
469
- console.log(`🏭 Skipping CircleCI call API at: ${url}.`);
470
- }
471
-
472
- // @ts-expect-error
473
- return _Promise__default["default"].resolve();
474
- }
475
- if (debug) {
476
- console.log(`🏭 Calling CircleCI API at: ${url}.`);
477
- }
478
- try {
479
- const response = await fetch(url, {
480
- headers: api.headers,
481
- method: api.method,
482
- body: api.method === 'POST' ? _JSON$stringify__default["default"](api.params) : undefined
483
- });
484
- const processedCircleCiResponse = await processCircleCiResponse(response);
485
- return processedCircleCiResponse;
486
- } catch (error) {
487
- console.log(`⚠️ Calling CircleCI API at: ${url} failed.`);
488
- throw error;
489
- }
490
- }
491
- return {
492
- pipelines: function () {
493
- let _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
494
- pageToken = _ref5.pageToken,
495
- _ref5$projectSlug = _ref5.projectSlug,
496
- projectSlug = _ref5$projectSlug === void 0 ? `gh/commercetools/${projectName}` : _ref5$projectSlug,
497
- _ref5$branch = _ref5.branch,
498
- branch = _ref5$branch === void 0 ? 'main' : _ref5$branch;
499
- return {
500
- execute: options => execute({
501
- url: `/project/${projectSlug}/pipeline`,
502
- headers: {
503
- 'Content-Type': 'application/json',
504
- // The CLI throws if this is not present on environment
505
- 'Circle-Token': process.env.CIRCLE_TOKEN
506
- },
507
- method: 'GET',
508
- params: {
509
- branch,
510
- 'page-token': pageToken
511
- }
512
- }, options)
513
- };
514
- },
515
- workflows: _ref6 => {
516
- let pipelineId = _ref6.pipelineId;
517
- return {
518
- execute: options => execute({
519
- url: `/pipeline/${pipelineId}/workflow`,
520
- headers: {
521
- 'Content-Type': 'application/json',
522
- // The CLI throws if this is not present on environment
523
- 'Circle-Token': process.env.CIRCLE_TOKEN
524
- },
525
- method: 'GET'
526
- }, options)
527
- };
528
- },
529
- jobs: _ref7 => {
530
- let workflowId = _ref7.workflowId;
531
- return {
532
- execute: options => execute({
533
- url: `/workflow/${workflowId}/job`,
534
- headers: {
535
- 'Content-Type': 'application/json',
536
- // The CLI throws if this is not present on environment
537
- 'Circle-Token': process.env.CIRCLE_TOKEN
538
- },
539
- method: 'GET'
540
- }, options)
541
- };
542
- },
543
- approve: _ref8 => {
544
- let workflowId = _ref8.workflowId,
545
- approvalRequestId = _ref8.approvalRequestId;
546
- return {
547
- execute: options => execute({
548
- url: `/workflow/${workflowId}/approve/${approvalRequestId} `,
549
- headers: {
550
- 'Content-Type': 'application/json',
551
- // The CLI throws if this is not present on environment
552
- 'Circle-Token': process.env.CIRCLE_TOKEN
553
- },
554
- method: 'POST'
555
- }, options)
556
- };
557
- }
558
- };
559
- }
560
-
561
- const cli = cac.cac('deployment-cli');
573
+ function ownKeys(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; }
574
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
575
+ commander.program.name('deployment-cli').description('CLI to help manage deployment pipelines').version(pkgJson.version).option('--dry-run', '(optional) Simulate a deployment.', false).option('--debug', '(optional) Print additional debug information.', false);
562
576
  async function run() {
563
577
  const config = await loadConfig();
564
578
  throwIfConfigurationLacksRequiredValues(config);
@@ -567,36 +581,16 @@ async function run() {
567
581
  apiBaseUrl: config.CircleCI.apiBaseUrl
568
582
  });
569
583
 
570
- // General CLI options
571
- cli.option('--dry-run', '(optional) Simulate a deployment.', {
572
- default: false
573
- });
574
- cli.option('--debug', '(optional) Print additional debug information.', {
575
- default: false
576
- });
577
-
578
- // Default command
579
- cli.command('').usage('\n\n Approve deployment jobs on CI for different components related to MC.').action(cli.outputHelp);
580
-
581
584
  // Command: Approve
582
- const usageApprove = 'Approves a job and by this triggers a deployment of a component. It requires a "CIRCLE_TOKEN" environment variable (https://circleci.com/docs/2.0/managing-api-tokens/).';
583
- cli.command('approve', usageApprove).usage(`approve \n\n ${usageApprove}`).option('--approval-job <string>', 'The name of the approval job to approve a deployment with.').option('--deployment-job [string]', '(optional) The name of the deployment job triggered by the approval job. If passed the CLI will print a URL to the deployment triggered on CircleCI.').option('--deployment [string]', '(optional) The name of a deployment configured in the configuration file.').option('--build-revision [git-sha]', '(optional) The git commit SHA that needs to be deployed. If not specified, the last successful pipeline is used.').option('--branch [string]', '(optional) The git branch to deploy from. If not specified.', {
584
- default: 'main'
585
- }).option('--yes', '(optional) Skip all confirmation prompts. Useful in Continuous integration (CI) to automatically answer confirmation questions.', {
586
- default: false
587
- }).action(async options => {
588
- if (options.dryRun) {
585
+ commander.program.command('approve').description('Approves a job and by this triggers a deployment of a component. It requires a "CIRCLE_TOKEN" environment variable (https://circleci.com/docs/2.0/managing-api-tokens/).').requiredOption('--approval-job <string>', 'The name of the approval job to approve a deployment with.').option('--deployment-job [string]', '(optional) The name of the deployment job triggered by the approval job. If passed the CLI will print a URL to the deployment triggered on CircleCI.').option('--deployment [string]', '(optional) The name of a deployment configured in the configuration file.').option('--build-revision [git-sha]', '(optional) The git commit SHA that needs to be deployed. If not specified, the last successful pipeline is used.').option('--branch [string]', '(optional) The git branch to deploy from. If not specified.', 'main').option('--yes', '(optional) Skip all confirmation prompts. Useful in Continuous integration (CI) to automatically answer confirmation questions.', false).action(async options => {
586
+ const globalOptions = commander.program.opts();
587
+ if (globalOptions.dryRun) {
589
588
  console.log(`🙊 Do not worry. This is a dry run!`);
590
589
  }
591
590
  throwIfRequiredEnvironmentVariableIsUnset(['CIRCLE_TOKEN']);
592
- await approve(options, config, circleCiApis);
593
- });
594
- cli.help();
595
- cli.version(pkgJson.version);
596
- cli.parse(process.argv, {
597
- run: false
591
+ await approve(_objectSpread(_objectSpread({}, globalOptions), options), config, circleCiApis);
598
592
  });
599
- await cli.runMatchedCommand();
593
+ commander.program.parse();
600
594
  }
601
595
 
602
596
  exports.run = run;
@@ -2,43 +2,56 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var cac = require('cac');
6
- var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
5
+ var _Object$keys = require('@babel/runtime-corejs3/core-js-stable/object/keys');
6
+ var _Object$getOwnPropertySymbols = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols');
7
+ var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter');
8
+ var _Object$getOwnPropertyDescriptor = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor');
7
9
  var _forEachInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/for-each');
10
+ var _Object$getOwnPropertyDescriptors = require('@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors');
11
+ var _Object$defineProperties = require('@babel/runtime-corejs3/core-js-stable/object/define-properties');
12
+ var _Object$defineProperty = require('@babel/runtime-corejs3/core-js-stable/object/define-property');
13
+ var _defineProperty = require('@babel/runtime-corejs3/helpers/defineProperty');
14
+ var commander = require('commander');
15
+ var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
16
+ var _URLSearchParams = require('@babel/runtime-corejs3/core-js-stable/url-search-params');
17
+ var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries');
18
+ var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise');
19
+ var _JSON$stringify = require('@babel/runtime-corejs3/core-js-stable/json/stringify');
20
+ var node_process = require('node:process');
21
+ var _findInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/find');
8
22
  var _mapInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/map');
9
- var _filterInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/filter');
10
23
  var _startsWithInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/starts-with');
11
24
  var _concatInstanceProperty = require('@babel/runtime-corejs3/core-js-stable/instance/concat');
12
25
  var cosmiconfig = require('cosmiconfig');
13
26
  var merge = require('lodash/merge');
14
27
  var prompts = require('prompts');
15
28
  var pRetry = require('p-retry');
16
- var _slicedToArray = require('@babel/runtime-corejs3/helpers/slicedToArray');
17
- var _URLSearchParams = require('@babel/runtime-corejs3/core-js-stable/url-search-params');
18
- var _Object$entries = require('@babel/runtime-corejs3/core-js-stable/object/entries');
19
- var _Promise = require('@babel/runtime-corejs3/core-js-stable/promise');
20
- var _JSON$stringify = require('@babel/runtime-corejs3/core-js-stable/json/stringify');
21
- var node_process = require('node:process');
22
29
 
23
30
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
24
31
 
25
- var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
32
+ var _Object$keys__default = /*#__PURE__*/_interopDefault(_Object$keys);
33
+ var _Object$getOwnPropertySymbols__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertySymbols);
34
+ var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
35
+ var _Object$getOwnPropertyDescriptor__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptor);
26
36
  var _forEachInstanceProperty__default = /*#__PURE__*/_interopDefault(_forEachInstanceProperty);
37
+ var _Object$getOwnPropertyDescriptors__default = /*#__PURE__*/_interopDefault(_Object$getOwnPropertyDescriptors);
38
+ var _Object$defineProperties__default = /*#__PURE__*/_interopDefault(_Object$defineProperties);
39
+ var _Object$defineProperty__default = /*#__PURE__*/_interopDefault(_Object$defineProperty);
40
+ var _URLSearchParams__default = /*#__PURE__*/_interopDefault(_URLSearchParams);
41
+ var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
42
+ var _Promise__default = /*#__PURE__*/_interopDefault(_Promise);
43
+ var _JSON$stringify__default = /*#__PURE__*/_interopDefault(_JSON$stringify);
44
+ var _findInstanceProperty__default = /*#__PURE__*/_interopDefault(_findInstanceProperty);
27
45
  var _mapInstanceProperty__default = /*#__PURE__*/_interopDefault(_mapInstanceProperty);
28
- var _filterInstanceProperty__default = /*#__PURE__*/_interopDefault(_filterInstanceProperty);
29
46
  var _startsWithInstanceProperty__default = /*#__PURE__*/_interopDefault(_startsWithInstanceProperty);
30
47
  var _concatInstanceProperty__default = /*#__PURE__*/_interopDefault(_concatInstanceProperty);
31
48
  var merge__default = /*#__PURE__*/_interopDefault(merge);
32
49
  var prompts__default = /*#__PURE__*/_interopDefault(prompts);
33
50
  var pRetry__default = /*#__PURE__*/_interopDefault(pRetry);
34
- var _URLSearchParams__default = /*#__PURE__*/_interopDefault(_URLSearchParams);
35
- var _Object$entries__default = /*#__PURE__*/_interopDefault(_Object$entries);
36
- var _Promise__default = /*#__PURE__*/_interopDefault(_Promise);
37
- var _JSON$stringify__default = /*#__PURE__*/_interopDefault(_JSON$stringify);
38
51
 
39
52
  var pkgJson = {
40
53
  name: "@commercetools-frontend/deployment-cli",
41
- version: "2.0.2",
54
+ version: "2.0.4",
42
55
  description: "CLI to manage Custom Applications deployments in Google Storage.",
43
56
  keywords: [
44
57
  "commercetools",
@@ -62,9 +75,8 @@ var pkgJson = {
62
75
  },
63
76
  dependencies: {
64
77
  "@babel/core": "^7.22.11",
65
- "@babel/runtime": "^7.21.0",
66
78
  "@babel/runtime-corejs3": "^7.21.0",
67
- cac: "^6.7.14",
79
+ commander: "^13.1.0",
68
80
  cosmiconfig: "9.0.0",
69
81
  lodash: "4.17.21",
70
82
  "p-retry": "4.6.2",
@@ -73,7 +85,7 @@ var pkgJson = {
73
85
  devDependencies: {
74
86
  "@tsconfig/node20": "20.1.4",
75
87
  "@types/lodash": "^4.14.198",
76
- "@types/node": "20.16.10",
88
+ "@types/node": "22.13.9",
77
89
  "@types/prompts": "2.4.9",
78
90
  msw: "1.3.5",
79
91
  typescript: "5.2.2"
@@ -93,6 +105,152 @@ var pkgJson = {
93
105
  }
94
106
  };
95
107
 
108
+ async function processCircleCiResponse(response) {
109
+ if (!response.ok) {
110
+ /**
111
+ * NOTE:
112
+ * Trying to handle known but undocumented responses of the CircleCI API.
113
+ *
114
+ * 1. Message: Already approved job
115
+ * Deployment was already triggered manually or by train the day before.
116
+ */
117
+
118
+ // Response data is a stream so text OR json can only be read once, so
119
+ // we read it as text first and then try to parse it as json to handle
120
+ // known error responses.
121
+ const error = await response.text();
122
+ try {
123
+ // The CircleCI API always uses content-type text/plain, so we always
124
+ // try parsing JSON to see if the message property is present.
125
+ const _JSON$parse = JSON.parse(error),
126
+ message = _JSON$parse.message;
127
+ if (message.match(/job already approved/i)) {
128
+ console.log('ℹ️ Deployment job is already approved.');
129
+ // TODO: can we return instead of force exiting?
130
+ node_process.exit(0);
131
+ }
132
+ } catch {
133
+ // Ignore JSON parsing errors
134
+ }
135
+ throw new Error(`${response.status}: Network response was not ok.\n
136
+ Status text is ${response.statusText} and text is ${error}.`);
137
+ }
138
+ return response.json();
139
+ }
140
+ function createCircleCiClient(_ref) {
141
+ let projectName = _ref.projectName,
142
+ apiBaseUrl = _ref.apiBaseUrl;
143
+ async function execute(api) {
144
+ let _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
145
+ skip = _ref2.skip,
146
+ debug = _ref2.debug;
147
+ let url = `${apiBaseUrl}${api.url}`;
148
+ if (api.params && api.method === 'GET') {
149
+ const urlSearchParams = new _URLSearchParams__default["default"]();
150
+ for (const _ref3 of _Object$entries__default["default"](api.params)) {
151
+ var _ref4 = _slicedToArray(_ref3, 2);
152
+ const key = _ref4[0];
153
+ const value = _ref4[1];
154
+ if (value !== null && value !== undefined) {
155
+ urlSearchParams.append(key, value);
156
+ }
157
+ }
158
+ url += `?${urlSearchParams.toString()}`;
159
+ }
160
+ if (skip) {
161
+ if (debug) {
162
+ console.log(`🏭 Skipping CircleCI call API at: ${url}.`);
163
+ }
164
+
165
+ // @ts-expect-error
166
+ return _Promise__default["default"].resolve();
167
+ }
168
+ if (debug) {
169
+ console.log(`🏭 Calling CircleCI API at: ${url}.`);
170
+ }
171
+ try {
172
+ const response = await fetch(url, {
173
+ headers: api.headers,
174
+ method: api.method,
175
+ body: api.method === 'POST' ? _JSON$stringify__default["default"](api.params) : undefined
176
+ });
177
+ const processedCircleCiResponse = await processCircleCiResponse(response);
178
+ return processedCircleCiResponse;
179
+ } catch (error) {
180
+ console.log(`⚠️ Calling CircleCI API at: ${url} failed.`);
181
+ throw error;
182
+ }
183
+ }
184
+ return {
185
+ pipelines: function () {
186
+ let _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
187
+ pageToken = _ref5.pageToken,
188
+ _ref5$projectSlug = _ref5.projectSlug,
189
+ projectSlug = _ref5$projectSlug === void 0 ? `gh/commercetools/${projectName}` : _ref5$projectSlug,
190
+ _ref5$branch = _ref5.branch,
191
+ branch = _ref5$branch === void 0 ? 'main' : _ref5$branch;
192
+ return {
193
+ execute: options => execute({
194
+ url: `/project/${projectSlug}/pipeline`,
195
+ headers: {
196
+ 'Content-Type': 'application/json',
197
+ // The CLI throws if this is not present on environment
198
+ 'Circle-Token': process.env.CIRCLE_TOKEN
199
+ },
200
+ method: 'GET',
201
+ params: {
202
+ branch,
203
+ 'page-token': pageToken
204
+ }
205
+ }, options)
206
+ };
207
+ },
208
+ workflows: _ref6 => {
209
+ let pipelineId = _ref6.pipelineId;
210
+ return {
211
+ execute: options => execute({
212
+ url: `/pipeline/${pipelineId}/workflow`,
213
+ headers: {
214
+ 'Content-Type': 'application/json',
215
+ // The CLI throws if this is not present on environment
216
+ 'Circle-Token': process.env.CIRCLE_TOKEN
217
+ },
218
+ method: 'GET'
219
+ }, options)
220
+ };
221
+ },
222
+ jobs: _ref7 => {
223
+ let workflowId = _ref7.workflowId;
224
+ return {
225
+ execute: options => execute({
226
+ url: `/workflow/${workflowId}/job`,
227
+ headers: {
228
+ 'Content-Type': 'application/json',
229
+ // The CLI throws if this is not present on environment
230
+ 'Circle-Token': process.env.CIRCLE_TOKEN
231
+ },
232
+ method: 'GET'
233
+ }, options)
234
+ };
235
+ },
236
+ approve: _ref8 => {
237
+ let workflowId = _ref8.workflowId,
238
+ approvalRequestId = _ref8.approvalRequestId;
239
+ return {
240
+ execute: options => execute({
241
+ url: `/workflow/${workflowId}/approve/${approvalRequestId} `,
242
+ headers: {
243
+ 'Content-Type': 'application/json',
244
+ // The CLI throws if this is not present on environment
245
+ 'Circle-Token': process.env.CIRCLE_TOKEN
246
+ },
247
+ method: 'POST'
248
+ }, options)
249
+ };
250
+ }
251
+ };
252
+ }
253
+
96
254
  async function loadConfig() {
97
255
  const deploymentConfigExplorer = cosmiconfig.cosmiconfig('deployment', {
98
256
  searchStrategy: 'project'
@@ -412,153 +570,9 @@ async function approve(cliFlags, config, circleCiApis) {
412
570
  }
413
571
  }
414
572
 
415
- async function processCircleCiResponse(response) {
416
- if (!response.ok) {
417
- /**
418
- * NOTE:
419
- * Trying to handle known but undocumented responses of the CircleCI API.
420
- *
421
- * 1. Message: Already approved job
422
- * Deployment was already triggered manually or by train the day before.
423
- */
424
-
425
- // Response data is a stream so text OR json can only be read once, so
426
- // we read it as text first and then try to parse it as json to handle
427
- // known error responses.
428
- const error = await response.text();
429
- try {
430
- // The CircleCI API always uses content-type text/plain, so we always
431
- // try parsing JSON to see if the message property is present.
432
- const _JSON$parse = JSON.parse(error),
433
- message = _JSON$parse.message;
434
- if (message.match(/job already approved/i)) {
435
- console.log('ℹ️ Deployment job is already approved.');
436
- // TODO: can we return instead of force exiting?
437
- node_process.exit(0);
438
- }
439
- } catch {
440
- // Ignore JSON parsing errors
441
- }
442
- throw new Error(`${response.status}: Network response was not ok.\n
443
- Status text is ${response.statusText} and text is ${error}.`);
444
- }
445
- return response.json();
446
- }
447
- function createCircleCiClient(_ref) {
448
- let projectName = _ref.projectName,
449
- apiBaseUrl = _ref.apiBaseUrl;
450
- async function execute(api) {
451
- let _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
452
- skip = _ref2.skip,
453
- debug = _ref2.debug;
454
- let url = `${apiBaseUrl}${api.url}`;
455
- if (api.params && api.method === 'GET') {
456
- const urlSearchParams = new _URLSearchParams__default["default"]();
457
- for (const _ref3 of _Object$entries__default["default"](api.params)) {
458
- var _ref4 = _slicedToArray(_ref3, 2);
459
- const key = _ref4[0];
460
- const value = _ref4[1];
461
- if (value !== null && value !== undefined) {
462
- urlSearchParams.append(key, value);
463
- }
464
- }
465
- url += `?${urlSearchParams.toString()}`;
466
- }
467
- if (skip) {
468
- if (debug) {
469
- console.log(`🏭 Skipping CircleCI call API at: ${url}.`);
470
- }
471
-
472
- // @ts-expect-error
473
- return _Promise__default["default"].resolve();
474
- }
475
- if (debug) {
476
- console.log(`🏭 Calling CircleCI API at: ${url}.`);
477
- }
478
- try {
479
- const response = await fetch(url, {
480
- headers: api.headers,
481
- method: api.method,
482
- body: api.method === 'POST' ? _JSON$stringify__default["default"](api.params) : undefined
483
- });
484
- const processedCircleCiResponse = await processCircleCiResponse(response);
485
- return processedCircleCiResponse;
486
- } catch (error) {
487
- console.log(`⚠️ Calling CircleCI API at: ${url} failed.`);
488
- throw error;
489
- }
490
- }
491
- return {
492
- pipelines: function () {
493
- let _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
494
- pageToken = _ref5.pageToken,
495
- _ref5$projectSlug = _ref5.projectSlug,
496
- projectSlug = _ref5$projectSlug === void 0 ? `gh/commercetools/${projectName}` : _ref5$projectSlug,
497
- _ref5$branch = _ref5.branch,
498
- branch = _ref5$branch === void 0 ? 'main' : _ref5$branch;
499
- return {
500
- execute: options => execute({
501
- url: `/project/${projectSlug}/pipeline`,
502
- headers: {
503
- 'Content-Type': 'application/json',
504
- // The CLI throws if this is not present on environment
505
- 'Circle-Token': process.env.CIRCLE_TOKEN
506
- },
507
- method: 'GET',
508
- params: {
509
- branch,
510
- 'page-token': pageToken
511
- }
512
- }, options)
513
- };
514
- },
515
- workflows: _ref6 => {
516
- let pipelineId = _ref6.pipelineId;
517
- return {
518
- execute: options => execute({
519
- url: `/pipeline/${pipelineId}/workflow`,
520
- headers: {
521
- 'Content-Type': 'application/json',
522
- // The CLI throws if this is not present on environment
523
- 'Circle-Token': process.env.CIRCLE_TOKEN
524
- },
525
- method: 'GET'
526
- }, options)
527
- };
528
- },
529
- jobs: _ref7 => {
530
- let workflowId = _ref7.workflowId;
531
- return {
532
- execute: options => execute({
533
- url: `/workflow/${workflowId}/job`,
534
- headers: {
535
- 'Content-Type': 'application/json',
536
- // The CLI throws if this is not present on environment
537
- 'Circle-Token': process.env.CIRCLE_TOKEN
538
- },
539
- method: 'GET'
540
- }, options)
541
- };
542
- },
543
- approve: _ref8 => {
544
- let workflowId = _ref8.workflowId,
545
- approvalRequestId = _ref8.approvalRequestId;
546
- return {
547
- execute: options => execute({
548
- url: `/workflow/${workflowId}/approve/${approvalRequestId} `,
549
- headers: {
550
- 'Content-Type': 'application/json',
551
- // The CLI throws if this is not present on environment
552
- 'Circle-Token': process.env.CIRCLE_TOKEN
553
- },
554
- method: 'POST'
555
- }, options)
556
- };
557
- }
558
- };
559
- }
560
-
561
- const cli = cac.cac('deployment-cli');
573
+ function ownKeys(e, r) { var t = _Object$keys__default["default"](e); if (_Object$getOwnPropertySymbols__default["default"]) { var o = _Object$getOwnPropertySymbols__default["default"](e); r && (o = _filterInstanceProperty__default["default"](o).call(o, function (r) { return _Object$getOwnPropertyDescriptor__default["default"](e, r).enumerable; })), t.push.apply(t, o); } return t; }
574
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty__default["default"](_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors__default["default"] ? _Object$defineProperties__default["default"](e, _Object$getOwnPropertyDescriptors__default["default"](t)) : _forEachInstanceProperty__default["default"](_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty__default["default"](e, r, _Object$getOwnPropertyDescriptor__default["default"](t, r)); }); } return e; }
575
+ commander.program.name('deployment-cli').description('CLI to help manage deployment pipelines').version(pkgJson.version).option('--dry-run', '(optional) Simulate a deployment.', false).option('--debug', '(optional) Print additional debug information.', false);
562
576
  async function run() {
563
577
  const config = await loadConfig();
564
578
  throwIfConfigurationLacksRequiredValues(config);
@@ -567,36 +581,16 @@ async function run() {
567
581
  apiBaseUrl: config.CircleCI.apiBaseUrl
568
582
  });
569
583
 
570
- // General CLI options
571
- cli.option('--dry-run', '(optional) Simulate a deployment.', {
572
- default: false
573
- });
574
- cli.option('--debug', '(optional) Print additional debug information.', {
575
- default: false
576
- });
577
-
578
- // Default command
579
- cli.command('').usage('\n\n Approve deployment jobs on CI for different components related to MC.').action(cli.outputHelp);
580
-
581
584
  // Command: Approve
582
- const usageApprove = 'Approves a job and by this triggers a deployment of a component. It requires a "CIRCLE_TOKEN" environment variable (https://circleci.com/docs/2.0/managing-api-tokens/).';
583
- cli.command('approve', usageApprove).usage(`approve \n\n ${usageApprove}`).option('--approval-job <string>', 'The name of the approval job to approve a deployment with.').option('--deployment-job [string]', '(optional) The name of the deployment job triggered by the approval job. If passed the CLI will print a URL to the deployment triggered on CircleCI.').option('--deployment [string]', '(optional) The name of a deployment configured in the configuration file.').option('--build-revision [git-sha]', '(optional) The git commit SHA that needs to be deployed. If not specified, the last successful pipeline is used.').option('--branch [string]', '(optional) The git branch to deploy from. If not specified.', {
584
- default: 'main'
585
- }).option('--yes', '(optional) Skip all confirmation prompts. Useful in Continuous integration (CI) to automatically answer confirmation questions.', {
586
- default: false
587
- }).action(async options => {
588
- if (options.dryRun) {
585
+ commander.program.command('approve').description('Approves a job and by this triggers a deployment of a component. It requires a "CIRCLE_TOKEN" environment variable (https://circleci.com/docs/2.0/managing-api-tokens/).').requiredOption('--approval-job <string>', 'The name of the approval job to approve a deployment with.').option('--deployment-job [string]', '(optional) The name of the deployment job triggered by the approval job. If passed the CLI will print a URL to the deployment triggered on CircleCI.').option('--deployment [string]', '(optional) The name of a deployment configured in the configuration file.').option('--build-revision [git-sha]', '(optional) The git commit SHA that needs to be deployed. If not specified, the last successful pipeline is used.').option('--branch [string]', '(optional) The git branch to deploy from. If not specified.', 'main').option('--yes', '(optional) Skip all confirmation prompts. Useful in Continuous integration (CI) to automatically answer confirmation questions.', false).action(async options => {
586
+ const globalOptions = commander.program.opts();
587
+ if (globalOptions.dryRun) {
589
588
  console.log(`🙊 Do not worry. This is a dry run!`);
590
589
  }
591
590
  throwIfRequiredEnvironmentVariableIsUnset(['CIRCLE_TOKEN']);
592
- await approve(options, config, circleCiApis);
593
- });
594
- cli.help();
595
- cli.version(pkgJson.version);
596
- cli.parse(process.argv, {
597
- run: false
591
+ await approve(_objectSpread(_objectSpread({}, globalOptions), options), config, circleCiApis);
598
592
  });
599
- await cli.runMatchedCommand();
593
+ commander.program.parse();
600
594
  }
601
595
 
602
596
  exports.run = run;
@@ -1,24 +1,31 @@
1
- import { cac } from 'cac';
2
- import _findInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/find';
1
+ import _Object$keys from '@babel/runtime-corejs3/core-js-stable/object/keys';
2
+ import _Object$getOwnPropertySymbols from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols';
3
+ import _filterInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/filter';
4
+ import _Object$getOwnPropertyDescriptor from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor';
3
5
  import _forEachInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/for-each';
6
+ import _Object$getOwnPropertyDescriptors from '@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors';
7
+ import _Object$defineProperties from '@babel/runtime-corejs3/core-js-stable/object/define-properties';
8
+ import _Object$defineProperty from '@babel/runtime-corejs3/core-js-stable/object/define-property';
9
+ import _defineProperty from '@babel/runtime-corejs3/helpers/esm/defineProperty';
10
+ import { program } from 'commander';
11
+ import _slicedToArray from '@babel/runtime-corejs3/helpers/esm/slicedToArray';
12
+ import _URLSearchParams from '@babel/runtime-corejs3/core-js-stable/url-search-params';
13
+ import _Object$entries from '@babel/runtime-corejs3/core-js-stable/object/entries';
14
+ import _Promise from '@babel/runtime-corejs3/core-js-stable/promise';
15
+ import _JSON$stringify from '@babel/runtime-corejs3/core-js-stable/json/stringify';
16
+ import { exit } from 'node:process';
17
+ import _findInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/find';
4
18
  import _mapInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/map';
5
- import _filterInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/filter';
6
19
  import _startsWithInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/starts-with';
7
20
  import _concatInstanceProperty from '@babel/runtime-corejs3/core-js-stable/instance/concat';
8
21
  import { cosmiconfig } from 'cosmiconfig';
9
22
  import merge from 'lodash/merge';
10
23
  import prompts from 'prompts';
11
24
  import pRetry from 'p-retry';
12
- import _slicedToArray from '@babel/runtime-corejs3/helpers/esm/slicedToArray';
13
- import _URLSearchParams from '@babel/runtime-corejs3/core-js-stable/url-search-params';
14
- import _Object$entries from '@babel/runtime-corejs3/core-js-stable/object/entries';
15
- import _Promise from '@babel/runtime-corejs3/core-js-stable/promise';
16
- import _JSON$stringify from '@babel/runtime-corejs3/core-js-stable/json/stringify';
17
- import { exit } from 'node:process';
18
25
 
19
26
  var pkgJson = {
20
27
  name: "@commercetools-frontend/deployment-cli",
21
- version: "2.0.2",
28
+ version: "2.0.4",
22
29
  description: "CLI to manage Custom Applications deployments in Google Storage.",
23
30
  keywords: [
24
31
  "commercetools",
@@ -42,9 +49,8 @@ var pkgJson = {
42
49
  },
43
50
  dependencies: {
44
51
  "@babel/core": "^7.22.11",
45
- "@babel/runtime": "^7.21.0",
46
52
  "@babel/runtime-corejs3": "^7.21.0",
47
- cac: "^6.7.14",
53
+ commander: "^13.1.0",
48
54
  cosmiconfig: "9.0.0",
49
55
  lodash: "4.17.21",
50
56
  "p-retry": "4.6.2",
@@ -53,7 +59,7 @@ var pkgJson = {
53
59
  devDependencies: {
54
60
  "@tsconfig/node20": "20.1.4",
55
61
  "@types/lodash": "^4.14.198",
56
- "@types/node": "20.16.10",
62
+ "@types/node": "22.13.9",
57
63
  "@types/prompts": "2.4.9",
58
64
  msw: "1.3.5",
59
65
  typescript: "5.2.2"
@@ -73,6 +79,152 @@ var pkgJson = {
73
79
  }
74
80
  };
75
81
 
82
+ async function processCircleCiResponse(response) {
83
+ if (!response.ok) {
84
+ /**
85
+ * NOTE:
86
+ * Trying to handle known but undocumented responses of the CircleCI API.
87
+ *
88
+ * 1. Message: Already approved job
89
+ * Deployment was already triggered manually or by train the day before.
90
+ */
91
+
92
+ // Response data is a stream so text OR json can only be read once, so
93
+ // we read it as text first and then try to parse it as json to handle
94
+ // known error responses.
95
+ const error = await response.text();
96
+ try {
97
+ // The CircleCI API always uses content-type text/plain, so we always
98
+ // try parsing JSON to see if the message property is present.
99
+ const _JSON$parse = JSON.parse(error),
100
+ message = _JSON$parse.message;
101
+ if (message.match(/job already approved/i)) {
102
+ console.log('ℹ️ Deployment job is already approved.');
103
+ // TODO: can we return instead of force exiting?
104
+ exit(0);
105
+ }
106
+ } catch {
107
+ // Ignore JSON parsing errors
108
+ }
109
+ throw new Error(`${response.status}: Network response was not ok.\n
110
+ Status text is ${response.statusText} and text is ${error}.`);
111
+ }
112
+ return response.json();
113
+ }
114
+ function createCircleCiClient(_ref) {
115
+ let projectName = _ref.projectName,
116
+ apiBaseUrl = _ref.apiBaseUrl;
117
+ async function execute(api) {
118
+ let _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
119
+ skip = _ref2.skip,
120
+ debug = _ref2.debug;
121
+ let url = `${apiBaseUrl}${api.url}`;
122
+ if (api.params && api.method === 'GET') {
123
+ const urlSearchParams = new _URLSearchParams();
124
+ for (const _ref3 of _Object$entries(api.params)) {
125
+ var _ref4 = _slicedToArray(_ref3, 2);
126
+ const key = _ref4[0];
127
+ const value = _ref4[1];
128
+ if (value !== null && value !== undefined) {
129
+ urlSearchParams.append(key, value);
130
+ }
131
+ }
132
+ url += `?${urlSearchParams.toString()}`;
133
+ }
134
+ if (skip) {
135
+ if (debug) {
136
+ console.log(`🏭 Skipping CircleCI call API at: ${url}.`);
137
+ }
138
+
139
+ // @ts-expect-error
140
+ return _Promise.resolve();
141
+ }
142
+ if (debug) {
143
+ console.log(`🏭 Calling CircleCI API at: ${url}.`);
144
+ }
145
+ try {
146
+ const response = await fetch(url, {
147
+ headers: api.headers,
148
+ method: api.method,
149
+ body: api.method === 'POST' ? _JSON$stringify(api.params) : undefined
150
+ });
151
+ const processedCircleCiResponse = await processCircleCiResponse(response);
152
+ return processedCircleCiResponse;
153
+ } catch (error) {
154
+ console.log(`⚠️ Calling CircleCI API at: ${url} failed.`);
155
+ throw error;
156
+ }
157
+ }
158
+ return {
159
+ pipelines: function () {
160
+ let _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
161
+ pageToken = _ref5.pageToken,
162
+ _ref5$projectSlug = _ref5.projectSlug,
163
+ projectSlug = _ref5$projectSlug === void 0 ? `gh/commercetools/${projectName}` : _ref5$projectSlug,
164
+ _ref5$branch = _ref5.branch,
165
+ branch = _ref5$branch === void 0 ? 'main' : _ref5$branch;
166
+ return {
167
+ execute: options => execute({
168
+ url: `/project/${projectSlug}/pipeline`,
169
+ headers: {
170
+ 'Content-Type': 'application/json',
171
+ // The CLI throws if this is not present on environment
172
+ 'Circle-Token': process.env.CIRCLE_TOKEN
173
+ },
174
+ method: 'GET',
175
+ params: {
176
+ branch,
177
+ 'page-token': pageToken
178
+ }
179
+ }, options)
180
+ };
181
+ },
182
+ workflows: _ref6 => {
183
+ let pipelineId = _ref6.pipelineId;
184
+ return {
185
+ execute: options => execute({
186
+ url: `/pipeline/${pipelineId}/workflow`,
187
+ headers: {
188
+ 'Content-Type': 'application/json',
189
+ // The CLI throws if this is not present on environment
190
+ 'Circle-Token': process.env.CIRCLE_TOKEN
191
+ },
192
+ method: 'GET'
193
+ }, options)
194
+ };
195
+ },
196
+ jobs: _ref7 => {
197
+ let workflowId = _ref7.workflowId;
198
+ return {
199
+ execute: options => execute({
200
+ url: `/workflow/${workflowId}/job`,
201
+ headers: {
202
+ 'Content-Type': 'application/json',
203
+ // The CLI throws if this is not present on environment
204
+ 'Circle-Token': process.env.CIRCLE_TOKEN
205
+ },
206
+ method: 'GET'
207
+ }, options)
208
+ };
209
+ },
210
+ approve: _ref8 => {
211
+ let workflowId = _ref8.workflowId,
212
+ approvalRequestId = _ref8.approvalRequestId;
213
+ return {
214
+ execute: options => execute({
215
+ url: `/workflow/${workflowId}/approve/${approvalRequestId} `,
216
+ headers: {
217
+ 'Content-Type': 'application/json',
218
+ // The CLI throws if this is not present on environment
219
+ 'Circle-Token': process.env.CIRCLE_TOKEN
220
+ },
221
+ method: 'POST'
222
+ }, options)
223
+ };
224
+ }
225
+ };
226
+ }
227
+
76
228
  async function loadConfig() {
77
229
  const deploymentConfigExplorer = cosmiconfig('deployment', {
78
230
  searchStrategy: 'project'
@@ -392,153 +544,9 @@ async function approve(cliFlags, config, circleCiApis) {
392
544
  }
393
545
  }
394
546
 
395
- async function processCircleCiResponse(response) {
396
- if (!response.ok) {
397
- /**
398
- * NOTE:
399
- * Trying to handle known but undocumented responses of the CircleCI API.
400
- *
401
- * 1. Message: Already approved job
402
- * Deployment was already triggered manually or by train the day before.
403
- */
404
-
405
- // Response data is a stream so text OR json can only be read once, so
406
- // we read it as text first and then try to parse it as json to handle
407
- // known error responses.
408
- const error = await response.text();
409
- try {
410
- // The CircleCI API always uses content-type text/plain, so we always
411
- // try parsing JSON to see if the message property is present.
412
- const _JSON$parse = JSON.parse(error),
413
- message = _JSON$parse.message;
414
- if (message.match(/job already approved/i)) {
415
- console.log('ℹ️ Deployment job is already approved.');
416
- // TODO: can we return instead of force exiting?
417
- exit(0);
418
- }
419
- } catch {
420
- // Ignore JSON parsing errors
421
- }
422
- throw new Error(`${response.status}: Network response was not ok.\n
423
- Status text is ${response.statusText} and text is ${error}.`);
424
- }
425
- return response.json();
426
- }
427
- function createCircleCiClient(_ref) {
428
- let projectName = _ref.projectName,
429
- apiBaseUrl = _ref.apiBaseUrl;
430
- async function execute(api) {
431
- let _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
432
- skip = _ref2.skip,
433
- debug = _ref2.debug;
434
- let url = `${apiBaseUrl}${api.url}`;
435
- if (api.params && api.method === 'GET') {
436
- const urlSearchParams = new _URLSearchParams();
437
- for (const _ref3 of _Object$entries(api.params)) {
438
- var _ref4 = _slicedToArray(_ref3, 2);
439
- const key = _ref4[0];
440
- const value = _ref4[1];
441
- if (value !== null && value !== undefined) {
442
- urlSearchParams.append(key, value);
443
- }
444
- }
445
- url += `?${urlSearchParams.toString()}`;
446
- }
447
- if (skip) {
448
- if (debug) {
449
- console.log(`🏭 Skipping CircleCI call API at: ${url}.`);
450
- }
451
-
452
- // @ts-expect-error
453
- return _Promise.resolve();
454
- }
455
- if (debug) {
456
- console.log(`🏭 Calling CircleCI API at: ${url}.`);
457
- }
458
- try {
459
- const response = await fetch(url, {
460
- headers: api.headers,
461
- method: api.method,
462
- body: api.method === 'POST' ? _JSON$stringify(api.params) : undefined
463
- });
464
- const processedCircleCiResponse = await processCircleCiResponse(response);
465
- return processedCircleCiResponse;
466
- } catch (error) {
467
- console.log(`⚠️ Calling CircleCI API at: ${url} failed.`);
468
- throw error;
469
- }
470
- }
471
- return {
472
- pipelines: function () {
473
- let _ref5 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
474
- pageToken = _ref5.pageToken,
475
- _ref5$projectSlug = _ref5.projectSlug,
476
- projectSlug = _ref5$projectSlug === void 0 ? `gh/commercetools/${projectName}` : _ref5$projectSlug,
477
- _ref5$branch = _ref5.branch,
478
- branch = _ref5$branch === void 0 ? 'main' : _ref5$branch;
479
- return {
480
- execute: options => execute({
481
- url: `/project/${projectSlug}/pipeline`,
482
- headers: {
483
- 'Content-Type': 'application/json',
484
- // The CLI throws if this is not present on environment
485
- 'Circle-Token': process.env.CIRCLE_TOKEN
486
- },
487
- method: 'GET',
488
- params: {
489
- branch,
490
- 'page-token': pageToken
491
- }
492
- }, options)
493
- };
494
- },
495
- workflows: _ref6 => {
496
- let pipelineId = _ref6.pipelineId;
497
- return {
498
- execute: options => execute({
499
- url: `/pipeline/${pipelineId}/workflow`,
500
- headers: {
501
- 'Content-Type': 'application/json',
502
- // The CLI throws if this is not present on environment
503
- 'Circle-Token': process.env.CIRCLE_TOKEN
504
- },
505
- method: 'GET'
506
- }, options)
507
- };
508
- },
509
- jobs: _ref7 => {
510
- let workflowId = _ref7.workflowId;
511
- return {
512
- execute: options => execute({
513
- url: `/workflow/${workflowId}/job`,
514
- headers: {
515
- 'Content-Type': 'application/json',
516
- // The CLI throws if this is not present on environment
517
- 'Circle-Token': process.env.CIRCLE_TOKEN
518
- },
519
- method: 'GET'
520
- }, options)
521
- };
522
- },
523
- approve: _ref8 => {
524
- let workflowId = _ref8.workflowId,
525
- approvalRequestId = _ref8.approvalRequestId;
526
- return {
527
- execute: options => execute({
528
- url: `/workflow/${workflowId}/approve/${approvalRequestId} `,
529
- headers: {
530
- 'Content-Type': 'application/json',
531
- // The CLI throws if this is not present on environment
532
- 'Circle-Token': process.env.CIRCLE_TOKEN
533
- },
534
- method: 'POST'
535
- }, options)
536
- };
537
- }
538
- };
539
- }
540
-
541
- const cli = cac('deployment-cli');
547
+ function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
548
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _context, _context2; var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? _forEachInstanceProperty(_context = ownKeys(Object(t), !0)).call(_context, function (r) { _defineProperty(e, r, t[r]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(e, _Object$getOwnPropertyDescriptors(t)) : _forEachInstanceProperty(_context2 = ownKeys(Object(t))).call(_context2, function (r) { _Object$defineProperty(e, r, _Object$getOwnPropertyDescriptor(t, r)); }); } return e; }
549
+ program.name('deployment-cli').description('CLI to help manage deployment pipelines').version(pkgJson.version).option('--dry-run', '(optional) Simulate a deployment.', false).option('--debug', '(optional) Print additional debug information.', false);
542
550
  async function run() {
543
551
  const config = await loadConfig();
544
552
  throwIfConfigurationLacksRequiredValues(config);
@@ -547,36 +555,16 @@ async function run() {
547
555
  apiBaseUrl: config.CircleCI.apiBaseUrl
548
556
  });
549
557
 
550
- // General CLI options
551
- cli.option('--dry-run', '(optional) Simulate a deployment.', {
552
- default: false
553
- });
554
- cli.option('--debug', '(optional) Print additional debug information.', {
555
- default: false
556
- });
557
-
558
- // Default command
559
- cli.command('').usage('\n\n Approve deployment jobs on CI for different components related to MC.').action(cli.outputHelp);
560
-
561
558
  // Command: Approve
562
- const usageApprove = 'Approves a job and by this triggers a deployment of a component. It requires a "CIRCLE_TOKEN" environment variable (https://circleci.com/docs/2.0/managing-api-tokens/).';
563
- cli.command('approve', usageApprove).usage(`approve \n\n ${usageApprove}`).option('--approval-job <string>', 'The name of the approval job to approve a deployment with.').option('--deployment-job [string]', '(optional) The name of the deployment job triggered by the approval job. If passed the CLI will print a URL to the deployment triggered on CircleCI.').option('--deployment [string]', '(optional) The name of a deployment configured in the configuration file.').option('--build-revision [git-sha]', '(optional) The git commit SHA that needs to be deployed. If not specified, the last successful pipeline is used.').option('--branch [string]', '(optional) The git branch to deploy from. If not specified.', {
564
- default: 'main'
565
- }).option('--yes', '(optional) Skip all confirmation prompts. Useful in Continuous integration (CI) to automatically answer confirmation questions.', {
566
- default: false
567
- }).action(async options => {
568
- if (options.dryRun) {
559
+ program.command('approve').description('Approves a job and by this triggers a deployment of a component. It requires a "CIRCLE_TOKEN" environment variable (https://circleci.com/docs/2.0/managing-api-tokens/).').requiredOption('--approval-job <string>', 'The name of the approval job to approve a deployment with.').option('--deployment-job [string]', '(optional) The name of the deployment job triggered by the approval job. If passed the CLI will print a URL to the deployment triggered on CircleCI.').option('--deployment [string]', '(optional) The name of a deployment configured in the configuration file.').option('--build-revision [git-sha]', '(optional) The git commit SHA that needs to be deployed. If not specified, the last successful pipeline is used.').option('--branch [string]', '(optional) The git branch to deploy from. If not specified.', 'main').option('--yes', '(optional) Skip all confirmation prompts. Useful in Continuous integration (CI) to automatically answer confirmation questions.', false).action(async options => {
560
+ const globalOptions = program.opts();
561
+ if (globalOptions.dryRun) {
569
562
  console.log(`🙊 Do not worry. This is a dry run!`);
570
563
  }
571
564
  throwIfRequiredEnvironmentVariableIsUnset(['CIRCLE_TOKEN']);
572
- await approve(options, config, circleCiApis);
573
- });
574
- cli.help();
575
- cli.version(pkgJson.version);
576
- cli.parse(process.argv, {
577
- run: false
565
+ await approve(_objectSpread(_objectSpread({}, globalOptions), options), config, circleCiApis);
578
566
  });
579
- await cli.runMatchedCommand();
567
+ program.parse();
580
568
  }
581
569
 
582
570
  export { run };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commercetools-frontend/deployment-cli",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "CLI to manage Custom Applications deployments in Google Storage.",
5
5
  "keywords": [
6
6
  "commercetools",
@@ -21,9 +21,8 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "@babel/core": "^7.22.11",
24
- "@babel/runtime": "^7.21.0",
25
24
  "@babel/runtime-corejs3": "^7.21.0",
26
- "cac": "^6.7.14",
25
+ "commander": "^13.1.0",
27
26
  "cosmiconfig": "9.0.0",
28
27
  "lodash": "4.17.21",
29
28
  "p-retry": "4.6.2",
@@ -32,7 +31,7 @@
32
31
  "devDependencies": {
33
32
  "@tsconfig/node20": "20.1.4",
34
33
  "@types/lodash": "^4.14.198",
35
- "@types/node": "20.16.10",
34
+ "@types/node": "22.13.9",
36
35
  "@types/prompts": "2.4.9",
37
36
  "msw": "1.3.5",
38
37
  "typescript": "5.2.2"