Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
40 changes: 40 additions & 0 deletions .github/actions/setup-windows/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: setup-windows
description: 'Set up a Windows CI runner for the B2C developer tooling monorepo (Node, pnpm, dependency install, pnpm-store cache).'

# Mirrors the SalesforceCommerceCloud/pwa-kit `setup_windows` composite action.
# Windows runs everything under Git Bash (`shell: bash`) so the inline `env VAR=value ...`
# style used in the package.json test scripts keeps working on windows-latest.

inputs:
node-version:
description: 'Node.js version to install (matches `actions/setup-node` input).'
required: false
default: '22.x'

runs:
using: composite
steps:
- name: Setup Node.js ${{ inputs.node-version }}
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version }}

- name: Setup pnpm
uses: pnpm/action-setup@v5

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_ENV"

- name: Setup pnpm cache
uses: actions/cache@v5
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
shell: bash
run: pnpm install --frozen-lockfile
102 changes: 102 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,105 @@ jobs:
packages/b2c-tooling-sdk/coverage/
packages/b2c-cli/coverage/
retention-days: 30

test-windows:
runs-on: windows-latest
# Advisory rollout: Windows coverage is new; surface failures without
# blocking Linux CI while remaining Windows-specific test issues are
# addressed in follow-up PRs. Remove once Windows tests are green.
continue-on-error: true
defaults:
run:
shell: bash
strategy:
fail-fast: false
matrix:
node-version: [22.x, 24.x]
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Windows machine
uses: ./.github/actions/setup-windows
with:
node-version: ${{ matrix.node-version }}
- name: Build packages
run: pnpm -r run build
- name: Run SDK tests
id: sdk-test
working-directory: packages/b2c-tooling-sdk
run: pnpm run pretest && pnpm run test:ci && pnpm run lint
- name: Run MCP tests
id: mcp-test
if: always() && steps.sdk-test.conclusion != 'cancelled'
working-directory: packages/b2c-dx-mcp
run: pnpm run pretest && pnpm run test:ci && pnpm run lint
- name: Run CLI tests
id: cli-test
if: always() && steps.mcp-test.conclusion != 'cancelled'
working-directory: packages/b2c-cli
# Use the Windows-specific script (test:ci:win) to bypass c8's
# coverage threshold check. V8 coverage on Windows double-counts
# some TS source files (distinct URL casing emitted by the tsx
# loader), which drops the reported function-coverage number below
# the Linux-calibrated 70% threshold even though the tests pass.
# Coverage is still generated and uploaded for inspection.
run: pnpm run pretest && pnpm run test:ci:win && pnpm run lint
- name: Run VS Extension checks
if: always() && steps.cli-test.conclusion != 'cancelled'
working-directory: packages/b2c-vs-extension
run: pnpm run typecheck:agent && pnpm run lint
- name: Print Windows test failures
# Mocha's JSON reporter swallows stdout, so Windows-only failures are
# invisible in the step log. Parse the per-package test-results.json
# and echo failing test titles + error messages to the job log for triage.
if: always() && steps.sdk-test.conclusion != 'cancelled'
shell: bash
run: |
node -e "
const fs = require('node:fs');
const path = require('node:path');
const reports = [
'packages/b2c-tooling-sdk/test-results.json',
'packages/b2c-dx-mcp/test-results.json',
'packages/b2c-cli/test-results.json',
];
let totalFailures = 0;
for (const report of reports) {
if (!fs.existsSync(report)) continue;
const data = JSON.parse(fs.readFileSync(report, 'utf8'));
const failures = data.failures || [];
if (failures.length === 0) continue;
totalFailures += failures.length;
console.log('\n=== ' + report + ' (' + failures.length + ' failures) ===');
for (const f of failures) {
console.log('\n✖ ' + f.fullTitle);
if (f.err && f.err.message) console.log(' message: ' + f.err.message.split('\n')[0]);
if (f.err && f.err.stack) console.log(' stack: ' + f.err.stack.split('\n').slice(0, 5).join('\n '));
}
}
if (totalFailures === 0) console.log('No Windows test failures reported.');
else console.log('\nTotal Windows failures: ' + totalFailures);
"
- name: Test Report
uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0
if: always() && steps.sdk-test.conclusion != 'cancelled'
with:
name: Test Results (Windows Node ${{ matrix.node-version }})
path: 'packages/*/test-results.json'
reporter: mocha-json
- name: Upload test results (for Windows triage)
if: always() && steps.sdk-test.conclusion != 'cancelled'
uses: actions/upload-artifact@v7
with:
name: test-results-windows-node-${{ matrix.node-version }}
path: 'packages/*/test-results.json'
retention-days: 30
- name: Upload coverage reports
if: always() && steps.sdk-test.conclusion != 'cancelled'
uses: actions/upload-artifact@v7
with:
name: coverage-reports-windows-node-${{ matrix.node-version }}
path: |
packages/b2c-tooling-sdk/coverage/
packages/b2c-cli/coverage/
retention-days: 30
64 changes: 64 additions & 0 deletions .github/workflows/e2e-shell-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,67 @@ jobs:
echo "Running E2E shell tests with realm: ${TEST_REALM}"
cd packages/b2c-cli
./test/functional/e2e_cli_test.sh

