Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/site-cartridge-path-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@salesforce/b2c-cli': minor
'@salesforce/b2c-tooling-sdk': minor
---

Add site cartridge path management commands (`sites cartridges list|add|remove|set`) with `--bm` flag for Business Manager support and automatic fallback to site archive import when OCAPI permissions are unavailable
171 changes: 171 additions & 0 deletions docs/cli/sites.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ Sites commands require OAuth authentication with OCAPI permissions for the `/sit
|----------|---------|
| `/sites` | GET |
| `/sites/*` | GET |
| `/sites/*/cartridges` | POST, PUT, DELETE |

Cartridge path commands also work without the cartridge-specific OCAPI permissions — they automatically fall back to site archive import/export when direct OCAPI access is unavailable. The fallback requires job execution permissions for `sfcc-site-archive-import` and WebDAV write access to `Impex/`.

### Configuration

Expand Down Expand Up @@ -77,3 +80,171 @@ Found 2 site(s):
Status: online
```

---

## Cartridge Commands

Manage the cartridge path for a site — the ordered list of cartridges that are active on a storefront. Use `sites cartridges` or the singular alias `sites cartridge`.

::: tip Business Manager
Use the `--bm` flag as a shorthand for `--site-id Sites-Site` to manage the Business Manager cartridge path. BM updates always use site archive import since OCAPI direct updates are not supported for the BM site.
:::

::: tip Automatic Fallback
If OCAPI permissions for `/sites/*/cartridges` are not available, cartridge commands automatically fall back to site archive import/export. This means the commands work even without specific cartridge OCAPI permissions, as long as job execution and WebDAV access are configured.
:::

---

### b2c sites cartridges list

List the cartridge path for a site.

#### Usage

```bash
b2c sites cartridges list --site-id <site-id>
b2c sites cartridges list --bm
```

#### Flags

| Flag | Description |
|------|-------------|
| `--site-id <id>` | Site ID (e.g. `RefArch`) |
| `--bm` | Use Business Manager site (`Sites-Site`) |
| `--json` | Output as JSON |

One of `--site-id` or `--bm` is required.

#### Examples

```bash
# List cartridge path for a storefront site
b2c sites cartridges list --site-id RefArch

# List Business Manager cartridge path
b2c sites cartridges list --bm

# JSON output for automation
b2c sites cartridges list --site-id RefArch --json
```

---

### b2c sites cartridges add

Add a cartridge to a site's cartridge path.

#### Usage

```bash
b2c sites cartridges add <cartridge> --site-id <site-id> [--position <position>] [--target <target>]
```

#### Arguments

| Argument | Description |
|----------|-------------|
| `cartridge` | Name of the cartridge to add |

#### Flags

| Flag | Description |
|------|-------------|
| `--site-id <id>` | Site ID (e.g. `RefArch`) |
| `--bm` | Use Business Manager site (`Sites-Site`) |
| `--position <pos>` | Position: `first` (default), `last`, `before`, `after` |
| `--target <name>` | Target cartridge (required when position is `before` or `after`) |
| `--json` | Output as JSON |

#### Examples

```bash
# Add to beginning of path (default)
b2c sites cartridges add plugin_applepay --site-id RefArch

# Add to end
b2c sites cartridges add plugin_applepay --site-id RefArch --position last

# Add after a specific cartridge
b2c sites cartridges add plugin_applepay --site-id RefArch --position after --target app_storefront_base

# Add to Business Manager
b2c sites cartridges add bm_extension --bm --position first
```

---

### b2c sites cartridges remove

Remove a cartridge from a site's cartridge path.

::: warning Destructive Operation
This command modifies the site cartridge path. It is blocked in safe mode — use `--safety-level off` to allow it.
:::

#### Usage

```bash
b2c sites cartridges remove <cartridge> --site-id <site-id>
```

#### Arguments

| Argument | Description |
|----------|-------------|
| `cartridge` | Name of the cartridge to remove |

#### Flags

| Flag | Description |
|------|-------------|
| `--site-id <id>` | Site ID (e.g. `RefArch`) |
| `--bm` | Use Business Manager site (`Sites-Site`) |
| `--json` | Output as JSON |

