@adinsure-ops/ops-cli 5.2.2 → 5.3.1

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.
Files changed (42) hide show
  1. package/README.md +123 -9
  2. package/lib/commands/config/get.d.ts +11 -0
  3. package/lib/commands/config/get.js +40 -0
  4. package/lib/commands/config/list.d.ts +8 -0
  5. package/lib/commands/config/list.js +26 -0
  6. package/lib/commands/config/set.d.ts +12 -0
  7. package/lib/commands/config/set.js +60 -0
  8. package/lib/commands/config/unset.d.ts +11 -0
  9. package/lib/commands/config/unset.js +42 -0
  10. package/lib/commands/download/client.d.ts +11 -3
  11. package/lib/commands/download/client.js +52 -8
  12. package/lib/commands/download/identity-server.d.ts +11 -3
  13. package/lib/commands/download/identity-server.js +53 -8
  14. package/lib/commands/download/jmeter.d.ts +1 -1
  15. package/lib/commands/download/jmeter.js +13 -3
  16. package/lib/commands/download/keycloak-scim.d.ts +1 -1
  17. package/lib/commands/download/keycloak-scim.js +13 -3
  18. package/lib/commands/download/platform.d.ts +11 -3
  19. package/lib/commands/download/platform.js +53 -8
  20. package/lib/commands/download/scheduler.d.ts +11 -3
  21. package/lib/commands/download/scheduler.js +52 -8
  22. package/lib/commands/download/server.d.ts +11 -3
  23. package/lib/commands/download/server.js +53 -9
  24. package/lib/commands/download/signalr.d.ts +11 -3
  25. package/lib/commands/download/signalr.js +52 -8
  26. package/lib/commands/download/studio.d.ts +10 -2
  27. package/lib/commands/download/studio.js +49 -7
  28. package/lib/commands/login.d.ts +9 -0
  29. package/lib/commands/login.js +60 -1
  30. package/lib/commands/login.ru.d.ts +12 -0
  31. package/lib/commands/login.ru.js +42 -0
  32. package/lib/commands/npm/check-publish.js +1 -1
  33. package/lib/commands/npm/publish.d.ts +2 -0
  34. package/lib/commands/npm/publish.js +61 -58
  35. package/lib/lib/config.d.ts +10 -0
  36. package/lib/lib/config.js +33 -0
  37. package/lib/lib/yarnrc.d.ts +22 -0
  38. package/lib/lib/yarnrc.js +40 -0
  39. package/lib/security/context.d.ts +22 -0
  40. package/lib/security/context.js +113 -0
  41. package/oclif.manifest.json +275 -11
  42. package/package.json +1 -1
