@coalescesoftware/coa 1.0.102 → 1.0.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/CLIProfile.js CHANGED
@@ -55,6 +55,7 @@ exports.GetDefaultLocationForCoaConfigFile = GetDefaultLocationForCoaConfigFile;
55
55
  exports.ICLIProfileExample = {
56
56
  profile: "Profile To Use",
57
57
  environmentID: "Environment ID",
58
+ parameters: "Parameters",
58
59
  snowflakeAccount: "Snowflake Account To Use",
59
60
  snowflakeAuthType: "Snowflake Auth Type (Basic, KeyPair)",
60
61
  snowflakeKeyPairPath: "Snowflake Key Pair Path",
@@ -68,21 +69,29 @@ exports.ICLIProfileExample = {
68
69
  exclude: "Coalesce Node Selector"
69
70
  };
70
71
  const ReadCLIProfiles = (filePath) => {
71
- return fs.promises.readFile(filePath, 'utf-8').then((file) => {
72
+ let filePathToUse;
73
+ if (!filePath) {
74
+ filePathToUse = (0, exports.GetDefaultLocationForCoaConfigFile)();
75
+ }
76
+ else {
77
+ filePathToUse = filePath;
78
+ }
79
+ return fs.promises.readFile(filePathToUse, 'utf-8')
80
+ .then((file) => {
72
81
  return ini.parse(file);
73
- }).catch((err) => {
82
+ })
83
+ .catch((err) => {
74
84
  CLILogger.error("unable to read cli profile file filePath:", filePath, err);
75
- throw new Error("unable to read cli profile");
85
+ if (!filePath) //if no filepath was specified, silently proceed
86
+ return {};
87
+ else //unable to proceed couldnt read file
88
+ throw new Error(`unable to read cli profile:${filePathToUse}`);
76
89
  });
77
90
  };
78
91
  const GetFinalCLIProfile = (commandLineOverrides, configFileLocation) => {
79
92
  let profileToUseOverride = commandLineOverrides.profile ? commandLineOverrides.profile : null;
80
- return ReadCLIProfiles(configFileLocation).then((cliProfiles) => {
81
- return cliProfiles;
82
- }).catch((err) => {
83
- //unable to read cli profiles
84
- return {};
85
- }).then((cliProfiles) => {
93
+ return ReadCLIProfiles(configFileLocation)
94
+ .then((cliProfiles) => {
86
95
  const defaultCLIProfile = cliProfiles.default;
87
96
  let finalCLIProfile = {};
88
97
  //if theres a default cli profile, start with that
@@ -124,7 +133,8 @@ const GetCLIConfig = (commandLineOverrides, configFileLocation) => {
124
133
  snowflakeRole: cliProfile.snowflakeRole,
125
134
  snowflakeUsername: cliProfile.snowflakeUsername,
126
135
  snowflakeWarehouse: cliProfile.snowflakeWarehouse
127
- }
136
+ },
137
+ runtimeParameters: cliProfile.parameters,
128
138
  };
129
139
  Shared.Common.CleanupUndefinedValuesFromObject(cliConfig);
130
140
  return cliConfig;
package/bin/CommonCLI.js CHANGED
@@ -19,11 +19,13 @@ var __importStar = (this && this.__importStar) || function (mod) {
19
19
  return result;
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.GetUserConnectionForCLI = exports.CleanupCLIJob = void 0;
22
+ exports.FinishWithOutputFile = exports.GetUserConnectionForCLI = exports.CleanupCLIJob = void 0;
23
23
  const Shared = __importStar(require("@coalescesoftware/shared"));
24
24
  const fs = __importStar(require("fs"));
25
25
  const crypto = __importStar(require("crypto"));
26
+ const RunOutput = __importStar(require("./RunOutput"));
26
27
  const RunnerBackendLogger = Shared.Logging.GetLogger(Shared.Logging.LoggingArea.RunnerBackend);
28
+ const LogCLIInternal = Shared.Logging.GetLogger(Shared.Logging.LoggingArea.CLI_INTERNAL);
27
29
  const CleanupCLIJob = (runCompletion, firebase, logContext, action) => {
28
30
  const cleanupPromise = new Promise((resolve, reject) => {
29
31
  let promiseError = null;
@@ -74,21 +76,20 @@ const GetKeyPairKey = (keyPairPath) => {
74
76
  };
75
77
  const GetUserConnectionForCLI = (userID, runInfo) => {
76
78
  return new Promise((resolve, reject) => {
77
- var _a, _b, _c, _d, _e, _f, _g;
79
+ var _a, _b, _c, _d, _e, _f;
78
80
  const output = {
79
81
  connectionDetails: {
80
82
  userID,
81
83
  user: (_a = runInfo.userCredentials) === null || _a === void 0 ? void 0 : _a.snowflakeUsername,
82
84
  role: (_b = runInfo.userCredentials) === null || _b === void 0 ? void 0 : _b.snowflakeRole,
83
85
  warehouse: (_c = runInfo.userCredentials) === null || _c === void 0 ? void 0 : _c.snowflakeWarehouse,
84
- authenticator: (_d = runInfo.userCredentials) === null || _d === void 0 ? void 0 : _d.snowflakeAuthType
85
86
  },
86
- connectionType: (_e = runInfo.userCredentials) === null || _e === void 0 ? void 0 : _e.snowflakeAuthType
87
+ connectionType: (_d = runInfo.userCredentials) === null || _d === void 0 ? void 0 : _d.snowflakeAuthType
87
88
  };
88
- if (!((_f = runInfo.userCredentials) === null || _f === void 0 ? void 0 : _f.snowflakeAuthType)) {
89
+ if (!((_e = runInfo.userCredentials) === null || _e === void 0 ? void 0 : _e.snowflakeAuthType)) {
89
90
  reject(new Error("ERROR (GetUserConnectionForCLI): no auth type provided"));
90
91
  }
91
- else if (((_g = runInfo.userCredentials) === null || _g === void 0 ? void 0 : _g.snowflakeAuthType) === Shared.ConnectionOperations.EUserConnectionTypes.keyPair) {
92
+ else if (((_f = runInfo.userCredentials) === null || _f === void 0 ? void 0 : _f.snowflakeAuthType) === Shared.ConnectionOperations.EUserConnectionTypes.keyPair) {
92
93
  GetKeyPairKey(Shared.Common.getValueSafe(runInfo, ["userCredentials", "snowflakeKeyPairPath"], ""))
93
94
  .then((keyPair) => {
94
95
  output.connectionDetails.keyPair = keyPair;
@@ -104,4 +105,14 @@ const GetUserConnectionForCLI = (userID, runInfo) => {
104
105
  });
105
106
  };
106
107
  exports.GetUserConnectionForCLI = GetUserConnectionForCLI;
108
+ const FinishWithOutputFile = (logContextToUse, outputFilePath, runCounter, token) => {
109
+ if (!outputFilePath) {
110
+ return Promise.resolve();
111
+ }
112
+ else {
113
+ LogCLIInternal.infoContext(logContextToUse, "saving run results to file", outputFilePath);
114
+ return RunOutput.SaveRunOutputToFile(outputFilePath, runCounter.toString(), token);
115
+ }
116
+ };
117
+ exports.FinishWithOutputFile = FinishWithOutputFile;
107
118
  //# sourceMappingURL=CommonCLI.js.map
package/bin/Deploy.js CHANGED
@@ -50,7 +50,7 @@ const DeployWithCLI = (plan, config, token) => {
50
50
  logContext = Shared.Logging.CreateLogContext(teamID, environmentID, userID);
51
51
  const connectionCache = new Shared.Snowflake.ConnectionStorageClass();
52
52
  RunSQL = Shared.SQLExecutorCreators.CreateRunSQLWithoutScheduler(teamDetails, connectionCache);
53
- runInfo = Shared.DeployOperations.CreateDeployRequestObject(plan.plan, plan.environmentState, teamInfo, plan.gitInfo, plan.targetEnvironment, "");
53
+ runInfo = Shared.DeployOperations.CreateDeployRequestObject(plan.plan, plan.environmentState, teamInfo, plan.gitInfo, plan.targetEnvironment, plan.runtimeParameters);
54
54
  runInfo.userCredentials = config.userCredentials;
55
55
  return CommonCLI.GetUserConnectionForCLI(userID, runInfo);
56
56
  })
package/bin/Package.js ADDED
@@ -0,0 +1,731 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ var __importDefault = (this && this.__importDefault) || function (mod) {
22
+ return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.GetPackageSupportedEntityTypes = exports.RemoveNamespacedWorkspaceData_Testing = exports.NamespaceWorkspaceData_Testing = exports.UninstallPackage = exports.InstallPackage = exports.PackageProvider = exports.GetPackageListMessageCLI = exports.InitializePackageContext = exports.localInstallKeyword = void 0;
26
+ /* eslint-disable multiline-comment-style */
27
+ const Shared = __importStar(require("@coalescesoftware/shared"));
28
+ const fs_1 = __importDefault(require("fs"));
29
+ const os = require("os");
30
+ /**
31
+ * coa package uses the same config file that should be setup to leverage coa.
32
+ *
33
+ * project - the user's ws data
34
+ * package - the targetted repo being applied to the project
35
+ * dependency - any package installed to a project/another package
36
+ *
37
+ * coa add works at a high level in these steps:
38
+ * 1 - collects ws data from a targetted repo (local or remote)
39
+ * 2 - namespaces that ws data w/package ID
40
+ * 3 - adds to firestore packages collection the info for the package being installed (dependency information) w/status: 'adding'
41
+ * 4 - updates project ws data on firestore with namespaced ws data from package
42
+ * 5 - updates firestore package dependency information w/status: 'added' and a timestamp
43
+ * 6 - in case of error, will clean up any added package information including dependency info from project ws data on FS
44
+ *
45
+ * coa remove works similarly
46
+ */
47
+ exports.localInstallKeyword = "file:";
48
+ const PackagesLogger = Shared.Logging.GetLogger(Shared.Logging.LoggingArea.CLI);
49
+ var EPackageStatus;
50
+ (function (EPackageStatus) {
51
+ EPackageStatus["adding"] = "adding";
52
+ EPackageStatus["added"] = "added";
53
+ EPackageStatus["removing"] = "removing";
54
+ EPackageStatus["error"] = "error";
55
+ })(EPackageStatus || (EPackageStatus = {}));
56
+ var EVersionType;
57
+ (function (EVersionType) {
58
+ EVersionType["file"] = "file";
59
+ EVersionType["url"] = "url";
60
+ })(EVersionType || (EVersionType = {}));
61
+ ////////////////////////////////////////////////////////////////////
62
+ //////////////////// reusable namespacing helper functions
63
+ ////////////////////////////////////////////////////////////////////
64
+ /**
65
+ * Recieves a string or a number that will be namespaced (NAMESPACEVALUE::STRINGorNUMBER)
66
+ * @param value
67
+ * @param namespace
68
+ * @returns
69
+ */
70
+ const UpdateValueWithNamespace = (value, namespace) => {
71
+ Shared.Common.assert(Shared.Logging.LoggingArea.Packages, typeof namespace === "string", `Namespace was not a string, but was type ${typeof namespace}`);
72
+ Shared.Common.assert(Shared.Logging.LoggingArea.Packages, !!namespace.trim(), `Namespace cannot be an empty string`);
73
+ Shared.Common.assert(Shared.Logging.LoggingArea.Packages, typeof value === "string" || typeof value === "number", `Value was not a string, but was type ${typeof value}`);
74
+ const stringValue = String(value);
75
+ Shared.Common.assert(Shared.Logging.LoggingArea.Packages, stringValue.indexOf("::") === -1, `value to namespace contained invalid character ":"`);
76
+ Shared.Common.assert(Shared.Logging.LoggingArea.Packages, namespace.indexOf("::") === -1, `namespace contained invalid character ":"`);
77
+ if (stringValue.trim() === "")
78
+ return stringValue; //empty string should not be namespaced but returned as is
79
+ return `${namespace}::${value}`;
80
+ };
81
+ /**
82
+ * Updates originalObject field values (fieldsToUpdate) using fieldValueUpdateCB and returns original value for non selected fields
83
+ *
84
+ * @param entity object with field values to update
85
+ * @param entityFieldsToUpdate an array of fields to update, or null to update all fields
86
+ * @param namespacerCB CB to call on each field to update
87
+ * @returns entity with field values that have been namespaced or updated with CB
88
+ */
89
+ const EntityNamespacer = (entity, entityFieldsToUpdate, namespacerCB) => {
90
+ const updatedEntity = Object.assign({}, entity);
91
+ if (!entityFieldsToUpdate) {
92
+ // update all
93
+ Object.keys(entity).forEach(field => {
94
+ updatedEntity[field] = namespacerCB(updatedEntity[field]);
95
+ });
96
+ }
97
+ else {
98
+ Object.keys(entity).forEach(key => {
99
+ // if key included in keypath array, update, otherwise return original value
100
+ if (entityFieldsToUpdate.includes(key)) {
101
+ updatedEntity[key] = namespacerCB(updatedEntity[key]);
102
+ }
103
+ else {
104
+ updatedEntity[key] = entity[key];
105
+ }
106
+ });
107
+ }
108
+ return updatedEntity;
109
+ };
110
+ ////////////////////////////////////////////////////////////////////
111
+ ///////////////// Parent namespacing functions
112
+ ////////////////////////////////////////////////////////////////////
113
+ /**
114
+ * internal functions that will add or remove namespaced package data
115
+ * to/from a project's ws data and return the modified workspace data
116
+ */
117
+ /**
118
+ * function adds namespaced package data to workspace data and return the modified workspace data
119
+ *
120
+ * @param allWorkspaceData
121
+ * @param packageInfo
122
+ * @param packageContext
123
+ * @returns AllWorkspaceData with added namespaced data
124
+ */
125
+ const GetNamespacedWorkspaceData = (allWorkspaceData, packageInfo, packageContext) => {
126
+ const { id, version } = packageInfo;
127
+ const namespacedWorkspaceData = {};
128
+ Object.keys(allWorkspaceData).forEach(wsKey => {
129
+ try {
130
+ if (WorkspaceEntityNamespaceFunctionLookup[wsKey]) {
131
+ PackagesLogger.info(`Preparing workspace data for import: ${wsKey}`);
132
+ namespacedWorkspaceData[wsKey] = WorkspaceEntityNamespaceFunctionLookup[wsKey].add(id, version, allWorkspaceData[wsKey]);
133
+ }
134
+ }
135
+ catch (error) {
136
+ const message = `error when preparing workspace data for import: ${wsKey}`;
137
+ if (error instanceof Error) {
138
+ PackagesLogger.errorContext(packageContext.loggerContext, message, error.message, error.name, error.stack, error);
139
+ }
140
+ else {
141
+ PackagesLogger.errorContext(packageContext.loggerContext, message, error);
142
+ }
143
+ throw error;
144
+ }
145
+ });
146
+ return namespacedWorkspaceData;
147
+ };
148
+ /**
149
+ * will remove namespaced package workspace data from allWorkspaceData and return the updated object
150
+ *
151
+ * @param allWorkspaceData
152
+ * @param packageInfo
153
+ * @param packageContext
154
+ * @returns AllWorkspaceData with added namespaced data
155
+ */
156
+ const RemoveNamespacedWorkspaceData = (allWorkspaceData, packageInfo, packageContext) => {
157
+ const prunedWorkspaceData = {};
158
+ const { id } = packageInfo;
159
+ Object.keys(allWorkspaceData).forEach(wsKey => {
160
+ try {
161
+ // if included in entities that should be imported, then add with namespace
162
+ if (WorkspaceEntityNamespaceFunctionLookup[wsKey]) {
163
+ PackagesLogger.info(`Pruning workspace data: ${wsKey}`);
164
+ prunedWorkspaceData[wsKey] = WorkspaceEntityNamespaceFunctionLookup[wsKey].remove(id, allWorkspaceData[wsKey]);
165
+ }
166
+ else {
167
+ // else just add back the original data
168
+ prunedWorkspaceData[wsKey] = allWorkspaceData[wsKey];
169
+ }
170
+ }
171
+ catch (error) {
172
+ const message = `error when pruning workspace data for: ${wsKey}`;
173
+ if (error instanceof Error) {
174
+ PackagesLogger.errorContext(packageContext.loggerContext, message, error.message, error.name, error.stack, error);
175
+ }
176
+ else {
177
+ PackagesLogger.errorContext(packageContext.loggerContext, message, error);
178
+ }
179
+ throw error;
180
+ }
181
+ });
182
+ return prunedWorkspaceData;
183
+ };
184
+ /**
185
+ * Takes a parentEntityObject (ie 'stepTypes') that contains nested entity objects (ie 'stepType-1') that will be namespaced
186
+ * a) all nested entity object keys will be namespaced
187
+ * b) all nested entity object field values (fieldsToUpdate) will be namespaced
188
+ * c) (optional) additional work can be done on the newly namespaced nested entity object using the nestedObjectUpdateCB
189
+ *
190
+ * @param parentEntityObject object containing nested entity objects which will be iterated through and namespaced
191
+ * @param packageID used as new namespace
192
+ * @param fieldsToUpdate array of fields whose values will be namespaced for each nested entity object, or null to update all fields
193
+ * @param nestedObjectUpdateCB (optional) any additional work that should be done to namespaced nested entity object
194
+ * @param entity the entity (singular) being updated, ie. "step type" or "folder", etc. used for info logger
195
+ * @returns
196
+ */
197
+ const NamespaceEntity = (parentEntityObject, packageID, packageVersion, fieldsToUpdate, nestedObjectUpdateCB, entity) => {
198
+ const namespacedDataObject = {};
199
+ Object.keys(parentEntityObject).forEach(key => {
200
+ const isPackageImportedObject = !!Shared.Common.getValueSafe(parentEntityObject[key], ["packageInfo", "id"], null);
201
+ if (isPackageImportedObject)
202
+ return; // do not namespace already namespaced data
203
+ const namespacedKey = UpdateValueWithNamespace(key, packageID);
204
+ PackagesLogger.info(`Adding ${entity}: ${key} as ${namespacedKey}`);
205
+ namespacedDataObject[namespacedKey] = Object.assign({}, EntityNamespacer(parentEntityObject[key], fieldsToUpdate, (fieldValueToUpdate) => UpdateValueWithNamespace(fieldValueToUpdate, packageID)));
206
+ // do additional work if needed on newly namespaced nested object
207
+ if (nestedObjectUpdateCB) {
208
+ nestedObjectUpdateCB(namespacedDataObject[namespacedKey], key);
209
+ }
210
+ // add package information to top level of entity
211
+ namespacedDataObject[namespacedKey]["packageInfo"] = BuildPackageInfoForEntity(packageID, packageVersion);
212
+ });
213
+ return namespacedDataObject;
214
+ };
215
+ ////////////////////////////////////////////////////////////////////
216
+ ///////////////// Namespacing function dictionary
217
+ ////////////////////////////////////////////////////////////////////
218
+ // to start packages will support import/removal of macros and steptypes
219
+ const WorkspaceEntityNamespaceFunctionLookup = {
220
+ macros: {
221
+ add: (packageID, packageVersion, macros) => MacrosNamespacer(packageID, macros, packageVersion),
222
+ remove: (packageID, macros) => MacrosNamespaceRemover(packageID, macros)
223
+ },
224
+ stepTypes: {
225
+ add: (packageID, packageVersion, stepTypes) => StepTypesNamespacer(packageID, stepTypes, packageVersion),
226
+ remove: (packageID, stepTypes) => StepTypesNamespaceRemover(packageID, stepTypes)
227
+ },
228
+ };
229
+ ////////////////////////////////////////////////////////////////////
230
+ ///////////////// Workspace Data Namespacing functions by entity (adding)
231
+ ////////////////////////////////////////////////////////////////////
232
+ const MacrosNamespacer = (packageID, macros, packageVersion) => {
233
+ return NamespaceEntity(macros, packageID, packageVersion, ["id"], null, "macro");
234
+ };
235
+ const StepTypesNamespacer = (packageID, stepTypes, packageVersion) => {
236
+ const filteredStepTypes = Object.keys(stepTypes)
237
+ .filter(stepTypeID => !Shared.StepTypes.isDefaultStepType(stepTypeID)) // remove default steptypes
238
+ .reduce((acc, id) => {
239
+ acc[id] = stepTypes[id];
240
+ return acc;
241
+ }, {});
242
+ const updateStepTypeCB = (namespacedStepTypeData) => {
243
+ namespacedStepTypeData.metadata["defaultStorageLocation"] = null;
244
+ };
245
+ return NamespaceEntity(filteredStepTypes, packageID, packageVersion, ["id"], updateStepTypeCB, "stepType");
246
+ };
247
+ ////////////////////////////////////////////////////////////////////
248
+ ///////////////// Workspace Data Namespace Remover functions by entity (remove)
249
+ ////////////////////////////////////////////////////////////////////
250
+ const EntityRemover = (packageID, parentEntityObject, entityName) => {
251
+ const prunedEntityObject = {};
252
+ Object.keys(parentEntityObject)
253
+ .filter(entityKey => {
254
+ const entityPackageInfo = Shared.Common.getValueSafe(parentEntityObject[entityKey], ["packageInfo"], null);
255
+ // if no info, then not imported, if info doesn't match, then not imported from this package
256
+ if (!entityPackageInfo || (!!entityPackageInfo && entityPackageInfo.id !== packageID)) {
257
+ return entityKey;
258
+ }
259
+ else {
260
+ PackagesLogger.info(`Removing ${entityName}: ${entityKey}`);
261
+ }
262
+ })
263
+ .forEach(prunedEntityKey => prunedEntityObject[prunedEntityKey] = parentEntityObject[prunedEntityKey]);
264
+ return prunedEntityObject;
265
+ };
266
+ const MacrosNamespaceRemover = (packageID, macros) => {
267
+ return EntityRemover(packageID, macros, "macro");
268
+ };
269
+ const StepTypesNamespaceRemover = (packageID, stepTypes) => {
270
+ return EntityRemover(packageID, stepTypes, "step type");
271
+ };
272
+ ////////////////////////////////////////////////////////////////////
273
+ ///////////////// Package/Dependency getters/builders
274
+ ////////////////////////////////////////////////////////////////////
275
+ const BuildPackageInfoForEntity = (packageID, version) => {
276
+ return {
277
+ id: packageID,
278
+ version
279
+ };
280
+ };
281
+ const InitializePackageDependencyInfoForPackage = (id, versionInfo, version, userID) => {
282
+ return {
283
+ id,
284
+ version,
285
+ versionInfo,
286
+ status: EPackageStatus.adding,
287
+ createdAt: null,
288
+ addedBy: userID
289
+ };
290
+ };
291
+ ////////////////////////////////////////////////////////////////////
292
+ ///////////////// Workspace Data Collection functions
293
+ ////////////////////////////////////////////////////////////////////
294
+ const GetRepoNameFromURL = (repoURL) => {
295
+ try {
296
+ const nameIndex = repoURL.lastIndexOf("/") + 1;
297
+ const gitIndex = repoURL.lastIndexOf(".git");
298
+ return repoURL.substring(nameIndex, gitIndex);
299
+ }
300
+ catch (error) {
301
+ PackagesLogger.error(`Error when reading package dependency URL: ${repoURL}`, error);
302
+ throw error;
303
+ }
304
+ };
305
+ const GetAllWorkspaceDataAndCommitHashFromRemoteRepository = (location) => {
306
+ return new Promise((resolve, reject) => {
307
+ // ensure a unique temp folder name
308
+ const tempRepoFolder = `COA_TEMP_${GetRepoNameFromURL(location)}_${Math.random()}`;
309
+ const tempFolderPath = os.tmpdir();
310
+ let commitHashToReturn;
311
+ let commitOrBranch = null;
312
+ let locationToUse = location;
313
+ const indexOfHash = location.indexOf("#");
314
+ // if hash indicator, then grab commit and remove #info from URL
315
+ if (indexOfHash > -1) {
316
+ commitOrBranch = location.slice(indexOfHash + 1);
317
+ locationToUse = location.substring(0, indexOfHash);
318
+ }
319
+ const toExecute = commitOrBranch ? `&& git checkout ${commitOrBranch} && git log -n 1 ${commitOrBranch}` : `&& git log -n 1`;
320
+ PackagesLogger.info(`Cloning Coalesce package from repo URL: ${locationToUse}`);
321
+ return Shared.Common.executeCommand(`cd ${tempFolderPath} && git clone ${locationToUse} ${tempRepoFolder} && ls && cd ${tempRepoFolder} && ls ${toExecute}`)
322
+ .then(res => {
323
+ const fileSystemSettings = { fs: fs_1.default, dir: `${tempFolderPath}/${tempRepoFolder}` };
324
+ const getCommitFromGitLogRegex = new RegExp(/commit ([\s\S]*?)\n/);
325
+ const regexResult = getCommitFromGitLogRegex.exec(res[0]);
326
+ if (!regexResult) {
327
+ const message = `No commit was found for ${commitOrBranch}`;
328
+ throw new Error(message);
329
+ }
330
+ commitHashToReturn = regexResult[1];
331
+ return GetAllWorkspaceDataFromFileSystem(fileSystemSettings);
332
+ })
333
+ .then((workspaceData) => {
334
+ const additionalLogInfo = commitOrBranch ? ` at commit or branch: "${commitOrBranch}"` : "";
335
+ PackagesLogger.info(`Reading data from Coalesce package ${workspaceData.projectName || GetRepoNameFromURL(locationToUse)}${additionalLogInfo}...`);
336
+ resolve({
337
+ workspaceData,
338
+ hashFromRemote: commitHashToReturn
339
+ });
340
+ })
341
+ .catch(error => {
342
+ PackagesLogger.error(`Error while collecting workspace data for repo ${GetRepoNameFromURL(locationToUse)}`, error);
343
+ reject(error);
344
+ });
345
+ });
346
+ };
347
+ const GetAllWorkspaceDataFromFileSystem = (fileSystemSettings) => {
348
+ return Shared.Workspaces.GetAllWorkspaceDataFromGitCommit(fileSystemSettings, null, null);
349
+ };
350
+ ////////////////////////////////////////////////////////////////////
351
+ ///////////////// Other helpers
352
+ ////////////////////////////////////////////////////////////////////
353
+ /**
354
+ * this function will remove any ws data associated with packageInfo from projectWorkspaceData
355
+ * before reinstalling to ensure up-to-date package information
356
+ *
357
+ * @param packageInfo
358
+ * @param packageWorkspaceData
359
+ * @param projectWorkspaceData
360
+ * @param packageContext
361
+ * @returns {
362
+ * desiredState: what FS should look like
363
+ * stateToReplace: what FS looks like now
364
+ * }
365
+ */
366
+ const GetNamespacedDataAndDesiredStateForFSFlush = (packageInfo, packageWorkspaceData, projectWorkspaceData, packageContext) => {
367
+ if (!packageInfo.id) {
368
+ const message = `PackageID was ${packageInfo.id} when it should be a string`;
369
+ Shared.Common.assert(Shared.Logging.LoggingArea.Packages, false, message);
370
+ throw new Error(message);
371
+ }
372
+ PackagesLogger.info(`Cleaning up any stale package data in your project...`);
373
+ // remove entities that were installed by current package
374
+ const prunedProjectWorkspaceData = RemoveNamespacedWorkspaceData(projectWorkspaceData, packageInfo, packageContext);
375
+ // get namespaced data for current package
376
+ const namespacedDataFromPackage = GetNamespacedWorkspaceData(packageWorkspaceData, packageInfo, packageContext);
377
+ // add namespaced data to project data
378
+ const projectWorkspaceDataWithUpdates = CombineWorkspaceDataByEntities(namespacedDataFromPackage, prunedProjectWorkspaceData);
379
+ return {
380
+ desiredState: projectWorkspaceDataWithUpdates,
381
+ namespacedData: namespacedDataFromPackage
382
+ };
383
+ };
384
+ // get a package context object utilized by much of the functionality in this file
385
+ const InitializePackageContext = (authInfo, environmentID) => {
386
+ return {
387
+ firestore: authInfo.firebase.firestore(),
388
+ timestamp: Shared.Firebase.serverTimestamp,
389
+ teamID: authInfo.teamInfo.fbTeamID,
390
+ environmentID,
391
+ loggerContext: { orgID: authInfo.teamInfo.fbTeamID, userID: authInfo.teamInfo.fbUserID }
392
+ };
393
+ };
394
+ exports.InitializePackageContext = InitializePackageContext;
395
+ const CombineWorkspaceDataByEntities = (namespacedWorkspaceData, projectWorkspaceData) => {
396
+ let updatedProjectWorkspaceData = {};
397
+ Object.keys(projectWorkspaceData).forEach(entityKey => {
398
+ updatedProjectWorkspaceData[entityKey] = projectWorkspaceData[entityKey];
399
+ });
400
+ Object.keys(namespacedWorkspaceData).forEach(entityKey => {
401
+ updatedProjectWorkspaceData[entityKey] = Object.assign(Object.assign({}, updatedProjectWorkspaceData[entityKey]), namespacedWorkspaceData[entityKey]);
402
+ });
403
+ return updatedProjectWorkspaceData;
404
+ };
405
+ const InitializeVersionInfo = (commitOrBranch, repoLocation) => {
406
+ if (!!commitOrBranch) {
407
+ return {
408
+ commit: commitOrBranch,
409
+ };
410
+ }
411
+ else {
412
+ return {
413
+ filePath: repoLocation,
414
+ };
415
+ }
416
+ };
417
+ const BuildManifest = (workspaceData) => {
418
+ const manifest = {};
419
+ const getEntityNameFromPackageManifest = (entityType, entityID) => {
420
+ return Shared.Common.getValueSafe(workspaceData, [entityType, entityID, "name"], entityID);
421
+ };
422
+ Object.keys(workspaceData).forEach(entityType => {
423
+ const entityImportedData = Object.keys(workspaceData[entityType]);
424
+ manifest[entityType] = {};
425
+ entityImportedData.forEach(namedEntityKey => {
426
+ debugger;
427
+ manifest[entityType][namedEntityKey] = {
428
+ id: namedEntityKey,
429
+ name: getEntityNameFromPackageManifest(entityType, namedEntityKey)
430
+ };
431
+ });
432
+ });
433
+ return manifest;
434
+ };
435
+ ////////////////////////////////////////////////////
436
+ ///////////// Firestore read/write funcs
437
+ ////////////////////////////////////////////////////
438
+ const GetPackageDocRefAdmin = (packageInfo, packageContext) => {
439
+ const { environmentID, teamID, firestore } = packageContext;
440
+ return Shared.CommonOperations.getOrgConfigDocRefAdmin(firestore, teamID).collection("workspaces").doc(environmentID.toString()).collection("packages").doc(packageInfo.id);
441
+ };
442
+ const UpdatePackageInformationFS = (packageInfo, status, packageContext, setTimestamp) => {
443
+ // example: setting timestamp once package has completed install
444
+ const createdAt = setTimestamp ? packageContext.timestamp : packageInfo.createdAt;
445
+ const update = Object.assign({}, Object.assign(Object.assign({}, packageInfo), { status, createdAt }));
446
+ return GetPackageDocRefAdmin(packageInfo, packageContext).set(update)
447
+ .then(() => {
448
+ return { status, id: packageInfo.id };
449
+ });
450
+ };
451
+ const RemovePackageInformationFS = (packageInfo, packageContext) => {
452
+ const { environmentID, teamID, firestore } = packageContext;
453
+ return Shared.CommonOperations.getOrgConfigDocRefAdmin(firestore, teamID).collection("workspaces").doc(environmentID.toString()).collection("packages").doc(packageInfo.id).delete();
454
+ };
455
+ const GetPackageListMessageCLI = (packageContext) => {
456
+ const { environmentID, teamID, firestore } = packageContext;
457
+ return Shared.Workspaces.getAllWorkspaceDataFromFirebase(firestore, teamID, environmentID)
458
+ .then(wsData => {
459
+ const packages = Shared.Common.getValueSafe(wsData, ["packages"], null);
460
+ if (packages && Object.keys(packages).length) {
461
+ const listForCLI = Object.keys(packages).map(packageID => `${packageID} \n`);
462
+ listForCLI.unshift(`\nPackages added to environment ${environmentID}: \n`);
463
+ return listForCLI.join("");
464
+ }
465
+ else {
466
+ return `No packages added to environment ${environmentID}`;
467
+ }
468
+ });
469
+ };
470
+ exports.GetPackageListMessageCLI = GetPackageListMessageCLI;
471
+ //////////////////////////////////////////////////////////
472
+ ///////////// install/remove kickoff functions and classes
473
+ //////////////////////////////////////////////////////////
474
+ class PackageProvider {
475
+ constructor(dirOrURL, loggerContext) {
476
+ this.version = dirOrURL;
477
+ this.loggerContext = loggerContext;
478
+ // these vars will be null until getWorkspaceDataFS is called
479
+ this.commitHash = null;
480
+ this.id = null;
481
+ this.workspaceData = null;
482
+ this.versionInfo = null;
483
+ // init logic
484
+ if (!(typeof this.version === "string")) {
485
+ const message = `Location should be a string, but is of type ${typeof this.version}`;
486
+ PackagesLogger.errorContext(this.loggerContext, message);
487
+ throw new Error(`Invalid Package package location: ${message}`);
488
+ }
489
+ }
490
+ _getVersionType() {
491
+ const fileKeywordIndex = this.version.indexOf(exports.localInstallKeyword);
492
+ const httpIndex = this.version.indexOf("http");
493
+ if (fileKeywordIndex === 0) {
494
+ return EVersionType.file;
495
+ }
496
+ else if (httpIndex === 0) {
497
+ return EVersionType.url;
498
+ }
499
+ else {
500
+ const message = `Error with URL or filepath: "${this.version}" - filepath missing prefix "filepath:" or URL missing "http://" or "https://"`;
501
+ PackagesLogger.errorContext(this.loggerContext, message);
502
+ throw new Error(message);
503
+ }
504
+ }
505
+ _isProjectNameValid(name) {
506
+ let isValid = true;
507
+ const indexOfNamespace = name.indexOf("::");
508
+ // projectName should not be only whitespace
509
+ if (indexOfNamespace > -1) {
510
+ isValid = false;
511
+ }
512
+ return isValid;
513
+ }
514
+ getWorkspaceDataFS() {
515
+ const versionType = this._getVersionType();
516
+ let wsDataFunc;
517
+ switch (versionType) {
518
+ case EVersionType.file: {
519
+ const dirWithoutKeyword = this.version.substring(exports.localInstallKeyword.length);
520
+ const fsSettings = { fs: fs_1.default, dir: dirWithoutKeyword };
521
+ this.commitHash = null;
522
+ wsDataFunc = GetAllWorkspaceDataFromFileSystem(fsSettings);
523
+ break;
524
+ }
525
+ case EVersionType.url: {
526
+ wsDataFunc = GetAllWorkspaceDataAndCommitHashFromRemoteRepository(this.version)
527
+ .then((response) => {
528
+ const { workspaceData, hashFromRemote } = response;
529
+ this.commitHash = hashFromRemote;
530
+ return workspaceData;
531
+ });
532
+ break;
533
+ }
534
+ default: {
535
+ const message = `versionType was invariant: ${versionType}`;
536
+ Shared.Common.assert(Shared.Logging.LoggingArea.Packages, false, message);
537
+ throw new Error(message);
538
+ }
539
+ }
540
+ return wsDataFunc
541
+ .then((workspaceData) => {
542
+ // ensure package ID
543
+ if (!workspaceData.projectName) {
544
+ PackagesLogger.errorContext(this.loggerContext, `Invalid Package: Missing project name`);
545
+ PackagesLogger.errorContext(this.loggerContext, `Package owner must add a project name in Coalesce git settings before deploying commit for Package`);
546
+ throw new Error(`Invalid Package: Missing package ID; Package owner must add a package name in Coalesce git settings before deploying commit for Package`);
547
+ }
548
+ // validate project name
549
+ if (!this._isProjectNameValid(workspaceData.projectName)) {
550
+ PackagesLogger.errorContext(this.loggerContext, `Invalid Package: Project name contains reserved namespacing characters "::"`);
551
+ throw new Error(`${workspaceData.projectName} contains "::" and is not a valid project name`);
552
+ }
553
+ this.id = workspaceData.projectName.toUpperCase(); // should be uppercase
554
+ this.workspaceData = workspaceData;
555
+ this.versionInfo = InitializeVersionInfo(this.commitHash, this.version);
556
+ return this.workspaceData;
557
+ });
558
+ }
559
+ }
560
+ exports.PackageProvider = PackageProvider;
561
+ const InstallPackage = (packageProvider, packageContext) => {
562
+ const { firestore, teamID, environmentID, loggerContext } = packageContext;
563
+ return new Promise((resolve, reject) => {
564
+ let projectWorkspaceData;
565
+ let packageDependencyInfo;
566
+ let manifest;
567
+ PackagesLogger.infoContext(loggerContext, `Gathering your project workspace data for environment: ${environmentID}`);
568
+ return Shared.Workspaces.getAllWorkspaceDataFromFirebase(firestore, teamID, environmentID)
569
+ .then((projectWorkspaceDataFromFirestore) => {
570
+ projectWorkspaceData = projectWorkspaceDataFromFirestore;
571
+ return packageProvider.getWorkspaceDataFS();
572
+ })
573
+ .then(() => {
574
+ const { id, versionInfo } = packageProvider; // at this point these vars have been defined
575
+ if (!versionInfo) {
576
+ const message = `versionInfo was ${versionInfo}`;
577
+ Shared.Common.assert(Shared.Logging.LoggingArea.Packages, false, message);
578
+ throw new Error(message);
579
+ }
580
+ PackagesLogger.infoContext(loggerContext, `Installing package using namespace "${id}::"`);
581
+ packageDependencyInfo = InitializePackageDependencyInfoForPackage(id, versionInfo, packageProvider.version, loggerContext.userID || "unknown");
582
+ return UpdatePackageInformationFS(packageDependencyInfo, EPackageStatus.adding, packageContext, false);
583
+ })
584
+ .then((packageInfoFS) => {
585
+ const { status, id } = packageInfoFS;
586
+ PackagesLogger.infoContext(loggerContext, `${status} ${id}...building firestore updates payload...`);
587
+ const { desiredState, namespacedData } = GetNamespacedDataAndDesiredStateForFSFlush(packageDependencyInfo, packageProvider.workspaceData, projectWorkspaceData, packageContext);
588
+ manifest = BuildManifest(namespacedData);
589
+ PackagesLogger.infoContext(loggerContext, "Applying updates to firestore...");
590
+ return Shared.Workspaces.FlushFirestoreWorkspaceWithAllWorkspaceData(firestore, teamID, environmentID, desiredState, projectWorkspaceData);
591
+ })
592
+ .then(() => {
593
+ return UpdatePackageInformationFS(Object.assign(Object.assign({}, packageDependencyInfo), { manifest }), EPackageStatus.added, packageContext, true);
594
+ })
595
+ .then((packageInfoFS) => {
596
+ const { status, id } = packageInfoFS;
597
+ PackagesLogger.infoContext(loggerContext, `${status} ${id}...update successful!`);
598
+ resolve();
599
+ })
600
+ .catch(error => {
601
+ if (!!packageDependencyInfo) {
602
+ return UpdatePackageInformationFS(packageDependencyInfo, EPackageStatus.error, packageContext, false)
603
+ .then((packageInfo) => {
604
+ const { status, id } = packageInfo;
605
+ PackagesLogger.errorContext(loggerContext, `Update failed for ${id}. Package ${id} status: ${status}, removing package information from project: `, error);
606
+ PackagesLogger.errorContext(loggerContext, error);
607
+ return (0, exports.UninstallPackage)(id, packageContext);
608
+ })
609
+ .then(() => {
610
+ const message = `All data from package ${packageDependencyInfo.id} removed from project after error during installation.`;
611
+ PackagesLogger.infoContext(loggerContext, message);
612
+ resolve();
613
+ })
614
+ .catch(error => reject(error));
615
+ }
616
+ else {
617
+ PackagesLogger.errorContext(loggerContext, `Update failed when installing package from ${packageProvider.version}`, error);
618
+ reject(error);
619
+ }
620
+ });
621
+ });
622
+ };
623
+ exports.InstallPackage = InstallPackage;
624
+ const UninstallPackage = (packageID, packageContext) => {
625
+ const { firestore, teamID, environmentID, loggerContext } = packageContext;
626
+ return new Promise((resolve, reject) => {
627
+ let projectWorkspaceData;
628
+ let prunedWorkspaceData;
629
+ PackagesLogger.infoContext(loggerContext, `Gathering your project workspace data for environment: ${environmentID}`);
630
+ return Shared.Workspaces.getAllWorkspaceDataFromFirebase(firestore, teamID, environmentID)
631
+ .then((projectWorkspaceDataFromFirestore) => {
632
+ projectWorkspaceData = projectWorkspaceDataFromFirestore;
633
+ const allProjectDependencies = Shared.Common.getValueSafe(projectWorkspaceDataFromFirestore, ["packages"], {});
634
+ const thisDependencyInfo = allProjectDependencies[packageID];
635
+ if (!thisDependencyInfo) {
636
+ const depKeys = Object.keys(allProjectDependencies);
637
+ reject(new Error(`Environment ${environmentID} has ${depKeys.length} packages installed: ${depKeys}. Package "${packageID}" is not installed to this environment.`));
638
+ return;
639
+ }
640
+ PackagesLogger.infoContext(loggerContext, `Removing package workspace data from your project`);
641
+ prunedWorkspaceData = RemoveNamespacedWorkspaceData(projectWorkspaceData, thisDependencyInfo, packageContext);
642
+ return UpdatePackageInformationFS(thisDependencyInfo, EPackageStatus.removing, packageContext, false)
643
+ .then(() => {
644
+ PackagesLogger.infoContext(loggerContext, `Applying updates to firestore...`);
645
+ return Shared.Workspaces.FlushFirestoreWorkspaceWithAllWorkspaceData(firestore, teamID, environmentID, prunedWorkspaceData, projectWorkspaceData);
646
+ })
647
+ .then(() => {
648
+ return RemovePackageInformationFS(thisDependencyInfo, packageContext);
649
+ })
650
+ .then(() => {
651
+ PackagesLogger.infoContext(loggerContext, `Removed ${packageID} workspace data from your project`);
652
+ resolve();
653
+ })
654
+ .catch((error) => {
655
+ if (!!thisDependencyInfo) {
656
+ return UpdatePackageInformationFS(thisDependencyInfo, EPackageStatus.error, packageContext, false)
657
+ .then(() => {
658
+ PackagesLogger.errorContext(loggerContext, `Encountered a problem when removing ${thisDependencyInfo.id}, removal aborted. Please try again.`);
659
+ reject(error);
660
+ })
661
+ .catch(error => reject(error));
662
+ }
663
+ else {
664
+ PackagesLogger.errorContext(loggerContext, `Update failed when removing package`, error);
665
+ reject(error);
666
+ }
667
+ });
668
+ });
669
+ });
670
+ };
671
+ exports.UninstallPackage = UninstallPackage;
672
+ ////////////////////////////////////////////////////
673
+ ///////////// helper Jest test funcs
674
+ ////////////////////////////////////////////////////
675
+ const NamespaceWorkspaceData_Testing = (allWorkspaceData, packageInfo) => {
676
+ const { id, version } = packageInfo;
677
+ const namespacedWorkspaceData = {};
678
+ Object.keys(allWorkspaceData).forEach(wsKey => {
679
+ try {
680
+ if (WorkspaceEntityNamespaceFunctionLookup[wsKey]) {
681
+ PackagesLogger.info(`JEST TEST: Preparing workspace data for import: ${wsKey}`);
682
+ namespacedWorkspaceData[wsKey] = WorkspaceEntityNamespaceFunctionLookup[wsKey].add(id, version, allWorkspaceData[wsKey]);
683
+ }
684
+ }
685
+ catch (error) {
686
+ const message = `JEST TEST: error when preparing workspace data for import: ${wsKey}`;
687
+ if (error instanceof Error) {
688
+ PackagesLogger.error(message, error.message, error.name, error.stack, error);
689
+ }
690
+ else {
691
+ PackagesLogger.error(message, error);
692
+ }
693
+ throw error;
694
+ }
695
+ });
696
+ return namespacedWorkspaceData;
697
+ };
698
+ exports.NamespaceWorkspaceData_Testing = NamespaceWorkspaceData_Testing;
699
+ // will remove namespaced package workspace data from allWorkspaceData and return the updated object
700
+ const RemoveNamespacedWorkspaceData_Testing = (allWorkspaceData, packageInfo) => {
701
+ const prunedWorkspaceData = {};
702
+ const { id } = packageInfo;
703
+ Object.keys(allWorkspaceData).forEach(wsKey => {
704
+ try {
705
+ if (WorkspaceEntityNamespaceFunctionLookup[wsKey]) {
706
+ PackagesLogger.info(`JEST TEST: Pruning workspace data: ${wsKey}`);
707
+ prunedWorkspaceData[wsKey] = WorkspaceEntityNamespaceFunctionLookup[wsKey].remove(id, allWorkspaceData[wsKey]);
708
+ }
709
+ else {
710
+ prunedWorkspaceData[wsKey] = allWorkspaceData[wsKey];
711
+ }
712
+ }
713
+ catch (error) {
714
+ const message = `JEST TEST: error when pruning workspace data for: ${wsKey}`;
715
+ if (error instanceof Error) {
716
+ PackagesLogger.error(message, error.message, error.name, error.stack, error);
717
+ }
718
+ else {
719
+ PackagesLogger.error(message, error);
720
+ }
721
+ throw error;
722
+ }
723
+ });
724
+ return prunedWorkspaceData;
725
+ };
726
+ exports.RemoveNamespacedWorkspaceData_Testing = RemoveNamespacedWorkspaceData_Testing;
727
+ const GetPackageSupportedEntityTypes = () => {
728
+ return Object.keys(WorkspaceEntityNamespaceFunctionLookup);
729
+ };
730
+ exports.GetPackageSupportedEntityTypes = GetPackageSupportedEntityTypes;
731
+ //# sourceMappingURL=Package.js.map
package/bin/Plan.js CHANGED
@@ -25,12 +25,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.CreatePlan = void 0;
26
26
  const Shared = __importStar(require("@coalescesoftware/shared"));
27
27
  const fs_1 = __importDefault(require("fs"));
28
- const CreatePlan = (path, environmentID, token, message) => {
28
+ const CreatePlan = (path, environmentID, token, message, runtimeParameters) => {
29
29
  const fsSettings = {
30
30
  fs: fs_1.default,
31
31
  dir: path,
32
32
  };
33
- return Shared.DeployOperations.CreatePlanCLI(environmentID, fsSettings, token, Shared.Templates.PlatformType.snowflake)
33
+ return Shared.DeployOperations.CreatePlanCLI(environmentID, fsSettings, token, Shared.Templates.PlatformType.snowflake, runtimeParameters)
34
34
  .then(result => {
35
35
  const { plan } = result;
36
36
  plan.gitInfo = { commit: { message: message }, oid: "" };
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.SaveRunOutputToFile = void 0;
23
+ const Shared = __importStar(require("@coalescesoftware/shared"));
24
+ const CLILogger = Shared.Logging.GetLogger(Shared.Logging.LoggingArea.CLI);
25
+ const cleanRunResult = (nodeID, runResult) => {
26
+ return {
27
+ nodeID,
28
+ queryResultSequence: runResult.queryResultSequence
29
+ };
30
+ };
31
+ const GetRunOutputForRunCounter = (firestore, teamID, runID) => {
32
+ let runData;
33
+ return Shared.Runs.getRun(firestore, teamID, runID).get()
34
+ .then((runDataResult /*query snapshot*/) => {
35
+ if (!runDataResult.docs.length) {
36
+ const errorMessage = `wasnt able to get exactly one run for runID ${runID}`;
37
+ CLILogger.emergContext({
38
+ orgID: teamID
39
+ }, errorMessage);
40
+ throw new Error(errorMessage);
41
+ }
42
+ const runInfo = runDataResult.docs[0].data();
43
+ runData = {
44
+ runResults: {
45
+ runStartTime: runInfo.runStartTime,
46
+ runEndTime: runInfo.runEndTime,
47
+ runType: runInfo.runType,
48
+ runStatus: runInfo.runStatus,
49
+ runID: runInfo.id,
50
+ runResults: []
51
+ }
52
+ };
53
+ return Shared.Runs.getRunRunResultsCollection(firestore, teamID, runID.toString())
54
+ .get().then((queryResult /*querySnapshot*/) => {
55
+ let runResults = {};
56
+ queryResult.docs.forEach((runResultDoc) => {
57
+ const stepCounter = runResultDoc.id;
58
+ const runResult = runResultDoc.data();
59
+ runResults[stepCounter] = cleanRunResult(stepCounter, runResult.history[0]);
60
+ });
61
+ runData.runResults.runResults = runInfo.runHistory.map((stepCounter) => {
62
+ return runResults[stepCounter];
63
+ });
64
+ });
65
+ }).then(() => {
66
+ return runData;
67
+ });
68
+ };
69
+ const SaveRunOutputToFile = (runOutputFileLocation, runIDString, token) => {
70
+ return Shared.SchedulerOperations.AuthenticateFirebaseTokenAndRetrieveTeamInfoForCLI(token, undefined).then((authInfo) => {
71
+ const { teamInfo, firebase } = authInfo;
72
+ const teamID = teamInfo.fbTeamID;
73
+ const firestore = firebase.firestore();
74
+ const runID = parseInt(runIDString);
75
+ return GetRunOutputForRunCounter(firestore, teamID, runID);
76
+ }).then((runOutput) => {
77
+ const runOutputJSON = Shared.Common.StringifyFirestoreObject(runOutput);
78
+ return Shared.Common.WriteFile(runOutputFileLocation, runOutputJSON + "\n");
79
+ });
80
+ };
81
+ exports.SaveRunOutputToFile = SaveRunOutputToFile;
82
+ //# sourceMappingURL=RunOutput.js.map
package/bin/index.js CHANGED
@@ -31,6 +31,7 @@ const Shared = __importStar(require("@coalescesoftware/shared"));
31
31
  const CLIProfile = __importStar(require("./CLIProfile"));
32
32
  const chalk_1 = __importDefault(require("chalk"));
33
33
  const commander_1 = require("commander");
34
+ const CommonCLI = __importStar(require("./CommonCLI"));
34
35
  const fs_1 = __importDefault(require("fs"));
35
36
  // setting this needs to be first!
36
37
  Shared.Logging.SetHTTPTransportOptions(Shared.Logging.EHttpServices.CLI);
@@ -39,7 +40,7 @@ const LogCLIInternal = Shared.Logging.GetLogger(Shared.Logging.LoggingArea.CLI_I
39
40
  const program = new commander_1.Command();
40
41
  program.version(require("../package.json").version, "-v, --version");
41
42
  program.option("-b, --debug", "Output extra debugging", false);
42
- program.option("--config <coa-config-location>", "coa config file location", CLIProfile.GetDefaultLocationForCoaConfigFile());
43
+ program.option("--config <coa-config-location>", "coa config file location");
43
44
  const AddOverridesToCommand = (command) => {
44
45
  Object.keys(CLIProfile.ICLIProfileExample).forEach((key) => {
45
46
  command.option(`--${key} <value>`, CLIProfile.ICLIProfileExample[key], "");
@@ -93,10 +94,17 @@ AddOverridesToCommand(program.command(Shared.CLIOperations.ECLICommands.Plan))
93
94
  .then((configResult) => {
94
95
  config = configResult;
95
96
  return Shared.SchedulerOperations.AuthenticateFirebaseTokenAndRetrieveTeamInfoForCLI(config.token, undefined);
96
- }).then(({ teamInfo }) => {
97
+ })
98
+ .then(({ teamInfo, firebase }) => {
99
+ const firestore = firebase.firestore();
97
100
  Shared.Logging.SetBackendGlobalContext({ userID: teamInfo.fbUserID, orgID: teamInfo.fbTeamID });
101
+ const teamID = teamInfo.fbTeamID;
102
+ const environmentID = config.runDetails.environmentID;
103
+ return Shared.SchedulerOperations.GetRuntimeParametersObjectForRun(firestore, teamID, environmentID, config.runtimeParameters);
104
+ })
105
+ .then((runtimeParametersObject) => {
98
106
  const envID = config.runDetails.environmentID;
99
- return Plan.CreatePlan(directoryPath, envID, config.token, `Deploy to environment: ${envID}`);
107
+ return Plan.CreatePlan(directoryPath, envID, config.token, `Deploy to environment: ${envID}`, JSON.stringify(runtimeParametersObject));
100
108
  }).then(result => {
101
109
  const { plan, logContext } = result;
102
110
  logContextToUse = logContext;
@@ -120,6 +128,7 @@ AddOverridesToCommand(program.command(Shared.CLIOperations.ECLICommands.Plan))
120
128
  */
121
129
  AddOverridesToCommand(program.command(Shared.CLIOperations.ECLICommands.Deploy))
122
130
  .option("-p, --plan <plan-location>", "Coalesce plan location", "./coa-plan.json")
131
+ .option("-o, --out <output json>", "Run Results Output in Json ")
123
132
  .description("run a coalesce deployment plan")
124
133
  .action((options, cmd) => {
125
134
  const cliOverrides = ComputeCLIOverrides(cmd);
@@ -153,7 +162,7 @@ AddOverridesToCommand(program.command(Shared.CLIOperations.ECLICommands.Deploy))
153
162
  environmentID: config.runDetails.environmentID
154
163
  },
155
164
  userCredentials: config.userCredentials,
156
- runTimeParameters: {}
165
+ runTimeParameters: plan.runtimeParameters || {},
157
166
  };
158
167
  Shared.Common.assert(Shared.Logging.LoggingArea.CLI, !!runInfo, "runinfo must exist to continue");
159
168
  return Deploy.DeployWithCLI(plan, runInfo, config.token)
@@ -161,7 +170,10 @@ AddOverridesToCommand(program.command(Shared.CLIOperations.ECLICommands.Deploy))
161
170
  const { runCounter, runCompletion, logContext } = result;
162
171
  logContextToUse = logContext !== null && logContext !== void 0 ? logContext : {};
163
172
  LogCLI.infoContext(logContextToUse, chalk_1.default.whiteBright(`Job ${runCounter}: ${chalk_1.default.green("running")}...`));
164
- return runCompletion;
173
+ return runCompletion.finally(() => {
174
+ const outputFilePath = optsWithGlobals.out;
175
+ CommonCLI.FinishWithOutputFile(logContextToUse, outputFilePath, runCounter, config.token);
176
+ });
165
177
  })
166
178
  .then(() => {
167
179
  LogCLI.infoContext(logContextToUse, "Deployment successful!");
@@ -178,6 +190,7 @@ AddOverridesToCommand(program.command(Shared.CLIOperations.ECLICommands.Deploy))
178
190
  */
179
191
  AddOverridesToCommand(program.command(Shared.CLIOperations.ECLICommands.Refresh))
180
192
  .description("runs a Coalesce pipeline")
193
+ .option("-o, --out <output json>", "Run Results Output in Json ")
181
194
  .action((options, command) => {
182
195
  const cliOverrides = ComputeCLIOverrides(command);
183
196
  const optsWithGlobals = command.optsWithGlobals();
@@ -206,8 +219,14 @@ AddOverridesToCommand(program.command(Shared.CLIOperations.ECLICommands.Refresh)
206
219
  config = configResult;
207
220
  return Shared.SchedulerOperations.AuthenticateFirebaseTokenAndRetrieveTeamInfoForCLI(config.token, undefined);
208
221
  })
209
- .then(({ teamInfo }) => {
222
+ .then(({ teamInfo, firebase }) => {
223
+ const firestore = firebase.firestore();
210
224
  Shared.Logging.SetBackendGlobalContext({ userID: teamInfo.fbUserID, orgID: teamInfo.fbTeamID });
225
+ const teamID = teamInfo.fbTeamID;
226
+ const environmentID = config.runDetails.environmentID;
227
+ return Shared.SchedulerOperations.GetRuntimeParametersObjectForRun(firestore, teamID, environmentID, config.runtimeParameters);
228
+ })
229
+ .then((runtimeParametersObject) => {
211
230
  runInfo = {
212
231
  runType: Shared.Runner.ERunType.refresh,
213
232
  runStatus: Shared.Runner.ERunStatus.running,
@@ -218,27 +237,30 @@ AddOverridesToCommand(program.command(Shared.CLIOperations.ECLICommands.Refresh)
218
237
  excludeNodesSelector: config.runDetails.excludeNodesSelector
219
238
  },
220
239
  userCredentials: config.userCredentials,
221
- runTimeParameters: {}
240
+ runTimeParameters: runtimeParametersObject,
222
241
  };
242
+ Shared.Common.CleanupUndefinedValuesFromObject(runInfo);
223
243
  LogCLIInternal.infoContext(logContextToUse, "cliOverrides", cliOverrides);
224
- LogCLIInternal.infoContext(logContextToUse, "got the cli config", config);
225
- LogCLIInternal.infoContext(logContextToUse, "got the run info", config);
244
+ //LogCLIInternal.infoContext(logContextToUse, "got the cli config", config)
245
+ //LogCLIInternal.infoContext(logContextToUse, "got the run info", config)
226
246
  Shared.Common.assert(Shared.Logging.LoggingArea.CLI, !!runInfo, "runinfo must exist to continue");
227
247
  return Refresh.RefreshWithCLI(config.token, runInfo)
228
248
  .then((result) => {
229
249
  const { runCounter, runCompletion, logContext } = result;
230
250
  logContextToUse = logContext !== null && logContext !== void 0 ? logContext : {};
231
251
  LogCLI.infoContext(logContextToUse, chalk_1.default.whiteBright(`Refresh run initialized. RunCounter: ${runCounter}`));
232
- return runCompletion;
252
+ return runCompletion.finally(() => {
253
+ const outputFilePath = optsWithGlobals.out;
254
+ CommonCLI.FinishWithOutputFile(logContextToUse, outputFilePath, runCounter, config.token);
255
+ }).then(() => {
256
+ LogCLI.infoContext(logContextToUse, "Refresh successful!");
257
+ Shared.CLIOperations.ExitCLISafe(0);
258
+ });
233
259
  })
234
- .then(() => {
235
- LogCLI.infoContext(logContextToUse, "Refresh successful!");
236
- Shared.CLIOperations.ExitCLISafe(0);
260
+ .catch(error => {
261
+ LogCLI.errorContext(logContextToUse, chalk_1.default.redBright(`Error during refresh!: ${error.toString()}`));
262
+ Shared.CLIOperations.ExitCLISafe(1);
237
263
  });
238
- })
239
- .catch(error => {
240
- LogCLI.errorContext(logContextToUse, chalk_1.default.redBright(`Error during refresh!: ${error.toString()}`));
241
- Shared.CLIOperations.ExitCLISafe(1);
242
264
  });
243
265
  });
244
266
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coalescesoftware/coa",
3
- "version": "1.0.102",
3
+ "version": "1.0.114",
4
4
  "license": "ISC",
5
5
  "author": "Coalesce Automation, Inc.",
6
6
  "main": "index.js",
@@ -12,7 +12,7 @@
12
12
  "start-cli-debug": "yarn run start --debug"
13
13
  },
14
14
  "dependencies": {
15
- "@coalescesoftware/shared": "^1.0.111",
15
+ "@coalescesoftware/shared": "^1.0.114",
16
16
  "chalk": "^4.1.2",
17
17
  "commander": "^9.2.0",
18
18
  "firebase": "8.2.0",