diff --git a/.images/vscodeScreenshot.png b/.images/vscodeScreenshot.png deleted file mode 100644 index 4b7206a47..000000000 Binary files a/.images/vscodeScreenshot.png and /dev/null differ diff --git a/.vscode/launch.json b/.vscode/launch.json index 40e2598a1..69722db67 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,8 @@ "request": "attach", "name": "Attach", "port": 9229, - "skipFiles": ["/**"] + "skipFiles": ["/**"], + "continueOnAttach": true }, { "name": "Run All Tests", diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ad2003ca..d6941253e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,24 +4,21 @@ All notable changes to this project will be documented in this file. See [standa ## [1.4.0](https://github.com/salesforcecli/plugin-source/compare/v1.3.1...v1.4.0) (2021-11-09) - ### Features -* listmetadata and describemetadata ([b00a59a](https://github.com/salesforcecli/plugin-source/commit/b00a59a3c06cd81b121c306b6b9af3ee581a61ad)) +- listmetadata and describemetadata ([b00a59a](https://github.com/salesforcecli/plugin-source/commit/b00a59a3c06cd81b121c306b6b9af3ee581a61ad)) ### [1.3.1](https://github.com/salesforcecli/plugin-source/compare/v1.3.0...v1.3.1) (2021-10-28) - ### Bug Fixes -* bump SDR to 5.1.1 ([#266](https://github.com/salesforcecli/plugin-source/issues/266)) ([ff6a4f7](https://github.com/salesforcecli/plugin-source/commit/ff6a4f74401668faec6404f941ce5cf73da426ff)) +- bump SDR to 5.1.1 ([#266](https://github.com/salesforcecli/plugin-source/issues/266)) ([ff6a4f7](https://github.com/salesforcecli/plugin-source/commit/ff6a4f74401668faec6404f941ce5cf73da426ff)) ## [1.3.0](https://github.com/salesforcecli/plugin-source/compare/v1.2.6...v1.3.0) (2021-10-28) - ### Features -* source tracking beta commands ([b871774](https://github.com/salesforcecli/plugin-source/commit/b87177498c184580db7e3bd81f164ddc77e6de0b)), closes [#253](https://github.com/salesforcecli/plugin-source/issues/253) [#251](https://github.com/salesforcecli/plugin-source/issues/251) [#230](https://github.com/salesforcecli/plugin-source/issues/230) [#260](https://github.com/salesforcecli/plugin-source/issues/260) [#259](https://github.com/salesforcecli/plugin-source/issues/259) +- source tracking beta commands ([b871774](https://github.com/salesforcecli/plugin-source/commit/b87177498c184580db7e3bd81f164ddc77e6de0b)), closes [#253](https://github.com/salesforcecli/plugin-source/issues/253) [#251](https://github.com/salesforcecli/plugin-source/issues/251) [#230](https://github.com/salesforcecli/plugin-source/issues/230) [#260](https://github.com/salesforcecli/plugin-source/issues/260) [#259](https://github.com/salesforcecli/plugin-source/issues/259) ### [1.2.6](https://github.com/salesforcecli/plugin-source/compare/v1.2.5...v1.2.6) (2021-10-21) diff --git a/README.md b/README.md index 6c12b8857..5ea6bc6d4 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ sfdx plugins # Usage + ```sh-session $ npm install -g @salesforce/plugin-source $ sfdx COMMAND @@ -86,26 +87,28 @@ USAGE $ sfdx COMMAND ... ``` + # Commands -* [`sfdx force:mdapi:describemetadata [-f ] [-u ] [-a ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcemdapidescribemetadata--f-filepath--u-string--a-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:mdapi:listmetadata -m [-f ] [--folder ] [-u ] [-a ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcemdapilistmetadata--m-string--f-filepath---folder-string--u-string--a-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:beta:pull [-f] [-w ] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetapull--f--w-minutes--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:beta:push [-f] [-w ] [-g] [-u ] [--apiversion ] [--quiet] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetapush--f--w-minutes--g--u-string---apiversion-string---quiet---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:beta:status [-l | -r] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetastatus--l---r--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:beta:tracking:clear [-p] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetatrackingclear--p--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:beta:tracking:reset [-r ] [-p] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetatrackingreset--r-integer--p--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:convert [-r ] [-d ] [-n ] [-p | -x | -m ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourceconvert--r-directory--d-directory--n-string--p-array---x-string---m-array---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx `](#sfdx-) -* [`sfdx force:source:deploy [--soapdeploy] [-w ] [-q | -x | -m | -p | -c | -l NoTestRun|RunSpecifiedTests|RunLocalTests|RunAllTestsInOrg | -r | -o | -g] [--predestructivechanges ] [--postdestructivechanges ] [-u ] [--apiversion ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcedeploy---soapdeploy--w-minutes--q-id---x-filepath---m-array---p-array---c---l-notestrunrunspecifiedtestsrunlocaltestsrunalltestsinorg---r-array---o---g---predestructivechanges-filepath----postdestructivechanges-filepath---u-string---apiversion-string---verbose---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:deploy:cancel [-w ] [-i ] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcedeploycancel--w-minutes--i-id--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:deploy:report [-w ] [-i ] [-u ] [--apiversion ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcedeployreport--w-minutes--i-id--u-string---apiversion-string---verbose---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:manifest:create [-m | -p ] [-n | -t pre|post|destroy|package] [-o ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcemanifestcreate--m-array---p-array--n-string---t-prepostdestroypackage--o-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:open -f [-r] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourceopen--f-filepath--r--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) -* [`sfdx force:source:retrieve [-p | -x | -m ] [-w ] [-n ] [-u ] [-a ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourceretrieve--p-array---x-filepath---m-array--w-minutes--n-array--u-string--a-string---verbose---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) + +- [`sfdx force:mdapi:describemetadata [-f ] [-u ] [-a ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcemdapidescribemetadata--f-filepath--u-string--a-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:mdapi:listmetadata -m [-f ] [--folder ] [-u ] [-a ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcemdapilistmetadata--m-string--f-filepath---folder-string--u-string--a-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:beta:pull [-f] [-w ] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetapull--f--w-minutes--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:beta:push [-f] [-w ] [-g] [-u ] [--apiversion ] [--quiet] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetapush--f--w-minutes--g--u-string---apiversion-string---quiet---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:beta:status [-l | -r] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetastatus--l---r--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:beta:tracking:clear [-p] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetatrackingclear--p--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:beta:tracking:reset [-r ] [-p] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcebetatrackingreset--r-integer--p--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:convert [-r ] [-d ] [-n ] [-p | -x | -m ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourceconvert--r-directory--d-directory--n-string--p-array---x-string---m-array---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx `](#sfdx-) +- [`sfdx force:source:deploy [--soapdeploy] [-w ] [-q | -x | -m | -p | -c | -l NoTestRun|RunSpecifiedTests|RunLocalTests|RunAllTestsInOrg | -r | -o | -g] [--predestructivechanges ] [--postdestructivechanges ] [-u ] [--apiversion ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcedeploy---soapdeploy--w-minutes--q-id---x-filepath---m-array---p-array---c---l-notestrunrunspecifiedtestsrunlocaltestsrunalltestsinorg---r-array---o---g---predestructivechanges-filepath----postdestructivechanges-filepath---u-string---apiversion-string---verbose---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:deploy:cancel [-w ] [-i ] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcedeploycancel--w-minutes--i-id--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:deploy:report [-w ] [-i ] [-u ] [--apiversion ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcedeployreport--w-minutes--i-id--u-string---apiversion-string---verbose---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:manifest:create [-m | -p ] [-n | -t pre|post|destroy|package] [-o ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourcemanifestcreate--m-array---p-array--n-string---t-prepostdestroypackage--o-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:open -f [-r] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourceopen--f-filepath--r--u-string---apiversion-string---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) +- [`sfdx force:source:retrieve [-p | -x | -m ] [-w ] [-n ] [-u ] [-a ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-forcesourceretrieve--p-array---x-filepath---m-array--w-minutes--n-array--u-string--a-string---verbose---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal) ## `sfdx force:mdapi:describemetadata [-f ] [-u ] [-a ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]` @@ -115,7 +118,7 @@ display the metadata types enabled for your org display the metadata types enabled for your org USAGE - $ sfdx force:mdapi:describemetadata [-f ] [-u ] [-a ] [--json] [--loglevel + $ sfdx force:mdapi:describemetadata [-f ] [-u ] [-a ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -149,7 +152,7 @@ display properties of metadata components of a specified type display properties of metadata components of a specified type USAGE - $ sfdx force:mdapi:listmetadata -m [-f ] [--folder ] [-u ] [-a ] [--json] + $ sfdx force:mdapi:listmetadata -m [-f ] [--folder ] [-u ] [-a ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -198,7 +201,7 @@ pull source from the scratch org to the project pull source from the scratch org to the project USAGE - $ sfdx force:source:beta:pull [-f] [-w ] [-u ] [--apiversion ] [--json] [--loglevel + $ sfdx force:source:beta:pull [-f] [-w ] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -209,8 +212,8 @@ OPTIONS username or alias for the target org; overrides default target org -w, --wait=wait - [default: 33 minutes] The number of minutes to wait for the command to complete and display results to the terminal - window. If the command continues to run after the wait period, the CLI returns control of the terminal window to + [default: 33 minutes] The number of minutes to wait for the command to complete and display results to the terminal + window. If the command continues to run after the wait period, the CLI returns control of the terminal window to you. The default is 33 minutes. --apiversion=apiversion @@ -233,7 +236,7 @@ push source to a scratch org from the project push source to a scratch org from the project USAGE - $ sfdx force:source:beta:push [-f] [-w ] [-g] [-u ] [--apiversion ] [--quiet] [--json] + $ sfdx force:source:beta:push [-f] [-w ] [-g] [-u ] [--apiversion ] [--quiet] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -247,8 +250,8 @@ OPTIONS username or alias for the target org; overrides default target org -w, --wait=wait - [default: 33 minutes] Number of minutes to wait for the command to complete and display results to the terminal - window. If the command continues to run after the wait period, the CLI returns control of the terminal window to + [default: 33 minutes] Number of minutes to wait for the command to complete and display results to the terminal + window. If the command continues to run after the wait period, the CLI returns control of the terminal window to you. The default is 33 minutes. --apiversion=apiversion @@ -274,7 +277,7 @@ list local changes and/or changes in a scratch org list local changes and/or changes in a scratch org USAGE - $ sfdx force:source:beta:status [-l | -r] [-u ] [--apiversion ] [--json] [--loglevel + $ sfdx force:source:beta:status [-l | -r] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -316,7 +319,7 @@ WARNING: This command deletes or overwrites all existing source tracking files. Clears all local source tracking information. When you next run force:source:beta:status, the CLI displays all local and remote files as changed, and any files with the same name are listed as conflicts. USAGE - $ sfdx force:source:beta:tracking:clear [-p] [-u ] [--apiversion ] [--json] [--loglevel + $ sfdx force:source:beta:tracking:clear [-p] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -337,7 +340,7 @@ OPTIONS DESCRIPTION WARNING: This command deletes or overwrites all existing source tracking files. Use with extreme caution. - Clears all local source tracking information. When you next run force:source:beta:status, the CLI displays all local + Clears all local source tracking information. When you next run force:source:beta:status, the CLI displays all local and remote files as changed, and any files with the same name are listed as conflicts. ``` @@ -350,7 +353,7 @@ reset local and remote source tracking ``` reset local and remote source tracking - WARNING: This command deletes or overwrites all existing source tracking files. Use with extreme caution. + WARNING: This command deletes or overwrites all existing source tracking files. Use with extreme caution. Resets local and remote source tracking so that the CLI no longer registers differences between your local files and those in the org. When you next run force:source:beta:status, the CLI returns no results, even though conflicts might actually exist. The CLI then resumes tracking new source changes as usual. @@ -358,7 +361,7 @@ Use the --revision parameter to reset source tracking to a specific revision num $ sfdx force:data:soql:query -q "SELECT MemberName, MemberType, RevisionCounter FROM SourceMember" -t USAGE - $ sfdx force:source:beta:tracking:reset [-r ] [-p] [-u ] [--apiversion ] [--json] [--loglevel + $ sfdx force:source:beta:tracking:reset [-r ] [-p] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -380,13 +383,13 @@ OPTIONS this command invocation DESCRIPTION - WARNING: This command deletes or overwrites all existing source tracking files. Use with extreme caution. + WARNING: This command deletes or overwrites all existing source tracking files. Use with extreme caution. - Resets local and remote source tracking so that the CLI no longer registers differences between your local files and - those in the org. When you next run force:source:beta:status, the CLI returns no results, even though conflicts might + Resets local and remote source tracking so that the CLI no longer registers differences between your local files and + those in the org. When you next run force:source:beta:status, the CLI returns no results, even though conflicts might actually exist. The CLI then resumes tracking new source changes as usual. - Use the --revision parameter to reset source tracking to a specific revision number of an org source member. To get + Use the --revision parameter to reset source tracking to a specific revision number of an org source member. To get the revision number, query the SourceMember Tooling API object with the force:data:soql:query command. For example: $ sfdx force:data:soql:query -q "SELECT MemberName, MemberType, RevisionCounter FROM SourceMember" -t ``` @@ -395,11 +398,11 @@ _See code: [src/commands/force/source/beta/tracking/reset.ts](https://github.com ## `sfdx force:source:convert [-r ] [-d ] [-n ] [-p | -x | -m ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]` -convert source into Metadata API format +convert source into Metadata API format ``` -convert source into Metadata API format - Converts source-formatted files into metadata that you can deploy using Metadata API. +convert source into Metadata API format + Converts source-formatted files into metadata that you can deploy using Metadata API. To convert source-formatted files into the metadata format, so that you can deploy them using Metadata API, run "sfdx force:source:convert". Then deploy the metadata using "sfdx force:mdapi:deploy". @@ -408,7 +411,7 @@ To convert Metadata API–formatted files into the source format, run "sfdx forc To specify a package name that includes spaces, enclose the name in single quotes. USAGE - $ sfdx force:source:convert [-r ] [-d ] [-n ] [-p | -x | -m ] + $ sfdx force:source:convert [-r ] [-d ] [-n ] [-p | -x | -m ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -438,7 +441,7 @@ OPTIONS this command invocation DESCRIPTION - Converts source-formatted files into metadata that you can deploy using Metadata API. + Converts source-formatted files into metadata that you can deploy using Metadata API. To convert source-formatted files into the metadata format, so that you can deploy them using Metadata API, run "sfdx force:source:convert". Then deploy the metadata using "sfdx force:mdapi:deploy". @@ -455,10 +458,10 @@ _See code: [src/commands/force/source/convert.ts](https://github.com/salesforcec ## `sfdx ` -delete source from your project and from a non-source-tracked org +delete source from your project and from a non-source-tracked org ``` -delete source from your project and from a non-source-tracked org +delete source from your project and from a non-source-tracked org Use this command to delete components from orgs that don’t have source tracking. To remove deleted items from scratch orgs, which have change tracking, use "sfdx force:source:push". @@ -528,9 +531,9 @@ If the comma-separated list you’re supplying contains spaces, enclose the enti If you use the --manifest, --predestructivechanges, or --postdestructivechanges parameters, run the force:source:manifest:create command to easily generate the different types of manifest files. USAGE - $ sfdx force:source:deploy [--soapdeploy] [-w ] [-q | -x | -m | -p | -c | -l - NoTestRun|RunSpecifiedTests|RunLocalTests|RunAllTestsInOrg | -r | -o | -g] [--predestructivechanges - ] [--postdestructivechanges ] [-u ] [--apiversion ] [--verbose] [--json] [--loglevel + $ sfdx force:source:deploy [--soapdeploy] [-w ] [-q | -x | -m | -p | -c | -l + NoTestRun|RunSpecifiedTests|RunLocalTests|RunAllTestsInOrg | -r | -o | -g] [--predestructivechanges + ] [--postdestructivechanges ] [-u ] [--apiversion ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -595,22 +598,22 @@ DESCRIPTION To take advantage of change tracking with scratch orgs, use "sfdx force:source:push". To deploy metadata that’s in metadata format, use "sfdx force:mdapi:deploy". - The source you deploy overwrites the corresponding metadata in your org. This command does not attempt to merge your + The source you deploy overwrites the corresponding metadata in your org. This command does not attempt to merge your source with the versions in your org. - To run the command asynchronously, set --wait to 0, which immediately returns the job ID. This way, you can continue + To run the command asynchronously, set --wait to 0, which immediately returns the job ID. This way, you can continue to use the CLI. To check the status of the job, use force:source:deploy:report. - If the comma-separated list you’re supplying contains spaces, enclose the entire comma-separated list in one set of + If the comma-separated list you’re supplying contains spaces, enclose the entire comma-separated list in one set of double quotes. On Windows, if the list contains commas, also enclose the entire list in one set of double quotes. - If you use the --manifest, --predestructivechanges, or --postdestructivechanges parameters, run the + If you use the --manifest, --predestructivechanges, or --postdestructivechanges parameters, run the force:source:manifest:create command to easily generate the different types of manifest files. EXAMPLES To deploy the source files in a directory: $ sfdx force:source:deploy -p path/to/source - To deploy a specific Apex class and the objects whose source is in a directory: + To deploy a specific Apex class and the objects whose source is in a directory: $ sfdx force:source:deploy -p "path/to/apex/classes/MyClass.cls,path/to/source/objects" To deploy source files in a comma-separated list that contains spaces: $ sfdx force:source:deploy -p "path/to/objects/MyCustomObject/fields/MyField.field-meta.xml, path/to/apex/classes" @@ -650,7 +653,7 @@ To run the command asynchronously, set --wait to 0, which immediately returns th To check the status of the job, use force:source:deploy:report. USAGE - $ sfdx force:source:deploy:cancel [-w ] [-i ] [-u ] [--apiversion ] [--json] [--loglevel + $ sfdx force:source:deploy:cancel [-w ] [-i ] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -673,10 +676,10 @@ OPTIONS this command invocation DESCRIPTION - Use this command to cancel a specified asynchronous source deployment. You can also specify a wait time (in minutes) + Use this command to cancel a specified asynchronous source deployment. You can also specify a wait time (in minutes) to check for updates to the canceled deploy status. - To run the command asynchronously, set --wait to 0, which immediately returns the job ID. This way, you can continue + To run the command asynchronously, set --wait to 0, which immediately returns the job ID. This way, you can continue to use the CLI. To check the status of the job, use force:source:deploy:report. @@ -695,14 +698,14 @@ _See code: [src/commands/force/source/deploy/cancel.ts](https://github.com/sales ## `sfdx force:source:deploy:report [-w ] [-i ] [-u ] [--apiversion ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]` -check the status of a metadata deployment +check the status of a metadata deployment ``` -check the status of a metadata deployment +check the status of a metadata deployment Specify the job ID for the deploy you want to check. You can also specify a wait time (minutes) to check for updates to the deploy status. USAGE - $ sfdx force:source:deploy:report [-w ] [-i ] [-u ] [--apiversion ] [--verbose] [--json] + $ sfdx force:source:deploy:report [-w ] [-i ] [-u ] [--apiversion ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -727,7 +730,7 @@ OPTIONS --verbose verbose output of deploy result DESCRIPTION - Specify the job ID for the deploy you want to check. You can also specify a wait time (minutes) to check for updates + Specify the job ID for the deploy you want to check. You can also specify a wait time (minutes) to check for updates to the deploy status. EXAMPLES @@ -745,10 +748,10 @@ _See code: [src/commands/force/source/deploy/report.ts](https://github.com/sales ## `sfdx force:source:manifest:create [-m | -p ] [-n | -t pre|post|destroy|package] [-o ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]` -create a project manifest that lists the metadata components you want to deploy or retrieve +create a project manifest that lists the metadata components you want to deploy or retrieve ``` -create a project manifest that lists the metadata components you want to deploy or retrieve +create a project manifest that lists the metadata components you want to deploy or retrieve Create a manifest from a list of metadata components (--metadata) or from one or more local directories that contain source files (--sourcepath). You can specify either of these parameters, not both. Use --manifesttype to specify the type of manifest you want to create. The resulting manifest files have specific names, such as the standard package.xml or destructiveChanges.xml to delete metadata. Valid values for this parameter, and their respective file names, are: @@ -758,14 +761,14 @@ Use --manifesttype to specify the type of manifest you want to create. The resul post : destructiveChangesPost.xml destroy : destructiveChanges.xml -See https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_deploy_deleting_files.htm for information about these destructive manifest files. +See https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_deploy_deleting_files.htm for information about these destructive manifest files. Use --manifestname to specify a custom name for the generated manifest if the pre-defined ones don’t suit your needs. You can specify either --manifesttype or --manifestname, but not both. USAGE - $ sfdx force:source:manifest:create [-m | -p ] [-n | -t pre|post|destroy|package] [-o - ] [--apiversion ] [--json] [--loglevel + $ sfdx force:source:manifest:create [-m | -p ] [-n | -t pre|post|destroy|package] [-o + ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -796,11 +799,11 @@ OPTIONS this command invocation DESCRIPTION - Create a manifest from a list of metadata components (--metadata) or from one or more local directories that contain + Create a manifest from a list of metadata components (--metadata) or from one or more local directories that contain source files (--sourcepath). You can specify either of these parameters, not both. - Use --manifesttype to specify the type of manifest you want to create. The resulting manifest files have specific - names, such as the standard package.xml or destructiveChanges.xml to delete metadata. Valid values for this parameter, + Use --manifesttype to specify the type of manifest you want to create. The resulting manifest files have specific + names, such as the standard package.xml or destructiveChanges.xml to delete metadata. Valid values for this parameter, and their respective file names, are: package : package.xml (default) @@ -808,10 +811,10 @@ DESCRIPTION post : destructiveChangesPost.xml destroy : destructiveChanges.xml - See https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_deploy_deleting_files.htm for - information about these destructive manifest files. + See https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_deploy_deleting_files.htm for + information about these destructive manifest files. - Use --manifestname to specify a custom name for the generated manifest if the pre-defined ones don’t suit your needs. + Use --manifestname to specify a custom name for the generated manifest if the pre-defined ones don’t suit your needs. You can specify either --manifesttype or --manifestname, but not both. EXAMPLES @@ -835,7 +838,7 @@ If no browser-based editor is available for the selected file, this command open To generate a URL for the browser-based editor but not open the editor, use --urlonly. USAGE - $ sfdx force:source:open -f [-r] [-u ] [--apiversion ] [--json] [--loglevel + $ sfdx force:source:open -f [-r] [-u ] [--apiversion ] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -856,8 +859,8 @@ OPTIONS this command invocation DESCRIPTION - Opens the specified Lightning Page in Lightning App Builder. Lightning Page files have the suffix .flexipage-meta.xml, - and are stored in the flexipages directory. If you specify a different type of file, this command opens your org’s + Opens the specified Lightning Page in Lightning App Builder. Lightning Page files have the suffix .flexipage-meta.xml, + and are stored in the flexipages directory. If you specify a different type of file, this command opens your org’s home page. The file opens in your default browser. @@ -874,10 +877,10 @@ _See code: [src/commands/force/source/open.ts](https://github.com/salesforcecli/ ## `sfdx force:source:retrieve [-p | -x | -m ] [-w ] [-n ] [-u ] [-a ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]` -retrieve source from an org +retrieve source from an org ``` -retrieve source from an org +retrieve source from an org Use this command to retrieve source (metadata that’s in source format) from an org. To take advantage of change tracking with scratch orgs, use "sfdx force:source:pull". To retrieve metadata that’s in metadata format, use "sfdx force:mdapi:retrieve". @@ -887,7 +890,7 @@ The source you retrieve overwrites the corresponding source files in your local If the comma-separated list you’re supplying contains spaces, enclose the entire comma-separated list in one set of double quotes. On Windows, if the list contains commas, also enclose it in one set of double quotes. USAGE - $ sfdx force:source:retrieve [-p | -x | -m ] [-w ] [-n ] [-u ] [-a + $ sfdx force:source:retrieve [-p | -x | -m ] [-w ] [-n ] [-u ] [-a ] [--verbose] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL] OPTIONS @@ -924,10 +927,10 @@ DESCRIPTION To take advantage of change tracking with scratch orgs, use "sfdx force:source:pull". To retrieve metadata that’s in metadata format, use "sfdx force:mdapi:retrieve". - The source you retrieve overwrites the corresponding source files in your local project. This command does not attempt + The source you retrieve overwrites the corresponding source files in your local project. This command does not attempt to merge the source from your org with your local source files. - If the comma-separated list you’re supplying contains spaces, enclose the entire comma-separated list in one set of + If the comma-separated list you’re supplying contains spaces, enclose the entire comma-separated list in one set of double quotes. On Windows, if the list contains commas, also enclose it in one set of double quotes. EXAMPLES @@ -936,7 +939,7 @@ EXAMPLES To retrieve a specific Apex class and the objects whose source is in a directory: $ sfdx force:source:retrieve -p "path/to/apex/classes/MyClass.cls,path/to/source/objects" To retrieve source files in a comma-separated list that contains spaces: - $ sfdx force:source:retrieve -p "path/to/objects/MyCustomObject/fields/MyField.field-meta.xml, + $ sfdx force:source:retrieve -p "path/to/objects/MyCustomObject/fields/MyField.field-meta.xml, path/to/apex/classes To retrieve all Apex classes: $ sfdx force:source:retrieve -m ApexClass @@ -951,7 +954,7 @@ EXAMPLES To retrieve metadata from a package or multiple packages: $ sfdx force:source:retrieve -n MyPackageName $ sfdx force:source:retrieve -n "Package1, PackageName With Spaces, Package3" - To retrieve all metadata from a package and specific components that aren’t in the package, specify both -n | + To retrieve all metadata from a package and specific components that aren’t in the package, specify both -n | --packagenames and one other scoping parameter: $ sfdx force:source:retrieve -n MyPackageName -p path/to/apex/classes $ sfdx force:source:retrieve -n MyPackageName -m ApexClass:MyApexClass @@ -959,4 +962,5 @@ EXAMPLES ``` _See code: [src/commands/force/source/retrieve.ts](https://github.com/salesforcecli/plugin-source/blob/v1.3.1/src/commands/force/source/retrieve.ts)_ + diff --git a/messages/retrieve.json b/messages/retrieve.json index 42bbe79e2..9bd8d0ff5 100644 --- a/messages/retrieve.json +++ b/messages/retrieve.json @@ -47,5 +47,10 @@ "columnNumberColumn": "COLUMN NUMBER", "lineNumberColumn": "LINE NUMBER", "errorColumn": "PROBLEM", - "wantsToRetrieveCustomFields": "Because you're retrieving one or more CustomFields, we're also retrieving the CustomObject to which it's associated." + "wantsToRetrieveCustomFields": "Because you're retrieving one or more CustomFields, we're also retrieving the CustomObject to which it's associated.", + "spinnerMessages": { + "componentSetBuild": "Preparing retrieve request", + "sendingRequest": "Sending request to org (metadata API version %s)", + "polling": "Waiting for the org to respond" + } } diff --git a/messages/status.json b/messages/status.json index 946a6a1fc..b82adf1d8 100644 --- a/messages/status.json +++ b/messages/status.json @@ -15,5 +15,6 @@ "remote": "list the changes that have been made in the scratch org", "remoteLong": "Lists the changes that have been made in the scratch org." }, - "humanSuccess": "Source Status" + "humanSuccess": "Source Status", + "noResults": "No local or remote changes found." } diff --git a/package.json b/package.json index 5342e3da1..b8d368d87 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "@oclif/config": "^1", "@salesforce/command": "^4.1.3", "@salesforce/core": "^2.28.0", - "@salesforce/source-deploy-retrieve": "^5.1.1", - "@salesforce/source-tracking": "^0.4.1", + "@salesforce/source-deploy-retrieve": "^5.3.0", + "@salesforce/source-tracking": "^0.4.2", "chalk": "^4.1.2", "cli-ux": "^5.6.3", "open": "^8.2.1", diff --git a/src/commands/force/source/beta/pull.ts b/src/commands/force/source/beta/pull.ts index 29cdb0377..9377131ed 100644 --- a/src/commands/force/source/beta/pull.ts +++ b/src/commands/force/source/beta/pull.ts @@ -156,9 +156,17 @@ export default class Pull extends SourceCommand { } protected resolveSuccess(): void { + const StatusCodeMap = new Map([ + [RequestStatus.Succeeded, 0], + [RequestStatus.Canceled, 1], + [RequestStatus.Failed, 1], + [RequestStatus.InProgress, 69], + [RequestStatus.Pending, 69], + [RequestStatus.Canceling, 69], + ]); // there might not be a retrieveResult if we don't have anything to retrieve - if (this.retrieveResult && this.retrieveResult.response.status !== RequestStatus.Succeeded) { - this.setExitCode(1); + if (this.retrieveResult && this.retrieveResult.response.status) { + this.setExitCode(StatusCodeMap.get(this.retrieveResult.response.status) ?? 1); } } diff --git a/src/commands/force/source/beta/push.ts b/src/commands/force/source/beta/push.ts index e21949986..8515778f7 100644 --- a/src/commands/force/source/beta/push.ts +++ b/src/commands/force/source/beta/push.ts @@ -11,7 +11,7 @@ import { Messages } from '@salesforce/core'; import { RequestStatus, ComponentStatus } from '@salesforce/source-deploy-retrieve'; import { SourceTracking, throwIfInvalid, replaceRenamedCommands } from '@salesforce/source-tracking'; -import { DeployCommand } from '../../../../deployCommand'; +import { DeployCommand, getVersionMessage } from '../../../../deployCommand'; import { PushResponse, PushResultFormatter } from '../../../../formatters/pushResultFormatter'; import { ProgressFormatter } from '../../../../formatters/progressFormatter'; import { DeployProgressBarFormatter } from '../../../../formatters/deployProgressBarFormatter'; @@ -51,11 +51,12 @@ export default class Push extends DeployCommand { protected static requiresProject = true; protected readonly lifecycleEventNames = ['predeploy', 'postdeploy']; - private isRest = false; + private tracking: SourceTracking; public async run(): Promise { await this.deploy(); this.resolveSuccess(); + await this.updateTrackingFiles(); return this.formatResult(); } @@ -66,19 +67,20 @@ export default class Push extends DeployCommand { toValidate: 'plugin-source', command: replaceRenamedCommands('force:source:push'), }); - const waitDuration = this.getFlag('wait'); - this.isRest = await this.isRestDeploy(); - const tracking = await SourceTracking.create({ + this.tracking = await SourceTracking.create({ org: this.org, project: this.project, apiVersion: this.flags.apiversion as string, }); if (!this.flags.forceoverwrite) { - processConflicts(await tracking.getConflicts(), this.ux, messages.getMessage('conflictMsg')); + processConflicts(await this.tracking.getConflicts(), this.ux, messages.getMessage('conflictMsg')); + } + const componentSet = await this.tracking.localChangesAsComponentSet(); + const sourceApiVersion = await this.getSourceApiVersion(); + if (sourceApiVersion) { + componentSet.sourceApiVersion = sourceApiVersion; } - const componentSet = await tracking.localChangesAsComponentSet(); - componentSet.sourceApiVersion = await this.getSourceApiVersion(); // there might have been components in local tracking, but they might be ignored by SDR or unresolvable. // SDR will throw when you try to resolve them, so don't @@ -89,13 +91,15 @@ export default class Push extends DeployCommand { // fire predeploy event for sync and async deploys await this.lifecycle.emit('predeploy', componentSet.toArray()); - this.ux.log(`*** Pushing with ${this.isRest ? 'REST' : 'SOAP'} API v${componentSet.sourceApiVersion} ***`); + + const isRest = await this.isRestDeploy(); + this.ux.log(getVersionMessage('Pushing', componentSet, isRest)); const deploy = await componentSet.deploy({ usernameOrConnection: this.org.getUsername(), apiOptions: { ignoreWarnings: this.getFlag('ignorewarnings', false), - rest: this.isRest, + rest: isRest, testLevel: 'NoTestRun', }, }); @@ -107,32 +111,52 @@ export default class Push extends DeployCommand { : new DeployProgressStatusFormatter(this.logger, this.ux); progressFormatter.progress(deploy); } - this.deployResult = await deploy.pollStatus(500, waitDuration.seconds); + this.deployResult = await deploy.pollStatus(500, this.getFlag('wait').seconds); + + if (this.deployResult) { + // Only fire the postdeploy event when we have results. I.e., not async. + await this.lifecycle.emit('postdeploy', this.deployResult); + } + } + protected async updateTrackingFiles(): Promise { + if (process.exitCode !== 0 || !this.deployResult) { + return; + } const successes = this.deployResult .getFileResponses() .filter((fileResponse) => fileResponse.state !== ComponentStatus.Failed); const successNonDeletes = successes.filter((fileResponse) => fileResponse.state !== ComponentStatus.Deleted); const successDeletes = successes.filter((fileResponse) => fileResponse.state === ComponentStatus.Deleted); - if (this.deployResult) { - // Only fire the postdeploy event when we have results. I.e., not async. - await this.lifecycle.emit('postdeploy', this.deployResult); - } - await Promise.all([ - tracking.updateLocalTracking({ + this.tracking.updateLocalTracking({ files: successNonDeletes.map((fileResponse) => fileResponse.filePath), deletedFiles: successDeletes.map((fileResponse) => fileResponse.filePath), }), - tracking.updateRemoteTracking(successes), + this.tracking.updateRemoteTracking(successes), ]); } protected resolveSuccess(): void { + const StatusCodeMap = new Map([ + [RequestStatus.Succeeded, 0], + [RequestStatus.Canceled, 1], + [RequestStatus.Failed, 1], + [RequestStatus.InProgress, 69], + [RequestStatus.Pending, 69], + [RequestStatus.Canceling, 69], + ]); // there might not be a deployResult if we exited early with an empty componentSet - if (this.deployResult && this.deployResult.response.status !== RequestStatus.Succeeded) { - this.setExitCode(1); + if (this.deployResult && this.deployResult.response.status) { + this.setExitCode(StatusCodeMap.get(this.deployResult.response.status) ?? 1); + // special override case for "only warnings about deleted things that are already deleted" + if ( + this.deployResult.response.status === RequestStatus.Failed && + this.deployResult.getFileResponses().every((fr) => fr.state !== 'Failed') + ) { + this.setExitCode(0); + } } } diff --git a/src/commands/force/source/deploy.ts b/src/commands/force/source/deploy.ts index 8913b57f8..f8688f426 100644 --- a/src/commands/force/source/deploy.ts +++ b/src/commands/force/source/deploy.ts @@ -9,8 +9,8 @@ import { flags, FlagsConfig } from '@salesforce/command'; import { Messages } from '@salesforce/core'; import { AsyncResult, DeployResult, RequestStatus } from '@salesforce/source-deploy-retrieve'; import { Duration, env, once } from '@salesforce/kit'; -import { getString, isString } from '@salesforce/ts-types'; -import { DeployCommand } from '../../../deployCommand'; +import { isString } from '@salesforce/ts-types'; +import { DeployCommand, getVersionMessage } from '../../../deployCommand'; import { ComponentSetBuilder } from '../../../componentSetBuilder'; import { DeployCommandResult, DeployResultFormatter } from '../../../formatters/deployResultFormatter'; import { DeployAsyncResultFormatter, DeployCommandAsyncResult } from '../../../formatters/deployAsyncResultFormatter'; @@ -144,7 +144,6 @@ export class Deploy extends DeployCommand { const waitDuration = this.getFlag('wait'); this.isAsync = waitDuration.quantity === 0; this.isRest = await this.isRestDeploy(); - this.ux.log(`*** Deploying with ${this.isRest ? 'REST' : 'SOAP'} API ***`); if (this.flags.validateddeployrequestid) { this.deployResult = await this.deployRecentValidation(); @@ -166,6 +165,7 @@ export class Deploy extends DeployCommand { }); // fire predeploy event for sync and async deploys await this.lifecycle.emit('predeploy', this.componentSet.toArray()); + this.ux.log(getVersionMessage('Deploying', this.componentSet, this.isRest)); const deploy = await this.componentSet.deploy({ usernameOrConnection: this.org.getUsername(), @@ -205,11 +205,17 @@ export class Deploy extends DeployCommand { * unsuccessful in oclif. */ protected resolveSuccess(): void { + const StatusCodeMap = new Map([ + [RequestStatus.Succeeded, 0], + [RequestStatus.Canceled, 1], + [RequestStatus.Failed, 1], + [RequestStatus.SucceededPartial, 68], + [RequestStatus.InProgress, 69], + [RequestStatus.Pending, 69], + [RequestStatus.Canceling, 69], + ]); if (!this.isAsync) { - const status = getString(this.deployResult, 'response.status'); - if (status !== RequestStatus.Succeeded) { - this.setExitCode(1); - } + this.setExitCode(StatusCodeMap.get(this.deployResult.response?.status) ?? 1); } } diff --git a/src/commands/force/source/retrieve.ts b/src/commands/force/source/retrieve.ts index 5676ce01a..1e2f9027d 100644 --- a/src/commands/force/source/retrieve.ts +++ b/src/commands/force/source/retrieve.ts @@ -10,8 +10,7 @@ import { join } from 'path'; import { flags, FlagsConfig } from '@salesforce/command'; import { Messages, SfdxProject } from '@salesforce/core'; import { Duration } from '@salesforce/kit'; -import { getString } from '@salesforce/ts-types'; -import { RetrieveResult, RequestStatus, ComponentSet } from '@salesforce/source-deploy-retrieve'; +import { RetrieveResult, ComponentSet, RequestStatus } from '@salesforce/source-deploy-retrieve'; import { SourceCommand } from '../../../sourceCommand'; import { RetrieveResultFormatter, @@ -77,6 +76,7 @@ export class Retrieve extends SourceCommand { } protected async retrieve(): Promise { + this.ux.startSpinner(messages.getMessage('spinnerMessages.componentSetBuild')); this.componentSet = await ComponentSetBuilder.build({ apiversion: this.getFlag('apiversion'), sourceapiversion: await this.getSourceApiVersion(), @@ -101,6 +101,11 @@ export class Retrieve extends SourceCommand { await this.lifecycle.emit('preretrieve', this.componentSet.toArray()); + this.ux.setSpinnerStatus( + messages.getMessage('spinnerMessages.sendingRequest', [ + this.componentSet.sourceApiVersion || this.componentSet.apiVersion, + ]) + ); const mdapiRetrieve = await this.componentSet.retrieve({ usernameOrConnection: this.org.getUsername(), merge: true, @@ -108,16 +113,24 @@ export class Retrieve extends SourceCommand { packageOptions: this.getFlag('packagenames'), }); + this.ux.setSpinnerStatus(messages.getMessage('spinnerMessages.polling')); this.retrieveResult = await mdapiRetrieve.pollStatus(1000, this.getFlag('wait').seconds); await this.lifecycle.emit('postretrieve', this.retrieveResult.getFileResponses()); + this.ux.stopSpinner(); } protected resolveSuccess(): void { - const status = getString(this.retrieveResult, 'response.status'); - if (status !== RequestStatus.Succeeded) { - this.setExitCode(1); - } + const StatusCodeMap = new Map([ + [RequestStatus.Succeeded, 0], + [RequestStatus.Canceled, 1], + [RequestStatus.Failed, 1], + [RequestStatus.InProgress, 69], + [RequestStatus.Pending, 69], + [RequestStatus.Canceling, 69], + ]); + + this.setExitCode(StatusCodeMap.get(this.retrieveResult.response.status) ?? 1); } protected async formatResult(): Promise { diff --git a/src/deployCommand.ts b/src/deployCommand.ts index cb83c8e18..e09b122f6 100644 --- a/src/deployCommand.ts +++ b/src/deployCommand.ts @@ -110,3 +110,32 @@ export abstract class DeployCommand extends SourceCommand { return new ConfigFile({ isGlobal: true, filename: 'stash.json' }); } } + +export const getVersionMessage = ( + action: 'Deploying' | 'Pushing', + componentSet: ComponentSet, + isRest: boolean +): string => { + // commands pass in the.componentSet, which may not exist in some tests + if (!componentSet) { + return; + } + // neither + if (!componentSet.sourceApiVersion && !componentSet.apiVersion) { + return `*** ${action} with ${isRest ? 'REST' : 'SOAP'} ***`; + } + // either OR both match (SDR will use either) + if ( + !componentSet.sourceApiVersion || + !componentSet.apiVersion || + componentSet.sourceApiVersion === componentSet.apiVersion + ) { + return `*** ${action} with ${isRest ? 'REST' : 'SOAP'} API v${ + componentSet.apiVersion ?? componentSet.sourceApiVersion + } ***`; + } + // has both but they don't match + return `*** ${action} v${componentSet.sourceApiVersion} metadata with ${isRest ? 'REST' : 'SOAP'} API v${ + componentSet.apiVersion + } connection ***`; +}; diff --git a/src/formatters/deployResultFormatter.ts b/src/formatters/deployResultFormatter.ts index ab51f1cf7..4a72e125d 100644 --- a/src/formatters/deployResultFormatter.ts +++ b/src/formatters/deployResultFormatter.ts @@ -147,7 +147,7 @@ export class DeployResultFormatter extends ResultFormatter { } protected displayFailures(): void { - if (this.hasStatus(RequestStatus.Failed)) { + if (this.hasStatus(RequestStatus.Failed) || this.hasStatus(RequestStatus.SucceededPartial)) { const failures: Array = []; const fileResponseFailures: Map = new Map(); diff --git a/src/formatters/pushResultFormatter.ts b/src/formatters/pushResultFormatter.ts index 692921e8b..baa1369b4 100644 --- a/src/formatters/pushResultFormatter.ts +++ b/src/formatters/pushResultFormatter.ts @@ -8,7 +8,6 @@ import * as chalk from 'chalk'; import { UX } from '@salesforce/command'; import { Logger, Messages, SfdxError } from '@salesforce/core'; -import { getString } from '@salesforce/ts-types'; import { DeployResult, FileResponse, @@ -66,7 +65,7 @@ export class PushResultFormatter extends ResultFormatter { } protected hasStatus(status: RequestStatus): boolean { - return getString(this.result, 'response.status') === status; + return this.result.response.status === status; } protected displaySuccesses(): void { diff --git a/src/formatters/resultFormatter.ts b/src/formatters/resultFormatter.ts index 2038bc3aa..4ebc486c1 100644 --- a/src/formatters/resultFormatter.ts +++ b/src/formatters/resultFormatter.ts @@ -38,7 +38,7 @@ export abstract class ResultFormatter { // Command success is determined by the command so it can set the // exit code on the process, which is done before formatting. public isSuccess(): boolean { - return getNumber(process, 'exitCode', 0) === 0; + return [0, 69].includes(getNumber(process, 'exitCode', 0)); } public isVerbose(): boolean { diff --git a/src/formatters/statusFormatter.ts b/src/formatters/statusFormatter.ts index a68b17fe5..b19735176 100644 --- a/src/formatters/statusFormatter.ts +++ b/src/formatters/statusFormatter.ts @@ -47,7 +47,7 @@ export class StatusFormatter extends ResultFormatter { public display(): void { if (this.statusRows.length === 0) { - this.ux.log('No results found'); + this.ux.log(messages.getMessage('noResults')); return; } this.ux.log(messages.getMessage('humanSuccess')); diff --git a/src/sourceCommand.ts b/src/sourceCommand.ts index dca04d941..a8941fdf3 100644 --- a/src/sourceCommand.ts +++ b/src/sourceCommand.ts @@ -21,6 +21,7 @@ export type ProgressBar = { export abstract class SourceCommand extends SfdxCommand { public static readonly DEFAULT_SRC_WAIT_MINUTES = 33; + protected xorFlags: string[] = []; protected progressBar?: ProgressBar; protected lifecycle = Lifecycle.getInstance(); diff --git a/test/commands/source/deployCommand.test.ts b/test/commands/source/deployCommand.test.ts new file mode 100644 index 000000000..8ca572c86 --- /dev/null +++ b/test/commands/source/deployCommand.test.ts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import { ComponentSet } from '@salesforce/source-deploy-retrieve'; +import { expect } from 'chai'; +import { getVersionMessage } from '../../../src/deployCommand'; + +describe('various version formatting options', () => { + it('returns basic response when nothing set', () => { + // SDR will get the apiVersion from the componentSet registry default when nothing is provided + expect(getVersionMessage('Pushing', new ComponentSet(), true).startsWith('*** Pushing with REST API v')); + }); + it('returns correct response for Deploying, SOAP, and apiVersion', () => { + const CS = new ComponentSet(); + CS.apiVersion = '52.0'; + expect(getVersionMessage('Deploying', CS, false)).to.equal('*** Deploying with SOAP API v52.0 ***'); + }); + it('handles differing APIs versions', () => { + const CS = new ComponentSet(); + CS.apiVersion = '52.0'; + CS.sourceApiVersion = '51.0'; + expect(getVersionMessage('Pushing', CS, true)).to.equal( + '*** Pushing v51.0 metadata with REST API v52.0 connection ***' + ); + }); +}); diff --git a/test/commands/source/deployResponses.ts b/test/commands/source/deployResponses.ts index 12116ffcd..7d077d299 100644 --- a/test/commands/source/deployResponses.ts +++ b/test/commands/source/deployResponses.ts @@ -76,15 +76,23 @@ export type DeployResponseType = | 'failed' | 'failedTest' | 'passedTest' - | 'passedAndFailedTest'; + | 'passedAndFailedTest' + | 'partialSuccessSync'; export const getDeployResponse = ( type: DeployResponseType, overrides?: Partial ): MetadataApiDeployStatus => { - // stringify --> parse to get a clone that doesn't affedt the base deploy response + // stringify --> parse to get a clone that doesn't affect the base deploy response const response = JSON.parse(JSON.stringify({ ...baseDeployResponse, ...overrides })) as MetadataApiDeployStatus; + if (type === 'inProgress') { + response.status = RequestStatus.InProgress; + response.success = false; + response.done = false; + response.details = {}; + } + if (type === 'canceled') { response.canceledBy = '0051h000006BHOq'; response.canceledByName = 'Canceling User'; @@ -252,6 +260,27 @@ export const getDeployResponse = ( }, ]; } + if (type === 'partialSuccessSync') { + response.status = RequestStatus.SucceededPartial; + response.success = true; + response.details.componentFailures = { + componentType: 'ApexClass', + success: 'false', + lineNumber: '1', + changed: false, + fileName: 'classes/testClass1', + createdDate: '2021-04-27T22:18:07.000Z', + created: false, + deleted: false, + id: '01p2100000A6XiqAAF', + fullName: 'testClass1', + } as DeployMessage; + response.rollbackOnError = true; + response.numberComponentErrors = 1; + response.numberComponentsTotal = 2; + response.details.componentFailures.problemType = 'Error'; + response.details.componentFailures.problem = 'This component has some problems'; + } return response; }; diff --git a/test/commands/source/retrieve.test.ts b/test/commands/source/retrieve.test.ts index d25ad0e14..4b550fb88 100644 --- a/test/commands/source/retrieve.test.ts +++ b/test/commands/source/retrieve.test.ts @@ -80,7 +80,11 @@ describe('force:source:retrieve', () => { ); cmd.setOrg(orgStub); }); + // keep the stdout from showing up in the test output stubMethod(sandbox, UX.prototype, 'log'); + stubMethod(sandbox, UX.prototype, 'setSpinnerStatus'); + stubMethod(sandbox, UX.prototype, 'startSpinner'); + stubMethod(sandbox, UX.prototype, 'stopSpinner'); stubMethod(sandbox, UX.prototype, 'styledHeader'); stubMethod(sandbox, UX.prototype, 'table'); return cmd.runIt(); diff --git a/test/formatters/deployResultFormatter.test.ts b/test/formatters/deployResultFormatter.test.ts index 2e566c63e..e8ca69017 100644 --- a/test/formatters/deployResultFormatter.test.ts +++ b/test/formatters/deployResultFormatter.test.ts @@ -20,6 +20,7 @@ describe('DeployResultFormatter', () => { const deployResultSuccess = getDeployResult('successSync'); const deployResultFailure = getDeployResult('failed'); + const deployResultPartialSuccess = getDeployResult('partialSuccessSync'); const deployResultTestFailure = getDeployResult('failedTest'); const deployResultTestSuccess = getDeployResult('passedTest'); const deployResultTestSuccessAndFailure = getDeployResult('passedAndFailedTest'); @@ -75,6 +76,16 @@ describe('DeployResultFormatter', () => { const formatter = new DeployResultFormatter(logger, ux, {}, deployResultFailure); expect(formatter.getJson()).to.deep.equal(expectedFailureResults); }); + + it('should return expected json for a partial success', () => { + const deployResponse = JSON.parse(JSON.stringify(deployResultPartialSuccess.response)) as DeployCommandResult; + const expectedPartialSuccessResponse = deployResultPartialSuccess.response as DeployCommandResult; + expectedPartialSuccessResponse.deployedSource = deployResultPartialSuccess.getFileResponses(); + expectedPartialSuccessResponse.outboundFiles = []; + expectedPartialSuccessResponse.deploys = [deployResponse]; + const formatter = new DeployResultFormatter(logger, ux, {}, deployResultPartialSuccess); + expect(formatter.getJson()).to.deep.equal(expectedPartialSuccessResponse); + }); }); describe('display', () => { @@ -135,5 +146,15 @@ describe('DeployResultFormatter', () => { expect(styledHeaderStub.args[2][0]).to.include('Test Success [1]'); expect(styledHeaderStub.args[3][0]).to.include('Apex Code Coverage'); }); + + it('shows success AND failures for partialSucceeded', async () => { + const formatter = new DeployResultFormatter(logger, ux, { verbose: true }, deployResultPartialSuccess); + formatter.display(); + expect(styledHeaderStub.callCount, 'styledHeaderStub.callCount').to.equal(2); + expect(logStub.callCount, 'logStub.callCount').to.equal(3); + expect(tableStub.callCount, 'tableStub.callCount').to.equal(2); + expect(styledHeaderStub.args[0][0]).to.include('Deployed Source'); + expect(styledHeaderStub.args[1][0]).to.include('Component Failures'); + }); }); }); diff --git a/yarn.lock b/yarn.lock index 8fa8581f1..e17db9637 100644 --- a/yarn.lock +++ b/yarn.lock @@ -823,10 +823,10 @@ unzipper "0.10.11" xmldom-sfdx-encoding "^0.1.29" -"@salesforce/source-deploy-retrieve@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-5.0.1.tgz#b4add87b3de0cb27e2432e64638ef145ae81d561" - integrity sha512-dcQv0ehKyXs+TzX/gHLhs1pV+cC2VmwsYe+sRjrqTxKsQ5qo+ByF8FhkbwtlYLdUOpRkaIS+/JwCpEwKgPKvKg== +"@salesforce/source-deploy-retrieve@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-5.1.1.tgz#066c931c5cb509f4bf13892fcfd70682c00a1195" + integrity sha512-wZjWv/9PmvOWpGpe3A4TAZp/Pg8Nkt5+l/Xo18E0tuKMDXmuBoltwT2i8M8dfTfpMgVcg0xYUz1yeItKGhLhkw== dependencies: "@salesforce/core" "2.28.0" "@salesforce/kit" "^1.5.0" @@ -839,10 +839,10 @@ unzipper "0.10.11" xmldom-sfdx-encoding "^0.1.29" -"@salesforce/source-deploy-retrieve@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-5.1.1.tgz#066c931c5cb509f4bf13892fcfd70682c00a1195" - integrity sha512-wZjWv/9PmvOWpGpe3A4TAZp/Pg8Nkt5+l/Xo18E0tuKMDXmuBoltwT2i8M8dfTfpMgVcg0xYUz1yeItKGhLhkw== +"@salesforce/source-deploy-retrieve@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-5.3.0.tgz#b3024fa0b44ccce340496f31ca9c272c6b8e5b73" + integrity sha512-8iV3jO7CmGwddvN87AR5PSsh1FU707JHauEUYTetNGrVffk4HkeQIVFVHYYKgQDIzjCKhBmtU19MpwjPh4rzzA== dependencies: "@salesforce/core" "2.28.0" "@salesforce/kit" "^1.5.0" @@ -872,14 +872,14 @@ sinon "^10.0.0" strip-ansi "^6.0.0" -"@salesforce/source-tracking@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@salesforce/source-tracking/-/source-tracking-0.4.1.tgz#eba5175923c7109e6c8a1587db3ee59653c7dc28" - integrity sha512-B1PQWPPXpm6uQ+6aVapW4fIuuF7m7CNEf9Xic9Ke8kjR6QBrbuKICpiRA05bKUwyk1uV1WaCZAwy5YIQ+hBBow== +"@salesforce/source-tracking@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@salesforce/source-tracking/-/source-tracking-0.4.2.tgz#63161302883783132b9cabd023dd1cbc35324547" + integrity sha512-+KbhpA/s/SdRsMnSlG4bAJCf1/k6PCl1XDJn2Nf2SScTucwXoFmDMduRpsepluX5Uq/cfzjYw0b8ofH7xGOAFQ== dependencies: "@salesforce/core" "^2.28.0" "@salesforce/kit" "^1.5.17" - "@salesforce/source-deploy-retrieve" "^5.0.1" + "@salesforce/source-deploy-retrieve" "^5.1.1" isomorphic-git "^1.9.2" ts-retry-promise "^0.6.0"