Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c84f9a7a9 | ||
|
|
6701c6bc0d | ||
|
|
52d8e0f7e0 |
@@ -0,0 +1,12 @@
|
||||
**/node_modules
|
||||
**/dist
|
||||
**/dist-capacitor
|
||||
mobile-client/android
|
||||
mobile-client/updates
|
||||
data
|
||||
.git
|
||||
.woodpecker
|
||||
.idea
|
||||
.claude
|
||||
**/.env
|
||||
*.log
|
||||
@@ -0,0 +1,69 @@
|
||||
# Woodpecker CI: typecheck + tests on every push/PR, container images on main and ci (tags: short SHA and branch name).
|
||||
#
|
||||
# Required secrets (Repository settings -> Secrets):
|
||||
# registry_username, registry_password
|
||||
# Images are built with kaniko: no privileged mode / Trusted flag needed.
|
||||
#
|
||||
variables:
|
||||
- &node_image 'node:22-alpine'
|
||||
- &kaniko 'woodpeckerci/plugin-kaniko:latest'
|
||||
- ®istry 'git.aggtaa.com'
|
||||
- &publish_when
|
||||
- event: [push, manual]
|
||||
branch: [main, ci]
|
||||
|
||||
steps:
|
||||
- name: check
|
||||
image: *node_image
|
||||
commands:
|
||||
- corepack enable
|
||||
- pnpm install --frozen-lockfile
|
||||
- pnpm typecheck
|
||||
- pnpm test
|
||||
when:
|
||||
- event: [push, pull_request, manual]
|
||||
|
||||
- name: image-server-be
|
||||
image: *kaniko
|
||||
depends_on: [check]
|
||||
settings:
|
||||
registry: *registry
|
||||
repo: ${CI_REPO_OWNER}/cardy-server-be
|
||||
dockerfile: docker/server-be.Dockerfile
|
||||
context: .
|
||||
tags: ['${CI_COMMIT_SHA:0:8}', '${CI_COMMIT_BRANCH}']
|
||||
username:
|
||||
from_secret: registry_username
|
||||
password:
|
||||
from_secret: registry_password
|
||||
when: *publish_when
|
||||
|
||||
- name: image-server-fe
|
||||
image: *kaniko
|
||||
depends_on: [check]
|
||||
settings:
|
||||
registry: *registry
|
||||
repo: ${CI_REPO_OWNER}/cardy-server-fe
|
||||
dockerfile: docker/server-fe.Dockerfile
|
||||
context: .
|
||||
tags: ['${CI_COMMIT_SHA:0:8}', '${CI_COMMIT_BRANCH}']
|
||||
username:
|
||||
from_secret: registry_username
|
||||
password:
|
||||
from_secret: registry_password
|
||||
when: *publish_when
|
||||
|
||||
- name: image-mobile-client
|
||||
image: *kaniko
|
||||
depends_on: [check]
|
||||
settings:
|
||||
registry: *registry
|
||||
repo: ${CI_REPO_OWNER}/cardy-mobile-client
|
||||
dockerfile: docker/mobile-client.Dockerfile
|
||||
context: .
|
||||
tags: ['${CI_COMMIT_SHA:0:8}', '${CI_COMMIT_BRANCH}']
|
||||
username:
|
||||
from_secret: registry_username
|
||||
password:
|
||||
from_secret: registry_password
|
||||
when: *publish_when
|
||||
@@ -96,3 +96,18 @@ with `/api` stripped; `DEV_REMOTE_EMAIL` in `server-fe/.env` fakes the Authelia
|
||||
pnpm test
|
||||
pnpm typecheck
|
||||
```
|
||||
|
||||
## Containers and CI (Woodpecker)
|
||||
|
||||
`docker/*.Dockerfile` build the three images from the repository root (`docker build -f docker/server-be.Dockerfile .`):
|
||||
|
||||
| Image | Port | Notes |
|
||||
|---|---|---|
|
||||
| `server-be` | 10002 | Node, data in volume `/data`. Listens on `0.0.0.0`: set `ADMIN_PROXY_SECRET` and don't publish the port publicly |
|
||||
| `server-fe` | 10001 | nginx with the admin UI build |
|
||||
| `mobile-client` | 10003 | nginx with the PWA; Android updates are mounted at `/updates` |
|
||||
|
||||
`.woodpecker/ci.yaml`: typecheck + tests on every push/PR, then images are pushed on `main` and `ci`
|
||||
(tags: short commit SHA and branch name, e.g. `:main`). Images go to the Gitea registry (`git.aggtaa.com/anton/cardy-*`); add secrets `registry_username`
|
||||
(Gitea user + access token with `write:package`) and `registry_password`. Images are built with kaniko, so the repository doesn't need to be Trusted.
|
||||
`docker/compose.example.yml` shows how to run the images. APK/OTA publishing stays local (needs the signing key).
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Example deployment. Put the reverse proxy (Authelia on /api/admin) in front, as described in README.
|
||||
services:
|
||||
server-be:
|
||||
image: git.aggtaa.com/anton/cardy-server-be:main
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
KEY_ENCRYPTION_SECRET: ${KEY_ENCRYPTION_SECRET}
|
||||
SUPERADMIN_EMAIL: ${SUPERADMIN_EMAIL}
|
||||
ADMIN_PROXY_SECRET: ${ADMIN_PROXY_SECRET} # the container listens on 0.0.0.0, so set it
|
||||
PUBLIC_API_URL: https://dev.aggtaa.com/api
|
||||
volumes:
|
||||
- cardy-data:/data
|
||||
ports:
|
||||
- "127.0.0.1:10002:10002"
|
||||
|
||||
server-fe:
|
||||
image: git.aggtaa.com/anton/cardy-server-fe:main
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:10001:10001"
|
||||
|
||||
mobile-client:
|
||||
image: git.aggtaa.com/anton/cardy-mobile-client:main
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./updates/android:/updates:ro # output of `publish:android` / `publish:android-apk`
|
||||
ports:
|
||||
- "127.0.0.1:10003:10003"
|
||||
|
||||
volumes:
|
||||
cardy-data:
|
||||
@@ -0,0 +1,19 @@
|
||||
# Build context: repository root.
|
||||
FROM node:22-alpine AS build
|
||||
RUN corepack enable
|
||||
WORKDIR /repo
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json tsconfig.base.json ./
|
||||
COPY shared ./shared
|
||||
COPY mobile-client ./mobile-client
|
||||
RUN pnpm install --frozen-lockfile --filter @cardy/mobile-client...
|
||||
# CARDY_UPDATE_URL is baked into the web build (where the Android app looks for live updates).
|
||||
ARG CARDY_UPDATE_URL=https://dev.aggtaa.com/m/android
|
||||
ENV CARDY_UPDATE_URL=$CARDY_UPDATE_URL
|
||||
RUN pnpm --filter @cardy/mobile-client exec vite build
|
||||
|
||||
FROM nginxinc/nginx-unprivileged:alpine
|
||||
COPY docker/nginx-mobile.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /repo/mobile-client/dist /usr/share/nginx/html
|
||||
# Android updates (version.json, bundles, APK) published with `publish:android*` are mounted here.
|
||||
VOLUME /updates
|
||||
EXPOSE 10003
|
||||
@@ -0,0 +1,31 @@
|
||||
# The reverse proxy strips /m before forwarding, so files are looked up at the root.
|
||||
# The "/m/" form is also accepted for direct access.
|
||||
server {
|
||||
listen 10003;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
rewrite ^/m/(.*)$ /$1 last;
|
||||
|
||||
# Published Android updates; open CORS because the app runs on https://localhost.
|
||||
location /android/ {
|
||||
alias /updates/;
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
add_header Cache-Control "no-cache";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location /assets/ {
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
}
|
||||
|
||||
# Service worker and manifest must always be revalidated.
|
||||
location ~ ^/(sw\.js|workbox-.*\.js|registerSW\.js|manifest\.webmanifest|index\.html)$ {
|
||||
add_header Cache-Control "no-cache";
|
||||
}
|
||||
|
||||
location / {
|
||||
add_header Cache-Control "no-cache";
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
server {
|
||||
listen 10001;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
location /assets/ {
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
}
|
||||
|
||||
location / {
|
||||
add_header Cache-Control "no-cache";
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# Build context: repository root.
|
||||
FROM node:22-alpine AS build
|
||||
RUN corepack enable
|
||||
WORKDIR /repo
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json tsconfig.base.json ./
|
||||
COPY shared ./shared
|
||||
COPY server-be ./server-be
|
||||
# Only the workspace packages needed by server-be (the other ones are not copied).
|
||||
RUN pnpm install --frozen-lockfile --filter @cardy/server-be...
|
||||
RUN pnpm --filter @cardy/server-be build \
|
||||
&& pnpm --filter @cardy/server-be --prod deploy --legacy /out
|
||||
|
||||
FROM node:22-alpine
|
||||
ENV NODE_ENV=production \
|
||||
DATA_DIR=/data \
|
||||
HOST=0.0.0.0 \
|
||||
PORT=10002
|
||||
WORKDIR /app
|
||||
COPY --from=build /out/package.json ./package.json
|
||||
COPY --from=build /out/node_modules ./node_modules
|
||||
COPY --from=build /out/dist ./dist
|
||||
RUN mkdir -p /data && chown node:node /data
|
||||
USER node
|
||||
VOLUME /data
|
||||
EXPOSE 10002
|
||||
CMD ["node", "dist/index.js"]
|
||||
@@ -0,0 +1,14 @@
|
||||
# Build context: repository root.
|
||||
FROM node:22-alpine AS build
|
||||
RUN corepack enable
|
||||
WORKDIR /repo
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json tsconfig.base.json ./
|
||||
COPY shared ./shared
|
||||
COPY server-fe ./server-fe
|
||||
RUN pnpm install --frozen-lockfile --filter @cardy/server-fe...
|
||||
RUN pnpm --filter @cardy/server-fe build
|
||||
|
||||
FROM nginxinc/nginx-unprivileged:alpine
|
||||
COPY docker/nginx-spa.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /repo/server-fe/dist /usr/share/nginx/html
|
||||
EXPOSE 10001
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "cardy",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@11.4.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "pnpm -r build",
|
||||
|
||||
Reference in New Issue
Block a user