feat: update deployment scripts and configuration; enhance CI/CD process with new scripts for staging and production

This commit is contained in:
2026-02-26 12:36:53 +00:00
parent 965a505e15
commit 5707eb30dd
9 changed files with 364 additions and 220 deletions

View File

@@ -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

View File

@@ -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
View File

@@ -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
```

View File

@@ -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

View File

@@ -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
View 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
View 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
View 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
View 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