Skip to content

fix(deploy): resolve compose project from container labels for no-env calls - #101

Merged
owine merged 3 commits into
mainfrom
fix/compose-project-from-labels
Oct 10, 2026
Merged

owine merged 3 commits into
mainfrom
fix/compose-project-from-labels

Conversation

@owine

@owine owine commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Follow-up to #99, from an independent review of 82ffe9a (no blockers; these were the latent findings). Land before Renovate bumps the consumer pins past b293264, so callers pick up both together.

Changes

  • compose_p helper (installed per job into $RUNNER_TEMP/compose-project.sh, three identical copies in deploy / health-check / rollback): resolves the stack's Compose project from the containers' com.docker.compose.project.working_dir label (fallback: stack name) and runs docker compose -p <project> from /. Replaces -p "$stack" at all 9 no-env call sites (removed-stack teardown, 3× diagnostics dc, 2× failure cleanup, health check, per-stack + whole-tree rollback teardown).
    • Fixes review findings 1–2: -p "$stack" silently no-op'd (exit 0, orphaned containers) when Compose normalises the dir name (., uppercase, leading _/- — all allowed by the stack regex) or a file sets top-level name:.
    • awk 'NR==1' not head (no SIGPIPE under pipefail); a failing docker ps falls back instead of aborting.
  • Finding 4: teardowns no longer require the compose file — down targets the project label; a missing file is now a ::notice::.
  • Finding 5: health check validates stack names like the other loops.
  • Finding 6: dropped redundant export -f dump_failed_stack (only called from subshells).
  • Finding 3: ensure-shared-networks.sh documents that label-based down removes a project-labelled network even after the file marks it external (only when idle).
  • CLAUDE.md: documents compose_p.

Verification

  • yamllint OK; shellcheck clean on every changed step (only pre-existing SC2164 on untouched cd lines).
  • Helper copies byte-identical across the 3 jobs.
  • Local throwaway projects: dir My.Stack → resolves mystack, ps finds it, down removes it (previously a silent no-op); top-level name: someothername → resolves + removed; unknown stack → fallback, 0 rows; trailing slash on LIVE_REPO_PATH handled.
  • Read-only on both Pis: helper resolves all 15 piwine stacks + office dozzle to the expected project with correct container counts; extracted health-check script → status=healthy on both.

Summary by Sourcery

Resolve no-environment Compose projects from container labels to make diagnostics, health checks, and cleanup reliable across normalized or explicitly named stacks.

Bug Fixes:

  • Resolve Docker Compose project names from container labels so no-environment diagnostics, health checks, and teardowns work with normalized directory names and top-level project names.
  • Allow stack teardown by project label even when the compose file is missing, while preventing operations from targeting a same-named project in another directory.
  • Validate health-check stack names consistently with deployment and rollback flows.

Enhancements:

  • Centralize no-environment Compose operations in a shared per-job helper and document its behavior.
  • Improve rollback and failure-cleanup handling while removing redundant shell-function exports.
  • Document the impact of label-based teardown on project-labelled shared networks.

Documentation:

  • Document the compose project helper and label-based project resolution behavior.

… calls

Follow-up to #99 review. ps/logs/down that run without `op run` now go
through a compose_p helper (installed per job into $RUNNER_TEMP) that reads
the project name from the containers' com.docker.compose.project.working_dir
label, falling back to the stack name. `-p "$stack"` silently missed when
Compose normalises the dir name (dots, uppercase, leading _/-) or a compose
file sets a top-level name:, leaving teardown/rollback a no-op (exit 0) and
orphaning containers.

Also: teardowns no longer require the compose file to exist (down targets the
project label; missing file is now a notice), the health check validates stack
names like the other loops, redundant export -f removed, and
ensure-shared-networks documents that label-based down removes a
project-labelled network even after it is marked external.
@sourcery-ai

sourcery-ai Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

The workflow now determines the effective Docker Compose project from container labels for all no-environment ps, logs, and down operations, preventing silent misses caused by Compose name normalization or top-level name: values while avoiding compose-file parsing; teardown also proceeds when files are missing, with related validation and documentation updates.

Sequence diagram for label-based Compose project resolution

