@celonis/content-cli 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -18
- package/commands/configuration-management/api/package-version-api.js +9 -0
- package/commands/configuration-management/batch-import-export.service.js +10 -2
- package/commands/configuration-management/interfaces/package-version.interfaces.js +6 -0
- package/commands/configuration-management/module.js +22 -0
- package/commands/configuration-management/package-version-command.service.js +5 -0
- package/commands/configuration-management/package-version.service.js +32 -0
- package/commands/profile/module.js +9 -0
- package/commands/profile/profile-command.service.js +18 -1
- package/core/profile/profile.service.js +45 -11
- package/core/profile/secret-storage.service.js +115 -0
- package/lib/commands/configuration-management/api/package-version-api.d.ts +2 -1
- package/lib/commands/configuration-management/batch-import-export.service.d.ts +1 -0
- package/lib/commands/configuration-management/interfaces/package-version.interfaces.d.ts +21 -0
- package/lib/commands/configuration-management/module.d.ts +1 -0
- package/lib/commands/configuration-management/package-version-command.service.d.ts +1 -0
- package/lib/commands/configuration-management/package-version.service.d.ts +2 -0
- package/lib/commands/profile/module.d.ts +1 -0
- package/lib/commands/profile/profile-command.service.d.ts +1 -0
- package/lib/core/profile/profile.interface.d.ts +7 -4
- package/lib/core/profile/profile.service.d.ts +2 -2
- package/lib/core/profile/secret-storage.service.d.ts +7 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -9,10 +9,11 @@ teams and execute commands in a profile's context.
|
|
|
9
9
|
|
|
10
10
|
1. [Getting Started](#getting-started)
|
|
11
11
|
2. [About the Project](#about-the-project)
|
|
12
|
-
3. [Building the Project](#building-the-project)
|
|
13
|
-
4. [
|
|
14
|
-
5. [
|
|
15
|
-
6. [
|
|
12
|
+
3. [Building the Project](#building-and-using-the-project-locally)
|
|
13
|
+
4. [Troubleshooting](#troubleshooting)
|
|
14
|
+
5. [Release Process](#release-process)
|
|
15
|
+
6. [Contributing](#contributing)
|
|
16
|
+
7. [License](#license)
|
|
16
17
|
|
|
17
18
|
## Getting Started
|
|
18
19
|
|
|
@@ -71,8 +72,8 @@ Example, if you want to push a package from zipped file "interesting-package.zip
|
|
|
71
72
|
content-cli push package -p my-dev-profile --spaceKey my-cool-space -f interesting-package.zip
|
|
72
73
|
```
|
|
73
74
|
|
|
74
|
-
A
|
|
75
|
-
[documentation](https://github.com/celonis/content-cli/
|
|
75
|
+
A comprehensive list of Content CLI capabilities can be found in the
|
|
76
|
+
[documentation](https://github.com/celonis/content-cli/tree/main/docs).
|
|
76
77
|
|
|
77
78
|
You can still explore the full capabilities of Content CLI and the list of options for the different commands
|
|
78
79
|
by using the `-h` option in your command.
|
|
@@ -85,16 +86,65 @@ content-cli pull package -h
|
|
|
85
86
|
## Building and Using the Project locally
|
|
86
87
|
|
|
87
88
|
This tool is tightly connected with the Celonis Platform and all capabilities require to have access to a Celonis Platform Team.
|
|
88
|
-
After cloning the project, the next step is to install the project dependencies. We use `yarn` as our package manager,
|
|
89
|
-
so running `yarn install` on the project root folder should install all the necessary dependencies. After installing
|
|
90
|
-
the project dependencies, you can run `yarn build` to build the project artifact. To use the built artifact, you can
|
|
91
|
-
run `node content-cli.js` in the generated `dist` folder.
|
|
92
89
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
|
|
90
|
+
### Prerequisites
|
|
91
|
+
|
|
92
|
+
- **Node.js** `>=18.20.5` (LTS recommended). Check with `node -v`.
|
|
93
|
+
- **yarn** package manager. Install with `npm install -g yarn` if not already available.
|
|
94
|
+
|
|
95
|
+
### Build steps
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
git clone https://github.com/celonis/content-cli.git
|
|
99
|
+
cd content-cli
|
|
100
|
+
yarn install
|
|
101
|
+
yarn build
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The build compiles TypeScript into the `dist/` folder and copies `package.json` there.
|
|
105
|
+
|
|
106
|
+
### Running the built CLI
|
|
107
|
+
|
|
108
|
+
You can run the built artifact directly from the `dist` folder:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
node dist/content-cli.js -h
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Installing the local build globally
|
|
115
|
+
|
|
116
|
+
To use your local build as the global `content-cli` command:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
cd dist
|
|
120
|
+
npm link
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
This creates a symbolic link in the global `node_modules` directory, allowing you to run `content-cli` from anywhere on your machine. To unlink later, run `npm unlink -g @celonis/content-cli`.
|
|
124
|
+
|
|
125
|
+
## Troubleshooting
|
|
126
|
+
|
|
127
|
+
### `Error: Cannot find module '...'`
|
|
128
|
+
|
|
129
|
+
If you see a `Cannot find module` error when running `content-cli`, dependencies may not be installed correctly:
|
|
130
|
+
|
|
131
|
+
- **Global install:** Try updating to the latest version: `npm install -g @celonis/content-cli@latest`.
|
|
132
|
+
- **Local development:** Run `yarn install` from the project root before building.
|
|
133
|
+
|
|
134
|
+
### `content-cli: command not found`
|
|
135
|
+
|
|
136
|
+
- Ensure Node.js is installed and on your `PATH` (`node -v`).
|
|
137
|
+
- If installed via `npm install -g`, check that npm's global `bin` directory is in your `PATH`. You can find it with `npm bin -g`.
|
|
138
|
+
- If using `npm link`, make sure you ran it from the `dist/` directory (not the project root).
|
|
139
|
+
|
|
140
|
+
### Node.js version errors
|
|
141
|
+
|
|
142
|
+
Content CLI requires Node.js `>=18.20.5`. If you see compatibility errors, upgrade your Node.js installation. We recommend using [nvm](https://github.com/nvm-sh/nvm) to manage Node.js versions:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
nvm install --lts
|
|
146
|
+
nvm use --lts
|
|
147
|
+
```
|
|
98
148
|
|
|
99
149
|
## Release Process
|
|
100
150
|
|
|
@@ -109,14 +159,14 @@ npm i -g @celonis/content-cli
|
|
|
109
159
|
## Contributing
|
|
110
160
|
|
|
111
161
|
We encourage public contributions! Please check
|
|
112
|
-
[CONTRIBUTING.md](https://github.com/celonis/content-cli/blob/
|
|
162
|
+
[CONTRIBUTING.md](https://github.com/celonis/content-cli/blob/main/CONTRIBUTING.md) for details on our
|
|
113
163
|
code of conduct.
|
|
114
164
|
|
|
115
|
-
For details on command development, refer to the [How to Add a Command](https://github.com/celonis/content-cli/blob/
|
|
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.
|
|
116
166
|
|
|
117
167
|
## License
|
|
118
168
|
|
|
119
169
|
Copyright (c) 2021 Celonis SE
|
|
120
170
|
|
|
121
171
|
This project is licensed under the MIT License -
|
|
122
|
-
see [LICENSE](https://github.com/celonis/content-cli/blob/
|
|
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
|
});
|
|
@@ -141,7 +141,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
141
141
|
const variablesManifests = this.parseEntryData(configs, batch_export_import_constants_1.BatchExportImportConstants.VARIABLES_FILE_NAME);
|
|
142
142
|
configs = yield this.studioService.mapSpaces(configs, studioManifests);
|
|
143
143
|
const existingStudioPackages = yield this.studioPackageApi.findAllPackages();
|
|
144
|
-
const formData = this.buildBodyForImport(configs, variablesManifests);
|
|
144
|
+
const formData = this.buildBodyForImport(configs, sourceToBeImported, variablesManifests);
|
|
145
145
|
const postPackageImportData = yield this.batchImportExportApi.importPackages(formData, overwrite);
|
|
146
146
|
yield this.studioService.processImportedPackages(configs, existingStudioPackages, studioManifests);
|
|
147
147
|
if (gitBranch) {
|
|
@@ -196,8 +196,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
196
196
|
file_service_1.fileService.writeToFileWithGivenName(JSON.stringify(packagesMetadata), filename);
|
|
197
197
|
logger_1.logger.info(file_service_1.FileService.fileDownloadedMessage + filename);
|
|
198
198
|
}
|
|
199
|
-
buildBodyForImport(configs, variablesManifests) {
|
|
199
|
+
buildBodyForImport(configs, sourcePath, variablesManifests) {
|
|
200
200
|
const formData = new FormData();
|
|
201
|
+
this.assertUncompressedSizeWithinLimit(configs, sourcePath);
|
|
201
202
|
const readableStream = this.getReadableStream(configs);
|
|
202
203
|
formData.append("file", readableStream, { filename: "configs.zip" });
|
|
203
204
|
if (variablesManifests) {
|
|
@@ -207,6 +208,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
207
208
|
}
|
|
208
209
|
return formData;
|
|
209
210
|
}
|
|
211
|
+
assertUncompressedSizeWithinLimit(configs, sourcePath) {
|
|
212
|
+
const MAX_ZIP_SIZE = 4 * 1024 * 1024 * 1024;
|
|
213
|
+
const totalUncompressedBytes = configs.getEntries().reduce((sum, entry) => sum + entry.header.size, 0);
|
|
214
|
+
if (totalUncompressedBytes > MAX_ZIP_SIZE) {
|
|
215
|
+
throw new Error(`Failed to handle zip file "${sourcePath}": uncompressed size ${(totalUncompressedBytes / (Math.pow(1024, 3))).toFixed(2)} GB exceeds the 4 GB limit.`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
210
218
|
getReadableStream(configs) {
|
|
211
219
|
return new stream_1.Readable({
|
|
212
220
|
read() {
|
|
@@ -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,6 +79,15 @@ 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")
|
|
@@ -162,6 +171,19 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
162
171
|
yield new package_version_command_service_1.PackageVersionCommandService(context).getPackageVersion(options.packageKey, options.packageVersion, options.json);
|
|
163
172
|
});
|
|
164
173
|
}
|
|
174
|
+
createPackageVersion(context, command, options) {
|
|
175
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
const hasExplicitVersion = !!options.packageVersion;
|
|
177
|
+
const hasVersionBump = options.versionBumpOption && options.versionBumpOption !== "NONE";
|
|
178
|
+
if (hasExplicitVersion && hasVersionBump) {
|
|
179
|
+
throw new Error("Please provide either --packageVersion or --versionBumpOption, but not both.");
|
|
180
|
+
}
|
|
181
|
+
if (!hasExplicitVersion && !hasVersionBump) {
|
|
182
|
+
throw new Error("Please provide either --packageVersion or --versionBumpOption PATCH.");
|
|
183
|
+
}
|
|
184
|
+
yield new package_version_command_service_1.PackageVersionCommandService(context).createPackageVersion(options.packageKey, options.packageVersion, options.versionBumpOption, options.summaryOfChanges, options.nodeFilterKeys, options.json);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
165
187
|
batchImportPackages(context, command, options) {
|
|
166
188
|
return __awaiter(this, void 0, void 0, function* () {
|
|
167
189
|
if (options.gitProfile && !options.gitBranch) {
|
|
@@ -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
|
});
|
|
@@ -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.
|
|
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
|
-
|
|
50
|
-
.
|
|
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
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
}
|
|
@@ -18,6 +18,7 @@ export declare class BatchImportExportService {
|
|
|
18
18
|
private getVersionedVariablesForPackagesWithKeys;
|
|
19
19
|
private exportListOfPackagesMetadata;
|
|
20
20
|
private buildBodyForImport;
|
|
21
|
+
private assertUncompressedSizeWithinLimit;
|
|
21
22
|
private getReadableStream;
|
|
22
23
|
private parseEntryData;
|
|
23
24
|
private downloadZip;
|
|
@@ -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
|
+
}
|
|
@@ -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
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
export interface
|
|
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.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "CLI Tool to help manage content in Celonis Platform",
|
|
5
5
|
"main": "content-cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -19,14 +19,16 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"adm-zip": "0.5.14",
|
|
22
|
+
"chalk": "4.1.2",
|
|
22
23
|
"uuid": "9.0.1",
|
|
23
24
|
"axios": "1.13.5",
|
|
24
25
|
"commander": "13.1.0",
|
|
25
26
|
"form-data": "4.0.4",
|
|
26
27
|
"openid-client": "5.6.1",
|
|
27
28
|
"hpagent": "1.2.0",
|
|
29
|
+
"@github/keytar": "7.10.6",
|
|
28
30
|
"semver": "7.6.3",
|
|
29
|
-
"simple-git": "3.
|
|
31
|
+
"simple-git": "3.32.3",
|
|
30
32
|
"valid-url": "1.0.9",
|
|
31
33
|
"winston": "3.17.0",
|
|
32
34
|
"yaml": "2.7.0"
|