@slamb2k/mad-skills 2.0.14 → 2.0.16
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/.claude-plugin/plugin.json +1 -1
- package/README.md +390 -15
- package/package.json +1 -1
- package/skills/brace/SKILL.md +1 -1
- package/skills/build/SKILL.md +6 -2
- package/skills/distil/SKILL.md +1 -1
- package/skills/dock/SKILL.md +357 -0
- package/skills/dock/references/dockerfile-templates.md +358 -0
- package/skills/dock/references/interview-guide.md +209 -0
- package/skills/dock/references/pipeline-templates.md +398 -0
- package/skills/dock/references/platform-deploy-guides.md +457 -0
- package/skills/dock/tests/evals.json +36 -0
- package/skills/keel/SKILL.md +498 -0
- package/skills/keel/references/bicep-templates.md +359 -0
- package/skills/keel/references/iac-pipeline-templates.md +519 -0
- package/skills/keel/references/interview-guide.md +257 -0
- package/skills/keel/references/terraform-templates.md +474 -0
- package/skills/keel/tests/evals.json +35 -0
- package/skills/manifest.json +27 -7
- package/skills/prime/SKILL.md +15 -10
- package/skills/prime/references/domains.md +27 -30
- package/skills/rig/SKILL.md +10 -1
- package/skills/ship/SKILL.md +1 -1
- package/skills/ship/references/stage-prompts.md +5 -13
- package/skills/speccy/SKILL.md +6 -2
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Dock Interview Guide
|
|
2
|
+
|
|
3
|
+
Prompts and questions for the detection and interview phases of /dock.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Detection Prompt
|
|
8
|
+
|
|
9
|
+
**Agent:** Explore (quick)
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Scan the codebase to detect stack, infrastructure, and existing deployment config.
|
|
13
|
+
Report findings in a structured DETECTION_REPORT.
|
|
14
|
+
|
|
15
|
+
Limit DETECTION_REPORT to 30 lines maximum.
|
|
16
|
+
|
|
17
|
+
## What to scan
|
|
18
|
+
|
|
19
|
+
1. **Language & Framework**
|
|
20
|
+
Look for these files (in priority order):
|
|
21
|
+
- package.json → Node.js (check for next, nuxt, express, fastify, etc.)
|
|
22
|
+
- requirements.txt / pyproject.toml / setup.py → Python (check for django, flask, fastapi)
|
|
23
|
+
- go.mod → Go
|
|
24
|
+
- Cargo.toml → Rust
|
|
25
|
+
- Gemfile → Ruby (check for rails, sinatra)
|
|
26
|
+
- pom.xml / build.gradle → Java/Kotlin (check for spring-boot)
|
|
27
|
+
- mix.exs → Elixir
|
|
28
|
+
- composer.json → PHP (check for laravel)
|
|
29
|
+
- *.csproj / *.sln → .NET
|
|
30
|
+
|
|
31
|
+
2. **Package Manager**
|
|
32
|
+
- Node: check for bun.lockb (bun), pnpm-lock.yaml (pnpm), yarn.lock (yarn), package-lock.json (npm)
|
|
33
|
+
- Python: check for uv.lock (uv), poetry.lock (poetry), Pipfile.lock (pipenv), requirements.txt (pip)
|
|
34
|
+
- Others: go.sum, Cargo.lock, Gemfile.lock, etc.
|
|
35
|
+
|
|
36
|
+
3. **Entry Point**
|
|
37
|
+
- package.json scripts.start / scripts.dev
|
|
38
|
+
- Procfile web entry
|
|
39
|
+
- main.go, main.py, app.py, server.js, index.ts, etc.
|
|
40
|
+
- Framework-specific (next start, rails server, uvicorn, gunicorn)
|
|
41
|
+
|
|
42
|
+
4. **Port**
|
|
43
|
+
- Existing Dockerfile EXPOSE
|
|
44
|
+
- Framework defaults (Next.js=3000, Django=8000, FastAPI=8000, Rails=3000, Spring=8080, Go=8080)
|
|
45
|
+
- Environment variable references to PORT
|
|
46
|
+
|
|
47
|
+
5. **Existing Dockerfile**
|
|
48
|
+
- Dockerfile, Dockerfile.*, .dockerignore
|
|
49
|
+
- docker-compose.yml, docker-compose.*.yml
|
|
50
|
+
- Report: exists? multi-stage? base image? what it does
|
|
51
|
+
|
|
52
|
+
6. **Existing CI/CD**
|
|
53
|
+
- .github/workflows/*.yml → GitHub Actions (list workflow names)
|
|
54
|
+
- azure-pipelines.yml → Azure Pipelines
|
|
55
|
+
- .gitlab-ci.yml → GitLab CI
|
|
56
|
+
- Report: what triggers exist, any deployment steps already present
|
|
57
|
+
|
|
58
|
+
7. **Existing Deployment Config**
|
|
59
|
+
- Helm charts (Chart.yaml)
|
|
60
|
+
- Kubernetes manifests (*.yaml with kind: Deployment/Service)
|
|
61
|
+
- fly.toml → Fly.io
|
|
62
|
+
- Procfile → Heroku/Dokku
|
|
63
|
+
- app.json → Heroku
|
|
64
|
+
- captain-definition → CapRover
|
|
65
|
+
- Bicep/ARM templates (*.bicep, azuredeploy.json)
|
|
66
|
+
- Terraform files (*.tf)
|
|
67
|
+
- CDK files (cdk.json)
|
|
68
|
+
- Pulumi files (Pulumi.yaml)
|
|
69
|
+
|
|
70
|
+
8. **Environment Config**
|
|
71
|
+
- .env, .env.example, .env.development, .env.production
|
|
72
|
+
- Environment variable patterns in config files
|
|
73
|
+
|
|
74
|
+
## Output Format
|
|
75
|
+
|
|
76
|
+
DETECTION_REPORT:
|
|
77
|
+
- language: {detected language}
|
|
78
|
+
- framework: {detected framework or "none"}
|
|
79
|
+
- package_manager: {detected package manager}
|
|
80
|
+
- entry_point: {main file or start command}
|
|
81
|
+
- port: {detected port or "unknown"}
|
|
82
|
+
- existing_dockerfile: {yes/no, brief description if yes}
|
|
83
|
+
- existing_ci: {system name or "none", brief description}
|
|
84
|
+
- existing_deploy: {platform or "none", brief description}
|
|
85
|
+
- env_files: {list or "none"}
|
|
86
|
+
- confidence: {high/medium/low — how certain the detection is}
|
|
87
|
+
- notes: {anything ambiguous or noteworthy}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Interview Questions
|
|
93
|
+
|
|
94
|
+
Use AskUserQuestion for each topic. Present detected values as defaults.
|
|
95
|
+
Skip questions where detection provided high-confidence answers (but mention
|
|
96
|
+
what was detected so the user can override).
|
|
97
|
+
|
|
98
|
+
### Registry Selection
|
|
99
|
+
|
|
100
|
+
Present detected registry or suggest based on CI system:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
Based on your {CI_SYSTEM} setup, I'd suggest {SUGGESTED_REGISTRY}.
|
|
104
|
+
|
|
105
|
+
Which container registry should images be pushed to?
|
|
106
|
+
|
|
107
|
+
Options:
|
|
108
|
+
1. GitHub Container Registry (ghcr.io) (Recommended for GitHub repos)
|
|
109
|
+
2. Azure Container Registry
|
|
110
|
+
3. AWS ECR (Elastic Container Registry)
|
|
111
|
+
4. Google Artifact Registry
|
|
112
|
+
5. Docker Hub
|
|
113
|
+
6. Self-hosted (provide URL)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Mapping:
|
|
117
|
+
- GitHub Actions → suggest ghcr.io
|
|
118
|
+
- Azure Pipelines → suggest Azure Container Registry
|
|
119
|
+
- GitLab CI → suggest GitLab Container Registry
|
|
120
|
+
- No CI → suggest ghcr.io (most universal)
|
|
121
|
+
|
|
122
|
+
### Environment Topology
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
How many deployment stages do you need?
|
|
126
|
+
|
|
127
|
+
Options:
|
|
128
|
+
1. Simple: dev → prod (2-stage) (Recommended for small projects)
|
|
129
|
+
2. Standard: dev → staging → prod (3-stage)
|
|
130
|
+
3. Custom: define your own stages
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Target Platform (per environment)
|
|
134
|
+
|
|
135
|
+
Ask once per environment:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
Where should the {ENV_NAME} environment be deployed?
|
|
139
|
+
|
|
140
|
+
Options:
|
|
141
|
+
1. Azure Container Apps (Recommended — serverless containers, auto-scaling)
|
|
142
|
+
2. Azure App Service for Containers
|
|
143
|
+
3. AWS Fargate (ECS)
|
|
144
|
+
4. Google Cloud Run
|
|
145
|
+
5. Kubernetes (any distribution)
|
|
146
|
+
6. Dokku (self-hosted PaaS)
|
|
147
|
+
7. Coolify (self-hosted PaaS)
|
|
148
|
+
8. CapRover (self-hosted PaaS)
|
|
149
|
+
9. Other (specify)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
If user picks the same platform for all environments, note that environment
|
|
153
|
+
separation will be handled via namespaces/resource groups/projects rather than
|
|
154
|
+
distinct platforms.
|
|
155
|
+
|
|
156
|
+
### Testing Gates
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
What tests should gate each environment promotion?
|
|
160
|
+
|
|
161
|
+
I'll set these defaults — adjust as needed:
|
|
162
|
+
|
|
163
|
+
Build stage: unit tests, linting, type checks
|
|
164
|
+
Dev deploy: smoke tests (health check + basic request)
|
|
165
|
+
Staging deploy: integration tests, e2e tests
|
|
166
|
+
Prod deploy: post-deploy smoke test
|
|
167
|
+
|
|
168
|
+
Do these work, or would you like to customize?
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Secrets Management
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
How are deployment secrets managed?
|
|
175
|
+
|
|
176
|
+
Options:
|
|
177
|
+
1. GitHub Secrets / Variables (Recommended for GitHub Actions)
|
|
178
|
+
2. Azure Key Vault
|
|
179
|
+
3. AWS Secrets Manager / Parameter Store
|
|
180
|
+
4. Environment variables in deployment platform
|
|
181
|
+
5. Doppler
|
|
182
|
+
6. 1Password (via CLI)
|
|
183
|
+
7. HashiCorp Vault
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Rollback Strategy
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
What rollback strategy should the pipeline use?
|
|
190
|
+
|
|
191
|
+
Options:
|
|
192
|
+
1. Simple rollback (Recommended) — redeploy previous image tag on failure
|
|
193
|
+
2. Blue-green — maintain two environments, swap traffic on deploy
|
|
194
|
+
3. Canary — gradual traffic shift with automatic rollback on error thresholds
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Networking (optional)
|
|
198
|
+
|
|
199
|
+
Only ask if the deployment target needs explicit networking config:
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
Do you need custom domain configuration?
|
|
203
|
+
|
|
204
|
+
Options:
|
|
205
|
+
1. No — use platform-provided URLs (Recommended for now)
|
|
206
|
+
2. Yes — I have domains for each environment
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
If yes, collect domain per environment and TLS preference (auto/manual).
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# CI/CD Pipeline Templates
|
|
2
|
+
|
|
3
|
+
Templates for the build-once-promote-everywhere pipeline pattern. Each template
|
|
4
|
+
implements the same three-trigger flow:
|
|
5
|
+
- **PR**: build + test (optionally push)
|
|
6
|
+
- **Merge to main**: build + test + push + deploy dev
|
|
7
|
+
- **Release tag**: retag (no rebuild) + deploy staging + deploy prod
|
|
8
|
+
|
|
9
|
+
Template variables:
|
|
10
|
+
- `{REGISTRY}`: Full registry URL (e.g., ghcr.io/owner/repo)
|
|
11
|
+
- `{IMAGE_NAME}`: Image name without registry (e.g., my-app)
|
|
12
|
+
- `{DEFAULT_BRANCH}`: main or master
|
|
13
|
+
- `{PORT}`: Application port
|
|
14
|
+
- `{DEV_TARGET}`, `{STAGING_TARGET}`, `{PROD_TARGET}`: Deployment platform per env
|
|
15
|
+
- `{TEST_CMD}`: Test command (e.g., npm test, pytest, go test ./...)
|
|
16
|
+
- `{BUILD_CMD}`: Build command if separate from Dockerfile (optional)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## GitHub Actions
|
|
21
|
+
|
|
22
|
+
### Build & Deploy Workflow
|
|
23
|
+
|
|
24
|
+
File: `.github/workflows/deploy.yml`
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
name: Build & Deploy
|
|
28
|
+
|
|
29
|
+
on:
|
|
30
|
+
pull_request:
|
|
31
|
+
push:
|
|
32
|
+
branches: [{DEFAULT_BRANCH}]
|
|
33
|
+
tags: ["v*"]
|
|
34
|
+
|
|
35
|
+
concurrency:
|
|
36
|
+
group: deploy-${{ github.ref }}
|
|
37
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
38
|
+
|
|
39
|
+
env:
|
|
40
|
+
REGISTRY: {REGISTRY}
|
|
41
|
+
IMAGE_NAME: {IMAGE_NAME}
|
|
42
|
+
|
|
43
|
+
permissions:
|
|
44
|
+
contents: read
|
|
45
|
+
packages: write
|
|
46
|
+
|
|
47
|
+
jobs:
|
|
48
|
+
# ── Build & Test ──────────────────────────────────────────────
|
|
49
|
+
build:
|
|
50
|
+
runs-on: ubuntu-latest
|
|
51
|
+
# Skip for release tags — we retag, not rebuild
|
|
52
|
+
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
|
53
|
+
outputs:
|
|
54
|
+
image_tag: ${{ steps.meta.outputs.tags }}
|
|
55
|
+
sha_short: ${{ steps.vars.outputs.sha_short }}
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
|
|
59
|
+
- name: Set variables
|
|
60
|
+
id: vars
|
|
61
|
+
run: echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
|
62
|
+
|
|
63
|
+
- name: Docker meta
|
|
64
|
+
id: meta
|
|
65
|
+
uses: docker/metadata-action@v5
|
|
66
|
+
with:
|
|
67
|
+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
68
|
+
tags: |
|
|
69
|
+
type=sha,prefix=
|
|
70
|
+
type=ref,event=pr,prefix=pr-
|
|
71
|
+
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', '{DEFAULT_BRANCH}') }}
|
|
72
|
+
|
|
73
|
+
- uses: docker/setup-buildx-action@v3
|
|
74
|
+
|
|
75
|
+
- name: Login to registry
|
|
76
|
+
uses: docker/login-action@v3
|
|
77
|
+
with:
|
|
78
|
+
registry: ${{ env.REGISTRY }}
|
|
79
|
+
username: ${{ github.actor }}
|
|
80
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
81
|
+
|
|
82
|
+
- name: Build and test
|
|
83
|
+
uses: docker/build-push-action@v6
|
|
84
|
+
with:
|
|
85
|
+
context: .
|
|
86
|
+
target: test
|
|
87
|
+
cache-from: type=gha
|
|
88
|
+
cache-to: type=gha,mode=max
|
|
89
|
+
|
|
90
|
+
- name: Build and push production image
|
|
91
|
+
if: github.event_name == 'push'
|
|
92
|
+
uses: docker/build-push-action@v6
|
|
93
|
+
with:
|
|
94
|
+
context: .
|
|
95
|
+
target: production
|
|
96
|
+
push: true
|
|
97
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
98
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
99
|
+
cache-from: type=gha
|
|
100
|
+
cache-to: type=gha,mode=max
|
|
101
|
+
|
|
102
|
+
# ── Deploy to Dev ─────────────────────────────────────────────
|
|
103
|
+
deploy-dev:
|
|
104
|
+
needs: build
|
|
105
|
+
if: github.ref == 'refs/heads/{DEFAULT_BRANCH}'
|
|
106
|
+
runs-on: ubuntu-latest
|
|
107
|
+
environment: dev
|
|
108
|
+
steps:
|
|
109
|
+
- uses: actions/checkout@v4
|
|
110
|
+
|
|
111
|
+
- name: Deploy to dev
|
|
112
|
+
run: |
|
|
113
|
+
# {DEV_DEPLOY_COMMANDS}
|
|
114
|
+
echo "Deploying ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.sha_short }} to dev"
|
|
115
|
+
|
|
116
|
+
- name: Smoke test
|
|
117
|
+
run: |
|
|
118
|
+
# Wait for deployment to be ready, then hit health endpoint
|
|
119
|
+
sleep 10
|
|
120
|
+
curl -f "$DEV_URL/healthz" || exit 1
|
|
121
|
+
|
|
122
|
+
# ── Promote to Staging (on release tag) ───────────────────────
|
|
123
|
+
promote-staging:
|
|
124
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
125
|
+
runs-on: ubuntu-latest
|
|
126
|
+
environment: staging
|
|
127
|
+
outputs:
|
|
128
|
+
version: ${{ steps.version.outputs.tag }}
|
|
129
|
+
steps:
|
|
130
|
+
- uses: actions/checkout@v4
|
|
131
|
+
|
|
132
|
+
- name: Extract version
|
|
133
|
+
id: version
|
|
134
|
+
run: echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
|
135
|
+
|
|
136
|
+
- name: Login to registry
|
|
137
|
+
uses: docker/login-action@v3
|
|
138
|
+
with:
|
|
139
|
+
registry: ${{ env.REGISTRY }}
|
|
140
|
+
username: ${{ github.actor }}
|
|
141
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
142
|
+
|
|
143
|
+
- name: Retag image (no rebuild)
|
|
144
|
+
run: |
|
|
145
|
+
SHA=$(git rev-parse --short HEAD)
|
|
146
|
+
docker buildx imagetools create \
|
|
147
|
+
--tag "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}" \
|
|
148
|
+
"${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${SHA}"
|
|
149
|
+
|
|
150
|
+
- name: Deploy to staging
|
|
151
|
+
run: |
|
|
152
|
+
# {STAGING_DEPLOY_COMMANDS}
|
|
153
|
+
echo "Deploying ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }} to staging"
|
|
154
|
+
|
|
155
|
+
- name: Run staging tests
|
|
156
|
+
run: |
|
|
157
|
+
# Integration and e2e tests against staging
|
|
158
|
+
echo "Running staging gate tests..."
|
|
159
|
+
|
|
160
|
+
# ── Promote to Production ─────────────────────────────────────
|
|
161
|
+
promote-prod:
|
|
162
|
+
needs: promote-staging
|
|
163
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
164
|
+
runs-on: ubuntu-latest
|
|
165
|
+
environment: production
|
|
166
|
+
steps:
|
|
167
|
+
- uses: actions/checkout@v4
|
|
168
|
+
|
|
169
|
+
- name: Deploy to production
|
|
170
|
+
run: |
|
|
171
|
+
# {PROD_DEPLOY_COMMANDS}
|
|
172
|
+
echo "Deploying ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.promote-staging.outputs.version }} to production"
|
|
173
|
+
|
|
174
|
+
- name: Post-deploy smoke test
|
|
175
|
+
run: |
|
|
176
|
+
sleep 10
|
|
177
|
+
curl -f "$PROD_URL/healthz" || exit 1
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Reusable deploy step patterns
|
|
181
|
+
|
|
182
|
+
The `{*_DEPLOY_COMMANDS}` placeholders are replaced with platform-specific
|
|
183
|
+
commands from `platform-deploy-guides.md`. See that file for per-platform
|
|
184
|
+
deployment steps.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Azure DevOps Pipelines
|
|
189
|
+
|
|
190
|
+
File: `azure-pipelines.yml`
|
|
191
|
+
|
|
192
|
+
```yaml
|
|
193
|
+
trigger:
|
|
194
|
+
branches:
|
|
195
|
+
include: [{DEFAULT_BRANCH}]
|
|
196
|
+
tags:
|
|
197
|
+
include: ["v*"]
|
|
198
|
+
|
|
199
|
+
pr:
|
|
200
|
+
branches:
|
|
201
|
+
include: [{DEFAULT_BRANCH}]
|
|
202
|
+
|
|
203
|
+
variables:
|
|
204
|
+
registry: {REGISTRY}
|
|
205
|
+
imageName: {IMAGE_NAME}
|
|
206
|
+
vmImageName: ubuntu-latest
|
|
207
|
+
|
|
208
|
+
stages:
|
|
209
|
+
# ── Build & Test ──────────────────────────────────────────────
|
|
210
|
+
- stage: Build
|
|
211
|
+
condition: not(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
|
212
|
+
jobs:
|
|
213
|
+
- job: BuildAndTest
|
|
214
|
+
pool:
|
|
215
|
+
vmImage: $(vmImageName)
|
|
216
|
+
steps:
|
|
217
|
+
- task: Docker@2
|
|
218
|
+
displayName: Build test image
|
|
219
|
+
inputs:
|
|
220
|
+
command: build
|
|
221
|
+
dockerfile: Dockerfile
|
|
222
|
+
arguments: --target test -t $(imageName):test
|
|
223
|
+
|
|
224
|
+
- task: Docker@2
|
|
225
|
+
displayName: Build production image
|
|
226
|
+
inputs:
|
|
227
|
+
command: build
|
|
228
|
+
dockerfile: Dockerfile
|
|
229
|
+
arguments: --target production -t $(registry)/$(imageName):$(Build.SourceVersion)
|
|
230
|
+
|
|
231
|
+
- task: Docker@2
|
|
232
|
+
displayName: Push to registry
|
|
233
|
+
condition: eq(variables['Build.SourceBranch'], 'refs/heads/{DEFAULT_BRANCH}')
|
|
234
|
+
inputs:
|
|
235
|
+
command: push
|
|
236
|
+
containerRegistry: $(dockerRegistryServiceConnection)
|
|
237
|
+
repository: $(imageName)
|
|
238
|
+
tags: |
|
|
239
|
+
$(Build.SourceVersion)
|
|
240
|
+
latest
|
|
241
|
+
|
|
242
|
+
# ── Deploy to Dev ─────────────────────────────────────────────
|
|
243
|
+
- stage: DeployDev
|
|
244
|
+
condition: eq(variables['Build.SourceBranch'], 'refs/heads/{DEFAULT_BRANCH}')
|
|
245
|
+
dependsOn: Build
|
|
246
|
+
jobs:
|
|
247
|
+
- deployment: DeployDev
|
|
248
|
+
environment: dev
|
|
249
|
+
pool:
|
|
250
|
+
vmImage: $(vmImageName)
|
|
251
|
+
strategy:
|
|
252
|
+
runOnce:
|
|
253
|
+
deploy:
|
|
254
|
+
steps:
|
|
255
|
+
- script: |
|
|
256
|
+
echo "Deploying $(registry)/$(imageName):$(Build.SourceVersion) to dev"
|
|
257
|
+
# {DEV_DEPLOY_COMMANDS}
|
|
258
|
+
|
|
259
|
+
# ── Promote to Staging ────────────────────────────────────────
|
|
260
|
+
- stage: PromoteStaging
|
|
261
|
+
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
|
262
|
+
jobs:
|
|
263
|
+
- deployment: DeployStaging
|
|
264
|
+
environment: staging
|
|
265
|
+
pool:
|
|
266
|
+
vmImage: $(vmImageName)
|
|
267
|
+
strategy:
|
|
268
|
+
runOnce:
|
|
269
|
+
deploy:
|
|
270
|
+
steps:
|
|
271
|
+
- script: |
|
|
272
|
+
TAG=${BUILD_SOURCEBRANCH#refs/tags/}
|
|
273
|
+
echo "Retagging and deploying $TAG to staging"
|
|
274
|
+
# Retag — do NOT rebuild
|
|
275
|
+
# {STAGING_DEPLOY_COMMANDS}
|
|
276
|
+
|
|
277
|
+
# ── Promote to Production ─────────────────────────────────────
|
|
278
|
+
- stage: PromoteProd
|
|
279
|
+
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
|
280
|
+
dependsOn: PromoteStaging
|
|
281
|
+
jobs:
|
|
282
|
+
- deployment: DeployProd
|
|
283
|
+
environment: production
|
|
284
|
+
pool:
|
|
285
|
+
vmImage: $(vmImageName)
|
|
286
|
+
strategy:
|
|
287
|
+
runOnce:
|
|
288
|
+
deploy:
|
|
289
|
+
steps:
|
|
290
|
+
- script: |
|
|
291
|
+
TAG=${BUILD_SOURCEBRANCH#refs/tags/}
|
|
292
|
+
echo "Deploying $TAG to production"
|
|
293
|
+
# {PROD_DEPLOY_COMMANDS}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## GitLab CI
|
|
299
|
+
|
|
300
|
+
File: `.gitlab-ci.yml`
|
|
301
|
+
|
|
302
|
+
```yaml
|
|
303
|
+
stages:
|
|
304
|
+
- build
|
|
305
|
+
- deploy-dev
|
|
306
|
+
- promote-staging
|
|
307
|
+
- promote-prod
|
|
308
|
+
|
|
309
|
+
variables:
|
|
310
|
+
REGISTRY: {REGISTRY}
|
|
311
|
+
IMAGE_NAME: {IMAGE_NAME}
|
|
312
|
+
|
|
313
|
+
# ── Build & Test ──────────────────────────────────────────────
|
|
314
|
+
build:
|
|
315
|
+
stage: build
|
|
316
|
+
image: docker:27
|
|
317
|
+
services:
|
|
318
|
+
- docker:27-dind
|
|
319
|
+
rules:
|
|
320
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
321
|
+
- if: $CI_COMMIT_BRANCH == "{DEFAULT_BRANCH}"
|
|
322
|
+
script:
|
|
323
|
+
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
324
|
+
- docker build --target test -t $IMAGE_NAME:test .
|
|
325
|
+
- docker build --target production -t $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA .
|
|
326
|
+
- |
|
|
327
|
+
if [ "$CI_COMMIT_BRANCH" = "{DEFAULT_BRANCH}" ]; then
|
|
328
|
+
docker tag $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA $REGISTRY/$IMAGE_NAME:latest
|
|
329
|
+
docker push $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA
|
|
330
|
+
docker push $REGISTRY/$IMAGE_NAME:latest
|
|
331
|
+
fi
|
|
332
|
+
|
|
333
|
+
# ── Deploy to Dev ─────────────────────────────────────────────
|
|
334
|
+
deploy-dev:
|
|
335
|
+
stage: deploy-dev
|
|
336
|
+
rules:
|
|
337
|
+
- if: $CI_COMMIT_BRANCH == "{DEFAULT_BRANCH}"
|
|
338
|
+
environment:
|
|
339
|
+
name: dev
|
|
340
|
+
script:
|
|
341
|
+
- echo "Deploying $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA to dev"
|
|
342
|
+
# {DEV_DEPLOY_COMMANDS}
|
|
343
|
+
|
|
344
|
+
# ── Promote to Staging ────────────────────────────────────────
|
|
345
|
+
promote-staging:
|
|
346
|
+
stage: promote-staging
|
|
347
|
+
rules:
|
|
348
|
+
- if: $CI_COMMIT_TAG =~ /^v/
|
|
349
|
+
environment:
|
|
350
|
+
name: staging
|
|
351
|
+
script:
|
|
352
|
+
- echo "Retagging $CI_COMMIT_SHORT_SHA as $CI_COMMIT_TAG — no rebuild"
|
|
353
|
+
# Retag in registry, then deploy
|
|
354
|
+
# {STAGING_DEPLOY_COMMANDS}
|
|
355
|
+
|
|
356
|
+
# ── Promote to Production ─────────────────────────────────────
|
|
357
|
+
promote-prod:
|
|
358
|
+
stage: promote-prod
|
|
359
|
+
rules:
|
|
360
|
+
- if: $CI_COMMIT_TAG =~ /^v/
|
|
361
|
+
environment:
|
|
362
|
+
name: production
|
|
363
|
+
needs: [promote-staging]
|
|
364
|
+
script:
|
|
365
|
+
- echo "Deploying $CI_COMMIT_TAG to production"
|
|
366
|
+
# {PROD_DEPLOY_COMMANDS}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## docker-compose.yml (Local Development)
|
|
372
|
+
|
|
373
|
+
Always generate this for local dev parity:
|
|
374
|
+
|
|
375
|
+
```yaml
|
|
376
|
+
services:
|
|
377
|
+
app:
|
|
378
|
+
build:
|
|
379
|
+
context: .
|
|
380
|
+
target: build # Use build stage for dev (includes dev deps)
|
|
381
|
+
ports:
|
|
382
|
+
- "{PORT}:{PORT}"
|
|
383
|
+
volumes:
|
|
384
|
+
- .:/app
|
|
385
|
+
- /app/node_modules # Prevent overwriting container's node_modules
|
|
386
|
+
environment:
|
|
387
|
+
- NODE_ENV=development
|
|
388
|
+
env_file:
|
|
389
|
+
- .env
|
|
390
|
+
restart: unless-stopped
|
|
391
|
+
healthcheck:
|
|
392
|
+
test: ["CMD", "wget", "--spider", "-q", "http://localhost:{PORT}/healthz"]
|
|
393
|
+
interval: 10s
|
|
394
|
+
timeout: 3s
|
|
395
|
+
start_period: 10s
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Adapt the volumes, environment, and healthcheck for the detected stack.
|