From 91d428e047c47fe4299613986ba7ea0bb96417c7 Mon Sep 17 00:00:00 2001 From: Albert Bausili Date: Sat, 26 Sep 2026 15:26:18 +0200 Subject: [PATCH 1/3] ci: set up CodeRabbit and Codecov (celeris#690) CodeRabbit: .coderabbit.yaml, validated against schema.v2.json. The review is advisory (never requests changes or approves) and is tuned to a docs site: every API, default and behaviour claim is checked against goceleris/celeris (a linked repository), unsourced technical claims and benchmark figures are flagged, and results/**/*.json cells are excluded from review because probatorium's publisher generates them. The assertive profile is chosen because CodeRabbit enables the Biome and React Doctor accessibility rules only in that profile. The summary goes into the walkthrough comment, not the PR description. Codecov: a new Coverage workflow runs bun test --coverage (lcov), refuses an empty profile, and uploads with codecov-action v7.1.1 over GitHub OIDC, so no CODECOV_TOKEN secret exists. codecov.yml keeps every status informational. v7 rather than v5: v6.0.1 fixed template injection in the action's own run steps and v5.5.5 does not carry that fix. The workflow is test-coverage.yml, not coverage.yml: the .gitignore line coverage.* matches coverage.yml and would have left it out of the commit. coverage/ (bun's output directory) is now ignored. --- .coderabbit.yaml | 286 ++++++++++++++++++++++++++++ .github/workflows/test-coverage.yml | 86 +++++++++ .gitignore | 3 + codecov.yml | 67 +++++++ 4 files changed, 442 insertions(+) create mode 100644 .coderabbit.yaml create mode 100644 .github/workflows/test-coverage.yml create mode 100644 codecov.yml diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..6fc5902 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,286 @@ +# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json +# +# CodeRabbit review configuration for goceleris/docs (goceleris.dev). +# +# CodeRabbit reads this file from the PR branch under review, and a committed +# .coderabbit.yaml takes precedence over the settings in the CodeRabbit web UI: +# https://docs.coderabbit.ai/getting-started/yaml-configuration +# Every key below is validated against the schema on the first line. The URL +# https://docs.coderabbit.ai/schema/coderabbit.json returns 404; schema.v2.json +# is the one CodeRabbit's own configuration guide links. +# +# The review is advisory. It never requests changes or approves: `main` is +# gated by the `build` CI job and a code-owner approval (CONTRIBUTING.md), and +# nothing here changes that. +# +# Public repositories with fewer than 10 stars are not reviewed automatically +# on the open-source plan (https://docs.coderabbit.ai/management/plans): until +# this repository reaches 10 stars, comment `@coderabbitai review` on a PR, or +# tick "Trigger review" in CodeRabbit's status comment. `auto_review` below is +# what applies once automatic reviews are available. + +# The site's prose uses British spelling ("behaviour" 54 times against +# "behavior" 12; "normalise", "licence"), so reviews and LanguageTool follow it. +language: en-GB +tone_instructions: >- + Be concise and specific. Name the file and line, and for any claim about + Celeris cite the celeris source file that confirms or contradicts it. +early_access: false + +reviews: + # assertive, not chill: CodeRabbit enables the accessibility rules of Biome + # and React Doctor only in the assertive profile, and accessibility of the + # site's components is one of the things this review is for. + # https://docs.coderabbit.ai/tools/biome https://docs.coderabbit.ai/tools/react-doctor + profile: assertive + # Never request changes and never approve. Merge gating stays with CI and + # CODEOWNERS. + request_changes_workflow: false + # Put the summary in CodeRabbit's walkthrough comment instead of appending + # it to the PR description, so hand-written descriptions stay as written. + high_level_summary: true + high_level_summary_in_walkthrough: true + review_status: true + # Lists the files it skipped, the linked repositories it used and the tool + # output, which is how a reader can tell what the review did and did not see. + review_details: true + collapse_walkthrough: true + changed_files_summary: true + # Most PRs here are prose and page markup; call-flow diagrams add length + # without information. + sequence_diagrams: false + estimate_code_review_effort: true + assess_linked_issues: true + related_issues: true + related_prs: true + suggested_labels: true + auto_apply_labels: false + # CODEOWNERS already requests the one owner on every PR. + suggested_reviewers: false + auto_assign_reviewers: false + poem: false + in_progress_fortune: false + abort_on_close: true + + auto_review: + enabled: true + auto_incremental_review: true + drafts: false + # Default branch (main) only. + base_branches: [] + # Deliberate red-run control PRs are titled "DO NOT MERGE: ..." (for + # example #68); they exist to fail a gate and need no review. + ignore_title_keywords: + - "DO NOT MERGE" + - "WIP" + + # Exclusions only (an include pattern would turn this into an allow list). + # Exclusions win regardless of order: + # https://docs.coderabbit.ai/configuration/path-instructions#how-path-filters-combine + # results/ holds benchmark cells that probatorium's publisher commits; they + # are generated datasets, never hand-edited (CONTRIBUTING.md), and + # `bun run validate` in CI is their gate. results/README.md stays in review. + # The .gz halves of each cell, bun.lock, dist/ and src/data/generated/ are + # already in CodeRabbit's default ignore list. + path_filters: + - "!results/**/*.json" + + path_instructions: + - path: "src/content/docs/**/*.{md,mdx}" + instructions: | + These pages document the Celeris framework (goceleris/celeris, linked below). Accuracy is + the priority: + - Check every API name, type, Config field, default value, option, middleware name and + behaviour claim against goceleris/celeris on its default branch. Flag anything that does + not exist there, has a different signature or default, or behaves differently. + - Flag any technical claim a reviewer cannot verify from the celeris source, a linked issue, + or a committed benchmark cell. Say what evidence would settle it. Do not accept "fast", + "zero-copy", "lock-free", "SIMD", "no allocations" or similar without a source. + - Pages cite source locations such as `celeris/resource/config.go:13-19`. Check that the + file exists and that those lines still say what the page claims; line numbers drift as + celeris changes. + - Go code samples must compile against the current celeris API: correct imports, types, + method names and error handling. Flag samples that would not compile or that teach an + unsafe pattern. + - Any benchmark figure (RPS, latency percentile, memory, a ratio or percentage against + another framework) must name the version, date and arch of the + results//// cell it comes from. Flag unsourced numbers. + - Check links: relative links must point at pages that exist under src/content/docs (the + site serves them at /docs/); anchors must match a heading; links into GitHub repos + must point at paths that exist. + - Frontmatter must satisfy src/content.config.ts: `title` is required; `description`, + `group`, `order` and `draft` are optional. + - Keep the existing British spelling (behaviour, normalise). + - path: "src/pages/**" + instructions: | + Astro pages of a fully static site (output: 'static', no SSR, no runtime server; deployed as + Cloudflare Workers static assets). + - Benchmark numbers on pages must be computed from the generated data (src/lib/site.ts, + src/data/generated/, public/data/) at build time, never typed in. Flag hard-coded figures + and fallbacks that would present an invented number as a measurement when data is missing. + - Hardware, cluster and methodology claims (methodology.astro) must agree with the env.json + of the committed cells in results/ and with how goceleris/probatorium runs the benchmarks. + Flag claims that cannot be traced to either. + - Accessibility: one h1 per page, headings in order, landmark elements, alt text on images, + accessible names on links and buttons, visible focus, and no information conveyed by + colour alone. + - SEO/meta: title and description set through BaseLayout, canonical URLs derived from + SITE_URL. + - path: "src/components/**/*.astro" + instructions: | + Shared Astro components (header, footer, SEO, theme toggle, code window). + - Accessibility: keyboard operability, focus order and visible focus, aria attributes only + where native semantics are missing, sufficient contrast in both themes, + prefers-reduced-motion respected. + - Script in components ships to every page. Flag client-side JavaScript that could be static + HTML/CSS, and any third-party request (fonts and assets are self-hosted on purpose). + - path: "src/dashboard/**/*.{ts,tsx}" + instructions: | + The benchmark dashboard: one Preact island (@astrojs/preact with compat: false, so + react/react-dom imports do not resolve) using @preact/signals and uPlot. + - Correctness of what is displayed: statuses not_applicable and dnf must never render as a + zero or a number, and suspect cells must never be shown as a winner (see + methodology.astro). Units, percentile labels and "higher/lower is better" must match the + metric. + - Accessibility: charts need a text alternative or data table, controls need accessible + names and keyboard support, and colour must not be the only way to tell series apart. + - TypeScript strict mode (astro/tsconfigs/strict). Flag `any`, non-null assertions on data + that can be missing, and effects or subscriptions that are never cleaned up. + - path: "src/lib/results/**/*.ts" + instructions: | + The typed data layer that walks, validates and aggregates benchmark cells. It is the + provenance boundary: every number the site shows passes through here. + - Never invent, impute or carry forward a value that a cell does not contain. Missing data + must stay missing and be labelled as such. + - Schema handling must match the cells goceleris/probatorium publishes (summary.json + schema_version, env.json). Flag a version check that would silently accept an incompatible + major. + - Status gating (ok, not_applicable, dnf, suspect) and smoke-run exclusion must stay + consistent with scripts/build-data.ts and methodology.astro. + - Behaviour changes need a matching test in scripts/build-data.test.ts. + - path: "scripts/build-data.ts" + instructions: | + Builds the dashboard assets and, with --validate-only, is the benchmark-data gate that CI + and sync-benchmarks.yml run. + - The gate must stay strict: a malformed cell exits non-zero, and examining zero cells + exits with status 2. Flag any change that could make it pass without inspecting cells. + - The normal build path must stay lenient (warn, mark excluded:invalid, continue) so one bad + cell cannot take the deploy down. + - path: "scripts/**/*.test.ts" + instructions: | + bun:test tests for the data layer. Each test should fail if the behaviour it names + regressed; flag assertions that cannot fail and tests that only exercise the happy path of a + gate. + - path: ".github/workflows/**" + instructions: | + Every action pinned to a full commit SHA with a version comment; top-level permissions {} or + contents: read, with anything wider granted only on the job that needs it; + persist-credentials: false on checkout unless the job pushes; no ${{ }} expressions inside + run: (pass them through env:); never pull_request_target. Flag anything that deploys: + Cloudflare Workers Builds is the only deployer. + + pre_merge_checks: + # This is a site, not a library; docstring coverage would only be noise. + docstrings: + mode: "off" + title: + mode: warning + requirements: >- + Conventional-commit prefix as CONTRIBUTING.md asks: docs, feat, fix, + chore, ci, deps, perf, refactor or test, with an optional (scope), + then a colon and an imperative summary. + description: + mode: warning + issue_assessment: + mode: warning + custom_checks: + - name: "Benchmark provenance" + mode: warning + instructions: | + Fail if the pull request adds or changes a benchmark figure (requests per second, a + latency percentile, memory or CPU usage, or a ratio or percentage comparing Celeris with + another framework) in src/content/**, src/pages/** or README.md, and that figure is + neither computed at build time from the results/ data (for example through + src/lib/site.ts) nor accompanied by the results//// cell it was + taken from. Also fail if the pull request edits an existing file under results/ by hand; + cells are committed only by goceleris/probatorium's publisher. Pass otherwise, including + when the pull request contains no benchmark figures. + + tools: + # Tools run only on files they understand; the ones listed are the ones + # that apply to this repository. None of them runs in this repository's + # GitHub workflows, so CodeRabbit does not skip them for duplication. + markdownlint: + enabled: true + languagetool: + enabled: true + level: default + biome: + enabled: true + reactDoctor: + enabled: true + # Off: with no stylelint config in the repo, CodeRabbit lints CSS against + # stylelint-config-standard-scss, a style guide this site does not follow. + # Biome already checks the CSS in the assertive profile. + # https://docs.coderabbit.ai/tools/stylelint + stylelint: + enabled: false + actionlint: + enabled: true + zizmor: + enabled: true + shellcheck: + enabled: true + yamllint: + enabled: true + gitleaks: + enabled: true + trufflehog: + enabled: true + osvScanner: + enabled: true + github-checks: + enabled: true + +chat: + auto_reply: true + art: false + # Only organization members can drive CodeRabbit from comments. Chat + # commands can start finishing touches (autofix, generated tests, merge + # conflict resolution) that push commits or open PRs. Outside contributors + # still get the automatic review on their PRs. + allow_non_org_members: false + +knowledge_base: + # Learnings, issue and PR context stay scoped to this repository (what + # `auto` resolves to for a public repository). + learnings: + scope: auto + web_search: + enabled: true + code_guidelines: + enabled: true + filePatterns: + - "CONTRIBUTING.md" + # The repositories whose behaviour these docs describe. Linked-repository + # context is part of the open-source plan (Team features, up to 5 links). + # https://docs.coderabbit.ai/knowledge-base/multi-repo-analysis + linked_repositories: + - repository: "goceleris/celeris" + instructions: >- + The Go HTTP framework these docs describe: its public API, Config + fields and defaults, the io_uring, epoll, adaptive and std engines, + and the in-tree middleware. Treat its default branch as the source of + truth for every API name, default and behaviour claim in + src/content/docs. + - repository: "goceleris/probatorium" + instructions: >- + The benchmark harness that produces and publishes the cells under + results/ (mage Publish), including the summary.json and env.json + schemas, the scenario set, the cluster hardware and how runs are + averaged. Source of truth for the methodology page and for data + provenance. + - repository: "goceleris/loadgen" + instructions: >- + The load generator the benchmarks use. Source of truth for how + requests, latency percentiles and errors are measured. diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml new file mode 100644 index 0000000..496b1b3 --- /dev/null +++ b/.github/workflows/test-coverage.yml @@ -0,0 +1,86 @@ +name: Coverage + +# Line and function coverage of the data-layer tests (`bun test`), uploaded to +# Codecov (codecov.yml holds the report settings). +# +# What the number means: Bun's coverage lists only the files the tests load, +# so the percentage is over src/lib/results/ (the code that walks, validates +# and aggregates benchmark cells). Pages, components and the dashboard have no +# tests and do not appear in the report at all, so they neither lower nor +# raise it. scripts/build-data.ts runs in a child process in its tests and is +# not measured either. +# +# Upload auth is GitHub OIDC (`use_oidc`): the job mints a short-lived token +# for Codecov, so no CODECOV_TOKEN secret exists anywhere. On a pull request +# from a fork GitHub issues no OIDC token; codecov-action detects the fork and +# falls back to Codecov's tokenless upload for public repositories. +# +# Not a merge gate. The `build` job in ci.yml stays the required check, and +# every Codecov status is informational (codecov.yml). This job fails only if +# the tests fail, the profile is empty, or the upload fails, so a coverage +# pipeline that silently stopped reporting shows up as a red check instead of +# a stale number. + +on: + pull_request: + push: + branches: [main] + +permissions: {} + +concurrency: + group: coverage-${{ github.event.pull_request.number || github.ref }} + # Supersede stale PR runs, but let every push to main finish: each one is + # the baseline the next PR is compared against. + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + coverage: + name: coverage + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read # checkout + # Granted here only; the workflow default above is no permissions. + id-token: write # codecov-action requests an OIDC token for the upload + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + # Same Bun as ci.yml, which mirrors Cloudflare Workers Builds. + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: latest + + - name: Install (frozen lockfile) + run: bun install --frozen-lockfile + + # The tests need neither build:data nor astro build: they read + # test/fixtures and synthesize their own trees. + - name: Test with coverage + run: bun test --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir=coverage + + # An lcov file with no source records would upload "successfully" and + # report nothing. Refuse it here. + - name: Check the coverage profile is not empty + run: | + set -euo pipefail + test -s coverage/lcov.info + # grep -c prints 0 and exits 1 on no match; keep the 0. + files=$(grep -c '^SF:' coverage/lcov.info || true) + lines=$(grep -c '^DA:' coverage/lcov.info || true) + echo "lcov.info: ${files} source files, ${lines} line records" + if [ "$files" -eq 0 ] || [ "$lines" -eq 0 ]; then + echo "::error::coverage/lcov.info has no source files or no line records" + exit 1 + fi + + - name: Upload to Codecov + uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 + with: + use_oidc: true + files: ./coverage/lcov.info + disable_search: true + name: bun-test + fail_ci_if_error: true diff --git a/.gitignore b/.gitignore index cef6f26..a8d2287 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,9 @@ public/data/ # Bun .bun/ +# `bun test --coverage` output (coverage/lcov.info); CI uploads it to Codecov +coverage/ + # Local demo benchmark tree (generated from fixtures for dashboard preview) .dev-results/ diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..bf7191d --- /dev/null +++ b/codecov.yml @@ -0,0 +1,67 @@ +# Codecov configuration for goceleris/docs. +# +# Reference: https://docs.codecov.com/docs/codecovyml-reference +# Validate a change before pushing it: +# curl -sS --data-binary @codecov.yml https://api.codecov.io/validate +# +# Coverage comes from `bun test --coverage` in +# .github/workflows/test-coverage.yml. Bun only reports files the tests load, +# so the percentage covers the data layer (src/lib/results/) and says nothing +# about pages or the dashboard, which have no tests. See that workflow's +# header. +# +# Every status is informational for now: Codecov reports, and never fails a +# PR. To make a status blocking later, remove `informational: true` from it +# and add its check name (codecov/project or codecov/patch) to the required +# status checks of the `main` ruleset. + +codecov: + # Wait for the commit's other CI checks to finish before posting (the + # defaults, stated so a reader does not have to look them up). + require_ci_to_pass: true + notify: + wait_for_ci: true + +coverage: + precision: 2 + round: down + range: "60..90" + status: + # Whole-project coverage against the base commit of the PR. + project: + default: + target: auto + threshold: 1% + informational: true + # Coverage of the lines this PR adds or changes. + patch: + default: + target: auto + informational: true + +comment: + layout: "condensed_header, condensed_files, condensed_footer" + behavior: default + # Most PRs here change prose or page markup that the tests never load. + # Comment only when coverage drops or the PR adds lines the tests miss; + # the statuses above report on every PR either way. + require_changes: "coverage_drop OR uncovered_patch" + require_base: false + require_head: true + +# Line annotations on the PR diff for added lines the tests do not cover. Only +# files the tests load appear in the report, so these land on data-layer +# changes, which is where they are useful. +github_checks: + annotations: true + +# Nothing below is code the site ships. Bun already leaves test files and +# node_modules out of the report; these entries keep generated data and +# fixtures out if a future test loads them. +ignore: + - "results/**" + - "test/**" + - "**/*.test.ts" + - "src/data/generated/**" + - "public/**" + - ".dev-results/**" From dfb9e1042e5900e0bc338c96dda4b9e7d2b035dc Mon Sep 17 00:00:00 2001 From: Albert Bausili Date: Sat, 26 Sep 2026 15:32:07 +0200 Subject: [PATCH 2/3] ci(codecov): report coverage even when another check fails require_ci_to_pass: true holds Codecov's status back whenever any other check on the commit fails. The upload here only happens after the coverage job's own tests and the non-empty-profile check pass, so the report is complete regardless; an unrelated failure (astro check in build, CodeQL) should not hide it. wait_for_ci stays true so a PR gets one status once CI finishes. Raised in CodeRabbit's review of #74. --- codecov.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/codecov.yml b/codecov.yml index bf7191d..5d025c5 100644 --- a/codecov.yml +++ b/codecov.yml @@ -16,10 +16,13 @@ # status checks of the `main` ruleset. codecov: - # Wait for the commit's other CI checks to finish before posting (the - # defaults, stated so a reader does not have to look them up). - require_ci_to_pass: true + # Report even when another check fails. The upload only happens after the + # coverage job's own tests and profile check pass, so the report is + # complete; a failure elsewhere (astro check, CodeQL) should not hide it. + # https://docs.codecov.com/docs/common-recipe-list + require_ci_to_pass: false notify: + # Still wait for CI to finish, so a PR gets one status, not a sequence. wait_for_ci: true coverage: From 2264a7147dc4673b0a944126624167056078a080 Mon Sep 17 00:00:00 2001 From: Albert Bausili Date: Sat, 26 Sep 2026 15:34:50 +0200 Subject: [PATCH 3/3] ci(coderabbit): say in the file that a PR editing it does not preview it On #74 CodeRabbit reviewed with its defaults and said why: a PR that changes the CodeRabbit configuration is reviewed with the configuration already on the target branch unless its author is a repository collaborator, and an org member (author_association MEMBER) does not count. Record that next to the sub-10-star note, and that automatic reviews on a repository this size come from the org's trial, not the open-source plan. --- .coderabbit.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 6fc5902..25ee74b 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -13,11 +13,17 @@ # gated by the `build` CI job and a code-owner approval (CONTRIBUTING.md), and # nothing here changes that. # +# A PR that edits this file does not preview it: CodeRabbit reviews such a PR +# with the configuration already on `main` unless its author is a repository +# collaborator, and it does not count org members as collaborators (it said +# so on #74). Changes here take effect once they are merged. +# # Public repositories with fewer than 10 stars are not reviewed automatically -# on the open-source plan (https://docs.coderabbit.ai/management/plans): until -# this repository reaches 10 stars, comment `@coderabbitai review` on a PR, or -# tick "Trigger review" in CodeRabbit's status comment. `auto_review` below is -# what applies once automatic reviews are available. +# on the open-source plan (https://docs.coderabbit.ai/management/plans), only +# during the org's trial: after it, comment `@coderabbitai review` on a PR, or +# tick "Trigger review" in CodeRabbit's status comment, until this repository +# reaches 10 stars. `auto_review` below is what applies when reviews are +# automatic. # The site's prose uses British spelling ("behaviour" 54 times against # "behavior" 12; "normalise", "licence"), so reviews and LanguageTool follow it.