@celonis/content-cli 1.7.0 → 1.8.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 (28) hide show
  1. package/README.md +5 -5
  2. package/commands/configuration-management/api/package-version-api.js +9 -0
  3. package/commands/configuration-management/api/staging-package-variables-api.js +39 -0
  4. package/commands/configuration-management/config-command.service.js +10 -2
  5. package/commands/configuration-management/interfaces/package-version.interfaces.js +6 -0
  6. package/commands/configuration-management/module.js +34 -3
  7. package/commands/configuration-management/package-version-command.service.js +5 -0
  8. package/commands/configuration-management/package-version.service.js +32 -0
  9. package/commands/configuration-management/variable.service.js +22 -1
  10. package/commands/profile/module.js +9 -0
  11. package/commands/profile/profile-command.service.js +18 -1
  12. package/core/profile/profile.service.js +45 -11
  13. package/core/profile/secret-storage.service.js +115 -0
  14. package/lib/commands/configuration-management/api/package-version-api.d.ts +2 -1
  15. package/lib/commands/configuration-management/api/staging-package-variables-api.d.ts +7 -0
  16. package/lib/commands/configuration-management/config-command.service.d.ts +1 -1
  17. package/lib/commands/configuration-management/interfaces/package-export.interfaces.d.ts +4 -0
  18. package/lib/commands/configuration-management/interfaces/package-version.interfaces.d.ts +21 -0
  19. package/lib/commands/configuration-management/module.d.ts +1 -0
  20. package/lib/commands/configuration-management/package-version-command.service.d.ts +1 -0
  21. package/lib/commands/configuration-management/package-version.service.d.ts +2 -0
  22. package/lib/commands/configuration-management/variable.service.d.ts +4 -0
  23. package/lib/commands/profile/module.d.ts +1 -0
  24. package/lib/commands/profile/profile-command.service.d.ts +1 -0
  25. package/lib/core/profile/profile.interface.d.ts +7 -4
  26. package/lib/core/profile/profile.service.d.ts +2 -2
  27. package/lib/core/profile/secret-storage.service.d.ts +7 -0
  28. package/package.json +4 -3
package/README.md CHANGED
@@ -72,8 +72,8 @@ Example, if you want to push a package from zipped file "interesting-package.zip
72
72
  content-cli push package -p my-dev-profile --spaceKey my-cool-space -f interesting-package.zip