@@ -18,56 +18,61 @@ class NpmPublish extends CommandBase {
18
18
  else {
19
19
  [sh, ...args] = cmd;
20
20
  }
21
+ this.cp = spawn(sh, args, {
22
+ cwd: pkg.path,
23
+ env: Object.assign(Object.assign({}, process.env), process.stdout.isTTY ? { FORCE_COLOR: process.env.FORCE_COLOR || '1' } : {}),
24
+ stdio: 'pipe',
25
+ });
26
+ let data = '';
21
27
  try {
22
- this.cp = spawn(sh, args, {
23
- cwd: pkg.path,
24
- env: Object.assign(Object.assign({}, process.env), process.stdout.isTTY ? { FORCE_COLOR: process.env.FORCE_COLOR || '1' } : {}),
25
- stdio: 'pipe',
26
- });
27
- let data = '';
28
- try {
29
- for (var _g = true, _h = __asyncValues(this.cp.stdout), _j; _j = yield _h.next(), _a = _j.done, !_a; _g = true) {
30
- _c = _j.value;
31
- _g = false;
32
- const chunk = _c;
33
- data += chunk;
34
- }
35
- }
36
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
37
- finally {
38
- try {
39
- if (!_g && !_a && (_b = _h.return)) yield _b.call(_h);
40
- }
41
- finally { if (e_1) throw e_1.error; }
28
+ for (var _g = true, _h = __asyncValues(this.cp.stdout), _j; _j = yield _h.next(), _a = _j.done, !_a; _g = true) {
29
+ _c = _j.value;
30
+ _g = false;
31
+ const chunk = _c;
32
+ data += chunk;
42
33
  }
43
- let error = '';
34
+ }
35
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
36
+ finally {
44
37
  try {
45
- for (var _k = true, _l = __asyncValues(this.cp.stderr), _m; _m = yield _l.next(), _d = _m.done, !_d; _k = true) {
46
- _f = _m.value;
47
- _k = false;
48
- const chunk = _f;
49
- error += chunk;
50
- }
38
+ if (!_g && !_a && (_b = _h.return)) yield _b.call(_h);
51
39
  }
52
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
53
- finally {
54
- try {
55
- if (!_k && !_d && (_e = _l.return)) yield _e.call(_l);
56
- }
57
- finally { if (e_2) throw e_2.error; }
40
+ finally { if (e_1) throw e_1.error; }
41
+ }
42
+ let error = '';
43
+ try {
44
+ for (var _k = true, _l = __asyncValues(this.cp.stderr), _m; _m = yield _l.next(), _d = _m.done, !_d; _k = true) {
45
+ _f = _m.value;
46
+ _k = false;
47
+ const chunk = _f;
48
+ error += chunk;
58
49
  }
59
- const exitCode = yield new Promise((resolve) => {
60
- this.cp.on('close', resolve);
61
- });
62
- if (exitCode) {
63
- throw new Error(`subprocess error exit ${exitCode}, ${error}`);
50
+ }
51
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
52
+ finally {
53
+ try {
54
+ if (!_k && !_d && (_e = _l.return)) yield _e.call(_l);
64
55
  }
65
- return { data, error };
66
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ finally { if (e_2) throw e_2.error; }
67
57
  }
68
- catch (error) {
69
- throw new Error(error.message);
58
+ const exitCode = yield new Promise((resolve) => {
59
+ this.cp.on('close', resolve);
60
+ });
61
+ return { data, error, exitCode };
62
+ });
63
+ }
64
+ wasPublished(data, error, exitCode) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ if (exitCode) {
67
+ return false;
68
+ }
69
+ // Extract the HTTP status code from the error message
70
+ const statusCodeMatch = /\d{3}/.exec(error);
71
+ const statusCode = statusCodeMatch ? Number.parseInt(statusCodeMatch[0], 10) : null;
72
+ if (statusCode && (statusCode >= 400 && statusCode < 600)) {
73
+ return false;
70
74
  }
75
+ return true;
71
76
  });
72
77
  }
