Skip to content
Open
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
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ jobs:
- name: Build packages
run: pnpm -r run build

- name: Build documentation
run: pnpm run docs:build

- name: Run SDK tests
id: sdk-test
working-directory: packages/b2c-tooling-sdk
Expand Down
55 changes: 41 additions & 14 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ permissions:

concurrency:
group: pages
cancel-in-progress: false
cancel-in-progress: true

jobs:
build:
Expand Down Expand Up @@ -54,20 +54,44 @@ jobs:
- name: Get latest release tag
id: release
run: |
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
# Find the most recent package tag and docs tag
PKG_TAG=$(git describe --tags --abbrev=0 --match '@salesforce/*' 2>/dev/null || echo "")
DOCS_TAG=$(git describe --tags --abbrev=0 --match 'docs@*' 2>/dev/null || echo "")

# Pick whichever tag is closer to HEAD (fewer commits behind)
LATEST_TAG=""
if [[ -n "$PKG_TAG" && -n "$DOCS_TAG" ]]; then
PKG_DISTANCE=$(git rev-list --count "${PKG_TAG}..HEAD")
DOCS_DISTANCE=$(git rev-list --count "${DOCS_TAG}..HEAD")
if [[ "$DOCS_DISTANCE" -le "$PKG_DISTANCE" ]]; then
LATEST_TAG="$DOCS_TAG"
else
LATEST_TAG="$PKG_TAG"
fi
elif [[ -n "$PKG_TAG" ]]; then
LATEST_TAG="$PKG_TAG"
elif [[ -n "$DOCS_TAG" ]]; then
LATEST_TAG="$DOCS_TAG"
fi

echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
echo "exists=$([[ -n $LATEST_TAG ]] && echo true || echo false)" >> $GITHUB_OUTPUT

- name: Build main documentation
run: pnpm run docs:build
env:
RELEASE_VERSION: ${{ steps.release.outputs.tag }}
- name: Read CLI version for display
id: cli-version
run: |
CLI_VERSION=$(node -p "require('./packages/b2c-cli/package.json').version")
echo "version=$CLI_VERSION" >> $GITHUB_OUTPUT

- name: Build release documentation
- name: Build dev documentation
run: |
IS_DEV_BUILD=true RELEASE_VERSION=${{ steps.cli-version.outputs.version }} pnpm run docs:build
mv docs/.vitepress/dist docs/.vitepress/dist-dev

- name: Build stable documentation from release tag
if: steps.release.outputs.exists == 'true'
run: |
# Save main build and config (config has version dropdown code)
mv docs/.vitepress/dist docs/.vitepress/dist-main
# Save config from main (has version dropdown and dynamic base path)
cp docs/.vitepress/config.mts /tmp/config.mts

# Remove S3-extracted uxstudio files before checkout to avoid conflicts
Expand All @@ -87,14 +111,17 @@ jobs:
# Restore config from main (has version dropdown and dynamic base path)
cp /tmp/config.mts docs/.vitepress/config.mts

# Build at release tag with main's config
# Build at release tag with main's config (no IS_DEV_BUILD = stable at root)
pnpm install --frozen-lockfile
pnpm -r run build
RELEASE_VERSION=${{ steps.release.outputs.tag }} IS_RELEASE_BUILD=true pnpm run docs:build
RELEASE_VERSION=${{ steps.cli-version.outputs.version }} pnpm run docs:build

# Combine: stable at root, dev in /dev/
mv docs/.vitepress/dist-dev docs/.vitepress/dist/dev

# Combine: main at root, release in /release/
mv docs/.vitepress/dist docs/.vitepress/dist-main/release
mv docs/.vitepress/dist-main docs/.vitepress/dist
- name: Use dev as root when no release exists
if: steps.release.outputs.exists != 'true'
run: mv docs/.vitepress/dist-dev docs/.vitepress/dist

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
Expand Down
37 changes: 36 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0 # Needed for docs tag detection

- name: Determine release type
id: release-type
Expand Down Expand Up @@ -81,6 +83,16 @@ jobs:
check_package "@salesforce/b2c-cli" "packages/b2c-cli" "cli"
check_package "@salesforce/b2c-dx-mcp" "packages/b2c-dx-mcp" "mcp"

