Skip to content

Redesign: example fixes and CI verification (merge 2 of 2) - #23

Merged
haksungjang merged 9 commits into
mainfrom
redesign-2026-09
Sep 4, 2026
Merged

Redesign: example fixes and CI verification (merge 2 of 2)#23
haksungjang merged 9 commits into
mainfrom
redesign-2026-09

Conversation

@haksungjang

Copy link
Copy Markdown
Contributor

Summary

Draft. Second and final merge of the site redesign, covering example-code defects and the
CI that keeps them from coming back. Opened early so the new static-verify steps get
exercised on a real pull request; K19 through K22 will land on this same branch before it
is marked ready.

Landed so far:

  • K17 Pinned the syft and grype minimum versions the guides actually need, added the
    sbom format not recognized troubleshooting note, quoted 34 unquoted $(pwd) volume
    paths, and replaced the OSV example with a request that returns real data.
  • K18 Two new checkers. check-code-blocks.py parses every documented code block
    (YAML, JSON, XML, TOML, GitHub Actions via actionlint, GitLab CI, bash via bash -n and
    shellcheck); check-code-refs.py resolves every uses: tag against the remote and every
    install URL over HTTP. Both are wired into static-verify, plus a weekly
    example-refs.yml that files an issue when an external reference disappears.

