diff --git a/README.MD b/README.MD index ddec830..dac9338 100644 --- a/README.MD +++ b/README.MD @@ -14,3 +14,9 @@ Lokal laufen lassen: ### Update aller Deps `bundle update` + +### Projektnotizen + +Wichtige, nicht offensichtliche Fakten zum Repo (Build/Deploy, CSP, SEO) +liegen unter [`memory/`](memory/README.md). Der Ordner wird nicht auf die +Website veröffentlicht. diff --git a/_config.yml b/_config.yml index 8e1a2ec..1cd1db6 100644 --- a/_config.yml +++ b/_config.yml @@ -57,6 +57,12 @@ exclude: - Gemfile - Gemfile.lock - bin/ + - memory/ # internal project notes, not part of the published site + - README.MD + - package.json + - package-lock.json + - scripts/ + - purgecss.config.js # But make sure to process the following directories and their entries. include: diff --git a/memory/README.md b/memory/README.md new file mode 100644 index 0000000..1bdf1dd --- /dev/null +++ b/memory/README.md @@ -0,0 +1,10 @@ +# Project notes + +Durable, non-obvious facts about this repo that aren't derivable from the code or git history. Keep each note short and factual; delete notes that go stale. + +For this project these notes live **in the repo** (here), not in the global Claude auto-memory location — add new durable findings as files in this directory +and link them below. + +- [ci-and-verification.md](ci-and-verification.md) — how the site builds/deploys and how to verify changes (incl. the rtk/prettier gotcha) +- [csp-policy.md](csp-policy.md) — the meta-tag CSP: what it allows and why, and what can't be enforced this way +- [seo-and-metadata.md](seo-and-metadata.md) — SEO/metadata decisions (German-only, no JSON-LD, canonical/feed) diff --git a/memory/ci-and-verification.md b/memory/ci-and-verification.md new file mode 100644 index 0000000..dbff205 --- /dev/null +++ b/memory/ci-and-verification.md @@ -0,0 +1,35 @@ +# CI, build, and verification + +## How the site is built and deployed + +- Deployed via **GitHub Actions** (`.github/workflows/jekyll.yml`) running `bundle exec jekyll build`, **not** the `github-pages` gem (it is commented out in + the `Gemfile`). Consequence: the site is **not** restricted to the GitHub Pages plugin allowlist — any Jekyll plugin listed in the `Gemfile` + `_config.yml` + `plugins:` works. (The old `whitelist:` block was removed as dead config.) +- PRs are gated by `.github/workflows/ci.yml`: build → PurgeCSS → html-proofer → pa11y (non-blocking) → prettier. `_site/` is built during CI, so anything that + scans the repo (like prettier) must ignore `_site/` via `.prettierignore` — do **not** pass `--ignore-path ''`, which disables it. + +## `.ruby-version` must be fully qualified + +rbenv does exact-string matching on `.ruby-version`. A bare minor like `3.4` fails with `rbenv: version '3.4' is not installed` and blocks every `ruby`/`bundle` +command. Use the installed patch version (e.g. `3.4.9`). `ruby/setup-ruby` in CI accepts the fully-qualified value fine. + +## Verifying prettier: bypass the rtk proxy + +An `rtk` shell hook in this environment intercepts `prettier` invocations and can print a **masked/canned result** (e.g. "All files formatted correctly") while +the real prettier is reporting failures. This caused false "prettier passes locally" claims that CI then failed on. When verifying formatting, run it through +the proxy bypass: `rtk proxy npx prettier --check .`. Treat CI green as the ground truth. + +## Bundler / rubygems networking + +`bundle update`/`install` fetches from `index.rubygems.org` (a CNAME to Fastly). Some local resolvers (e.g. a consumer router's DNS forwarder) fail to resolve +that specific hostname while resolving the `rubygems.org` apex fine — symptom is "Could not reach host index.rubygems.org" even though the network is up. Fix on +the machine: point DNS at a public resolver (1.1.1.1 / 8.8.8.8). Not a repo problem. + +## New top-level files leak into the published site + +Jekyll publishes any top-level file or directory that is not in the `_config.yml` `exclude:` list. Dotfiles/dot-dirs are auto-excluded, but plain-named ones are +NOT. This repo had `README.MD`, `package.json`, `package-lock.json`, `scripts/`, `purgecss.config.js`, and a `memory/` dir all being served publicly (e.g. +`https://www.inoeg.de/package.json` was live) until they were added to `exclude:`. + +Rule: whenever you add a plain-named top-level file or directory that is repo tooling / internal docs (not site content), add it to the `exclude:` list in +`_config.yml`, then confirm it is absent from `_site/` after a build. diff --git a/memory/csp-policy.md b/memory/csp-policy.md new file mode 100644 index 0000000..77a7c6e --- /dev/null +++ b/memory/csp-policy.md @@ -0,0 +1,24 @@ +# Content-Security-Policy (delivered via meta tag) + +The site has no HTTP-header control (GitHub Pages), so the CSP is a `` in `_includes/head.html`. + +Current policy: + +``` +default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; +font-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self' +``` + +Why each non-obvious part is there: + +- **`style-src 'unsafe-inline'`** — several Webflow-exported pages (`_pages/projekte.html`, `_pages/offenerbrief-epa-2025.html`) use inline `style=` attributes. + Removing it breaks their layout; a CSP that breaks styling is worse than none. +- **`font-src 'self' data:`** — `webflow.css` embeds an icon font as a `data:application/x-font-ttf` URI. Without `data:` the font is blocked (was a real + console error after the CSP first shipped). +- **`img-src 'self' data:`** — same reason (data-URI images in the CSS). + +Cannot be enforced via a meta tag (header-only — omitted to avoid console noise / false sense of security): `X-Content-Type-Options`, `X-Frame-Options`, and CSP +`frame-ancestors`. Enforcing those needs a header-capable front (e.g. Cloudflare). + +Implication: **do not add inline `