e2e-shell-tests-windows:
runs-on: windows-latest
# Advisory rollout: surface Windows shell-test failures without blocking
# the Linux shell-test job while Windows-specific issues are triaged.
continue-on-error: true
environment: e2e-dev
timeout-minutes: 45
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6

- name: Check for required secrets and vars
id: check-secrets
env:
SFCC_CLIENT_ID: ${{ vars.SFCC_CLIENT_ID }}
SFCC_CLIENT_SECRET: ${{ secrets.SFCC_CLIENT_SECRET }}
TEST_REALM: ${{ vars.TEST_REALM }}
SFCC_ACCOUNT_MANAGER_HOST: ${{ vars.SFCC_ACCOUNT_MANAGER_HOST }}
SFCC_SANDBOX_API_HOST: ${{ vars.SFCC_SANDBOX_API_HOST }}
SFCC_SHORTCODE: ${{ vars.SFCC_SHORTCODE }}
run: |
missing=""
[ -z "$SFCC_CLIENT_ID" ] && missing="$missing SFCC_CLIENT_ID"
[ -z "$SFCC_CLIENT_SECRET" ] && missing="$missing SFCC_CLIENT_SECRET"
[ -z "$TEST_REALM" ] && missing="$missing TEST_REALM"
[ -z "$SFCC_ACCOUNT_MANAGER_HOST" ] && missing="$missing SFCC_ACCOUNT_MANAGER_HOST"
[ -z "$SFCC_SANDBOX_API_HOST" ] && missing="$missing SFCC_SANDBOX_API_HOST"
[ -z "$SFCC_SHORTCODE" ] && missing="$missing SFCC_SHORTCODE"

if [ -z "$missing" ]; then
echo "has-secrets=true" >> "$GITHUB_OUTPUT"
else
echo "has-secrets=false" >> "$GITHUB_OUTPUT"
echo "Windows E2E shell tests skipped - missing required variables:$missing" >> "$GITHUB_STEP_SUMMARY"
fi

- name: Setup Windows machine
if: steps.check-secrets.outputs.has-secrets == 'true'
uses: ./.github/actions/setup-windows
with:
node-version: '24'

- name: Build packages
if: steps.check-secrets.outputs.has-secrets == 'true'
run: pnpm -r run build

- name: Run E2E Shell Tests
if: steps.check-secrets.outputs.has-secrets == 'true'
env:
SFCC_CLIENT_ID: ${{ vars.SFCC_CLIENT_ID }}
SFCC_CLIENT_SECRET: ${{ secrets.SFCC_CLIENT_SECRET }}
SFCC_ACCOUNT_MANAGER_HOST: ${{ vars.SFCC_ACCOUNT_MANAGER_HOST }}
SFCC_SANDBOX_API_HOST: ${{ vars.SFCC_SANDBOX_API_HOST }}
SFCC_SHORTCODE: ${{ vars.SFCC_SHORTCODE }}
TEST_REALM: ${{ vars.TEST_REALM }}
SFCC_EXTRA_HEADERS: ${{ secrets.SFCC_EXTRA_HEADERS }}
CURL_EXTRA_HEADERS: ${{ secrets.CURL_EXTRA_HEADERS }}
run: |
echo "Running Windows E2E shell tests with realm: ${TEST_REALM}"
cd packages/b2c-cli
./test/functional/e2e_cli_test.sh
77 changes: 77 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,83 @@ jobs:
github.rest.issues.create(issue);
}

