Skip to content

Commit

Permalink
feat: add configuration for Bot metadata (#148)
Browse files Browse the repository at this point in the history
  • Loading branch information
scolladon authored Jun 14, 2021
1 parent c580cfb commit fa60587
Show file tree
Hide file tree
Showing 18 changed files with 270 additions and 89 deletions.
1 change: 1 addition & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

yarn pack
yarn test:coverage
138 changes: 88 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@

Generate the sfdx content in source format and destructive change from two git commits.

## TL;DR:

```sh
sfdx plugins:install sfdx-git-delta
```

```sh
sfdx sgd:source:delta --to "HEAD" --from "HEAD^" --output "."
```

```sh
echo "--- package.xml generated with added and modified metadata ---"
cat package/package.xml
echo
echo "---- Deploying added and modified metadata ----"
sfdx force:source:deploy -x package/package.xml
```

```sh
echo "--- destructiveChanges.xml generated with deleted metadata ---"
cat destructiveChanges/destructiveChanges.xml
echo
echo "--- Deleting removed metadata ---"
sfdx force:mdapi:deploy -d destructiveChanges --ignorewarnings
```

## What is SFDX-Git-Delta?

**SFDX-Git-Delta** (\*a.k.a. **SGD\***) helps Salesforce Architects and Developers accomplish 2 things with their source deployments:
Expand Down Expand Up @@ -43,38 +69,21 @@ Pro tips: If you are in the process of building your CI/CD pipeline, make sure y

You can use SGD as a Salesforce CLI plugin (`sfdx sgd:source:delta`), and this is now the recommended approach to get SGD:

```
```sh
sfdx plugins:install sfdx-git-delta
```

Because this plugin is not signed, you will get a warning saying that "This plugin is not digitally signed and its authenticity cannot be verified". This is expected, and you will have to answer `y` (yes) to proceed with the installation.

If you run your CI/CD jobs inside a Docker image, you can add the plugin to your image. Here is an example of a Dockerfile including the SGD plugin: https://github.com/mehdisfdc/sfdx-cli-gitlab

To view the full list and description of the sgd options, run `sfdx sgd:source:delta --help`

```
-t, --to [sha] commit sha to where the diff is done [HEAD] (default: "HEAD")
-f, --from [sha] commit sha from where the diff is done [git rev-list —max-parents=0 HEAD]
-o, --output [dir] source package specific output [./output] (default: "./output")
-a, --api-version [version] salesforce API version [50] (default: "50")
-i, --ignore specify the ignore file (default: ".forceignore")
-D, --ignore-destructive specify the ignore file (default: ".forceignore")
-r, --repo [dir] git repository location [.] (default: ".")
-d, --generate-delta generate delta files in [./output] folder
-h, --help output usage information
```

### Option #2 (legacy) - Install as the sgd command

Before the Salesforce CLI plugin was available, the old way to use this tool was through the `sgd` command (as described in the [old README](https://github.com/scolladon/sfdx-git-delta/blob/1093db6bd19eb48905db8f9aa5db086aa6707613/README.md)).
It is now recommended to use `sfdx sgd:source:delta`, but if you feel nostalgic about the `sgd` command, you can still get it through yarn (or npm): `yarn sfdx-git-delta@latest -g`

### Prerequisites

Works in Unix like system.
Windows is not tested.

Git command line is required on the system where the command line is running.

**Node v14.6.0 or above is required**.
Expand All @@ -83,35 +92,55 @@ If you encounter this issue while having installed the correct version of node o

## How to use it?

### TL;DR:
<!-- commands -->

```sh
sfdx sgd:source:delta --to HEAD --from HEAD^ --output .
```
- [`sfdx sgd:source:delta -f <string> [-t <string>] [-r <filepath>] [-i <filepath>] [-D <filepath>] [-o <filepath>] [-a <number>] [-d] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`](#sfdx-sgdsourcedelta--f-string--t-string--r-filepath--i-filepath--d-filepath--o-filepath--a-number--d---json---loglevel-tracedebuginfowarnerrorfataltracedebuginfowarnerrorfatal)

## `sfdx sgd:source:delta -f <string> [-t <string>] [-r <filepath>] [-i <filepath>] [-D <filepath>] [-o <filepath>] [-a <number>] [-d] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]`

Generate the sfdx content in source format and destructive change from two git commits

```sh
echo "--- package.xml generated with added and modified metadata ---"
cat package/package.xml
echo
echo "---- Deploying added and modified metadata ----"
sfdx force:source:deploy -x package/package.xml
```
USAGE
$ sfdx sgd:source:delta -f <string> [-t <string>] [-r <filepath>] [-i <filepath>] [-D <filepath>] [-o <filepath>] [-a
<number>] [-d] [--json] [--loglevel trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL]
```sh
echo "--- destructiveChanges.xml generated with deleted metadata ---"
cat destructiveChanges/destructiveChanges.xml
echo
echo "--- Deleting removed metadata ---"
sfdx force:mdapi:deploy -d destructiveChanges --ignorewarnings
OPTIONS
-D, --ignore-destructive=ignore-destructive ignore file to use
-a, --api-version=api-version [default: 51] salesforce API version
-d, --generate-delta generate delta files in [--output]
folder
-f, --from=from (required) commit sha from where the
diff is done [git rev-list
--max-parents=0 HEAD]
-i, --ignore=ignore ignore file to use
-o, --output=output [default: ./output] source package
specific output
-r, --repo=repo [default: .] git repository location
-t, --to=to [default: HEAD] commit sha to where
the diff is done
--json format output as json
--loglevel=(trace|debug|info|warn|error|fatal|TRACE|DEBUG|INFO|WARN|ERROR|FATAL) [default: warn] logging level for
this command invocation
```

_See code: [src/commands/sgd/source/delta.ts](https://github.com/scolladon/sfdx-git-delta/blob/v4.5.0/src/commands/sgd/source/delta.ts)_

<!-- commandsstop -->

### Important note for Windows users:

If you run SGD on a Windows system, make sure to use double quotes [to prevent the parameters from being interpreted by the terminal](https://github.com/scolladon/sfdx-git-delta/issues/134):
```sh
sfdx sgd:source:delta --to "HEAD" --from "HEAD^" --output .
```

### Scenario:
## Scenario:

Let’s take a look at the following scenario:

Expand All @@ -138,7 +167,7 @@ So let’s do it!
From the project repo folder, the CI pipeline will run the following command:

```sh
sfdx sgd:source:delta --to HEAD --from HEAD^ --output .
sfdx sgd:source:delta --to "HEAD" --from "HEAD^" --output .
```

which means:
Expand Down Expand Up @@ -212,9 +241,9 @@ Comparing changes performed in the `develop` branch since its common ancestor wi
sfdx sgd:source:delta --to develop --from $(git merge-base develop master) --output .
```

