UNPKG

@salesforce/packaging

Version:

Packaging library for the Salesforce packaging platform

351 lines 19.5 kB
"use strict"; /* * Copyright 2026, Salesforce, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.findOrCreatePackage2 = findOrCreatePackage2; exports.convertPackage = convertPackage; exports.createPackageVersionCreateRequest = createPackageVersionCreateRequest; const node_path_1 = __importDefault(require("node:path")); const node_os_1 = __importDefault(require("node:os")); const node_fs_1 = __importDefault(require("node:fs")); const core_1 = require("@salesforce/core"); const kit_1 = require("@salesforce/kit"); const project_1 = require("@salesforce/core/project"); const pkgUtils = __importStar(require("../utils/packageUtils")); const packageUtils_1 = require("../utils/packageUtils"); const interfaces_1 = require("../interfaces"); const pvcr = __importStar(require("./packageVersionCreateRequest")); const packageVersionCreateRequest_1 = require("./packageVersionCreateRequest"); const packageVersionCreate_1 = require("./packageVersionCreate"); var Package2VersionStatus = interfaces_1.PackagingSObjects.Package2VersionStatus; core_1.Messages.importMessagesDirectory(__dirname); const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'package_version_create'); const POLL_INTERVAL_SECONDS = 30; async function findOrCreatePackage2(seedPackage, connection, project) { const query = `SELECT Id, Name FROM Package2 WHERE ConvertedFromPackageId = '${seedPackage}'`; const queryResult = (await connection.tooling.query(query)).records; if (queryResult?.length > 1) { const ids = queryResult.map((r) => r.Id); throw messages.createError('errorMoreThanOnePackage2WithSeed', [ids.join(', ')]); } if (queryResult?.length === 1) { // return the package2 object if (project) { await addPackageAlias(project, queryResult[0].Name, queryResult[0].Id); } return queryResult[0].Id; } // Need to create a new Package2 const subQuery = `SELECT Name, Description, NamespacePrefix FROM SubscriberPackage WHERE Id = '${seedPackage}'`; let subscriberResult; try { subscriberResult = await connection.singleRecordQuery(subQuery, { tooling: true, }); } catch (e) { throw messages.createError('errorNoSubscriberPackageRecord', [seedPackage]); } const request = { Name: subscriberResult.Name, Description: subscriberResult.Description, NamespacePrefix: subscriberResult.NamespacePrefix, ContainerOptions: 'Managed', ConvertedFromPackageId: seedPackage, }; const createResult = await connection.tooling.create('Package2', request); if (!createResult.success) { throw pkgUtils.combineSaveErrors('Package2', 'create', createResult.errors); } if (project) { await addPackageAlias(project, subscriberResult.Name, createResult.id); } return createResult.id; } async function convertPackage(pkg, connection, options, project) { const maxRetries = options.wait ? (60 / POLL_INTERVAL_SECONDS) * options.wait.minutes : 0; const branch = 'main'; const packageId = await findOrCreatePackage2(pkg, connection, project); const apiVersion = project?.getSfProjectJson()?.get('sourceApiVersion'); const request = await createPackageVersionCreateRequest({ installationkey: options.installationKey, definitionfile: options.definitionfile, buildinstance: options.buildInstance, seedmetadata: options.seedMetadata, patchversion: options.patchversion, codecoverage: options.codecoverage, }, packageId, // TODO: createPackageVersionCreateRequest requires apiVersion exist. // UT fail if we validate that it exists (there might not even be a project) apiVersion, project); // TODO: a lot of this is duplicated from PC, PVC, and PVCR. const createResult = await connection.tooling.create('Package2VersionCreateRequest', request); if (!createResult.success) { const errStr = createResult?.errors.length ? createResult.errors.join(', ') : createResult.errors; const id = createResult.id ?? ''; throw messages.createError('failedToCreatePVCRequest', [id, errStr.toString()]); } let results; if (options.wait) { try { results = await pollForStatusWithInterval(createResult.id, maxRetries, packageId, branch, project, connection, options.frequency ?? kit_1.Duration.seconds(POLL_INTERVAL_SECONDS)); } catch (e) { if (e instanceof core_1.SfError) { const err = e; if (err.name === 'PollingClientTimeout') { err.setData({ VersionCreateRequestId: createResult.id }); err.message += ` Run 'sf package version create report -i ${createResult.id}' to check the status.`; } throw pkgUtils.applyErrorAction(pkgUtils.massageErrorMessage(err)); } throw e; } } else { results = await (0, packageVersionCreateRequest_1.byId)(packageId, connection); } return Array.isArray(results) ? results[0] : results; } /** * Convert the list of command line options to a JSON object that can be used to create an Package2VersionCreateRequest entity. * * @param context: command context * @param packageId: package2 id to create a package version for * @returns {{Package2Id: string, Package2VersionMetadata: *, Tag: *, Branch: number}} * @private */ async function createPackageVersionCreateRequest(context, packageId, apiVersion, project) { const uniqueId = (0, packageUtils_1.uniqid)({ template: `${packageId}-%s` }); const packageVersTmpRoot = node_path_1.default.join(node_os_1.default.tmpdir(), uniqueId); const packageVersMetadataFolder = node_path_1.default.join(packageVersTmpRoot, 'md-files'); const seedMetadataFolder = node_path_1.default.join(packageVersTmpRoot, 'seed-md-files'); const unpackagedMetadataFolder = node_path_1.default.join(packageVersTmpRoot, 'unpackaged-md-files'); const packageVersBlobDirectory = node_path_1.default.join(packageVersTmpRoot, 'package-version-info'); const seedMetadataZipFile = node_path_1.default.join(packageVersBlobDirectory, 'seed-metadata-package.zip'); const unpackagedMetadataZipFile = node_path_1.default.join(packageVersBlobDirectory, 'unpackaged-metadata-package.zip'); const settingsZipFile = node_path_1.default.join(packageVersBlobDirectory, 'settings.zip'); const metadataZipFile = node_path_1.default.join(packageVersBlobDirectory, 'package.zip'); const packageVersBlobZipFile = node_path_1.default.join(packageVersTmpRoot, 'package-version-info.zip'); const packageObject = project ? (0, packageUtils_1.findPackageDirectory)(project, packageId) : undefined; let packageDescriptorJson = buildPackageDescriptorJson({ packageId, base: { versionNumber: context.patchversion }, packageObject, }); const settingsGenerator = new core_1.ScratchOrgSettingsGenerator({ asDirectory: true }); const definitionFile = context.definitionfile; let definitionFileJson; if (definitionFile) { if (!node_fs_1.default.existsSync(definitionFile)) { throw messages.createError('errorReadingDefintionFile', [definitionFile]); } const definitionFilePayload = await node_fs_1.default.promises.readFile(definitionFile, 'utf8'); definitionFileJson = JSON.parse(definitionFilePayload); // Load any settings from the definition await settingsGenerator.extract(definitionFileJson); if (settingsGenerator.hasSettings() && definitionFileJson.orgPreferences) { // this is not allowed, exit with an error throw messages.createError('signupDuplicateSettingsSpecified'); } packageDescriptorJson = (0, packageUtils_1.copyDescriptorProperties)(packageDescriptorJson, definitionFileJson); } await node_fs_1.default.promises.mkdir(packageVersTmpRoot, { recursive: true }); await node_fs_1.default.promises.mkdir(packageVersBlobDirectory, { recursive: true }); await node_fs_1.default.promises.mkdir(packageVersMetadataFolder, { recursive: true }); const seedMetadataPath = context.seedmetadata ?? packageDescriptorJson.seedMetadata?.path; const hasSeedMetadata = await new packageVersionCreate_1.MetadataResolver().resolveMetadata(seedMetadataPath, seedMetadataFolder, 'seedMDDirectoryDoesNotExist', apiVersion); if (context.codecoverage) { const unpackagedMetadataPath = packageDescriptorJson.unpackagedMetadata?.path; const hasUnpackaged = await new packageVersionCreate_1.MetadataResolver().resolveMetadata(unpackagedMetadataPath, unpackagedMetadataFolder, 'unpackagedMDDirectoryDoesNotExist', apiVersion); if (hasUnpackaged) { core_1.Logger.childFromRoot('packageConvert').debug(`Including unpackaged metadata found in '${unpackagedMetadataPath ?? '<undefined unpackagedmetadata>'}'.`); await pkgUtils.zipDir(unpackagedMetadataFolder, unpackagedMetadataZipFile); } } if (hasSeedMetadata) { // Zip the seedMetadataFolder folder and put the zip in {packageVersBlobDirectory}/{seedMetadataZipFile} core_1.Logger.childFromRoot('packageConvert:pollForStatusWithInterval').debug(`Including metadata found in '${seedMetadataPath ?? '<undefined seedmetadata>'}'.`); await pkgUtils.zipDir(seedMetadataFolder, seedMetadataZipFile); } await settingsGenerator.createDeploy(); await settingsGenerator.createDeployPackageContents(apiVersion); await pkgUtils.zipDir(`${settingsGenerator.getDestinationPath() ?? ''}${node_path_1.default.sep}${settingsGenerator.getShapeDirName()}`, settingsZipFile); const shapeDirectory = `${settingsGenerator.getDestinationPath() ?? ''}${node_path_1.default.sep}${settingsGenerator.getShapeDirName()}`; const currentPackageXml = await node_fs_1.default.promises.readFile(node_path_1.default.join(shapeDirectory, 'package.xml'), 'utf8'); await node_fs_1.default.promises.writeFile(node_path_1.default.join(packageVersMetadataFolder, 'package.xml'), currentPackageXml, 'utf-8'); // Zip the packageVersMetadataFolder folder and put the zip in {packageVersBlobDirectory}/package.zip await pkgUtils.zipDir(packageVersMetadataFolder, metadataZipFile); packageDescriptorJson = (0, packageUtils_1.resolveBuildUserPermissions)(packageDescriptorJson, context.codecoverage ?? false); packageDescriptorJson = (0, packageUtils_1.cleanPackageDescriptorJson)(packageDescriptorJson); await node_fs_1.default.promises.writeFile(node_path_1.default.join(packageVersBlobDirectory, 'package2-descriptor.json'), JSON.stringify(packageDescriptorJson, undefined, 2)); // Zip the Version Info and package.zip files into another zip await pkgUtils.zipDir(packageVersBlobDirectory, packageVersBlobZipFile); return createRequestObject(packageId, context, packageVersTmpRoot, packageVersBlobZipFile); } function buildPackageDescriptorJson(args) { const { packageId, base, packageObject } = args; const descriptor = { id: packageId, ...(base ?? {}), }; if (packageObject && (0, project_1.isPackagingDirectory)(packageObject)) { const allowedKeys = ['apexTestAccess', 'seedMetadata', 'unpackagedMetadata']; for (const key of allowedKeys) { if (Object.prototype.hasOwnProperty.call(packageObject, key)) { const value = packageObject[key]; if (value !== undefined) { descriptor[key] = value; } } } } return descriptor; } async function createRequestObject(packageId, options, packageVersTmpRoot, packageVersBlobZipFile) { const zipFileBase64 = (await node_fs_1.default.promises.readFile(packageVersBlobZipFile)).toString('base64'); const requestObject = { Package2Id: packageId, VersionInfo: zipFileBase64, InstallKey: options.installationkey, Instance: options.buildinstance, IsConversionRequest: true, CalculateCodeCoverage: options.codecoverage ?? false, }; await node_fs_1.default.promises.rm(packageVersTmpRoot, { recursive: true }); return requestObject; } async function pollForStatusWithInterval(id, retries, packageId, branch, project, connection, interval) { const logger = core_1.Logger.childFromRoot('packageConvert:pollForStatusWithInterval'); let remainingRetries = retries; const pollingClient = await core_1.PollingClient.create({ poll: async () => { const results = await pvcr.byId(id, connection); if (isStatusEqualTo(results, [Package2VersionStatus.success, Package2VersionStatus.error])) { // complete if (isStatusEqualTo(results, [Package2VersionStatus.success])) { // update sfdx-project.json let projectUpdated = false; if (project && !kit_1.env.getBoolean('SF_PROJECT_AUTOUPDATE_DISABLE_FOR_PACKAGE_VERSION_CREATE')) { projectUpdated = true; const query = `SELECT MajorVersion, MinorVersion, PatchVersion, BuildNumber FROM Package2Version WHERE Id = '${results[0].Package2VersionId}'`; const packageVersionVersionString = await connection.tooling .query(query) .then((pkgQueryResult) => { const record = pkgQueryResult.records[0]; return `${record.MajorVersion}.${record.MinorVersion}.${record.PatchVersion}-${record.BuildNumber}`; }); if (!results[0]?.SubscriberPackageVersionId) { throw new core_1.SfError('No SubscriberPackageVersionId found'); } const [alias, writtenId] = await (0, packageUtils_1.generatePackageAliasEntry)(connection, project, results[0].SubscriberPackageVersionId, packageVersionVersionString, branch, packageId); project.getSfProjectJson().addPackageAlias(alias, writtenId); await project.getSfProjectJson().write(); } await core_1.Lifecycle.getInstance().emit(interfaces_1.PackageEvents.convert.success, { id, packageVersionCreateRequestResult: results[0], projectUpdated, }); return { completed: true, payload: results[0] }; } else { let status = 'Unknown Error'; if (results?.length > 0 && results[0].Error.length > 0) { const errors = []; // for multiple errors, display one per line prefixed with (x) if (results[0].Error.length > 1) { results[0].Error.forEach((error) => { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions errors.push(`(${errors.length + 1}) ${error}`); }); errors.unshift(messages.getMessage('versionCreateFailedWithMultipleErrors')); } status = errors.length !== 0 ? errors.join('\n') : results[0].Error.join('\n'); } await core_1.Lifecycle.getInstance().emit(interfaces_1.PackageEvents.convert.error, { id, status }); throw new core_1.SfError(status); } } else { const remainingTime = kit_1.Duration.seconds(interval.seconds * remainingRetries); await core_1.Lifecycle.getInstance().emit(interfaces_1.PackageEvents.convert.progress, { id, packageVersionCreateRequestResult: results[0], message: '', timeRemaining: remainingTime, }); logger.info(`Request in progress. Sleeping ${interval.seconds} seconds. Will wait a total of ${remainingTime.seconds} more seconds before timing out. Current Status='${(0, kit_1.camelCaseToTitleCase)(results[0]?.Status)}'`); remainingRetries--; return { completed: false, payload: results[0] }; } }, frequency: kit_1.Duration.seconds(interval.seconds), timeout: kit_1.Duration.seconds(interval.seconds * retries), }); return pollingClient.subscribe(); } async function addPackageAlias(project, packageName, packageId) { if (!kit_1.env.getBoolean('SF_PROJECT_AUTOUPDATE_DISABLE_FOR_PACKAGE_CREATE')) { project.getSfProjectJson().addPackageAlias(packageName, packageId); await project.getSfProjectJson().write(); } } /** * Return true if the queryResult.records[0].Status is equal to one of the values in statuses. * * @param results to examine * @param statuses array of statuses to look for * @returns {boolean} if one of the values in status is found. */ const isStatusEqualTo = (results, statuses = []) => (!results?.length ? false : statuses.some((status) => results[0].Status === status)); //# sourceMappingURL=packageConvert.js.map