Skip to content

Commit f75bf3e

Browse files
committed
Merge remote-tracking branch 'upstream/main' into Keep-realm-topic-under-ODS
2 parents 3204373 + ca22f4d commit f75bf3e

19 files changed

Lines changed: 227 additions & 49 deletions

File tree

actions/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ GitHub Actions for automating Salesforce B2C Commerce operations with the [`@sal
4747
client-secret: ${{ secrets.SFCC_CLIENT_SECRET }}
4848
server: ${{ vars.SFCC_SERVER }}
4949
code-version: ${{ vars.SFCC_CODE_VERSION }}
50-
reload: true
50+
activate: true
5151
cartridges: 'app_storefront_base,app_custom'
5252
```
5353

actions/code-deploy/action.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ inputs:
99
description: 'Path to cartridge source directory'
1010
required: false
1111
default: '.'
12+
activate:
13+
description: 'Activate the code version after deploy'
14+
required: false
15+
default: 'false'
1216
reload:
13-
description: 'Activate/reload the code version after deploy'
17+
description: 'Alias for activate (deprecated)'
1418
required: false
15-
default: 'true'
19+
default: 'false'
1620
code-version:
1721
description: 'Code version to deploy to (overrides SFCC_CODE_VERSION env var)'
1822
required: false
@@ -81,7 +85,9 @@ runs:
8185
run: |
8286
CMD="code deploy ${{ inputs.cartridge-path }}"
8387
84-
if [ "${{ inputs.reload }}" = "true" ]; then
88+
if [ "${{ inputs.activate }}" = "true" ]; then
89+
CMD="$CMD --activate"
90+
elif [ "${{ inputs.reload }}" = "true" ]; then
8591
CMD="$CMD --reload"
8692
fi
8793

actions/workflow-templates/b2c-code-deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ jobs:
1818
username: ${{ secrets.SFCC_USERNAME }}
1919
password: ${{ secrets.SFCC_PASSWORD }}
2020
code-version: ${{ vars.SFCC_CODE_VERSION }}
21-
reload: true
21+
activate: true

docs/cli/code.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ In addition to [global flags](./index#global-flags):
113113

114114
| Flag | Description | Default |
115115
|------|-------------|---------|
116-
| `--reload`, `-r` | Reload (re-activate) code version after deploy | `false` |
116+
| `--activate`, `-a` | Activate code version after deploy | `false` |
117+
| `--reload`, `-r` | Reload (toggle activation to force reload) code version after deploy | `false` |
117118
| `--delete` | Delete existing cartridges before upload | `false` |
118119
| `--cartridge`, `-c` | Include specific cartridge(s) (can be repeated) | |
119120
| `--exclude-cartridge`, `-x` | Exclude specific cartridge(s) (can be repeated) | |
@@ -127,8 +128,8 @@ b2c code deploy --server my-sandbox.demandware.net --code-version v1
127128
# Deploy from a specific directory
128129
b2c code deploy ./my-project --server my-sandbox.demandware.net --code-version v1
129130

130-
# Deploy and reload the code version
131-
b2c code deploy --reload
131+
# Deploy and activate the code version
132+
b2c code deploy --activate
132133

133134
# Deploy specific cartridges only
134135
b2c code deploy -c app_storefront_base -c plugin_applepay
@@ -139,8 +140,8 @@ b2c code deploy -x test_cartridge -x int_debug
139140
# Delete existing cartridges before upload
140141
b2c code deploy --delete
141142

142-
# Delete and reload
143-
b2c code deploy --delete --reload
143+
# Delete and activate
144+
b2c code deploy --delete --activate
144145

145146
# Using environment variables
146147
export SFCC_SERVER=my-sandbox.demandware.net

docs/guide/ci-cd.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The official actions handle CLI installation, credential configuration, and Node
88

99
The actions are available from the `SalesforceCommerceCloud/b2c-developer-tooling` repository and support:
1010

11-
- **Code deployment** — deploy cartridges with reload
11+
- **Code deployment** — deploy and activate cartridges
1212
- **Data import** — import site archives in a single step
1313
- **MRT deployment** — push and deploy MRT storefront bundles
1414
- **Job execution** — run B2C jobs with wait and timeout
@@ -80,7 +80,7 @@ jobs:
8080
- uses: SalesforceCommerceCloud/b2c-developer-tooling/actions/code-deploy@v1
8181
with:
8282
code-version: ${{ steps.version.outputs.code-version }}
83-
reload: true
83+
activate: true
8484
```
8585
8686
The **setup** step installs the CLI and configures credentials for all subsequent steps. Everything after that — your build, version calculation, and deploy — can focus on your project's needs.
@@ -169,7 +169,7 @@ Deploy cartridges with typed inputs.
169169
| Input | Default | Description |
170170
|-------|---------|-------------|
171171
| `cartridge-path` | `.` | Path to cartridge source directory |
172-
| `reload` | `true` | Activate code version after deploy |
172+
| `activate` | `false` | Activate code version after deploy |
173173
| `code-version` || Code version (overrides env) |
174174
| `cartridges` || Comma-separated cartridges to include |
175175
| `exclude-cartridges` || Comma-separated cartridges to exclude |

packages/b2c-cli/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# @salesforce/b2c-cli
22

3+
## 1.0.1
4+
5+
### Patch Changes
6+
7+
- [`e597e61`](https://github.com/SalesforceCommerceCloud/b2c-developer-tooling/commit/e597e6131b9965e88ef75954a935695fa7f6d70f) - Add `--activate` flag to `code deploy` for activating a code version after deploy without the toggle behavior of `--reload`. Both `--activate` and `--reload` now error on failure instead of silently continuing. (Thanks [@clavery](https://github.com/clavery)!)
8+
9+
- Updated dependencies [[`e597e61`](https://github.com/SalesforceCommerceCloud/b2c-developer-tooling/commit/e597e6131b9965e88ef75954a935695fa7f6d70f)]:
10+
- @salesforce/b2c-tooling-sdk@1.0.1
11+
312
## 1.0.0
413

514
### Major Changes

packages/b2c-cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@salesforce/b2c-cli",
33
"description": "A Salesforce B2C Commerce CLI",
4-
"version": "1.0.0",
4+
"version": "1.0.1",
55
"author": "Charles Lavery",
66
"bin": {
77
"b2c": "./bin/run.js"

packages/b2c-cli/src/commands/code/deploy.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
uploadCartridges,
99
deleteCartridges,
1010
getActiveCodeVersion,
11+
activateCodeVersion,
1112
reloadCodeVersion,
1213
type DeployResult,
1314
} from '@salesforce/b2c-tooling-sdk/operations/code';
@@ -32,19 +33,27 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
3233
'<%= config.bin %> <%= command.id %>',
3334
'<%= config.bin %> <%= command.id %> ./my-cartridges',
3435
'<%= config.bin %> <%= command.id %> --server my-sandbox.demandware.net --code-version v1',
36+
'<%= config.bin %> <%= command.id %> --activate',
37+
'<%= config.bin %> <%= command.id %> --delete --activate',
3538
'<%= config.bin %> <%= command.id %> --reload',
36-
'<%= config.bin %> <%= command.id %> --delete --reload',
3739
'<%= config.bin %> <%= command.id %> -c app_storefront_base -c plugin_applepay',
3840
'<%= config.bin %> <%= command.id %> -x test_cartridge',
3941
];
4042

4143
static flags = {
4244
...CartridgeCommand.baseFlags,
4345
...CartridgeCommand.cartridgeFlags,
46+
activate: Flags.boolean({
47+
char: 'a',
48+
description: 'Activate code version after deploy',
49+
default: false,
50+
exclusive: ['reload'],
51+
}),
4452
reload: Flags.boolean({
4553
char: 'r',
46-
description: 'Reload (re-activate) code version after deploy',
54+
description: 'Reload (toggle activation to force reload) code version after deploy',
4755
default: false,
56+
exclusive: ['activate'],
4857
}),
4958
delete: Flags.boolean({
5059
description: 'Delete existing cartridges before upload',
@@ -56,6 +65,7 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
5665
uploadCartridges,
5766
deleteCartridges,
5867
getActiveCodeVersion,
68+
activateCodeVersion,
5969
reloadCodeVersion,
6070
};
6171

@@ -65,15 +75,15 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
6575
const hostname = this.resolvedConfig.values.hostname!;
6676
let version = this.resolvedConfig.values.codeVersion;
6777

68-
// OAuth is only required if:
78+
// OAuth is required if:
6979
// 1. No code version specified (need to auto-discover via OCAPI)
70-
// 2. --reload flag is set (need to call OCAPI to reload)
71-
const needsOAuth = !version || this.flags.reload;
80+
// 2. --activate or --reload flag is set (need to call OCAPI)
81+
const needsOAuth = !version || this.flags.activate || this.flags.reload;
7282
if (needsOAuth && !this.hasOAuthCredentials()) {
7383
const reason = version
7484
? t(
75-
'commands.code.deploy.oauthRequiredForReload',
76-
'The --reload flag requires OAuth credentials to reload the code version via OCAPI.',
85+
'commands.code.deploy.oauthRequiredForActivate',
86+
'The --activate/--reload flag requires OAuth credentials to manage the code version via OCAPI.',
7787
)
7888
: t(
7989
'commands.code.deploy.oauthRequiredForDiscovery',
@@ -109,6 +119,7 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
109119
cartridgePath: this.cartridgePath,
110120
hostname,
111121
codeVersion: version,
122+
activate: this.flags.activate,
112123
reload: this.flags.reload,
113124
delete: this.flags.delete,
114125
...this.cartridgeOptions,
@@ -125,6 +136,7 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
125136
return {
126137
cartridges: [],
127138
codeVersion: version,
139+
activated: false,
128140
reloaded: false,
129141
};
130142
}
@@ -159,20 +171,33 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
159171
// Upload cartridges
160172
await this.operations.uploadCartridges(this.instance, cartridges);
161173

162-
// Optionally reload code version
174+
// Optionally activate or reload code version
175+
let activated = false;
163176
let reloaded = false;
164-
if (this.flags.reload) {
165-
try {
177+
try {
178+
if (this.flags.activate) {
179+
await this.operations.activateCodeVersion(this.instance, version);
180+
activated = true;
181+
} else if (this.flags.reload) {
166182
await this.operations.reloadCodeVersion(this.instance, version);
183+
activated = true;
167184
reloaded = true;
168-
} catch (error) {
169-
this.logger?.debug(`Could not reload code version: ${error instanceof Error ? error.message : error}`);
170185
}
186+
} catch (error) {
187+
const clientId = this.resolvedConfig.values.clientId ?? 'unknown';
188+
this.error(
189+
t(
190+
'commands.code.deploy.activateFailed',
191+
'Failed to activate code version "{{version}}": {{message}}\n\nEnsure your OCAPI client ({{clientId}}) is configured with Data API permissions.\nSee: https://salesforcecommercecloud.github.io/b2c-developer-tooling/guide/authentication.html#ocapi-configuration',
192+
{version, message: error instanceof Error ? error.message : String(error), clientId},
193+
),
194+
);
171195
}
172196

173197
const result: DeployResult = {
174198
cartridges,
175199
codeVersion: version,
200+
activated,
176201
reloaded,
177202
};
178203

@@ -187,8 +212,12 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
187212
),
188213
);
189214

190-
if (result.reloaded) {
191-
this.log(t('commands.code.deploy.reloaded', 'Code version reloaded'));
215+
if (result.activated) {
216+
this.log(
217+
result.reloaded
218+
? t('commands.code.deploy.reloaded', 'Code version reloaded')
219+
: t('commands.code.deploy.activated', 'Code version activated'),
220+
);
192221
}
193222

194223
// Run afterOperation hooks with success

packages/b2c-cli/test/commands/code/deploy.test.ts

Lines changed: 88 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ describe('code deploy', () => {
4242

4343
const result = await command.run();
4444

45-
expect(result).to.deep.equal({cartridges: [], codeVersion: 'v1', reloaded: false});
45+
expect(result).to.deep.equal({cartridges: [], codeVersion: 'v1', activated: false, reloaded: false});
4646
});
4747

4848
it('errors when no cartridges are found', async () => {
@@ -90,11 +90,60 @@ describe('code deploy', () => {
9090
expect(uploadStub.calledOnceWithExactly(instance, cartridges)).to.equal(true);
9191
expect(reloadStub.calledOnceWithExactly(instance, 'v1')).to.equal(true);
9292

93-
expect(result).to.deep.include({codeVersion: 'v1', reloaded: true});
93+
expect(result).to.deep.include({codeVersion: 'v1', activated: true, reloaded: true});
9494
expect(afterHooksStub.calledOnce).to.equal(true);
9595
});
9696

97-
it('swallows reload errors and still succeeds', async () => {
97+
it('calls activate after deploy when --activate is set', async () => {
98+
const command: any = await createCommand({activate: true}, {cartridgePath: '.'});
99+
const instance = stubCommon(command);
100+
101+
sinon.stub(command, 'runBeforeHooks').resolves({skip: false});
102+
sinon.stub(command, 'runAfterHooks').resolves(void 0);
103+
104+
const cartridges = [{name: 'c1', src: '/tmp/c1', dest: 'c1'}];
105+
sinon.stub(command, 'findCartridgesWithProviders').resolves(cartridges);
106+
107+
const uploadStub = sinon.stub().resolves(void 0);
108+
const activateStub = sinon.stub().resolves(void 0);
109+
command.operations = {...command.operations, uploadCartridges: uploadStub, activateCodeVersion: activateStub};
110+
111+
const result = await command.run();
112+
113+
expect(activateStub.calledOnceWithExactly(instance, 'v1')).to.equal(true);
114+
expect(result).to.deep.include({codeVersion: 'v1', activated: true, reloaded: false});
115+
});
116+
117+
it('errors when activate fails', async () => {
118+
const command: any = await createCommand({activate: true}, {cartridgePath: '.'});
119+
stubCommon(command);
120+
121+
sinon.stub(command, 'runBeforeHooks').resolves({skip: false});
122+
sinon.stub(command, 'runAfterHooks').resolves(void 0);
123+
124+
const cartridges = [{name: 'c1', src: '/tmp/c1', dest: 'c1'}];
125+
sinon.stub(command, 'findCartridgesWithProviders').resolves(cartridges);
126+
127+
const uploadStub = sinon.stub().resolves(void 0);
128+
const activateStub = sinon.stub().rejects(new Error('activate failed'));
129+
command.operations = {...command.operations, uploadCartridges: uploadStub, activateCodeVersion: activateStub};
130+
131+
const errorStub = sinon.stub(command, 'error').throws(new Error('Expected error'));
132+
133+
try {
134+
await command.run();
135+
expect.fail('Should have thrown');
136+
} catch {
137+
// expected
138+
}
139+
140+
expect(errorStub.called).to.equal(true);
141+
const errorMessage = errorStub.firstCall.args[0];
142+
expect(errorMessage).to.include('activate failed');
143+
expect(errorMessage).to.include('OCAPI');
144+
});
145+
146+
it('errors when reload fails', async () => {
98147
const command: any = await createCommand({reload: true}, {cartridgePath: '.'});
99148
stubCommon(command);
100149

@@ -108,9 +157,19 @@ describe('code deploy', () => {
108157
const reloadStub = sinon.stub().rejects(new Error('reload failed'));
109158
command.operations = {...command.operations, uploadCartridges: uploadStub, reloadCodeVersion: reloadStub};
110159

111-
const result = await command.run();
160+
const errorStub = sinon.stub(command, 'error').throws(new Error('Expected error'));
112161

113-
expect(result.reloaded).to.equal(false);
162+
try {
163+
await command.run();
164+
expect.fail('Should have thrown');
165+
} catch {
166+
// expected
167+
}
168+
169+
expect(errorStub.called).to.equal(true);
170+
const errorMessage = errorStub.firstCall.args[0];
171+
expect(errorMessage).to.include('reload failed');
172+
expect(errorMessage).to.include('OCAPI');
114173
});
115174

116175
it('errors when no code version and no OAuth credentials', async () => {
@@ -156,7 +215,30 @@ describe('code deploy', () => {
156215

157216
expect(errorStub.calledOnce).to.equal(true);
158217
const errorMessage = errorStub.firstCall.args[0];
159-
expect(errorMessage).to.include('reload');
218+
expect(errorMessage).to.include('activate');
219+
});
220+
221+
it('errors when --activate flag set but no OAuth credentials', async () => {
222+
const command: any = await createCommand({activate: true}, {cartridgePath: '.'});
223+
224+
sinon.stub(command, 'requireWebDavCredentials').returns(void 0);
225+
sinon.stub(command, 'hasOAuthCredentials').returns(false);
226+
sinon.stub(command, 'log').returns(void 0);
227+
sinon.stub(command, 'warn').returns(void 0);
228+
sinon.stub(command, 'resolvedConfig').get(() => ({values: {hostname: 'example.com', codeVersion: 'v1'}}));
229+
230+
const errorStub = sinon.stub(command, 'error').throws(new Error('OAuth required'));
231+
232+
try {
233+
await command.run();
234+
expect.fail('Should have thrown');
235+
} catch {
236+
// expected
237+
}
238+
239+
expect(errorStub.calledOnce).to.equal(true);
240+
const errorMessage = errorStub.firstCall.args[0];
241+
expect(errorMessage).to.include('activate');
160242
});
161243

162244
it('uses active code version when resolvedConfig is missing codeVersion', async () => {

0 commit comments

Comments
 (0)