73
78
  checkPackageCanPublish(pkg, allPackages, version, retries) {
@@ -83,22 +88,17 @@ class NpmPublish extends CommandBase {
83
88
  if (!matchedPackage) {
84
89
  ux.action.stop(`${chalk.green('new package')}`);
85
90
  ux.action.start(`publish new package ${pkg.name}@${pkg.version} from ${pkg.jsonPath}`, undefined, { stdout: true });
86
- try {
87
- yield this.publishPackage(pkg);
88
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
- }
90
- catch (error) {
91
- if (error instanceof Error && error.message.includes('subprocess error exit')) {
92
- // Check if the error message contains 'subprocess error exit'
93
- // Extract the HTTP status code from the error message
94
- const statusCodeMatch = /\d{3}/.exec(error.message);
95
- const statusCode = statusCodeMatch ? Number.parseInt(statusCodeMatch[0], 10) : null;
96
- if (statusCode && (statusCode >= 400 && statusCode < 600)) {
97
- for (let i = 0; i < retries - 1; i++) {
98
- yield this.publishPackage(pkg);
99
- }
100
- }
91
+ let published = false;
92
+ for (let i = 0; i < retries - 1; i++) {
93
+ const { data, error, exitCode } = yield this.publishPackage(pkg);
94
+ if (!this.wasPublished(data, error, exitCode)) {
95
+ continue;
101
96
  }
97
+ published = true;
98
+ break;
99
+ }
100
+ if (!published) {
101
+ throw new Error(`publishing of ${pkg.name}@${pkg.version} failed`);
102
102
  }
103
103
  ux.action.stop(`${chalk.green('done')}`);
104
104
  return true;
@@ -107,7 +107,10 @@ class NpmPublish extends CommandBase {
107
107
  if (!matchVersion) {
108
108
  ux.action.stop(`${chalk.yellow('version missing')}`);
109
109
  ux.action.start(`publish new version ${pkg.name}@${pkg.version} from ${pkg.jsonPath}`, undefined, { stdout: true });
110
- yield this.publishPackage(pkg);
110
+ const { data, error, exitCode } = yield this.publishPackage(pkg);
111
+ if (!this.wasPublished(data, error, exitCode)) {
112
+ throw new Error(`publishing of ${pkg.name}@${pkg.version} failed`);
113
+ }
111
114
  ux.action.stop(`${chalk.green('done')}`);
112
115
  return true;
113
116
  }
@@ -0,0 +1,10 @@
1
+ export default class OpsConfig {
2
+ private filePath;
3
+ private data;
4
+ constructor(filePath: string);
5
+ get(key: string): unknown;
6
+ set(key: string, value: unknown): void;
7
+ unset(key: string): void;
8
+ list(): Record<string, unknown>;
9
+ save(): void;
10
+ }
@@ -0,0 +1,33 @@
1
+ import fs from 'fs';
2
+ export default class OpsConfig {
3
+ constructor(filePath) {
4
+ this.data = {};
5
+ this.filePath = filePath;
6
+ // Load existing config or initialize an empty one
7
+ if (fs.existsSync(this.filePath)) {
8
+ const fileContent = fs.readFileSync(this.filePath, 'utf8');
9
+ try {
10
+ this.data = JSON.parse(fileContent);
11
+ }
12
+ catch (_a) {
13
+ console.log("Could not read data from config.json");
14
+ this.data = {};
15
+ }
16
+ }
17
+ }
18
+ get(key) {
19
+ return this.data[key];
20
+ }
21
+ set(key, value) {
22
+ this.data[key] = value;
23
+ }
24
+ unset(key) {
25
+ delete this.data[key];
26
+ }
27
+ list() {
28
+ return this.data;
29
+ }
30
+ save() {
31
+ fs.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), 'utf8');
32
+ }
33
+ }
@@ -0,0 +1,22 @@
1
+ export default class YarnRc {
2
+ private readonly filePath;
3
+ private config;
4
+ constructor(createIfNotExists?: boolean);
5
+ /**
6
+ * Load config file
7
+ * @returns {void}
8
+ */
9
+ load(): void;
10
+ /**
11
+ * Add registry to config file
12
+ * @param key Config key
13
+ * @param value Config value
14
+ * @returns {void}
15
+ */
16
+ set(key: string, value: string): void;
17
+ /**
18
+ * Set token for Registry
19
+ * @returns {void}
20
+ */
21
+ save(): void;
22
+ }
@@ -0,0 +1,40 @@
1
+ import fs from "fs";
2
+ import os from "os";
3
+ import path from "path";
4
+ import yaml from "yaml";
5
+ export default class YarnRc {
6
+ constructor(createIfNotExists = true) {
7
+ this.config = {};
8
+ this.filePath = path.resolve(os.homedir(), `.yarnrc.yml`);
9
+ if (!fs.existsSync(this.filePath) && createIfNotExists) {
10
+ fs.writeFileSync(this.filePath, JSON.stringify({}));
11
+ }
12
+ if (fs.existsSync(this.filePath) &&
13
+ fs.lstatSync(this.filePath).isFile()) {
14
+ this.load();
15
+ }
16
+ }
17
+ /**
18
+ * Load config file
19
+ * @returns {void}
20
+ */
21
+ load() {
22
+ this.config = yaml.parse(fs.readFileSync(this.filePath, "utf8"));
23
+ }
24
+ /**
25
+ * Add registry to config file
26
+ * @param key Config key
27
+ * @param value Config value
28
+ * @returns {void}
29
+ */
30
+ set(key, value) {
31
+ this.config[key] = value;
32
+ }
33
+ /**
34
+ * Set token for Registry
35
+ * @returns {void}
36
+ */
37
+ save() {
38
+ fs.writeFileSync(this.filePath, yaml.stringify(this.config));
39
+ }
40
+ }
@@ -7,11 +7,16 @@ import { ParsedUrlQuery } from 'querystring';
7
7
  export default class SecurityContext {
8
8
  private readonly clientId;
9
9
  private readonly devopsResourceId;
10
+ private readonly managementCoreWindowsNet;
10
11
  private readonly tenant;
12
+ private readonly tenantId;
13
+ private readonly registry;
11
14
  private readonly authorityUrl;
12
15
  private readonly redirectUri;
13
16
  private readonly resource;
14
17
  private readonly _accessTokenPath;
18
+ private readonly _accessTokenRuPath;
19
+ private readonly _accessConfigPath;
15
20
  private readonly _pca;
16
21
  private readonly _cryptoProvider;
17
22
  private _verifier;
@@ -26,6 +31,16 @@ export default class SecurityContext {
26
31
  * @returns {string} Returns access token path
27
32
  */
28
33
  getTokenPath(): string;
34
+ /**
35
+ * Get the accessTokenRuPath, as it is a private variable
36
+ * @returns {string} Returns access token Ru path
37
+ */
38
+ getTokenRuPath(): string;
39
+ /**
40
+ * Get the accessConfigPath, as it is a private variable
41
+ * @returns {string} Returns config.json path
42
+ */
43
+ getConfigPath(): string;
29
44
  /**
30
45
  * Contacts authority server, fetches and saves the authentication token to be used in subsequent requests.
31
46
  *
@@ -58,6 +73,13 @@ export default class SecurityContext {
58
73
  * @returns Promise that resolves bearer authentication token
59
74
  */
60
75
  getToken(): Promise<string>;
76
+ /**
77
+ * Fetches the locally stored token for GitlabRu
78
+ * @returns authentication token for gitlabru.adacta-fintech.ru
79
+ */
80
+ getTokenRu(): string;
81
+ getAADToken(): Promise<string>;
82
+ getACRToken(): Promise<string>;
61
83
  /**
62
84
  * Contacts authority server, fetches and saves the authentication token to be used in subsequent requests.
63
85
  *
@@ -2,7 +2,10 @@ import { __awaiter } from "tslib";
2
2
  import { AuthError, ConfidentialClientApplication, CryptoProvider, InteractionRequiredAuthError, PublicClientApplication, } from '@azure/msal-node';
3
3
  import { reject } from 'lodash-es';
4
4
  import path from 'path';
5
+ import fs from 'fs';
6
+ import querystring from 'querystring';
5
7
  import LocalCachePlugin from './cache.js';
8
+ import axios from 'axios';
6
9
  /**
7
10
  * Security context that keeps track of current authentication token.
8
11
  * It's not recommended you instantiate this class on your own but rather inherit your commands from [[CommandBase]].
@@ -15,13 +18,18 @@ export default class SecurityContext {
15
18
  constructor(configDir) {
16
19
  this.clientId = 'bed40d09-53d7-4af8-bf63-3288658b65fb';
17
20
  this.devopsResourceId = '499b84ac-1321-427f-aa17-267ca6975798';
21
+ this.managementCoreWindowsNet = 'https://management.core.windows.net//';
18
22
  this.tenant = 'adfintech.onmicrosoft.com';
23
+ this.tenantId = 'cdeb9156-219a-49e3-b414-f63acd298e9c';
24
+ this.registry = 'adinsure.azurecr.io';
19
25
  this.authorityUrl = `https://login.microsoftonline.com/${this.tenant}/`;
20
26
  this.redirectUri = 'http://localhost:3044';
21
27
  this.resource = 'https://ops.adinsure.com';
22
28
  this._verifier = null;
23
29
  this._challenge = null;
24
30
  this._accessTokenPath = path.resolve(configDir, 'accessToken.json');
31
+ this._accessTokenRuPath = path.resolve(configDir, 'accessTokenRu.json');
32
+ this._accessConfigPath = path.resolve(configDir, 'config.json');
25
33
  this._cryptoProvider = new CryptoProvider();
26
34
  this._pca = new PublicClientApplication({
27
35
  auth: {
@@ -40,6 +48,20 @@ export default class SecurityContext {
40
48
  getTokenPath() {
41
49
  return this._accessTokenPath;
42
50
  }
51
+ /**
52
+ * Get the accessTokenRuPath, as it is a private variable
53
+ * @returns {string} Returns access token Ru path
54
+ */
55
+ getTokenRuPath() {
56
+ return this._accessTokenRuPath;
57
+ }
58
+ /**
59
+ * Get the accessConfigPath, as it is a private variable
60
+ * @returns {string} Returns config.json path
61
+ */
62
+ getConfigPath() {
63
+ return this._accessConfigPath;
64
+ }
43
65
  /**
44
66
  * Contacts authority server, fetches and saves the authentication token to be used in subsequent requests.
45
67
  *
@@ -202,6 +224,97 @@ export default class SecurityContext {
202
224
  }));
203
225
  });
204
226
  }
227
+ /**
228
+ * Fetches the locally stored token for GitlabRu
229
+ * @returns authentication token for gitlabru.adacta-fintech.ru
230
+ */
231
+ getTokenRu() {
232
+ try {
233
+ const fileContent = fs.readFileSync(this._accessTokenRuPath, 'utf8').trim();
234
+ if (!fileContent) {
235
+ throw new Error('Token file is empty.');
236
+ }
237
+ const tokenData = JSON.parse(fileContent);
238
+ if (!tokenData.accessTokenRu) {
239
+ throw new Error('GitLabRu token not found in file.');
240
+ }
241
+ return tokenData.accessTokenRu;
242
+ //eslint-disable-next-line @typescript-eslint/no-explicit-any
243
+ }
244
+ catch (err) {
245
+ throw new Error(`Failed to retrieve GitLabRu token: ${err.message}`);
246
+ }
247
+ }
248
+ getAADToken() {
249
+ return __awaiter(this, void 0, void 0, function* () {
250
+ const msalTokenCache = this._pca.getTokenCache();
251
+ const cachedAccounts = yield msalTokenCache.getAllAccounts();
252
+ return new Promise((resolve, reject) => {
253
+ if (cachedAccounts.length > 0) {
254
+ this._pca
255
+ .acquireTokenSilent({
256
+ account: cachedAccounts[0],
257
+ scopes: [
258
+ this.managementCoreWindowsNet + '.default',
259
+ 'offline_access',
260
+ ],
261
+ })
262
+ .then((response) => {
263
+ if (response) {
264
+ resolve(response.accessToken);
265
+ }
266
+ else {
267
+ reject(new Error('Error getting token, please call `ops login`.'));
268
+ }
269
+ })
270
+ .catch((error) => {
271
+ reject(new Error(error));
272
+ });
273
+ }
274
+ else {
275
+ reject();
276
+ }
277
+ });
278
+ });
279
+ }
280
+ getACRToken() {
281
+ return __awaiter(this, void 0, void 0, function* () {
282
+ try {
283
+ // First, retrieve the AAD token using your existing method.
284
+ const aadAccessToken = yield this.getAADToken();
285
+ // Build the form data as per the ACR token exchange API:
286
+ // grant_type=access_token
287
+ // service=<registry>
288
+ // tenant=<tenantId>
289
+ // access_token=<aadAccessToken>
290
+ const postData = querystring.stringify({
291
+ grant_type: 'access_token',
292
+ service: this.registry, // e.g., "adinsure.azurecr.io"
293
+ tenant: this.tenantId, // e.g., "cdeb9156-219a-49e3-b414-f63acd298e9c"
294
+ access_token: aadAccessToken,
295
+ });
296
+ // Define the URL for token exchange:
297
+ const url = `https://${this.registry}/oauth2/exchange`;
298
+ // Make the POST request with the appropriate headers.
299
+ const response = yield axios.post(url, postData, {
300
+ headers: {
301
+ 'Content-Type': 'application/x-www-form-urlencoded',
302
+ },
303
+ });
304
+ // The ACR token should be in the "access_token" field of the response.
305
+ if (response.data && response.data.refresh_token) {
306
+ return response.data.refresh_token;
307
+ }
308
+ else {
309
+ throw new Error('ACR token not found in the response.');
310
+ }
311
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
312
+ }
313
+ catch (error) {
314
+ throw new Error('Failed to retrieve ACR token: ' + error.message);
315
+ }
316
+ });
317
+ }
205
318
  /**
206
319
  * Contacts authority server, fetches and saves the authentication token to be used in subsequent requests.
207
320
  *