# Check if docs version changed (private package — not published to npm, uses git tag)
DOCS_VERSION=$(node -p "require('./docs/package.json').version")
if git rev-parse "docs@${DOCS_VERSION}" >/dev/null 2>&1; then
echo "publish_docs=false" >> $GITHUB_OUTPUT
else
echo "publish_docs=true" >> $GITHUB_OUTPUT
echo "version_docs=${DOCS_VERSION}" >> $GITHUB_OUTPUT
fi
echo "@salesforce/b2c-docs: version=${DOCS_VERSION}"

- name: Create snapshot versions
if: steps.release-type.outputs.type == 'nightly'
run: |
Expand Down Expand Up @@ -147,6 +159,14 @@ jobs:
echo "No tags to create"
fi

- name: Create docs tag if version changed
if: steps.release-type.outputs.type == 'stable' && steps.packages.outputs.publish_docs == 'true'
run: |
DOCS_TAG="docs@${{ steps.packages.outputs.version_docs }}"
git tag "$DOCS_TAG"
git push origin "$DOCS_TAG"
echo "Created docs tag: $DOCS_TAG"

- name: Extract changelogs for release
if: steps.release-type.outputs.type == 'stable'
run: |
Expand Down Expand Up @@ -180,6 +200,13 @@ jobs:
extract_latest packages/b2c-tooling-sdk/CHANGELOG.md
echo ""
fi

if [[ "${{ steps.packages.outputs.publish_docs }}" == "true" && -f docs/CHANGELOG.md ]]; then
echo "## Documentation"
echo ""
extract_latest docs/CHANGELOG.md
echo ""
fi
} > /tmp/release-notes.md

- name: Create GitHub Release
Expand All @@ -192,6 +219,8 @@ jobs:
RELEASE_TAG="@salesforce/b2c-tooling-sdk@${{ steps.packages.outputs.version_sdk }}"
elif [[ "${{ steps.packages.outputs.publish_mcp }}" == "true" ]]; then
RELEASE_TAG="@salesforce/b2c-dx-mcp@${{ steps.packages.outputs.version_mcp }}"
elif [[ "${{ steps.packages.outputs.publish_docs }}" == "true" ]]; then
RELEASE_TAG="docs@${{ steps.packages.outputs.version_docs }}"
else
echo "No packages published, skipping release"
exit 0
Expand Down Expand Up @@ -224,10 +253,16 @@ jobs:
elif [[ "${{ steps.packages.outputs.publish_mcp }}" == "true" ]]; then
RELEASE_TAG="@salesforce/b2c-dx-mcp@${{ steps.packages.outputs.version_mcp }}"
else
echo "No release to upload to"
echo "No package release to upload to"
exit 0
fi

gh release upload "$RELEASE_TAG" b2c-skills.zip b2c-cli-skills.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Trigger documentation deployment
if: steps.release-type.outputs.type == 'stable'
run: gh workflow run deploy-docs.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36 changes: 32 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This is a monorepo project with the following packages:
- `./packages/b2c-cli` - the command line interface built with oclif
- `./packages/b2c-tooling-sdk` - the SDK/library for B2C Commerce operations; supports the CLI and can be used standalone
- `./packages/b2c-dx-mcp` - Model Context Protocol server; also built with oclif
- `./docs` - documentation site (private `@salesforce/b2c-dx-docs` workspace package; not published to npm)

## Common Commands

Expand Down Expand Up @@ -97,10 +98,33 @@ See [documentation skill](./.claude/skills/documentation/SKILL.md) for details o
# Run docs dev server (from project root)
pnpm run docs:dev

# Build docs for production
# Build docs for production (stable mode — root base path)
pnpm run docs:build

# Build docs in dev mode (/dev/ base path)
IS_DEV_BUILD=true pnpm run docs:build
```

### Docs workspace package

The `./docs` directory is a private workspace package (`@salesforce/b2c-dx-docs`) with a dependency on `@salesforce/b2c-tooling-sdk`. This exists to support doc-only releases and changelog tracking via changesets, not for npm publishing. It has no build script — `pnpm -r run build` skips it.

**Deployed URL structure:** stable/released docs are served at the root URL, dev docs (from `main`) live at `/dev/`.

**Doc-only releases:** to release documentation changes without bumping CLI/SDK/MCP, create a changeset that targets only the docs package:

```md
---
'@salesforce/b2c-dx-docs': patch
---