e2e-tests-windows:
name: e2e-tests (windows)
# Advisory rollout: Windows E2E coverage is new; surface failures without
# blocking Linux E2E while remaining Windows-specific test issues are
# addressed in follow-up PRs. Remove once Windows E2E is green.
continue-on-error: true
strategy:
fail-fast: false
matrix:
node-version: [22.x]
runs-on: windows-latest
environment: e2e-dev
timeout-minutes: 60
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6
- name: Check for required secrets and vars
id: check-secrets
env:
SFCC_CLIENT_ID: ${{ vars.SFCC_CLIENT_ID }}
SFCC_CLIENT_SECRET: ${{ secrets.SFCC_CLIENT_SECRET }}
TEST_REALM: ${{ vars.TEST_REALM }}
SFCC_ACCOUNT_MANAGER_HOST: ${{ vars.SFCC_ACCOUNT_MANAGER_HOST }}
SFCC_SANDBOX_API_HOST: ${{ vars.SFCC_SANDBOX_API_HOST }}
SFCC_SHORTCODE: ${{ vars.SFCC_SHORTCODE }}
run: |
if [ -n "$SFCC_CLIENT_ID" ] && [ -n "$SFCC_CLIENT_SECRET" ] && [ -n "$TEST_REALM" ] && [ -n "$SFCC_ACCOUNT_MANAGER_HOST" ] && [ -n "$SFCC_SANDBOX_API_HOST" ] && [ -n "$SFCC_SHORTCODE" ]; then
echo "has-secrets=true" >> "$GITHUB_OUTPUT"
else
echo "has-secrets=false" >> "$GITHUB_OUTPUT"
echo "Windows E2E tests skipped - missing required variables" >> "$GITHUB_STEP_SUMMARY"
fi
- name: Setup Windows machine
if: steps.check-secrets.outputs.has-secrets == 'true'
uses: ./.github/actions/setup-windows
with:
node-version: ${{ matrix.node-version }}
- name: Build package
if: steps.check-secrets.outputs.has-secrets == 'true'
run: pnpm -r run build
- name: Run E2E Tests
if: steps.check-secrets.outputs.has-secrets == 'true'
id: e2e-test
working-directory: packages/b2c-cli
env:
SFCC_CLIENT_ID: ${{ inputs.sfcc_client_id || vars.SFCC_CLIENT_ID }}
SFCC_CLIENT_SECRET: ${{ inputs.sfcc_client_secret || secrets.SFCC_CLIENT_SECRET }}
SFCC_ACCOUNT_MANAGER_HOST: ${{ inputs.sfcc_account_manager_host || vars.SFCC_ACCOUNT_MANAGER_HOST }}
SFCC_SANDBOX_API_HOST: ${{ inputs.sfcc_sandbox_api_host || vars.SFCC_SANDBOX_API_HOST }}
TEST_REALM: ${{ inputs.test_realm || vars.TEST_REALM }}
SFCC_SHORTCODE: ${{ vars.SFCC_SHORTCODE }}
SFCC_EXTRA_HEADERS: ${{ secrets.SFCC_EXTRA_HEADERS }}
SFCC_MRT_CLOUD_ORIGIN: ${{ vars.SFCC_MRT_CLOUD_ORIGIN }}
SFCC_MRT_API_KEY: ${{ secrets.SFCC_MRT_API_KEY }}
NODE_ENV: test
SFCC_LOG_LEVEL: silent
run: |
echo "Running Windows E2E tests with realm: $TEST_REALM"
echo "Node version: $(node --version)"
pnpm run test:e2e:ci && pnpm run lint
- name: E2E Test Report
uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2 # v3.0.0
if: always() && steps.e2e-test.conclusion != 'cancelled' && steps.check-secrets.outputs.has-secrets == 'true'
with:
name: E2E Test Results (Windows Node ${{ matrix.node-version }})
path: 'packages/b2c-cli/test-results.json'
reporter: mocha-json
- name: Upload E2E Test Results
if: always() && steps.e2e-test.conclusion != 'cancelled' && steps.check-secrets.outputs.has-secrets == 'true'
uses: actions/upload-artifact@v7
with:
name: e2e-test-results-windows-node-${{ matrix.node-version }}-${{ github.run_number }}
path: packages/b2c-cli/test-results.json
retention-days: 30

mcp-e2e-tests:
name: MCP E2E
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions packages/b2c-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@
"pretest": "tsc --noEmit -p test",
"test": "c8 env OCLIF_TEST_ROOT=. mocha --forbid-only --exclude \"test/functional/e2e/**\" \"test/**/*.test.ts\"",
"test:ci": "c8 env OCLIF_TEST_ROOT=. mocha --forbid-only --exclude \"test/functional/e2e/**\" --reporter json --reporter-option output=test-results.json \"test/**/*.test.ts\"",
"test:ci:win": "c8 --check-coverage=false env OCLIF_TEST_ROOT=. mocha --forbid-only --exclude \"test/functional/e2e/**\" --reporter json --reporter-option output=test-results.json \"test/**/*.test.ts\"",
"test:unit": "env OCLIF_TEST_ROOT=. mocha --forbid-only --exclude \"test/functional/e2e/**\" \"test/**/*.test.ts\"",
"test:agent": "env OCLIF_TEST_ROOT=. mocha --forbid-only --reporter min --exclude \"test/functional/e2e/**\" \"test/**/*.test.ts\"",
"test:e2e": "env TEST_USE_SHARED_SANDBOX=true OCLIF_TEST_ROOT=. mocha --forbid-only --require test/functional/e2e/hooks.ts --node-option import=tsx --timeout 30000 --retries 2 --reporter spec \"test/functional/e2e/**/*.test.ts\"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ describe('mrt save-credentials', () => {
expect(content).to.deep.equal({username: 'user@example.com', api_key: 'abc123'});
});

it('sets file permissions to 0o600', async () => {
it('sets file permissions to 0o600', async function () {
// NTFS does not honor POSIX permission bits: fs.chmod(0o600) silently
// leaves the mode as 0o666, so the assertion can only be validated on
// POSIX platforms. Credential files on Windows are protected via ACLs,
// not mode bits.
if (process.platform === 'win32') {
this.skip();
}
const credFile = path.join(tempDir, '.mobify');
const command = createCommand({
user: 'user@example.com',
Expand Down
Loading
Loading