| name | rsv-new-project |
|---|---|
| description | Bootstrap a new FixMyBerlin RSV landing-page repo (rsv-<slug>) from an existing RSV site via shared/scripts/new-project.ts, then complete the manual follow-ups: git init and rsv-shared submodule, GitHub repo, Keystatic GitHub App, brand colors and map style, favicons and OG image, Netlify site, production DNS, Trassenscout API, content, Matomo. Use when spinning up a new RSV landing page, copying an existing site as a template (as rsv-veloroute was), or when the user mentions new-project.ts or the RSV bootstrap checklist. |
| disable-model-invocation | true |
Copy an existing rsv-* website into a sibling rsv-<slug>, rewrite the
project-specific values, then finish git, CMS, and hosting by hand.
rsv-veloroute was spun up this way.
Not for greenfield FMC/TanStack repos — that is new-project-setup.
After the script runs, the new folder has copied code and a configured
BASE_CONFIG, but no .git, no shared/ submodule, no GitHub
repo, and no installed dependencies. Everything under
Manual follow-ups is still on you.
- Run from inside an existing RSV website repo (e.g.
rsv-rs21), not from the parent folder and not fromrsv-shared. - Preflight refuses to run unless: the parent folder is named
rsv-landingpages(the required local layout), the current repo haspackage.json,config/config.ts,astro.config.mjs, andshared/, and the target../rsv-<slug>does not exist yet. - Collect from the user: slug, CMS name, display name, production URL, and whether to clear content.
bun ./shared/scripts/new-project.ts \
--slug <slug> \
--cms-name <CMS_NAME> \
--display-name "<Display Name>" \
--url https://example.de \
[--trassenscout-slug <slug>] \
[--clear-content]| Flag | Example | Effect |
|---|---|---|
--slug |
rs8 |
Folder ../rsv-<slug>, repo name, Keystatic app slug |
--cms-name |
RS8 |
BASE_CONFIG.CMS_NAME |
--display-name |
Radschnellweg 8 |
README.md heading and META.title |
--url |
https://rs8.example.de |
BASE_CONFIG.PRODUCTION_URL |
--trassenscout-slug |
defaults to --slug |
TRASSENSCOUT_PROJECT_API_URL |
--clear-content |
flag | Empties collection folders under src/content/, keeps singletons |
Slug must be lowercase alphanumeric with dashes; URL must be http(s).
Run with --help for the same list.
What the script changes in the new folder:
- Copies the whole tree except
.git,.gitmodules,node_modules,.astro,dist,.netlify,shared,.DS_Store. package.json→name: rsv-<slug>.config/config.ts→ regenerated from scratch:META.title, a TODOMETA.description,USE_MATOMO: false, andBASE_CONFIG.README.md→ regenerated with the display name, production URL, and CMS URL..env.local→ Keystatic secrets blanked,PUBLIC_KEYSTATIC_GITHUB_APP_SLUGset torsv-lp-<slug>-keystatic(also rewrites a legacy.envif present)..env.example.local/.env.example.netlify/.env.example.ionos→ same app slug rewrite.
It runs neither git nor npm.
Astro/Vite loads both .env and .env.local in all modes. For RSV sites use:
| File | Tracked? | Purpose |
|---|---|---|
.env.local |
no (gitignored) | Local secrets for npm run dev |
.env |
no (gitignored) | Legacy/alternate local file — also ignored |
.env.example.local |
yes | Template → copy to .env.local |
.env.example.netlify |
yes | Reference for Netlify env vars |
.env.example.ionos |
yes | Reference for IONOS env vars |
Do not create working copies named .env.netlify or .env.ionos. Copy only
.env.example.local → .env.local. .gitignore ignores .env, .env.local, and
.env.*, with an exception for .env.example.*.
If the bootstrap left no .env.local, run:
cp .env.example.local .env.localThen fill Keystatic credentials (step 1).
Work through this list inside the new project folder (e.g.
rsv-landingpages/rsv-<slug>/) before shipping. Replace <slug> with the value
passed to --slug (e.g. rs8) and <display-name> with the human-readable
name (e.g. Radschnellweg 8).
Track progress with this checklist:
- [ ] 0. Git repo + shared/ submodule
- [ ] 1. Keystatic GitHub App
- [ ] 2. Brand colors and map styling
- [ ] 3. Favicons, OG image, CMS logo
- [ ] 4. Netlify site
- [ ] 5. Production URL & DNS
- [ ] 6. Trassenscout integration (optional)
- [ ] 7. Content
- [ ] 8. Matomo tracking (optional)
- [ ] 9. Final verification
The script creates no repository. Do this first, then continue with the Keystatic app.
-
git init -
git branch -M main(skip if the default branch is alreadymain) -
git submodule add https://github.com/FixMyBerlin/rsv-shared.git shared - Create
FixMyBerlin/rsv-<slug>on GitHub, add it asorigin, runnpm install, thengit add -A, commit, and push
The script cannot create the GitHub App — there is no public API for it.
- Follow Keystatic GitHub mode setup
and create a new GitHub App in the
FixMyBerlinorg namedrsv-lp-<slug>-keystatic. The app slug must matchPUBLIC_KEYSTATIC_GITHUB_APP_SLUGin.env.local/.env.example.*. - Grant the App access to the new
FixMyBerlin/rsv-<slug>repo only. - Copy the App's
Client ID,Client secret, and a freshly generatedKEYSTATIC_SECRETinto all three places:- the new repo's local
.env.local, sonpm run devworks locally - Bitwarden, so the credentials don't get lost
- the Netlify site's environment variables (step 4)
- the new repo's local
The colors are still those of the template project.
- Update brand colors in
config/styles.ts—COLORS,COLORSCLASSES, andLINKCLASSES - Update map styling in
config/map.ts— especiallyMAPTILER_STYLE - Re-run
npm run predevto make surevalidateConfig.tsis still happy after your edits
- Replace the favicons under
public/icons/. Keep the file names soBASE_CONFIG.CMS_LOGO_PATHkeeps working — update it if you rename anything. - Replace the OG / social-sharing image under
public/social-sharing/
- Create a Netlify site for the repo (Add new site → Import from Git →
FixMyBerlin/rsv-<slug>) - Set the site name to
cms-rsv-<slug>so the CMS lives athttps://cms-rsv-<slug>.netlify.app/keystatic— this URL is hard-coded in the new repo'sREADME.md - Add all variables from
.env.example.netlifyunder Site settings → Environment variables, using the Keystatic credentials from step 1 - Trigger a first deploy and confirm
/keystaticloads
- Point the production domain at the Netlify site
- Confirm
BASE_CONFIG.PRODUCTION_URLinconfig/config.tsmatches the live URL (the script set it from--url) - Replace the TODO
META.descriptioninconfig/config.tswith a real SEO description - Run
npm run predev—shared/scripts/validateConfig.tsfails loudly if any requiredBASE_CONFIGfield is still missing or malformed
- Once a Trassenscout project exists for this corridor, set
BASE_CONFIG.TRASSENSCOUT_PROJECT_API_URLinconfig/config.tsto the real URL. The script pre-fillshttps://trassenscout.de/api/projects/<trassenscout-slug>.
If you ran the script with --clear-content, every collection folder under
src/content/ is empty; singletons like homepageHero/index.yaml are kept so
the schema stays valid.
- Create the actual content via the Keystatic CMS, or copy MDX/YAML files from the template project and adapt them
- Replace the hero image at
src/content/homepageHero/image.jpg
- Enable
USE_MATOMOinconfig/config.tsand add the correctsiteIdinastro.config.mjsonce Matomo is set up for this site
Once all boxes above are ticked, do a fresh clone of the repo, run
npm install && npm run predev && npm run dev, and verify the homepage loads
without console errors.
Implementation: scripts/new-project.ts in
this repo, reachable as shared/scripts/new-project.ts from each site.