73
73
  ```
74
74
 
75
- A more comprehensive list of Content CLI capabilities can be found on the following
76
- [documentation](https://github.com/celonis/content-cli/blob/master/DOCUMENTATION.md).
75
+ A comprehensive list of Content CLI capabilities can be found in the
76
+ [documentation](https://github.com/celonis/content-cli/tree/main/docs).
77
77
 
78
78
  You can still explore the full capabilities of Content CLI and the list of options for the different commands
79
79
  by using the `-h` option in your command.
@@ -159,14 +159,14 @@ npm i -g @celonis/content-cli
159
159
  ## Contributing
160
160
 
161
161
  We encourage public contributions! Please check
162
- [CONTRIBUTING.md](https://github.com/celonis/content-cli/blob/master/CONTRIBUTING.md) for details on our
162
+ [CONTRIBUTING.md](https://github.com/celonis/content-cli/blob/main/CONTRIBUTING.md) for details on our
163
163
  code of conduct.
164
164
 
165
- For details on command development, refer to the [How to Add a Command](https://github.com/celonis/content-cli/blob/master/docs/how-to-add-command.md) guide.
165
+ For details on command development, refer to the [How to Add a Command](https://github.com/celonis/content-cli/blob/main/docs/how-to-add-command.md) guide.
166
166
 
167
167
  ## License
168
168
 
169
169
  Copyright (c) 2021 Celonis SE
170
170
 
171
171
  This project is licensed under the MIT License -
172
- see [LICENSE](https://github.com/celonis/content-cli/blob/master/LICENSE) file for details.
172
+ see [LICENSE](https://github.com/celonis/content-cli/blob/main/LICENSE) file for details.
@@ -33,6 +33,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
33
33
  });
34
34
  });
35
35
  }
36
+ createVersion(packageKey, request) {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ return this.httpClient()
39
+ .post(`/pacman/api/core/packages/${packageKey}/versions`, request)
40
+ .catch(e => {
41
+ throw new logger_1.FatalError(`Problem creating version for package '${packageKey}': ${e}`);
42
+ });
43
+ });
44
+ }
36
45
  }
37
46
  exports.PackageVersionApi = PackageVersionApi;
38
47
  });
@@ -0,0 +1,39 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ (function (factory) {
11
+ if (typeof module === "object" && typeof module.exports === "object") {
12
+ var v = factory(require, exports);
13
+ if (v !== undefined) module.exports = v;
14
+ }
15
+ else if (typeof define === "function" && define.amd) {
16
+ define(["require", "exports", "../../../core/utils/logger"], factory);
17
+ }
18
+ })(function (require, exports) {
19
+ "use strict";
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.StagingPackageVariablesApi = void 0;
22
+ const logger_1 = require("../../../core/utils/logger");
23
+ class StagingPackageVariablesApi {
24
+ constructor(context) {
25
+ this.httpClient = () => context.httpClient;
26
+ }
27
+ findAllByPackageKeys(packageKeys) {
28
+ return __awaiter(this, void 0, void 0, function* () {
29
+ const path = `/pacman/api/core/staging/packages/variables/by-package-keys`;
30
+ return yield this.httpClient()
31
+ .post(path, packageKeys)
32
+ .catch(e => {
33
+ throw new logger_1.FatalError(`Problem listing staging variables for packages: ${e}`);
34
+ });
35
+ });
36
+ }
37
+ }
38
+ exports.StagingPackageVariablesApi = StagingPackageVariablesApi;
39
+ });
@@ -46,9 +46,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
46
46
  }
47
47
  });
48
48
  }
49
- listVariables(jsonResponse, keysByVersion, keysByVersionFile) {
49
+ listVariables(jsonResponse, keysByVersion, keysByVersionFile, packageKeys) {
50
50
  return __awaiter(this, void 0, void 0, function* () {
51
- if (jsonResponse) {
51
+ if (packageKeys.length > 0) {
52
+ if (jsonResponse) {
53
+ yield this.variableService.exportStagingVariables(packageKeys);
54
+ }
55
+ else {
56
+ yield this.variableService.listStagingVariables(packageKeys);
57
+ }
58
+ }
59
+ else if (jsonResponse) {
52
60
  yield this.variableService.exportVariables(keysByVersion, keysByVersionFile);
53
61
  }
54
62
  else {
@@ -9,4 +9,10 @@
9
9
  })(function (require, exports) {
10
10
  "use strict";
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.VersionBumpOption = void 0;
13
+ var VersionBumpOption;
14
+ (function (VersionBumpOption) {
15
+ VersionBumpOption["NONE"] = "NONE";
16
+ VersionBumpOption["PATCH"] = "PATCH";
17
+ })(VersionBumpOption || (exports.VersionBumpOption = VersionBumpOption = {}));
12
18
  });
@@ -79,12 +79,22 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
79
79
  .requiredOption("--packageVersion <packageVersion>", "Version of the package. Can be a specific version or \"LATEST\" keyword, which will return the most recently created version.")
80
80
  .option("--json", "Return the response as a JSON file")
81
81
  .action(this.getPackageVersion);
82
+ configVersionCommand.command("create")
83
+ .description("Create a new version for a package")
84
+ .requiredOption("--packageKey <packageKey>", "Identifier of the package")
85
+ .option("--packageVersion <packageVersion>", "Version string (required if versionBumpOption is NONE)")
86
+ .option("--versionBumpOption <versionBumpOption>", "Version bump option: NONE or PATCH", "NONE")
87
+ .option("--summaryOfChanges <summaryOfChanges>", "Summary of changes for this version")
88
+ .option("--nodeFilterKeys <nodeFilterKeys...>", "Node keys to include in the version. If omitted, all nodes of the package are included.")
89
+ .option("--json", "Return the response as a JSON file")
90
+ .action(this.createPackageVersion);
82
91
  const variablesCommand = configCommand.command("variables")
83
92
  .description("Commands related to variable configs");
84
93
  variablesCommand.command("list")
85
- .description("Command to list versioned variables of packages")
94
+ .description("List package variables: use --packageKeys for unversioned, or --keysByVersion / --keysByVersionFile for versioned packages")
86
95
  .option("--json", "Return response as json type", "")
87
- .option("--keysByVersion <keysByVersion...>", "Mapping of package keys and versions", "")
96
+ .option("--packageKeys <packageKeys...>", "Package keys (unversioned variables only; mutually exclusive with versioned options)", [])
97
+ .option("--keysByVersion <keysByVersion...>", "Mapping of package keys and versions", [])
88
98
  .option("--keysByVersionFile <keysByVersionFile>", "Package keys by version mappings file path.", "")
89
99
  .action(this.listVariables);
90
100
  const nodesCommand = configCommand.command("nodes")
@@ -162,6 +172,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
162
172
  yield new package_version_command_service_1.PackageVersionCommandService(context).getPackageVersion(options.packageKey, options.packageVersion, options.json);
163
173
  });
164
174
  }
175
+ createPackageVersion(context, command, options) {
176
+ return __awaiter(this, void 0, void 0, function* () {
177
+ const hasExplicitVersion = !!options.packageVersion;
178
+ const hasVersionBump = options.versionBumpOption && options.versionBumpOption !== "NONE";
179
+ if (hasExplicitVersion && hasVersionBump) {
180
+ throw new Error("Please provide either --packageVersion or --versionBumpOption, but not both.");
181
+ }
182
+ if (!hasExplicitVersion && !hasVersionBump) {
183
+ throw new Error("Please provide either --packageVersion or --versionBumpOption PATCH.");
184
+ }
185
+ yield new package_version_command_service_1.PackageVersionCommandService(context).createPackageVersion(options.packageKey, options.packageVersion, options.versionBumpOption, options.summaryOfChanges, options.nodeFilterKeys, options.json);
186
+ });
187
+ }
165
188
  batchImportPackages(context, command, options) {
166
189
  return __awaiter(this, void 0, void 0, function* () {
167
190
  if (options.gitProfile && !options.gitBranch) {
@@ -177,7 +200,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
177
200
  }
178
201
  listVariables(context, command, options) {
179
202
  return __awaiter(this, void 0, void 0, function* () {
180
- yield new config_command_service_1.ConfigCommandService(context).listVariables(options.json, options.keysByVersion, options.keysByVersionFile);
203
+ const hasStagingKeys = options.packageKeys.length > 0;
204
+ const hasVersioned = options.keysByVersion.length > 0 || options.keysByVersionFile !== "";
205
+ if (hasStagingKeys && hasVersioned) {
206
+ throw new Error("Please provide either --packageKeys or --keysByVersion/--keysByVersionFile, but not both.");
207
+ }
208
+ if (!hasStagingKeys && !hasVersioned) {
209
+ throw new Error("Please provide --packageKeys for staging, or --keysByVersion / --keysByVersionFile for versioned packages.");
210
+ }
211
+ yield new config_command_service_1.ConfigCommandService(context).listVariables(options.json, options.keysByVersion, options.keysByVersionFile, options.packageKeys);
181
212
  });
182
213
  }
183
214
  listAssignments(context, command, options) {
@@ -29,6 +29,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
29
29
  yield this.packageVersionService.findPackageVersion(packageKey, version, jsonResponse);
30
30
  });
31
31
  }
32
+ createPackageVersion(packageKey, version, versionBumpOption, summaryOfChanges, nodeFilterKeys, jsonResponse) {
33
+ return __awaiter(this, void 0, void 0, function* () {
34
+ yield this.packageVersionService.createPackageVersion(packageKey, version, versionBumpOption, summaryOfChanges, nodeFilterKeys, jsonResponse);
35
+ });
36
+ }
32
37
  }
33
38
  exports.PackageVersionCommandService = PackageVersionCommandService;
34
39
  });
@@ -40,6 +40,30 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
40
40
  }
41
41
  });
42
42
  }
43
+ createPackageVersion(packageKey, version, versionBumpOption, summaryOfChanges, nodeFilterKeys, jsonResponse) {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ const request = {
46
+ version: version,
47
+ versionBumpOption: versionBumpOption,
48
+ summaryOfChanges: summaryOfChanges,
49
+ };
50
+ if (nodeFilterKeys && nodeFilterKeys.length > 0) {
51
+ request.nodeFilter = {
52
+ filterType: "INCLUDE",
53
+ keys: nodeFilterKeys,
54
+ };
55
+ }
56
+ const created = yield this.packageVersionApi.createVersion(packageKey, request);
57
+ if (jsonResponse) {
58
+ const filename = (0, uuid_1.v4)() + ".json";
59
+ file_service_1.fileService.writeToFileWithGivenName(JSON.stringify(created, null, 2), filename);
60
+ logger_1.logger.info(file_service_1.FileService.fileDownloadedMessage + filename);
61
+ }
62
+ else {
63
+ this.printPackageVersionCreatedTransport(created);
64
+ }
65
+ });
66
+ }
43
67
  printPackageVersionTransport(packageVersionTransport) {
44
68
  logger_1.logger.info(`Package Key: ${packageVersionTransport.packageKey}`);
45
69
  logger_1.logger.info(`Version: ${packageVersionTransport.version}`);
@@ -50,6 +74,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
50
74
  logger_1.logger.info(`Deployed: ${packageVersionTransport.deployed}`);
51
75
  logger_1.logger.info(`Published By: ${packageVersionTransport.publishedBy}`);
52
76
  }
77
+ printPackageVersionCreatedTransport(transport) {
78
+ logger_1.logger.info(`Successfully created version ${transport.version} for package ${transport.packageKey}`);
79
+ logger_1.logger.info(`Version: ${transport.version}`);
80
+ logger_1.logger.info(`Package Key: ${transport.packageKey}`);
81
+ logger_1.logger.info(`Summary of Changes: ${transport.summaryOfChanges}`);
82
+ logger_1.logger.info(`Creation Date: ${new Date(transport.creationDate).toISOString()}`);
83
+ logger_1.logger.info(`Created By: ${transport.createdBy}`);
84
+ }
53
85
  }
54
86
  exports.PackageVersionService = PackageVersionService;
55
87
  });
@@ -13,7 +13,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
13
13
  if (v !== undefined) module.exports = v;
14
14
  }
15
15
  else if (typeof define === "function" && define.amd) {
16
- define(["require", "exports", "uuid", "../../core/utils/logger", "./studio.service", "../../core/utils/file-service", "./api/batch-import-export-api", "url", "./api/variable-assignment-candidates-api"], factory);
16
+ define(["require", "exports", "uuid", "../../core/utils/logger", "./studio.service", "../../core/utils/file-service", "./api/batch-import-export-api", "url", "./api/variable-assignment-candidates-api", "./api/staging-package-variables-api"], factory);
17
17
  }
18
18
  })(function (require, exports) {
19
19
  "use strict";
@@ -26,10 +26,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
26
26
  const batch_import_export_api_1 = require("./api/batch-import-export-api");
27
27
  const url_1 = require("url");
28
28
  const variable_assignment_candidates_api_1 = require("./api/variable-assignment-candidates-api");
29
+ const staging_package_variables_api_1 = require("./api/staging-package-variables-api");
29
30
  class VariableService {
30
31
  constructor(context) {
31
32
  this.batchImportExportApi = new batch_import_export_api_1.BatchImportExportApi(context);
32
33
  this.variableAssignmentCandidatesApi = new variable_assignment_candidates_api_1.VariableAssignmentCandidatesApi(context);
34
+ this.stagingPackageVariablesApi = new staging_package_variables_api_1.StagingPackageVariablesApi(context);
33
35
  this.studioService = new studio_service_1.StudioService(context);
34
36
  }
35
37
  listVariables(keysByVersion, keysByVersionFile) {
@@ -62,6 +64,25 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
62
64
  this.exportToJson(variableManifests);
63
65
  });
64
66
  }
67
+ listStagingVariables(packageKeys) {
68
+ return __awaiter(this, void 0, void 0, function* () {
69
+ const byPackage = yield this.fetchStagingVariablesByPackageKeys(packageKeys);
70
+ byPackage.forEach(entry => {
71
+ logger_1.logger.info(JSON.stringify(entry));
72
+ });
73
+ });
74
+ }
75
+ exportStagingVariables(packageKeys) {
76
+ return __awaiter(this, void 0, void 0, function* () {
77
+ const byPackage = yield this.fetchStagingVariablesByPackageKeys(packageKeys);
78
+ this.exportToJson(byPackage);
79
+ });
80
+ }
81
+ fetchStagingVariablesByPackageKeys(packageKeys) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ return yield this.stagingPackageVariablesApi.findAllByPackageKeys(packageKeys);
84
+ });
85
+ }
65
86
  getVersionedVariablesByKeyVersionPairs(keysByVersion, keysByVersionFile) {
66
87
  return __awaiter(this, void 0, void 0, function* () {
67
88
  const variablesExportRequest = yield this.buildKeyVersionPairs(keysByVersion, keysByVersionFile);
@@ -37,6 +37,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
37
37
  command.command("default <profile>")
38
38
  .description("Command to set a profile as default")
39
39
  .action(this.defaultProfile);
40
+ command.command("secure <profile>")
41
+ .description("Migrate a profile's secrets to secure system keychain storage")
42
+ .action(this.secureProfile);
40
43
  }
41
44
  defaultProfile(context, command) {
42
45
  return __awaiter(this, void 0, void 0, function* () {
@@ -55,6 +58,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
55
58
  yield new profile_command_service_1.ProfileCommandService().listProfiles();
56
59
  });
57
60
  }
61
+ secureProfile(context, command) {
62
+ return __awaiter(this, void 0, void 0, function* () {
63
+ const profile = command.args[0];
64
+ yield new profile_command_service_1.ProfileCommandService().secureProfile(profile);
65
+ });
66
+ }
58
67
  }
59
68
  return Module;
60
69
  });
@@ -55,7 +55,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
55
55
  }
56
56
  profile.authenticationType = yield profile_validator_1.ProfileValidator.validateProfile(profile);
57
57
  yield this.profileService.authorizeProfile(profile);
58
- this.profileService.storeProfile(profile);
58
+ yield this.profileService.storeProfile(profile);
59
59
  if (setAsDefault) {
60
60
  yield this.makeDefaultProfile(profile.name);
61
61
  }
@@ -89,6 +89,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
89
89
  logger_1.logger.info("Default profile: " + profile);
90
90
  });
91
91
  }
92
+ secureProfile(profileName) {
93
+ return __awaiter(this, void 0, void 0, function* () {
94
+ const profile = yield this.profileService.findProfile(profileName);
95
+ if (profile.secretsStoredSecurely) {
96
+ logger_1.logger.info(`Profile "${profileName}" is already using secure storage.`);
97
+ return;
98
+ }
99
+ yield this.profileService.storeProfile(profile);
100
+ const updatedProfile = yield this.profileService.findProfile(profileName);
101
+ if (updatedProfile.secretsStoredSecurely) {
102
+ logger_1.logger.info(`Profile "${profileName}" secrets have been migrated to secure storage.`);
103
+ }
104
+ else {
105
+ logger_1.logger.warn(`Failed to migrate profile "${profileName}" to secure storage. Secrets remain in plaintext.`);
106
+ }
107
+ });
108
+ }
92
109
  }
93
110
  exports.ProfileCommandService = ProfileCommandService;
94
111
  });
@@ -13,12 +13,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
13
13
  if (v !== undefined) module.exports = v;
14
14
  }
15
15
  else if (typeof define === "function" && define.amd) {
16
- define(["require", "exports", "./profile.interface", "./profile.validator", "path", "fs", "../utils/logger", "openid-client", "axios", "os"], factory);
16
+ define(["require", "exports", "./profile.interface", "./profile.validator", "path", "fs", "../utils/logger", "openid-client", "axios", "os", "./secret-storage.service"], factory);
17
17
  }
18
18
  })(function (require, exports) {
19
19
  "use strict";
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.profileService = exports.ProfileService = void 0;
21
+ exports.ProfileService = void 0;
22
22
  const profile_interface_1 = require("./profile.interface");
23
23
  const profile_validator_1 = require("./profile.validator");
24
24
  const path = require("path");
@@ -27,6 +27,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
27
27
  const openid_client_1 = require("openid-client");
28
28
  const axios_1 = require("axios");
29
29
  const os = require("os");
30
+ const secret_storage_service_1 = require("./secret-storage.service");
30
31
  const homedir = os.homedir();
31
32
  // use 5 seconds buffer to avoid rare cases when accessToken is just about to expire before the command is sent
32
33
  const expiryBuffer = 5000;
@@ -38,6 +39,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
38
39
  constructor() {
39
40
  this.profileContainerPath = path.resolve(homedir, ".celonis-content-cli-profiles");
40
41
  this.configContainer = path.resolve(this.profileContainerPath, "config.json");
42
+ this.secureSecretStorageService = new secret_storage_service_1.SecureSecretStorageService();
41
43
  }
42
44
  findProfile(profileName) {
43
45
  return __awaiter(this, void 0, void 0, function* () {
@@ -46,8 +48,28 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
46
48
  if (!this.checkIfMissingProfile(profileName)) {
47
49
  const file = fs.readFileSync(path.resolve(this.profileContainerPath, this.constructProfileFileName(profileName)), { encoding: "utf-8" });
48
50
  const profile = JSON.parse(file);
49
- this.refreshProfile(profile)
50
- .then(() => resolve(profile));
51
+ if (profile.secretsStoredSecurely) {
52
+ this.secureSecretStorageService.getSecrets(profileName)
53
+ .then(profileSecureSecrets => {
54
+ if (profileSecureSecrets) {
55
+ profile.apiToken = profileSecureSecrets.apiToken;
56
+ profile.refreshToken = profileSecureSecrets.refreshToken;
57
+ profile.clientSecret = profileSecureSecrets.clientSecret;
58
+ this.refreshProfile(profile)
59
+ .then(() => resolve(profile))
60
+ .catch(() => reject(`The profile ${profileName} couldn't be resolved.`));
61
+ }
62
+ else {
63
+ reject("Failed to read secrets from system keychain.");
64
+ }
65
+ })
66
+ .catch(() => reject(`The profile ${profileName} couldn't be resolved.`));
67
+ }
68
+ else {
69
+ this.refreshProfile(profile)
70
+ .then(() => resolve(profile))
71
+ .catch(() => reject(`The profile ${profileName} couldn't be resolved.`));
72
+ }
51
73
  }
