Add GitHub Actions CI, wire ESLint, lock in Caddy
CI pipeline with parallel jobs for fast feedback: - typecheck, lint, unit tests, build run in parallel - e2e tests run after build - Playwright artifacts uploaded on failure ESLint wired into both apps with sensible defaults: - underscore-prefixed unused vars allowed - root package.json marked as type: module Architecture updated: Caddy as reverse proxy with auto HTTPS. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9ad2f5785e
commit
f58fe99cde
9 changed files with 123 additions and 6 deletions
87
.github/workflows/ci.yml
vendored
Normal file
87
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
typecheck:
|
||||||
|
name: Typecheck
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm typecheck
|
||||||
|
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm lint
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: Unit Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm test
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm build
|
||||||
|
|
||||||
|
e2e:
|
||||||
|
name: E2E Tests
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm exec playwright install chromium
|
||||||
|
- run: pnpm test:e2e
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: |
|
||||||
|
e2e/results/
|
||||||
|
playwright-report/
|
||||||
|
retention-days: 7
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { Route } from "./+types/home";
|
import type { Route } from "./+types/home";
|
||||||
|
|
||||||
export function meta({}: Route.MetaArgs) {
|
export function meta(_args: Route.MetaArgs) {
|
||||||
return [
|
return [
|
||||||
{ title: "trails.cool" },
|
{ title: "trails.cool" },
|
||||||
{ name: "description", content: "Your outdoor activity journal" },
|
{ name: "description", content: "Your outdoor activity journal" },
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
"dev": "react-router dev",
|
"dev": "react-router dev",
|
||||||
"build": "react-router build",
|
"build": "react-router build",
|
||||||
"start": "react-router-serve ./build/server/index.js",
|
"start": "react-router-serve ./build/server/index.js",
|
||||||
"typecheck": "react-router typegen && tsc"
|
"typecheck": "react-router typegen && tsc",
|
||||||
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@trails-cool/ui": "workspace:*",
|
"@trails-cool/ui": "workspace:*",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { Route } from "./+types/home";
|
import type { Route } from "./+types/home";
|
||||||
|
|
||||||
export function meta({}: Route.MetaArgs) {
|
export function meta(_args: Route.MetaArgs) {
|
||||||
return [
|
return [
|
||||||
{ title: "trails.cool Planner" },
|
{ title: "trails.cool Planner" },
|
||||||
{ name: "description", content: "Collaborative route planning" },
|
{ name: "description", content: "Collaborative route planning" },
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
"dev": "react-router dev",
|
"dev": "react-router dev",
|
||||||
"build": "react-router build",
|
"build": "react-router build",
|
||||||
"start": "react-router-serve ./build/server/index.js",
|
"start": "react-router-serve ./build/server/index.js",
|
||||||
"typecheck": "react-router typegen && tsc"
|
"typecheck": "react-router typegen && tsc",
|
||||||
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@trails-cool/ui": "workspace:*",
|
"@trails-cool/ui": "workspace:*",
|
||||||
|
|
|
||||||
|
|
@ -316,9 +316,28 @@ services:
|
||||||
image: dxflrs/garage:v1.0
|
image: dxflrs/garage:v1.0
|
||||||
volumes: ["media:/var/lib/garage"]
|
volumes: ["media:/var/lib/garage"]
|
||||||
|
|
||||||
|
caddy:
|
||||||
|
image: caddy:2
|
||||||
|
ports: ["80:80", "443:443"]
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile:/etc/caddy/Caddyfile
|
||||||
|
- caddy_data:/data
|
||||||
|
depends_on: [journal]
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
pgdata:
|
pgdata:
|
||||||
media:
|
media:
|
||||||
|
caddy_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
Caddy is the reverse proxy for all instances. It provides automatic HTTPS
|
||||||
|
via Let's Encrypt with zero configuration beyond the Caddyfile:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Caddyfile (self-hosted example)
|
||||||
|
bob.trails.xyz {
|
||||||
|
reverse_proxy journal:3000
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### With Self-Hosted Planner (Advanced)
|
### With Self-Hosted Planner (Advanced)
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,13 @@ export default tseslint.config(
|
||||||
{ ignores: ["**/build/", "**/dist/", "**/.react-router/", "**/node_modules/"] },
|
{ ignores: ["**/build/", "**/dist/", "**/.react-router/", "**/node_modules/"] },
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
...tseslint.configs.recommended,
|
...tseslint.configs.recommended,
|
||||||
|
{
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"error",
|
||||||
|
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
prettier,
|
prettier,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@
|
||||||
- [ ] 3.2 Create Docker Compose for all services (Journal, Planner, BRouter, PostgreSQL+PostGIS, Garage)
|
- [ ] 3.2 Create Docker Compose for all services (Journal, Planner, BRouter, PostgreSQL+PostGIS, Garage)
|
||||||
- [ ] 3.3 Create BRouter Dockerfile with segment volume mount
|
- [ ] 3.3 Create BRouter Dockerfile with segment volume mount
|
||||||
- [ ] 3.4 Create segment download script (Germany: E5_N45, E5_N50, E10_N45, E10_N50)
|
- [ ] 3.4 Create segment download script (Germany: E5_N45, E5_N50, E10_N45, E10_N50)
|
||||||
- [ ] 3.5 Configure DNS for trails.cool and planner.trails.cool with TLS (Caddy or Traefik)
|
- [ ] 3.5 Configure DNS for trails.cool and planner.trails.cool with TLS (Caddy)
|
||||||
- [ ] 3.6 Set up GitHub Actions CI pipeline (build, typecheck, lint)
|
- [x] 3.6 Set up GitHub Actions CI pipeline (build, typecheck, lint, unit tests, e2e)
|
||||||
- [ ] 3.7 Set up GitHub Actions CD pipeline (build Docker images, push to ghcr.io, deploy to Hetzner)
|
- [ ] 3.7 Set up GitHub Actions CD pipeline (build Docker images, push to ghcr.io, deploy to Hetzner)
|
||||||
- [ ] 3.8 Set up PostgreSQL backup cron (daily pg_dump to Hetzner Storage Box)
|
- [ ] 3.8 Set up PostgreSQL backup cron (daily pg_dump to Hetzner Storage Box)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
"name": "trails.cool",
|
"name": "trails.cool",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Collaborative route planning + federated activity sharing for outdoor enthusiasts",
|
"description": "Collaborative route planning + federated activity sharing for outdoor enthusiasts",
|
||||||
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"packageManager": "pnpm@10.6.5",
|
"packageManager": "pnpm@10.6.5",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue