diff --git a/evals/power-pages/migrate-bootstrap/evals.json b/evals/power-pages/migrate-bootstrap/evals.json new file mode 100644 index 000000000..70767b26f --- /dev/null +++ b/evals/power-pages/migrate-bootstrap/evals.json @@ -0,0 +1,72 @@ +{ + "skill_name": "migrate-bootstrap", + "evals": [ + { + "id": 1, + "prompt": "Migrate my classic Power Pages site from Bootstrap 3 to Bootstrap 5. I'm already authenticated and the environment is correct. The downloaded site is in the ./v3site folder.", + "expected_output": "The skill verifies PAC CLI and that both pac pages bootstrap-migrate and pac pages upload are available, confirms the folder is a classic site (not a code site), snapshots the source with git, assesses Bootstrap-3 usage, runs pac pages bootstrap-migrate to produce v3siteV5, reviews logs.txt and groups auto-applied vs hierarchy/manual changes, applies the residual fixes with per-category consent, pre-flights the manifest, uploads with pac pages upload, verifies the Bootstrap 5 runtime flag actually flipped, and validates the site via /test-site.", + "files": [], + "expectations": [ + "TaskCreate is used upfront to create a task list covering all 8 phases before starting work", + "PAC CLI is verified by running pac help", + "pac pages help is run to confirm both bootstrap-migrate and upload verbs are available before proceeding", + "The target is confirmed to be a classic site (presence of website.yml/sitesetting.yml) and not a code site (powerpages.config.json)", + "The source folder is snapshotted with git as a pre-migration restore point before any migration", + "Bootstrap-3 usage is inventoried and the user is told this is an assisted migration, not push-button", + "Explicit consent is requested before running the migration engine", + "pac pages bootstrap-migrate --path is run on the site folder and produces a sibling V5 directory", + "logs.txt is parsed/read and changes are grouped into no-change, auto-applied, and hierarchy (manual) categories", + "Residual hierarchy/CSS fixes are applied with per-category consent using the bootstrap-v5-manual-fixes reference", + "Before upload, the manifest is pre-flighted: website.yml has a valid adx_websiteid and sitesetting.yml contains Site/BootstrapV5Enabled true", + "A final consent gate is presented before the outward-facing upload", + "pac pages upload (not upload-code-site) is run against the V5 directory", + "The Bootstrap 5 runtime flag flip is explicitly verified after upload rather than trusting the upload exit code", + "The site is validated via /test-site and skill usage is recorded with --skillName MigrateBootstrap" + ] + }, + { + "id": 2, + "prompt": "I want to upgrade an old Power Pages portal to Bootstrap 5 but I haven't downloaded it yet and I'm not signed in to PAC CLI.", + "expected_output": "The skill verifies PAC CLI is installed, detects the user is not authenticated, asks for the environment URL and runs pac auth create, confirms the target environment, lists websites and downloads the chosen one with pac pages download, snapshots it, then proceeds through assessment, the bootstrap-migrate engine, change review, residual fixes, upload, runtime-flag verification, and validation.", + "files": [], + "expectations": [ + "PAC CLI is verified by running pac help", + "pac auth who is run and the unauthenticated state is detected", + "AskUserQuestion is used to request the environment URL and pac auth create --environment is run", + "pac pages help confirms bootstrap-migrate and upload are available", + "pac pages list is run and AskUserQuestion is used to choose which website to migrate", + "pac pages download --path --webSiteId is run to fetch the classic site locally", + "The downloaded folder is snapshotted with git before migration", + "pac pages bootstrap-migrate is run and the V5 output is confirmed", + "The change report is reviewed and residual fixes are applied with per-category consent", + "pac pages upload is run on the V5 folder and the runtime flag flip is verified afterward", + "No automated rollback is attempted on failure; the source snapshot is the rollback path" + ] + }, + { + "id": 3, + "prompt": "Run the Bootstrap 5 migration on my React Power Pages code site in the current directory.", + "expected_output": "The skill detects that the target is a code site (React/Vue/Angular/Astro) rather than a classic site and stops, explaining that Bootstrap 3 to 5 migration does not apply to code sites because they are never Bootstrap-3-based, without running the migration engine.", + "files": [], + "expectations": [ + "The skill recognizes a code site by the presence of powerpages.config.json and a framework package.json", + "The skill stops and explains the migration does not apply to code sites because they are never Bootstrap-3-based", + "pac pages bootstrap-migrate is NOT run against the code site", + "No upload or flag-flip is attempted" + ] + }, + { + "id": 4, + "prompt": "I ran the migration earlier and uploaded my v3siteV5 folder, but the live site still looks like Bootstrap 3. Help.", + "expected_output": "The skill recognizes this as a runtime-flag-flip failure that pac pages upload swallows silently. It pre-flights the manifest (sitesetting.yml must contain Site/BootstrapV5Enabled true and website.yml must have a valid adx_websiteid), re-uploads if needed, and verifies the Bootstrap 5 runtime flag is actually enabled, since the upload exit code does not prove the flag flipped.", + "files": [], + "expectations": [ + "The skill explains that pac pages upload flips the Bootstrap 5 runtime flag automatically but swallows flip failures, so a successful upload does not prove the flag flipped", + "sitesetting.yml in the V5 folder is checked for Site/BootstrapV5Enabled set to true", + "website.yml in the V5 folder is checked for a valid adx_websiteid", + "The Bootstrap 5 runtime flag state is explicitly verified rather than assumed from the upload result", + "If the gating files were missing, the skill fixes them and re-uploads with pac pages upload" + ] + } + ] +} diff --git a/plugins/power-pages/README.md b/plugins/power-pages/README.md index 705bcd0f6..da9a615e1 100644 --- a/plugins/power-pages/README.md +++ b/plugins/power-pages/README.md @@ -38,7 +38,7 @@ This keeps hook behavior in one place and avoids relying on skill-frontmatter ho ## Skills -The plugin provides 31 skills that cover the full lifecycle of a Power Pages code site — scaffolding, deployment, data modeling, backend integration, authentication, ALM and CI/CD, security review, testing, and auditing. Each skill is invoked conversationally — just describe what you want to do. +The plugin provides 33 skills that cover the full lifecycle of a Power Pages site — scaffolding, deployment, data modeling, backend integration, authentication, ALM and CI/CD, security review, testing, auditing, and Bootstrap 3→5 migration. Each skill is invoked conversationally — just describe what you want to do. ### Site scaffolding and deployment @@ -388,6 +388,18 @@ Adds search engine optimization artifacts: `robots.txt`, `sitemap.xml`, and meta - Generates sitemap with production URLs - Adds viewport, charset, description, and social sharing meta tags +### Migration + +#### `/migrate-bootstrap` + +> "Upgrade my Power Pages site from Bootstrap 3 to Bootstrap 5" + +Migrates a traditional Power Pages site (Liquid web templates, not code sites) from Bootstrap 3 to Bootstrap 5. Runs the `pac pages bootstrap-migrate` engine for the bulk class renames, then assists with the residual hierarchy/CSS fixes the engine can only flag. + +- Non-destructive: the engine writes a new `V5` copy and never edits the source +- AI-assisted per-category fixes for grid, navbar, panel/card, and page-header changes +- Uploads (auto-enabling the Bootstrap 5 runtime flag) and verifies the flip via `pac-log.txt` + ### Support #### `/report-issue` diff --git a/plugins/power-pages/references/approval-gates.md b/plugins/power-pages/references/approval-gates.md index e670c89ba..dcd663860 100644 --- a/plugins/power-pages/references/approval-gates.md +++ b/plugins/power-pages/references/approval-gates.md @@ -685,6 +685,19 @@ New skill (Power Pages source & dependency security scan). Runs local static ana --- +### 6.31 `migrate-bootstrap` (6 calls / 5 gates + 1 sub-prompt) + +New skill (migrates a traditional Power Pages site from Bootstrap 3 to Bootstrap 5). The engine does the bulk class renames; the skill assists with residual hierarchy/CSS fixes, then uploads (which auto-flips the runtime flag). + +| ID | Kind | Category | Phase | Trigger / question | Cancel leaves | +|---|---|---|---|---|---| +| `migrate-bootstrap:1.confirm-env` | gate | consent | 1 | Echoes current env — *"Use this environment / choose another"*. Migrating against the wrong environment is destructive, so confirmation is mandatory. Covers the free-text env-URL sub-prompt in the same step. | nothing | +| `migrate-bootstrap:2.1.select-site` | gate | plan | 2.1 | Multiple websites found — *"Which website to download?"* | nothing | +| `migrate-bootstrap:4.run-engine` | gate | consent | 4 | *"Run the Bootstrap 5 migration on ``?"* — engine writes a new `V5` copy and never edits the source. | nothing | +| `migrate-bootstrap:6.residual-fixes` | gate | progress | 6 | Per-category consent — *"Apply the `` fixes to `` file(s)?"* — repeated per residual category; changes are local to the V5 copy. | nothing | +| `migrate-bootstrap:7.2.upload` | gate | final | 7.2 | First outward-facing change — *"Upload `` to ``?"* — publishes the Bootstrap 5 site and auto-enables the runtime flag. | nothing | + +--- ### Cross-plugin shared skills — out of catalog scope `report-issue` — Its prompts are cross-plugin, not power-pages-specific, so they are not catalogued here. If the shared workflow is ever governed by per-plugin approval-gate linting, add a `report-issue:*` section to this catalog. diff --git a/plugins/power-pages/references/skill-tracking-reference.md b/plugins/power-pages/references/skill-tracking-reference.md index 994f7038a..223b50746 100644 --- a/plugins/power-pages/references/skill-tracking-reference.md +++ b/plugins/power-pages/references/skill-tracking-reference.md @@ -56,6 +56,7 @@ If the tracking script creates or updates site setting YAML files, include those | deploy-pipeline | DeployPipeline | Site/AI/Skills/DeployPipeline | | ensure-pipelines-host | EnsurePipelinesHost | Site/AI/Skills/EnsurePipelinesHost | | force-link-environment | ForceLinkEnvironment | Site/AI/Skills/ForceLinkEnvironment | +| migrate-bootstrap | MigrateBootstrap | Site/AI/Skills/MigrateBootstrap | ## YAML Format diff --git a/plugins/power-pages/skills/migrate-bootstrap/SKILL.md b/plugins/power-pages/skills/migrate-bootstrap/SKILL.md new file mode 100644 index 000000000..31d8b74cb --- /dev/null +++ b/plugins/power-pages/skills/migrate-bootstrap/SKILL.md @@ -0,0 +1,433 @@ +--- +name: migrate-bootstrap +description: >- + Migrates a traditional Power Pages site from Bootstrap 3 to Bootstrap 5. Downloads the + site, runs the pac pages bootstrap-migrate engine, reviews the change report, applies + AI-assisted fixes for the residual hierarchy/CSS changes the engine only flags, uploads + the migrated site (which auto-enables the Bootstrap 5 runtime flag), verifies the flag, + and validates. Use when the user wants to upgrade an older Bootstrap-3 portal to + Bootstrap 5. NOT for code sites (React/Vue/Angular/Astro) — those are never Bootstrap-3. +user-invocable: true +argument-hint: Optional website name or local site folder path +allowed-tools: Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion, Skill, Task, TaskCreate, TaskUpdate, TaskList +model: opus +--- + +> **Plugin check**: Run `node "${PLUGIN_ROOT}/scripts/check-version.js"` — if it outputs a message, show it to the user before proceeding. + +# Migrate a Traditional Power Pages Site from Bootstrap 3 to Bootstrap 5 + +Guide the user through migrating a **traditional / native** Power Pages site from Bootstrap 3 +to Bootstrap 5. Follow a systematic approach: verify tooling, acquire and back up the site, +assess scope, run the `pac pages bootstrap-migrate` engine, review the change report, apply +the residual fixes the engine can only flag, upload (which auto-enables the runtime flag), +verify, and validate. + +## Core Principles + +- **Traditional sites only**: This skill targets traditional Power Pages sites (Liquid web templates, + `*.webtemplate.source.html`, `*.html/.aspx/.ascx`, `*.css`, `sitesetting.yml`). It does **NOT** + apply to code sites (React/Vue/Angular/Astro) — they are never Bootstrap-3-based. If the target + is a code site, stop and tell the user this skill doesn't apply. +- **The engine does the bulk; you do the residual**: `pac pages bootstrap-migrate` deterministically + applies the well-known class renames. The skill's value is assisting with the **hierarchy / CSS + changes the engine only logs** (see [references/bootstrap-v5-manual-fixes.md](references/bootstrap-v5-manual-fixes.md)). +- **Non-destructive, with a restore point**: The engine writes a **new `V5`** copy and never + edits the source in place. Still, snapshot the source folder (git) before doing anything. +- **Assist and flag — never silently rewrite**: Residual fixes are Liquid-aware and can break layouts. + Apply them with per-category consent; when a case is ambiguous (e.g. Liquid conditionals), flag it + for the user instead of guessing. +- **Verify the runtime flag flip via `pac-log.txt`**: `pac pages upload` flips the server-side + Bootstrap 5 flag automatically, but **flip outcomes are logged and swallowed** by the CLI. Never + trust the upload exit code alone. The authoritative record is the PAC diagnostic log (`pac telemetry + status` → `pac-log.txt`); grep it for `BootstrapV5UploadPostProcessor`. The flip **requires an + active portal** — a website record with no provisioned site logs `no portal found` and skips. +- **Use TaskCreate/TaskUpdate**: Create the todo list upfront with all 8 phases before starting work. + +**Initial request:** $ARGUMENTS + +--- + +## Phase 1: Verify Prerequisites + +**Goal**: Ensure PAC CLI is installed, the user is authenticated, the target environment is +confirmed, and the required commands are available. + +**Actions**: + +1. Create the todo list with all 8 phases (see [Progress Tracking](#progress-tracking)). +2. Run `pac help` to confirm PAC CLI is installed and on PATH. If missing, point the user to + `https://aka.ms/PowerPlatformCLI` (`dotnet tool install --global Microsoft.PowerApps.CLI.Tool`) + and re-verify. + + +3. Run `pac auth who`. If not authenticated, ask for the environment URL via `AskUserQuestion`, + then `pac auth create --environment ""` and re-verify. Capture the environment name, URL, + and ID. + + + +> 🚦 **Gate (consent · migrate-bootstrap:1.confirm-env):** Confirm the target environment before any download, migration, or upload. Running the migration against the wrong environment is destructive, so this confirmation is mandatory. + +4. Confirm the target environment with the user (`AskUserQuestion`: use this environment / choose + another via `pac org list` + `pac org select`). +5. **Probe command availability** — both verbs are required: + + ```bash + pac pages help + ``` + + Confirm `bootstrap-migrate` and `upload` appear in the verb list. If either is missing (feature + not enabled in this CLI build / tenant), stop and tell the user the migration can't proceed + until those commands are available. +6. **Locate the PAC diagnostic log** — capture the path now; you will need it in Phase 7 to verify + the flag flip: + + ```bash + pac telemetry status + ``` + + It prints `The diagnostic logs can be found at: <…>\logs\pac-log.txt`. This is a **rolling log + across all `pac` runs** and is the **authoritative record of the Bootstrap V5 flag flip**. Note: + `pac pages upload` writes **no per-folder log** — `pac-log.txt` is the only place its post-processor + records the flip result. Set `PAC_LOG = `. + +**Output**: PAC CLI verified, authenticated session, confirmed environment, `bootstrap-migrate` +and `upload` confirmed available, `PAC_LOG` path captured. + +--- + +## Phase 2: Acquire the Site & Back Up + +**Goal**: Get a local copy of the traditional site to migrate, and establish a restore point. + +**Actions**: + +### 2.1 Locate or download the site + +- **If the user provided a local site folder path** (or `$ARGUMENTS` names one): verify it exists + and looks like a downloaded traditional site (contains `website.yml` and `sitesetting.yml`). Use it + as `SITE_FOLDER`. +- **Otherwise**: list available websites and download the chosen one. + + ```bash + pac pages list + ``` + + + + > 🚦 **Gate (plan · migrate-bootstrap:2.1.select-site):** Choose which website to download when more than one exists. Canceling leaves nothing changed. + + Present the websites via `AskUserQuestion`, then download the selected site: + + ```bash + pac pages download --path "" --webSiteId "" + ``` + + Use the downloaded directory as `SITE_FOLDER`. + +> Confirm this is a **traditional** site, not a code site. A code-site project has `powerpages.config.json` +> and a framework `package.json`; a traditional site has `website.yml`, `sitesetting.yml`, and +> `*.webtemplate.source.html` files. If it's a code site, stop — this skill does not apply. + +### 2.2 Confirm the site is ACTIVATED (not just a website record) + +`pac pages list` shows **website records** (`adx_website`). A website record can exist with **no +provisioned/active Power Pages site (portal)** behind it — created by data import/clone, deactivated, +or orphaned. The Phase 7 flag flip (`SetPortalBootstrapV5Enabled`) targets the **portal**, not the +website record, so **if no active portal exists the flip silently skips and the live site won't +render** — and you won't find this out until after uploading unless you check now. + +Resolve the site URL from `websitebinding.yml` (`adx_sitename`) and probe the **final** status code +(`-L` follows redirects so a sign-in / canonical-host redirect resolves to its real code): + +```bash +curl -sL -o /dev/null -w "%{http_code}" "https:///" +``` + +- **`2xx` or a redirect that resolves to `2xx`/`3xx`** → active portal; proceed. A private or + protected site legitimately redirects to sign-in or a canonical host, so a resolved `3xx` is + **active**, not a failure. +- **`4xx`/`5xx`** → inconclusive from the status alone. A private site can return a non-`200` without + being unactivated, so **do not conclude "not activated" from the code alone** — fetch the error + body (or open the URL in a browser) to look for the unactivated signature: + + ```bash + curl -sL "https:///" | head -c 4000 + ``` + + If the body shows a Dataverse-connection null-ref (`Object reference not set …` / + `CrmOnlineOrganizationService.ToOrganizationService`), or the URL fails to render a portal page in + the browser, **the site is most likely NOT activated**. This is the same condition that makes the + Phase 7 flag flip log `no portal found for website via Power Pages API`. A `4xx`/`5xx` **without** + that signature is more likely an auth wall or transient error on an active site — treat as active and + note it for the user rather than blocking. + +If the site is confirmed not active, **stop and tell the user to activate/provision it first** (Power +Pages admin center, or the `/power-pages:activate-site` skill). Migrating content into an unactivated +website still uploads, but the runtime flag can't flip and the site can't be verified. + +### 2.3 Snapshot the source + +Create a restore point before any migration: + +```bash +cd "" +git init -q && git add -A && git commit -q -m "Pre-migration snapshot (Bootstrap 3)" +``` + +If the folder is already a git repo, just commit any pending changes so the pre-migration state +is captured. + +**Output**: `SITE_FOLDER` resolved, confirmed traditional, source snapshot committed. + +--- + +## Phase 3: Pre-Migration Assessment + +**Goal**: Set expectations by inventorying Bootstrap-3 usage and flagging risk areas before +running the engine. + +**Actions**: + +1. Scan the site for Bootstrap-3 markers using `Grep` over `*.html`, `*.aspx`, `*.ascx`, `*.css`: + - Component classes: `panel`, `navbar-header`, `img-responsive`, `btn-block`, `pull-left|pull-right`, + `col-(xs|sm|md|lg)-`, `glyphicon`, `label-`, `page-header`, `pager`, `data-toggle|data-dismiss`. + - Count affected files and the rough number of occurrences per category. +2. Flag risk areas: + - Heavy **custom CSS** (large `.css` files with non-Bootstrap selectors) — won't be auto-converted. + - **Liquid-entangled markup** (`{% ... %}` around classes, conditional dropdowns) — needs review. +3. Present a concise scope summary and set expectations: + + > "This is an **assisted** migration. The engine will auto-apply the common Bootstrap 3→5 class + > renames. Some changes (grid hierarchy, navbar structure, panel/page-header/pager styling) can + > only be flagged — I'll help apply those in Phase 6. Visual parity isn't guaranteed and you'll + > want to QA the result." + +**Output**: Scope summary presented; user understands this is assisted, not push-button. + +--- + +## Phase 4: Run the Migration Engine + +**Goal**: Produce the migrated `V5` copy. + +**Actions**: + + + +> 🚦 **Gate (consent · migrate-bootstrap:4.run-engine):** Explicit consent before running `pac pages bootstrap-migrate`. The engine writes a new `V5` copy and never edits the source, so canceling leaves nothing changed. + +1. Get explicit consent to run the engine (`AskUserQuestion`: "Run the Bootstrap 5 migration on + ``? This creates a new `V5` copy and does not modify the original."). +2. Run: + + ```bash + pac pages bootstrap-migrate --path "" + ``` + +3. Confirm the engine produced **`V5`** containing: + - Rewritten `*.html / *.js / *.aspx / *.ascx / *.css` files + - Swapped `bootstrap.min.css` (Bootstrap 5) + - Updated `sitesetting.yml` with a `Site/BootstrapV5Enabled` record + - `logs.txt` and per-file `*-diff.json` + + See [references/migration-engine-reference.md](references/migration-engine-reference.md) for the + full output contract. Set `MIGRATED_FOLDER = V5`. + +**Output**: `MIGRATED_FOLDER` produced with rewritten files, swapped CSS, `logs.txt`, and diffs. + +--- + +## Phase 5: Review the Change Report + +**Goal**: Turn `logs.txt` into a structured, grouped summary that surfaces the manual work. + +**Actions**: + +1. Read `/logs.txt` and categorize per the format documented in the engine + reference. The report opens with three file lists — **no change**, **auto-applied** + (Replacement/Addition/Deletion), and **hierarchy changes** (logged only) — followed by a + per-file detail block (`Total Number of Changes:` + change lines). Use `Grep` to count and + locate the work: + + - `Grep` for `Need hierarchy change` → the manual hierarchy items (often **zero**). + - The most reliable residual signal is the **V5 output itself**: `Grep` the migrated `*.html` + for surviving Bootstrap-3 markers (`page-header`, `glyphicon`, `label-(info|danger|primary)`, + `panel-(primary|success|info|warning|danger)`, `btn-block`, `navbar-header`). Whatever the + engine left behind is exactly the Phase 6 work. + +2. Present a summary: + - Counts per category. + - The list of files with **hierarchy changes** and **CSS-dependent contextual classes** (e.g. + `panel-primary`) — these are the Phase 6 work items. + +**Output**: Grouped change summary; explicit list of residual manual items. + +--- + +## Phase 6: AI-Assisted Residual Fixes + +**Goal**: Apply the structural / CSS fixes the engine flagged but could not safely auto-apply, in +the `MIGRATED_FOLDER` files. + +> Reference recipes: [references/bootstrap-v5-manual-fixes.md](references/bootstrap-v5-manual-fixes.md) + +**Actions**: + +1. Group the residual items by category (grid hierarchy, navbar structure, panel/card styling, + page-header, pager, btn-block, Liquid edge cases, partial paths). + + +> 🚦 **Gate (progress · migrate-bootstrap:6.residual-fixes):** Per-category consent before applying each residual fix to the `MIGRATED_FOLDER` files. Changes are local to the V5 copy and committed per category; canceling leaves nothing outward-facing changed. + +2. For **each category**, get per-category consent (`AskUserQuestion`: "Apply the `` fixes + to `` file(s)?"). On consent: + - Apply the recipe from the manual-fixes reference using `Edit`. + - Re-check the affected lines against the file's `*-diff.json` to confirm you didn't disturb + auto-applied changes. + - Commit per category: `git -C "" add -A && git -C "" commit -m "v5 fixes: "` + (init the V5 folder as a repo first if needed). Use `-C ""` on **both** + `add` and `commit` so they run against the V5 copy, not the current working directory. +3. For **ambiguous / Liquid-entangled** cases, do **not** rewrite — list them for the user with the + file and line and a suggested manual change. + +**Output**: Residual fixes applied (or flagged) per category, each committed; ambiguous items listed. + +--- + +## Phase 7: Upload, Auto-Enable Runtime Flag, and Verify + +**Goal**: Upload the migrated site (which automatically flips the server-side Bootstrap 5 flag) and +**verify** the flag actually took effect. + +**Actions**: + +### 7.1 Pre-flight the manifest + +The automatic flag flip is gated on two files in `MIGRATED_FOLDER`: + +- `website.yml` must have a valid `adx_websiteid`. +- `sitesetting.yml` must contain `Site/BootstrapV5Enabled` with value `true`. + +Verify both (`Grep`/`Read`). If either is missing, the flag flip silently no-ops — fix before uploading. + +### 7.2 Final consent gate, then upload + + + +> 🚦 **Gate (final · migrate-bootstrap:7.2.upload):** Final sign-off before the first outward-facing change. `pac pages upload` publishes the Bootstrap 5 site and auto-enables the runtime flag. Canceling before upload leaves the live site untouched. + +This is the first **outward-facing** change. Get explicit consent (`AskUserQuestion`: "Upload +`` to environment ``? This publishes the Bootstrap 5 site and enables the +Bootstrap 5 runtime."). On consent: + +```bash +pac pages upload --path "" +``` + +Leave `--modelVersion` at its default (`Standard`) for a traditional site. `pac pages upload` uploads the +content **and** auto-flips the server-side Bootstrap 5 flag via its post-processor (triggered by the +`Site/BootstrapV5Enabled=true` setting). + +> Use `pac pages upload` — **not** `upload-code-site` (that is the code-site path and will corrupt a +> traditional site). + +### 7.3 Verify the flag flip (mandatory) + +The CLI logs and **swallows** flag-flip outcomes, so a successful upload does **not** prove the +runtime is on Bootstrap 5. **The authoritative record is `pac-log.txt`** (the `PAC_LOG` path captured +in Phase 1), not the live site. Check it **first** — it is decisive in seconds, whereas the live +site can be misleading (an unactivated site throws a 500 that has nothing to do with Bootstrap): + +```bash +# grep the PAC diagnostic log for the post-processor's result (most recent run is last) +grep -iE "BootstrapV5UploadPostProcessor|SetPortalBootstrapV5Enabled" "" +``` + +Three possible outcomes: + +| Log line | Meaning | Action | +|----------|---------|--------| +| `Set … BootstrapV5Enabled` / success (`INF`) | Flip **applied** | ✅ Proceed; optionally confirm on the live site. | +| `Skipping SetPortalBootstrapV5Enabled: no portal found for website via Power Pages API` (`WRN`) | **No active portal** — flip never attempted | ❌ The site isn't activated. Activate it (see Phase 2.2 / `/power-pages:activate-site`), then re-run `pac pages upload`. | +| `ERR`/exception from the post-processor | Flip **attempted but failed** (auth/HTTP) | ❌ Re-confirm 7.1 manifest + permissions, then re-run upload. | + +Then **corroborate on the live site** (only meaningful if the log shows *applied*): re-render a known +page and confirm the Bootstrap 5 bundle is served, or re-confirm the portal's Bootstrap V5 enabled +state via the Power Pages management surface. If the live URL returns a `5xx` with a Dataverse-connection +null-ref (`CrmOnlineOrganizationService.ToOrganizationService`), that is the **"site not activated"** +signature — match it against the log's `no portal found` line rather than treating it as a transient +restart. + +**Output**: Migrated site uploaded; flag-flip outcome read from `pac-log.txt` (applied / skipped-no-portal +/ errored) and, when applied, confirmed on the live site. + +--- + +## Phase 8: Validate & Summarize + +**Goal**: Confirm the live site works on Bootstrap 5 and record the outcome. + +**Actions**: + +1. **Runtime smoke test** — invoke `/test-site` against the site URL to crawl key pages, verify they + render, and capture console/network errors. +2. **Before/after visual check** — spot-check the highest-traffic pages (home, navbar, any + panels/cards, forms, pagination) for layout regressions introduced by the migration. +3. **Record skill usage** — follow `${PLUGIN_ROOT}/references/skill-tracking-reference.md`, passing + `--projectRoot ""` (the folder containing `powerpages.config.json`) and + `--skillName "MigrateBootstrap"`. Tracking only writes when the project is a **code site** + (`.powerpages-site/site-settings/` exists); for a traditional/native download the script exits + silently as a no-op — that is expected, so call it unconditionally and don't treat the no-op as an + error. If tracking files are written, include them in the final commit. +4. **Summary** — present: + - Files changed per category (auto-applied vs assisted vs flagged-for-manual). + - Residual items that still need human attention. + - Confirmation the Bootstrap 5 runtime flag is enabled. + - Location of the source snapshot (for rollback) and `MIGRATED_FOLDER`. +5. **Suggest next steps** — manual QA pass, `/test-site` re-runs, and (if promoting across + environments) the ALM skills (`/plan-alm`). + +**Output**: Site validated on Bootstrap 5, usage recorded, summary + residual list presented. + +--- + +## Important Notes + +### Throughout All Phases + +- **Use TaskCreate/TaskUpdate** to track progress at every phase. +- **Ask for user confirmation** at the key decision points (below). +- **Present errors clearly** — show the relevant command output and explain it before suggesting fixes. +- **No automated rollback** after upload — rely on the Phase 2 source snapshot; report failures and + continue/triage rather than auto-reverting. + +### Key Decision Points (Wait for User) + +1. Phase 1: If not authenticated, get environment URL; confirm/switch target environment. +2. Phase 2: If multiple sites, which website to download; confirm traditional (not code) site. +3. Phase 4: Consent to run the migration engine. +4. Phase 6: Per-category consent before applying residual fixes. +5. Phase 7: Final consent before uploading (outward-facing). + +### Progress Tracking + +Before starting Phase 1, create a task list with all phases using `TaskCreate`: + +| Task subject | activeForm | Description | +|-------------|------------|-------------| +| Verify prerequisites | Verifying prerequisites | PAC CLI, auth, environment, and command availability (`bootstrap-migrate`, `upload`) | +| Acquire site and back up | Acquiring site | Download/locate the traditional site; snapshot the source | +| Pre-migration assessment | Assessing scope | Inventory Bootstrap-3 usage; flag custom CSS / Liquid risk | +| Run migration engine | Running migration | `pac pages bootstrap-migrate` → `V5` | +| Review change report | Reviewing report | Parse `logs.txt`; group auto-applied vs hierarchy/manual | +| Apply residual fixes | Applying fixes | Per-category assisted fixes for the flagged hierarchy/CSS items | +| Upload and verify flag | Uploading and verifying | `pac pages upload`; verify the Bootstrap 5 runtime flag flipped | +| Validate and summarize | Validating | `/test-site`, visual check, record usage, summary | + +Mark each task `in_progress` when starting and `completed` when done via `TaskUpdate`. + +--- + +**Begin with Phase 1: Verify Prerequisites** diff --git a/plugins/power-pages/skills/migrate-bootstrap/references/bootstrap-v5-manual-fixes.md b/plugins/power-pages/skills/migrate-bootstrap/references/bootstrap-v5-manual-fixes.md new file mode 100644 index 000000000..8262798ab --- /dev/null +++ b/plugins/power-pages/skills/migrate-bootstrap/references/bootstrap-v5-manual-fixes.md @@ -0,0 +1,258 @@ +# Bootstrap 5 Manual-Fix Recipes (residual items the engine only logs) + +`pac pages bootstrap-migrate` auto-applies the well-known class renames but can only **flag** +structural ("hierarchy") changes and changes that require new CSS. Those appear in `logs.txt` +as `Need hierarchy change for …` lines or as "replaced with styles" notes. This reference is the +recipe book the `/migrate-bootstrap` skill uses in **Phase 6** to apply (or flag) each one. + +**Apply with per-category consent. Never blindly rewrite Liquid-entangled markup — flag it instead.** +After each edit, re-check the affected lines against the file's `-diff.json` so you don't +disturb the engine's auto-applied changes. + +--- + +## 1. Grid hierarchy — move `.row` inside a `.container` + +**Log signal:** `Need hierarchy change` referencing `row` / "Moving row inside container". + +**Why:** Bootstrap 5 grid requires rows to live inside a `.container` / `.container-fluid` (or a +`.col`). Bootstrap 3 markup often has bare `.row`s. + +**Before** +```html +
+
+
+``` + +**After** +```html +
+
+
+
+
+``` + +**Watch for:** reverse-row layouts — if the original used a `flex-direction: row-reverse` style, +keep it on the `.row` element (`
`). + +--- + +## 2. Navbar structure + +### 2a. Delete `navbar-header` + +**Log signal:** "navbar-header is dropped. Delete the division containing this class". + +**Before** +```html + +``` + +**After** — remove the `navbar-header` wrapper, lift its children to be direct children of `.navbar`: +```html + +``` + +### 2b. Collapse three `icon-bar`s into one `navbar-toggler-icon` + +**Log signal:** "One navbar-toggler-icon is sufficient to replace 3 icon-bar". + +**Before** +```html + +``` + +**After** +```html + +``` + +> The engine adds `navbar-expand-md`, `nav-item`, and `nav-link` where it can. Verify the toggler's +> `data-bs-toggle="collapse"` / `data-bs-target` point at the collapse container's id. + +--- + +## 3. Panels → cards: contextual classes need CSS + +The engine maps `panel*` → `card*` but **contextual** panel classes +(`panel-primary/success/info/warning/danger`) carried color styling that Bootstrap 5 cards don't +reproduce. The engine flags these as "replaced with styles." + +**Apply the inline style on the card** (Bootstrap-3 color values): + +| Class | Add to the card element | +|-------|-------------------------| +| `panel-primary` | `style="color:#fff;background-color:#337ab7;border-color:#337ab7;"` | +| `panel-success` | `style="color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6;"` | +| `panel-info` | `style="color:#31708f;background-color:#d9edf7;border-color:#bce8f1;"` | +| `panel-warning` | `style="color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc;"` | +| `panel-danger` | `style="color:#a94442;background-color:#f2dede;border-color:#ebccd1;"` | + +**Before** +```html +
+``` + +**After** +```html +
+``` + +> Prefer migrating these to project CSS classes or Bootstrap 5 utilities (`text-bg-primary`, border +> utilities) if the user wants a cleaner result — the inline styles are a faithful 1:1 fallback. + +--- + +## 4. Page header — `page-header` dropped + +**Log signal:** "page-header is dropped. Can be replaced with style attributes". + +**Before** +```html + +``` + +**After** +```html +

