diff --git a/README.md b/README.md index 22464168..941c3d18 100644 --- a/README.md +++ b/README.md @@ -518,7 +518,7 @@ Create a package version in the Dev Hub org. ``` USAGE $ sf package version create -v [--json] [--flags-dir ] [--api-version ] [-b ] [-c | - --skip-validation] [-f ] [-k ] [-x] [-p ] [-d ] [--post-install-script ] + --skip-validation | --async-validation] [-f ] [-k ] [-x] [-p ] [-d ] [--post-install-script ] [--post-install-url ] [--releasenotes-url ] [--skip-ancestor-check] [-t ] [--uninstall-script ] [-e ] [-a ] [-n ] [-w ] [--language ] [--verbose] @@ -554,6 +554,7 @@ FLAGS that isn’t the highest released package version. --skip-validation Skip validation during package version creation; you can’t promote unvalidated package versions. + --async-validation Return a new package version before completing package validations. --uninstall-script= Uninstall script name; applies to managed packages only. --verbose Display verbose command output. @@ -602,6 +603,9 @@ EXAMPLES $ sf package version create --path common --installation-key password123 --skip-validation + Create a package version and perform package validations asynchronously: + $ sf package version create --path common --installation-key password123 --async-validation + FLAG DESCRIPTIONS -c, --code-coverage @@ -652,6 +656,12 @@ FLAG DESCRIPTIONS versions. Skipping validation can suppress important errors that can surface at a later stage. You can specify skip validation or code coverage, but not both. Code coverage is calculated during validation. + --async-validation Return a new package version before completing package validations. + + Specifying async validation returns the package version earlier in the process, allowing you to install and test + the new version right away. If your development team is using continuous integration (CI) scripts, async validation + can reduce your overall CI run time. + --uninstall-script= Uninstall script name; applies to managed packages only. The uninstall script is an Apex class within this package that is run in the installing org after uninstallations of diff --git a/command-snapshot.json b/command-snapshot.json index ec872615..91947cc8 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -229,6 +229,7 @@ "releasenotesurl", "skipancestorcheck", "skipvalidation", + "asyncvalidation", "target-hub-org", "targetdevhubusername", "uninstallscript", @@ -258,6 +259,7 @@ "releasenotes-url", "skip-ancestor-check", "skip-validation", + "async-validation", "tag", "target-dev-hub", "uninstall-script", diff --git a/messages/package_version_create.md b/messages/package_version_create.md index 3f837ff9..d951c062 100644 --- a/messages/package_version_create.md +++ b/messages/package_version_create.md @@ -33,6 +33,10 @@ We don’t calculate code coverage for org-dependent unlocked packages, or for p <%= config.bin %> <%= command.id %> --path common --installation-key password123 --skip-validation +- Create a package version and perform package validations asynchronously: + + <%= config.bin %> <%= command.id %> --path common --installation-key password123 --async-validation + # flags.package.summary ID (starts with 0Ho) or alias of the package to create a version of. @@ -129,6 +133,14 @@ Skips validation of dependencies, package ancestors, and metadata during package Skipping validation suppresses errors that usually surface during package version creation. Instead, these errors surface at a later stage, such as installation or post-installation. If you encounter errors that are difficult to debug, retry package version creation without the --skip-validation parameter. +# flags.async-validation.summary + +Return a new package version before completing package validations. + +# flags.async-validation.description + +Specifying async validation returns the package version earlier in the process, allowing you to install and test the new version right away. If your development team is using continuous integration (CI) scripts, async validation can reduce your overall CI run time. + # flags.skip-ancestor-check.summary Overrides ancestry requirements, which allows you to specify a package ancestor that isn’t the highest released package version. @@ -199,6 +211,12 @@ Version create. %d minutes remaining until timeout. Create version status: %s +# packageVersionCreatePerformingValidations + +The validations for this package version are in progress, but you can now begin testing this package version. +To determine whether all package validations completed successfully, run sf package version create report and review the Async Validation Status. +Async validated package versions can be promoted only if all validations completed successfully. + # packageVersionCreateFinalStatus Create version status: %s diff --git a/package.json b/package.json index f64233f7..5a828ad9 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "bugs": "https://github.com/forcedotcom/cli/issues", "dependencies": { "@oclif/core": "^4", - "@salesforce/core": "^7.3.8", - "@salesforce/kit": "^3.1.0", - "@salesforce/packaging": "^3.5.16", + "@salesforce/core": "^7.4.1", + "@salesforce/kit": "^3.1.6", + "@salesforce/packaging": "^3.7.1", "@salesforce/sf-plugins-core": "^11.1.0", "chalk": "^5.3.0" }, diff --git a/schemas/package-convert.json b/schemas/package-convert.json index 88efb0e7..8ed88379 100644 --- a/schemas/package-convert.json +++ b/schemas/package-convert.json @@ -86,7 +86,8 @@ "VerifyingFeaturesAndSettings", "VerifyingDependencies", "VerifyingMetadata", - "FinalizingPackageVersion" + "FinalizingPackageVersion", + "PerformingValidations" ] } } diff --git a/schemas/package-version-create-list.json b/schemas/package-version-create-list.json index 068e7870..5c9f6452 100644 --- a/schemas/package-version-create-list.json +++ b/schemas/package-version-create-list.json @@ -92,7 +92,8 @@ "VerifyingFeaturesAndSettings", "VerifyingDependencies", "VerifyingMetadata", - "FinalizingPackageVersion" + "FinalizingPackageVersion", + "PerformingValidations" ] } } diff --git a/schemas/package-version-create-report.json b/schemas/package-version-create-report.json index e42c71d5..9de96e1a 100644 --- a/schemas/package-version-create-report.json +++ b/schemas/package-version-create-report.json @@ -92,7 +92,8 @@ "VerifyingFeaturesAndSettings", "VerifyingDependencies", "VerifyingMetadata", - "FinalizingPackageVersion" + "FinalizingPackageVersion", + "PerformingValidations" ] } } diff --git a/schemas/package-version-create.json b/schemas/package-version-create.json index 0e762ba5..4cb32315 100644 --- a/schemas/package-version-create.json +++ b/schemas/package-version-create.json @@ -68,7 +68,8 @@ "VerifyingFeaturesAndSettings", "VerifyingDependencies", "VerifyingMetadata", - "FinalizingPackageVersion" + "FinalizingPackageVersion", + "PerformingValidations" ] } } diff --git a/schemas/package-version-report.json b/schemas/package-version-report.json index 46fe3936..cd7f3775 100644 --- a/schemas/package-version-report.json +++ b/schemas/package-version-report.json @@ -125,6 +125,9 @@ "ValidationSkipped": { "type": "boolean" }, + "ValidatedAsync": { + "type": "boolean" + }, "Name": { "type": "string" }, diff --git a/src/commands/package/version/create.ts b/src/commands/package/version/create.ts index f61bf38d..57be74c5 100644 --- a/src/commands/package/version/create.ts +++ b/src/commands/package/version/create.ts @@ -121,7 +121,13 @@ export class PackageVersionCreateCommand extends SfCommand { - if (data.Status !== Package2VersionStatus.success && data.Status !== Package2VersionStatus.error) { + if (data.Status !== Package2VersionStatus.success && data.Status !== Package2VersionStatus.error && data.Status !== Package2VersionStatus.performingValidations + ) { const status = messages.getMessage('packageVersionCreateWaitingStatus', [ data.remainingWaitTime.minutes, data.Status, @@ -251,6 +258,18 @@ export class PackageVersionCreateCommand extends SfCommand `${os.EOL}(${i + 1}) ${e}`).join(''), ]); + case Package2VersionStatus.performingValidations: + this.log(messages.getMessage('packageVersionCreatePerformingValidations')); + this.log( + messages.getMessage(Package2VersionStatus.success, [ + result.Id, + result.SubscriberPackageVersionId, + INSTALL_URL_BASE.toString(), + result.SubscriberPackageVersionId, + this.config.bin, + ]) + ); + break; case Package2VersionStatus.success: this.log( messages.getMessage(result.Status, [ diff --git a/test/commands/package/packageVersion.nut.ts b/test/commands/package/packageVersion.nut.ts index d9280282..78bebf3c 100644 --- a/test/commands/package/packageVersion.nut.ts +++ b/test/commands/package/packageVersion.nut.ts @@ -80,6 +80,17 @@ describe('package:version:*', () => { expect(result).to.match(/Run "sfd?x? package:version:create:report -i 08c.{15}" to query for status\./); }); + it('should create a new package version with async-validation', () => { + const result = execCmd( + `package:version:create --package ${pkgName} -x --async-validation --version-description "Initial version"`, + { ensureExitCode: 0 } + ).shellOutput.stdout; + // eslint-disable-next-line no-console + console.log(result); + expect(result).to.include("Package version creation request status is '"); + expect(result).to.match(/Run "sfd?x? package:version:create:report -i 08c.{15}" to query for status\./); + }); + // package:version:create --wait --json is tested in versionPromoteUpdate.nut.ts it('should create a new package version and wait (human)', () => { const result = execCmd( diff --git a/test/commands/package/packageVersionCreate.test.ts b/test/commands/package/packageVersionCreate.test.ts index f66aac08..9430e077 100644 --- a/test/commands/package/packageVersionCreate.test.ts +++ b/test/commands/package/packageVersionCreate.test.ts @@ -102,6 +102,37 @@ describe('package:version:create - tests', () => { ]); }); + it('should create a new package version with async validation', async () => { + createStub = $$.SANDBOX.stub(PackageVersion, 'create'); + createStub.resolves(pkgVersionCreateSuccessResult); + const envSpy = $$.SANDBOX.spy(env, 'setBoolean').withArgs('SF_APPLY_REPLACEMENTS_ON_CONVERT', true); + + const cmd = new PackageVersionCreateCommand( + ['-p', '05i3i000000Gmj6XXX', '-v', 'test@hub.org', '-x', '--async-validation'], + config + ); + stubSpinner(cmd); + const res = await cmd.run(); + expect(envSpy.calledOnce).to.equal(true); + expect(res).to.deep.equal({ + Branch: undefined, + CreatedBy: '0053i000001ZIyGAAW', + CreatedDate: '2022-11-03 09:46', + Error: [], + HasMetadataRemoved: false, + Id: '08c3i000000fylgAAA', + Package2Id: '0Ho3i000000TNHYCA4', + Package2VersionId: '05i3i000000fxw1AAA', + Status: 'Success', + SubscriberPackageVersionId: '04t3i000002eya2AAA', + Tag: undefined, + }); + expect(logStub.callCount).to.equal(1); + expect(logStub.args[0]).to.deep.equal([ + `Successfully created the package version [08c3i000000fylgAAA]. Subscriber Package Version Id: 04t3i000002eya2AAA${os.EOL}Package Installation URL: https://login.salesforce.com/packaging/installPackage.apexp?p0=04t3i000002eya2AAA${os.EOL}As an alternative, you can use the "sf package:install" command.`, + ]); + }); + it('should report multiple errors', async () => { createStub = $$.SANDBOX.stub(PackageVersion, 'create'); createStub.resolves(pkgVersionCreateErrorResult); diff --git a/test/commands/package/packageVersionCreateReport.test.ts b/test/commands/package/packageVersionCreateReport.test.ts index be824354..368e9c03 100644 --- a/test/commands/package/packageVersionCreateReport.test.ts +++ b/test/commands/package/packageVersionCreateReport.test.ts @@ -63,6 +63,23 @@ const pkgVersionCreateSuccessResult: PackageVersionCreateRequestResult = { CreatedBy: '0053i000001ZIyGAAW', }; +const pkgVersionCreateSuccessResultAsyncValidation: PackageVersionCreateRequestResult = { + Id: '08c3i000000fylgAAA', + Status: Package2VersionStatus.success, + Package2Id: '0Ho3i000000TNHYCA4', + Package2VersionId: '05i3i000000fxw1AAA', + SubscriberPackageVersionId: '04t3i000002eya2AAA', + // @ts-ignore + Tag: null, + // @ts-ignore + Branch: null, + Error: [], + CreatedDate: '2022-11-03 09:46', + HasMetadataRemoved: false, + CreatedBy: '0053i000001ZIyGAAW', + ValidatedAsync: true, +}; + describe('package:version:create:report - tests', () => { const $$ = new TestContext(); const testOrg = new MockTestOrgData(); @@ -116,6 +133,34 @@ describe('package:version:create:report - tests', () => { expect(styledHeaderStub.callCount).to.equal(1); }); + it('should report on a new package version with async validation', async () => { + createStatusStub = $$.SANDBOX.stub(PackageVersion, 'getCreateStatus'); + createStatusStub.resolves(pkgVersionCreateSuccessResultAsyncValidation); + const res = await new PackageVersionCreateReportCommand( + ['-i', '08c3i000000fyoVAAQ', '-v', 'test@hub.org'], + config + ).run(); + + expect(res).to.deep.equal([ + { + Branch: null, + CreatedBy: '0053i000001ZIyGAAW', + CreatedDate: '2022-11-03 09:46', + Error: [], + HasMetadataRemoved: false, + Id: '08c3i000000fylgAAA', + Package2Id: '0Ho3i000000TNHYCA4', + Package2VersionId: '05i3i000000fxw1AAA', + Status: 'Success', + SubscriberPackageVersionId: '04t3i000002eya2AAA', + Tag: null, + ValidatedAsync: true, + }, + ]); + expect(tableStub.callCount).to.equal(1); + expect(styledHeaderStub.callCount).to.equal(1); + }); + it('should report multiple errors', async () => { createStatusStub = $$.SANDBOX.stub(PackageVersion, 'getCreateStatus'); createStatusStub.resolves(pkgVersionCreateErrorResult); diff --git a/yarn.lock b/yarn.lock index e0cf8637..6002c49a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1478,7 +1478,7 @@ strip-ansi "6.0.1" ts-retry-promise "^0.8.0" -"@salesforce/core@^7.0.0", "@salesforce/core@^7.3.1", "@salesforce/core@^7.3.5", "@salesforce/core@^7.3.6", "@salesforce/core@^7.3.8", "@salesforce/core@^7.4.1": +"@salesforce/core@^7.0.0", "@salesforce/core@^7.3.1", "@salesforce/core@^7.3.12", "@salesforce/core@^7.3.6", "@salesforce/core@^7.4.1": version "7.4.1" resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-7.4.1.tgz#7c37623f6a89c199bf12cd6dc28d89bc950914ef" integrity sha512-ccYs7uL4GYjdOcc44trfRnaz69kG0jU0aoT0qjPkIel8oVOyEoXaoDCG0A+2diqmicDp5uWK0pNs+tdWNj2mcQ== @@ -1546,19 +1546,19 @@ dependencies: "@salesforce/ts-types" "^2.0.10" -"@salesforce/packaging@^3.5.16": - version "3.5.16" - resolved "https://registry.yarnpkg.com/@salesforce/packaging/-/packaging-3.5.16.tgz#24ca1c0c15b8416f038de5ae136a2ede396c02bf" - integrity sha512-9dQoO7RSXPEfjZkZBQg3eqNUH8lJwc4voXkVFILZpeqbPVp2z20fLBppsiuVjpDG+AQl7boKGcUr8zjJAiOk0g== +"@salesforce/packaging@^3.7.1": + version "3.7.1" + resolved "https://registry.yarnpkg.com/@salesforce/packaging/-/packaging-3.7.1.tgz#079745eceef4cee98a23792b39c1f3a962ef8669" + integrity sha512-MbY8bh5pXgjO8XyVW1zAeXrmIVl3vJZMeicnzKMJ8H91CWMlkAVAorvAlQf23VcJXapXzr3PvfzWNwQy1r4xLw== dependencies: "@jsforce/jsforce-node" "^3.2.0" - "@salesforce/core" "^7.3.6" - "@salesforce/kit" "^3.1.1" - "@salesforce/schemas" "^1.7.0" - "@salesforce/source-deploy-retrieve" "^11.4.3" + "@salesforce/core" "^7.4.1" + "@salesforce/kit" "^3.1.6" + "@salesforce/schemas" "^1.9.0" + "@salesforce/source-deploy-retrieve" "^11.6.5" "@salesforce/ts-types" "^2.0.9" "@salesforce/types" "^1.1.0" - fast-xml-parser "^4.3.6" + fast-xml-parser "^4.4.0" globby "^11" graphology "^0.25.4" graphology-traversal "^0.3.1" @@ -1585,7 +1585,7 @@ resolved "https://registry.yarnpkg.com/@salesforce/prettier-config/-/prettier-config-0.0.3.tgz#ba648d4886bb38adabe073dbea0b3a91b3753bb0" integrity sha512-hYOhoPTCSYMDYn+U1rlEk16PoBeAJPkrdg4/UtAzupM1mRRJOwEPMG1d7U8DxJFKuXW3DMEYWr2MwAIBDaHmFg== -"@salesforce/schemas@^1.7.0", "@salesforce/schemas@^1.9.0": +"@salesforce/schemas@^1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@salesforce/schemas/-/schemas-1.9.0.tgz#ba477a112653a20b4edcf989c61c57bdff9aa3ca" integrity sha512-LiN37zG5ODT6z70sL1fxF7BQwtCX9JOWofSU8iliSNIM+WDEeinnoFtVqPInRSNt8I0RiJxIKCrqstsmQRBNvA== @@ -1621,12 +1621,12 @@ "@salesforce/ts-types" "^2.0.9" chalk "^5.3.0" -"@salesforce/source-deploy-retrieve@^11.4.3": - version "11.4.3" - resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-11.4.3.tgz#57ff91f5f0021804b268002362a695489f34ac5a" - integrity sha512-/WFSqf+yTO3kNM41r/duVEHGH2Eq18lavX/o/yA0hJEmxAYBV7Olv6JIMnbly8hZQo7LoX/nE0LnNK3JYphZQQ== +"@salesforce/source-deploy-retrieve@^11.6.5": + version "11.6.8" + resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-11.6.8.tgz#c50e2dbf921048451ee614bb4cb6089c8799b756" + integrity sha512-Z3/ehMn3ioUW50rVGcMIw+X6wqcD9Pgab626AxmGwjfPXeZhesuiV/qSuK7Y+yLmNmdNLoXbK10JsC28v1ObTA== dependencies: - "@salesforce/core" "^7.3.5" + "@salesforce/core" "^7.3.12" "@salesforce/kit" "^3.1.1" "@salesforce/ts-types" "^2.0.9" fast-levenshtein "^3.0.0" @@ -3988,10 +3988,10 @@ fast-xml-parser@4.2.5: dependencies: strnum "^1.0.5" -fast-xml-parser@^4.3.6: - version "4.3.6" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.3.6.tgz#190f9d99097f0c8f2d3a0e681a10404afca052ff" - integrity sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw== +fast-xml-parser@^4.3.6, fast-xml-parser@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.0.tgz#341cc98de71e9ba9e651a67f41f1752d1441a501" + integrity sha512-kLY3jFlwIYwBNDojclKsNAC12sfD6NwW74QB2CoNGPvtVxjliYehVunB3HYyNi+n4Tt1dAcgwYvmKF/Z18flqg== dependencies: strnum "^1.0.5"