Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 2 additions & 27 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Deploy

# Job structure (consolidated 2026-04-30; see runbook for rationale):
# prepare → discover stacks, capture previous SHA, classify removed/existing/new
# deploy → skip-gate, teardown, tree update, registry login, dockge, all stacks
# deploy → skip-gate, teardown, tree update, registry login, all stacks
# health-check → validate critical stacks (separate job for distinct timeout profile)
# rollback → git reset to previous SHA + redeploy on deploy/health failure
# notify → Discord notification (always)
Expand All @@ -19,11 +19,6 @@ on:
description: "Absolute path to the live compose tree on the runner host"
required: true
type: string
live-dockge-path:
description: "Absolute path to dockge's compose tree (when has-dockge=true)"
required: false
type: string
default: /opt/dockge
Comment on lines -22 to -26

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High · Caller deploys fail to start

When a caller still passes either removed input when this workflow version is invoked, gitHub rejects the reusable-workflow call before any jobs run because the caller supplies an input that is no longer declared, so that repository cannot deploy.

Merge the caller updates that remove both inputs before merging this workflow change.

Also at .github/workflows/deploy.yml:39.

Prompt for AI agents
In `.github/workflows/deploy.yml` at lines 22-26:

**Caller deploys fail to start**

When a caller still passes either removed input when this workflow version is invoked, gitHub rejects the reusable-workflow call before any jobs run because the caller supplies an input that is no longer declared, so that repository cannot deploy.

Merge the caller updates that remove both inputs before merging this workflow change.

Also at `.github/workflows/deploy.yml:39`.

repo-name:
required: true
type: string
Expand All @@ -36,9 +31,6 @@ on:
target-ref:
required: true
type: string
has-dockge:
type: boolean
default: false
shared-networks:
description: "Space-separated bridge networks to create (if missing) before any stack starts, for stacks that join them as `external: true`. Created outside any Compose project, so no stack's `down` can remove them."
type: string
Expand Down Expand Up @@ -261,7 +253,7 @@ jobs:

deploy:
# Consolidated deploy phase. Replaces 7 prior jobs:
# teardown-removed, update-tree, registry-login, deploy-dockge,
# teardown-removed, update-tree, registry-login, deploy-dockge (dockge since removed),
# deploy-existing (matrix), deploy-new (matrix), cleanup-failed-new.
# Single self-hosted runner serializes matrix entries anyway, so the
# collapse just removes job cold-start overhead with no parallelism loss.
Expand All @@ -276,7 +268,6 @@ jobs:
new_failed_stacks: ${{ steps.summary.outputs.new_failed_stacks }}
env:
LIVE_REPO_PATH: ${{ inputs.live-repo-path }}
DOCKGE_PATH: ${{ inputs.live-dockge-path }}
TARGET_REF: ${{ inputs.target-ref }}
FORCE_DEPLOY: ${{ inputs.force-deploy || inputs.force-recreate }}
# Expanded as ${RECREATE_FLAG:+"$RECREATE_FLAG"} so an empty value adds
Expand All @@ -286,7 +277,6 @@ jobs:
IMAGE_PULL_TIMEOUT: ${{ inputs.image-pull-timeout }}
IMAGE_PULL_ATTEMPTS: ${{ inputs.image-pull-attempts }}
FAILED_LOG_LINES: ${{ inputs.failed-container-log-lines }}
HAS_DOCKGE: ${{ inputs.has-dockge }}
SHARED_NETWORKS: ${{ inputs.shared-networks }}
HAS_REMOVED: ${{ needs.prepare.outputs.has_removed_stacks }}
HAS_EXISTING: ${{ needs.prepare.outputs.has_existing_stacks }}
Expand Down Expand Up @@ -552,21 +542,6 @@ jobs:
read -ra nets <<< "$SHARED_NETWORKS"
./.compose-workflow/scripts/deployment/ensure-shared-networks.sh "${nets[@]}"

- name: Deploy dockge
if: |
steps.skip-gate.outputs.skipped == 'false'
&& env.HAS_DOCKGE == 'true'
run: |
set -euo pipefail
# shellcheck source=/dev/null
source "$RUNNER_TEMP/compose-pull.sh"
cd "$DOCKGE_PATH"
compose_pull_retry "$LIVE_REPO_PATH/compose.env"
timeout "$SERVICE_STARTUP_TIMEOUT" op run --no-masking --env-file="$LIVE_REPO_PATH/compose.env" -- \
docker compose up -d --build --quiet-pull --quiet-build \
--wait --wait-timeout "$COMPOSE_WAIT_TIMEOUT" --remove-orphans \
${RECREATE_FLAG:+"$RECREATE_FLAG"}