sequenceDiagram
    participant Workflow
    participant Helper as compose_p
    participant Docker
    participant Compose

    Workflow->>Helper: compose_p(stack, args)
    Helper->>Docker: ps -a filtered by working_dir label
    Docker-->>Helper: Effective project label
    alt matching containers found
        Helper->>Compose: docker compose -p project args from /
    else no matching containers or ps failure
        Helper->>Compose: docker compose -p stack args from /
    end
    Compose-->>Workflow: ps, logs, or down result
Loading

File-Level Changes

Change Details Files
Resolve Compose project names from container metadata for compose operations that run without environment injection.
  • Install identical per-job helpers that query com.docker.compose.project.working_dir and project labels, with stack-name fallback.
  • Route no-env diagnostics, health checks, failure cleanup, removed-stack teardown, and rollback teardown through compose_p from /.
  • Avoid parsing compose files during these operations, including files requiring unavailable ${VARS} values.
  • Use awk safely under pipefail and tolerate failed docker ps lookups.
.github/workflows/deploy.yml
Make teardown behavior independent of compose-file availability and validate health-check inputs consistently.
  • Attempt label-based teardown even when a compose file is missing, emitting notices instead of skipping.
  • Reject invalid critical stack names before health-check operations.
  • Remove the unnecessary exported diagnostic function.
.github/workflows/deploy.yml
Document the network and workflow implications of label-based Compose teardown.
  • Document that project-labelled shared networks may be removed by down once idle and should be recreated without the project label when appropriate.
  • Document the compose_p helper and the three copies that must remain synchronized.
scripts/deployment/ensure-shared-networks.sh
CLAUDE.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 4 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path=".github/workflows/deploy.yml" line_range="407" />
<code_context>
+            [[ -n "$compose_file" ]] \
+              || echo "::notice::compose file missing for removed stack $stack; tearing down by project label"
+            echo "🛑 Stopping $stack"
+            compose_p "$stack" down || echo "::warning::down failed for $stack"
           done

</code_context>
<issue_to_address>
**Teardown stops a sibling stack**

When two stack directories resolve to the same Compose project name, `compose_project` resolves a project name from one stack's working-directory label, but `docker compose down` acts on that project name, not that working directory. When two stack directories normalize to the same project name or specify the same top-level `name:`, tearing down either one also stops the other stack's containers.

Reject duplicate resolved project names before deployment, or make teardown target only containers belonging to the selected working directory.

Also at `.github/workflows/deploy.yml:696`, `.github/workflows/deploy.yml:801`, `.github/workflows/deploy.yml:1224`, `.github/workflows/deploy.yml:1296`.
</issue_to_address>

### Comment 2
<location path=".github/workflows/deploy.yml" line_range="1296" />
<code_context>
+            [[ -n "$compose_file" ]] \
+              || echo "::notice::compose file missing for removed stack $stack; tearing down by project label"
+            echo "🛑 Stopping $stack"
+            compose_p "$stack" down || echo "::warning::down failed for $stack"
           done

</code_context>
<issue_to_address>
**Rollback stacks fail to restart**

When a rollback tears down a stack while a project-labelled network is idle, configured as shared or external, and needed by a restored stack, `compose_p`'s `down` removes the network. Rollback's earlier `ensure-shared-networks.sh` runs before these teardowns and is not rerun, so a later `up` fails.

Preserve shared or external networks during teardown, or rerun `ensure-shared-networks.sh` after teardown and before redeployment.

Also at `.github/workflows/deploy.yml:1224`.
</issue_to_address>

### Comment 3
<location path=".github/workflows/deploy.yml" line_range="362" />
<code_context>
+            p=$(docker ps -a \
+                  --filter "label=com.docker.compose.project.working_dir=${LIVE_REPO_PATH%/}/$1" \
+                  --format '{{.Label "com.docker.compose.project"}}' 2>/dev/null \
+                  | sort -u | awk 'NR == 1') || p=""
+            printf '%s\n' "${p:-$1}"
+          }
</code_context>
<issue_to_address>
**Other project containers remain running**

When containers from multiple Compose project names share a stack's working-directory label, such as after a project-name change, `compose_project` sorts the labels and returns only the first, so `compose_p` runs health checks or `down` against just that project; the other project's containers remain running or the wrong containers are checked.