#### Examples

```bash
b2c sites cartridges remove old_cartridge --site-id RefArch
b2c sites cartridges remove bm_extension --bm
```

---

### b2c sites cartridges set

Replace the entire cartridge path for a site.

::: warning Destructive Operation
This command replaces the entire cartridge path. It is blocked in safe mode — use `--safety-level off` to allow it.
:::

#### Usage

```bash
b2c sites cartridges set <cartridges> --site-id <site-id>
```

#### Arguments

| Argument | Description |
|----------|-------------|
| `cartridges` | New cartridge path (colon-separated, e.g. `cart1:cart2:cart3`) |

#### Flags

| Flag | Description |
|------|-------------|
| `--site-id <id>` | Site ID (e.g. `RefArch`) |
| `--bm` | Use Business Manager site (`Sites-Site`) |
| `--json` | Output as JSON |

#### Examples

```bash
b2c sites cartridges set "app_storefront_base:plugin_applepay:plugin_wishlists" --site-id RefArch
b2c sites cartridges set "bm_ext1:bm_ext2" --bm
```

2 changes: 1 addition & 1 deletion docs/guide/sdk-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ The OCAPI client is generated from the OpenAPI spec, so all paths, parameters, a
| `code.compare(...)` | _Not ported_ | |
| `code.diffdeploy(...)` | _Not ported_ | |
| `manifest.generate(...)` | _Not ported_ | |
| `cartridge.add(...)` | _Planned for a future release_ | |
| `cartridge.add(...)` | `addCartridge(instance, siteId, opts)` | `*/operations/sites` |
| `instance.upload(instance, file, token, opts, cb)` | `instance.webdav.put(path, data)` | `*/instance` |
| `instance.import(instance, file, token, cb)` | `siteArchiveImport(instance, zipPath)` | `*/operations/jobs` |
| `job.run(instance, jobId, params, token, cb)` | `executeJob(instance, jobId, opts?)` | `*/operations/jobs` |
Expand Down
11 changes: 11 additions & 0 deletions docs/guide/sfcc-ci-migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ The B2C CLI's canonical syntax uses spaces instead of colons (e.g., `b2c code de
| `sfcc-ci instance:import <archive>` | `b2c content import <archive>` | Or `b2c job run sfcc-site-archive-import` |
| `sfcc-ci instance:export` | `b2c content export` | See [Content commands](/cli/content) |

### Cartridge Path

| sfcc-ci | b2c-cli | Notes |
|---------|---------|-------|
| `sfcc-ci cartridge:add <name> --siteid <id>` | `b2c sites cartridges add <name> --site-id <id>` | Supports `--position` and `--target` flags |
| _(no equivalent)_ | `b2c sites cartridges list --site-id <id>` | List the active cartridge path |
| _(no equivalent)_ | `b2c sites cartridges remove <name> --site-id <id>` | Remove a cartridge from the path |
| _(no equivalent)_ | `b2c sites cartridges set <path> --site-id <id>` | Replace the entire cartridge path |