52
74
  else if (process.env.TEAM_URL && process.env.API_TOKEN) {
53
75
  resolve(this.buildProfileFromEnvVariables());
@@ -92,11 +114,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
92
114
  }
93
115
  }
94
116
  storeProfile(profile) {
95
- this.createProfileContainerIfNotExists();
96
- const newProfileFileName = this.constructProfileFileName(profile.name);
97
- profile.team = this.getBaseTeamUrl(profile.team);
98
- fs.writeFileSync(path.resolve(this.profileContainerPath, newProfileFileName), JSON.stringify(profile), {
99
- encoding: "utf-8",
117
+ return __awaiter(this, void 0, void 0, function* () {
118
+ this.createProfileContainerIfNotExists();
119
+ const newProfileFileName = this.constructProfileFileName(profile.name);
120
+ profile.team = this.getBaseTeamUrl(profile.team);
121
+ const secretsStoredInKeychain = yield this.secureSecretStorageService.storeSecrets(profile);
122
+ const profileToStore = Object.assign({}, profile);
123
+ if (secretsStoredInKeychain) {
124
+ profileToStore.secretsStoredSecurely = true;
125
+ delete profileToStore.apiToken;
126
+ delete profileToStore.refreshToken;
127
+ delete profileToStore.clientSecret;
128
+ }
129
+ else {
130
+ profileToStore.secretsStoredSecurely = false;
131
+ }
132
+ fs.writeFileSync(path.resolve(this.profileContainerPath, newProfileFileName), JSON.stringify(profileToStore), {
133
+ encoding: "utf-8",
134
+ });
100
135
  });
101
136
  }
102
137
  buildProfileFromEnvVariables() {
@@ -263,7 +298,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
263
298
  logger_1.logger.error(new logger_1.FatalError("The profile cannot be refreshed. Please retry or recreate profile."));
264
299
  }
265
300
  }
266
- this.storeProfile(profile);
301
+ yield this.storeProfile(profile);
267
302
  });
