Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **CI is now a job DAG instead of one monolithic job, and the docs deploys no longer expose the Cloudflare token to PR-authored code** (`.github/workflows/ci.yml`, `.github/workflows/housekeeping.yml`, `.github/actions/setup-env/action.yml`, `Makefile`, `docs/wrangler.jsonc`, `AGENTS.md`, `docs/src/content/docs/development.md`, `docs/src/content/docs/claude-code.md`, `CONTRIBUTING.md`, `scripts/lint-pr-title.sh`, `.claude/hooks/agent-bash-gate.sh`): closes #305. The single `make ci` job becomes parallel jobs over the *same Makefile targets* (local `make ci` stays the dev mirror): `lint`, `unit`, `integration`, `e2e` (builds its own SDK dist + cover binary via `make -j test-e2e` on a warm per-suite cache and runs the suite exactly like a local run), `coverage` (a dedicated job that merges every suite's `coverage-<suite>` fragment and applies every threshold gate via `make cov` — like local `make ci`'s final step, so the gate is decoupled from the e2e suite; it's `needs: changes` only and *polls* for the fragments rather than `needs`-ing the suites, so its setup overlaps them and the merge fires ~10s after the last suite instead of serializing ~50s of setup onto the critical path), and `docs-build` (`make build-docs`, docs-affecting changes only, uploads the docs dist artifact the preview/deploy jobs consume) — public-repo runners are free and 4-core, so the pipeline spreads horizontally instead of queueing in one process. The architecture is documented once, in `.github/workflows/README.md` (DAG diagram, design invariants, cache key policy, add-a-job recipe, and the measured-but-deferred optimizations — e2e sharding among them), and the workflow's logic lives in shellcheck-gated scripts (`scripts/ci/` — `classify-changes.sh`, `check-pr-title.sh`, `docs-preview-comment.sh`, `timing-summary.sh`, `wait-artifact.sh`; over the shared, dependency-free path classifier `scripts/classify-paths.sh`, unit-tested by `scripts/classify-paths.test.sh` via `make test-classify-paths` and reused by the `pre-push` git hook so a docs/prose-only push requires only `make verify`, not a full `make ci` — the same suites CI skips for those changes) rather than inline YAML; caches are owned end-to-end by `setup-env` via nested `actions/cache` (automatic post-job saves — the per-job save-step boilerplate is gone); a non-gating `Timing summary` job writes a per-job wall-clock table to every run's Summary page; and `make verify` gains two leaves that gate the new surface area — `lint-sh` (shellcheck `v0.11.0`, checksum-verified install via `scripts/install-shellcheck.sh`) and `lint-gha` (actionlint `v1.7.12`) — so the CI plumbing is linted like any other source. The workflow also handles `merge_group` events (full suite against the merge-group ref), enabling a **merge queue** on `main`: the queue re-tests each PR against current main at landing time, which replaces the ruleset's "require branches to be up to date" rule — no more manual branch updates after every sibling merge. A new aggregator job named `CI` is the ruleset's **sole required status check** (it fails on any failed/cancelled job and counts skipped jobs as passing), so docs-only PRs skip the Go suites without orphaning the gate and future job changes never require ruleset edits. The PR-title (Conventional Commits) gate moves into the `PR title` job under that aggregator, validated by the same `scripts/lint-pr-title.sh` from a trusted `main` checkout; `PR housekeeping` (`pull_request_target`) drops to non-required and keeps what needs fork-PR write access — path labels, the sticky title-explainer comment, and a new nudge that re-runs the failed `PR title` job when a title edit fixes it (the job re-reads the title from the API, so no new push is needed). The **#305 fix**: docs previews/production deploys run in dedicated `docs-preview`/`docs-deploy` jobs that check out trusted `main` (wrangler, worker source, and config never resolve from the PR tree), consume only the static `docs/dist` artifact, and are the only jobs that reference `CLOUDFLARE_*` secrets; previews now publish right after `docs-build` instead of waiting on the full test pipeline, and the `docs-preview` deploy is **non-gating** — it's not in the `CI` aggregator's `needs` (only `docs-build` gates), so a slow or failed Cloudflare preview reports its own "Docs preview" check but never delays or reds the required check; production (`docs-deploy`, on the post-merge main push) still requires everything green. Per-job least-privilege permissions replace the old workflow-wide `contents: write`, and the Go build cache is partitioned per job (unit/integration/e2e compile with different flags) so each suite stays warm.

