generated from salesforcecli/plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* refactor: redo open for sfdx-core dependency reasons * test: ebikes for nuts * feat: source tracking commands in source:beta * test: nuts for source tracking commands * docs: new subtopics * chore: remove imported asserted project/org * chore: restore breaking comma * chore: bump source tracking version * chore: rebuild with latest lockfile * refactor: feedback from WR * style: typo and cleaner concats * chore: pr feedback * refactor: pull more like retrieve * refactor: status formatter * test: remove unused export * chore: bump stl * fix: missing header for pull * fix: show conflicts from both remote and local, including remote filename * test: better output, workaroun for Audience type * refactor: consistent naming of command classes * refactor: push feedback * style: comment is no longer a question * test: ebikes from clone instead of fs * fix: status flags are better * test: nuts with conn adjusted for git checkout * chore: bump sdr for Created state fix * test: unignore profiles * test: was not using ebikes, revert * chore: change status' flags in snapshot * feat: show conflicts from both remote and local, including remote filename * fix: show conflicts from both remote and local, including remote filename * test: better output, workaroun for Audience type * fix: push gets its own formatter for json reasons * fix: missing pull messages * chore: bump SDR to 4.5.7 (#253) * chore(release): 1.2.3 [ci skip] * Updated/Added CODEOWNERS with ECCN * chore(release): 1.2.4 [ci skip] * chore: sync .gitignore [skip-validate-pr] (#251) Authored via Leif * test: parallelize nuts, exclude async deploy nut * test: one NUT for special types * test: remove tracking NUTs * ci: custom nut commands * test: fix folderTypes, cross-env for windows * test: cross-env for windows SEED_FILTER, nyc only top-level * ci: no sha; change nut order * test: deploy nuts in parallel * ci: disable async nuts * test: exclude async nuts * test: revert async-exclude * test: revert tsconfig, revert config.yml * test: exclude env for seeds * feat: mark ignores * feat: status shows remote ignores * refactor: consistent naming of command classes * refactor: push feedback * style: comment is no longer a question * test: ebikes from clone instead of fs * fix: status flags are better * test: nuts with conn adjusted for git checkout * chore: bump sdr for Created state fix * test: unignore profiles * test: was not using ebikes, revert * chore: change status' flags in snapshot * refactor: move status logic to STL * test: update ignore nut * style: formatting line length * fix: push uses correct json * test: update tests for new push json format * test: ut on lts, not node17 * Wr/destructive deploy (#230) * fix: implementing destructive change deploy, 1 NUT * chore: add NUTs, fix UTs * chore: post-review board updates * chore: fix linting * chore: minor updates to flag logic, redo NUT query method * chore: remove unnecessary checks in NUTs * chore: bump SDR to 5, include deploy:destructive in NUTs * chore(release): 1.2.5 [ci skip] * fix: use cross-env for test:nuts script (#260) * chore(release): 1.2.6 [ci skip] * chore: unhide beta commands * chore: bump SDR and STL * test: and tracking nuts * ci: windows UT * test: nuts are hub-auth-agnostic * ci: windows only * ci: restore windows tests Co-authored-by: Willie Ruemmele <[email protected]> Co-authored-by: SF-CLI-BOT <[email protected]> Co-authored-by: svc-scm <[email protected]> Co-authored-by: Steve Hetzel <[email protected]> * Sm/quiet-flag (#259) * fix: push gets its own formatter for json reasons * fix: missing pull messages * feat: mark ignores * feat: status shows remote ignores * refactor: move status logic to STL * test: update ignore nut * style: formatting line length * fix: push uses correct json * test: update tests for new push json format * test: ut on lts, not node17 * test: include tracking nuts * feat: quiet flag on push (for stdout and json) * test: put the individual tracking nuts back * feat: quiet shows only failures in json * refactor: simplify quiet logic * test: ut for formatter with quiet * feat: show ignored if any * refactor: ignored as first column * fix: no spinner for conflict check on --overwrite * fix: early exit when there are no results * fix: handle no-result (deletes only, or no changes) * chore: bump sdr and stl Co-authored-by: Willie Ruemmele <[email protected]> Co-authored-by: SF-CLI-BOT <[email protected]> Co-authored-by: svc-scm <[email protected]> Co-authored-by: Steve Hetzel <[email protected]>
- Loading branch information
1 parent
84396b7
commit b871774
Showing
35 changed files
with
2,226 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,3 +39,6 @@ node_modules | |
# os specific files | ||
.DS_Store | ||
.idea | ||
|
||
# ignore generated nut tests | ||
test/nuts/generated/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"description": "pull source from the scratch org to the project", | ||
"descriptionLong": "Pulls changed source from the scratch org to your project to keep them in sync.", | ||
"help": "If the command detects a conflict, it displays the conflicts but does not complete the process. After reviewing the conflict, rerun the command with the --forceoverwrite parameter.", | ||
"flags": { | ||
"forceoverwrite": "ignore conflict warnings and overwrite changes to the project", | ||
"forceoverwriteLong": "Runs the pull command even if conflicts exist. Changes in the scratch org overwrite changes in the project.", | ||
"waitLong": "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." | ||
}, | ||
"NonScratchOrgPull": "We can\"t retrieve your changes. \"force:source:pull\" is only available for orgs that have source tracking enabled. Use \"force:source:retrieve\" or \"force:mdapi:retrieve\" instead.", | ||
"sourceConflictDetected": "Source conflict(s) detected.", | ||
"pull": "Your retrieve request did not complete within the specified wait time [%s minutes]. Try again with a longer wait time.", | ||
"retrievedSourceHeader": "Retrieved Source", | ||
"NoResultsFound": "No results found", | ||
"retrievedSourceWarningsHeader": "Retrieved Source Warnings", | ||
"retrieveTimeout": "Your retrieve request did not complete within the specified wait time [%s minutes]. Try again with a longer wait time." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
{ | ||
"description": "push source to a scratch org from the project", | ||
"descriptionLong": "Pushes changed source from your project to a scratch org to keep them in sync.", | ||
"help": "If the command detects a conflict, it displays the conflicts but does not complete the process. After reviewing the conflict, rerun the command with the --forceoverwrite parameter.", | ||
"flags": { | ||
"waitLong": "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.", | ||
"forceoverwrite": "ignore conflict warnings and overwrite changes to scratch org", | ||
"forceoverwriteLong": "Runs the push command even if conflicts exist. Changes in the project overwrite changes in the scratch org.", | ||
"replacetokens": "replace tokens in source files prior to deployment", | ||
"replacetokensLong": "Replaces tokens in source files prior to deployment.", | ||
"ignorewarnings": "deploy changes even if warnings are generated", | ||
"ignorewarningsLong": "Completes the deployment even if warnings are generated.", | ||
"quiet": "minimize json and sdtout output on success" | ||
}, | ||
"sourcepushFailed": "Push failed.", | ||
"conflictMsg": "We couldn't complete the push operation due to conflicts. Verify that you want to keep the local versions, then run \"sfdx force:source:push -f\" with the --forceoverwrite (-f) option." | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"description": "list local changes and/or changes in a scratch org", | ||
"LongDescription": "Lists changes that have been made locally, in a scratch org, or both.", | ||
"examples": [ | ||
"sfdx force:source:status -l", | ||
"sfdx force:source:status -r", | ||
"sfdx force:source:status -a", | ||
"sfdx force:source:status -a -u [email protected] --json" | ||
], | ||
"flags": { | ||
"all": "list all the changes that have been made", | ||
"allLong": "Lists all the changes that have been made.", | ||
"local": "list the changes that have been made locally", | ||
"localLong": "Lists the changes that have been made locally.", | ||
"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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"resetDescription": "reset local and remote source tracking\n\n WARNING: This command deletes or overwrites all existing source tracking files. Use with extreme caution. \n\nResets 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:status, the CLI returns no results, even though conflicts might actually exist. The CLI then resumes tracking new source changes as usual.\n\nUse 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:\n $ sfdx force:data:soql:query -q \"SELECT MemberName, MemberType, RevisionCounter FROM SourceMember\" -t", | ||
"clearDescription": "clear all local source tracking information\n\nWARNING: This command deletes or overwrites all existing source tracking files. Use with extreme caution.\n\nClears all local source tracking information. When you next run force:source:status, the CLI displays all local and remote files as changed, and any files with the same name are listed as conflicts.", | ||
"nopromptDescription": "do not prompt for source tracking override confirmation", | ||
"revisionDescription": "reset to a specific SourceMember revision counter number", | ||
"promptMessage": "WARNING: This operation will modify all your local source tracking files. The operation can have unintended consequences on all the force:source commands. Are you sure you want to proceed (y/n)?" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/* | ||
* 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 { FlagsConfig, flags } from '@salesforce/command'; | ||
import { Duration } from '@salesforce/kit'; | ||
import { Messages } from '@salesforce/core'; | ||
import { | ||
FileResponse, | ||
SourceComponent, | ||
ComponentSet, | ||
RetrieveResult, | ||
RequestStatus, | ||
ComponentStatus, | ||
} from '@salesforce/source-deploy-retrieve'; | ||
import { SourceTracking, throwIfInvalid, replaceRenamedCommands, ChangeResult } from '@salesforce/source-tracking'; | ||
import { processConflicts } from '../../../../formatters/conflicts'; | ||
import { SourceCommand } from '../../../../sourceCommand'; | ||
import { PullResponse, PullResultFormatter } from '../../../../formatters/pullFormatter'; | ||
Messages.importMessagesDirectory(__dirname); | ||
const messages: Messages = Messages.loadMessages('@salesforce/plugin-source', 'pull'); | ||
|
||
export default class Pull extends SourceCommand { | ||
public static description = messages.getMessage('description'); | ||
public static help = messages.getMessage('help'); | ||
protected static readonly flagsConfig: FlagsConfig = { | ||
forceoverwrite: flags.boolean({ | ||
char: 'f', | ||
description: messages.getMessage('flags.forceoverwrite'), | ||
}), | ||
// TODO: use shared flags from plugin-source | ||
wait: flags.minutes({ | ||
char: 'w', | ||
default: Duration.minutes(33), | ||
min: Duration.minutes(0), // wait=0 means deploy is asynchronous | ||
description: messages.getMessage('flags.waitLong'), | ||
}), | ||
}; | ||
|
||
protected static requiresUsername = true; | ||
protected static requiresProject = true; | ||
protected readonly lifecycleEventNames = ['preretrieve', 'postretrieve']; | ||
protected tracking: SourceTracking; | ||
protected retrieveResult: RetrieveResult; | ||
protected deleteFileResponses: FileResponse[]; | ||
|
||
public async run(): Promise<PullResponse[]> { | ||
await this.preChecks(); | ||
await this.retrieve(); | ||
// do not parallelize delete and retrieve...we only get to delete IF retrieve was successful | ||
await this.doDeletes(); // deletes includes its tracking file operations | ||
await this.updateTrackingFilesWithRetrieve(); | ||
this.ux.stopSpinner(); | ||
|
||
return this.formatResult(); | ||
} | ||
|
||
protected async preChecks(): Promise<void> { | ||
// checks the source tracking file version and throws if they're toolbelt's old version | ||
throwIfInvalid({ | ||
org: this.org, | ||
projectPath: this.project.getPath(), | ||
toValidate: 'plugin-source', | ||
command: replaceRenamedCommands('force:source:pull'), | ||
}); | ||
|
||
this.ux.startSpinner('Loading source tracking information'); | ||
this.tracking = await SourceTracking.create({ | ||
org: this.org, | ||
project: this.project, | ||
}); | ||
|
||
await this.tracking.ensureRemoteTracking(true); | ||
|
||
if (!this.flags.forceoverwrite) { | ||
this.ux.setSpinnerStatus('Checking for conflicts'); | ||
processConflicts(await this.tracking.getConflicts(), this.ux, messages.getMessage('sourceConflictDetected')); | ||
} | ||
} | ||
|
||
protected async doDeletes(): Promise<void> { | ||
this.ux.setSpinnerStatus('Checking for deletes from the org and updating source tracking files'); | ||
const changesToDelete = await this.tracking.getChanges<SourceComponent>({ | ||
origin: 'remote', | ||
state: 'delete', | ||
format: 'SourceComponent', | ||
}); | ||
this.deleteFileResponses = await this.tracking.deleteFilesAndUpdateTracking(changesToDelete); | ||
} | ||
|
||
protected async updateTrackingFilesWithRetrieve(): Promise<void> { | ||
this.ux.setSpinnerStatus('Updating source tracking files'); | ||
|
||
// might not exist if we exited from retrieve early | ||
if (!this.retrieveResult) { | ||
return; | ||
} | ||
const successes = this.retrieveResult | ||
.getFileResponses() | ||
.filter((fileResponse) => fileResponse.state !== ComponentStatus.Failed); | ||
|
||
await Promise.all([ | ||
// commit the local file successes that the retrieve modified | ||
this.tracking.updateLocalTracking({ | ||
files: successes.map((fileResponse) => fileResponse.filePath).filter(Boolean), | ||
}), | ||
this.tracking.updateRemoteTracking( | ||
successes.map(({ state, fullName, type, filePath }) => ({ state, fullName, type, filePath })), | ||
true // skip polling because it's a pull | ||
), | ||
]); | ||
} | ||
|
||
protected async retrieve(): Promise<void> { | ||
const componentSet = new ComponentSet(); | ||
( | ||
await this.tracking.getChanges<ChangeResult>({ | ||
origin: 'remote', | ||
state: 'nondelete', | ||
format: 'ChangeResult', | ||
}) | ||
).map((component) => { | ||
if (component.type && component.name) { | ||
componentSet.add({ | ||
type: component.type, | ||
fullName: component.name, | ||
}); | ||
} | ||
}); | ||
|
||
if (componentSet.size === 0) { | ||
return; | ||
} | ||
componentSet.sourceApiVersion = await this.getSourceApiVersion(); | ||
if (this.getFlag<string>('apiversion')) { | ||
componentSet.apiVersion = this.getFlag<string>('apiversion'); | ||
} | ||
|
||
const mdapiRetrieve = await componentSet.retrieve({ | ||
usernameOrConnection: this.org.getUsername(), | ||
merge: true, | ||
output: this.project.getDefaultPackage().path, | ||
}); | ||
|
||
this.ux.setSpinnerStatus('Retrieving metadata from the org'); | ||
|
||
// assume: remote deletes that get deleted locally don't fire hooks? | ||
await this.lifecycle.emit('preretrieve', componentSet.toArray()); | ||
this.retrieveResult = await mdapiRetrieve.pollStatus(1000, this.getFlag<Duration>('wait').seconds); | ||
|
||
// Assume: remote deletes that get deleted locally don't fire hooks. | ||
await this.lifecycle.emit('postretrieve', this.retrieveResult.getFileResponses()); | ||
} | ||
|
||
protected resolveSuccess(): void { | ||
// 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); | ||
} | ||
} | ||
|
||
protected formatResult(): PullResponse[] { | ||
const formatterOptions = { | ||
verbose: this.getFlag<boolean>('verbose', false), | ||
}; | ||
|
||
const formatter = new PullResultFormatter( | ||
this.logger, | ||
this.ux, | ||
formatterOptions, | ||
this.retrieveResult, | ||
this.deleteFileResponses | ||
); | ||
|
||
// Only display results to console when JSON flag is unset. | ||
if (!this.isJsonOutput()) { | ||
formatter.display(); | ||
} | ||
|
||
return formatter.getJson(); | ||
} | ||
} |
Oops, something went wrong.