Skip to content

Make CI check what it claims to check - #98

Merged
guanzhousongmicrosoft merged 2 commits into
documentdb:mainfrom
GuanzhouSong:chore/ci-hardening
Jul 30, 2026
Merged

Make CI check what it claims to check#98
guanzhousongmicrosoft merged 2 commits into
documentdb:mainfrom
GuanzhouSong:chore/ci-hardening

Conversation

@GuanzhouSong

Copy link
Copy Markdown
Contributor

Four fixes, each for something currently unguarded. No behaviour change to the site itself.

1. YAML schema validation has never validated anything

The step ran three commands that all point at paths which do not exist:

npx yaml-ls-check .\articles

Those are Windows separators. On the Ubuntu runner bash treats \a as an escape, so the argument reaching the tool is .articles. Every run to date has printed:

Looking for YAML files to validate at: .articles
Validating 0 YAML files.
Validation complete.        # exit 0

Correcting the paths alone would not have fixed it. yaml-ls-check reads its schema mapping from <root>/.vscode/settings.json, where <root> is the directory you pass — so yaml-ls-check articles looks for articles/.vscode/settings.json, finds nothing, and silently degrades to syntax-only checking. A relative root fails differently: the mapping's ./schema/... entries resolve to /schema/... and the schema cannot be loaded.

Passing the workspace as an absolute path is the form that works. Verified against a deliberately broken file:

articles/content.yml:1:1: Missing property "landing". yaml-schema: Articles Content Configuration
articles/content.yml:1:1: Property notLanding is not allowed.
EXIT=1

Against the tree as it stands it validates 11 files, all passing, so this tightens the check without needing content changes.

The separate npm install yaml-ls-check step is also gone. It pulled in the entire project dependency tree as a side effect and wrote to package.json and package-lock.json inside CI; npx --yes fetches just the tool.

2. Lint never ran anywhere

next build does not lint, and no workflow invoked npm run lint. An ESLint or TypeScript upgrade could go green without its effect on the lint rules ever being exercised — which is exactly the situation the recent ESLint 10 and TypeScript 6 upgrades were in.

3. No job had a timeout

A stalled step ran against GitHub's 6-hour default before failing. Not hypothetical: a Validate YAML files step recently sat over six minutes on work that normally takes 24 seconds, and had to be cancelled by hand. The deployment workflow is the more serious case — its pages concurrency group means one stuck run blocks every deployment queued behind it.

Added: 10 min (YAML), 20 min (build), 30 min (deploy build), 10 min (deploy).

4. Nothing checked where the lockfile resolves from

A package-lock.json generated behind a corporate registry proxy records that proxy's backing-feed URLs rather than registry.npmjs.org. Those hosts are unreachable from public CI and by outside contributors, and the resulting failure surfaces far from its cause. The new step asserts every resolved URL points at the public registry and prints the offenders on failure. Dry-run against current main: passes.

Plus: concurrency

A rebase or force-push now supersedes in-flight PR runs instead of leaving several racing on stale commits — during the recent Dependabot burst four runs were in flight at once. Pushes to main are never cancelled, so every default-branch commit keeps a recorded result.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WWCBtvyCpxc2aqtyLDhDeE

Four fixes, each for something currently unguarded.

1. YAML schema validation never validated anything.

The step ran three commands that all pointed at paths which do not exist:

  npx yaml-ls-check .\articles

The backslashes are Windows separators. On the Ubuntu runner bash treats
`\a` as an escape, so the argument reaching the tool is `.articles`, and
the output was `Validating 0 YAML files. Validation complete.` with exit
0 on every run.

Correcting the paths alone would not have been enough. yaml-ls-check
reads its schema mapping from `<root>/.vscode/settings.json`, where
`<root>` is the directory passed on the command line, so
`yaml-ls-check articles` looks for `articles/.vscode/settings.json`,
finds nothing, and falls back to syntax-only checking. Relative roots
also break the mapping's `./schema/...` references, which resolve to
`/schema/...`. Passing the workspace as an absolute path is the form
that works: the schemas load and violations are reported. Against the
tree as it stands this validates 11 files, all passing.