Handle all matching project labels, or uniquely identify the intended project instead of silently choosing the first.

Also at `.github/workflows/deploy.yml:860-862`, `.github/workflows/deploy.yml:1083-1084`.
</issue_to_address>

### Comment 4
<location path=".github/workflows/deploy.yml" line_range="363" />
<code_context>
+                  --filter "label=com.docker.compose.project.working_dir=${LIVE_REPO_PATH%/}/$1" \
+                  --format '{{.Label "com.docker.compose.project"}}' 2>/dev/null \
+                  | sort -u | awk 'NR == 1') || p=""
+            printf '%s\n' "${p:-$1}"
+          }
+          # usage: compose_p <stack> <compose args...>
</code_context>
<issue_to_address>
**Orphaned project resources remain**

When a stack has no containers but still has project-labelled resources, and its Compose project name differs from its stack name, `compose_project` falls back to the stack name, so `compose_p ... down` targets the wrong project and leaves the remaining project-labelled resources behind.

Identify the project from remaining Compose resources before falling back to the stack name.

Also at `.github/workflows/deploy.yml:364`, `.github/workflows/deploy.yml:862`, `.github/workflows/deploy.yml:1084-1085`.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 4 findings to address first, and if project-label resolution selects the wrong Compose project or the no-file Compose commands do not behave as expected, health checks and rollback/teardown can fail, or the wrong containers can be stopped during a production deployment. Reverting restores the previous commands, but it cannot undo an outage, failed deployment, or containers already removed.

Blocking findings: .github/workflows/deploy.yml:407, .github/workflows/deploy.yml:1296, .github/workflows/deploy.yml:362, .github/workflows/deploy.yml:363


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread .github/workflows/deploy.yml
Comment thread .github/workflows/deploy.yml
Comment thread .github/workflows/deploy.yml Outdated
Comment thread .github/workflows/deploy.yml Outdated
owine added 2 commits October 9, 2026 23:54
Sourcery review: when containers from more than one Compose project share a
stack's working_dir label (e.g. leftovers after a project rename),
compose_project returned only the first, so health checks missed the others
and down left them running. compose_projects now returns all distinct
matches and compose_p runs the command for each (stdin from /dev/null so
compose can't eat the loop input), failing if any project's command fails.
…; review nits

Independent review of #101: with no containers in the stack dir, compose_p
fell back to `-p <stack>`, which matches a same-named project running from
any directory — teardown (now ungated on the compose file) could down it and
the health check could pass on its containers. compose_projects now returns
nothing in that case (down no-op, health check fails as "no containers").
The foreign check captures docker output before grep -q to avoid SIGPIPE
under pipefail.

Nits: update the stale project-name comment, drop the unused helper source in
Deploy dockge, and build the health-check failed_stacks JSON with jq.
@owine
owine merged commit f83af51 into main Oct 10, 2026
3 checks passed
@owine
owine deleted the fix/compose-project-from-labels branch October 10, 2026 05:04
owine added a commit that referenced this pull request Oct 10, 2026
…ir labels (#103)

QA review of #101. compose_projects is now scripts/deployment/lib/compose-project.sh,
checked out at job.workflow_sha and copied to $RUNNER_TEMP by the deploy,
health-check and rollback jobs (replaces three inline "keep in sync" copies).

- Stale working_dir: containers reused from an older checkout/manual `up` keep
  their original working_dir label, so a stack whose containers all came from
  another dir also named <stack> is now resolved by its normalised project name
  (with a ::warning::) instead of returning nothing. Previously a critical stack
  in that state failed the health check ("no containers") and triggered a
  rollback every deploy.
- Foreign same-named project: still left alone, but now with a ::warning::
  instead of a silent no-op down.
- The fallback no longer passes the raw stack name to `docker compose -p`
  (rejected for uppercase/dots); the label lookup uses the normalised name.
- health-check gains a compose-workflow checkout; rollback's checkout is now
  unconditional (its continue-on-error moves to the network step only).
- New scripts/testing/test-compose-project.sh (17 cases, fake docker on PATH).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant