@polymorphism-tech/morph-spec 7.5.0 → 7.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents.json +1 -1
- package/framework/commands/morph-preflight.md +10 -5
- package/framework/rules/infrastructure-standards.md +4 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +22 -2
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +84 -5
- package/framework/skills/core/morph-apply/SKILL.md +1 -1
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +1 -1
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +80 -8
- package/framework/standards/ai-agents/durable-workflows-hitl.md +191 -0
- package/framework/standards/ai-agents/rag-custom-pgvector.md +246 -243
- package/framework/standards/ai-agents/setup.md +3 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +170 -0
- package/framework/standards/ai-agents/workflows.md +190 -187
- package/framework/standards/infrastructure/docker/{easypanel-deploy.md → coolify-deploy.md} +49 -20
- package/framework/templates/README.md +7 -7
- package/framework/templates/REGISTRY.json +14 -14
- package/framework/templates/context/CONTEXT.md +3 -3
- package/framework/templates/docs/proposal.md +2 -2
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +1 -1
- package/framework/templates/infrastructure/docker/coolify.template.json +49 -0
- package/framework/templates/infrastructure/github/README.md +6 -6
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +33 -0
- package/package.json +1 -1
- package/framework/templates/infrastructure/docker/easypanel.template.json +0 -46
- package/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +0 -31
|
@@ -1,23 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Coolify Deployment Standard
|
|
2
2
|
|
|
3
3
|
> **Scope:** nextjs-neon
|
|
4
4
|
> **Layer:** 2 (on keyword)
|
|
5
|
-
> **Keywords:**
|
|
6
|
-
> **Load When:**
|
|
5
|
+
> **Keywords:** coolify, docker, deploy, deployment, container, vps, ssh
|
|
6
|
+
> **Load When:** coolify or docker deployment keywords detected
|
|
7
7
|
|
|
8
|
-
**Verified against:** .NET 10 + Next.js 15 + Node 22 (Docker base images). Last-verified: 2026-
|
|
8
|
+
**Verified against:** .NET 10 + Next.js 15 + Node 22 (Docker base images). Last-verified: 2026-07-07.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
Stack: Next.js 15 + Neon PostgreSQL + .NET Backend
|
|
13
13
|
|
|
14
|
+
## What is Coolify and why we use it
|
|
15
|
+
|
|
16
|
+
[Coolify](https://coolify.io) is a self-hosted, open-source PaaS -- an alternative to Heroku/Vercel/Railway that runs on your own VPS. It manages Docker-based deployments end-to-end: builds from a Dockerfile or docker-compose, reverse-proxies traffic (Traefik under the hood), issues and renews Let's Encrypt SSL certificates, and exposes a REST API for automation.
|
|
17
|
+
|
|
18
|
+
Coolify is Polymorphism Tech's standard self-hosted deployment platform for Docker workloads.
|
|
19
|
+
|
|
20
|
+
There is no MCP server or CLI tool for Coolify. All access is via **SSH into the VPS**, from which the Coolify API is reached at `http://localhost:8000/api/v1/` (it is not exposed publicly). The API token lives server-side only (e.g. `~/.coolify-token`, `chmod 600`) -- never print it, never commit it, never let it leave the SSH session.
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Canonical access pattern — token is read and used entirely server-side
|
|
24
|
+
ssh <vps-ssh-alias> 'T=$(cat ~/.coolify-token); curl -s -H "Authorization: Bearer $T" http://localhost:8000/api/v1/projects'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Useful endpoints: `/version`, `/projects`, `/servers`, `/applications`, `/deploy?uuid={uuid}`. Changing a domain: `PATCH /applications/{uuid}` with `{"domains":"https://x,https://www.x"}`, then `GET /deploy?uuid={uuid}` to redeploy.
|
|
28
|
+
|
|
14
29
|
## Core Rules
|
|
15
30
|
|
|
16
31
|
- ALWAYS use multi-stage Docker builds for minimal image size
|
|
17
32
|
- ALWAYS configure health checks for zero-downtime deploys
|
|
18
|
-
- NEVER hardcode secrets in Dockerfiles -- use
|
|
33
|
+
- NEVER hardcode secrets in Dockerfiles -- use Coolify environment variables
|
|
19
34
|
- ALWAYS use `.dockerignore` to exclude node_modules, .git, .env files
|
|
20
35
|
- SSL is automatic via Let's Encrypt -- no manual certificate management
|
|
36
|
+
- NEVER call the Coolify API directly from your local machine or from CI without going through SSH -- the API port is not publicly reachable and the token must stay server-side
|
|
21
37
|
|
|
22
38
|
## .NET 10 Dockerfile
|
|
23
39
|
|
|
@@ -85,7 +101,7 @@ test
|
|
|
85
101
|
__tests__
|
|
86
102
|
```
|
|
87
103
|
|
|
88
|
-
##
|
|
104
|
+
## Coolify Service Config
|
|
89
105
|
|
|
90
106
|
| Setting | .NET Backend | Next.js Frontend |
|
|
91
107
|
|---------|-------------|-----------------|
|
|
@@ -97,20 +113,22 @@ __tests__
|
|
|
97
113
|
|
|
98
114
|
### GitHub Integration
|
|
99
115
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
116
|
+
Coolify deploys straight from a GitHub repo (via GitHub App install or a deploy key) and redeploys automatically on push when auto-deploy is enabled for the application.
|
|
117
|
+
|
|
118
|
+
1. In Coolify, add the GitHub source (GitHub App install or deploy key)
|
|
119
|
+
2. Create the Application, pointing it at the repo/branch and the Dockerfile path
|
|
120
|
+
3. Enable "Auto Deploy" for webhook-triggered deploys on push to the tracked branch
|
|
121
|
+
4. Record the application's `uuid` -- every subsequent API call (`/deploy?uuid=`) targets it
|
|
104
122
|
|
|
105
123
|
### Domain and SSL
|
|
106
124
|
|
|
107
|
-
DNS setup (
|
|
125
|
+
DNS setup (A record to the Coolify server):
|
|
108
126
|
```
|
|
109
|
-
app.example.com
|
|
110
|
-
api.example.com
|
|
127
|
+
app.example.com A <vps-ip>
|
|
128
|
+
api.example.com A <vps-ip>
|
|
111
129
|
```
|
|
112
130
|
|
|
113
|
-
SSL via Let's Encrypt is automatic. Force HTTPS enabled by default.
|
|
131
|
+
SSL via Let's Encrypt is automatic (Coolify runs Traefik as the reverse proxy). Force HTTPS enabled by default.
|
|
114
132
|
|
|
115
133
|
## Environment Variables
|
|
116
134
|
|
|
@@ -147,11 +165,11 @@ export async function GET() {
|
|
|
147
165
|
|
|
148
166
|
## Zero-Downtime Deploys
|
|
149
167
|
|
|
150
|
-
|
|
168
|
+
Coolify uses the same Docker HEALTHCHECK contract to determine container readiness:
|
|
151
169
|
|
|
152
170
|
1. New container starts alongside old container
|
|
153
171
|
2. Health check passes after start-period + retries
|
|
154
|
-
3. Traffic shifts to new container
|
|
172
|
+
3. Traffic shifts to new container (Traefik)
|
|
155
173
|
4. Old container stopped
|
|
156
174
|
|
|
157
175
|
| HEALTHCHECK Param | Value | Purpose |
|
|
@@ -163,8 +181,8 @@ EasyPanel uses Docker HEALTHCHECK to determine container readiness:
|
|
|
163
181
|
|
|
164
182
|
## Monitoring
|
|
165
183
|
|
|
166
|
-
- **Logs**:
|
|
167
|
-
- **Metrics**: CPU, memory, network via dashboard
|
|
184
|
+
- **Logs**: `docker logs <container>` over SSH, or Coolify's live log viewer in the UI
|
|
185
|
+
- **Metrics**: CPU, memory, network via Coolify's dashboard, or `docker stats` over SSH
|
|
168
186
|
- **Restart**: Auto-restart on crash (default)
|
|
169
187
|
|
|
170
188
|
Structured logging:
|
|
@@ -182,6 +200,17 @@ import pino from "pino";
|
|
|
182
200
|
const logger = pino({ level: process.env.LOG_LEVEL ?? "info" });
|
|
183
201
|
```
|
|
184
202
|
|
|
203
|
+
## Triggering a Deploy via SSH + API
|
|
204
|
+
|
|
205
|
+
Since there is no MCP server or CLI for Coolify, every deploy operation (manual redeploy, CI/CD, or troubleshooting) goes through the same SSH + curl pattern used for all other Coolify access:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Redeploy an existing application after a new image/commit is ready
|
|
209
|
+
ssh <vps-ssh-alias> 'T=$(cat ~/.coolify-token); curl -s -X GET -H "Authorization: Bearer $T" "http://localhost:8000/api/v1/deploy?uuid=<application-uuid>"'
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
In GitHub Actions this becomes an SSH step using a deploy key stored as a secret (see the `github-workflow-deploy-coolify` template) -- never a direct HTTP call to the Coolify API from the runner, since the API port is not reachable outside the VPS.
|
|
213
|
+
|
|
185
214
|
## Deployment Checklist
|
|
186
215
|
|
|
187
216
|
| Step | Action |
|
|
@@ -189,8 +218,8 @@ const logger = pino({ level: process.env.LOG_LEVEL ?? "info" });
|
|
|
189
218
|
| 1 | Verify `output: "standalone"` in next.config.ts |
|
|
190
219
|
| 2 | Test Docker build locally |
|
|
191
220
|
| 3 | Test health endpoint |
|
|
192
|
-
| 4 | Configure env vars in
|
|
221
|
+
| 4 | Configure env vars in Coolify (UI or API) |
|
|
193
222
|
| 5 | Set up custom domain + DNS |
|
|
194
223
|
| 6 | Verify SSL certificate |
|
|
195
224
|
| 7 | Enable auto-deploy from GitHub |
|
|
196
|
-
| 8 | Push to main, verify deployment |
|
|
225
|
+
| 8 | Push to main, verify deployment via SSH (`docker ps` / Coolify logs) |
|
|
@@ -54,18 +54,18 @@ framework/templates/
|
|
|
54
54
|
│ └── postgresql-rls-policy.sql # Row-level security
|
|
55
55
|
│
|
|
56
56
|
├── infrastructure/
|
|
57
|
-
│ ├── docker/ # Docker &
|
|
57
|
+
│ ├── docker/ # Docker & Coolify
|
|
58
58
|
│ │ ├── dockerfile-api.dockerfile # .NET API
|
|
59
59
|
│ │ ├── dockerfile-web.dockerfile # Next.js web
|
|
60
60
|
│ │ ├── Dockerfile.template # Generic
|
|
61
61
|
│ │ ├── docker-compose.template.yml # Compose
|
|
62
|
-
│ │ └──
|
|
62
|
+
│ │ └── coolify.template.json # Coolify config
|
|
63
63
|
│ │
|
|
64
64
|
│ └── github/ # GitHub Actions CI/CD
|
|
65
65
|
│ ├── workflows/ # Reusable workflow templates
|
|
66
66
|
│ │ ├── dotnet-build.yml.hbs # .NET restore, build, test
|
|
67
67
|
│ │ ├── docker-build-push.yml.hbs # Docker build & push
|
|
68
|
-
│ │ └── deploy-
|
|
68
|
+
│ │ └── deploy-coolify.yml.hbs # Coolify deployment (via SSH)
|
|
69
69
|
│ │
|
|
70
70
|
│ ├── actions/ # Composite action templates
|
|
71
71
|
│ │ ├── docker-build-push/action.yml.hbs
|
|
@@ -313,7 +313,7 @@ infrastructure/github/
|
|
|
313
313
|
├── workflows/ # Reusable workflow templates
|
|
314
314
|
│ ├── dotnet-build.yml.hbs # .NET restore, build, test
|
|
315
315
|
│ ├── docker-build-push.yml.hbs # Docker build & push
|
|
316
|
-
│ └── deploy-
|
|
316
|
+
│ └── deploy-coolify.yml.hbs # Coolify deployment via SSH
|
|
317
317
|
│
|
|
318
318
|
└── actions/ # Composite action templates
|
|
319
319
|
├── docker-build-push/action.yml.hbs
|
|
@@ -329,8 +329,8 @@ infrastructure/github/
|
|
|
329
329
|
|
|
330
330
|
### Deployment Strategy Difference
|
|
331
331
|
|
|
332
|
-
**NextJS-Neon:** Docker containers to
|
|
333
|
-
- `docker build` → `docker push` →
|
|
332
|
+
**NextJS-Neon:** Docker containers to Coolify
|
|
333
|
+
- `docker build` → `docker push` → Coolify deploy triggered via SSH (Coolify's API is not publicly exposed — the runner SSHes into the VPS and calls `localhost:8000/api/v1/deploy` there)
|
|
334
334
|
- Multi-service deployment (API + Web)
|
|
335
335
|
- Registry: GitHub Container Registry (ghcr.io)
|
|
336
336
|
|
|
@@ -368,7 +368,7 @@ infrastructure/github/
|
|
|
368
368
|
```
|
|
369
369
|
|
|
370
370
|
**GitHub Secrets to Configure:**
|
|
371
|
-
- NextJS-Neon: `GITHUB_TOKEN` (auto), `
|
|
371
|
+
- NextJS-Neon: `GITHUB_TOKEN` (auto), `COOLIFY_SSH_HOST`, `COOLIFY_SSH_USER`, `COOLIFY_SSH_KEY`, `COOLIFY_APPLICATION_UUID`
|
|
372
372
|
|
|
373
373
|
### Stack-Specific Overrides
|
|
374
374
|
|
|
@@ -127,6 +127,8 @@
|
|
|
127
127
|
"APP_INSIGHTS",
|
|
128
128
|
"AZURE_LOCATION",
|
|
129
129
|
"CONTAINER_ENV",
|
|
130
|
+
"COOLIFY_APP_URL",
|
|
131
|
+
"COOLIFY_SERVER",
|
|
130
132
|
"DEVOPS_AREA_PATH",
|
|
131
133
|
"DEVOPS_BUILD_PIPELINE",
|
|
132
134
|
"DEVOPS_ORG",
|
|
@@ -134,8 +136,6 @@
|
|
|
134
136
|
"DEVOPS_RELEASE_PIPELINE",
|
|
135
137
|
"DEVOPS_WIKI_NAME",
|
|
136
138
|
"DOTNET_VERSION",
|
|
137
|
-
"EASYPANEL_API_URL",
|
|
138
|
-
"EASYPANEL_WEB_URL",
|
|
139
139
|
"EF_VERSION",
|
|
140
140
|
"INFERRED_STANDARDS",
|
|
141
141
|
"MORPH_VERSION",
|
|
@@ -519,7 +519,7 @@
|
|
|
519
519
|
"category": "infrastructure",
|
|
520
520
|
"phase": "implement",
|
|
521
521
|
"required": false,
|
|
522
|
-
"description": ".NET API Dockerfile for
|
|
522
|
+
"description": ".NET API Dockerfile for Coolify/Docker",
|
|
523
523
|
"outputName": "Dockerfile.api",
|
|
524
524
|
"placeholders": [],
|
|
525
525
|
"stackSpecific": false,
|
|
@@ -540,7 +540,7 @@
|
|
|
540
540
|
"category": "infrastructure",
|
|
541
541
|
"phase": "implement",
|
|
542
542
|
"required": false,
|
|
543
|
-
"description": "Next.js web Dockerfile for
|
|
543
|
+
"description": "Next.js web Dockerfile for Coolify/Docker",
|
|
544
544
|
"outputName": "Dockerfile.web",
|
|
545
545
|
"placeholders": [],
|
|
546
546
|
"stackSpecific": false,
|
|
@@ -595,16 +595,16 @@
|
|
|
595
595
|
"validatePlaceholders": true
|
|
596
596
|
},
|
|
597
597
|
{
|
|
598
|
-
"id": "
|
|
598
|
+
"id": "coolify-config",
|
|
599
599
|
"kind": "template",
|
|
600
|
-
"name": "
|
|
601
|
-
"path": "infrastructure/docker/
|
|
600
|
+
"name": "Coolify Configuration",
|
|
601
|
+
"path": "infrastructure/docker/coolify.template.json",
|
|
602
602
|
"location": "framework",
|
|
603
603
|
"category": "infrastructure",
|
|
604
604
|
"phase": "implement",
|
|
605
605
|
"required": false,
|
|
606
|
-
"description": "
|
|
607
|
-
"outputName": "
|
|
606
|
+
"description": "Coolify deployment configuration — accessed via SSH + API, no MCP/CLI",
|
|
607
|
+
"outputName": "coolify.json",
|
|
608
608
|
"placeholders": [
|
|
609
609
|
"GITHUB_OWNER",
|
|
610
610
|
"GITHUB_REPO"
|
|
@@ -835,17 +835,17 @@
|
|
|
835
835
|
"validatePlaceholders": true
|
|
836
836
|
},
|
|
837
837
|
{
|
|
838
|
-
"id": "github-workflow-deploy-
|
|
838
|
+
"id": "github-workflow-deploy-coolify",
|
|
839
839
|
"kind": "template",
|
|
840
|
-
"name": "GitHub Actions - Deploy to
|
|
841
|
-
"path": "infrastructure/github/workflows/deploy-
|
|
840
|
+
"name": "GitHub Actions - Deploy to Coolify",
|
|
841
|
+
"path": "infrastructure/github/workflows/deploy-coolify.yml.hbs",
|
|
842
842
|
"location": "framework",
|
|
843
843
|
"category": "infrastructure",
|
|
844
844
|
"technology": "github-actions",
|
|
845
845
|
"phase": "deploy",
|
|
846
846
|
"required": false,
|
|
847
|
-
"description": "Reusable workflow for deploying to
|
|
848
|
-
"outputName": "deploy-
|
|
847
|
+
"description": "Reusable workflow for deploying to Coolify via SSH (API is not publicly exposed)",
|
|
848
|
+
"outputName": "deploy-coolify.yml",
|
|
849
849
|
"placeholders": [],
|
|
850
850
|
"stackSpecific": false,
|
|
851
851
|
"applicableStacks": [
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
{{#if MONITORING_ENABLED}}- **Application Insights** ({{APP_INSIGHTS}}){{/if}}
|
|
41
41
|
{{/if}}
|
|
42
42
|
|
|
43
|
-
{{#if
|
|
44
|
-
-
|
|
45
|
-
-
|
|
43
|
+
{{#if COOLIFY_ENABLED}}- **Coolify** (Docker Deployment via SSH)
|
|
44
|
+
- Server: {{COOLIFY_SERVER}}
|
|
45
|
+
- App: {{COOLIFY_APP_URL}}
|
|
46
46
|
{{/if}}
|
|
47
47
|
|
|
48
48
|
---
|
|
@@ -70,7 +70,7 @@ Verifiable conditions that mean this feature is done:
|
|
|
70
70
|
| **Database** | Neon (PostgreSQL + RLS) | {tables, policies, storage} |
|
|
71
71
|
| **Backend API** | .NET 10 Minimal API | {endpoints, business logic} |
|
|
72
72
|
| **Frontend** | Next.js 15 (App Router) | {pages, components, auth} |
|
|
73
|
-
| **Infra** |
|
|
73
|
+
| **Infra** | Coolify + Docker | {deployment, SSL} |
|
|
74
74
|
{{/if}}
|
|
75
75
|
|
|
76
76
|
### User Journey
|
|
@@ -90,7 +90,7 @@ Verifiable conditions that mean this feature is done:
|
|
|
90
90
|
| Resource | Provider | Monthly Cost | Notes |
|
|
91
91
|
|----------|----------|-------------|-------|
|
|
92
92
|
| Neon | Free / Launch | $0 - $19 | {DB, Connection Pooling, Branching} |
|
|
93
|
-
| VPS (
|
|
93
|
+
| VPS (Coolify) | {provider} | ${X} | {API + Web containers} |
|
|
94
94
|
| External APIs | {provider} | ${X} | {if applicable} |
|
|
95
95
|
| **Total** | | **${X}/month** | |
|
|
96
96
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{kebabCase FEATURE_NAME}}",
|
|
3
|
+
"provider": "coolify",
|
|
4
|
+
"access": {
|
|
5
|
+
"method": "ssh",
|
|
6
|
+
"note": "No MCP/CLI exists for Coolify — replace sshHost with the real SSH alias for the target VPS. The API is reached only from inside the VPS, at apiBaseUrl.",
|
|
7
|
+
"sshHost": "REPLACE_WITH_VPS_SSH_ALIAS",
|
|
8
|
+
"apiBaseUrl": "http://localhost:8000/api/v1",
|
|
9
|
+
"tokenPath": "~/.coolify-token"
|
|
10
|
+
},
|
|
11
|
+
"application": {
|
|
12
|
+
"uuid": "REPLACE_WITH_COOLIFY_APPLICATION_UUID",
|
|
13
|
+
"projectUuid": "REPLACE_WITH_COOLIFY_PROJECT_UUID",
|
|
14
|
+
"serverUuid": "REPLACE_WITH_COOLIFY_SERVER_UUID"
|
|
15
|
+
},
|
|
16
|
+
"source": {
|
|
17
|
+
"type": "github",
|
|
18
|
+
"owner": "{{GITHUB_OWNER}}",
|
|
19
|
+
"repo": "{{GITHUB_REPO}}",
|
|
20
|
+
"branch": "main",
|
|
21
|
+
"autoDeploy": true
|
|
22
|
+
},
|
|
23
|
+
"build": {
|
|
24
|
+
"type": "dockerfile",
|
|
25
|
+
"dockerfile": "Dockerfile"
|
|
26
|
+
},
|
|
27
|
+
"deploy": {
|
|
28
|
+
"replicas": 1,
|
|
29
|
+
"zeroDowntime": true
|
|
30
|
+
},
|
|
31
|
+
"env": [
|
|
32
|
+
{
|
|
33
|
+
"key": "NODE_ENV",
|
|
34
|
+
"value": "production"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"key": "DATABASE_URL",
|
|
38
|
+
"value": "${DATABASE_URL}"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"domains": [
|
|
42
|
+
{
|
|
43
|
+
"host": "{{kebabCase FEATURE_NAME}}.example.com",
|
|
44
|
+
"port": 3000,
|
|
45
|
+
"https": true
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"deployCommand": "ssh REPLACE_WITH_VPS_SSH_ALIAS 'T=$(cat ~/.coolify-token); curl -s -X GET -H \"Authorization: Bearer $T\" \"http://localhost:8000/api/v1/deploy?uuid=REPLACE_WITH_COOLIFY_APPLICATION_UUID\"'"
|
|
49
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# GitHub Actions Templates for MORPH-SPEC
|
|
2
2
|
|
|
3
|
-
> **Framework-Level CI/CD Templates for
|
|
3
|
+
> **Framework-Level CI/CD Templates for Coolify/Docker Deployments**
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
Reusable GitHub Actions workflows and composite actions for the **nextjs-neon** stack (
|
|
7
|
+
Reusable GitHub Actions workflows and composite actions for the **nextjs-neon** stack (Coolify/Docker deployment).
|
|
8
8
|
|
|
9
9
|
**Key Features:**
|
|
10
10
|
- Handlebars v2.0 templating with helpers
|
|
11
11
|
- Composite actions for shared logic
|
|
12
12
|
- Docker multi-stage builds (.NET API + Next.js Web)
|
|
13
|
-
-
|
|
13
|
+
- Coolify deployment via SSH (no MCP/CLI exists — the Coolify API is not publicly exposed, so the runner SSHes into the VPS to trigger it)
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -21,7 +21,7 @@ infrastructure/github/
|
|
|
21
21
|
├── workflows/
|
|
22
22
|
│ ├── dotnet-build.yml.hbs # .NET restore, build, test, coverage
|
|
23
23
|
│ ├── docker-build-push.yml.hbs # Docker build & push to registry
|
|
24
|
-
│ └── deploy-
|
|
24
|
+
│ └── deploy-coolify.yml.hbs # Coolify deployment via SSH
|
|
25
25
|
│
|
|
26
26
|
└── actions/
|
|
27
27
|
├── docker-build-push/action.yml.hbs # Build & push Docker image
|
|
@@ -38,8 +38,8 @@ infrastructure/github/
|
|
|
38
38
|
- Multi-stage Docker build
|
|
39
39
|
- Push to container registry (GHCR or Docker Hub)
|
|
40
40
|
|
|
41
|
-
### Deploy
|
|
42
|
-
-
|
|
41
|
+
### Deploy Coolify (`deploy-coolify.yml.hbs`)
|
|
42
|
+
- SSHes into the VPS and calls the local Coolify API (`localhost:8000/api/v1/deploy?uuid=...`) after Docker push
|
|
43
43
|
- Runs health check after deployment
|
|
44
44
|
- Supports staging + production environments
|
|
45
45
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Deploy to Coolify
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
environment:
|
|
7
|
+
required: true
|
|
8
|
+
type: string
|
|
9
|
+
image:
|
|
10
|
+
required: true
|
|
11
|
+
type: string
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
deploy:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
environment: ${{{{ inputs.environment }}}}
|
|
17
|
+
steps:
|
|
18
|
+
- name: Configure SSH
|
|
19
|
+
run: |
|
|
20
|
+
mkdir -p ~/.ssh
|
|
21
|
+
echo "${{{{ secrets.COOLIFY_SSH_KEY }}}}" > ~/.ssh/id_ed25519
|
|
22
|
+
chmod 600 ~/.ssh/id_ed25519
|
|
23
|
+
ssh-keyscan -H ${{{{ secrets.COOLIFY_SSH_HOST }}}} >> ~/.ssh/known_hosts
|
|
24
|
+
|
|
25
|
+
- name: Trigger Coolify Deploy (via SSH — the Coolify API is not publicly exposed)
|
|
26
|
+
run: |
|
|
27
|
+
ssh -i ~/.ssh/id_ed25519 ${{{{ secrets.COOLIFY_SSH_USER }}}}@${{{{ secrets.COOLIFY_SSH_HOST }}}} \
|
|
28
|
+
'T=$(cat ~/.coolify-token); curl -s -X GET -H "Authorization: Bearer $T" "http://localhost:8000/api/v1/deploy?uuid=${{{{ secrets.COOLIFY_APPLICATION_UUID }}}}"'
|
|
29
|
+
|
|
30
|
+
- name: Health Check
|
|
31
|
+
run: |
|
|
32
|
+
sleep 30 # Wait for deployment
|
|
33
|
+
curl --retry 10 --retry-delay 5 --retry-connrefused ${{{{ secrets.APP_URL }}}}/health
|
package/package.json
CHANGED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{kebabCase FEATURE_NAME}}",
|
|
3
|
-
"services": [
|
|
4
|
-
{
|
|
5
|
-
"type": "app",
|
|
6
|
-
"data": {
|
|
7
|
-
"projectName": "{{kebabCase FEATURE_NAME}}",
|
|
8
|
-
"serviceName": "{{kebabCase FEATURE_NAME}}-app",
|
|
9
|
-
"source": {
|
|
10
|
-
"type": "github",
|
|
11
|
-
"owner": "{{GITHUB_OWNER}}",
|
|
12
|
-
"repo": "{{GITHUB_REPO}}",
|
|
13
|
-
"ref": "main",
|
|
14
|
-
"autoDeploy": true
|
|
15
|
-
},
|
|
16
|
-
"build": {
|
|
17
|
-
"type": "dockerfile",
|
|
18
|
-
"dockerfile": "Dockerfile"
|
|
19
|
-
},
|
|
20
|
-
"deploy": {
|
|
21
|
-
"replicas": 1,
|
|
22
|
-
"command": null,
|
|
23
|
-
"zeroDowntime": true
|
|
24
|
-
},
|
|
25
|
-
"env": [
|
|
26
|
-
{
|
|
27
|
-
"key": "NODE_ENV",
|
|
28
|
-
"value": "production"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"key": "DATABASE_URL",
|
|
32
|
-
"value": "${DATABASE_URL}"
|
|
33
|
-
}
|
|
34
|
-
],
|
|
35
|
-
"domains": [
|
|
36
|
-
{
|
|
37
|
-
"host": "{{kebabCase FEATURE_NAME}}.easypanel.host",
|
|
38
|
-
"port": 3000,
|
|
39
|
-
"https": true
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
|
-
"mounts": []
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
name: Deploy to EasyPanel
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_call:
|
|
5
|
-
inputs:
|
|
6
|
-
environment:
|
|
7
|
-
required: true
|
|
8
|
-
type: string
|
|
9
|
-
image:
|
|
10
|
-
required: true
|
|
11
|
-
type: string
|
|
12
|
-
|
|
13
|
-
jobs:
|
|
14
|
-
deploy:
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
environment: ${{{{ inputs.environment }}}}
|
|
17
|
-
steps:
|
|
18
|
-
- name: Trigger EasyPanel Deploy
|
|
19
|
-
run: |
|
|
20
|
-
curl -X POST ${{{{ secrets.EASYPANEL_WEBHOOK_URL }}}} \
|
|
21
|
-
-H "Authorization: Bearer ${{{{ secrets.EASYPANEL_API_TOKEN }}}}" \
|
|
22
|
-
-H "Content-Type: application/json" \
|
|
23
|
-
-d '{
|
|
24
|
-
"image": "${{{{ inputs.image }}}}",
|
|
25
|
-
"environment": "${{{{ inputs.environment }}}}"
|
|
26
|
-
}'
|
|
27
|
-
|
|
28
|
-
- name: Health Check
|
|
29
|
-
run: |
|
|
30
|
-
sleep 30 # Wait for deployment
|
|
31
|
-
curl --retry 10 --retry-delay 5 --retry-connrefused ${{{{ secrets.APP_URL }}}}/health
|