@salesforce/packaging 0.0.3 → 0.0.6

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 (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/lib/constants.d.ts +21 -0
  3. package/lib/constants.js +40 -0
  4. package/lib/interfaces/packagingInterfacesAndType.d.ts +111 -0
  5. package/lib/interfaces/packagingSObjects.d.ts +2 -1
  6. package/lib/interfaces/packagingSObjects.js +0 -6
  7. package/lib/package/index.d.ts +4 -0
  8. package/lib/package/index.js +8 -0
  9. package/lib/package/packageConvert.d.ts +11 -0
  10. package/lib/package/packageConvert.js +78 -0
  11. package/lib/package/packageCreate.d.ts +26 -0
  12. package/lib/package/packageCreate.js +115 -0
  13. package/lib/package/packageDelete.d.ts +3 -0
  14. package/lib/package/packageDelete.js +26 -0
  15. package/lib/package/packageVersion.d.ts +8 -0
  16. package/lib/package/packageVersion.js +27 -0
  17. package/lib/package/packageVersionCreate.d.ts +70 -0
  18. package/lib/package/packageVersionCreate.js +781 -0
  19. package/lib/package/packageVersionCreateRequest.d.ts +4 -0
  20. package/lib/package/packageVersionCreateRequest.js +85 -0
  21. package/lib/package/packageVersionList.d.ts +9 -0
  22. package/lib/package/packageVersionList.js +91 -0
  23. package/lib/package/profileApi.d.ts +55 -0
  24. package/lib/package/profileApi.js +275 -0
  25. package/lib/utils/index.d.ts +1 -0
  26. package/lib/utils/index.js +1 -0
  27. package/lib/utils/packageUtils.d.ts +7 -6
  28. package/lib/utils/packageUtils.js +51 -13
  29. package/lib/utils/srcDevUtils.d.ts +12 -0
  30. package/lib/utils/srcDevUtils.js +62 -0
  31. package/lib/utils/versionNumber.d.ts +1 -0
  32. package/lib/utils/versionNumber.js +5 -0
  33. package/messages/messages.md +17 -0
  34. package/package.json +10 -5
  35. package/lib/package/packageVersionCreateRequestApi.d.ts +0 -17
  36. package/lib/package/packageVersionCreateRequestApi.js +0 -92
@@ -0,0 +1,781 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2020, salesforce.com, inc.
4
+ * All rights reserved.
5
+ * Licensed under the BSD 3-Clause license.
6
+ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.PackageVersionCreate = void 0;
10
+ const util = require("util");
11
+ const path = require("path");
12
+ const os = require("os");
13
+ const fs = require("fs");
14
+ const core_1 = require("@salesforce/core");
15
+ const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
16
+ const testSetup_1 = require("@salesforce/core/lib/testSetup");
17
+ const scratchOrgSettingsGenerator_1 = require("@salesforce/core/lib/org/scratchOrgSettingsGenerator");
18
+ const xml2js = require("xml2js");
19
+ const kit_1 = require("@salesforce/kit");
20
+ const pkgUtils = require("../utils/packageUtils");
21
+ const constants_1 = require("../constants");
22
+ const utils_1 = require("../utils");
23
+ const versionNumber_1 = require("../utils/versionNumber");
24
+ const profileApi_1 = require("./profileApi");
25
+ const packageVersionCreateRequest_1 = require("./packageVersionCreateRequest");
26
+ core_1.Messages.importMessagesDirectory(__dirname);
27
+ const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'messages');
28
+ const logger = core_1.Logger.childFromRoot('packageVersionCreate');
29
+ const DESCRIPTOR_FILE = 'package2-descriptor.json';
30
+ const POLL_INTERVAL_WITHOUT_VALIDATION_SECONDS = 5;
31
+ class PackageVersionCreate {
32
+ constructor(options) {
33
+ this.options = options;
34
+ this.packageDirs = [];
35
+ this.connection = this.options.connection;
36
+ this.project = this.options.project;
37
+ }
38
+ createPackageVersion(options) {
39
+ return this.packageVersionCreate(options).catch((err) => {
40
+ // TODO: until package2 is GA, wrap perm-based errors w/ 'contact sfdc' action (REMOVE once package2 is GA'd)
41
+ err = pkgUtils.massageErrorMessage(err);
42
+ throw pkgUtils.applyErrorAction(err);
43
+ });
44
+ }
45
+ async listRequest(createdlastdays, status) {
46
+ return (0, packageVersionCreateRequest_1.list)({ createdlastdays, status, connection: this.connection });
47
+ }
48
+ async listRequestById(id, connection) {
49
+ return (0, packageVersionCreateRequest_1.byId)(id, connection);
50
+ }
51
+ rejectWithInstallKeyError() {
52
+ // This command also requires either the installationkey flag or installationkeybypass flag
53
+ const errorString = messages.getMessage('errorMissingFlagsInstallationKey', [
54
+ '--installationkey',
55
+ '--installationkeybypass',
56
+ ]);
57
+ const error = new Error(errorString);
58
+ error['name'] = 'requiredFlagMissing';
59
+ return Promise.reject(error);
60
+ }
61
+ // convert source to mdapi format and copy to tmp dir packaging up
62
+ async generateMDFolderForArtifact(options) {
63
+ const componentSet = await source_deploy_retrieve_1.ComponentSetBuilder.build({
64
+ sourceapiversion: this.project.getSfProjectJson().get('sourceApiVersion'),
65
+ sourcepath: options.sourcePaths,
66
+ manifest: {
67
+ manifestPath: options.manifest,
68
+ directoryPaths: this.project.getPackageDirectories().map((dir) => dir.path),
69
+ },
70
+ metadata: {
71
+ metadataEntries: options.metadataPaths,
72
+ directoryPaths: this.project.getPackageDirectories().map((dir) => dir.path),
73
+ },
74
+ });
75
+ const packageName = options.packageName;
76
+ const outputDirectory = path.resolve(options.outputDir);
77
+ const converter = new source_deploy_retrieve_1.MetadataConverter();
78
+ const convertResult = await converter.convert(componentSet, 'metadata', {
79
+ type: 'directory',
80
+ outputDirectory,
81
+ packageName,
82
+ genUniqueDir: false,
83
+ });
84
+ if (packageName) {
85
+ // SDR will build an output path like /output/directory/packageName/package.xml
86
+ // this was breaking from toolbelt, so to revert it we copy the directory up a level and delete the original
87
+ (0, utils_1.copyDir)(convertResult.packagePath, outputDirectory);
88
+ try {
89
+ fs.rmSync(convertResult.packagePath, { recursive: true });
90
+ }
91
+ catch (e) {
92
+ // rmdirSync is being deprecated and emits a warning
93
+ // but rmSync is introduced in node 14 so fall back to rmdirSync
94
+ fs.rmdirSync(convertResult.packagePath, { recursive: true });
95
+ }
96
+ convertResult.packagePath = outputDirectory;
97
+ return convertResult;
98
+ }
99
+ }
100
+ validateDependencyValues(dependency) {
101
+ // If valid 04t package, just return it to be used straight away.
102
+ if (dependency.subscriberPackageVersionId) {
103
+ pkgUtils.validateId(pkgUtils.BY_LABEL.SUBSCRIBER_PACKAGE_VERSION_ID, dependency.subscriberPackageVersionId);
104
+ return Promise.resolve();
105
+ }
106
+ if (dependency.packageId && dependency.package) {
107
+ throw messages.createError('errorPackageAndPackageIdCollision', []);
108
+ }
109
+ const packageIdFromAlias = pkgUtils.getPackageIdFromAlias(dependency.packageId || dependency.package, this.project);
110
+ // If valid 04t package, just return it to be used straight away.
111
+ if (pkgUtils.validateIdNoThrow(pkgUtils.BY_LABEL.SUBSCRIBER_PACKAGE_VERSION_ID, packageIdFromAlias)) {
112
+ dependency.subscriberPackageVersionId = packageIdFromAlias;
113
+ return Promise.resolve();
114
+ }
115
+ if (!packageIdFromAlias || !dependency.versionNumber) {
116
+ throw messages.createError('errorDependencyPair', [JSON.stringify(dependency)]);
117
+ }
118
+ // Just override dependency.packageId value to the resolved alias.
119
+ dependency.packageId = packageIdFromAlias;
120
+ pkgUtils.validateId(pkgUtils.BY_LABEL.PACKAGE_ID, dependency.packageId);
121
+ pkgUtils.validateVersionNumber(dependency.versionNumber, versionNumber_1.BuildNumberToken.LATEST_BUILD_NUMBER_TOKEN, versionNumber_1.BuildNumberToken.RELEASED_BUILD_NUMBER_TOKEN);
122
+ // Validate that the Package2 id exists on the server
123
+ const query = `SELECT Id FROM Package2 WHERE Id = '${dependency.packageId}'`;
124
+ return this.connection.tooling.query(query).then((pkgQueryResult) => {
125
+ const subRecords = pkgQueryResult.records;
126
+ if (!subRecords || subRecords.length !== 1) {
127
+ throw messages.createError('errorNoIdInHub', [dependency.packageId]);
128
+ }
129
+ });
130
+ }
131
+ /**
132
+ * A dependency in the workspace config file may be specified using either a subscriber package version id (04t)
133
+ * or a package Id (0Ho) + a version number. Additionally, a build number may be the actual build number, or a
134
+ * keyword: LATEST or RELEASED (meaning the latest or released build number for a given major.minor.patch).
135
+ *
136
+ * This method resolves a package Id + version number to a subscriber package version id (04t)
137
+ * and adds it as a SubscriberPackageVersionId parameter in the dependency object.
138
+ */
139
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
+ async retrieveSubscriberPackageVersionId(dependency, branchFromFlagOrDef) {
141
+ await this.validateDependencyValues(dependency);
142
+ if (dependency.subscriberPackageVersionId) {
143
+ delete dependency.package;
144
+ // if a 04t id is specified just use it.
145
+ return dependency;
146
+ }
147
+ const versionNumber = versionNumber_1.VersionNumber.from(dependency.versionNumber);
148
+ const buildNumber = versionNumber.build;
149
+ // use the dependency.branch if present otherwise use the branch of the version being created
150
+ const branch = dependency.branch || dependency.branch === '' ? dependency.branch : branchFromFlagOrDef;
151
+ const branchString = !branch || branch === '' ? 'null' : `'${branch}'`;
152
+ // resolve a build number keyword to an actual number, if needed
153
+ const resolvedBuildNumber = await this.resolveBuildNumber(versionNumber, dependency.packageId, branch);
154
+ // now that we have a full build number, query for the associated 04t.
155
+ // because the build number may not be unique across versions, add in conditionals for
156
+ // the branch or the RELEASED token (if used)
157
+ const branchOrReleasedCondition = buildNumber === versionNumber_1.BuildNumberToken.RELEASED_BUILD_NUMBER_TOKEN
158
+ ? 'AND IsReleased = true'
159
+ : `AND Branch = ${branchString}`;
160
+ const query = `SELECT SubscriberPackageVersionId FROM Package2Version WHERE Package2Id = '${dependency.packageId}' AND MajorVersion = ${versionNumber[0]} AND MinorVersion = ${versionNumber[1]} AND PatchVersion = ${versionNumber[2]} AND BuildNumber = ${resolvedBuildNumber} ${branchOrReleasedCondition}`;
161
+ const pkgVerQueryResult = await this.connection.tooling.query(query);
162
+ const subRecords = pkgVerQueryResult.records;
163
+ if (!subRecords || subRecords.length !== 1) {
164
+ throw new Error(`No version number was found in Dev Hub for package id ${dependency.packageId} and branch ${branchString} and version number ${versionNumber.toString()} that resolved to build number ${resolvedBuildNumber}`);
165
+ }
166
+ dependency.subscriberPackageVersionId = pkgVerQueryResult.records[0].SubscriberPackageVersionId;
167
+ // warn user of the resolved build number when LATEST and RELEASED keywords are used
168
+ if (versionNumber.isbuildKeyword()) {
169
+ versionNumber.build = resolvedBuildNumber;
170
+ if (buildNumber === versionNumber_1.BuildNumberToken.LATEST_BUILD_NUMBER_TOKEN) {
171
+ logger.info(messages.getMessage('buildNumberResolvedForLatest', [
172
+ dependency.package,
173
+ versionNumber.toString(),
174
+ branchString,
175
+ dependency.subscriberPackageVersionId,
176
+ ]));
177
+ }
178
+ else if (buildNumber === versionNumber_1.BuildNumberToken.RELEASED_BUILD_NUMBER_TOKEN) {
179
+ logger.info(messages.getMessage('buildNumberResolvedForReleased', [
180
+ dependency.package,
181
+ versionNumber.toString(),
182
+ dependency.subscriberPackageVersionId,
183
+ ]));
184
+ }
185
+ }
186
+ delete dependency.packageId;
187
+ delete dependency.package;
188
+ delete dependency.versionNumber;
189
+ delete dependency.branch;
190
+ return dependency;
191
+ }
192
+ async resolveBuildNumber(versionNumber, packageId, branch) {
193
+ if (!versionNumber.isbuildKeyword()) {
194
+ // The build number is already specified so just return it using the tooling query result obj structure
195
+ return versionNumber.build;
196
+ }
197
+ // query for the LATEST or RELEASED build number (excluding deleted versions)
198
+ let branchCondition = '';
199
+ let releasedCondition = '';
200
+ if (versionNumber[3] === versionNumber_1.BuildNumberToken.LATEST_BUILD_NUMBER_TOKEN) {
201
+ // respect the branch when querying for LATEST
202
+ const branchString = !branch || branch === '' ? 'null' : `'${branch}'`;
203
+ branchCondition = `AND Branch = ${branchString}`;
204
+ }
205
+ else if (versionNumber[3] === versionNumber_1.BuildNumberToken.RELEASED_BUILD_NUMBER_TOKEN) {
206
+ releasedCondition = 'AND IsReleased = true';
207
+ }
208
+ const query = `SELECT MAX(BuildNumber) FROM Package2Version WHERE Package2Id = '${packageId}' AND IsDeprecated != true AND MajorVersion = ${versionNumber.major} AND MinorVersion = ${versionNumber.minor} AND PatchVersion = ${versionNumber.patch} ${branchCondition} ${releasedCondition}`;
209
+ const results = await this.connection.tooling.query(query);
210
+ const records = results.records;
211
+ if (!records || records.length === 0 || records[0].expr0 == null) {
212
+ if (versionNumber.build === versionNumber_1.BuildNumberToken.RELEASED_BUILD_NUMBER_TOKEN) {
213
+ throw new Error(`No released version was found in Dev Hub for package id ${packageId} and version number ${versionNumber.toString()}`);
214
+ }
215
+ else {
216
+ throw new Error(`No version number was found in Dev Hub for package id ${packageId} and branch ${branch} and version number ${versionNumber.toString()}`);
217
+ }
218
+ }
219
+ return `${results.records[0].expr0}`;
220
+ }
221
+ async createRequestObject(packageId, options, preserveFiles, packageVersTmpRoot, packageVersBlobZipFile) {
222
+ const zipFileBase64 = fs.readFileSync(packageVersBlobZipFile).toString('base64');
223
+ const requestObject = {
224
+ Package2Id: packageId,
225
+ VersionInfo: zipFileBase64,
226
+ Tag: options.tag,
227
+ Branch: options.branch,
228
+ InstallKey: options.installationkey,
229
+ Instance: options.buildinstance,
230
+ SourceOrg: options.sourceorg,
231
+ CalculateCodeCoverage: options.codecoverage,
232
+ SkipValidation: options.skipvalidation,
233
+ };
234
+ if (preserveFiles) {
235
+ logger.info(messages.getMessage('tempFileLocation', [packageVersTmpRoot]));
236
+ return requestObject;
237
+ }
238
+ else {
239
+ return fs.promises.unlink(packageVersTmpRoot).then(() => requestObject);
240
+ }
241
+ }
242
+ getPackageDescriptorJsonFromPackageId(packageId, flags) {
243
+ const artDir = flags.path;
244
+ const packageDescriptorJson = this.packageDirs.find((packageDir) => {
245
+ const packageDirPackageId = pkgUtils.getPackageIdFromAlias(packageDir.package, this.project);
246
+ return !!packageDirPackageId && packageDirPackageId === packageId ? packageDir : null;
247
+ });
248
+ if (!packageDescriptorJson) {
249
+ throw new Error(`${constants_1.consts.WORKSPACE_CONFIG_FILENAME} does not contain a packaging directory for ${artDir}`);
250
+ }
251
+ return packageDescriptorJson;
252
+ }
253
+ /**
254
+ * Convert the list of command line options to a JSON object that can be used to create an Package2VersionCreateRequest entity.
255
+ *
256
+ * @param options
257
+ * @param packageId
258
+ * @param versionNumberString
259
+ * @returns {{Package2Id: (*|p|boolean), Package2VersionMetadata: *, Tag: *, Branch: number}}
260
+ * @private
261
+ */
262
+ async createPackageVersionCreateRequestFromOptions(options, packageId, versionNumberString) {
263
+ const artDir = options.path;
264
+ const preserveFiles = !util.isNullOrUndefined(options.preserve || process.env.SFDX_PACKAGE2_VERSION_CREATE_PRESERVE);
265
+ const uniqueHash = (0, testSetup_1.uniqid)({ template: `${packageId}-%s` });
266
+ const packageVersTmpRoot = path.join(os.tmpdir(), `${uniqueHash}`);
267
+ const packageVersMetadataFolder = path.join(packageVersTmpRoot, 'md-files');
268
+ const unpackagedMetadataFolder = path.join(packageVersTmpRoot, 'unpackaged-md-files');
269
+ const packageVersProfileFolder = path.join(packageVersMetadataFolder, 'profiles');
270
+ const packageVersBlobDirectory = path.join(packageVersTmpRoot, 'package-version-info');
271
+ const metadataZipFile = path.join(packageVersBlobDirectory, 'package.zip');
272
+ const unpackagedMetadataZipFile = path.join(packageVersBlobDirectory, 'unpackaged-metadata-package.zip');
273
+ const settingsZipFile = path.join(packageVersBlobDirectory, 'settings.zip');
274
+ const packageVersBlobZipFile = path.join(packageVersTmpRoot, 'package-version-info.zip');
275
+ const sourceBaseDir = path.join(this.project.getPath(), artDir);
276
+ const mdOptions = {
277
+ deploydir: packageVersMetadataFolder,
278
+ sourcedir: sourceBaseDir,
279
+ };
280
+ // Stores any additional client side info that might be needed later on in the process
281
+ const clientSideInfo = new Map();
282
+ const settingsGenerator = new scratchOrgSettingsGenerator_1.default();
283
+ // Copy all of the metadata from the workspace to a tmp folder
284
+ await this.generateMDFolderForArtifact(mdOptions);
285
+ const packageDescriptorJson = this.getPackageDescriptorJsonFromPackageId(packageId, options);
286
+ if (packageDescriptorJson.package) {
287
+ delete packageDescriptorJson.package;
288
+ packageDescriptorJson.id = packageId;
289
+ }
290
+ const definitionFile = options.definitionfile ? options.definitionfile : packageDescriptorJson.definitionFile;
291
+ if (definitionFile) {
292
+ // package2-descriptor.json sent to the server should contain only the features, snapshot & orgPreferences
293
+ // defined in the definition file.
294
+ delete packageDescriptorJson.features;
295
+ delete packageDescriptorJson.orgPreferences;
296
+ delete packageDescriptorJson.definitionFile;
297
+ delete packageDescriptorJson.snapshot;
298
+ const definitionFilePayload = await fs.promises.readFile(definitionFile, 'utf8');
299
+ const definitionFileJson = JSON.parse(definitionFilePayload);
300
+ const pkgProperties = [
301
+ 'country',
302
+ 'edition',
303
+ 'language',
304
+ 'features',
305
+ 'orgPreferences',
306
+ 'snapshot',
307
+ 'release',
308
+ 'sourceOrg',
309
+ ];
310
+ // Load any settings from the definition
311
+ await settingsGenerator.extract(definitionFileJson);
312
+ if (settingsGenerator.hasSettings() && definitionFileJson.orgPreferences) {
313
+ // this is not allowed, exit with an error
314
+ return Promise.reject(messages.createError('signupDuplicateSettingsSpecified'));
315
+ }
316
+ pkgProperties.forEach((prop) => {
317
+ const propValue = definitionFileJson[prop];
318
+ if (propValue) {
319
+ packageDescriptorJson[prop] = propValue;
320
+ }
321
+ });
322
+ }
323
+ // @ts-ignore
324
+ const [hasUnpackagedMetadata, unpackagedPromise] = await this.resolveUnpackagedMetadata(packageDescriptorJson, unpackagedMetadataFolder, clientSideInfo, options.codecoverage);
325
+ this.resolveApexTestPermissions(packageDescriptorJson, options);
326
+ // All dependencies for the packaging dir should be resolved to an 04t id to be passed to the server.
327
+ // (see _retrieveSubscriberPackageVersionId for details)
328
+ const dependencies = packageDescriptorJson.dependencies;
329
+ // branch can be set via flag or descriptor; flag takes precedence
330
+ options.branch = options.branch ? options.branch : packageDescriptorJson.branch;
331
+ const resultValues = await Promise.all(dependencies
332
+ ? []
333
+ : dependencies.map((dependency) => this.retrieveSubscriberPackageVersionId(dependency, options.branch)));
334
+ const ancestorId = await pkgUtils.getAncestorId(packageDescriptorJson, this.connection, this.project, versionNumberString, options.skipancestorcheck);
335
+ // If dependencies exist, the resultValues array will contain the dependencies populated with a resolved
336
+ // subscriber pkg version id.
337
+ if (resultValues.length > 0) {
338
+ packageDescriptorJson.dependencies = resultValues;
339
+ }
340
+ this.cleanPackageDescriptorJson(packageDescriptorJson);
341
+ this.setPackageDescriptorJsonValues(packageDescriptorJson, options);
342
+ await fs.promises.mkdir(packageVersTmpRoot, { recursive: true });
343
+ await fs.promises.mkdir(packageVersBlobDirectory, { recursive: true });
344
+ if (Object.prototype.hasOwnProperty.call(packageDescriptorJson, 'ancestorVersion')) {
345
+ delete packageDescriptorJson.ancestorVersion;
346
+ }
347
+ packageDescriptorJson.ancestorId = ancestorId;
348
+ await fs.promises.writeFile(path.join(packageVersBlobDirectory, DESCRIPTOR_FILE),
349
+ // TODO: need to make sure packageDescriptorJson contains the right values for the descriptor
350
+ JSON.stringify(packageDescriptorJson, undefined, 2), 'utf-8');
351
+ // As part of the source convert process, the package.xml has been written into the tmp metadata directory.
352
+ // The package.xml may need to be manipulated due to processing profiles in the workspace or additional
353
+ // metadata exclusions. If necessary, read the existing package.xml and then re-write it.
354
+ const currentPackageXml = await fs.promises.readFile(path.join(packageVersMetadataFolder, 'package.xml'), 'utf8');
355
+ // convert to json
356
+ const packageJson = xml2js.parseStringAsync(currentPackageXml);
357
+ fs.mkdirSync(packageVersMetadataFolder, { recursive: true });
358
+ fs.mkdirSync(packageVersProfileFolder, { recursive: true });
359
+ // Apply any necessary exclusions to typesArr.
360
+ let typesArr = packageJson.Package.types;
361
+ this.apiVersionFromPackageXml = packageJson.Package.version;
362
+ // if we're using unpackaged metadata, don't package the profiles located there
363
+ if (hasUnpackagedMetadata) {
364
+ typesArr = options.profileApi.filterAndGenerateProfilesForManifest(typesArr, [
365
+ clientSideInfo.get('UnpackagedMetadataPath'),
366
+ ]);
367
+ }
368
+ else {
369
+ typesArr = options.profileApi.filterAndGenerateProfilesForManifest(typesArr);
370
+ }
371
+ // Next generate profiles and retrieve any profiles that were excluded because they had no matching nodes.
372
+ const excludedProfiles = options.profileApi.generateProfiles(packageVersProfileFolder, {
373
+ Package: { types: typesArr },
374
+ }, [clientSideInfo.get('UnpackagedMetadataPath')]);
375
+ if (excludedProfiles.length > 0) {
376
+ const profileIdx = typesArr.findIndex((e) => e.name[0] === 'Profile');
377
+ typesArr[profileIdx].members = typesArr[profileIdx].members.filter((e) => excludedProfiles.indexOf(e) === -1);
378
+ }
379
+ packageJson.Package.types = typesArr;
380
+ // Re-write the package.xml in case profiles have been added or removed
381
+ const xmlBuilder = new xml2js.Builder({
382
+ xmldec: { version: '1.0', encoding: 'UTF-8' },
383
+ });
384
+ const xml = xmlBuilder.buildObject(packageJson);
385
+ // Log information about the profiles being packaged up
386
+ const profiles = options.profileApi.getProfileInformation();
387
+ profiles.forEach((profile) => {
388
+ if (logger.shouldLog(core_1.LoggerLevel.DEBUG)) {
389
+ logger.debug(profile.logDebug());
390
+ }
391
+ else if (logger.shouldLog(core_1.LoggerLevel.INFO)) {
392
+ logger.info(profile.logInfo());
393
+ }
394
+ });
395
+ // TODO: confirm that param xml is writeable
396
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
397
+ await fs.promises.writeFile(path.join(packageVersMetadataFolder, 'package.xml'), xml, 'utf-8');
398
+ // Zip the packageVersMetadataFolder folder and put the zip in {packageVersBlobDirectory}/package.zip
399
+ (0, utils_1.zipDir)(packageVersMetadataFolder, metadataZipFile);
400
+ if (hasUnpackagedMetadata) {
401
+ // Zip the unpackagedMetadataFolder folder and put the zip in {packageVersBlobDirectory}/{unpackagedMetadataZipFile}
402
+ await (0, utils_1.zipDir)(unpackagedMetadataFolder, unpackagedMetadataZipFile);
403
+ }
404
+ // Zip up the expanded settings (if present)
405
+ if (settingsGenerator.hasSettings()) {
406
+ await settingsGenerator.createDeploy();
407
+ const settingsRoot = settingsGenerator.getShapeDirName();
408
+ // The SettingsGenerator now generates md files in source format, not mdapi format,
409
+ // so we need to convert to mdapi format here.
410
+ const compSet = source_deploy_retrieve_1.ComponentSet.fromSource(settingsRoot);
411
+ compSet.apiVersion = this.apiVersionFromPackageXml;
412
+ compSet.sourceApiVersion = this.apiVersionFromPackageXml;
413
+ const mdConverter = new source_deploy_retrieve_1.MetadataConverter();
414
+ const convertResult = await mdConverter.convert(compSet, 'metadata', {
415
+ type: 'directory',
416
+ outputDirectory: path.join(settingsRoot, 'pkgMdSettings'),
417
+ genUniqueDir: false,
418
+ });
419
+ (0, utils_1.zipDir)(convertResult.packagePath, settingsZipFile);
420
+ }
421
+ // Zip the Version Info and package.zip files into another zip
422
+ (0, utils_1.zipDir)(packageVersBlobDirectory, packageVersBlobZipFile);
423
+ return this.createRequestObject(packageId, options, preserveFiles, packageVersTmpRoot, packageVersBlobZipFile);
424
+ }
425
+ resolveApexTestPermissions(packageDescriptorJson, options) {
426
+ // Process permissionSet and permissionSetLicenses that should be enabled when running Apex tests
427
+ // This only applies if code coverage is enabled
428
+ if (options.codecoverage) {
429
+ // Assuming no permission sets are named 0, 0n, null, undefined, false, NaN, and the empty string
430
+ if (packageDescriptorJson.apexTestAccess && packageDescriptorJson.apexTestAccess.permissionSets) {
431
+ let permSets = packageDescriptorJson.apexTestAccess.permissionSets;
432
+ if (!Array.isArray(permSets)) {
433
+ permSets = permSets.split(',');
434
+ }
435
+ packageDescriptorJson.permissionSetNames = permSets.map((s) => s.trim());
436
+ }
437
+ if (packageDescriptorJson.apexTestAccess && packageDescriptorJson.apexTestAccess.permissionSetLicenses) {
438
+ let permissionSetLicenses = packageDescriptorJson.apexTestAccess.permissionSetLicenses;
439
+ if (!Array.isArray(permissionSetLicenses)) {
440
+ permissionSetLicenses = permissionSetLicenses.split(',');
441
+ }
442
+ packageDescriptorJson.permissionSetLicenseDeveloperNames = permissionSetLicenses.map((s) => s.trim());
443
+ }
444
+ }
445
+ delete packageDescriptorJson.apexTestAccess;
446
+ }
447
+ async resolveUnpackagedMetadata(packageDescriptorJson, unpackagedMetadataFolder, clientSideInfo, codeCoverage) {
448
+ let unpackagedPromise = null;
449
+ let hasUnpackagedMetadata = false;
450
+ // Add the Unpackaged Metadata, if any, to the output directory, only when code coverage is specified
451
+ if (codeCoverage && packageDescriptorJson.unpackagedMetadata && packageDescriptorJson.unpackagedMetadata.path) {
452
+ hasUnpackagedMetadata = true;
453
+ const unpackagedPath = path.join(process.cwd(), packageDescriptorJson.unpackagedMetadata.path);
454
+ try {
455
+ fs.statSync(unpackagedPath);
456
+ }
457
+ catch (err) {
458
+ throw new Error(`Unpackaged metadata directory '${packageDescriptorJson.unpackagedMetadata.path}' was specified but does not exist`);
459
+ }
460
+ fs.mkdirSync(unpackagedMetadataFolder, { recursive: true });
461
+ unpackagedPromise = this.generateMDFolderForArtifact({
462
+ deploydir: unpackagedMetadataFolder,
463
+ sourceDir: unpackagedPath,
464
+ });
465
+ // Set which package is the "unpackaged" package
466
+ clientSideInfo.set('UnpackagedMetadataPath', packageDescriptorJson.unpackagedMetadata.path);
467
+ }
468
+ return [hasUnpackagedMetadata, unpackagedPromise];
469
+ }
470
+ getPackagePropertyFromPackage(packageDirs, options) {
471
+ let foundByPackage = packageDirs.some((x) => x['package'] === options.package);
472
+ let foundById = packageDirs.some((x) => x['id'] === options.package);
473
+ if (foundByPackage && foundById) {
474
+ throw messages.createError('errorPackageAndIdCollision', []);
475
+ }
476
+ // didn't find anything? let's see if we can reverse look up
477
+ if (!foundByPackage && !foundById) {
478
+ // is it an alias?
479
+ const pkgId = pkgUtils.getPackageIdFromAlias(options.package, this.project);
480
+ if (pkgId === options.package) {
481
+ // not an alias, or not a valid one, try to reverse lookup an alias in case this is an id
482
+ const aliases = pkgUtils.getPackageAliasesFromId(options.package, this.project);
483
+ // if we found an alias, try to look that up in the config.
484
+ foundByPackage = aliases.some((alias) => packageDirs.find((x) => x['package'] === alias));
485
+ }
486
+ else {
487
+ // it is an alias; try to lookup it's id in the config
488
+ foundByPackage = packageDirs.some((x) => x['package'] === pkgId);
489
+ foundById = packageDirs.some((x) => x['id'] === pkgId);
490
+ if (!foundByPackage && !foundById) {
491
+ // check if any configs use a different alias to that same id
492
+ const aliases = pkgUtils.getPackageAliasesFromId(pkgId, this.project);
493
+ foundByPackage = aliases.some((alias) => {
494
+ const pd = packageDirs.find((x) => x['package'] === alias);
495
+ if (pd) {
496
+ // if so, set this.options.package.flags.package to be this alias instead of the alternate
497
+ options.package = alias;
498
+ }
499
+ return pd;
500
+ });
501
+ }
502
+ }
503
+ // if we still didn't find anything, throw the error
504
+ if (!foundByPackage && !foundById) {
505
+ throw messages.createError('errorMissingPackage', [pkgId]);
506
+ }
507
+ }
508
+ return foundByPackage ? 'package' : 'id';
509
+ }
510
+ getPackageValuePropertyFromDirectory(directoryFlag, options) {
511
+ const packageValue = this.getConfigPackageDirectoriesValue(this.project.getPackageDirectories(), 'package', 'path', options.path, directoryFlag, options);
512
+ const packageIdValue = this.getConfigPackageDirectoriesValue(this.project.getPackageDirectories(), 'id', 'path', options.path, directoryFlag, options);
513
+ let packagePropVal;
514
+ if (!packageValue && !packageIdValue) {
515
+ throw messages.createError('errorMissingPackage', []);
516
+ }
517
+ else if (packageValue && packageIdValue) {
518
+ throw messages.createError('errorPackageAndIdCollision', []);
519
+ }
520
+ else if (packageValue) {
521
+ packagePropVal = {
522
+ packageProperty: 'package',
523
+ packageValue,
524
+ };
525
+ }
526
+ else {
527
+ packagePropVal = {
528
+ packageProperty: 'id',
529
+ packageValue: packageIdValue,
530
+ };
531
+ }
532
+ return packagePropVal;
533
+ }
534
+ /**
535
+ * Returns the property value that corresponds to the propertyToLookup. This value found for a particular
536
+ * package directory element that matches the knownProperty and knownValue. In other words, we locate a package
537
+ * directory element whose knownProperty matches the knownValue, then we grab the value for the propertyToLookup
538
+ * and return it.
539
+ *
540
+ * @param packageDirs The list of all the package directories from the sfdx-project.json
541
+ * @param propertyToLookup The property ID whose value we want to find
542
+ * @param knownProperty The JSON property in the packageDirectories that is already known
543
+ * @param knownValue The value that corresponds to the knownProperty in the packageDirectories JSON
544
+ * @param knownFlag The flag details e.g. short/long name, etc. Only used for the error message
545
+ * @param options
546
+ */
547
+ getConfigPackageDirectoriesValue(packageDirs, propertyToLookup, knownProperty, knownValue, knownFlag, options
548
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
549
+ ) {
550
+ let value;
551
+ let packageDir = packageDirs.find((x) => x[knownProperty] === knownValue);
552
+ if (!packageDir && knownFlag === 'path' && knownValue.endsWith(path.sep)) {
553
+ // if this is the directory flag, try removing the trailing slash added by CLI auto-complete
554
+ const dirWithoutTrailingSlash = knownValue.slice(0, -1);
555
+ packageDir = packageDirs.find((x) => x[knownProperty] === dirWithoutTrailingSlash);
556
+ if (packageDir) {
557
+ // TODO: how to deal with this side effect?
558
+ options.path = dirWithoutTrailingSlash;
559
+ }
560
+ }
561
+ // didn't find it with the package property, try a reverse lookup with alias and id
562
+ if (!packageDir && knownProperty === 'package') {
563
+ const pkgId = pkgUtils.getPackageIdFromAlias(knownValue, this.project);
564
+ if (pkgId !== knownValue) {
565
+ packageDir = packageDirs.find((x) => x[knownProperty] === pkgId);
566
+ }
567
+ else {
568
+ const aliases = pkgUtils.getPackageAliasesFromId(knownValue, this.project);
569
+ aliases.some((alias) => {
570
+ packageDir = packageDirs.find((x) => x[knownProperty] === alias);
571
+ return packageDir;
572
+ });
573
+ }
574
+ }
575
+ if (packageDir) {
576
+ value = packageDir[propertyToLookup];
577
+ }
578
+ else {
579
+ throw messages.createError('errorNoMatchingPackageDirectory', [`--${knownFlag}`, knownValue, knownProperty]);
580
+ }
581
+ return value;
582
+ }
583
+ // eslint-disable-next-line complexity
584
+ async packageVersionCreate(options) {
585
+ var _a;
586
+ let pollInterval = kit_1.Duration.seconds(pkgUtils.POLL_INTERVAL_SECONDS);
587
+ let maxRetries = 0;
588
+ if (options.wait.milliseconds > 0) {
589
+ if (options.skipvalidation === true) {
590
+ pollInterval = kit_1.Duration.seconds(POLL_INTERVAL_WITHOUT_VALIDATION_SECONDS);
591
+ }
592
+ maxRetries = (60 / pollInterval.seconds) * options.wait.seconds;
593
+ }
594
+ // For the first rollout of validating sfdx-project.json data against schema, make it optional and defaulted
595
+ // to false. Validation only occurs if the hidden -j (--validateschema) flag has been specified.
596
+ if (options.validateschema) {
597
+ await this.project.getSfProjectJson().schemaValidate();
598
+ }
599
+ // Check for empty packageDirectories
600
+ if (((_a = this.project.getSfProjectJson().getContents().packageDirectories) === null || _a === void 0 ? void 0 : _a.length) === 0) {
601
+ throw messages.createError('errorEmptyPackageDirs');
602
+ }
603
+ const canonicalPackageProperty = this.resolveCanonicalPackageProperty(options);
604
+ const resolvedPackageId = pkgUtils.getPackageIdFromAlias(options.package, this.project);
605
+ // At this point, the packageIdFromAlias should have been resolved to an Id. Now, we
606
+ // need to validate that the Id is correct.
607
+ pkgUtils.validateId(pkgUtils.BY_LABEL.PACKAGE_ID, resolvedPackageId);
608
+ await this.validateFlagsForPackageType(resolvedPackageId, options);
609
+ const versionNumberString = await this.validateVersionNumber(canonicalPackageProperty, resolvedPackageId, options);
610
+ try {
611
+ fs.statSync(path.join(process.cwd(), options.path));
612
+ }
613
+ catch (err) {
614
+ throw new Error(`Directory '${options.path}' does not exist`);
615
+ }
616
+ options.profileApi = this.resolveUserLicenses(canonicalPackageProperty, options);
617
+ [pollInterval, maxRetries] = await this.resolveOrgDependentPollingTime(resolvedPackageId, options, pollInterval, maxRetries);
618
+ const request = await this.createPackageVersionCreateRequestFromOptions(options, resolvedPackageId, versionNumberString);
619
+ const createResult = await this.connection.tooling.create('Package2VersionCreateRequest', request);
620
+ if (!createResult.success) {
621
+ const errStr = createResult.errors && createResult.errors.length ? createResult.errors.join(', ') : createResult.errors;
622
+ throw new Error(`Failed to create request${createResult.id ? ` [${createResult.id}]` : ''}: ${errStr}`);
623
+ }
624
+ let result;
625
+ if (options.wait && options.wait.milliseconds > 0) {
626
+ pollInterval = pollInterval !== null && pollInterval !== void 0 ? pollInterval : kit_1.Duration.seconds(options.wait.seconds / maxRetries);
627
+ if (pollInterval) {
628
+ result = await pkgUtils.pollForStatusWithInterval(createResult.id, maxRetries, resolvedPackageId, options.branch, this.project, this.connection, pollInterval);
629
+ }
630
+ }
631
+ else {
632
+ result = await this.listRequestById(createResult.id, this.connection);
633
+ }
634
+ return result;
635
+ }
636
+ // TODO: should be in pkg utils
637
+ resolveCanonicalPackageProperty(options) {
638
+ let canonicalPackageProperty;
639
+ if (!options.package) {
640
+ const packageValProp = this.getPackageValuePropertyFromDirectory(options.path, options);
641
+ options.package = packageValProp.packageValue;
642
+ canonicalPackageProperty = packageValProp.packageProperty;
643
+ }
644
+ else if (!options.path) {
645
+ canonicalPackageProperty = this.getPackagePropertyFromPackage(this.project.getPackageDirectories(), options);
646
+ options.path = this.getConfigPackageDirectoriesValue(this.project.getPackageDirectories(), 'path', canonicalPackageProperty, options.package, 'package', options);
647
+ }
648
+ else {
649
+ canonicalPackageProperty = this.getPackagePropertyFromPackage(this.project.getPackageDirectories(), options);
650
+ this.getConfigPackageDirectoriesValue(this.project.getPackageDirectories(), canonicalPackageProperty, 'path', options.path, 'path', options);
651
+ const expectedPackageId = this.getConfigPackageDirectoriesValue(this.packageDirs, canonicalPackageProperty, 'path', options.path, 'path', options);
652
+ // This will throw an error if the package id flag value doesn't match
653
+ // any of the :id values in the package dirs.
654
+ this.getConfigPackageDirectoriesValue(this.project.getPackageDirectories(), 'path', canonicalPackageProperty, options.package, 'package', options);
655
+ // This will throw an error if the package id flag value doesn't match
656
+ // the correct corresponding directory with that packageId.
657
+ if (options.package !== expectedPackageId) {
658
+ throw messages.createError('errorDirectoryIdMismatch', ['--path', options.path, '--package', options.package]);
659
+ }
660
+ }
661
+ return canonicalPackageProperty;
662
+ }
663
+ // TODO: should be in pkg utils
664
+ async validateVersionNumber(canonicalPackageProperty, resolvedPackageId, options) {
665
+ // validate the versionNumber flag value if specified, otherwise the descriptor value
666
+ const versionNumberString = options.versionnumber
667
+ ? options.versionnumber
668
+ : this.getConfigPackageDirectoriesValue(this.packageDirs, 'versionNumber', canonicalPackageProperty, options.package, 'package', options);
669
+ pkgUtils.validateVersionNumber(versionNumberString, versionNumber_1.BuildNumberToken.NEXT_BUILD_NUMBER_TOKEN, null);
670
+ await pkgUtils.validatePatchVersion(this.connection, versionNumberString, resolvedPackageId);
671
+ return versionNumberString;
672
+ }
673
+ resolveUserLicenses(canonicalPackageProperty, options) {
674
+ // Check for an includeProfileUserLiceneses flag in the packageDirectory
675
+ const includeProfileUserLicenses = this.getConfigPackageDirectoriesValue(this.packageDirs, 'includeProfileUserLicenses', canonicalPackageProperty, options.package, 'package', options);
676
+ if (includeProfileUserLicenses !== undefined &&
677
+ includeProfileUserLicenses !== true &&
678
+ includeProfileUserLicenses !== false) {
679
+ throw messages.createError('errorProfileUserLicensesInvalidValue', [includeProfileUserLicenses]);
680
+ }
681
+ const shouldGenerateProfileInformation = logger.shouldLog(core_1.LoggerLevel.INFO) || logger.shouldLog(core_1.LoggerLevel.DEBUG);
682
+ return new profileApi_1.ProfileApi(this.project, includeProfileUserLicenses, shouldGenerateProfileInformation);
683
+ }
684
+ async resolveOrgDependentPollingTime(resolvedPackageId, options, pollInterval, maxRetries) {
685
+ let pi = pollInterval;
686
+ let mr = maxRetries;
687
+ // If we are polling check to see if the package is Org-Dependent, if so, update the poll time
688
+ if (options.wait) {
689
+ const query = `SELECT IsOrgDependent FROM Package2 WHERE Id = '${resolvedPackageId}'`;
690
+ try {
691
+ const pkgQueryResult = await this.connection.singleRecordQuery(query, {
692
+ tooling: true,
693
+ });
694
+ if (pkgQueryResult.IsOrgDependent) {
695
+ pi = kit_1.Duration.seconds(POLL_INTERVAL_WITHOUT_VALIDATION_SECONDS);
696
+ mr = (60 / pollInterval.seconds) * options.wait.seconds;
697
+ }
698
+ }
699
+ catch {
700
+ // do nothing
701
+ }
702
+ }
703
+ return [pi, mr];
704
+ }
705
+ async validateFlagsForPackageType(packageId, options) {
706
+ const packageType = await pkgUtils.getPackage2Type(packageId, this.connection);
707
+ if (packageType === 'Unlocked') {
708
+ if (options.postinstallscript || options.uninstallscript) {
709
+ // migrate coreMessages to messages
710
+ throw messages.createError('version_create.errorScriptsNotApplicableToUnlockedPackage');
711
+ }
712
+ // Don't allow ancestor in unlocked packages
713
+ const packageDescriptorJson = this.getPackageDescriptorJsonFromPackageId(packageId, options);
714
+ const ancestorId = packageDescriptorJson.ancestorId;
715
+ const ancestorVersion = packageDescriptorJson.ancestorVersion;
716
+ if (ancestorId || ancestorVersion) {
717
+ throw messages.createError('version_create.errorAncestorNotApplicableToUnlockedPackage');
718
+ }
719
+ }
720
+ }
721
+ /**
722
+ * Cleans invalid attribute(s) from the packageDescriptorJSON
723
+ */
724
+ cleanPackageDescriptorJson(packageDescriptorJson) {
725
+ if (typeof packageDescriptorJson.default !== 'undefined') {
726
+ delete packageDescriptorJson.default; // for client-side use only, not needed
727
+ }
728
+ if (typeof packageDescriptorJson.includeProfileUserLicenses !== 'undefined') {
729
+ delete packageDescriptorJson.includeProfileUserLicenses; // for client-side use only, not needed
730
+ }
731
+ if (typeof packageDescriptorJson.unpackagedMetadata !== 'undefined') {
732
+ delete packageDescriptorJson.unpackagedMetadata; // for client-side use only, not needed
733
+ }
734
+ if (typeof packageDescriptorJson.branch !== 'undefined') {
735
+ delete packageDescriptorJson.branch; // for client-side use only, not needed
736
+ }
737
+ }
738
+ /**
739
+ * Sets default or override values for packageDescriptorJSON attribs
740
+ */
741
+ setPackageDescriptorJsonValues(packageDescriptorJson, options) {
742
+ if (options.versionname) {
743
+ packageDescriptorJson.versionName = options.versionname;
744
+ }
745
+ if (options.versiondescription) {
746
+ packageDescriptorJson.versionDescription = options.versiondescription;
747
+ }
748
+ if (options.versionnumber) {
749
+ packageDescriptorJson.versionNumber = options.versionnumber;
750
+ }
751
+ // default versionName to versionNumber if unset, stripping .NEXT if present
752
+ if (!packageDescriptorJson.versionName) {
753
+ const versionNumber = packageDescriptorJson.versionNumber;
754
+ packageDescriptorJson.versionName =
755
+ versionNumber.split(pkgUtils.VERSION_NUMBER_SEP)[3] === versionNumber_1.BuildNumberToken.NEXT_BUILD_NUMBER_TOKEN
756
+ ? versionNumber.substring(0, versionNumber.indexOf(pkgUtils.VERSION_NUMBER_SEP + versionNumber_1.BuildNumberToken.NEXT_BUILD_NUMBER_TOKEN))
757
+ : versionNumber;
758
+ logger.warn(options, messages.getMessage('defaultVersionName', [packageDescriptorJson.versionName]));
759
+ }
760
+ if (options.releasenotesurl) {
761
+ packageDescriptorJson.releaseNotesUrl = options.releasenotesurl;
762
+ }
763
+ if (packageDescriptorJson.releaseNotesUrl && !pkgUtils.validUrl(packageDescriptorJson.releaseNotesUrl)) {
764
+ throw new Error(messages.getMessage('malformedUrl', ['releaseNotesUrl', packageDescriptorJson.releaseNotesUrl]));
765
+ }
766
+ if (options.postinstallurl) {
767
+ packageDescriptorJson.postInstallUrl = options.postinstallurl;
768
+ }
769
+ if (packageDescriptorJson.postInstallUrl && !pkgUtils.validUrl(packageDescriptorJson.postInstallUrl)) {
770
+ throw new Error(messages.getMessage('malformedUrl', ['postInstallUrl', packageDescriptorJson.postInstallUrl]));
771
+ }
772
+ if (options.postinstallscript) {
773
+ packageDescriptorJson.postInstallScript = options.postinstallscript;
774
+ }
775
+ if (options.uninstallscript) {
776
+ packageDescriptorJson.uninstallScript = options.uninstallscript;
777
+ }
778
+ }
779
+ }
780
+ exports.PackageVersionCreate = PackageVersionCreate;
781
+ //# sourceMappingURL=packageVersionCreate.js.map