diff --git a/.github/workflows/sync-react-b2e-sample.yml b/.github/workflows/sync-react-b2e-sample.yml deleted file mode 100644 index 072a4d0..0000000 --- a/.github/workflows/sync-react-b2e-sample.yml +++ /dev/null @@ -1,82 +0,0 @@ -# Syncs @salesforce/webapp-template-app-react-sample-b2e-experimental into -# samples/webapp-template-app-react-sample-b2e-experimental. Opens a PR only when -# the npm package version has changed. Same steps as running locally: -# npm install && npm run sync-react-b2e-sample -# -# Uses IDEE_GH_TOKEN (bot) for checkout and PR creation; same pattern as -# salesforcedx-vscode-einstein-gpt/.github/workflows/build.yml - -name: Sync React B2E sample from npm - -on: - workflow_dispatch: - schedule: - # Nightly at 02:00 UTC - - cron: "0 2 * * *" - -jobs: - sync: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.IDEE_GH_TOKEN }} - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version-file: ".nvmrc" - cache: npm - - - name: Get latest npm version - id: npm-version - run: | - LATEST=$(npm view @salesforce/webapp-template-app-react-sample-b2e-experimental version) - echo "latest=$LATEST" >> $GITHUB_OUTPUT - - - name: Read current synced version - id: current-version - run: | - VERSION_FILE="samples/webapp-template-app-react-sample-b2e-experimental/.version" - if [ -f "$VERSION_FILE" ]; then - CURRENT=$(cat "$VERSION_FILE" | tr -d '[:space:]') - echo "current=$CURRENT" >> $GITHUB_OUTPUT - else - echo "current=" >> $GITHUB_OUTPUT - fi - - - name: Skip if version unchanged - id: skip - run: | - if [ "${{ steps.current-version.outputs.current }}" = "${{ steps.npm-version.outputs.latest }}" ]; then - echo "skip=true" >> $GITHUB_OUTPUT - echo "No version change. Current=${{ steps.current-version.outputs.current }}, Latest=${{ steps.npm-version.outputs.latest }}" - else - echo "skip=false" >> $GITHUB_OUTPUT - echo "Version change. Current=${{ steps.current-version.outputs.current }}, Latest=${{ steps.npm-version.outputs.latest }}" - fi - - - name: Install dependencies and run sync (same as local) - if: steps.skip.outputs.skip != 'true' - run: | - npm install - npm run sync-react-b2e-sample - - - name: Create PR on version change - if: steps.skip.outputs.skip != 'true' - uses: peter-evans/create-pull-request@v6 - with: - token: ${{ secrets.IDEE_GH_TOKEN }} - branch: chore/sync-react-b2e-sample-${{ steps.npm-version.outputs.latest }} - base: main - title: "chore: sync React B2E sample from npm @ ${{ steps.npm-version.outputs.latest }}" - body: | - Synced `@salesforce/webapp-template-app-react-sample-b2e-experimental` to `samples/webapp-template-app-react-sample-b2e-experimental`. - - **Previous version:** ${{ steps.current-version.outputs.current || 'none' }} - - **New version:** ${{ steps.npm-version.outputs.latest }} - Same flow as running locally: `npm install` then `npm run sync-react-b2e-sample`. - commit-message: "chore: sync react-b2e-sample from npm @ ${{ steps.npm-version.outputs.latest }}" - committer: svc-idee-bot - author: svc-idee-bot diff --git a/.github/workflows/sync-react-samples.yml b/.github/workflows/sync-react-samples.yml new file mode 100644 index 0000000..360f638 --- /dev/null +++ b/.github/workflows/sync-react-samples.yml @@ -0,0 +1,161 @@ +# Syncs React sample packages from npm into samples/: +# - @salesforce/webapp-template-app-react-sample-b2e-experimental +# - @salesforce/webapp-template-app-react-sample-b2x-experimental +# +# Opens a single PR when either (or both) npm package versions have changed. +# Same steps as running locally: npm install && npm run sync-react-b2e-sample +# and npm run sync-react-b2x-sample. +# +# Uses IDEE_GH_TOKEN (bot) for checkout and PR creation; same pattern as +# salesforcedx-vscode-einstein-gpt/.github/workflows/build.yml + +name: Sync React samples from npm + +on: + workflow_dispatch: + schedule: + # Nightly at 02:00 UTC + - cron: "0 2 * * *" + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.IDEE_GH_TOKEN }} + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version-file: ".nvmrc" + cache: npm + + # ── B2E version check ────────────────────────────────────────── + - name: Get latest B2E npm version + id: npm-version-b2e + run: | + LATEST=$(npm view @salesforce/webapp-template-app-react-sample-b2e-experimental version) + echo "latest=$LATEST" >> $GITHUB_OUTPUT + + - name: Read current synced B2E version + id: current-version-b2e + run: | + VERSION_FILE="samples/webapp-template-app-react-sample-b2e-experimental/.version" + if [ -f "$VERSION_FILE" ]; then + CURRENT=$(cat "$VERSION_FILE" | tr -d '[:space:]') + echo "current=$CURRENT" >> $GITHUB_OUTPUT + else + echo "current=" >> $GITHUB_OUTPUT + fi + + # ── B2X version check ────────────────────────────────────────── + - name: Get latest B2X npm version + id: npm-version-b2x + run: | + LATEST=$(npm view @salesforce/webapp-template-app-react-sample-b2x-experimental version) + echo "latest=$LATEST" >> $GITHUB_OUTPUT + + - name: Read current synced B2X version + id: current-version-b2x + run: | + VERSION_FILE="samples/webapp-template-app-react-sample-b2x-experimental/.version" + if [ -f "$VERSION_FILE" ]; then + CURRENT=$(cat "$VERSION_FILE" | tr -d '[:space:]') + echo "current=$CURRENT" >> $GITHUB_OUTPUT + else + echo "current=" >> $GITHUB_OUTPUT + fi + + # ── Skip logic ───────────────────────────────────────────────── + - name: Check for version changes + id: skip + run: | + B2E_CHANGED="false" + B2X_CHANGED="false" + + if [ "${{ steps.current-version-b2e.outputs.current }}" != "${{ steps.npm-version-b2e.outputs.latest }}" ]; then + B2E_CHANGED="true" + fi + if [ "${{ steps.current-version-b2x.outputs.current }}" != "${{ steps.npm-version-b2x.outputs.latest }}" ]; then + B2X_CHANGED="true" + fi + + echo "b2e_changed=$B2E_CHANGED" >> $GITHUB_OUTPUT + echo "b2x_changed=$B2X_CHANGED" >> $GITHUB_OUTPUT + + if [ "$B2E_CHANGED" = "false" ] && [ "$B2X_CHANGED" = "false" ]; then + echo "skip=true" >> $GITHUB_OUTPUT + echo "No version changes detected." + else + echo "skip=false" >> $GITHUB_OUTPUT + echo "Changes detected — B2E=$B2E_CHANGED B2X=$B2X_CHANGED" + fi + + # ── Install + sync ───────────────────────────────────────────── + - name: Install dependencies + if: steps.skip.outputs.skip != 'true' + run: npm install + + - name: Sync B2E sample + if: steps.skip.outputs.skip != 'true' + run: npm run sync-react-b2e-sample + + - name: Sync B2X sample + if: steps.skip.outputs.skip != 'true' + run: npm run sync-react-b2x-sample + + # ── PR creation ──────────────────────────────────────────────── + - name: Build PR metadata + if: steps.skip.outputs.skip != 'true' + id: pr-meta + run: | + PARTS="" + BODY="" + + if [ "${{ steps.skip.outputs.b2e_changed }}" = "true" ]; then + PARTS="${PARTS}b2e-${{ steps.npm-version-b2e.outputs.latest }}" + BODY="${BODY}- **B2E** \`@salesforce/webapp-template-app-react-sample-b2e-experimental\`: ${{ steps.current-version-b2e.outputs.current || 'none' }} → ${{ steps.npm-version-b2e.outputs.latest }}"$'\n' + fi + if [ "${{ steps.skip.outputs.b2x_changed }}" = "true" ]; then + if [ -n "$PARTS" ]; then PARTS="${PARTS}-"; fi + PARTS="${PARTS}b2x-${{ steps.npm-version-b2x.outputs.latest }}" + BODY="${BODY}- **B2X** \`@salesforce/webapp-template-app-react-sample-b2x-experimental\`: ${{ steps.current-version-b2x.outputs.current || 'none' }} → ${{ steps.npm-version-b2x.outputs.latest }}"$'\n' + fi + + echo "branch=chore/sync-react-samples-${PARTS}" >> $GITHUB_OUTPUT + + # Multi-line body via heredoc + { + echo "body<> $GITHUB_OUTPUT + + TITLE="chore: sync React samples from npm" + if [ "${{ steps.skip.outputs.b2e_changed }}" = "true" ] && [ "${{ steps.skip.outputs.b2x_changed }}" = "true" ]; then + TITLE="chore: sync React B2E (${{ steps.npm-version-b2e.outputs.latest }}) & B2X (${{ steps.npm-version-b2x.outputs.latest }}) samples from npm" + elif [ "${{ steps.skip.outputs.b2e_changed }}" = "true" ]; then + TITLE="chore: sync React B2E sample from npm @ ${{ steps.npm-version-b2e.outputs.latest }}" + else + TITLE="chore: sync React B2X sample from npm @ ${{ steps.npm-version-b2x.outputs.latest }}" + fi + echo "title=${TITLE}" >> $GITHUB_OUTPUT + + - name: Create PR on version change + if: steps.skip.outputs.skip != 'true' + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.IDEE_GH_TOKEN }} + branch: ${{ steps.pr-meta.outputs.branch }} + base: main + title: ${{ steps.pr-meta.outputs.title }} + body: ${{ steps.pr-meta.outputs.body }} + commit-message: ${{ steps.pr-meta.outputs.title }} + committer: svc-idee-bot + author: svc-idee-bot diff --git a/package.json b/package.json index 9dd8839..d0e11ad 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,11 @@ "description": "AI prompts and rules library for Agentforce Vibes development", "private": true, "devDependencies": { - "@salesforce/webapp-template-app-react-sample-b2e-experimental": "*" + "@salesforce/webapp-template-app-react-sample-b2e-experimental": "*", + "@salesforce/webapp-template-app-react-sample-b2x-experimental": "*" }, "scripts": { - "sync-react-b2e-sample": "node scripts/sync-react-b2e-sample.js" + "sync-react-b2e-sample": "node scripts/sync-react-b2e-sample.js", + "sync-react-b2x-sample": "node scripts/sync-react-b2x-sample.js" } } diff --git a/samples/README.md b/samples/README.md index d9d860a..aed2c51 100644 --- a/samples/README.md +++ b/samples/README.md @@ -4,11 +4,11 @@ Sample apps and templates synced into this repo for reference and reuse. ## webapp-template-app-react-sample-b2e-experimental -Source is synced from the npm package [@salesforce/webapp-template-app-react-sample-b2e-experimental](https://www.npmjs.com/package/@salesforce/webapp-template-app-react-sample-b2e-experimental). Only the package’s source (no `node_modules`) is copied into `samples/webapp-template-app-react-sample-b2e-experimental/`. +Source is synced from the npm package [@salesforce/webapp-template-app-react-sample-b2e-experimental](https://www.npmjs.com/package/@salesforce/webapp-template-app-react-sample-b2e-experimental). Only the package's source (no `node_modules`) is copied into `samples/webapp-template-app-react-sample-b2e-experimental/`. -### How it’s updated +### How it's updated -- **GitHub Action**: Runs nightly and can be triggered manually from the **Actions** tab (“Sync React B2E sample from npm”). The workflow runs the same steps as below and opens a PR against `main` only when the npm package version has changed. +- **GitHub Action**: Runs nightly and can be triggered manually from the **Actions** tab ("Sync React samples from npm"). The workflow runs the same steps as below and opens a PR against `main` only when the npm package version has changed. - **Local**: From the **repo root** you can run the same sync anytime: ```bash @@ -21,3 +21,23 @@ Source is synced from the npm package [@salesforce/webapp-template-app-react-sam ### Version tracking The file `samples/webapp-template-app-react-sample-b2e-experimental/.version` stores the last-synced npm version. The Action compares it to the latest on npm and only creates a PR when they differ. + +## webapp-template-app-react-sample-b2x-experimental + +Source is synced from the npm package [@salesforce/webapp-template-app-react-sample-b2x-experimental](https://www.npmjs.com/package/@salesforce/webapp-template-app-react-sample-b2x-experimental). Only the package's source (no `node_modules`) is copied into `samples/webapp-template-app-react-sample-b2x-experimental/`. + +### How it's updated + +- **GitHub Action**: Runs nightly and can be triggered manually from the **Actions** tab ("Sync React samples from npm"). The workflow runs the same steps as below and opens a PR against `main` only when the npm package version has changed. +- **Local**: From the **repo root** you can run the same sync anytime: + + ```bash + npm install + npm run sync-react-b2x-sample + ``` + + This installs the package into root `node_modules` and copies its source into `samples/webapp-template-app-react-sample-b2x-experimental/`, and updates `.version` in that folder. + +### Version tracking + +The file `samples/webapp-template-app-react-sample-b2x-experimental/.version` stores the last-synced npm version. The Action compares it to the latest on npm and only creates a PR when they differ. diff --git a/scripts/sync-react-b2x-sample.js b/scripts/sync-react-b2x-sample.js new file mode 100644 index 0000000..da867f0 --- /dev/null +++ b/scripts/sync-react-b2x-sample.js @@ -0,0 +1,74 @@ +#!/usr/bin/env node +/** + * Syncs source from @salesforce/webapp-template-app-react-sample-b2x-experimental + * into samples/webapp-template-app-react-sample-b2x-experimental/. + * Run from repo root after npm install. Same flow used locally and in CI. + */ + +const fs = require('fs'); +const path = require('path'); + +const PACKAGE_NAME = '@salesforce/webapp-template-app-react-sample-b2x-experimental'; +const SAMPLE_DIR = path.join(process.cwd(), 'samples', 'webapp-template-app-react-sample-b2x-experimental'); + +const pkgRoot = path.join(process.cwd(), 'node_modules', PACKAGE_NAME.replace('/', path.sep)); + +if (!fs.existsSync(pkgRoot)) { + console.error(`Package not found at ${pkgRoot}. Run "npm install" from repo root first.`); + process.exit(1); +} + +// Read version from the package we're syncing from +const pkgJsonPath = path.join(pkgRoot, 'package.json'); +const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); +const version = pkgJson.version; + +function copyRecursive(src, dest, excludeDirs = new Set(['node_modules', '.git'])) { + const stat = fs.statSync(src); + const basename = path.basename(src); + if (excludeDirs.has(basename)) return; + + if (stat.isDirectory()) { + if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true }); + for (const name of fs.readdirSync(src)) { + copyRecursive(path.join(src, name), path.join(dest, name), excludeDirs); + } + } else { + fs.mkdirSync(path.dirname(dest), { recursive: true }); + fs.copyFileSync(src, dest); + } +} + +// Ensure sample dir exists and clear it (we replace with fresh copy from dist) +if (fs.existsSync(SAMPLE_DIR)) { + for (const name of fs.readdirSync(SAMPLE_DIR)) { + if (name === '.version') continue; // keep .version until we overwrite it + const itemPath = path.join(SAMPLE_DIR, name); + if (fs.statSync(itemPath).isDirectory()) { + fs.rmSync(itemPath, { recursive: true }); + } else { + fs.unlinkSync(itemPath); + } + } +} else { + fs.mkdirSync(SAMPLE_DIR, { recursive: true }); +} + +// Copy dist/ contents into sample dir (package ships source in dist/) +const distPath = path.join(pkgRoot, 'dist'); +if (fs.existsSync(distPath)) { + for (const name of fs.readdirSync(distPath)) { + copyRecursive(path.join(distPath, name), path.join(SAMPLE_DIR, name)); + } +} + +// Copy LICENSE from package root +const licensePath = path.join(pkgRoot, 'LICENSE.txt'); +if (fs.existsSync(licensePath)) { + fs.copyFileSync(licensePath, path.join(SAMPLE_DIR, 'LICENSE.txt')); +} + +// Write .version for version tracking (used by workflow and local) +fs.writeFileSync(path.join(SAMPLE_DIR, '.version'), version + '\n', 'utf8'); + +console.log(`Synced ${PACKAGE_NAME}@${version} to ${SAMPLE_DIR}`);