Test plan

  • check-code-blocks.py --selftest 5/5 — deliberately broken blocks are caught, validate=skip is honoured
  • Full run: 536 blocks pass, 4 skipped
  • check-code-refs.py full run: 15 action refs, 5 install URLs, all resolve
  • actionlint .github/workflows/*.yml exit 0
  • bash .claude/scripts/verify.sh 13/13
  • static-verify green on this PR
  • K19 through K22

The diagnostic run found six defects that break a reader following the
examples. This fixes all of them in Korean and English.

- Quote $(pwd) in every docker -v argument (34 places). An unquoted
  command substitution splits on a path containing spaces.
- State the minimum tool versions for CycloneDX 1.7: syft 1.51 emits it,
  grype 0.118 reads it. An older syft hard-errors on
  -o cyclonedx-json@1.7, so the previous remedy ("pin the specification
  version") failed on exactly the setup that needed it.
- Add a troubleshooting note for "sbom format not recognized", which is
  what an older grype reports when handed a 1.7 SBOM.
- Replace the OSV connectivity check: curl -I https://api.osv.dev
  answers 404 even when the service is healthy. Use a real query whose
  success is legible.
- Mark the two blocks that cannot pass validation with validate=skip:
  the pull_request_target anti-pattern and the cosign placeholder.

Also widen the progress-infra.md ignore rule to every directory. The
rule was anchored to the repository root, so the copy under website/
was untracked and the pre-commit hook swept it into the staged set.

Verified by running the changed blocks: 87 bash blocks pass bash -n and
shellcheck with zero SC2046, verify.sh 13/13, the site builds, and the
OSV replacement command returns a vulns array.

Signed-off-by: Haksung Jang <haksung@sk.com>
The diagnostic run found a workflow that did not parse as YAML and an
action tag that never existed. Both were published and neither was
caught by review, so check them on every pull request.

Two scripts:

- check-code-blocks.py (L1, L2) parses yaml/json/xml/toml, runs
  actionlint on GitHub Actions blocks, checks GitLab CI structure, and
  runs bash -n plus shellcheck. It carries a --selftest that plants
  known-bad blocks and asserts they are caught, because a checker that
  silently inspects nothing also reports zero findings.
- check-code-refs.py (L3) resolves every uses: tag and install URL with
  git ls-remote and curl. actionlint does not resolve references, which
  is how trivy-action@0.36.0 passed every check while being wrong.

Wiring: Layer 4 and Layer 5 run in pre-merge static-verify, with
actionlint pinned. Layer 5 only inspects files the pull request touched,
since a reference can break without anyone editing the docs. The full
sweep runs weekly in example-refs.yml and opens an issue instead of
blocking a merge.

website/reference/samples/ is excluded because it is generated from
output-sample/; fixing the generated copy would be undone on the next
regeneration. Blocks that cannot pass carry validate=skip on the fence.

Also quote four shell variables in notify-ai-coding-update.yml and
sync-agents.yml so actionlint passes across every workflow.

Signed-off-by: Haksung Jang <haksung@sk.com>
The runner has no PyYAML, so Layer 4 died on import before checking
anything. Install it alongside anthropic, and fail early with a readable
message instead of a traceback when it is absent.

The import was the visible half of a wider problem. actionlint,
shellcheck and tomllib were each handled by printing a skip notice and
carrying on, so a machine missing any of them would have reported a
clean pass over blocks nobody inspected. That is the failure mode the
selftest exists to prevent, reproduced inside the checker itself.

Skipped checks are now collected and reported as a failure, listing what
did not run. --allow-missing-tools opts out locally; CI does not use it.
Running under Python 3.9 now correctly fails on the missing tomllib
rather than passing over the toml blocks in silence.

Verified against the CI configuration: Python 3.11 with PyYAML gives a
clean pass over 536 blocks, and Python without PyYAML reproduces the
runner failure and now exits 2 with the message rather than a traceback.
check-code-refs.py needs only the standard library plus git and curl,
both present on the runner.

Signed-off-by: Haksung Jang <haksung@sk.com>
The earlier layers only prove a code block parses. They cannot catch a
toolchain that stops fitting together, which is how a syft and grype
version pair shipped where the SBOM one produced the other refused to
read.

example-e2e.sh runs the three sample walkthroughs the way the READMEs
describe them and checks values rather than exit codes. Java must yield
four components and its scan must contain GHSA-jfh8-c2jp-5v3q, python
five, nodejs more than zero once installed with vendor/legacy-parser
still carrying no license field, and the SBOM syft produces must
actually parse in grype.

Exit codes are not enough on their own. Docker on macOS mounts a host
path outside its sharing list as an empty directory rather than failing,
so syft exits 0 and emits a valid SBOM with nothing in it. --selftest
feeds an empty directory through the same path and asserts the check
rejects it, so a green run means the assertions looked at something.

Image tags are pinned. syft v1.51.1 and grype v0.118.0 are the pair that
emit and read CycloneDX 1.7; an older grype fails this check by design.

The workflow runs weekly and on demand, and on pull requests only when
samples/ or docs/05-tools/ change. A grype database download alone
exceeded seven minutes in testing, so this does not belong in the gate
that every pull request waits on.

Signed-off-by: Haksung Jang <haksung@sk.com>
The npm container ran as root, so every file it wrote under the work
directory belonged to root and the runner could not remove them. Cleanup
emitted 1668 Permission denied lines, which would bury a real error in
any later failure.

Run that container as the calling user instead, giving npm a writable
HOME and cache path inside the container so it still works. Cleanup also
falls back to removing the directory from inside a container if anything
root-owned survives.

Moving the work directory fixed a second problem found while testing
this. It was created with mktemp -d, which on macOS lands under
/var/folders, outside Docker Desktop's file sharing list. Docker mounts
an unshared path as an empty directory rather than failing, so the nodejs
walkthrough could never run on a Mac: npm reported a missing
package.json and the script reported an install failure that had nothing
to do with npm. The work directory now lives under the repository, which
is already a shared path, and the run behaves the same on both systems.

Verified locally after the change: all seven assertions pass, including
the three nodejs ones that had only ever run in CI, with 73 components
matching the CI result exactly, and cleanup writes nothing to stderr.

Signed-off-by: Haksung Jang <haksung@sk.com>
The dark search modal put white text on #8ab4f8 at 2.11:1, the only AA
failure the U-26 audit found. The active row foreground now uses
--brand-fill-fg, which is #fff in light and #062330 in dark, so light keeps
its 5.37:1 and dark rises to 7.72:1. That matches how the dark primary button
already works.

Three lower-priority items from the same audit:

- Result headings are h2 but the theme forced weight 400, breaking the
  heading hierarchy on this page alone. They follow the site token again.
- Result rows were 135px tall, mostly from body heading padding and
  breadcrumb padding the results list does not need. Rows are now 71px, so a
  1000px viewport shows 14 results instead of 7.
- The match highlight was DocSearch's cream with no dark variant. It now
  mixes the primary token, and contrast improves in both themes
  (light 8.39 to 13.18, dark 4.90 to 5.62).

Also refreshed two TRUSCA statements that no longer matched the portal: the
workflow count is twenty-five, not twenty-three, and a hosted demo now
exists where the guide said none was provided.

Signed-off-by: Haksung Jang <haksung@sk.com>
The troubleshooting heading still said [1/12] in both the Korean and the
English section, while the same file states 13 items twice elsewhere.
verify.sh has run 13 checks since the ko/en parity check was added.

Found while auditing K17 to K20 for English drift. No anchor links to the
heading exist, so renaming it breaks nothing.

Signed-off-by: Haksung Jang <haksung@sk.com>
0bd5d5a changed only the first heading in each language, leaving ten
[n/12] headings behind while claiming the count was corrected. verify.sh
prints [n/13] for all thirteen items.

The L4 section described example-e2e.sh but not example-e2e.yml, so a
reader would take the layer for a manual script with no automation. Added
its schedule and triggers, and the reason it is not a PR gate.

Signed-off-by: Haksung Jang <haksung@sk.com>
229cbea added a working directory paragraph without noticing the same
explanation fifteen lines below, so the L4 section stated it twice and
repeated the database size. Kept the lower paragraph, which also covers
gitignore and cleanup, and moved the one sentence it lacked into it.

Signed-off-by: Haksung Jang <haksung@sk.com>
@haksungjang
haksungjang marked this pull request as ready for review September 4, 2026 01:58
@haksungjang
haksungjang merged commit 3d1db2c into main Sep 4, 2026
8 checks passed
@haksungjang
haksungjang deleted the redesign-2026-09 branch September 4, 2026 01:58
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.

1 participant