Improved authentication guide with step-by-step examples
```

This produces a `docs@<version>` tag and triggers a docs rebuild on merge of the version PR.

**Automatic cascade:** because the docs package depends on the SDK, when the SDK version is bumped by a changeset, `updateInternalDependencies: "patch"` auto-bumps the docs version too — triggering a docs rebuild (correct since API docs are generated from the SDK).

## Logging

- when logging use the logger instance from `@salesforce/b2c-tooling-sdk/logger` package
Expand Down Expand Up @@ -142,8 +166,9 @@ This project uses [Changesets](https://github.com/changesets/changesets) for ver

**How it works:**
- A changeset affecting only the SDK bumps only the SDK version
- Packages that depend on a bumped package get an automatic patch bump (via `updateInternalDependencies: "patch"`) — e.g., if SDK bumps, CLI and MCP auto-get a patch bump because they depend on it
- Only packages with a newer version than what's on npm get published
- Packages that depend on a bumped package get an automatic patch bump (via `updateInternalDependencies: "patch"`) — e.g., if SDK bumps, CLI, MCP, and Docs all auto-get a patch bump
- Only packages with a newer version than what's on npm get published (docs package is private and uses git tags instead)
- A changeset targeting only `@salesforce/b2c-dx-docs` triggers a doc-only release — no npm packages are published, just a `docs@<version>` tag and docs rebuild

Changeset guidelines:
- Create a changeset for any user-facing changes (features, bug fixes); typically in new pull requests
Expand All @@ -157,7 +182,9 @@ Changeset guidelines:
- HOW a consumer should update their code
- Good changesets are brief and user-focused (not contributor); they are generally 1 line or two; The content of the changeset is used in CHANGELOG and release notes. You do not need to list internal implementation details or all details of commands; just the high level summary for users.

create a changeset file directly in `.changeset/` with a unique filename (e.g., `descriptive-change-name.md`):
Valid changeset packages: `@salesforce/b2c-cli`, `@salesforce/b2c-tooling-sdk`, `@salesforce/b2c-dx-mcp`, `@salesforce/b2c-dx-docs`

Create a changeset file directly in `.changeset/` with a unique filename (e.g., `descriptive-change-name.md`):

```md
---
Expand All @@ -169,3 +196,4 @@ Description of the change explaining WHAT, WHY, and HOW to update
```

- Include only the packages that were directly modified
- For doc-only changes, target `@salesforce/b2c-dx-docs` instead of the CLI/SDK/MCP packages
32 changes: 16 additions & 16 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import typedocSidebar from '../api/typedoc-sidebar.json';

// Version configuration from environment
const releaseVersion = process.env.RELEASE_VERSION || 'unreleased';
const isReleaseBuild = process.env.IS_RELEASE_BUILD === 'true';
const isDevBuild = process.env.IS_DEV_BUILD === 'true';

// Base paths - release build lives in /release/ subdirectory
// Base paths - dev build lives in /dev/ subdirectory, stable/release is at root
const siteBase = '/b2c-developer-tooling';
const basePath = isReleaseBuild ? `${siteBase}/release/` : `${siteBase}/`;
const basePath = isDevBuild ? `${siteBase}/dev/` : `${siteBase}/`;

// Build version dropdown items
// VitePress prepends base path to links starting with /, so we use relative paths
Expand All @@ -18,19 +18,19 @@ function getVersionItems() {
return [{text: 'Development (main)', link: '/'}];
}

if (isReleaseBuild) {
// Release build: base is /b2c-developer-tooling/release/
// Use ../ to navigate up to main docs
if (isDevBuild) {
// Dev build: base is /b2c-developer-tooling/dev/
// Use ../ to navigate up to stable docs at root
return [
{text: 'Development (main)', link: '../'},
{text: 'Latest Release', link: '/'},
{text: 'Latest Release', link: '../'},
{text: 'Development (main)', link: '/'},
];
}

// Main build: base is /b2c-developer-tooling/
// Stable build: base is /b2c-developer-tooling/
return [
{text: 'Development (main)', link: '/'},
{text: 'Latest Release', link: '/release/'},
{text: 'Latest Release', link: '/'},
{text: 'Development (main)', link: '/dev/'},
];
}

Expand Down Expand Up @@ -99,15 +99,15 @@ document.addEventListener('click', (e) => {
if (!link) return;
const href = link.getAttribute('href');
// Check if this is a version switch link
if (href && (href.includes('/release/') || href === '../')) {
if (href && (href.includes('/dev/') || href === '../')) {
e.preventDefault();
e.stopPropagation();
if (href === '../') {
// Navigate from /release/ back to main - construct path explicitly
// Navigate from /dev/ back to stable root - construct path explicitly
// to avoid relative path issues with trailing slashes
const path = window.location.pathname;
const mainPath = path.replace(/\\/release\\/.*$/, '/').replace(/\\/release$/, '/');
window.location.href = mainPath;
const stablePath = path.replace(/\\/dev\\/.*$/, '/').replace(/\\/dev$/, '/');
window.location.href = stablePath;
} else {
window.location.href = link.href;
}
Expand Down Expand Up @@ -140,7 +140,7 @@ export default defineConfig({
{text: 'CLI Reference', link: '/cli/'},
{text: 'API Reference', link: '/api/'},
{
text: isReleaseBuild ? 'Latest Release' : 'dev',
text: isDevBuild ? 'dev' : `v${releaseVersion}`,
items: getVersionItems(),
},
],
Expand Down
19 changes: 19 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@salesforce/b2c-dx-docs",
"version": "0.1.0",
"private": true,
"description": "Documentation for B2C Developer Tooling",
"scripts": {
"docs:api": "typedoc",
"docs:dev": "pnpm run docs:api && vitepress dev",
"docs:build": "pnpm run docs:api && vitepress build",
"docs:preview": "vitepress preview"
},
"devDependencies": {
"@salesforce/b2c-tooling-sdk": "workspace:*",
"typedoc": "^0.28.14",
"typedoc-plugin-markdown": "^4.9.0",
"typedoc-vitepress-theme": "^1.1.2",
"vitepress": "^1.6.4"
}
}
37 changes: 37 additions & 0 deletions docs/typedoc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$schema": "https://typedoc.org/schema.json",
"entryPoints": [
"../packages/b2c-tooling-sdk/src/config/index.ts",
"../packages/b2c-tooling-sdk/src/auth/index.ts",
"../packages/b2c-tooling-sdk/src/clients/index.ts",
"../packages/b2c-tooling-sdk/src/instance/index.ts",
"../packages/b2c-tooling-sdk/src/logging/index.ts",
"../packages/b2c-tooling-sdk/src/operations/code/index.ts",
"../packages/b2c-tooling-sdk/src/operations/cip/index.ts",
"../packages/b2c-tooling-sdk/src/operations/jobs/index.ts",
"../packages/b2c-tooling-sdk/src/operations/logs/index.ts",
"../packages/b2c-tooling-sdk/src/operations/mrt/index.ts",
"../packages/b2c-tooling-sdk/src/operations/ods/index.ts",
"../packages/b2c-tooling-sdk/src/scaffold/index.ts",
"../packages/b2c-tooling-sdk/src/docs/index.ts",
"../packages/b2c-tooling-sdk/src/schemas/index.ts",
"../packages/b2c-tooling-sdk/src/cli/index.ts",
"../packages/b2c-tooling-sdk/src/i18n/index.ts"
],
"exclude": ["**/*.generated.ts"],
"out": "./api",
"plugin": ["typedoc-plugin-markdown", "typedoc-vitepress-theme"],
"tsconfig": "../packages/b2c-tooling-sdk/tsconfig.json",
"readme": "./api-readme.md",
"excludePrivate": true,
"excludeProtected": true,
"excludeInternal": true,
"hideGenerator": true,
"githubPages": false,
"entryPointStrategy": "resolve",
"docsRoot": ".",
"navigation": {
"includeCategories": true,
"includeGroups": true
}
}
Loading
Loading