- **Brand kit gains `branding/avatar.png` — a 512×512 white tile for square/rounded avatar slots** (`docs/scripts/branding/generate.sh`, `docs/public/branding/avatar.png`): `apple-touch-icon.png` is only 180×180 and goes soft when a consumer blows it up to a GitHub org/team avatar (or a Slack/social profile picture). `generate.sh` gains a section 5b that emits a 512px tile — the same solid-bg + centered-mark composition as the touch icon, but on a white background with the brand-blue mark (so it reads as a profile picture rather than a black square in light UIs), rendered from the source `mark.svg` (vector → crisp at any size) with the mark centered and padded so it clears GitHub's rounded-square crop. Both tiles share one `solid_tile_svg` helper so the composition is defined once. No code or runtime surface changes; regenerate with `make branding-docs`.

### Security

- **The structured-query column allowlist is now a hard cap on every column a query references — closing a fail-open data-exposure family on the primary read path** (`internal/query/builder.go`, `internal/query/errors.go` (new), `internal/policy/policy.go`, `internal/discovery/discovery.go`, `internal/api/structured_query.go`, `docs/src/content/docs/access-control.md`, `docs/src/content/docs/api.md`, plus tests in `internal/query/builder_test.go`, `internal/api/structured_query_test.go`, `internal/policy/policy_test.go`, `internal/discovery/discovery_test.go`, `tests/e2e/sdk/query.test.ts`): closes #223. `POST /v1/query` previously authorized only the columns a caller *explicitly* listed, so several inputs bypassed a role's `allow_columns`/`deny_columns` entirely:
Expand Down
Binary file added docs/public/branding/avatar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 33 additions & 4 deletions docs/scripts/branding/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# branding/favicon-{light,dark}.svg static variants (Head.astro live swap)
# branding/favicon.ico 16/32/48 multi-resolution legacy fallback
# branding/apple-touch-icon.png 180x180 app-icon tile (iOS)
# branding/avatar.png 512x512 solid tile (GitHub org/team avatar, Slack, …)
# branding/og.png 1200x630 social card
# branding/mark-light.svg mark in the light-mode accent
# branding/mark-dark.svg mark in the dark-mode accent
Expand Down Expand Up @@ -62,6 +63,13 @@ OG_INK_MUTED=$(brand_color brand-ink-muted) # OG tagline / footer text
WORDMARK_LIGHT=$(brand_color brand-charcoal) # lockup wordmark on light bg (dark ink)
COLOR_TOUCH_BG=$OG_BG
COLOR_TOUCH_FG=$COLOR_DARK
# The avatar tile (GitHub org/team, Slack, …) sits on a white tile rather than
# the dark site bg, so it reads as a distinct profile picture instead of a black
# square in those light UIs. Pure white is a plain neutral, not a brand hue —
# nothing on the live site to drift from — so it's the one literal color here;
# the mark stays the brand-blue accent.
COLOR_AVATAR_BG="#FFFFFF"
COLOR_AVATAR_FG=$COLOR_DARK

# --- Source + output paths ----------------------------------------------------
SRC="$ROOT/docs/src/assets/branding"
Expand Down Expand Up @@ -135,6 +143,15 @@ fi
mark_with_color() { printf '%s' "$MARK_INNER" | sed "s|currentColor|$1|g"; }
lockup_with_colors() { printf '%s' "$LOCKUP_INNER" | sed "s|currentColor|$1|g; s|__WM_FILL__|$2|g"; }