268
303
  }
269
304
  getProfileEnvVariables() {
@@ -394,5 +429,4 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
394
429
  }
395
430
  }
396
431
  exports.ProfileService = ProfileService;
397
- exports.profileService = new ProfileService();
398
432
  });
@@ -0,0 +1,115 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ (function (factory) {
11
+ if (typeof module === "object" && typeof module.exports === "object") {
12
+ var v = factory(require, exports);
13
+ if (v !== undefined) module.exports = v;
14
+ }
15
+ else if (typeof define === "function" && define.amd) {
16
+ define(["require", "exports", "../utils/logger"], factory);
17
+ }
18
+ })(function (require, exports) {
19
+ "use strict";
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.SecureSecretStorageService = void 0;
22
+ const logger_1 = require("../utils/logger");
23
+ var PROFILE_SECRET_TYPE;
24
+ (function (PROFILE_SECRET_TYPE) {
25
+ PROFILE_SECRET_TYPE["API_TOKEN"] = "apiToken";
26
+ PROFILE_SECRET_TYPE["CLIENT_SECRET"] = "clientSecret";
27
+ PROFILE_SECRET_TYPE["REFRESH_TOKEN"] = "refreshToken";
28
+ })(PROFILE_SECRET_TYPE || (PROFILE_SECRET_TYPE = {}));
29
+ const CONTENT_CLI_SERVICE_NAME = "celonis-content-cli";
30
+ // Lazy load keytar to handle cases where native dependencies are not available
31
+ let keytar = null;
32
+ let keytarLoadError = null;
33
+ function getKeytar() {
34
+ if (keytar !== null) {
35
+ return keytar;
36
+ }
37
+ if (keytarLoadError !== null) {
38
+ return null;
39
+ }
40
+ try {
41
+ keytar = require("@github/keytar");
42
+ return keytar;
43
+ }
44
+ catch (error) {
45
+ keytarLoadError = error;
46
+ return null;
47
+ }
48
+ }
49
+ class SecureSecretStorageService {
50
+ storeSecrets(profile) {
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ let secretsStoredInKeychain = true;
53
+ const secretEntries = [
54
+ { type: PROFILE_SECRET_TYPE.API_TOKEN, value: profile.apiToken },
55
+ { type: PROFILE_SECRET_TYPE.CLIENT_SECRET, value: profile.clientSecret },
56
+ { type: PROFILE_SECRET_TYPE.REFRESH_TOKEN, value: profile.refreshToken },
57
+ ];
58
+ for (const secretEntry of secretEntries) {
59
+ if (secretEntry.value) {
60
+ const stored = yield this.storeSecret(this.getSecretServiceName(profile.name), secretEntry.type, secretEntry.value);
61
+ if (!stored) {
62
+ secretsStoredInKeychain = false;
63
+ }
64
+ }
65
+ }
66
+ if (!secretsStoredInKeychain) {
67
+ logger_1.logger.warn("⚠️ Failed to store secrets securely. They will be stored in plain text file.");
68
+ return false;
69
+ }
70
+ return true;
71
+ });
72
+ }
73
+ getSecrets(profileName) {
74
+ return __awaiter(this, void 0, void 0, function* () {
75
+ const keytarModule = getKeytar();
76
+ if (!keytarModule) {
77
+ return undefined;
78
+ }
79
+ try {
80
+ const secrets = yield keytarModule.findCredentials(this.getSecretServiceName(profileName));
81
+ if (!secrets.length) {
82
+ return undefined;
83
+ }
84
+ const profileSecrets = {};
85
+ for (const secret of secrets) {
86
+ profileSecrets[secret.account] = secret.password;
87
+ }
88
+ return profileSecrets;
89
+ }
90
+ catch (err) {
91
+ return undefined;
92
+ }
93
+ });
94
+ }
95
+ getSecretServiceName(profileName) {
96
+ return `${CONTENT_CLI_SERVICE_NAME}:${profileName}`;
97
+ }
98
+ storeSecret(service, account, secret) {
99
+ return __awaiter(this, void 0, void 0, function* () {
100
+ const keytarModule = getKeytar();
101
+ if (!keytarModule) {
102
+ return false;
103
+ }
104
+ try {
105
+ yield keytarModule.setPassword(service, account, secret);
106
+ return true;
107
+ }
108
+ catch (err) {
109
+ return false;
110
+ }
111
+ });
112
+ }
113
+ }
114
+ exports.SecureSecretStorageService = SecureSecretStorageService;
115
+ });
@@ -1,7 +1,8 @@
1
1
  import { Context } from "../../../core/command/cli-context";
