You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: SPEC.md
+27-4Lines changed: 27 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,19 @@ Every service in `docker-compose.yml` is classified by a label:
29
29
30
30
### 2.2 Deploy Lifecycle
31
31
32
-
For each service with `deploy.role=app`, in the order they appear in the compose file:
32
+
The deploy runs as a single transaction — `flow-deploy deploy --tag <sha>` owns the full lifecycle including git operations. The `--tag` value serves double duty: it is both the Docker image tag and the git SHA to checkout.
33
+
34
+
**Pre-flight and git checkout (before any service work):**
35
+
36
+
```
37
+
0a. Dirty check git status --porcelain
38
+
If non-empty → log "working tree is dirty — deploy aborted", exit 1
39
+
0b. Fetch git fetch origin
40
+
0c. Record previous SHA previous_sha = git rev-parse HEAD
**For each service with `deploy.role=app`, in the order they appear in the compose file:**
33
45
34
46
```
35
47
1. Pull new image <compose-command> pull <service>
@@ -43,9 +55,14 @@ For each service with `deploy.role=app`, in the order they appear in the compose
43
55
4b. If unhealthy:
44
56
Stop new container docker stop <new_id> && docker rm <new_id>
45
57
Scale back to 1 <compose-command> up -d --no-deps --scale <service>=1
58
+
Restore repo git checkout --detach <previous_sha>
46
59
✗ Abort deploy, exit 1
47
60
```
48
61
62
+
**On success:** the server is in detached HEAD at `<sha>`. Log `HEAD detached at <sha>`.
63
+
64
+
**On failure:** the repo is restored to `<previous_sha>` before exiting. The invariant is preserved: `git rev-parse HEAD` always matches the image SHA that is actively serving traffic.
65
+
49
66
Where `<compose-command>` is the project's compose wrapper (see §3.1).
50
67
51
68
### 2.3 Graceful Shutdown
@@ -398,9 +415,16 @@ Failure output:
398
415
[12:37:14] rollback complete, old container still serving
[12:34:56] ERROR: working tree is dirty — deploy aborted
426
+
```
427
+
404
428
### 6.1 GitHub Actions Integration
405
429
406
430
Since the tool runs over SSH, output naturally appears in Actions logs. For richer integration, the tool emits GitHub Actions log commands when it detects the `GITHUB_ACTIONS=true` environment variable (passed through SSH):
@@ -421,7 +445,7 @@ For multi-host deploys or when you want host discovery from compose labels, use
421
445
2. Runs `<command> config` to get the fully merged compose YAML
422
446
3. Parses `x-deploy` and `deploy.*` labels to discover hosts
423
447
4. Groups services by host
424
-
5. SSHes to each host: `git pull` → `flow-deploy deploy --tag <tag>`
448
+
5. SSHes to each host: `flow-deploy deploy --tag <tag>`
425
449
6. Streams logs back to GitHub Actions
426
450
427
451
```yaml
@@ -504,11 +528,10 @@ For single-host projects, the action is optional. A raw SSH command works:
This is the simplest possible deploy: one SSH call, no action, no host discovery. The tool runs locally on the server, calls `script/prod`, and handles the rolling deploy.
534
+
This is the simplest possible deploy: one SSH call, no action, no host discovery. The tool handles git operations (fetch, detached checkout), calls `script/prod`, and runs the rolling deploy. No separate `git pull` or `git checkout` is needed — the tool owns the full transaction.
0 commit comments