Skip to content

fix: detect images pulled by digest (no RepoTags) (#101)#106

Open
AmSach wants to merge 1 commit into
RealOrangeOne:masterfrom
AmSach:fix/images-pulled-by-digest-not-detected
Open

fix: detect images pulled by digest (no RepoTags) (#101)#106
AmSach wants to merge 1 commit into
RealOrangeOne:masterfrom
AmSach:fix/images-pulled-by-digest-not-detected

Conversation

@AmSach

@AmSach AmSach commented Jul 6, 2026

Copy link
Copy Markdown

Fixes #101

When a container's image was pulled by digest (e.g. via image@sha256:... in a compose file), Docker does not assign any RepoTags, so container.image.tags is an empty list and the container was silently skipped during backup.

This was particularly painful for Immich, whose compose file pins the postgres image by digest, e.g.:

ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:32324a2f41df5de9efe1af166b7008c3f55646f8d0e00d9550c16c9822366b4a

Before: get_container_names would return an empty set, immich-app/postgres never matched a backup pattern, and the database was silently skipped.

After: get_container_names falls back to container.attrs['Config']['Image'] (which preserves the original digest reference) and the new _name_from_image_reference helper correctly strips the @sha256:... digest suffix before matching.

Changes

  • db-auto-backup.py:

    • New _name_from_image_reference(reference) helper that handles name:tag, name@digest, and registry/name:tag@digest forms.
    • get_container_names now also reads Config.Image as a fallback when RepoTags is empty, and uses the new helper.
    • The old image.split(":", 1) crash when no tag separator is present is gone — the helper tolerates tags being absent (digest-only references).
  • tests/tests.py:

    • test_name_from_image_reference_with_digest — 4 parametrized cases covering Immich-style, official-library, and pure-digest references.
    • test_name_from_image_reference_without_digest — 4 cases for the existing name:tag form (including tag-less images).
    • test_get_container_names_falls_back_to_config_image — end-to-end check that the digest-pinned Immich image is now detected.
    • test_get_container_names_empty_when_no_reference — confirms we degrade gracefully when both RepoTags and Config.Image are missing.

Test run

python3 -m pytest tests/tests.py --deselect tests/tests.py::test_backup_runs --deselect tests/tests.py::test_backup_runs_compressed
================= 37 passed, 6 deselected, 1 warning in 0.20s ==================

(Integration tests test_backup_runs / test_backup_runs_compressed were skipped because they require a live Docker test container from tests/conftest.py; they are unchanged.)

black, ruff, and mypy all pass clean.

— Sent via @AmSach bot

When a container's image was pulled by digest (e.g. via
'image@sha256:...' in a compose file), Docker does not assign
any RepoTags, so container.image.tags is an empty list and the
container was silently skipped during backup.

This was particularly painful for Immich, whose compose file pins
the postgres image by digest, so the immich-app/postgres pattern
in BACKUP_PROVIDERS never matched.

Fix by:
  - extracting image-reference parsing into _name_from_image_reference
    (handles name:tag, name@sha256:..., and registry/name:tag@sha256:...
    forms, plus a missing tag when only the digest is referenced)
  - falling back to container.attrs['Config']['Image'] when RepoTags
    is empty
  - adding tests covering all the new cases
Comment thread tests/tests.py
),
],
)
def test_name_from_image_reference_with_digest(reference: str, name: str) -> None:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Suggestion: This test can be folded into test_get_container_names - I don't think the internal helper needs its own tests

Comment thread db-auto-backup.py
"""
# Strip the digest suffix if present (e.g. @sha256:...).
# We only care about the name part for matching against patterns.
image_ref = reference.split("@", 1)[0]

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Suggestion: Split from the right

Suggested change
image_ref = reference.split("@", 1)[0]
image_ref = reference.rsplit("@", 1)[0]

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.

Images pulled by digest are not detected (no RepoTags)

2 participants