ci: add sample-app PR gate#595
Conversation
Adds scripts/check-samples.mjs and a Sample App Checks workflow that validates sample apps under samples/: example config present, no committed uipath.json, .gitignore ignoring .uipath, README Preview section with media, and (where applicable) favicon, @uipath/coded-apps-dev devDependency, and vite.config base + import. Rules are pure functions with a --selftest. The CI job gates only the apps changed in a PR (--changed), so new/edited samples must comply while the existing backlog is fixed over time. Run all apps locally with npm run check:samples. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
Committing uipath.json is allowed; remove that check.
Review summaryOne issue found:
|
A rel=icon link passes even when the referenced file was deleted. Resolve the href (handling absolute /public and root paths) and assert the file exists, so the check validates the artifact rather than the promise.
The favicon check pulled the href out of index.html with two regexes. Regex on HTML breaks on attribute order and quoting, so this uses node-html-parser and a `link[rel~=icon]` selector instead. The other checks are grep/presence tests, not parsers, and stay as-is. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
Review summaryOne new finding this run:
(Three previously-raised threads on |
Action apps use the @uipath/coded-action-app harness, which supplies base and config at runtime, so they don't need @uipath/coded-apps-dev or its vite import. The coded-apps-dev and vite-config rules now accept either harness and skip the vite-import check for action apps. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
Review summaryOne new finding this run:
(Four previously-raised threads remain open: missing |
Review summaryOne new finding this run:
(Five previously-raised threads remain open: missing |
Moves the sample-app gate from a standalone sample-checks.yml workflow into a check-samples job in pr-checks.yml (SHA-pinned actions, npm ci, and a --selftest step), and deletes sample-checks.yml. These are filesystem /structural checks, so they stay a script + CI job rather than an oxlint rule. Also addresses review findings in check-samples.mjs: - guard the entry point so importing checkApp doesn't run() / shell out to git - favicon check uses the git-tracked set (not existsSync) so local matches CI - previewSection includes the heading line (media on the heading line counts) - selftest: add negative cases for example-config and coded-apps-dev Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
Review summaryOne new finding this run:
|
github.base_ref is empty on workflow_dispatch, which produced an invalid "origin/" ref and failed the job on every manual trigger. Fall back to main so it diffs against origin/main instead. Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
|
✅ No issues found. Checked for bugs and CLAUDE.md compliance. |
Replaces the always-run --selftest CI step with a vitest unit test (tests/unit/scripts/check-samples.test.ts) that exercises the rule engine. The rule logic is now covered by the normal test run, and the check-samples job only gates the apps changed in a PR.
|
|
||
| it('flags a Preview heading with no media', () => { | ||
| expect(rules({ ...clean, 'README.md': '# App\n## Preview\njust text' })).toContain('readme-preview'); | ||
| }); |
There was a problem hiding this comment.
The previewSection off-by-one fix changed slice(start + 1, …) to slice(start, …) specifically to handle media placed directly on the heading line (e.g. ## Preview ). Neither of the two existing readme-preview tests exercises that case:
'# App\n## Preview\n\n'— media is on the next line; both old and new slice would match.'# App\n## Preview\njust text'— no media anywhere; both would fail.
If the slice were accidentally reverted to start + 1, all current tests would still pass. Suggest adding:
| }); | |
| }); | |
| it('passes when media is placed on the Preview heading line', () => { | |
| expect(rules({ ...clean, 'README.md': '# App\n## Preview \n' })).not.toContain('readme-preview'); | |
| }); | |
| it('flags a missing favicon link', () => { |
|




Sample-app PR gate
Adds
scripts/check-samples.mjsand a Sample App Checks workflow that enforces the sample-app conventions.Rules
uipath.json.example.gitignorepresent and ignores.uipathPreviewheading with an image/gif/videoindex.htmlfavicon<link>points at a file that existsindex.html@uipath/coded-apps-dev, or@uipath/coded-action-appfor action apps)index.htmlvite.configsetsbase: './'and (non-action apps) imports@uipath/coded-apps-devApps are auto-discovered (any tracked
package.jsonundersamples/). Rules are pure functions with a--selftest. Run all apps locally:npm run check:samples.How the files are read
The favicon rule reads an attribute out of
index.html, so it parses the HTML withnode-html-parserrather than a regex. The rest are grep-style checks (does the README mention media, does the vite config containbase: './'), which is what regex andString.includesare for. The vite check greps the source on purpose: loading the config to readbasewould neednpm ciper app, which is too heavy for a structural gate.Scope: changed apps only (for now)
The CI job runs
--changed, gating only the sample apps touched in a PR. Existing apps currently have 47 violations (mostly missing demo gifs/favicons and per-app config the app owners need to supply). Rather than block every PR on that backlog, each app becomes enforced the next time it is changed. Flip CI to a fullnpm run check:samplesonce the backlog is cleared.Generated with Claude Code