- name: Deploy existing stacks
id: deploy-existing
if: |
Expand Down
5 changes: 2 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,15 @@ Three reusable workflows live in `.github/workflows/`:
- **Runs on**: `[self-hosted, <runner-label>]` (e.g. `[self-hosted, piwine]`)
- **5 jobs** (consolidated 2026-04-30 from a prior 11-job structure — single-runner concurrency=1 means matrices serialize anyway):
1. **`prepare`** — discover stacks, capture previous SHA, classify removed/existing/new, detect critical stacks
2. **`deploy`** — skip-gate, teardown removed, update tree from workspace (no `git fetch` from origin needed; runner has no GitHub creds), 1P configure, multi-registry login (ghcr/dockerhub/gitlab/gitlab-zenterprise), dockge (optional), ensure shared networks (optional), existing stacks, new stacks, cleanup-on-failure, summary outputs
2. **`deploy`** — skip-gate, teardown removed, update tree from workspace (no `git fetch` from origin needed; runner has no GitHub creds), 1P configure, multi-registry login (ghcr/dockerhub/gitlab/gitlab-zenterprise), ensure shared networks (optional), existing stacks, new stacks, cleanup-on-failure, summary outputs
3. **`health-check`** — validate critical stacks via inline `docker compose ps -a` parsing (no separate script). Skips one-shot exit-0 containers (e.g. migration sidecars gated via `service_completed_successfully`)
4. **`rollback`** — `git reset --hard <previous_sha>` + redeploy if `deploy` or `health-check` failed
5. **`notify`** — Discord webhook with status, pipeline icon line, and PR comment posting (when invoked from a PR-triggering chain)
- **Key inputs**:
- `runner-label` — e.g. `piwine`, `piwine-office`, `zendc`. Combined with implicit `self-hosted`
- `live-repo-path` — absolute path on the runner host (typically `/opt/compose`)
- `live-dockge-path` — absolute path to dockge tree (when `has-dockge: true`)
- `repo-name`, `webhook-url`, `discord-user-id`, `target-ref`
- `has-dockge` — boolean (`false` everywhere since dockge was retired on piwine/piwine-office, 2026-10)
- (`has-dockge` / `live-dockge-path` were removed after dockge was retired on every host, 2026-10; callers must not pass them)
- `shared-networks` — space-separated cross-stack networks (e.g. `"proxy backup"` on piwine), created by `ensure-shared-networks.sh` in both `deploy` and `rollback` before any `up`; labelled `com.compose-workflow.shared=true` so `docker-prune` skips them
- `force-deploy` — skip the "already at target SHA" gate
- `auto-detect-critical` — read `com.compose.tier: infrastructure` labels (default: true)
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,11 @@ jobs:
webhook-url: "op://Docker/discord-github-notifications/piwine_webhook_url"
discord-user-id: "op://Docker/discord-github-notifications/user_id"
target-ref: ${{ github.event.workflow_run.head_sha || github.sha }}
has-dockge: false # true only if the host runs a dockge tree
shared-networks: "proxy backup" # optional; omit when no stack needs one
force-deploy: ${{ inputs.force-deploy || false }}
```

Optional inputs: `live-dockge-path` (when `has-dockge: true`), `shared-networks` (see below), `auto-detect-critical` (default `true`), `critical-services` (manual override), `image-pull-timeout`, `image-pull-attempts`, `service-startup-timeout`, `failed-container-log-lines`.
Optional inputs: `shared-networks` (see below), `auto-detect-critical` (default `true`), `critical-services` (manual override), `image-pull-timeout`, `image-pull-attempts`, `service-startup-timeout`, `failed-container-log-lines`.

### Shared networks

Expand Down
8 changes: 3 additions & 5 deletions docs/superpowers/runbooks/adding-a-new-host.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ Pick these before starting and substitute throughout:
| `<label>` | runner label (unique per host) |
| `<admin>` | human admin / SSH user on the host |
| `<arch>` | `linux/amd64` or `linux/arm64/v8` (lint) + `x64`/`arm64` (runner tarball) |
| `<has-dockge>` | `false` (dockge was retired on every host in 2026-10; `true` only for a host that still runs a dockge tree) |
| `<webhook>` | Discord webhook 1P ref |

---
Expand All @@ -34,7 +33,7 @@ datacenter/cloud). The repo needs:
.github/actionlint.yaml # declares the <label> runner label
.github/renovate.json # extends github>owine/compose-workflow
.github/workflows/lint.yml # calls compose-lint.yml; platforms: "<arch>"
.github/workflows/deploy.yml # calls deploy.yml; runner-label/has-dockge/webhook
.github/workflows/deploy.yml # calls deploy.yml; runner-label/webhook
.gitignore # MUST include `!compose.env` after `*.env`
.yamllint
compose.env # op:// references — MUST be tracked (force-add)
Expand All @@ -46,9 +45,8 @@ Gotchas:
`!compose.env` negation so the file (op:// references only, no secret values)
stays in the repo. The deploy reads `$LIVE_REPO_PATH/compose.env` and the live
tree is reset to repo HEAD — an untracked env file would never reach the host.
- `deploy.yml`: set `runner-label: <label>`, `has-dockge: <has-dockge>`,
`concurrency.group: deploy-<label>`, `live-repo-path: /opt/compose` (omit
`live-dockge-path` when `has-dockge: false`). If stacks must talk across
- `deploy.yml`: set `runner-label: <label>`,
`concurrency.group: deploy-<label>`, `live-repo-path: /opt/compose`. If stacks must talk across
Compose projects (e.g. a reverse proxy), list those networks in
`shared-networks` and declare them `external: true` in each stack.
- `lint.yml`: set `platforms: "<arch>"` and `repo-name`/`target-repository`.
Expand Down