# A solid square tile: a filled background with the mark placed on top via an
# explicit transform — the shared composition behind both the apple-touch-icon
# and the larger avatar.png. Vector source, so the caller rasterizes it to any
# size and stays crisp. Args: <canvas-units> <bg> <fg> <mark-transform>.
solid_tile_svg() {
printf '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 %s %s"><rect width="%s" height="%s" fill="%s"/><g transform="%s">%s</g></svg>' \
"$1" "$1" "$1" "$1" "$2" "$4" "$(mark_with_color "$3")"
}

printf '%s==> Generating brand kit from %s + %s%s\n' \
"$CYAN" "${MARK_SRC#"$ROOT/"}" "${GLOBAL_CSS#"$ROOT/"}" "$RESET"

Expand Down Expand Up @@ -193,11 +210,23 @@ magick "$TMP/fav-16.png" "$TMP/fav-32.png" "$TMP/fav-48.png" "$OUT_KIT/favicon.i
cp "$OUT_KIT/favicon.ico" "$OUT_ROOT/favicon.ico"

# --- 5. apple-touch-icon.png (180x180 solid tile) -----------------------------
cat > "$TMP/touch.svg" <<EOF
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 180"><rect width="180" height="180" fill="$COLOR_TOUCH_BG"/><g transform="translate(28 28) scale(1.24)">$(mark_with_color "$COLOR_TOUCH_FG")</g></svg>
EOF
solid_tile_svg 180 "$COLOR_TOUCH_BG" "$COLOR_TOUCH_FG" "translate(28 28) scale(1.24)" > "$TMP/touch.svg"
rsvg-convert -w 180 -h 180 "$TMP/touch.svg" -o "$OUT_KIT/apple-touch-icon.png"

# --- 5b. avatar.png (512x512 solid tile) --------------------------------------
# A larger sibling of the touch icon for places that crop a square/rounded
# avatar and render it big: GitHub org/team avatars, Slack, social profiles.
# apple-touch-icon is only 180px and looks soft when blown up to a team avatar;
# this renders the same solid-bg + centered-mark composition at 512px (vector
# source → crisp at any size), but on a white tile + brand-blue mark (see the
# COLOR_AVATAR_* note above) so it reads as a profile picture in light UIs. The
# mark's stroked content bbox is x 3..96 (the leftmost round-capped wave's
# stroke reaches ~x3), y 11..89 — center (49.5,50). scale 0.7416 maps it to ~69%
# of the canvas; tx=50-49.5*0.7416=13.29, ty=50-50*0.7416=12.92 center it
# exactly, leaving even padding so the mark clears GitHub's rounded-square crop.
solid_tile_svg 100 "$COLOR_AVATAR_BG" "$COLOR_AVATAR_FG" "translate(13.29 12.92) scale(0.7416)" > "$TMP/avatar.svg"
rsvg-convert -w 512 -h 512 "$TMP/avatar.svg" -o "$OUT_KIT/avatar.png"

# --- 6. og.png (1200x630 social card) -----------------------------------------
# Fill the template's color placeholders from --brand-*, splice the lockup
# (mark accent + wordmark ink) into __LOCKUP_GROUP__. Escape `&` (sed RHS).
Expand Down Expand Up @@ -226,7 +255,7 @@ render_text_png "$OUT_KIT/lockup-dark.svg" "$OUT_KIT/lockup-dark.png" 1200 250
printf '%s colors%s light=%s dark=%s bg=%s ink=%s\n' \
"$CYAN" "$RESET" "$COLOR_LIGHT" "$COLOR_DARK" "$OG_BG" "$OG_INK"
for out in favicon.svg favicon-light.svg favicon-dark.svg favicon.ico \
apple-touch-icon.png og.png \
apple-touch-icon.png avatar.png og.png \
mark-light.svg mark-dark.svg lockup-dark.svg lockup-light.svg \
mark-light.png mark-dark.png lockup-light.png lockup-dark.png; do
printf ' %s✓%s %s\n' "$GREEN" "$RESET" "docs/public/branding/$out"
Expand Down
Loading