✨ feat: update deployment scripts and configuration; enhance CI/CD process with new scripts for staging and production
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
name: deploy to production
|
||||
|
||||
on: "push"
|
||||
# push:
|
||||
# branches:
|
||||
# - master
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
@@ -14,7 +11,7 @@ jobs:
|
||||
volumes:
|
||||
- /data:/data
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
@@ -23,11 +20,10 @@ jobs:
|
||||
- run: |
|
||||
git fetch --force --tags
|
||||
|
||||
# setup node 20
|
||||
- name: setup node 20
|
||||
uses: actions/setup-node@v3
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
node-version: 22
|
||||
|
||||
- name: install dependencies
|
||||
run: |
|
||||
@@ -35,40 +31,7 @@ jobs:
|
||||
yarn install
|
||||
|
||||
- name: modify config
|
||||
run: |
|
||||
sed -i 's#\(sentryEnvironment.*\)".*"#\1"${GITHUB_REF_NAME}"#g' frontend/src/config.ts
|
||||
sed -i 's#//\( sentry\\.init.*\)#\1#g' frontend/src/config.ts
|
||||
sed -i 's#metrictCall = false#metrictCall = true#g' frontend/src/config.ts
|
||||
|
||||
set -o allexport
|
||||
. ./.env
|
||||
echo "PROJECT_RELEASE=${SENTRY_PROJECT}.r`git rev-list HEAD --count`-`git describe --all --long | sed 's+/+-+'`" >> .env
|
||||
. ./.env
|
||||
set +o allexport
|
||||
echo ______ .env ______
|
||||
cat .env
|
||||
echo
|
||||
sed -i 's#\(const release = \).*#\1"'${PROJECT_RELEASE}'"#g' api/hooks/config-client.js
|
||||
sed -i 's#\(const originURL = \).*#\1"'${LIVE_URL}'"#g' api/hooks/config-client.js
|
||||
|
||||
# bash scripts/preload-meta.sh frontend/spa.html
|
||||
# bash scripts/preload-meta.sh frontend/spa.html > frontend/_spa.html
|
||||
# cp frontend/_spa.html frontend/spa.html
|
||||
|
||||
export stamp=`date +%s`
|
||||
sed -i s/__TIMESTAMP__/$stamp/g frontend/spa.html
|
||||
# sed -i s/__TIMESTAMP__/$stamp/g frontend/serviceworker.js
|
||||
# cat frontend/serviceworker.js
|
||||
|
||||
test -d api/templates && test -L api/templates/spa.html && rm api/templates/spa.html || test -d api/templates && test -f api/templates/spa.html && rm api/templates/spa.html
|
||||
test -d api/templates && cp frontend/spa.html api/templates/spa.html
|
||||
cp frontend/spa.html api/templates/spa.html
|
||||
echo ______ frontend/spa.html ______
|
||||
cat frontend/spa.html
|
||||
|
||||
sed -i 's#\(PREVIEW_URL=\).*#\1'${LIVE_URL}/preview'#g' api/config.yml.env
|
||||
echo ______ api/config.yml.env ______
|
||||
cat api/config.yml.env
|
||||
run: ./scripts/ci-modify-config.sh
|
||||
|
||||
- name: build
|
||||
env:
|
||||
@@ -88,114 +51,22 @@ jobs:
|
||||
run: |
|
||||
yarn build:server
|
||||
|
||||
- name: build legacy
|
||||
- name: upload sourcemaps to sentry
|
||||
env:
|
||||
FORCE_COLOR: "true"
|
||||
run: |
|
||||
yarn build:legacy
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
run: ./scripts/ci-upload-sourcemaps.sh
|
||||
|
||||
- name: staging
|
||||
# only if branch is dev
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
env:
|
||||
# /data/ORG/PROJECT/BRANCH
|
||||
API_BASEDIR: /data/${{ github.repository }}/${{ github.ref_name }}
|
||||
COMPOSE_PROJECT_NAME: ${{ github.repository }}-${{ github.ref_name }}
|
||||
run: |
|
||||
# read .env
|
||||
set -o allexport
|
||||
. ./.env
|
||||
. ./api/config.yml.env
|
||||
set +o allexport
|
||||
|
||||
# replace / with -
|
||||
COMPOSE_PROJECT_NAME=`echo $COMPOSE_PROJECT_NAME | sed 's+/+-+g'`
|
||||
|
||||
mkdir -p $API_BASEDIR/frontend
|
||||
rsync -av api $API_BASEDIR/
|
||||
rsync -av frontend/dist $API_BASEDIR/frontend/
|
||||
rsync -av frontend/assets $API_BASEDIR/frontend/
|
||||
sed -i 's#\(PREVIEW_URL=\).*#\1'${STAGING_URL}/preview'#g' $API_BASEDIR/api/config.yml.env
|
||||
|
||||
docker compose -f docker-compose-staging.yml -p $COMPOSE_PROJECT_NAME up -d --build --remove-orphans
|
||||
|
||||
reloadUrl=${STAGING_URL}/api/_/admin/reload
|
||||
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" -d '{}' "${reloadUrl}"
|
||||
|
||||
# clear ssr cache
|
||||
ssrUrl=${STAGING_URL}/api/ssr
|
||||
curl -X POST -H "Content-Type: application/json" -d '{}' "${ssrUrl}?clear=1"
|
||||
run: ./scripts/ci-staging.sh
|
||||
|
||||
- name: deploy
|
||||
# only if branch is master
|
||||
if: github.ref == 'refs/heads/master'
|
||||
env:
|
||||
RSYNC_USER: ${{ github.repository }}
|
||||
RSYNC_PASS: ${{ github.token }}
|
||||
BRANCH: ${{ github.ref_name }}
|
||||
run: |
|
||||
# read .env
|
||||
set -o allexport
|
||||
. ./.env
|
||||
. ./api/config.yml.env
|
||||
set +o allexport
|
||||
|
||||
|
||||
# if RSYNC_USER or RSYNC_KEY is not set, exit
|
||||
if [ -z "${RSYNC_USER}" ]; then
|
||||
echo "RSYNC_USER missing, exiting"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${RSYNC_PASS}" ]; then
|
||||
echo "RSYNC_PASS missing, exiting"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${RSYNC_HOST}" ]; then
|
||||
echo "RSYNC_HOST missing, exiting"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${RSYNC_PORT}" ]; then
|
||||
echo "RSYNC_PORT missing, exiting"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Deploying ${BRANCH} to ${RSYNC_HOST} on port ${RSYNC_PORT} as ${RSYNC_USER}"
|
||||
|
||||
excludes=""
|
||||
if [ "${BRANCH}" = "master" ]; then
|
||||
excludes='--exclude=src --exclude=*.map'
|
||||
echo "master deploy, excluding $excludes"
|
||||
fi
|
||||
|
||||
SSH_CMD="sshpass -p ${RSYNC_PASS} ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p ${RSYNC_PORT} -l ${RSYNC_USER}"
|
||||
|
||||
# sync frontend
|
||||
rsync -rlcgD --perms -i -u -v --stats --progress \
|
||||
--delete \
|
||||
-e "${SSH_CMD}" \
|
||||
$excludes \
|
||||
frontend/ \
|
||||
${RSYNC_HOST}:./frontend/ \
|
||||
|
||||
# sync api config
|
||||
rsync -rlcgD --perms -i -u -v --stats --progress \
|
||||
--delete \
|
||||
-e "${SSH_CMD}" \
|
||||
api/ \
|
||||
${RSYNC_HOST}:./api/
|
||||
|
||||
# create media directory
|
||||
mkdir media
|
||||
chmod 770 media
|
||||
rsync -rlcgD --perms -i -u -v --stats --progress \
|
||||
-e "${SSH_CMD}" \
|
||||
media \
|
||||
${RSYNC_HOST}:./
|
||||
|
||||
|
||||
reloadUrl=${LIVE_URL}/api/_/admin/reload
|
||||
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" -d '{}' "${reloadUrl}"
|
||||
|
||||
# clear ssr cache
|
||||
ssrUrl=${LIVE_URL}/api/ssr
|
||||
curl -X POST -H "Content-Type: application/json" -d '{}' "${ssrUrl}?clear=1"
|
||||
run: ./scripts/ci-deploy.sh
|
||||
|
||||
32
AGENTS.md
32
AGENTS.md
@@ -12,6 +12,8 @@ Tibi CMS starter template — Svelte 5 SPA with esbuild, SSR via goja, and Playw
|
||||
|
||||
## Setup commands
|
||||
|
||||
> **New project from this template?** Follow the step-by-step guide in [README.md](README.md).
|
||||
|
||||
- Install deps: `yarn install`
|
||||
- Start dev: `make docker-up && make docker-start`
|
||||
- Start with mock data: set `MOCK=1` in `.env`, then `make docker-up && make docker-start`
|
||||
@@ -59,6 +61,36 @@ Tibi CMS starter template — Svelte 5 SPA with esbuild, SSR via goja, and Playw
|
||||
- API access to collections uses the reverse proxy: `CODING_URL/api/<collection>` (e.g. `CODING_URL/api/content`).
|
||||
- Auth via `Token` header with ADMIN_TOKEN from `api/config.yml.env`.
|
||||
|
||||
## Reference repositories
|
||||
|
||||
These sibling repos in the workspace provide documentation, types, and reference implementations:
|
||||
|
||||
| Repository | Path | Purpose |
|
||||
| --- | --- | --- |
|
||||
| **tibi-types** | `../../cms/tibi-types` | TypeScript type definitions for hooks, collections, permissions, etc. Included via `tsconfig.json` — **read-only, do not modify**. Key file: `index.d.ts`. JSON schemas in `schemas/api-config/`. |
|
||||
| **tibi-server** | `../../cms/tibi-server` | Go source code of the server. `docs/` has detailed documentation (16 files), `examples/` has sample projects. |
|
||||
| **tibi-admin-nova** | `../../cms/tibi-admin-nova` | Admin UI reference project. Key file: `types/admin.d.ts` (1147 lines — all admin types: `AdminCollection`, `AdminCollectionField`, `AdminCollectionMeta`, `AdminDashboard`, etc.). Use as reference for collection field configs, dashboard setup, and fieldLists. |
|
||||
|
||||
### When to consult which repo
|
||||
|
||||
- **Write collection YAML** → `tibi-server/docs/04-collections.md` + `tibi-types/schemas/api-config/collection.json` (JSON schema)
|
||||
- **Write/debug hooks** → `tibi-server/docs/06-hooks.md` + `tibi-types/index.d.ts` (context types)
|
||||
- **Configure admin UI** → `tibi-admin-nova/types/admin.d.ts` (field types, meta, dashboard) + `tibi-admin-nova/api/collections/` (real examples)
|
||||
- **Permissions** → `tibi-server/docs/05-authentication.md`
|
||||
- **Realtime/SSE** → `tibi-server/docs/07-realtime.md`
|
||||
- **Images/uploads** → `tibi-server/docs/08-file-upload-images.md`
|
||||
- **LLM integration** → `tibi-server/docs/09-llm-integration.md`
|
||||
|
||||
### TypeScript types
|
||||
|
||||
`tibi-types` is included via `tsconfig.json`:
|
||||
|
||||
```json
|
||||
"include": ["frontend/src/**/*", "types/**/*", "./../../cms/tibi-types", "api/**/*"]
|
||||
```
|
||||
|
||||
Project-specific types (e.g. `Ssr`, `ApiOptions`, `ContentEntry`) live in `types/global.d.ts`.
|
||||
|
||||
## Code style
|
||||
|
||||
- Follow the existing code style and conventions used in the project.
|
||||
|
||||
209
README.md
209
README.md
@@ -2,87 +2,170 @@
|
||||
|
||||
Starter Kit für SPAs(s) `;)` mit Svelte und TibiCMS inkl. SSR
|
||||
|
||||
## Neues Projekt starten
|
||||
## Neues Projekt erstellen — Schritt-für-Schritt
|
||||
|
||||
Nachdem du dieses Repository als Vorlage geklont hast, passe die Platzhalter an dein Projekt an:
|
||||
Diese Anleitung ist so geschrieben, dass ein autonomer Agent oder ein Entwickler sie 1:1 abarbeiten kann, um ein eigenständiges tibi-Projekt mit eigener Code-Server-Subdomain zu erstellen.
|
||||
|
||||
1. **`.env`**: Ersetze `__PROJECT_NAME__` mit deinem Projektnamen (z.B. `mein-projekt`).
|
||||
Die folgenden URLs werden automatisch abgeleitet:
|
||||
- `CODING_URL=https://mein-projekt.code.testversion.online`
|
||||
- `STAGING_URL=https://dev-mein-projekt.staging.testversion.online`
|
||||
2. **`frontend/spa.html`** / **`api/templates/spa.html`**: Ersetze `__PROJECT_TITLE__` mit dem Seitentitel.
|
||||
3. **`api/config.yml.env`**: Passe `ADMIN_TOKEN`, Datenbank-Name und weitere Secrets an.
|
||||
4. **`docker-compose-local.yml`**: Suche nach `project_name__` und ersetze mit deinem Projektnamen (Container-Benennung).
|
||||
5. **Demo-Inhalte entfernen**: Die Demo-Seite besteht aus diesen Dateien, die für ein echtes Projekt entfernt/ersetzt werden können:
|
||||
- `frontend/src/blocks/` — Block-Komponenten (HeroBlock, FeaturesBlock, RichtextBlock, AccordionBlock, ContactFormBlock, BlockRenderer, NotFound)
|
||||
- `frontend/mocking/content.json` — Demo-Mockdaten für Content
|
||||
- `frontend/mocking/navigation.json` — Demo-Mockdaten für Navigation
|
||||
- `api/collections/content.yml` — Content-Collection-Konfiguration
|
||||
- `api/collections/navigation.yml` — Navigation-Collection-Konfiguration
|
||||
- `tests/e2e/demo.spec.ts` — Demo-E2E-Tests
|
||||
- `video-tours/tours/demo-showcase.tour.ts` — Demo-Video-Tour
|
||||
6. **`frontend/src/App.svelte`**: Passe Header, Footer und Content-Loading an dein Datenmodell an.
|
||||
### Voraussetzungen
|
||||
|
||||
- Zugang zum Code-Server unter `*.code.testversion.online`
|
||||
- `git`, `yarn`, `make`, `docker compose` verfügbar
|
||||
- Traefik-Reverse-Proxy läuft auf dem Host (verwaltet `*.code.testversion.online`-Subdomains automatisch via Docker-Labels)
|
||||
|
||||
### 1. Repository klonen (falls noch nicht geschehen)
|
||||
|
||||
Wenn du bereits in einem geklonten Projekt arbeitest, überspringe diesen Schritt und gehe direkt zu **Schritt 2**.
|
||||
|
||||
```sh
|
||||
# Platzhalter ersetzen (Beispiel für Linux/Mac):
|
||||
sed -i 's/__PROJECT_NAME__/mein-projekt/g' .env
|
||||
sed -i 's/__PROJECT_TITLE__/Mein Projekt/g' frontend/spa.html api/templates/spa.html
|
||||
# Im Workspace-Verzeichnis (z.B. /WM_Dev/src/gitbase.de/cms/)
|
||||
git clone https://gitbase.de/cms/tibi-svelte-starter.git mein-projekt
|
||||
cd mein-projekt
|
||||
git remote rename origin template
|
||||
# Neues Remote-Repo anlegen (z.B. auf gitbase.de) und als origin setzen:
|
||||
# git remote add origin https://gitbase.de/org/mein-projekt.git
|
||||
```
|
||||
|
||||
## Wozu?
|
||||
### 2. Platzhalter ersetzen
|
||||
|
||||
Via Svelte wird eine SPA (Single-Page-App) programmiert. Dazu wird der Code einmal für den Browser aufgebreitet und außerdem für den Server kompiliert und transpiliert. Der Server-Code wird in einem tibi-server SSR-Hook (server side rendering) eingebunden und generiert dort fertiges HTML anhand der aktuelle Route für SEO und optimierte Ladezeiten.
|
||||
Alle Platzhalter müssen durch die echten Projektwerte ersetzt werden. Hier die vollständige Liste:
|
||||
|
||||
Die Navigation innerhalb der APP im Browser löst dagegen nur API-Aufrufe aus ohne jedesmal einen SSR-Prozess anzustoßen.
|
||||
|
||||
Um die SSR-Last so gering wie möglich zu halten, wurde ein Caching in der "ssr"-Collection der API implementiert.
|
||||
|
||||
## Toolchain
|
||||
|
||||
### git
|
||||
|
||||
nach `git clone ...`
|
||||
| Platzhalter | Wo | Ersetzen durch | Beispiel |
|
||||
| --- | --- | --- | --- |
|
||||
| `__PROJECT_NAME__` | `.env` | Projektname (kebab-case, wird für URLs und Container verwendet) | `mein-projekt` |
|
||||
| `__TIBI_NAMESPACE__` | `.env` | Tibi-Namespace (snake_case, wird als DB-Prefix und in API-URLs verwendet) | `mein_projekt` |
|
||||
| `__NAMESPACE__` | `api/config.yml`, `frontend/.htaccess` | Gleicher Wert wie `TIBI_NAMESPACE` | `mein_projekt` |
|
||||
|
||||
```sh
|
||||
git lfs install
|
||||
git lfs pull
|
||||
# Projektname (kebab-case) — für URLs, Docker-Container, Subdomains
|
||||
PROJECT=mein-projekt
|
||||
# Tibi-Namespace (snake_case) — für DB, API-Pfade
|
||||
NAMESPACE=mein_projekt
|
||||
|
||||
sed -i "s/__PROJECT_NAME__/$PROJECT/g" .env
|
||||
sed -i "s/__TIBI_NAMESPACE__/$NAMESPACE/g" .env
|
||||
sed -i "s/__NAMESPACE__/$NAMESPACE/g" api/config.yml frontend/.htaccess
|
||||
```
|
||||
|
||||
### Abhängigkeiten laden
|
||||
**Ergebnis in `.env`:**
|
||||
|
||||
```dotenv
|
||||
PROJECT_NAME=mein-projekt
|
||||
TIBI_NAMESPACE=mein_projekt
|
||||
CODING_URL=https://mein-projekt.code.testversion.online
|
||||
STAGING_URL=https://dev-mein-projekt.staging.testversion.online
|
||||
```
|
||||
|
||||
### 3. Seitentitel anpassen
|
||||
|
||||
Der Seitentitel wird dynamisch über `<svelte:head>` in `frontend/src/App.svelte` gesetzt. Die Demo-App verwendet dafür die Konstante `SITE_NAME`. In einem neuen Projekt wird `App.svelte` typischerweise komplett neu geschrieben — dabei einfach sicherstellen, dass `<svelte:head>` mit einem passenden `<title>` vorhanden ist. SSR injiziert ihn dann automatisch über den `<!--HEAD-->`-Platzhalter in `spa.html`.
|
||||
|
||||
### 4. Admin-Token prüfen
|
||||
|
||||
`api/config.yml.env` enthält bereits ein `ADMIN_TOKEN`. Für Produktionsprojekte durch ein sicheres Token ersetzen:
|
||||
|
||||
```sh
|
||||
echo "ADMIN_TOKEN=$(openssl rand -hex 20)" > api/config.yml.env
|
||||
```
|
||||
|
||||
### 5. Subdomains prüfen
|
||||
|
||||
Traefik erkennt die Docker-Labels automatisch. Nach `make docker-up` sind folgende URLs verfügbar:
|
||||
|
||||
| Dienst | URL |
|
||||
| --- | --- |
|
||||
| Website (BrowserSync) | `https://{PROJECT_NAME}.code.testversion.online/` |
|
||||
| Tibi Admin | `https://{PROJECT_NAME}-tibiadmin.code.testversion.online/` |
|
||||
| Tibi Server API | `https://{PROJECT_NAME}-tibiserver.code.testversion.online/` |
|
||||
| Maildev | `https://{PROJECT_NAME}-maildev.code.testversion.online/` |
|
||||
|
||||
**Es ist KEINE manuelle Traefik-Konfiguration nötig.** Die Subdomains werden automatisch über das Docker-Label `online.testversion.code.subdomain=${PROJECT_NAME}` registriert. Traefik überwacht Docker-Events und erstellt die Routen dynamisch.
|
||||
|
||||
### 6. Starten
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
```
|
||||
|
||||
### Entwickeln auf dem Code-Server mit Docker Compose Stack
|
||||
|
||||
```sh
|
||||
make docker-start
|
||||
make docker-up # Stack im Hintergrund starten
|
||||
# oder
|
||||
make docker-up
|
||||
make docker-down
|
||||
|
||||
# "make help" zeigt alle Kommandos
|
||||
make docker-start # Stack im Vordergrund (CTRL-C zum Stoppen)
|
||||
```
|
||||
|
||||
| UI | URL |
|
||||
| --- | --- |
|
||||
| Website | <https://tibi-svelte-starter.code.testversion.online/> |
|
||||
| Tibi Admin | <https://tibi-svelte-starter-tibiadmin.code.testversion.online/> |
|
||||
| Maildev | <https://tibi-svelte-starter-maildev.code.testversion.online/> |
|
||||
|
||||
### Bauen
|
||||
Prüfe, ob alles läuft:
|
||||
|
||||
```sh
|
||||
# moderne Browser
|
||||
yarn build
|
||||
|
||||
# alte Browser (IE11)
|
||||
yarn build:legacy
|
||||
|
||||
# serverseitiges Rendering
|
||||
yarn build:server
|
||||
|
||||
# Admin-Module
|
||||
yarn build:admin
|
||||
make docker-ps # Container-Status anzeigen
|
||||
make docker-logs # Logs verfolgen
|
||||
```
|
||||
|
||||
### 7. Mock-Modus (Frontend ohne tibi-server)
|
||||
|
||||
Für schnelle UI-Entwicklung ohne Backend:
|
||||
|
||||
```sh
|
||||
# In .env setzen:
|
||||
MOCK=1
|
||||
# Dann:
|
||||
make docker-up
|
||||
```
|
||||
|
||||
Mock-Daten liegen in `frontend/mocking/` als JSON-Dateien. Fehlende Endpunkte liefern 404.
|
||||
|
||||
### 8. Demo-Inhalte entfernen
|
||||
|
||||
Für ein echtes Projekt die Demo-Dateien entfernen/ersetzen:
|
||||
|
||||
| Datei/Ordner | Inhalt |
|
||||
| --- | --- |
|
||||
| `frontend/src/blocks/` | Demo-Block-Komponenten (HeroBlock, FeaturesBlock, etc.) |
|
||||
| `frontend/mocking/content.json` | Demo-Mockdaten für Content |
|
||||
| `frontend/mocking/navigation.json` | Demo-Mockdaten für Navigation |
|
||||
| `api/collections/content.yml` | Content-Collection-Konfiguration |
|
||||
| `api/collections/navigation.yml` | Navigation-Collection-Konfiguration |
|
||||
| `tests/e2e/` | Demo-E2E-Tests |
|
||||
| `video-tours/tours/` | Demo-Video-Tour |
|
||||
|
||||
Danach `frontend/src/App.svelte` (Header, Footer, Content-Loading) an das eigene Datenmodell anpassen.
|
||||
|
||||
### 9. Build und Deployment
|
||||
|
||||
```sh
|
||||
yarn build # Frontend für moderne Browser
|
||||
yarn build:server # SSR-Bundle (für tibi-server goja-Hooks)
|
||||
yarn build:admin # Admin-Module (optional)
|
||||
yarn validate # TypeScript + Svelte prüfen (muss 0 Fehler/Warnungen zeigen)
|
||||
```
|
||||
|
||||
### Checkliste für autonome Agents
|
||||
|
||||
```text
|
||||
[ ] Repository geklont und Remote gesetzt
|
||||
[ ] __PROJECT_NAME__ in .env ersetzt
|
||||
[ ] __TIBI_NAMESPACE__ in .env ersetzt
|
||||
[ ] __NAMESPACE__ in api/config.yml und frontend/.htaccess ersetzt
|
||||
[ ] App.svelte mit eigenem Template und <svelte:head> (inkl. <title>) erstellt
|
||||
[ ] ADMIN_TOKEN in api/config.yml.env gesetzt
|
||||
[ ] yarn install ausgeführt
|
||||
[ ] make docker-up gestartet
|
||||
[ ] Website unter https://{PROJECT_NAME}.code.testversion.online/ erreichbar
|
||||
[ ] yarn validate zeigt 0 Fehler und 0 Warnungen
|
||||
[ ] yarn build und yarn build:server erfolgreich
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architektur
|
||||
|
||||
Via Svelte wird eine SPA (Single-Page-App) programmiert. Der Code wird einmal für den Browser aufbereitet und außerdem für den Server kompiliert und transpiliert. Der Server-Code wird in einem tibi-server SSR-Hook eingebunden und generiert fertiges HTML anhand der aktuellen Route — für SEO und optimierte Ladezeiten.
|
||||
|
||||
Die Navigation innerhalb der App löst nur API-Aufrufe aus, ohne jedes Mal SSR anzustoßen. Ein Cache in der `ssr`-Collection minimiert die SSR-Last.
|
||||
|
||||
**SSR-Details:**
|
||||
|
||||
- `<title>` und `<meta description>` kommen über `<svelte:head>` aus `App.svelte` → SSR injiziert sie in den `<!--HEAD-->`-Platzhalter von `spa.html`
|
||||
- `<html lang>` wird vom SSR-Hook (`api/hooks/ssr/get_read.js`) anhand der URL-Sprache gesetzt
|
||||
- SSR-Bundle wird mit `yarn build:server` erstellt und landet in `api/hooks/lib/app.server.js`
|
||||
|
||||
**Weiteres Build-Target:**
|
||||
|
||||
```sh
|
||||
yarn build:admin # Admin-Module
|
||||
```
|
||||
|
||||
@@ -131,23 +131,6 @@ services:
|
||||
- ./tmp/mongo-data:/data/db
|
||||
user: ${CODER_UID}:${CODER_GID}
|
||||
|
||||
adminmongo:
|
||||
profiles:
|
||||
- tibi
|
||||
- tibi-dev
|
||||
image: gitbase.de/server/adminmongo
|
||||
environment:
|
||||
CONN_NAME: mongo
|
||||
DB_HOST: mongo
|
||||
PORT: 1234
|
||||
expose:
|
||||
- 1234
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- online.testversion.code.subdomain=${PROJECT_NAME}-adminmongo
|
||||
- traefik.http.routers.${PROJECT_NAME}-adminmongo.middlewares=${PROJECT_NAME}-adminmongo
|
||||
- traefik.http.middlewares.${PROJECT_NAME}-adminmongo.basicauth.usersfile=${PWD}/.basic-auth-code
|
||||
|
||||
maildev:
|
||||
profiles:
|
||||
- tibi
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
<body>
|
||||
<div id="appContainer"><!--HTML--></div>
|
||||
<script type="module" src="/dist/index.mjs?t=__TIMESTAMP__"></script>
|
||||
<script nomodule src="/dist/index.es5.js?t=__TIMESTAMP__"></script>
|
||||
<!-- <script
|
||||
src="//cc.webmakers.de/cc.js"
|
||||
defer
|
||||
|
||||
62
scripts/ci-deploy.sh
Executable file
62
scripts/ci-deploy.sh
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# CI step: deploy to production via rsync
|
||||
# Called from .gitea/workflows/deploy.yml
|
||||
# Required env: RSYNC_USER, RSYNC_PASS, BRANCH
|
||||
|
||||
# Read .env
|
||||
set -o allexport
|
||||
. ./.env
|
||||
. ./api/config.yml.env
|
||||
set +o allexport
|
||||
|
||||
# Validate required env vars
|
||||
for var in RSYNC_USER RSYNC_PASS RSYNC_HOST RSYNC_PORT; do
|
||||
if [ -z "${!var:-}" ]; then
|
||||
echo "${var} missing, exiting"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Deploying ${BRANCH} to ${RSYNC_HOST} on port ${RSYNC_PORT} as ${RSYNC_USER}"
|
||||
|
||||
# Exclude source and sourcemaps on master deploy
|
||||
excludes=""
|
||||
if [ "${BRANCH}" = "master" ]; then
|
||||
excludes='--exclude=src --exclude=*.map'
|
||||
echo "master deploy, excluding ${excludes}"
|
||||
fi
|
||||
|
||||
SSH_CMD="sshpass -p ${RSYNC_PASS} ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p ${RSYNC_PORT} -l ${RSYNC_USER}"
|
||||
|
||||
# Sync frontend
|
||||
rsync -rlcgD --perms -i -u -v --stats --progress \
|
||||
--delete \
|
||||
-e "${SSH_CMD}" \
|
||||
${excludes} \
|
||||
frontend/ \
|
||||
"${RSYNC_HOST}":./frontend/
|
||||
|
||||
# Sync API config and hooks
|
||||
rsync -rlcgD --perms -i -u -v --stats --progress \
|
||||
--delete \
|
||||
-e "${SSH_CMD}" \
|
||||
api/ \
|
||||
"${RSYNC_HOST}":./api/
|
||||
|
||||
# Ensure media directory exists on remote
|
||||
mkdir -p media
|
||||
chmod 770 media
|
||||
rsync -rlcgD --perms -i -u -v --stats --progress \
|
||||
-e "${SSH_CMD}" \
|
||||
media \
|
||||
"${RSYNC_HOST}":./
|
||||
|
||||
# Reload tibi-server config
|
||||
reloadUrl="${LIVE_URL}/api/_/admin/reload"
|
||||
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" -d '{}' "${reloadUrl}"
|
||||
|
||||
# Clear SSR cache
|
||||
ssrUrl="${LIVE_URL}/api/ssr"
|
||||
curl -X POST -H "Content-Type: application/json" -d '{}' "${ssrUrl}?clear=1"
|
||||
45
scripts/ci-modify-config.sh
Executable file
45
scripts/ci-modify-config.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# CI step: modify config for deployment
|
||||
# Called from .gitea/workflows/deploy.yml
|
||||
|
||||
# Enable sentry
|
||||
sed -i 's#\(sentryEnvironment.*\)".*"#\1"'"${GITHUB_REF_NAME}"'"#g' frontend/src/config.ts
|
||||
sed -i 's#//\( sentry\\.init.*\)#\1#g' frontend/src/config.ts
|
||||
sed -i 's#metrictCall = false#metrictCall = true#g' frontend/src/config.ts
|
||||
|
||||
# Build release string from git info
|
||||
set -o allexport
|
||||
. ./.env
|
||||
echo "PROJECT_RELEASE=${SENTRY_PROJECT}.r$(git rev-list HEAD --count)-$(git describe --all --long | sed 's+/+-+')" >> .env
|
||||
. ./.env
|
||||
set +o allexport
|
||||
|
||||
echo "______ .env ______"
|
||||
cat .env
|
||||
echo
|
||||
|
||||
# Inject release and origin URL into hooks
|
||||
sed -i 's#\(const release = \).*#\1"'"${PROJECT_RELEASE}"'"#g' api/hooks/config-client.js
|
||||
sed -i 's#\(const originURL = \).*#\1"'"${LIVE_URL}"'"#g' api/hooks/config-client.js
|
||||
|
||||
# Inject cache-busting timestamp
|
||||
stamp=$(date +%s)
|
||||
sed -i "s/__TIMESTAMP__/${stamp}/g" frontend/spa.html
|
||||
|
||||
# Copy spa.html to api/templates (remove symlink or file first)
|
||||
if [ -d api/templates ]; then
|
||||
[ -L api/templates/spa.html ] && rm api/templates/spa.html
|
||||
[ -f api/templates/spa.html ] && rm api/templates/spa.html
|
||||
cp frontend/spa.html api/templates/spa.html
|
||||
fi
|
||||
|
||||
echo "______ frontend/spa.html ______"
|
||||
cat frontend/spa.html
|
||||
|
||||
# Set preview URL
|
||||
sed -i 's#\(PREVIEW_URL=\).*#\1'"${LIVE_URL}"'/preview#g' api/config.yml.env
|
||||
|
||||
echo "______ api/config.yml.env ______"
|
||||
cat api/config.yml.env
|
||||
37
scripts/ci-staging.sh
Executable file
37
scripts/ci-staging.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# CI step: deploy to staging environment
|
||||
# Called from .gitea/workflows/deploy.yml
|
||||
# Required env: API_BASEDIR, COMPOSE_PROJECT_NAME
|
||||
|
||||
# Read .env
|
||||
set -o allexport
|
||||
. ./.env
|
||||
. ./api/config.yml.env
|
||||
set +o allexport
|
||||
|
||||
# Sanitize compose project name (replace / with -)
|
||||
COMPOSE_PROJECT_NAME=$(echo "${COMPOSE_PROJECT_NAME}" | sed 's+/+-+g')
|
||||
export COMPOSE_PROJECT_NAME
|
||||
|
||||
# Sync files to staging directory
|
||||
mkdir -p "${API_BASEDIR}/frontend"
|
||||
rsync -av api "${API_BASEDIR}/"
|
||||
rsync -av frontend/dist "${API_BASEDIR}/frontend/"
|
||||
rsync -av frontend/assets "${API_BASEDIR}/frontend/"
|
||||
sed -i 's#\(PREVIEW_URL=\).*#\1'"${STAGING_URL}"'/preview#g' "${API_BASEDIR}/api/config.yml.env"
|
||||
|
||||
# Start staging stack
|
||||
docker compose -f docker-compose-staging.yml -p "${COMPOSE_PROJECT_NAME}" up -d --build --remove-orphans
|
||||
|
||||
# Wait for API to be ready
|
||||
sleep 5
|
||||
|
||||
# Reload tibi-server config
|
||||
reloadUrl="${STAGING_URL}/api/_/admin/reload"
|
||||
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer ${ADMIN_TOKEN}" -d '{}' "${reloadUrl}"
|
||||
|
||||
# Clear SSR cache
|
||||
ssrUrl="${STAGING_URL}/api/ssr"
|
||||
curl -X POST -H "Content-Type: application/json" -d '{}' "${ssrUrl}?clear=1"
|
||||
32
scripts/ci-upload-sourcemaps.sh
Executable file
32
scripts/ci-upload-sourcemaps.sh
Executable file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# CI step: upload sourcemaps to Sentry
|
||||
# Called from .gitea/workflows/deploy.yml
|
||||
# Required env: SENTRY_AUTH_TOKEN (passed from workflow secrets)
|
||||
|
||||
# Read .env
|
||||
set -o allexport
|
||||
. ./.env
|
||||
set +o allexport
|
||||
|
||||
# Skip gracefully if no token is configured
|
||||
if [ -z "${SENTRY_AUTH_TOKEN:-}" ]; then
|
||||
echo "SENTRY_AUTH_TOKEN not set, skipping sourcemap upload"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "SENTRY_URL=${SENTRY_URL:-}"
|
||||
echo "SENTRY_ORG=${SENTRY_ORG:-}"
|
||||
echo "SENTRY_PROJECT=${SENTRY_PROJECT:-}"
|
||||
echo "PROJECT_RELEASE=${PROJECT_RELEASE:-}"
|
||||
|
||||
echo "Injecting debug IDs..."
|
||||
npx sentry-cli sourcemaps --log-level info inject frontend/dist
|
||||
|
||||
echo "Creating release and uploading sourcemaps..."
|
||||
npx sentry-cli sourcemaps --log-level info upload \
|
||||
--release="${PROJECT_RELEASE:-${SENTRY_PROJECT}.dirty}" \
|
||||
--url-prefix='~/dist' \
|
||||
--ext js --ext mjs --ext map \
|
||||
frontend/dist
|
||||
Reference in New Issue
Block a user