2
- import { PackageVersionTransport } from "../interfaces/package-version.interfaces";
2
+ import { PackageVersionCreatedTransport, PackageVersionTransport, SavePackageVersionTransport } from "../interfaces/package-version.interfaces";
3
3
  export declare class PackageVersionApi {
4
4
  private httpClient;
5
5
  constructor(context: Context);
6
6
  findOne(packageKey: string, version: string): Promise<PackageVersionTransport>;
7
+ createVersion(packageKey: string, request: SavePackageVersionTransport): Promise<PackageVersionCreatedTransport>;
7
8
  }
@@ -0,0 +1,7 @@
1
+ import { Context } from "../../../core/command/cli-context";
2
+ import { StagingVariableManifestTransport } from "../interfaces/package-export.interfaces";
3
+ export declare class StagingPackageVariablesApi {
4
+ private readonly httpClient;
5
+ constructor(context: Context);
6
+ findAllByPackageKeys(packageKeys: string[]): Promise<StagingVariableManifestTransport[]>;
7
+ }
@@ -5,7 +5,7 @@ export declare class ConfigCommandService {
5
5
  private diffService;
6
6
  constructor(context: Context);
7
7
  listPackages(jsonResponse: boolean, flavors: string[], withDependencies: boolean, packageKeys: string[], keysByVersion: string[], variableValue: string, variableType: string): Promise<void>;
8
- listVariables(jsonResponse: boolean, keysByVersion: string[], keysByVersionFile: string): Promise<void>;
8
+ listVariables(jsonResponse: boolean, keysByVersion: string[], keysByVersionFile: string, packageKeys: string[]): Promise<void>;
9
9
  batchExportPackages(packageKeys: string[], packageKeysByVersion: string[], withDependencies: boolean, gitBranch: string, unzip: boolean): Promise<void>;
10
10
  batchExportPackagesMetadata(packageKeys: string[], jsonResponse: boolean): Promise<void>;
11
11
  batchImportPackages(file: string, directory: string, overwrite: boolean, gitBranch: string): Promise<void>;
@@ -39,6 +39,10 @@ export interface VariableManifestTransport {
39
39
  version: string;
40
40
  variables?: VariableExportTransport[];
41
41
  }
42
+ export interface StagingVariableManifestTransport {
43
+ packageKey: string;
44
+ variables?: VariableExportTransport[];
45
+ }
42
46
  export interface PackageKeyAndVersionPair {
43
47
  packageKey: string;
44
48
  version: string;
@@ -8,3 +8,24 @@ export interface PackageVersionTransport {
8
8
  deployed: boolean;
9
9
  publishedBy: string;
10
10
  }
11
+ export interface SavePackageVersionTransport {
12
+ version?: string;
13
+ versionBumpOption?: VersionBumpOption;
14
+ summaryOfChanges?: string;
15
+ nodeFilter?: NodeFilterTransport;
16
+ }
17
+ export declare enum VersionBumpOption {
18
+ NONE = "NONE",
19
+ PATCH = "PATCH"
20
+ }
21
+ export interface NodeFilterTransport {
22
+ filterType: "INCLUDE";
23
+ keys: string[];
24
+ }
25
+ export interface PackageVersionCreatedTransport {
26
+ packageKey: string;
27
+ version: string;
28
+ summaryOfChanges: string;
29
+ creationDate: string;
30
+ createdBy: string;
31
+ }
@@ -9,6 +9,7 @@ declare class Module extends IModule {
9
9
  private batchExportPackages;
10
10
  private batchExportPackagesMetadata;
11
11
  private getPackageVersion;
12
+ private createPackageVersion;
12
13
  private batchImportPackages;
13
14
  private diffPackages;
14
15
  private listVariables;
@@ -3,4 +3,5 @@ export declare class PackageVersionCommandService {
3
3
  private packageVersionService;
4
4
  constructor(context: Context);
5
5
  getPackageVersion(packageKey: string, version: string, jsonResponse: boolean): Promise<void>;
6
+ createPackageVersion(packageKey: string, version: string | undefined, versionBumpOption: string, summaryOfChanges: string | undefined, nodeFilterKeys: string[] | undefined, jsonResponse: boolean): Promise<void>;
6
7
  }
@@ -3,5 +3,7 @@ export declare class PackageVersionService {
3
3
  private packageVersionApi;
4
4
  constructor(context: Context);
5
5
  findPackageVersion(packageKey: string, version: string, jsonResponse: boolean): Promise<void>;
6
+ createPackageVersion(packageKey: string, version: string | undefined, versionBumpOption: string, summaryOfChanges: string | undefined, nodeFilterKeys: string[] | undefined, jsonResponse: boolean): Promise<void>;
6
7
  private printPackageVersionTransport;
8
+ private printPackageVersionCreatedTransport;
7
9
  }
@@ -2,12 +2,16 @@ import { Context } from "../../core/command/cli-context";
2
2
  export declare class VariableService {
3
3
  private batchImportExportApi;
4
4
  private variableAssignmentCandidatesApi;
5
+ private readonly stagingPackageVariablesApi;
5
6
  private studioService;
6
7
  constructor(context: Context);
7
8
  listVariables(keysByVersion: string[], keysByVersionFile: string): Promise<void>;
8
9
  listCandidateAssignments(type: string, params: string): Promise<void>;
9
10
  findAndExportCandidateAssignments(type: string, params: string): Promise<void>;
10
11
  exportVariables(keysByVersion: string[], keysByVersionFile: string): Promise<void>;
12
+ listStagingVariables(packageKeys: string[]): Promise<void>;
13
+ exportStagingVariables(packageKeys: string[]): Promise<void>;
14
+ private fetchStagingVariablesByPackageKeys;
11
15
  private getVersionedVariablesByKeyVersionPairs;
12
16
  private buildKeyVersionPairs;
13
17
  private buildKeyAndVersionPairsFromArrayInput;
@@ -8,5 +8,6 @@ declare class Module extends IModule {
8
8
  private defaultProfile;
9
9
  private createProfile;
10
10
  private listProfiles;
11
+ private secureProfile;
11
12
  }
12
13
  export = Module;
@@ -3,4 +3,5 @@ export declare class ProfileCommandService {
3
3
  createProfile(setAsDefault: boolean): Promise<void>;
4
4
  listProfiles(): Promise<void>;
5
5
  makeDefaultProfile(profile: string): Promise<void>;
6
+ secureProfile(profileName: string): Promise<void>;
6
7
  }
@@ -1,14 +1,17 @@
1
- export interface Profile {
1
+ export interface ProfileSecrets {
2
+ apiToken: string;
3
+ clientSecret?: string;
4
+ refreshToken?: string;
5
+ secretsStoredSecurely?: boolean;
6
+ }
7
+ export interface Profile extends ProfileSecrets {
2
8
  name: string;
3
9
  team: string;
4
10
  type: ProfileType;
5
- apiToken: string;
6
11
  authenticationType: AuthenticationType;
7
12
  clientId?: string;
8
- clientSecret?: string;
9
13
  scopes?: string[];
10
14
  clientAuthenticationMethod?: ClientAuthenticationMethod;
11
- refreshToken?: string;
12
15
  expiresAt?: number;
13
16
  }
14
17
  export type AuthenticationType = "Bearer" | "AppKey";
@@ -5,10 +5,11 @@ export interface Config {
5
5
  export declare class ProfileService {
6
6
  private profileContainerPath;
7
7
  private configContainer;
8
+ private secureSecretStorageService;
8
9
  findProfile(profileName: string): Promise<Profile>;
9
10
  makeDefaultProfile(profileName: string): Promise<Profile>;
10
11
  getDefaultProfile(): string;
11
- storeProfile(profile: Profile): void;
12
+ storeProfile(profile: Profile): Promise<void>;
12
13
  private buildProfileFromEnvVariables;
13
14
  private storeConfig;
14
15
  private createProfileContainerIfNotExists;
@@ -40,4 +41,3 @@ export declare class ProfileService {
40
41
  */
41
42
  private extractScopesFromTokenSet;
42
43
  }
43
- export declare const profileService: ProfileService;
@@ -0,0 +1,7 @@
1
+ import { Profile, ProfileSecrets } from "./profile.interface";
2
+ export declare class SecureSecretStorageService {
3
+ storeSecrets(profile: Profile): Promise<boolean>;
4
+ getSecrets(profileName: string): Promise<ProfileSecrets | undefined>;
5
+ private getSecretServiceName;
6
+ private storeSecret;
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celonis/content-cli",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "CLI Tool to help manage content in Celonis Platform",
5
5
  "main": "content-cli.js",
6
6
  "bin": {
@@ -26,11 +26,12 @@
26
26
  "form-data": "4.0.4",
27
27
  "openid-client": "5.6.1",
28
28
  "hpagent": "1.2.0",
29
+ "@github/keytar": "7.10.6",
29
30
  "semver": "7.6.3",
30
- "simple-git": "3.28.0",
31
+ "simple-git": "3.32.3",
31
32
  "valid-url": "1.0.9",
32
33
  "winston": "3.17.0",
33
- "yaml": "2.7.0"
34
+ "yaml": "2.8.3"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/adm-zip": "0.5.7",