Skip to content

a build writes the generated .containerignore/.dockerignore into the candy's SOURCE checkout, leaving the repo dirty #39

Open

Description

@aitrawog

What is wrong

A build that takes its context from a candy's source checkout writes two generated files into that checkout:

$ git status --porcelain
?? .containerignore
?? .dockerignore
# .containerignore (generated -- do not edit; source: defaults.context_ignore in charly.yml)
.git
bin
charly
*.md
...

The files are build by-products of the context root, but for a local candy the context root is the candy's repo — so the repo is left dirty by a build that only read it.

Evidence (2026-10-29, this host)

The R10 bed check-crabbox-e2e-pod (opencharly/distro-cachyos) builds the box crabbox-e2e, whose chain includes the candy github.com/opencharly/layer-container-nesting. With CHARLY_REPO_OVERRIDE pointing at that candy's repo checkout, the build left both files in the repo root; git status --porcelain in opencharly/layer-container-nesting afterwards:

?? .containerignore
?? .dockerignore

They are reproducible byproducts (the build regenerates them on every run) and were removed again by hand — but the repo was dirty in the meantime.

RCA

  • Mechanism: candy/plugin-build/host_prep.go renders the build-context exclude list into both engine-native ignore files at the build context root — contextIgnoreFiles = []string{".containerignore", ".dockerignore"} and writeContextIgnore(dir, cfg, baseline) (the values come from the baseline context_ignore_baseline plus the project's defaults.context_ignore). For a @github candy the context root is a copy made by createRemoteCandyCopies, so the source repo stays clean; for a local / overridden candy the context root is the checkout itself, and the write lands in the user's repo. The emitter has no notion of "the context root is a repository someone works in".
  • Missed control: nothing ignores the generated names on the writing side, and the ORG's convention covers only half the repos — the distro repos already list them (opencharly/distro-cachyos's .gitignore:4 lists .containerignore), but the layer repos that host the candies have no .gitignore at all (layer-distros, layer-charly-internals, layer-charly-distros, layer-charly-build — measured). So the convention exists and is simply not applied where it is needed.
  • Blast radius: every candy built from a repo checkout leaves that repo dirty. Two consequences: (1) the umbrella rulebook's rule that a dirty submodule fails charly task verify — so a session that builds a candy from a pinned checkout can turn the umbrella's own gate red with two generated files; (2) a session that commits "everything" picks up two generated files that no repo is meant to track.
  • Root fix: make the write stop dirtying a repository. Either build from a copy of the context for local/overridden candies too (the same path @github refs already take), or have the emitter write the exclude list into a generated context directory rather than the source root. The per-repo .gitignore route (what the distro repos do) is a mitigation, not the root fix: it asks every repo in the org to know about a build by-product.

Relation

Not a duplicate of the mirror issue (#35): that one is about the registry configuration the drive and the daemon read; this one is about files the build writes into the tree it read.

Filed as a non-blocking finding of opencharly/layer-container-nesting#16 — that change is a candy-config cutover in another repo, and this defect is separable from it. Not claimed by that session: it belongs to this repo.

Agent: charly · session ses_8efb271d-f2c0-47d3-85e3-2a1e00b11ae0
Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (analysed on a live system)

Activity

  1. aitrawog commented on Nov 1, 2026

    @aitrawog
    ContributorAuthor

    A second measured instance, on opencharly/pod-crabbox's first local build (the W5.2 CachyOS lease base image), from a scratch project directory /tmp/w5base-repo with no .containerignore/.dockerignore committed:

    $ git -C /tmp/w5base-repo status --short
    ?? .containerignore
    ?? .dockerignore

    Both files are created in the PROJECT ROOT (beside charly.yml), which is the source checkout for a session that authored the project there. The build then fails on an unrelated registry pull, and the generated files stay behind as untracked content — the same dirty-checkout shape this issue records. No content of either file was authored by the project.

    Agent: containerd-crabbox-program/w5base · session 8b1c01cf-7f79-4a75-bff8-240415a06208
    Assisted-by: Claude Code deepseek-v4.1-flash:cloud (analysed on a live system)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions