feat(deploy): use pre-built GHCR images, add release workflow

Author: Aaron Steven White
Commit cd4eaca9ef7e2ff764b35f71ae2b6988d973ba07
Parent: ce2bfee6a3
Structural diff unavailable

These commits were pushed via plain git push, so no pre-parsed schemas are available. Install git-remote-cospan and re-push via panproto:// to see scope-level changes, breaking change detection, and semantic diffs.

brew install panproto/tap/git-remote-cospan
5 files changed +86 -12
@@ -1,6 +1,9 @@
11 # Cospan Production Environment
22 # Copy to .env.production and fill in all values
33 
4+# Image version (defaults to latest)
5+# COSPAN_VERSION=0.2.0
6+
47 # Domain (required) - point DNS A record at your server IP
58 DOMAIN=cospan.dev
69 
@@ -0,0 +1,54 @@
1+name: Release
2+
3+on:
4+  push:
5+    tags: ["v*"]
6+
7+env:
8+  REGISTRY: ghcr.io
9+
10+jobs:
11+  build-and-push:
12+    name: Build and push Docker images
13+    runs-on: ubuntu-latest
14+    permissions:
15+      contents: read
16+      packages: write
17+    strategy:
18+      matrix:
19+        include:
20+          - name: node
21+            dockerfile: crates/cospan-node/Dockerfile
22+            image: ghcr.io/cospan-dev/cospan-node
23+          - name: appview
24+            dockerfile: crates/cospan-appview/Dockerfile
25+            image: ghcr.io/cospan-dev/cospan-appview
26+          - name: web
27+            dockerfile: apps/web/Dockerfile
28+            image: ghcr.io/cospan-dev/cospan-web
29+    steps:
30+      - uses: actions/checkout@v4
31+
32+      - uses: docker/login-action@v3
33+        with:
34+          registry: ${{ env.REGISTRY }}
35+          username: ${{ github.actor }}
36+          password: ${{ secrets.GITHUB_TOKEN }}
37+
38+      - uses: docker/metadata-action@v5
39+        id: meta
40+        with:
41+          images: ${{ matrix.image }}
42+          tags: |
43+            type=semver,pattern={{version}}
44+            type=semver,pattern={{major}}.{{minor}}
45+            type=sha
46+            type=raw,value=latest
47+
48+      - uses: docker/build-push-action@v6
49+        with:
50+          context: .
51+          file: ${{ matrix.dockerfile }}
52+          push: true
53+          tags: ${{ steps.meta.outputs.tags }}
54+          labels: ${{ steps.meta.outputs.labels }}
@@ -74,6 +74,28 @@ pnpm build
7474 PORT=3000 node build/index.js
7575 ```
7676 
77+### Production deployment
78+
79+Pre-built Docker images are published to GHCR on each release. Deploy to any VPS with Docker:
80+
81+```bash
82+ssh root@your-server
83+
84+# Install Docker
85+curl -fsSL https://get.docker.com | sh
86+
87+# Clone and configure
88+git clone https://github.com/cospan-dev/cospan.git
89+cd cospan
90+cp .env.production.example .env.production
91+nano .env.production  # Set DOMAIN, POSTGRES_PASSWORD, NODE_DID
92+
93+# Deploy (pulls pre-built images, starts all services)
94+./scripts/deploy.sh
95+```
96+
97+Caddy handles TLS automatically via Let's Encrypt. Requires two DNS A records pointing at the server: `cospan.dev` and `node.cospan.dev`.
98+
7799 ### Using the CLI
78100 
79101 Cospan repos are managed through panproto's `schema` CLI:
@@ -3,7 +3,8 @@
33 #   1. Copy .env.production.example to .env.production and fill in values
44 #   2. docker compose -f docker-compose.prod.yml up -d
55 #
6-# Requires: Docker, ~2GB RAM, domain pointed at the server
6+# Images are pre-built and pushed to GHCR by the release workflow.
7+# Requires: Docker, ~1GB RAM, domain pointed at the server
78 
89 services:
910   # Reverse proxy with auto-TLS
@@ -72,9 +73,7 @@ services:
7273 
7374   # panproto-vcs node server
7475   node:
75-    build:
76-      context: .
77-      dockerfile: crates/cospan-node/Dockerfile
76+    image: ghcr.io/cospan-dev/cospan-node:${COSPAN_VERSION:-latest}
7877     container_name: cospan-node
7978     restart: unless-stopped
8079     volumes:
@@ -93,9 +92,7 @@ services:
9392 
9493   # ATProto AppView
9594   appview:
96-    build:
97-      context: .
98-      dockerfile: crates/cospan-appview/Dockerfile
95+    image: ghcr.io/cospan-dev/cospan-appview:${COSPAN_VERSION:-latest}
9996     container_name: cospan-appview
10097     restart: unless-stopped
10198     environment:
@@ -123,9 +120,7 @@ services:
123120 
124121   # SvelteKit frontend
125122   web:
126-    build:
127-      context: .
128-      dockerfile: apps/web/Dockerfile
123+    image: ghcr.io/cospan-dev/cospan-web:${COSPAN_VERSION:-latest}
129124     container_name: cospan-web
130125     restart: unless-stopped
131126     environment:
@@ -59,8 +59,8 @@ echo -e "${GREEN}Node DID:${NC} $NODE_DID"
5959 echo ""
6060 
6161 # Build and deploy
62-echo -e "${YELLOW}Building images...${NC}"
63-docker compose -f docker-compose.prod.yml --env-file .env.production build
62+echo -e "${YELLOW}Pulling images...${NC}"
63+docker compose -f docker-compose.prod.yml --env-file .env.production pull
6464 
6565 echo -e "${YELLOW}Starting services...${NC}"
6666 docker compose -f docker-compose.prod.yml --env-file .env.production up -d
cospan · schematic version control on atproto built on AT Protocol