Skip to content

Commit 1b5bd69

Browse files
committed
Merge remote-tracking branch 'origin/main' into priyanshu92-spa-templates-create-site
# Conflicts: # plugins/power-pages/scripts/lib/render-template.js
2 parents 49d4e48 + 5ebf14d commit 1b5bd69

436 files changed

Lines changed: 53888 additions & 4644 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ body:
1010
options:
1111
- canvas-apps
1212
- code-apps
13+
- mcp-apps
14+
- mobile-app
1315
- model-apps
1416
- power-automate
1517
- power-pages

.github/ISSUE_TEMPLATE/feature_request.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ body:
1010
options:
1111
- canvas-apps
1212
- code-apps
13+
- mcp-apps
14+
- mobile-app
1315
- model-apps
1416
- power-automate
1517
- power-pages
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Functional unit tests for the model-apps plugin, across ubuntu × windows × macos
2+
# and Node 20 × 22.
3+
#
4+
# Why a matrix: these scripts shell out to `pac`/`az`, build filesystem paths, and
5+
# parse CLI output, so path-separator and shell differences are a real regression
6+
# class that a single-OS run would miss. Both Node versions are covered because the
7+
# plugin is developed on Node 22 while the repo's other test workflow pins Node 20 —
8+
# a version-only failure should be attributable, not a mystery.
9+
#
10+
# Deliberately mirrors `power-pages-script-tests.yml` rather than extending it: the
11+
# two plugins have independent path filters, so a change to one must not spend CI
12+
# time on the other.
13+
name: model-apps-script-tests
14+
15+
on:
16+
pull_request:
17+
branches:
18+
- main
19+
paths:
20+
- "plugins/model-apps/**"
21+
# The eval harness lives at the repo root but is owned by this plugin, so
22+
# an eval-only change must still trigger the eval job below.
23+
- "evals/model-apps/**"
24+
- ".github/workflows/model-apps-script-tests.yml"
25+
26+
jobs:
27+
test-model-apps-scripts:
28+
name: test-model-apps-scripts (${{ matrix.os }}, node ${{ matrix.node }})
29+
runs-on: ${{ matrix.os }}
30+
# Defense-in-depth: opt this CI job out of telemetry transmission so a test
31+
# that forgets to isolate emission can never POST to a real collector. The
32+
# plugin's committed ikey.json currently ships `disabled: true`, but that is
33+
# a value that can change; the guard must not depend on it. Suppresses
34+
# TRANSMISSION only — the local diagnostic mirror is still written, so this
35+
# cannot change what the tests actually assert.
36+
env:
37+
POWER_PLATFORM_SKILLS_TELEMETRY_MODEL_APPS_OPTOUT: "1"
38+
strategy:
39+
fail-fast: false
40+
matrix:
41+
os:
42+
- ubuntu-latest
43+
- windows-latest
44+
- macos-latest
45+
node:
46+
- 20
47+
- 22
48+
steps:
49+
- name: checkout
50+
uses: actions/checkout@v4
51+
52+
- name: setup-node
53+
uses: actions/setup-node@v4
54+
with:
55+
node-version: ${{ matrix.node }}
56+
57+
# run-tests.js is the plugin's own regression gate: it runs every
58+
# scripts/tests/*.test.js and exits non-zero on any failure. Invoking it
59+
# (rather than a bare `node --test`) keeps CI and the documented local
60+
# command identical, so a green PR means the same thing a green local run
61+
# does. The vendored SDK's own Jest suite is intentionally NOT run here —
62+
# it needs a Node-20-ABI `canvas` native module and the SDK source tree,
63+
# neither of which exists in this repo; the real-bundle contract tests in
64+
# scripts/tests/ cover the shipped bundle instead.
65+
- name: run-model-apps-script-tests
66+
shell: bash
67+
working-directory: plugins/model-apps
68+
run: node scripts/run-tests.js
69+
70+
test-model-apps-evals:
71+
name: test-model-apps-evals
72+
runs-on: ubuntu-latest
73+
env:
74+
POWER_PLATFORM_SKILLS_TELEMETRY_MODEL_APPS_OPTOUT: "1"
75+
steps:
76+
- name: checkout
77+
uses: actions/checkout@v4
78+
79+
- name: setup-node
80+
uses: actions/setup-node@v4
81+
with:
82+
node-version: 20
83+
84+
# The eval harness lives at the repo root (evals/model-apps/**) but is
85+
# owned by this plugin, so it is gated by the same path filter. These are
86+
# the offline scoring/fixture tests only — the Layer 1/2 runners need a
87+
# live Dataverse org and are run manually. `evals/model-apps/tests/` holds
88+
# tests for code shared by the app-builder and genpage runners; a new test
89+
# directory has to be added here or it silently never runs.
90+
- name: run-model-apps-eval-tests
91+
shell: bash
92+
run: node --test evals/model-apps/tests/*.test.js evals/model-apps/app-builder/tests/*.test.js evals/model-apps/genpage/tests/*.test.js

.github/workflows/validate-repository-metadata.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ name: validate-repository-metadata
22

33
on:
44
pull_request:
5-
branches:
6-
- main
75
workflow_dispatch:
86

97
jobs:
108
validate-repository-metadata:
119
name: validate-repository-metadata
1210
runs-on: ubuntu-latest
11+
env:
12+
POWER_PLATFORM_SKILLS_TELEMETRY_POWER_PAGES_OPTOUT: "1"
1313
steps:
1414
- name: checkout
1515
uses: actions/checkout@v4
@@ -30,3 +30,9 @@ jobs:
3030

3131
- name: validate-telemetry-ikeys
3232
run: node scripts/validate-telemetry-ikeys.js
33+
34+
- name: test-secure-process-execution-validator
35+
run: node --test scripts/tests/validate-secure-process-execution.test.js
36+
37+
- name: validate-secure-process-execution
38+
run: node scripts/validate-secure-process-execution.js

.npmrc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Never write registry URLs into a committed package-lock.json.
2+
#
3+
# `resolved` records the exact feed a package came from. Public npm is blocked on some Microsoft
4+
# networks, so a contributor installing through an internal Azure Artifacts feed would otherwise
5+
# rewrite every `resolved` to that internal URL and leak it into this OSS repo on the next commit.
6+
# Omitting the field keeps `integrity` (the sha512 that actually protects the supply chain) while
7+
# making the lock feed-agnostic, so it resolves against whatever registry each contributor uses.
8+
# Same setting microsoft/powerplatform-vscode uses.
9+
#
10+
# NOTE: npm reads the per-project .npmrc from the directory that OWNS package.json -- it does not
11+
# walk up. This root copy therefore does NOT cover plugins/model-apps/scripts/_vendor-build, which
12+
# has its own package.json and its own .npmrc (verified with `npm config get` from inside it). This
13+
# copy is the default for any future package.json added at the repo root.
14+
#
15+
# It likewise does NOT affect the other plugins: plugins/mobile-apps/template and the
16+
# plugins/power-pages/skills/create-site/assets/* packages each own a package.json, and
17+
# `npm config get omit-lockfile-registry-resolved` reads false in each. That is deliberate -- those
18+
# are templates scaffolded into USER projects, and forcing our lockfile policy onto a user's project
19+
# would be wrong. They also ship no lock file today. A repo-wide test
20+
# (plugins/model-apps/scripts/tests/run-tests.test.js) sweeps every committed package-lock.json and
21+
# fails if any records a non-public feed URL, so a lock added elsewhere is still covered.
22+
omit-lockfile-registry-resolved=true

AGENTS.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,25 @@ claude --plugin-dir /path/to/plugins/<plugin-name>
4141

4242
No root-level build, lint, or test commands exist. Build/test tooling lives inside each plugin.
4343

44+
## CI
45+
46+
**Only two workflows run on every PR**`validate-keyword-case` and `validate-repository-metadata`.
47+
Both are repo-wide and enforce metadata/marketplace rules, not behavior.
48+
49+
**Every test workflow is path-filtered to a single plugin** (`power-pages``plugins/power-pages/**`;
50+
`model-apps``plugins/model-apps/**` + `evals/model-apps/**`). This is deliberate — a PR should not
51+
spend CI on a plugin it never touched — but it has a corollary: *a green PR does not mean the repo is
52+
green*, only that the paths you touched are.
53+
54+
**A test suite with no workflow silently never runs.** When you add tests to a plugin, add or extend
55+
that plugin's own path-filtered workflow in the same PR; do not widen another plugin's filter to
56+
cover yours.
57+
58+
A plugin that has adopted telemetry must also set its opt-out env var on any job that could execute a
59+
telemetry-emitting hook or script (see `## Shared Telemetry`) — e.g.
60+
`POWER_PLATFORM_SKILLS_TELEMETRY_MODEL_APPS_OPTOUT: "1"`. It suppresses transmission only, so it
61+
cannot change what a test asserts.
62+
4463
## Plugin Conventions
4564

4665
Each plugin follows this structure:
@@ -53,7 +72,7 @@ Each plugin follows this structure:
5372
- `scripts/` — Shared utility scripts referenced by skills and agents
5473
- `references/` — Shared reference documents used by multiple skills
5574

56-
Skills are defined in `SKILL.md` files with YAML frontmatter (name, description, allowed-tools, model, hooks). The `allowed-tools` field must use a **comma-separated list** (e.g., `allowed-tools: Read, Write, Edit, Bash, Glob, Grep`) — not JSON array syntax (`["Read", "Write"]`) or YAML list syntax. Each skill may include validation scripts in a `scripts/` subdirectory, run as Stop hooks when the skill session ends.
75+
Skills are defined in `SKILL.md` files with YAML frontmatter (name, description, allowed-tools, model, hooks). The `allowed-tools` field must use a **comma-separated list** (e.g., `allowed-tools: Read, Write, Edit, Bash, Glob, Grep`) — not JSON array syntax (`["Read", "Write"]`) or YAML list syntax. Each skill may include a validation script in a `scripts/` subdirectory (the first `validate*.js`, discovered automatically). It is run by the plugin's **`PostToolUse` hook on the `Skill` tool**`hooks/run-skill-posttool-validation.js` looks the script up and executes it after the Skill tool returns, propagating its exit code. Note this fires when the skill is **invoked**, not when its work finishes, so a validator must no-op (approve) when the artifacts it checks are not present yet.
5776

5877
## Cross-Plugin Shared Skills
5978

@@ -73,6 +92,8 @@ This keeps the skill discoverable in each plugin while preserving install-time p
7392

7493
Edit `shared/telemetry/` first, then refresh every adopting plugin's copied `scripts/lib/telemetry/lib` directory in the same change so the canonical source and bundled plugin content stay in sync.
7594

95+
`eventInfo` is a dynamic escape hatch whose nested keys are not enforced by `FIELD_TYPES`. Follow the approved schema in `shared/telemetry/README.md`; do not add nested fields or arbitrary payloads without privacy review and coordinated disclosure, schema, and test updates.
96+
7697
**Never reuse another plugin's instrumentation key or event stream.** When adopting telemetry in a new plugin, copy only the routing-agnostic library (`shared/telemetry/lib``plugins/<plugin>/scripts/lib/telemetry/lib`) — do **not** copy an existing adopter's real `ikey.json` (or its `resolver.js`). Each plugin's `ikey.json` carries that plugin's own instrumentation key(s), collector routing, and `event_stream_name`; start from the placeholder `shared/telemetry/ikey.json` (every region key is `PLACEHOLDER_REPLACE_BEFORE_SHIPPING` and it ships `disabled: true`) and provision a fresh, plugin-specific key before shipping. Copying a key already committed to another plugin (e.g. lifting `power-pages`'s `ikey.json` wholesale) mis-attributes the new plugin's events to the other plugin's Kusto stream and pollutes it — the copy step must bring over library code only, never another plugin's provisioned `ikey.json`/`resolver.js`.
7798

7899
This invariant is CI-enforced: `node scripts/validate-telemetry-ikeys.js` (wired into the `validate-repository-metadata` workflow) scans every `plugins/*/**/ikey.json`, ignores placeholder/empty values, and fails if the same instrumentation key or `event_stream_name` appears under two different plugins. A single plugin reusing one key across regions is allowed; only cross-plugin reuse fails. Run it locally after touching any plugin's `ikey.json`.

README.md

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,13 @@ Create and deploy Power Pages sites using modern development approaches.
6363

6464
### [Model Apps](plugins/model-apps/README.md) (`plugins/model-apps`)
6565

66-
Build and deploy Power Apps generative pages for model-driven apps.
66+
Build model-driven Power Apps end to end, and the generative pages that go in them.
6767

68-
**Stack**: React + TypeScript + Fluent, deployed via PAC CLI
68+
**Skills**: `/app-builder` (**Preview**) builds and edits a whole app — tables, relationships, forms,
69+
views, charts, security roles, app + sitemap — from a natural-language intent; `/genpage` builds
70+
generative pages for an app that already exists. Use either independently — neither requires the other
71+
72+
**Stack**: React + TypeScript + Fluent, deployed via PAC CLI and the headless `cds-maker-sdk`
6973

7074
### [MCP Apps](plugins/mcp-apps/README.md) (`plugins/mcp-apps`)
7175

@@ -265,4 +269,6 @@ Any use of third-party trademarks or logos are subject to those third-party's po
265269
266270
## Telemetry
267271
268-
Plugins that ship 1DS telemetry (currently: `power-pages`) gather anonymous usage signals. Telemetry is default-on; users opt out per-plugin via the `/<plugin>:telemetry off` command (e.g. `/power-pages:telemetry off`), stored in `~/.power-platform-skills/config.json`. For automation/CI, each adopting plugin also honors a per-plugin opt-out environment variable `POWER_PLATFORM_SKILLS_TELEMETRY_<PLUGIN>_OPTOUT` (e.g. `POWER_PLATFORM_SKILLS_TELEMETRY_POWER_PAGES_OPTOUT=1`); when set it disables transmission with the highest precedence, overriding any `/<plugin>:telemetry` choice. See `shared/telemetry/README.md`.
272+
Power Pages and Model Apps ship 1DS telemetry code, but their committed configurations have different states. Power Pages telemetry is enabled and default-on; its usage events can include Dataverse organization and Entra tenant GUIDs when PAC is signed in, plus the signed-in user's Entra object ID when PAC exposes it. Model Apps ships hard-disabled (`disabled: true`), so it currently transmits no events and writes no local telemetry mirror. If Model Apps is enabled later, its events can include organization and tenant GUIDs but exclude the signed-in user's Entra object ID.
273+
274+
For an enabled plugin, users opt out of transmission via `/<plugin>:telemetry off` (for example, `/power-pages:telemetry off`), stored in `~/.power-platform-skills/config.json`. The local diagnostic mirror is still written after this transmission-only opt-out. Each adopting plugin also honors `POWER_PLATFORM_SKILLS_TELEMETRY_<PLUGIN>_OPTOUT` for automation and CI. The environment variable has highest precedence and disables transmission regardless of the saved command choice. See `shared/telemetry/README.md` for the full field list, kill-switch semantics, and local-mirror behavior.

0 commit comments

Comments
 (0)