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
7 changes: 7 additions & 0 deletions .changeset/sfcc-ci-migration-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@salesforce/b2c-dx-docs': patch
'@salesforce/b2c-tooling-sdk': patch
'@salesforce/b2c-cli': patch
---

Added sfcc-ci migration guide with command mappings and CI/CD migration instructions. Added backward-compatible sfcc-ci command aliases (`client:auth`, `code:deploy`, `code:list`, `code:activate`, `job:run`, etc.) and environment variable aliases (`SFCC_OAUTH_CLIENT_ID`, `SFCC_OAUTH_CLIENT_SECRET`, `SFCC_LOGIN_URL`).
1 change: 1 addition & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const guidesSidebar = [
items: [
{text: 'Authentication Setup', link: '/guide/authentication'},
{text: 'CI/CD with GitHub Actions', link: '/guide/ci-cd'},
{text: 'sfcc-ci Migration', link: '/guide/sfcc-ci-migration'},
{text: 'Account Manager', link: '/guide/account-manager'},
{text: 'Analytics Reports (CIP/CCAC)', link: '/guide/analytics-reports-cip-ccac'},
{text: 'IDE Integration', link: '/guide/ide-integration'},
Expand Down
215 changes: 215 additions & 0 deletions docs/guide/sfcc-ci-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
---
description: Migrate from sfcc-ci to @salesforce/b2c-cli with command mappings, environment variable compatibility, and CI/CD pipeline updates.
---

# Migrating from sfcc-ci

[sfcc-ci](https://github.com/SalesforceCommerceCloud/sfcc-ci) is deprecated. The `@salesforce/b2c-cli` package is its replacement, providing the same core capabilities plus additional features.

If you haven't installed the B2C CLI yet, see the [Installation guide](./installation).

## Authentication

The biggest change from sfcc-ci is how authentication works by default.

**sfcc-ci** uses **stateful auth** — you run `sfcc-ci client:auth` once to store a token, then all subsequent commands use it automatically.

**b2c-cli** defaults to **stateless auth** — credentials are provided per-command via environment variables or flags. This is more explicit and works better in CI/CD pipelines where each step is independent.

### Stateful Auth (sfcc-ci Compatible)

For users who prefer the sfcc-ci workflow, the B2C CLI includes a compatibility layer that mirrors the stateful auth pattern:

| sfcc-ci | b2c-cli |
|---------|---------|
| `sfcc-ci client:auth <id> <secret>` | `b2c client:auth --client-id <id> --client-secret <secret>` |
| `sfcc-ci client:auth:renew` | `b2c client:auth:renew` |
| `sfcc-ci client:auth:token` | `b2c client:auth:token` |
| `sfcc-ci auth:login [client]` | `b2c auth:login [client]` |
| `sfcc-ci auth:logout` | `b2c auth:logout` |

The colon-separated forms are aliases — the canonical commands are `b2c auth client`, `b2c auth login`, etc.

After authenticating, subsequent commands automatically use the stored token when it is valid. See [Auth Commands](/cli/auth) for full details.

### Stateless Auth (Recommended for CI/CD)

For automation and CI/CD, set credentials as environment variables and the CLI uses them directly — no separate auth step needed:

```bash
export SFCC_CLIENT_ID=my-client-id
export SFCC_CLIENT_SECRET=my-secret

# Commands authenticate automatically
b2c code list
b2c sandbox list
```

See [Authentication Setup](./authentication) for the full guide including Account Manager configuration, OCAPI permissions, and WebDAV access.

::: tip
Use **stateless auth** (environment variables) for CI/CD pipelines and **stateful auth** (`b2c auth client` or `b2c auth login`) for local development.
:::

## Command Mapping

The B2C CLI's canonical syntax uses spaces instead of colons (e.g., `b2c code deploy`), but for the most commonly used commands, sfcc-ci's colon-separated syntax is also accepted (e.g., `b2c code:deploy`). The tables below show the drop-in colon form where available.

### Code Management

| sfcc-ci | b2c-cli | Notes |
|---------|---------|-------|
| `sfcc-ci code:list` | `b2c code:list` | |
| `sfcc-ci code:deploy <archive>` | `b2c code:deploy` | Deploys from local cartridge source |
| `sfcc-ci code:activate <version>` | `b2c code:activate <version>` | |
| `sfcc-ci code:delete` | `b2c code:delete` | |

### Instance / Data

| sfcc-ci | b2c-cli | Notes |
|---------|---------|-------|
| `sfcc-ci instance:upload <file>` | `b2c webdav put <file> <remote-path>` | See [WebDAV commands](/cli/webdav) |
| `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) |

### Jobs

| sfcc-ci | b2c-cli | Notes |
|---------|---------|-------|
| `sfcc-ci job:run <id>` | `b2c job:run <id>` | Supports `--wait` and `--timeout` |
| `sfcc-ci job:status <id> <exec>` | `b2c job wait <id> --execution-id <exec>` | |

### Sandbox

Sandbox commands map directly, with spaces replacing colons:

| sfcc-ci | b2c-cli |
|---------|---------|
| `sfcc-ci sandbox:list` | `b2c sandbox list` |
| `sfcc-ci sandbox:create` | `b2c sandbox create` |
| `sfcc-ci sandbox:get` | `b2c sandbox get` |
| `sfcc-ci sandbox:delete` | `b2c sandbox delete` |
| `sfcc-ci sandbox:start` | `b2c sandbox start` |
| `sfcc-ci sandbox:stop` | `b2c sandbox stop` |
| `sfcc-ci sandbox:restart` | `b2c sandbox restart` |
| `sfcc-ci sandbox:reset` | `b2c sandbox reset` |
| `sfcc-ci sandbox:alias:list` | `b2c sandbox alias list` |
| `sfcc-ci sandbox:alias:add` | `b2c sandbox alias create` |
| `sfcc-ci sandbox:alias:delete` | `b2c sandbox alias delete` |

### SLAS

| sfcc-ci | b2c-cli |
|---------|---------|
| `sfcc-ci slas:client:add` | `b2c slas client create` |
| `sfcc-ci slas:client:get` | `b2c slas client get` |
| `sfcc-ci slas:client:list` | `b2c slas client list` |
| `sfcc-ci slas:client:delete` | `b2c slas client delete` |

### User / Org / Role (Account Manager)

Account Manager operations are under the `am` topic:

| sfcc-ci | b2c-cli |
|---------|---------|
| `sfcc-ci user:list` | `b2c am users list` |
| `sfcc-ci user:create` | `b2c am users create` |
| `sfcc-ci user:delete` | `b2c am users delete` |
| `sfcc-ci org:list` | `b2c am orgs list` |
| `sfcc-ci role:list` | `b2c am roles list` |
| `sfcc-ci role:grant` | `b2c am roles grant` |
| `sfcc-ci role:revoke` | `b2c am roles revoke` |

## Environment Variables

Most sfcc-ci environment variables are supported directly or through backward-compatible aliases. Existing CI/CD configurations using these variables will continue to work.

| sfcc-ci | b2c-cli | Status |
|---------|---------|--------|
| `SFCC_OAUTH_CLIENT_ID` | `SFCC_CLIENT_ID` | Both accepted |
| `SFCC_OAUTH_CLIENT_SECRET` | `SFCC_CLIENT_SECRET` | Both accepted |
| `SFCC_LOGIN_URL` | `SFCC_ACCOUNT_MANAGER_HOST` | Both accepted |
| `SFCC_OAUTH_USER_NAME` | `SFCC_OAUTH_USER_NAME` | Same |
| `SFCC_OAUTH_USER_PASSWORD` | `SFCC_OAUTH_USER_PASSWORD` | Same |
| `SFCC_SANDBOX_API_HOST` | `SFCC_SANDBOX_API_HOST` | Same |
| `SFCC_SCAPI_SHORTCODE` | `SFCC_SHORTCODE` | Renamed |
| `SFCC_SCAPI_TENANTID` | `SFCC_TENANT_ID` | Renamed |

The B2C CLI also introduces new environment variables not present in sfcc-ci:

| Variable | Purpose |
|----------|---------|
| `SFCC_SERVER` | B2C instance hostname |
| `SFCC_USERNAME` | WebDAV / Basic auth username |
| `SFCC_PASSWORD` | WebDAV / Basic auth password |
| `SFCC_CODE_VERSION` | Code version for deployments |
| `SFCC_AUTH_METHODS` | Auth method priority (comma-separated) |

See the [Configuration guide](./configuration) for the complete list of environment variables and configuration sources.

## Configuration

The `dw.json` configuration file is fully supported. Fields are normalized to camelCase (e.g., `client-id` in the file becomes `clientId` internally), but both kebab-case and camelCase are accepted.

The B2C CLI adds a layered configuration system with a clear priority order:

1. CLI flags (highest priority)
2. Environment variables (`SFCC_*`)
3. `dw.json` in the current directory
4. Instance configuration (`b2c setup instance`)
5. Defaults (lowest priority)

Instance management uses `b2c setup instance create` instead of `sfcc-ci instance:add`. See the [Configuration guide](./configuration) for details.

## CI/CD Migration

### Before (sfcc-ci)

sfcc-ci CI/CD pipelines typically authenticate first, then run commands:

```bash
# Authenticate (stores token)
sfcc-ci client:auth $SFCC_OAUTH_CLIENT_ID $SFCC_OAUTH_CLIENT_SECRET

# Deploy code
sfcc-ci code:deploy build/code.zip -i $INSTANCE
sfcc-ci code:activate v1 -i $INSTANCE
```

### After (b2c-cli — stateless)

With the B2C CLI, set environment variables and run commands directly:

```bash
# No separate auth step — credentials are used automatically
export SFCC_CLIENT_ID=$SFCC_OAUTH_CLIENT_ID
export SFCC_CLIENT_SECRET=$SFCC_OAUTH_CLIENT_SECRET
export SFCC_SERVER=$INSTANCE

b2c code deploy
b2c code activate v1
```

### After (b2c-cli — stateful, sfcc-ci compatible)

If you prefer minimal changes to your existing pipeline scripts:

```bash
# Same pattern as sfcc-ci — colon-separated aliases are supported
b2c client:auth --client-id $SFCC_OAUTH_CLIENT_ID --client-secret $SFCC_OAUTH_CLIENT_SECRET

b2c code:deploy
b2c code:activate v1 --server $INSTANCE
```

### GitHub Actions

The B2C CLI provides official GitHub Actions that handle installation, credential configuration, and caching automatically. See the [CI/CD with GitHub Actions](./ci-cd) guide for complete examples and action reference.

## Next Steps

- [Installation](./installation) — install the B2C CLI
- [Authentication Setup](./authentication) — configure API clients, OCAPI, and WebDAV
- [Configuration](./configuration) — environment variables, dw.json, and instance management
- [CI/CD with GitHub Actions](./ci-cd) — official GitHub Actions for automation
- [CLI Reference](/cli/) — browse all available commands
5 changes: 5 additions & 0 deletions packages/b2c-cli/src/commands/auth/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
* Use --renew to enable automatic token renewal for later use with `auth client renew`.
*/
export default class AuthClient extends BaseCommand<typeof AuthClient> {
static hiddenAliases = ['client:auth'];

static description = t('commands.auth.client.description', 'Authenticate an API client and save session');

Check warning on line 26 in packages/b2c-cli/src/commands/auth/client/index.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Expected "description" to come before "hiddenAliases"

Check warning on line 26 in packages/b2c-cli/src/commands/auth/client/index.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Expected "description" to come before "hiddenAliases"

static examples = [
'<%= config.bin %> <%= command.id %> --client-id <id> --client-secret <secret>',
Expand All @@ -34,16 +36,19 @@
'client-id': Flags.string({
description: 'Client ID for OAuth',
env: 'SFCC_CLIENT_ID',
default: async () => process.env.SFCC_OAUTH_CLIENT_ID || undefined,
helpGroup: 'AUTH',
}),
'client-secret': Flags.string({
description: 'Client secret for OAuth',
env: 'SFCC_CLIENT_SECRET',
default: async () => process.env.SFCC_OAUTH_CLIENT_SECRET || undefined,
helpGroup: 'AUTH',
}),
'account-manager-host': Flags.string({
description: `Account Manager hostname for OAuth (default: ${DEFAULT_ACCOUNT_MANAGER_HOST})`,
env: 'SFCC_ACCOUNT_MANAGER_HOST',
default: async () => process.env.SFCC_LOGIN_URL || undefined,
helpGroup: 'AUTH',
}),
'auth-scope': Flags.string({
Expand Down
3 changes: 3 additions & 0 deletions packages/b2c-cli/src/commands/auth/client/renew.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
* to client_credentials grant using the stored base64-encoded client:secret.
*/
export default class AuthClientRenew extends BaseCommand<typeof AuthClientRenew> {
static hiddenAliases = ['client:auth:renew'];

static description = t('commands.auth.client.renew.description', 'Renew the client authentication token');

Check warning on line 24 in packages/b2c-cli/src/commands/auth/client/renew.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Expected "description" to come before "hiddenAliases"

Check warning on line 24 in packages/b2c-cli/src/commands/auth/client/renew.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Expected "description" to come before "hiddenAliases"

static examples = ['<%= config.bin %> <%= command.id %>'];

Expand All @@ -27,6 +29,7 @@
'account-manager-host': Flags.string({
description: `Account Manager hostname for OAuth (default: ${DEFAULT_ACCOUNT_MANAGER_HOST})`,
env: 'SFCC_ACCOUNT_MANAGER_HOST',
default: async () => process.env.SFCC_LOGIN_URL || undefined,
helpGroup: 'AUTH',
}),
};
Expand Down
2 changes: 2 additions & 0 deletions packages/b2c-cli/src/commands/auth/client/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
* Mirrors sfcc-ci `client:auth:token` command behavior.
*/
export default class AuthClientToken extends BaseCommand<typeof AuthClientToken> {
static hiddenAliases = ['client:auth:token'];

static description = t(

Check warning on line 30 in packages/b2c-cli/src/commands/auth/client/token.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Expected "description" to come before "hiddenAliases"

Check warning on line 30 in packages/b2c-cli/src/commands/auth/client/token.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Expected "description" to come before "hiddenAliases"
'commands.auth.client.token.description',
'Return the current authentication token (stateful)',
);
Expand Down
3 changes: 3 additions & 0 deletions packages/b2c-cli/src/commands/auth/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
* until it expires or you run auth:logout.
*/
export default class AuthLogin extends BaseCommand<typeof AuthLogin> {
static hiddenAliases = ['auth:login'];

static args = {

Check warning on line 20 in packages/b2c-cli/src/commands/auth/login.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Expected "args" to come before "hiddenAliases"

Check warning on line 20 in packages/b2c-cli/src/commands/auth/login.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Expected "args" to come before "hiddenAliases"
clientId: Args.string({
description: 'Client ID for OAuth (falls back to SFCC_CLIENT_ID env var)',
required: false,
Expand All @@ -33,6 +35,7 @@
'account-manager-host': Flags.string({
description: `Account Manager hostname for OAuth (default: ${DEFAULT_ACCOUNT_MANAGER_HOST})`,
env: 'SFCC_ACCOUNT_MANAGER_HOST',
default: async () => process.env.SFCC_LOGIN_URL || undefined,
helpGroup: 'AUTH',
}),
'auth-scope': Flags.string({
Expand Down
2 changes: 2 additions & 0 deletions packages/b2c-cli/src/commands/auth/logout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
* (client credentials or implicit) when configured.
*/
export default class AuthLogout extends BaseCommand<typeof AuthLogout> {
static hiddenAliases = ['auth:logout'];

static description = withDocs(

Check warning on line 18 in packages/b2c-cli/src/commands/auth/logout.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Expected "description" to come before "hiddenAliases"

Check warning on line 18 in packages/b2c-cli/src/commands/auth/logout.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Expected "description" to come before "hiddenAliases"
t('commands.auth.logout.description', 'Clear stored session (stateful auth)'),
'/cli/auth.html#b2c-auth-logout',
);
Expand Down
2 changes: 2 additions & 0 deletions packages/b2c-cli/src/commands/code/activate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import {t, withDocs} from '../../i18n/index.js';

export default class CodeActivate extends InstanceCommand<typeof CodeActivate> {
static hiddenAliases = ['code:activate'];

static args = {

Check warning on line 14 in packages/b2c-cli/src/commands/code/activate.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Expected "args" to come before "hiddenAliases"

Check warning on line 14 in packages/b2c-cli/src/commands/code/activate.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Expected "args" to come before "hiddenAliases"
codeVersion: Args.string({
description: 'Code version ID to activate',
required: false,
Expand Down
2 changes: 2 additions & 0 deletions packages/b2c-cli/src/commands/code/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
}

export default class CodeDelete extends InstanceCommand<typeof CodeDelete> {
static hiddenAliases = ['code:delete'];

static args = {

Check warning on line 32 in packages/b2c-cli/src/commands/code/delete.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Expected "args" to come before "hiddenAliases"

Check warning on line 32 in packages/b2c-cli/src/commands/code/delete.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Expected "args" to come before "hiddenAliases"
codeVersion: Args.string({
description: 'Code version ID to delete',
required: true,
Expand Down
2 changes: 2 additions & 0 deletions packages/b2c-cli/src/commands/code/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
import {t, withDocs} from '../../i18n/index.js';

export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
static hiddenAliases = ['code:deploy'];

static args = {

Check warning on line 20 in packages/b2c-cli/src/commands/code/deploy.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Expected "args" to come before "hiddenAliases"

Check warning on line 20 in packages/b2c-cli/src/commands/code/deploy.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Expected "args" to come before "hiddenAliases"
...CartridgeCommand.baseArgs,
};

Expand Down Expand Up @@ -57,7 +59,7 @@
reloadCodeVersion,
};

async run(): Promise<DeployResult> {

Check warning on line 62 in packages/b2c-cli/src/commands/code/deploy.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Async method 'run' has a complexity of 21. Maximum allowed is 20

Check warning on line 62 in packages/b2c-cli/src/commands/code/deploy.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Async method 'run' has a complexity of 21. Maximum allowed is 20
this.requireWebDavCredentials();

const hostname = this.resolvedConfig.values.hostname!;
Expand Down
2 changes: 2 additions & 0 deletions packages/b2c-cli/src/commands/code/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
const DEFAULT_COLUMNS = ['id', 'active', 'rollback', 'lastModified', 'cartridges'];

export default class CodeList extends InstanceCommand<typeof CodeList> {
static hiddenAliases = ['code:list'];

static description = withDocs(

Check warning on line 39 in packages/b2c-cli/src/commands/code/list.ts

View workflow job for this annotation

GitHub Actions / test (22.x)

Expected "description" to come before "hiddenAliases"

Check warning on line 39 in packages/b2c-cli/src/commands/code/list.ts

View workflow job for this annotation

GitHub Actions / test (24.x)

Expected "description" to come before "hiddenAliases"
t('commands.code.list.description', 'List code versions on a B2C Commerce instance'),
'/cli/code.html#b2c-code-list',
);
Expand Down
2 changes: 2 additions & 0 deletions packages/b2c-cli/src/commands/job/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
import {t, withDocs} from '../../i18n/index.js';

export default class JobRun extends JobCommand<typeof JobRun> {
static hiddenAliases = ['job:run'];

static args = {
jobId: Args.string({
description: 'Job ID to execute',
Expand Down
3 changes: 3 additions & 0 deletions packages/b2c-tooling-sdk/src/cli/oauth-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ export abstract class OAuthCommand<T extends typeof Command> extends BaseCommand
'client-id': Flags.string({
description: 'Client ID for OAuth',
env: 'SFCC_CLIENT_ID',
default: async () => process.env.SFCC_OAUTH_CLIENT_ID || undefined,
helpGroup: 'AUTH',
}),
'client-secret': Flags.string({
description: 'Client Secret for OAuth',
env: 'SFCC_CLIENT_SECRET',
default: async () => process.env.SFCC_OAUTH_CLIENT_SECRET || undefined,
helpGroup: 'AUTH',
}),
'auth-scope': Flags.string({
Expand Down Expand Up @@ -90,6 +92,7 @@ export abstract class OAuthCommand<T extends typeof Command> extends BaseCommand
'account-manager-host': Flags.string({
description: `Account Manager hostname for OAuth (default: ${DEFAULT_ACCOUNT_MANAGER_HOST})`,
env: 'SFCC_ACCOUNT_MANAGER_HOST',
default: async () => process.env.SFCC_LOGIN_URL || undefined,
helpGroup: 'AUTH',
}),
};
Expand Down
4 changes: 4 additions & 0 deletions packages/b2c-tooling-sdk/src/config/sources/env-source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import {getLogger} from '../../logging/logger.js';
* Mapping of SFCC_* environment variable names to NormalizedConfig field names.
*/
const ENV_VAR_MAP: Record<string, keyof NormalizedConfig> = {
// sfcc-ci legacy aliases — listed first so canonical names below take precedence
SFCC_OAUTH_CLIENT_ID: 'clientId',
SFCC_OAUTH_CLIENT_SECRET: 'clientSecret',
SFCC_LOGIN_URL: 'accountManagerHost',
SFCC_SERVER: 'hostname',
SFCC_WEBDAV_SERVER: 'webdavHostname',
SFCC_CODE_VERSION: 'codeVersion',
Expand Down
Loading