Title

+``` + +--- + +## 5. Pager — dropped, replace with styles + +**Log signal:** "Pager is dropped. Replacing with style attributes to achieve the same effects". + +**Before** +```html + +``` + +**After** +```html + +``` + +> For a plain (non-prev/next) pager link use the base style without the `float`. Regular +> `pagination` markup (`page-item` / `page-link`) is handled automatically — only `pager` needs this. + +--- + +## 6. Button block — `btn-block` → `.d-grid` + +**Log signal:** "Instead of using .btn-block, wrap buttons with .d-grid and a .gap-*". + +**Before** +```html + +``` + +**After** +```html +
+ +
+``` + +> Group adjacent full-width buttons inside a single `.d-grid gap-2` wrapper. + +--- + +## 7. Form structure + +The engine renames most form classes (`control-label` → `col-form-label`, `input-lg` → +`form-control-lg`, `input-group-addon` → `input-group-text`, `help-block` → `form-text`, etc.) but a +few are structural / deprecated: + +| Bootstrap 3 | Action in Bootstrap 5 | +|-------------|-----------------------| +| `form-group` | Deprecated; use spacing utilities (e.g. `mb-3`) on the wrapper, or `.row` for horizontal forms | +| `form-inline` | Removed; rebuild with grid/flex utilities (`d-flex`, `gap-*`, `align-items-center`) | +| `form-horizontal` | Removed; use `.row` + `col-form-label` + grid columns | +| `form-control-static` | `form-control-plaintext` | + +These need a judgment call about the intended layout — apply the closest utility-based equivalent and +flag for visual QA. + +--- + +## 8. Carousel + +The engine handles most carousel renames (`item` → `carousel-item`, glyphicon chevrons → +`carousel-control-prev-icon` / `-next-icon`, drops `carousel-control`). Verify the result matches the +Bootstrap 5 control structure: + +```html + +``` + +Ensure carousel images carry `d-block w-100` and that `data-bs-ride` / `data-bs-slide-to` replaced the +`data-ride` / `data-slide-to` attributes. + +--- + +## 9. Glyphicons → icons + +Bootstrap 5 ships no glyphicons. The engine maps a few known ones (e.g. `glyphicon-search` → +`fa-solid fa-magnifying-glass`, carousel chevrons). Any remaining `glyphicon-*` references must be +replaced with the site's icon library (Font Awesome / Bootstrap Icons). List remaining glyphicons for +the user and propose mappings — don't guess silently for icons with no obvious equivalent. + +--- + +## 10. Liquid edge cases — flag, don't rewrite + +Where Bootstrap classes are entangled with Liquid, the engine deliberately leaves them. **Flag these +for the user with file + line; do not auto-rewrite:** + +- Conditional dropdowns: `{% if … %} … dropdown … {% endif %}`. +- Class values built inside Liquid (`class="{% if … %}…{% endif %}"`). +- Liquid tags containing `>` (`{% … > … %}`) near markup the mappers scan. + +For these, describe the change the user should make (e.g. add `dropdown-menu-end`, rename +`data-toggle` → `data-bs-toggle`) rather than editing across the Liquid boundary. + +--- + +## 11. Web template partial paths — `RenderPartialHtml` + +**Log signal:** runtime replacement note for `RenderPartialHtml`. + +If web templates reference partials by path that changed during migration, update the +`RenderPartialHtml` path so the partial resolves. Verify against the migrated folder structure. + +--- + +## Order of operations within a file + +1. Let the engine's auto-applied changes stand (they're already in the V5 copy). +2. Apply structural fixes outermost-first: container/row hierarchy (§1) and navbar (§2) before + component-level styling (§3–§9). +3. Re-render and visually QA — these residual fixes are exactly the ones the engine couldn't verify, + so they're the most likely source of layout regressions. diff --git a/plugins/power-pages/skills/migrate-bootstrap/references/migration-engine-reference.md b/plugins/power-pages/skills/migrate-bootstrap/references/migration-engine-reference.md new file mode 100644 index 000000000..c9625a882 --- /dev/null +++ b/plugins/power-pages/skills/migrate-bootstrap/references/migration-engine-reference.md @@ -0,0 +1,154 @@ +# Migration Engine Reference — `pac pages bootstrap-migrate` and the auto flag-flip + +This reference documents the CLI engine the `/migrate-bootstrap` skill orchestrates: what +`pac pages bootstrap-migrate` does, what it produces, and how `pac pages upload` +automatically flips the server-side Bootstrap 5 runtime flag. + +## Site type: classic only + +This engine operates on **classic / native** Power Pages sites — the downloaded site +folder containing Liquid web templates and config: + +- `website.yml` (has `adx_websiteid`) +- `sitesetting.yml` +- `*.webtemplate.source.html`, `*.html`, `*.aspx`, `*.ascx`, `*.css`, `*.js` + +It does **not** apply to code sites (React/Vue/Angular/Astro), which have +`powerpages.config.json` + a framework `package.json` and are never Bootstrap-3-based. + +## `pac pages bootstrap-migrate` + +```bash +pac pages bootstrap-migrate --path "" +``` + +| Argument | Alias | Required | Notes | +|----------|-------|----------|-------| +| `--path` | `-p` | Yes | Path to the downloaded classic site folder | + +### Behavior (non-destructive) + +The engine never edits the source in place. It: + +1. Copies the entire `` tree to a **new sibling folder `V5`**. +2. Rewrites Bootstrap-3 classes → Bootstrap-5 across `*.html`, `*.js`, `*.aspx`, `*.ascx` (markup) + and `*.css` (stylesheets), via ~20 component-specific mappers + (Navbar, Panel→Card, Grid, Glyphicon→FontAwesome, Tooltip, Dropdown, Form, Table, Pagination, + Badge, ProgressBar, Carousel, Button, InputGroup, Image, ListGroup, Utility classes, Breadcrumb, + plus generic `data-*` → `data-bs-*`). +3. Replaces the embedded `bootstrap.min.css` (v3) with the Bootstrap 5 stylesheet. +4. Appends a `Site/BootstrapV5Enabled` record (value `true`) to `sitesetting.yml` + (skipped if already present). +5. Writes a human-readable **`logs.txt`** change report at the root of `V5`. +6. Writes a per-file **`-diff.json`** capturing the exact replacements applied to each file. + +### Representative auto-applied renames + +| Bootstrap 3 | Bootstrap 5 | +|-------------|-------------| +| `panel-heading / -body / -footer / -title` | `card-header / card-body / card-footer / card-title` | +| `navbar-toggle` | `navbar-toggler` | +| `navbar-right / navbar-left` | `ms-auto / me-auto` | +| `img-responsive / img-circle` | `img-fluid / rounded-circle` | +| `pull-left / pull-right` | `float-start / float-end` | +| `text-left / text-right` | `text-start / text-end` | +| `data-toggle / data-dismiss / data-target` | `data-bs-toggle / data-bs-dismiss / data-bs-target` | +| `label-*` | `bg-*` | +| `col-xs-* → col-sm-* → col-md-* → col-lg-* → col-xl-* → col-xxl-*` | grid tiers shifted up one | +| `sr-only` | `visually-hidden` | +| `table-condensed` | `table-sm` | + +Some classes are **dropped** (e.g. `panel-default`, `panel-group`, `text-hide`) and some are +**replaced with inline styles** (panel contextual classes) — see the manual-fixes reference for the +ones that need follow-up. + +## `logs.txt` format + +The report opens with three file lists (the engine's categorization): + +1. **HTML files with No change** +2. **HTML files with Replacement/Addition/Deletion changes** (auto-applied) +3. **HTML files with Hierarchy changes** (auto-detected but **logged only — manual work required**) + +Then, per changed file, a block separated by a line of dashes: + +``` + +Total Number of Changes: +1 Replacing with at line: col: +2 Adding at line: col: +3 Deleting at line: col: +4 Need hierarchy change for at line: col: +... +``` + +Message verbs map to `ModificationType`: + +| Log verb | ModificationType | Auto-applied? | +|----------|------------------|---------------| +| `Replacing … with …` | Replacement | Yes | +| `Adding …` | Addition | Yes | +| `Deleting …` | Deletion | Yes | +| `Need hierarchy change for …` | HierarchyChange | **No — manual** | + +A file lands in category (3) if it contains any `HierarchyChange`; otherwise category (2) if it has +any changes, else category (1). + +## `pac pages upload` — content upload + automatic runtime flag flip + +```bash +pac pages upload --path "V5" +``` + +`--modelVersion` defaults to `Standard` (correct for a classic site). Use `pac pages upload` — +**not** `upload-code-site`. + +After uploading content, the upload verb runs an internal **Bootstrap V5 post-processor** +automatically. Its logic: + +1. If the uploaded `sitesetting.yml` does **not** contain `Site/BootstrapV5Enabled = true` → **no-op**. +2. Read `adx_websiteid` from `website.yml`. If missing/invalid → skip (logged warning). +3. Resolve the portal id(s) for that website via the Power Pages API. +4. For each portal, PATCH `SetPortalBootstrapV5Enabled` (the server-side runtime flag). + +> **Step 3 is the common failure.** `adx_website` is a Dataverse **content record**; the "portal" is +> the **provisioned, running site** registered with the Power Pages management layer. A website can +> exist (and be listed/downloaded) with **no active portal**. When step 3 finds none, the post-processor +> logs `Skipping SetPortalBootstrapV5Enabled: no portal found for website via Power Pages API` +> (level `WRN`) and the flag is **never set**. The fix is to **activate the site first**, then re-upload. + +### Critical caveat: flag-flip outcomes are swallowed — read `pac-log.txt` + +The post-processor catches skip/HTTP/auth conditions and logs them (`WRN`/`Error`) — it does **not** +raise. So **a successful `pac pages upload` exit code does not prove the runtime flag flipped.** There +is **no per-folder upload log**; the only record is the rolling PAC diagnostic log: + +```bash +pac telemetry status # prints: The diagnostic logs can be found at: <…>\logs\pac-log.txt +grep -iE "BootstrapV5UploadPostProcessor|SetPortalBootstrapV5Enabled" "" +``` + +Three outcomes: **applied** (`INF`) → on; **`Skipping … no portal found`** (`WRN`) → site not +activated; **`ERR`/exception** → flip attempted but failed (auth/HTTP). The skill must read this in +Phase 7.3 *before* trusting the live site — an unactivated site returns a 500 (Dataverse-connection +null-ref) that is unrelated to Bootstrap and must not be mistaken for a transient post-upload restart. + +### Pre-flight the flag-flip gate + +Because the flip is gated on file contents, before uploading confirm in `V5`: + +- `website.yml` has a valid `adx_websiteid`, and +- `sitesetting.yml` contains `Site/BootstrapV5Enabled` = `true` (the engine adds this; verify it + survived any Phase 6 edits). + +If either is absent, the flip silently no-ops even though the content uploads fine. + +## Required commands / feature availability + +The skill depends on these verbs being present in the CLI build / tenant: + +- `pac pages download` — fetch a classic site locally. +- `pac pages bootstrap-migrate` — the migration engine. +- `pac pages upload` — upload + auto flag-flip. + +Probe with `pac pages help` in Phase 1 and stop with guidance if any are missing.