The B2C CLI also supports the Business Manager cartridge path via the `--bm` flag (shorthand for `--site-id Sites-Site`). When OCAPI direct permissions are unavailable, the commands automatically fall back to site archive import/export. See [Sites commands](/cli/sites#cartridge-commands) for details.

### Jobs

| sfcc-ci | b2c-cli | Notes |
Expand Down
2 changes: 2 additions & 0 deletions packages/b2c-cli/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ dw.json

export/
cartridges/
!src/commands/**/cartridges/
!test/commands/**/cartridges/
7 changes: 6 additions & 1 deletion packages/b2c-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,12 @@
}
},
"sites": {
"description": "List and inspect storefront sites\n\nDocs: https://salesforcecommercecloud.github.io/b2c-developer-tooling/cli/sites.html"
"description": "List and manage storefront sites\n\nDocs: https://salesforcecommercecloud.github.io/b2c-developer-tooling/cli/sites.html",
"subtopics": {
"cartridges": {
"description": "Manage site cartridge path (list, add, remove, set)"
}
}
},
"am": {
"description": "Manage Account Manager resources",
Expand Down
129 changes: 129 additions & 0 deletions packages/b2c-cli/src/commands/sites/cartridges/add.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright (c) 2025, Salesforce, Inc.
* SPDX-License-Identifier: Apache-2
* For full license text, see the license.txt file in the repo root or http://www.apache.org/licenses/LICENSE-2.0
*/
import {Args, Flags} from '@oclif/core';
import {InstanceCommand} from '@salesforce/b2c-tooling-sdk/cli';
import {
type CartridgePathResult,
type CartridgePosition,
BM_SITE_ID,
addCartridge,
} from '@salesforce/b2c-tooling-sdk/operations/sites';
import {t, withDocs} from '../../../i18n/index.js';

export default class SitesCartridgesAdd extends InstanceCommand<typeof SitesCartridgesAdd> {
static description = withDocs(
t('commands.sites.cartridges.add.description', "Add a cartridge to a site's cartridge path"),
'/cli/sites.html#b2c-sites-cartridges-add',
);

static enableJsonFlag = true;

static examples = [
'<%= config.bin %> sites cartridges add my_cartridge --site-id RefArch',
'<%= config.bin %> sites cartridges add my_cartridge --site-id RefArch --position first',
'<%= config.bin %> sites cartridges add my_cartridge --site-id RefArch --position after --target app_storefront_base',
'<%= config.bin %> sites cartridges add bm_extension --bm',
];

static hiddenAliases = ['sites:cartridge:add'];

static args = {
cartridge: Args.string({
description: t('args.cartridge.description', 'Cartridge name to add'),
required: true,
}),
};

static flags = {
'site-id': Flags.string({
description: t('flags.siteId.description', 'Site ID (e.g. RefArch)'),
exclusive: ['bm'],
}),
bm: Flags.boolean({
description: t('flags.bm.description', 'Use Business Manager site (Sites-Site)'),
exclusive: ['site-id'],
}),
position: Flags.string({
description: t('flags.position.description', 'Position to add the cartridge'),
options: ['first', 'last', 'before', 'after'],
default: 'first',
}),
target: Flags.string({
description: t('flags.target.description', 'Target cartridge (required when position is before/after)'),
}),
};

async run(): Promise<CartridgePathResult> {
this.requireOAuthCredentials();

const siteId = this.resolveSiteId();
const {cartridge} = this.args;
const position = this.flags.position as CartridgePosition;
const {target} = this.flags;

// Validate target is provided for relative positions
if ((position === 'before' || position === 'after') && !target) {
this.error(
t('commands.sites.cartridges.add.targetRequired', '--target is required when --position is "{{position}}"', {
position,
}),
);
}

const result = await addCartridge(
this.instance,
siteId,
{name: cartridge, position, target},
{
log: (msg) => {
if (!this.jsonEnabled()) this.log(msg);
},
waitOptions: {
onProgress: (exec, elapsed) => {
if (!this.jsonEnabled()) {
const elapsedSec = Math.floor(elapsed / 1000);
this.log(
t('commands.sites.cartridges.jobProgress', ' Status: {{status}} ({{elapsed}}s elapsed)', {
status: exec.execution_status,
elapsed: elapsedSec.toString(),
}),
);
}
},
},
},
);

if (this.jsonEnabled()) {
return result;
}

this.log(
t('commands.sites.cartridges.add.success', 'Added "{{cartridge}}" to site "{{siteId}}" cartridge path.', {
cartridge,
siteId,
}),
);
this.log(
t('commands.sites.cartridges.add.updatedPath', 'Updated path: {{cartridges}}', {
cartridges: result.cartridges,
}),
);

return result;
}

private resolveSiteId(): string {
const siteId = this.flags['site-id'];
const bm = this.flags.bm;

if (!siteId && !bm) {
this.error(t('commands.sites.cartridges.siteIdRequired', 'Provide --site-id <id> or --bm to specify a site.'));
}

return bm ? BM_SITE_ID : siteId!;
}
}
Loading
Loading