The separate `npm install yaml-ls-check` step is gone. It installed the
entire project dependency tree as a side effect and wrote to
package.json and package-lock.json in CI; `npx --yes` fetches just the
tool.

2. Lint never ran anywhere.

`next build` does not lint, and no workflow invoked `npm run lint`, so
an ESLint or TypeScript upgrade could go green without its effect on the
lint rules ever being exercised.

3. No job had a timeout.

A stalled step therefore ran against GitHub's 6-hour default before
failing. This is not hypothetical: a `Validate YAML files` step recently
sat over six minutes on work that normally takes 24 seconds. The
deployment workflow is the more serious case, since its `pages`
concurrency group means one stuck run blocks every deployment queued
behind it.

4. Nothing checked where the lockfile resolves from.

A lockfile generated behind a corporate registry proxy records that
proxy's backing-feed URLs instead of registry.npmjs.org. Those hosts are
unreachable from public CI and by outside contributors, and the failure
appears far from its cause. The new step asserts every `resolved` URL
points at the public registry and prints the offenders when it fails.

Also adds a concurrency group so a rebase or force-push supersedes
in-flight pull request runs rather than leaving several racing on stale
commits. Pushes to main are never cancelled, so every default-branch
commit keeps a recorded result.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WWCBtvyCpxc2aqtyLDhDeE
The new lint step failed on third-party JavaScript, not ours:

  vendor/bundle/ruby/3.3.0/gems/jekyll-4.4.1/lib/jekyll/commands/serve/
  livereload_assets/livereload.js
    1:221  error  Missing semicolon  semi

ruby/setup-ruby runs with bundler-cache: true, which vendors gems into
vendor/bundle before the lint step, so ESLint sees Jekyll's minified
livereload.js. The directory is already in .gitignore, but ESLint flat
config does not read .gitignore, so the ignore has to be repeated in
eslint.config.mjs. This is invisible locally, where vendor/ does not
exist.

Also ignores .jekyll-cache and any _site output for the same reason.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WWCBtvyCpxc2aqtyLDhDeE
@guanzhousongmicrosoft
guanzhousongmicrosoft merged commit e24932f into documentdb:main Jul 30, 2026
2 checks passed
@GuanzhouSong
GuanzhouSong deleted the chore/ci-hardening branch July 30, 2026 15:35
guanzhousongmicrosoft pushed a commit that referenced this pull request Jul 30, 2026
#98 fixed the invocation so schema validation runs at all. With it
running, two of the three mappings turn out to match nothing:

  ./schema/blog.content.schema.json      -> blogs/content.{yml,yaml}
  ./schema/reference.content.schema.json -> reference/content.{yml,yaml}

There is no blogs/content.yml; the blog data lives in
blogs/_data/posts.yml. And schema/reference.content.schema.json does not
exist in the repository at all, while reference/ is generated at build
time from documentdb/docs and contains no content.yml either.

The net effect was that of the 11 YAML files the job validates, exactly
one had a schema attached and the other ten were syntax-checked only.

The blog schema needed no changes beyond its category list: it is titled
"Blog Post Link", is an array of objects with title, category,
description, tags and uri, which is precisely the shape of
blogs/_data/posts.yml. Only the glob was wrong.

Changes:

- Repoint blog.content.schema.json at blogs/_data/posts.{yml,yaml}.
- Drop the reference/ mapping. Its schema file does not exist, and the
  directory it targets is generated rather than authored here.
- Add blog.categories.schema.json for blogs/_data/categories.yml, which
  had no schema. It constrains the identifier format and requires label
  and meta on every entry.
- Add documentdb-blog to the category enum. It is defined in
  categories.yml but was missing from the enum, so the first post filed
  under it would have failed validation for no good reason.

Verified with the same yaml-ls-check invocation CI uses. The tree as it
stands passes, and both new mappings reject bad input:

  posts.yml:14:13: Value is not accepted. Valid values: "documentdb-blog",
    ... yaml-schema: Blog Post Link
  categories.yml:7:1: Missing property "label". yaml-schema: Blog Categories


Claude-Session: https://claude.ai/code/session_01WWCBtvyCpxc2aqtyLDhDeE

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants