Updated the README file in the repository root.

This commit is contained in:
2026-08-20 01:10:37 +02:00
parent b6ad9ff497
commit 6ecc20987f
+68 -58
View File
@@ -1,97 +1,97 @@
# Website Template # Website Template
This repository is a **template** for the platform's websites. It ships as a A **template** for the platform's websites, shipping as a complete, buildable
complete, buildable reference site whose identity is the neutral placeholder reference site under the placeholder identity **`Site`** / **`site`**. Generating
**`Site`** / **`site`**. You generate a real site from it in two steps: create a real site takes two steps: create a fresh repository, then run the bootstrap
a fresh repository, then run the bootstrap script to stamp in your site's script to stamp in the display name, slug, and canonical URL.
display name, slug, and canonical URL.
## What's in here ## What's in here
| Path | Role | | Path | Role |
| --- | --- | | --- | --- |
| [`Services/Website`](Services/Website/README.md) | The Hummingbird website service — the executable, the pages, the static assets, and the Docker/Compose deployment. Its README is the operational reference. | | [`Services/Website`](Services/Website/README.md) | The Hummingbird website service — executable, pages, static assets, Docker/Compose deployment. Its README is the operational reference. |
| [`Packages/Infrastructure`](Packages/Infrastructure/README.md) | Shared Hummingbird toolkit: declarative routing, the hardened middlewares, the `Page`/`Asset` scaffolding, and the pre-rendered localized responses. | | [`Packages/Infrastructure`](Packages/Infrastructure/README.md) | Shared Hummingbird toolkit: declarative routing, the hardened middlewares, the `Page`/`Asset` scaffolding, the head-metadata types (`SocialCard`, `StructuredData`, `Analytics`), and the pre-rendered localized responses. |
| [`Packages/Localization`](Packages/Localization/README.md) | String Catalog lookups, `Accept-Language` negotiation, and the catalog-derived language list. | | [`Packages/Localization`](Packages/Localization/README.md) | String Catalog lookups, `Accept-Language` negotiation, and the catalog-derived language list. |
| [`Packages/Persistence`](Packages/Persistence/README.md) | The Fluent data layer: driver selection, migration registration, and the readiness probe. | | [`Packages/Persistence`](Packages/Persistence/README.md) | The Fluent data layer: driver selection (in-memory SQLite or PostgreSQL), migration registration, and the readiness probe. |
| [`Packages/Utility`](Packages/Utility/README.md) | Dependency-free helpers shared across the services. | | [`Packages/Utility`](Packages/Utility/README.md) | Dependency-free helpers shared across the services. |
| `Site.xcodeproj` | The umbrella Xcode project (renamed to your site by bootstrap). | | `Site.xcodeproj` | The umbrella Xcode project (renamed to your site by bootstrap). |
| `Scripts/bootstrap` | The generator. Rewrites placeholders, then deletes itself. | | `Scripts/bootstrap` | The generator. Rewrites placeholders, then deletes itself. |
| `Makefile` | Bootstrap convenience only (`make bootstrap`); removed by the script. | | `Makefile` | Bootstrap convenience only (`make bootstrap`); removed by the script. |
All four packages are **vendored**, not fetched: the service depends on them by All four packages are **vendored**, not fetched: the service depends on them by
relative path, so each generated repository owns its own copy. That is relative path, so every generated repository owns its copy and can diverge
deliberate — a generated site is fully self-contained and can diverge freely. freely. The trade-off is that engine fixes reach existing sites only by hand.
The trade-off is that engine fixes are propagated to existing sites by hand (or
by re-generating and porting content).
## Requirements ## Requirements
- Swift 6.3 toolchain (every manifest is `swift-tools-version:6.3`). - Swift 6.3 toolchain (every manifest is `swift-tools-version:6.3`).
- Docker — optional, for the containerized run/deploy workflow. - Docker — optional: the containerized run/deploy workflow, the local PostgreSQL
- The Hummingbird CLI (`hb`) — optional, only for `make site-run`. instance (`make db-mount`), and the asset-optimization preview.
- Hummingbird CLI (`hb`) — optional, only for `make site-run`.
See [the service README](Services/Website/README.md) for configuration keys, Configuration keys, persistence, testing, and deployment are documented in
persistence, testing, and deployment. [the service README](Services/Website/README.md).
## Generating a new site ## Generating a new site
### 1. Create a repository from this template ### 1. Create a repository from this template
On Gitea, mark this repository as a template once (repository **Settings On Gitea, mark this repository as a template once (**Settings → Template
Template → “Template” checkbox**). New sites are then created with the “Template”**); new sites then come from the **“Use this template”** button, each
**“Use this template”** button, which produces a fresh repository with its own with its own history.
history — no template commits carried over.
> No Gitea button? Just clone this repo, `rm -rf .git`, and continue — the > No Gitea button? Clone this repo, `rm -rf .git`, and continue — bootstrap
> bootstrap script offers to re-initialise git for you. > offers to re-initialise git.
### 2. Bootstrap ### 2. Bootstrap
Clone the new repository, then from its root run: From the new repository's root:
```sh ```sh
make bootstrap # or: ./Scripts/bootstrap make bootstrap # or: ./Scripts/bootstrap
``` ```
You'll be asked for:
| Prompt | Example | Rewrites | | Prompt | Example | Rewrites |
| --- | --- | --- | | --- | --- | --- |
| **Site display name** (PascalCase) | `Loud` | Server name (`LoudWebsite`), `Loud.xcodeproj`, README title. | | **Site display name** (PascalCase) | `Loud` | Server name (`LoudWebsite`), `Loud.xcodeproj`, README title. |
| **Project slug** (lowercase) | `loud-ams` | Container owner, database name/user, Compose project name. | | **Project slug** (lowercase) | `loud-ams` | Container owner, database name/user, Compose project name. |
| **Canonical site URL** (scheme + host) | `https://loud.amsterdam` | The `Sitemap:` reference in `robots.txt` and the `<loc>` entry in `sitemap.xml`. | | **Canonical site URL** (scheme + host) | `https://loud.amsterdam` | The `Sitemap:` reference in `robots.txt` and the `<loc>` entry in `sitemap.xml`. |
The canonical URL defaults to the reserved placeholder `https://site.example.com`; - The URL defaults to the reserved `https://site.example.com`; leaving it is
leaving it there is allowed (the script warns), so you can bootstrap before the allowed (the script warns) and can be corrected later in the two crawler files.
domain is decided and set it later in the two crawler files. - Keeping the default display name `Site` is allowed too: the Xcode rename is
skipped and the placeholders stay in place.
The script rewrites the placeholder occurrences in place, renames - The script refuses to run outside the repository root and validates the URL
`Site.xcodeproj`, optionally starts a fresh git history, and then removes before writing, so a rejected value leaves the tree untouched.
itself along with this file and the root `Makefile`. It refuses to run outside - It rewrites in place, renames `Site.xcodeproj`, optionally starts a fresh git
the repository root, and validates the URL before touching anything — a history, then deletes itself, this file, and the root `Makefile`.
rejected value leaves the tree untouched, so it is safe to re-run.
Accepting the default display name (`Site`) is supported: the rename is skipped
and the placeholders simply stay in place.
### 3. Fill in the content ### 3. Fill in the content
Bootstrap leaves reminders, but in short:
1. **Copy & localization**`Services/Website/Sources/Library/Catalogs/Localizable.xcstrings` 1. **Copy & localization**`Services/Website/Sources/Library/Catalogs/Localizable.xcstrings`
(English only out of the box) and the landing / 404 pages under (English only out of the box) and the landing / 404 pages under
`Services/Website/Sources/Library`. `Services/Website/Sources/Library`.
2. **Static assets**`Services/Website/Resources/Static` (CSS, JS, favicon, icons). 2. **Static assets**`Services/Website/Resources/Static` (CSS, JS, favicon, icons).
The manifest's `name` / `short_name` are empty and are not rewritten by bootstrap. The manifest's `name` / `short_name` are empty and are not rewritten by bootstrap.
3. **Head metadata** — the pages ship no `summary`, `canonicalURL`, `socialCard`, or 3. **Head metadata** — the pages ship no `summary`, `canonicalURL`, `socialCard`, or
`structuredData`, so there is no meta description, no link preview, and no JSON-LD `structuredData`: no meta description, no link preview, no JSON-LD until you
until you supply them. See supply them. See [Page metadata](Services/Website/README.md#page-metadata).
[Page metadata](Services/Website/README.md#page-metadata). 4. **Analytics** — the cookieless [Umami](https://umami.is) tracker ships **off**
4. **Secrets** — set a real `DATABASE_PASSWORD` in a git-ignored (`analytics.websiteID` is empty, so no third-party script is requested).
`Services/Website/.env` (the committed `.env.local` is an example only). Enabling it, in order: point `String.Analytics.origin` at the instance you
5. **Domain models** — the `Persistence` package ships a sample report to, allow that origin in `security.contentSecurityPolicy` (`.env.local`
`ExampleRecord` / `ExampleRepository`; replace it when you add real data. already does; `docker-compose.yml` falls back to a `'self'`-only policy), then
set `ANALYTICS_WEBSITE_ID`. `ANALYTICS_DOMAINS` limits which hosts report;
`ANALYTICS_RECORDER` opts into the session recorder. See
[Analytics](Services/Website/README.md#analytics).
5. **Secrets** — set a real `DATABASE_PASSWORD` in a git-ignored
`Services/Website/.env`; the committed `.env.local` is an example and defaults
the password to the slug.
6. **Domain models** — replace the `Persistence` package's sample `ExampleRecord`
/ `ExampleRepository`. The default driver is in-memory SQLite, migrated on
every boot; a real site sets `DATABASE_DRIVER=postgres` and migrates out of
band (`swift run Website --database-migrate`). See
[Persistence](Services/Website/README.md#persistence).
### 4. Build & run ### 4. Build & run
@@ -105,31 +105,41 @@ make site-mount # docker compose up --build
## What bootstrap does NOT change ## What bootstrap does NOT change
- Organisation (`Röck+Cöde VoF`) and Xcode `DEVELOPMENT_TEAM` — shared across all sites. - Organisation (`Röck+Cöde VoF`) and Xcode `DEVELOPMENT_TEAM` — shared across all sites.
- The package architecture, middleware, security headers, and persistence layer. - The package architecture, middlewares, security headers, and persistence layer
- The generic `website` image name and `Makefile` command names (`site-run`, `site-mount`, …). (PostgreSQL, behind the in-memory SQLite default).
- The four `Packages/*/README.md` files — no rewrite line covers them, so whatever - The analytics origin: `String.Analytics.origin` and the `Content-Security-Policy`
site name they mention ships as-is into every generated repository. in `.env.local` that allows it both name the platform's shared Umami instance.
Tracking stays off until a site sets `ANALYTICS_WEBSITE_ID`.
- The generic `website` image name and the `Makefile` target names (`site-run`,
`site-mount`, …).
- The four `Packages/*/README.md` files and the package sources — no rewrite line
covers them, so whatever site name they carry ships as-is. `Packages/Persistence`'s
PostgreSQL integration test defaults its `POSTGRES_TEST_*` credentials to `site`.
## Maintaining the template itself ## Maintaining the template itself
Edit and test the reference site directly — it builds and its tests pass with Edit and test the reference site directly — it builds and its tests pass with the
the `Site` / `site` placeholders in place: `Site` / `site` placeholders in place:
```sh ```sh
(cd Services/Website && make pkg-test) # the service's own two test targets (cd Services/Website && make pkg-test) # the service's own two test targets
(cd Packages/Infrastructure && swift test) # and likewise for each vendored package (cd Packages/Infrastructure && swift test) # and likewise for each vendored package
``` ```
Keep the placeholders intact so the bootstrap script's targeted replacements No external infrastructure is needed: `Packages/Persistence`'s PostgreSQL
keep matching. They are: integration test skips itself unless `POSTGRES_TEST_HOST` is set. To run it,
start the database (`cd Services/Website && make db-mount`), then
`POSTGRES_TEST_HOST=127.0.0.1 swift test`.
Keep the placeholders intact so the bootstrap rewrites keep matching:
| Placeholder | Appears in | | Placeholder | Appears in |
| --- | --- | | --- | --- |
| `Site` | Xcode project name and `project.pbxproj`, the service README title | | `Site` | Xcode project name and `project.pbxproj`, the service README title |
| `SiteWebsite` | `String+Constants.swift`, `.env.local`, `docker-compose.yml`, the service README | | `SiteWebsite` | `String+Constants.swift`, `.env.local`, `docker-compose.yml`, the service README |
| `site` | `String+Constants.swift` defaults, `HOST_OWNER` / `DATABASE_*` in `.env.local` and Compose, the `site-platform` Compose project, the `Makefile` `db-shell` fallbacks | | `site` | `String+Constants.swift` defaults, `HOST_OWNER` / `DATABASE_*` in `.env.local` and both Compose files, the `site-platform` Compose project, the `Makefile` `db-shell` fallbacks |
| `https://site.example.com` | `Resources/Static/robots.txt`, `Resources/Static/sitemap.xml` | | `https://site.example.com` | `Resources/Static/robots.txt`, `Resources/Static/sitemap.xml` |
If you introduce a new site-specific value, give it a placeholder from this set A new site-specific value needs a placeholder from this set and a matching
and add a matching rewrite line in `Scripts/bootstrap` — a value that no rewrite rewrite line in `Scripts/bootstrap`; anything else ships verbatim into every
covers ships verbatim into every generated site. generated site.