← Commit history

Initial commit: skill/adom-bluegreen

Ray ·250518d99b ·15d ago
2 files changed +141
README.mdadded+72
@@ -0,0 +1,72 @@+# adom-bluegreen++**Deploy a service container without failing a single request.**++Most of our service containers deploy the same way. A watchdog notices a new commit on `main`, kills+the process and starts the new one. Everything that calls the service fails until the new process has+warmed up. For Component Ocean that took 60–120 seconds per push. For anything production-dependent+that is not acceptable: the bar is **zero failed requests**, not a short gap.++adom-bluegreen is the setup that got Component Ocean there, packaged so any service container can+use it.++| Component Ocean, live | requests during the deploy | failed |+|---|---|---|+| automatic deploy (cron picked up the push) | 1,372 | **0** |+| manual deploy | 2,365 | **0** |+| kit self-test: deploy under load, plus a refused broken build | 13,368 | **0** |++## The idea++```+ public URL ──> :3456  nginx  (the front door; it reloads, it never restarts)+                    ├── :3457  blue   slot  = git worktree at the commit being served+                    └── :3458  green  slot  = git worktree at the next commit, warming up+```++1. **Warm the new version next to the old one.** A new commit starts in the idle slot while the live+   slot keeps serving.+2. **Switch only when the new version says it's ready, at the right commit.** Each service answers+   `GET /ready` with `200 {"ready":true,"git_sha":...,"inflight":n}` once it is fully warm. If the new+   slot never gets there, it is never served, and the old one keeps going.+3. **Switch without closing the door.** nginx reloads its config while keeping the listening socket.+   New requests go to the new slot, and requests already in progress finish on the old one.+4. **Let the old version finish before it stops.** The old slot is stopped only when its `inflight`+   count has been 0 for 5 seconds, so every request it accepted gets its answer.++## Using it on your service++1. Add the `/ready` contract. `examples/` has copy-ready versions for **Bun, Node and Python**: about+   15 lines, including an in-flight counter.+2. Write a small config (`examples/service.conf.example`): name, repo, branch, three ports, the start+   command, and which gitignored paths (such as `data/`) both slots share.+3. Run `scripts/install.sh your.conf`. It checks the config, installs nginx and schedules the watchdog.+4. Prove it: run `scripts/probe.sh` against your service while you push a commit. It must end with+   `0 failed`.++Before relying on it on a new container, run `tests/selftest.sh`. It performs a real deploy of a+throwaway app on spare ports and must report `10 passed, 0 failed`.++The full guide, including operating it, rolling back and adopting a port a service already uses, is+in `SKILL.md`. It installs as a skill, so any Claude or Codex session can set this up for you.++## What we learned the hard way++Each item below failed once while this was built. The kit handles all of them, and the reasons are+written into the code:++- **A process that inherits the watchdog's lock holds it forever.** Every later run exits silently and+  deploys just stop, with no error anywhere. It happened twice.+- **`$!` is the wrapper shell, not your server.** Kill the wrong one and the old server keeps its+  port, so the next deploy can't start.+- **Two processes can't share a port.** Any handoff of the public port is a gap, so the front door is+  never replaced while it serves. nginx changes by reload instead.+- **"The port is open" isn't "ready".** The first version would have switched traffic before a+  search index had loaded.+- **Containers can't do the kernel-level tricks.** There's no `NET_ADMIN`, `/proc/sys` is read-only+  and the public port mapping can't be re-pointed. That's why this is nginx-based.++## Status++Proven on **Component Ocean**. The next step is making it the standard way our service containers+deploy. If you adopt it on your service, run the probe and add your numbers here.
page.jsonadded+69
@@ -0,0 +1,69 @@+{+  "name": "adom-bluegreen",+  "slug": "adom-bluegreen",+  "title": "Adom Blue-Green",+  "version": "0.1.0",+  "type": "skill",+  "description": "Zero-failed-request deploys for any Adom service container: an nginx front door, blue and green slots, a flip only when the new build reports ready at the new commit, and the old slot stopped only after it has served every request it accepted.",+  "brief": "Blue-green deploys with zero failed requests for service containers. Proven on Component Ocean.",+  "license": "MIT",+  "files": [+    "package.json",+    "SKILL.md",+    "README.md",+    "install.sh",+    "uninstall.sh",+    "scripts/install.py",+    "scripts/uninstall.py",+    "skills/**",+    "docs/hero.png",+    "screenshots/showcase.png"+  ],+  "scripts": {+    "install": "./install.sh",+    "uninstall": "./uninstall.sh"+  },+  "dependencies": {},+  "tags": [+    "deploy",+    "blue-green",+    "zero-downtime",+    "service-container",+    "nginx",+    "devops"+  ],+  "discovery_triggers": [+    "blue-green deploy",+    "zero downtime deploy",+    "my service is down while it updates",+    "zero failed requests",+    "hitless deploy",+    "add a /ready endpoint",+    "readiness probe for my service"+  ],+  "org": "adom",+  "discovery_pitch": "Use when a service container is down while it updates: deploys it with zero failed requests.",+  "sample_prompts": [+    {+      "label": "Make my service zero-downtime",+      "prompt": "my service is down for two minutes every time I push; set up adom-bluegreen on it"+    },+    {+      "label": "Add a /ready endpoint",+      "prompt": "add the adom-bluegreen /ready contract to this server, with an in-flight counter"+    },+    {+      "label": "Prove a deploy dropped nothing",+      "prompt": "run the adom-bluegreen probe while I push and tell me how many requests failed"+    }+  ],+  "hero": {+    "type": "image",+    "path": "docs/hero.png"+  },+  "visibility": "public",+  "author": {+    "name": "Ray",+    "email": "[email protected]"+  }+}\ No newline at end of file