diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4a68785..5b3f1739 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 9ef3eba6..9f8851f8 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -12,7 +12,7 @@ permissions: concurrency: group: pages - cancel-in-progress: false + cancel-in-progress: true jobs: build: @@ -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 @@ -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 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8b1d3fb5..4cab8d00 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -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 @@ -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: | @@ -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: | @@ -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 @@ -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 @@ -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 }} diff --git a/AGENTS.md b/AGENTS.md index 595fca28..d79fcf8b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 @@ -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@` 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 @@ -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@` tag and docs rebuild Changeset guidelines: - Create a changeset for any user-facing changes (features, bug fixes); typically in new pull requests @@ -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 --- @@ -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 diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index cebe765c..a39a568d 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -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 @@ -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/'}, ]; } @@ -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; } @@ -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(), }, ], diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..933f3d86 --- /dev/null +++ b/docs/package.json @@ -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" + } +} diff --git a/docs/typedoc.json b/docs/typedoc.json new file mode 100644 index 00000000..6a6a4158 --- /dev/null +++ b/docs/typedoc.json @@ -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 + } +} diff --git a/package.json b/package.json index bf869a67..d2cf9221 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,10 @@ "lint:agent": "pnpm -r run lint:agent", "typecheck:agent": "pnpm -r run typecheck:agent", "build": "pnpm -r run build", - "docs:api": "typedoc", - "docs:dev": "pnpm run docs:api && vitepress dev docs", - "docs:build": "pnpm run docs:api && vitepress build docs", - "docs:preview": "vitepress preview docs", + "docs:api": "pnpm --filter @salesforce/b2c-dx-docs run docs:api", + "docs:dev": "pnpm --filter @salesforce/b2c-dx-docs run docs:dev", + "docs:build": "pnpm --filter @salesforce/b2c-dx-docs run docs:build", + "docs:preview": "pnpm --filter @salesforce/b2c-dx-docs run docs:preview", "changeset": "changeset", "version": "changeset version", "release": "echo 'Releases are handled by CI (publish.yml). Use workflow_dispatch for manual releases.' && exit 1" @@ -30,10 +30,6 @@ "@changesets/changelog-github": "^0.5.2", "@changesets/cli": "^2.29.8", "eslint-plugin-prettier": "catalog:", - "prettier": "catalog:", - "typedoc": "^0.28.14", - "typedoc-plugin-markdown": "^4.9.0", - "typedoc-vitepress-theme": "^1.1.2", - "vitepress": "^1.6.4" + "prettier": "catalog:" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 524170d1..2331b47b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -122,6 +122,12 @@ importers: prettier: specifier: 'catalog:' version: 3.6.2 + + docs: + devDependencies: + '@salesforce/b2c-tooling-sdk': + specifier: workspace:* + version: link:../packages/b2c-tooling-sdk typedoc: specifier: ^0.28.14 version: 0.28.14(typescript@5.9.3) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 90b7adcd..7f7f8bba 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,6 @@ packages: - packages/* + - docs catalog: # Production dependencies (exact versions for published packages) diff --git a/typedoc.json b/typedoc.json deleted file mode 100644 index b5f8ac8f..00000000 --- a/typedoc.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "$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": "./docs/api", - "plugin": ["typedoc-plugin-markdown", "typedoc-vitepress-theme"], - "tsconfig": "./packages/b2c-tooling-sdk/tsconfig.json", - "readme": "./docs/api-readme.md", - "excludePrivate": true, - "excludeProtected": true, - "excludeInternal": true, - "hideGenerator": true, - "githubPages": false, - "entryPointStrategy": "resolve", - "docsRoot": "./docs", - "navigation": { - "includeCategories": true, - "includeGroups": true - } -}