generate-source
Recipe card from the charly-internals plugin (Development — contributor internals).
This card has additional detail pages:
Generate - Containerfile Generation
Section titled “Generate - Containerfile Generation”Overview
Section titled “Overview”charly box generate reads charly.yml and candy/, resolves dependency graphs, and writes Containerfiles to .build/. Generation is idempotent and .build/ is disposable (gitignored). Understanding the generated output is essential for debugging build issues.
Build-mode Containerfile generation is the WriteCandySteps → EmitTasks path in sdk/deploykit (deploykit.Generator, relocated from charly/generate.go in #67, driven by candy/plugin-build over the resolved-project envelope + HostBuild("render-seam"); the charly/generate.go top-level Generate/writeCandySteps orchestrator is DELETED). emitTasks (charly/tasks.go) is a thin shim to deploykit.Generator.EmitTasks that STAYS for the pod-overlay deploy path (the candy plugin-deploy-pod constructs deploykit.OCITarget + renders via deploykit.NewRenderGeneratorFromProject, the per-step fragment over the HostBuild("step-emit","oci-emit-step") seam). The render walks each layer’s ops directly to write Containerfile text — NOT the InstallPlan IR. The IR + deploykit.OCITarget.Emit is the DEPLOY-mode path: deploykit.OCITarget is constructed only by the candy plugin-deploy-pod’s buildOverlay for add_candy: overlay-Containerfile synthesis, alongside the external out-of-process deploys consuming the same IR. (The local/vm/k8s/android substrates are external plugins via pluginDeployTarget (S3b, dispatched through candy/plugin-bundle’s Invoke(OpDeployDispatch)): deploy:local (candy/plugin-deploy-local) and deploy:vm (candy/plugin-deploy-vm) DO consume the IR — the plugin walks it via kit.WalkPlans over the reverse channel, the vm one over the guest SSHExecutor so the walk runs inside the guest — while deploy:k8s (candy/plugin-kube) does NOT, generating a Kustomize tree host-side.) Build shares the compiler helpers (resolveCascadePackages, compileShellSnippetSteps, deploykit.RenderLocalPkgImageInstall — one source of truth, R3, relocated to sdk/deploykit in W3) with the IR, but not the deploykit.OCITarget walk. For the IR shape and step kinds, see /charly-internals:install-plan. For local-deploy supporting files (ledger, builder_run, shell_profile, reverse_ops, service_render, deploy_ref), see /charly-internals:local-infra.
Quick Reference
Section titled “Quick Reference”| Action | Command | Description |
|---|---|---|
| Generate all | charly box generate |
Write .build/ (Containerfiles) |
| Generate with tag | charly box generate --tag v1.0.0 |
Override CalVer tag |
| List targets | charly box list targets |
Build targets in dependency order |
| Inspect config | charly box inspect <image> |
Show resolved config JSON |
Generated Output Structure
Section titled “Generated Output Structure”.build/├── <image>/│ ├── Containerfile # One per image│ ├── supervisor/*.conf # Supervisord configs (if service layers)│ └── traefik-routes.yml # Traefik config (if route layers)└── _layers/<name> # Symlinks to remote layersContainerfile Structure
Section titled “Containerfile Structure”The generated Containerfile follows this order:
- Multi-stage build stages — scratch stages per layer, builder stages resolved via the builder plugins’
OpResolveleg (pixi, npm, aur, cargo —kit.BuilderResolve, C10), init system config assembly (driven by the embeddedinit:vocabulary), traefik routes FROM ${BASE_IMAGE}— external bases get bootstrap from the embeddeddistro:vocabulary (install cmd, cache mounts, workarounds); internal bases getUSER root- Image metadata — consolidated
ENVdirectives,EXPOSEports,ai.opencharly.*labels - COPY build artifacts — from each builder plugin’s
OpResolvereply (CopyArtifacts+ the once-per-builderCopyBinary) - Per-layer install steps — see “Task emission pipeline” below.
USERtoggles as each task’srun_as:field requires. - Final assembly — init system config assembly, traefik routes COPY,
USER <UID>,RUN bootc container lint(bootc images only)
Config-driven generation: Format-specific install commands, cache mounts, repo setup, and init fragments are defined in the embedded build vocabulary (charly/charly.yml, //go:embed) as Go text/template strings — three top-level sections: distro:, builder:, init:. Each distro entry contains both bootstrap config and its package format definitions. The builder: section retains each builder’s DETECTION (detect_file/detect_config), cache mounts, the deploy-time host phase, and (pixi) the runtime-env contract + install_command/manylinux_fix/build_script context inputs — but the build-time multi-stage TEMPLATE moved out to the builder plugins’ kit.BuilderResolve (C10). The embedded vocabulary is parsed by the same unified loader as any project charly.yml. Adding a new format (e.g., apk for Alpine) requires only YAML changes — zero Go code modifications.
Reference Index
Section titled “Reference Index”| Topic | File |
|---|---|
Task emission pipeline — per-verb emitters, Task struct, emitter helpers, shell-quoting, inline-content staging, user resolution, variable substitution, adjacent-coalescing, parent-dir auto-insertion, tag-section install emission |
references/task-emission.md |
Multi-stage build stages (pixi/npm/AUR/OpResolve builders, scratch context), auto-intermediate grouping by (Base, UID), intermediate images, container user resolution (adopt vs create) |
references/multistage-and-intermediates.md |
| LABEL placement + cache efficiency, OCI label reference table, runtime-only features, cache mounts | references/labels-and-caching.md |
Common Workflows
Section titled “Common Workflows”Debug Why a Build Fails
Section titled “Debug Why a Build Fails”charly box generate # Generate Containerfilescat .build/my-image/Containerfile # Inspect the generated Containerfilecharly box validate # Check for validation errorscharly box inspect my-image # See resolved configUnderstand Layer Ordering
Section titled “Understand Layer Ordering”charly box list targets # Shows dependency-ordered build sequencecharly box inspect my-image --format layers # Shows layer list for an imageCross-References
Section titled “Cross-References”/charly-image:layer— Canonical author-facing reference for the task verb catalog,var:substitution, YAML anchors, execution order. The emitter pipeline here implements what’s documented there./charly-build:generate— User-facingcharly box generatecommand./charly-internals:go— Source code map:charly/tasks.go(emitter pipeline,emitTasksshim → deploykit),sdk/deploykit/candy_steps.go:WriteCandySteps(orchestrator, relocated in #67),charly/layers.go:Taskstruct,candy/plugin-box/validate_rules.go:validateCandyTasks./charly-build:validate— User-facing validation rules (whatvalidateCandyTasksenforces)./charly-build:build— Building from generated Containerfiles./charly-internals:egress— the emitted Containerfile is egress-validated (writeContainerfile→#RenderedText, rejecting the<no value>template-failure marker) before it is written; the traefik-routes scratch-stage input is likewise validated (#TraefikRoutes).- Source:
sdk/deploykit(render DRIVE, relocated in #67),charly/tasks.go(emitTasksshim),charly/generate.go(staying helpers),sdk/deploykit/intermediates.go(moved fromcharly/intermediates.go;charly/intermediates_shim.godelegates to it),sdk/deploykit/graph.go(thincharly/graph_shim.gowrappers delegate to it).
When to Use This Skill
Section titled “When to Use This Skill”MUST be invoked before reading or modifying Go source files. Invoke this skill BEFORE launching Explore agents on charly/ code.