### Advanced use-cases:
## Advanced use-cases:

#### Generate a folder containing only the added/modified sources:
### Generate a folder containing only the added/modified sources:

Using a package.xml file to deploy a subset of the metadata is propably the simpliest approach to delta deployments. But there are some situations where you may want to have the actual source files related to all the components that have been changed recently.

Expand All @@ -226,15 +255,15 @@ Let's use this option with our previous example:

```sh
mkdir changed-sources
sfdx sgd:source:delta --to HEAD --from HEAD^ --output changed-sources/ --generate-delta
sfdx sgd:source:delta --to "HEAD" --from "HEAD^" --output changed-sources/ --generate-delta
```

In addition to the `package` and `destructiveChanges` folders, the `sfdx sgd:source:delta` command will also produce a copy of the added/changed files in the ouput folder.

_Content of the output folder when using the --generate-delta option, with the same scenario as above:_
![delta-source](/img/example_generateDelta.png)

#### Exclude some metadata only from destructiveChanges.xml:
### Exclude some metadata only from destructiveChanges.xml:

The `--ignore [-i]` parameter allows you to specify an [ignore file](https://git-scm.com/docs/gitignore) used to filter the
element on the diff to ignore. Every diff line matching the pattern from the ignore file specified in the `--ignore [-i]` will be ignored by SGD,
Expand All @@ -256,25 +285,34 @@ $ sfdx sgd:source:delta --from commit --ignore-destructive destructiveignore

Note that in a situation where only the `--ignore [-i]` parameter is specified (and `--ignore-destructive [-D]` is not specified), then the plugin will ignore items matching `--ignore [-i]` parameter in all situations: Addition, Modification and Deletion.

#### Generate a comma-separated list of the added and modified Apex classes:
### Generate a comma-separated list of the added and modified Apex classes:

Depending on your testing strategy, [you may be interested in generating a a comma-separated list of the added and modified Apex classes](https://github.com/scolladon/sfdx-git-delta/issues/126) (to use in the `sfdx force:source:deploy --testlevel RunSpecifiedTests` command, for example).

To cover this requirement, you can use a tool such as [yq](https://github.com/kislyuk/yq) to parse the content of the package.xml file produced by SGD:

`xq . < package/package.xml | jq '.Package.types | if type=="array" then .[] else . end | select(.name=="ApexClass") | .members | join(",")'`

## Javascript Module
### Use the module in your own node application

If you want to embed sgd in your node application, install it has a dependency for your application

```sh
yarn add sfdx-git-delta
```

Then use the javascript module

```js
// sample/app.js
const sgd = require('sfdx-git-delta')

const work = sgd({
to: '', // commit sha to where the diff is done. Default : HEAD
from: '', // commit sha from where the diff is done. Default : git rev-list --max-parents=0 HEAD
output: '', // source package specific output. Default : ./output
apiVersion: '', // salesforce API version. Default : 46
repo: '', // git repository location. Default : ./repo
to: '', // commit sha to where the diff is done. [default : "HEAD"]
from: '', // (required) commit sha from where the diff is done. [default : git rev-list --max-parents=0 HEAD]
output: '', // source package specific output. [default : "./output"]
apiVersion: '', // salesforce API version. [default : latest]
repo: '', // git repository location. [default : "."]
})

console.log(JSON.stringify(work))
Expand Down
File renamed without changes.
31 changes: 19 additions & 12 deletions __tests__/unit/lib/service/inFileHandler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,22 @@ const testContext = {
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>${os.EOL}<SharingRules xmlns="http://soap.sforce.com/2006/04/metadata">${os.EOL}<sharingCriteriaRules>${os.EOL}<fullName>TestCBS</fullName>${os.EOL}</sharingCriteriaRules>${os.EOL}<sharingOwnerRules>${os.EOL}<fullName>TestOBS</fullName>${os.EOL}</sharingOwnerRules>${os.EOL}</SharingRules>`,
'{"SharingRules":{"$":{"xmlns":"http://soap.sforce.com/2006/04/metadata"},"sharingCriteriaRules":[{"fullName":["TestCBS"]}],"sharingOwnerRules":[{"fullName":["TestOBS"]}]}}',
],
[
'bot',
'force-app/main/default/bot/Test.bot-meta.xml',
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>${os.EOL}<Bot xmlns="http://soap.sforce.com/2006/04/metadata">${os.EOL}<botVersions>${os.EOL}<fullName>v1</fullName>${os.EOL}</botVersions></Bot>`,
'{"Bot":{"$":{"xmlns":"http://soap.sforce.com/2006/04/metadata"},"BotVersion":[{"fullName":["v1"]}]}}',
],
],
expectedData: {
workflows: { 'workflows.alerts': new Set(['Account.TestEA']) },
labels: {},
sharingRules: {
'sharingRules.sharingCriteriaRules': new Set(['Account.TestCBS']),
},
bot: {
'bot.botVersions': new Set(['Test.v1']),
},
},
}
testContext.expectedData.labels[mc.LABEL_DIRECTORY_NAME] = new Set([
Expand All @@ -65,6 +74,7 @@ fsMocked.__setMockFiles({
[testContext.testData[2][1]]: testContext.testData[2][2],
[testContext.testData[3][1]]: testContext.testData[3][2],
[testContext.testData[4][1]]: testContext.testData[4][2],
[testContext.testData[5][1]]: testContext.testData[5][2],
})

xml2jsMocked.__setMockContent({
Expand All @@ -73,18 +83,23 @@ xml2jsMocked.__setMockContent({
[testContext.testData[2][2]]: testContext.testData[2][3],
[testContext.testData[3][2]]: testContext.testData[3][3],
[testContext.testData[4][2]]: testContext.testData[4][3],
[testContext.testData[5][2]]: testContext.testData[5][3],
})

// eslint-disable-next-line no-undef
describe(`test if inFileHandler`, () => {
let work
beforeEach(() => {
work = {
config: { output: '', repo: '', generateDelta: true },
diffs: { package: {}, destructiveChanges: {} },
warnings: [],
}
})
describe.each(testContext.testData)(
'handles',
(expectedType, changePath, xmlContent) => {
test('addition', () => {
const work = {
config: { output: '', repo: '', generateDelta: true },
diffs: { package: {}, destructiveChanges: {} },
}
const handler = new testContext.handler(
`A ${changePath}`,
expectedType,
Expand All @@ -110,10 +125,6 @@ describe(`test if inFileHandler`, () => {
}
})
test('deletion', () => {
const work = {
config: { output: '', repo: '', generateDelta: true },
diffs: { package: {}, destructiveChanges: {} },
}
const handler = new testContext.handler(
`D ${changePath}`,
expectedType,
Expand All @@ -138,10 +149,6 @@ describe(`test if inFileHandler`, () => {
expect(work.diffs.destructiveChanges).not.toHaveProperty('sharingRules')
})
test('modification', () => {
const work = {
config: { output: '', repo: '', generateDelta: true },
diffs: { package: {}, destructiveChanges: {} },
}
const handler = new testContext.handler(
`M ${changePath}`,
expectedType,
Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ module.exports = {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 75,
branches: 90,
functions: 90,
lines: 95,
statements: 95,
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"/npm-shrinkwrap.json",
"/oclif.manifest.json"
],
"main": "src/main.js",
"main": "lib/main.js",
"bin": {
"sgd": "./bin/cli"
},
Expand Down Expand Up @@ -109,4 +109,4 @@
"sfdc": {
"latestApiVersion": "51"
}
}
}
18 changes: 18 additions & 0 deletions src/metadata/a48.json
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,24 @@
"metaFile": false,
"xmlName": "WaveTemplateBundle"
},
{
"childXmlNames": [
"BotVersion"
],
"directoryName": "bot",
"inFolder": false,
"metaFile": false,
"suffix": "bot",
"xmlName": "Bot"
},
{
"directoryName": "bot.botVersions",
"inFolder": false,
"metaFile": false,
"parentXmlName": "Bot",
"xmlName": "BotVersion",
"xmlTag": "botVersions"
},
{
"directoryName": "workflows.alerts",
"inFolder": false,
Expand Down
18 changes: 18 additions & 0 deletions src/metadata/v46.json
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,24 @@
"metaFile": false,
"xmlName": "WaveTemplateBundle"
},
{
"childXmlNames": [
"BotVersion"
],
"directoryName": "bot",
"inFolder": false,
"metaFile": false,
"suffix": "bot",
"xmlName": "Bot"
},
{
"directoryName": "bot.botVersions",
"inFolder": false,
"metaFile": false,
"parentXmlName": "Bot",
"xmlName": "BotVersion",
"xmlTag": "botVersions"
},
{
"directoryName": "workflows.alerts",
"inFolder": false,
Expand Down
Loading

0 comments on commit fa60587

Please sign in to comment.