README
¶
decomk
decomk is an isconf-inspired bootstrap wrapper for devcontainers.
Philosophy
decomk separates concerns:
- Policy lives in shared config (
decomk.conf): context expansion, tuple values, and action-variable composition. - Execution graph lives in a
Makefilethat is shared across multiple devcontainer repos: file targets in the stamp directory define idempotent, dependency-ordered work. - Stage-0 lifecycle files (
.devcontainer/devcontainer.json,.devcontainer/decomk-stage0.sh) are generated scaffolding and should be treated as managed bootstrap wrappers, not as the place to encode per-repo tool policy.
For deeper design background, see:
doc/decomk-design.md(decomk behavior and selftest design)doc/isconf-design.md(isconf algorithm lineage)
Current commands
decomk init -conf— scaffold a shared conf repo (decomk.conf+Makefile+ producer.devcontainer/)decomk init— scaffold image-consumer stage-0 files in.devcontainer/with a minimaldevcontainer.json(name+image)decomk version— print the decomk CLI version stringdecomk plan— resolve tuples/targets + runmake -nin the stamp directorydecomk run— write env export file + runmakein the stamp directorydecomk checkpoint— build/push/tag shared checkpoint images for theupdateContentphase
Versioning and release
VERSIONat repo root is the canonical CLI release version source.go generate ./...updatescmd/decomk/version_generated.gofromVERSION.decomk versionprints the generated value at runtime (unless overridden by build-time ldflags).- Immutable git tags from
VERSIONare the audit/release identity. - Moving git branches are the tool-install channels:
main— active developmenttesting— opt-in validation channelstable— default consumer channel
@latestis not the channel contract for this repo; generated stage-0 defaults and normal operator installs should follow@stable, while@testingis reserved for validation before stable promotion.
Immutable release tag workflow:
make release-minor
This runs scripts/release.sh minor, which:
- refuses to run if the repo is dirty,
- reads and bumps
VERSIONminor (vMAJOR.MINOR[.PATCH] -> vMAJOR.(MINOR+1).0), - regenerates
cmd/decomk/version_generated.go, - commits the version bump,
- creates a matching git tag,
- pushes branch and tags.
Channel promotion workflow:
# from branch main
make promote-testing
# after validation passes, from branch testing
make promote-stable
Promotion rules:
release-minorruns onmainand only creates/pushes an immutable version tag.promote-testingfast-forwards/pushestestingfrom the currentmainHEAD.promote-stablefast-forwards/pushesstablefrom the currenttestingHEAD.- Both promotion commands refuse dirty repos and refuse non-fast-forward channel movement.
Step-by-step onboarding
1) Bootstrap one shared conf repo with decomk init -conf
Install decomk on your own machine:
go install github.com/stevegt/decomk/cmd/decomk@stable
To validate pending branch-channel changes before they reach stable:
go install github.com/stevegt/decomk/cmd/decomk@testing
In your shared conf repo:
decomk init -conf -conf-uri git:<your-shared-conf-repo-url> -image mcr.microsoft.com/devcontainers/base:ubuntu-24.04
This writes a starter tree:
decomk.confMakefileREADME.mdbin/hello-world.sh.devcontainer/devcontainer.json.devcontainer/decomk-stage0.sh.devcontainer/Dockerfile
2) Customize and push the shared conf repo
- Edit
decomk.confcontexts/tuples for your org/repo policy. - Edit
Makefiletargets for idempotent shared setup work. - Push the repo and keep its URI ready as:
git:<repo-url>[?ref=<git-ref>]
3) Run decomk init in each image-consumer repo
In each workspace repo you want managed:
decomk init -conf-url https://github.com/<org>/<your-shared-conf-repo>.git?ref=main
# or
decomk init -image ghcr.io/<org>/<base-image>:stable
This writes:
.devcontainer/devcontainer.json.devcontainer/decomk-stage0.sh
4) Start/rebuild the devcontainer
The generated stage-0 hooks:
- ensures
decomkis available inPATHfromDECOMK_TOOL_URI, - syncs config repo from
DECOMK_CONF_URIinto<DECOMK_HOME>/conf, - runs
decomk run <action-args>(defaulting to lifecycle phase selectorupdateContent/postCreate).
decomk init -conf safety and overwrite policy
decomk init -conf is conservative by default:
- If any managed conf-repo file already exists,
decomk init -conffails. - It does not overwrite existing files in default mode.
- It does not write alternate temp merge files in default mode.
If you intentionally want to regenerate/replace files, use force:
-f(alias)-force
When -f is used and .devcontainer/Dockerfile already exists, decomk init -conf
reuses these values as prompt defaults:
- first
FROMimage ENV DECOMK_REMOTE_USER=...ENV DECOMK_REMOTE_UID=...
If Dockerfile parsing fails, init prints a warning and continues with fallback defaults.
Recommended reconciliation workflow when files already exist:
git commit -m "Checkpoint existing conf-repo files"
decomk init -conf -f -conf-uri git:<your-shared-conf-repo-url>
git difftool -- decomk.conf Makefile README.md .devcontainer/devcontainer.json .devcontainer/decomk-stage0.sh .devcontainer/Dockerfile
decomk init safety and overwrite policy
decomk init is conservative by default:
- If either target stage-0 file already exists,
decomk initfails. - It does not overwrite existing files in default mode.
- It does not write alternate temp merge files in default mode.
This is safe to run in existing repos because it will stop before changing managed files.
If you intentionally want to regenerate/replace files, use force:
-f(alias)-force
When -f is used and .devcontainer/devcontainer.json already exists,
decomk init reuses existing name/image values as defaults so reruns do not
require re-entering the same configuration.
Image-consumer decomk init resolves image with this precedence:
-image- derived from
-conf-url(HTTP(S), optional?ref=...) - interactive source selection menu
- existing local image in
-no-promptmode
In image-consumer mode, producer-only flags (-conf-uri, -tool-uri, -home,
-log-dir, -fail-no-boot) are treated as misuse and fail with:
-<flag> is only valid with -conf.
Recommended reconciliation workflow when files already exist:
git commit -m "Checkpoint existing devcontainer files"
decomk init -f -conf-url https://github.com/<org>/<your-shared-conf-repo>.git?ref=main
git difftool -- .devcontainer/devcontainer.json .devcontainer/decomk-stage0.sh
Stage-0 template ownership
Canonical sources:
cmd/decomk/templates/devcontainer.json.tmplcmd/decomk/templates/consumer.devcontainer.json.tmplcmd/decomk/templates/decomk-stage0.sh.tmpl
Generated copies:
examples/devcontainer/devcontainer.jsonexamples/devcontainer/decomk-stage0.shexamples/decomk-selftest/devpod-local/workspace-template/.devcontainer/devcontainer.jsonexamples/decomk-selftest/devpod-local/workspace-template/.devcontainer/decomk-stage0.sh
Regenerate/check:
make generate
make check-generated
init -conf canonical sources:
cmd/decomk/templates/confrepo.decomk.conf.tmplcmd/decomk/templates/confrepo.Makefile.tmplcmd/decomk/templates/confrepo.README.md.tmplcmd/decomk/templates/confrepo.hello-world.sh.tmplcmd/decomk/templates/confrepo.devcontainer.json.tmplcmd/decomk/templates/confrepo.Dockerfile.tmpl
Generated copy:
examples/confrepo/*
Key stage-0 environment contract
Primary stage-0 vars in devcontainer.json:
DECOMK_TOOL_URI— tool source (go:orgit:URI; generated default points atgo:github.com/stevegt/decomk/cmd/decomk@stable)DECOMK_CONF_URI— config source (git:URI)DECOMK_HOME— state root (default/var/decomk)DECOMK_LOG_DIR— run-log root (default/var/log/decomk)DECOMK_FAIL_NOBOOT— stage-0 failure policy (falsedefault: continue boot after writing diagnostics;true: fail startup)
Generated lifecycle hooks call one script with explicit phase args:
updateContentCommand:bash .devcontainer/decomk-stage0.sh updateContentpostCreateCommand:bash .devcontainer/decomk-stage0.sh postCreate
When stage-0 fails, generated script behavior is:
DECOMK_FAIL_NOBOOT=true: exit non-zero and fail startup.- unset/false: write phase-specific failure marker/log under
<DECOMK_HOME>/stage0/failure/, write a MOTD hint (or fallback hint file), then return success so container boot continues.
Legacy variable-name migration mapping is documented in:
TODO/TODO-jirin-decomk-devcontainer-tool-bootstrap.md(Legacy stage-0 variable migration mapping)
Run/plan quick examples
decomk plan INSTALL
decomk run INSTALL
With explicit local files for experimentation:
DECOMK_HOME=/tmp/decomk \
decomk plan INSTALL -config ./decomk.conf -makefile ./Makefile -context myrepo
DECOMK_HOME=/tmp/decomk DECOMK_LOG_DIR=/tmp/decomk/log \
decomk run INSTALL -config ./decomk.conf -makefile ./Makefile -context myrepo
decomk run writes <DECOMK_HOME>/env.sh and runs make in <DECOMK_HOME>/stamps.
Checkpoint quick examples
# Build a local checkpoint candidate by running devcontainer prebuild lifecycle.
decomk checkpoint build -workspace-folder . -config .devcontainer/devcontainer.json -tag ghcr.io/acme/base:block10-candidate
# Publish candidate to immutable + testing tags.
decomk checkpoint push ghcr.io/acme/base:block10-candidate \
ghcr.io/acme/base:block10-20260420 \
ghcr.io/acme/base:testing
# After external/manual tests pass, move stable explicitly.
decomk checkpoint tag -m ghcr.io/acme/base:block10-20260420 ghcr.io/acme/base:stable
push/tag fail if a destination tag already exists unless -m is set.
checkpoint build is verbose by default (lifecycle logs on stderr); pass -q
to suppress lifecycle log output.
For operator/CI checkpoint rollout handoff details:
TODO/TODO-luvov-single-path-checkpoints.md(luvov.7 Operator/CI handoff contract) — canonical step-by-step contract and artifact requirements.doc/image-management.md— design rationale and lifecycle context.
Consumer selector policy (TODO-topan)
Consumer repos should use one canonical .devcontainer/devcontainer.json
and select checkpoint images via image: tags:
- Channel-following (for example
:stableor:testing) when teams want automatic uptake of tested promotions. - Immutable pinning (for example
:block10-20260420) when teams need a controlled freeze window.
No .devcontainer/BlockXX/... profile-path switching is required in the
current model. Maintainers update selector policy via normal repo commits.
See TODO/TODO-topan-codespaces-block-prebuild-profiles.md for policy details.
Logging and state defaults
- state root:
/var/decomk(overrideDECOMK_HOME/-home) - run logs:
/var/log/decomk(overrideDECOMK_LOG_DIR/-log-dir) - default log-root fallback:
<DECOMK_HOME>/logwhen default/var/log/decomkis not writable
MOTD run summaries (DECOMK_MOTD_PHASES)
decomk run can publish post-run MOTD files when the tuple
DECOMK_MOTD_PHASES is present in resolved config.
Format:
- CSV entries in
NN:phaseform, for example:DECOMK_MOTD_PHASES='88:version,93:updateContent,94:postCreate'
NNmust be exactly two digits.phasemay contain letters, numbers,_,-, and..
Behavior:
- If
DECOMK_MOTD_PHASESis unset, no run-summary MOTD files are written. - If the current
DECOMK_STAGE0_PHASEhas a mapping, decomk writes:/etc/motd.d/<NN>-decomk-<phase>
- If
versionis mapped (for example88:version), decomk also writes:/etc/motd.d/<NN>-decomk-version- content starts with a blank line, then
decomk version: <value>.
- If
/etc/motd.dcannot be written, decomk falls back to:<DECOMK_HOME>/stage0/failure/<same-filename>
- Invalid mapping syntax is surfaced as a run warning.
Concepts
Context
A context key selects a set of tokens from decomk.conf. Typical context
keys are:
DEFAULT(common baseline)owner/repo(derived from the workspace repo’sremote.origin.urlwhen available)repo(fallback)
In the typical devcontainer case, decomk applies multiple context keys in one run:
DEFAULT(when defined)- plus one key per discovered workspace repo (when that key exists in config)
You can force a single context with -context / DECOMK_CONTEXT.
Tokens
Each context key maps to a list of tokens. Tokens are one of:
- a macro reference (token matches another key in
decomk.conf) - a
NAME=valuetuple (passed tomakeon argv as a variable assignment)
Bare RHS tokens that are not tuples must resolve to keys defined in decomk.conf.
Unknown bare RHS tokens are rejected as config errors.
Action args (isconf-style)
Positional args to decomk plan/run are interpreted like isconf:
- If an arg matches the name of a resolved tuple variable (for example
INSTALL), decomk interprets that variable’s value as a whitespace-separated list of make targets. - Otherwise, the arg is treated as a literal make target name.
This lets you define “what to run” as action variables rather than embedding targets directly in the context expansion:
DEFAULT: INSTALL='install-neovim install-codex'
repo1: DEFAULT INSTALL='install-mob-consensus'
Usage:
decomk run INSTALL
decomk run install-neovim # literal target fallback
decomk plan and decomk run require at least one positional action arg.
Stamps
decomk runs make in a stamp directory outside the workspace repo.
Make targets should usually be file targets (not .PHONY), whose recipes
end by creating/updating $@ (often via touch $@). Because make is run in
the stamp directory, $@ becomes a persistent “stamp file” that records that
the step has succeeded.
How decomk works (algorithm)
decomk plan and decomk run share the same resolution pipeline:
-
Determine
DECOMK_HOME- flag:
-home - env:
DECOMK_HOME - default:
/var/decomk
- flag:
-
Determine the starting directory (like
make -C)- flag:
-C <dir> - default:
. - decomk changes directory to this path before resolving relative
-configand-makefilepaths
- flag:
-
Determine the workspaces root directory to scan
- flag:
-workspaces <dir> - env:
DECOMK_WORKSPACES_DIR - default:
/workspaces
- flag:
-
Stage-0 bootstrap (outside decomk core)
- lifecycle tooling (for example
.devcontainer/decomk-stage0.sh) ensures adecomkbinary is available inPATH:DECOMK_TOOL_URI=go:<module>@<version>:go install <module>@<version>(typically an immutable tag or moving channel branch such astesting/stable)DECOMK_TOOL_URI=git:<repo-url>[?ref=<git-ref>]: clone/pull repo into<DECOMK_HOME>/src/decomk, optionally checkout ref, thengo install ./cmd/decomk
- lifecycle tooling syncs
DECOMK_CONF_URI=git:<repo-url>[?ref=<git-ref>]into<DECOMK_HOME>/conf decomk plan/runconsumes this local state and does not clone/pull repos itself.
- lifecycle tooling (for example
-
Load config definitions (
decomk.conf)- config repo (optional):
<DECOMK_HOME>/conf/decomk.conf - explicit override (optional):
-config <path>orDECOMK_CONFIG
Precedence is “last wins”:
- config repo (lowest)
- explicit
-config/DECOMK_CONFIG(highest)
Each of those sources is loaded as a tree:
- the base
decomk.conf - plus optional
decomk.d/*.confin lexical order- later files override earlier ones by key
- config repo (optional):
-
Choose which context keys to apply
-context <key>/DECOMK_CONTEXT(must exist in config) forces a single context- otherwise:
- scan
<workspacesDir>/*for workspace repos - for each repo, try to find the most specific matching config key (first match wins):
owner/repo(derived from that workspace repo’sremote.origin.url)repo(derived from origin URL or directory basename)- workspace directory basename
- include a workspace’s key only if it exists in the loaded config
- deduplicate keys across workspaces
- scan
-
Seed tokens
- in the common case, seed tokens are:
DEFAULT(when defined)- plus the selected per-workspace keys (when any)
- in the common case, seed tokens are:
-
Expand macros (recursive)
- if a token exactly matches a key in the config map, it is replaced by that key’s token list, recursively
- unknown bare RHS tokens are rejected before expansion (
decomk.confmust be tuple-or-key only) - guardrails:
- cycle detection
- maximum depth (default 64; override with
-max-expand-depth)
-
Partition expanded tokens
- tuples:
NAME=valuewhereNAMEmatches[A-Za-z_][A-Za-z0-9_]* - targets: must be empty (non-empty output is treated as invalid config)
- tuples:
-
Select make targets (isconf-style action args)
- Build an “effective tuple map” from the tuple list (last assignment wins).
- Positional args are required.
- For each arg:
- if arg matches a tuple variable name: split its value on whitespace and append as targets
- else: treat arg as a literal make target
- decomk exposes the selected targets as
DECOMK_PACKAGES(exported in the env export file and passed to make).
-
Compute state paths
- stamp dir (global):
<DECOMK_HOME>/stamps/
- env export file (stable):
<DECOMK_HOME>/env.sh
-
Plan (
decomk plan)- print the resolved plan (tuples + targets)
- print the env exports that
runwould write (dry-run; does not write the env file) - run
make -nin the stamp dir to show what would execute (dry-run)
-
Execute make (
decomk run)- write the env export file:
<DECOMK_HOME>/env.sh
- determine
Makefilepath:-makefile <path>if set- otherwise, first existing of:
- sibling of explicit
-config(if set):<dir-of-config>/Makefile <DECOMK_HOME>/conf/Makefile
- sibling of explicit
- acquire an exclusive global stamps lock:
<DECOMK_HOME>/stamps/.lock
- ensure stamp dir exists, then touch existing stamps once (see below)
- determine log root (first match wins):
-log-dir <abs-path>(overridesDECOMK_LOG_DIR)DECOMK_LOG_DIR/var/log/decomk(falls back to<DECOMK_HOME>/logwhen not writable)
- create a per-run log dir (one per make invocation):
<logRoot>/<runID>/runIDincludes sub-second time + pid for uniqueness
- run:
make -f <Makefile> <tuples...> <targets...>- working directory = stamp dir
- stdout/stderr are teed to
make.logunder the per-run log dir
- optionally write MOTD summaries when
DECOMK_MOTD_PHASESis configured:<NN>-decomk-<DECOMK_STAGE0_PHASE>when current phase is mapped<NN>-decomk-versionwhenversionis mapped- fallback under
<DECOMK_HOME>/stage0/failure/when/etc/motd.dis not writable
- write the env export file:
decomk.conf format
decomk.conf is intentionally small and deterministic:
- Whole-line comments start with
#. - Key lines are
key: token token token.- The
:must be followed by whitespace or end-of-line (this avoids treatinghttp://...as a key line). - Keys cannot contain
=.
- The
- Any other non-empty, non-comment line is a continuation line and appends more tokens to the previous key.
- Tokens are whitespace-separated.
- Single quotes may be used to include spaces inside a token:
FOO='bar baz'parses as one tokenFOO=bar baz
- Backslash escapes the next rune when not in single quotes.
NAME=$is a passthrough sentinel for tuples:- if incoming env contains
NAME, decomk uses that value - else if an earlier tuple already set
NAME, decomk keeps that fallback - else decomk fails fast
- if incoming env contains
- Single quotes may be used to include spaces inside a token:
- Incoming
DECOMK_*environment variables are automatically carried into the canonical env export/make contract (unless later tuple/computed values override them). DECOMK_MOTD_PHASESis a regular tuple value that controls optional run MOTD writes (NN:phaseCSV); example:DEFAULT: DECOMK_MOTD_PHASES='88:version,93:updateContent,94:postCreate'
Makefile expectations and example
decomk runs make in the stamp directory and passes:
- tuples as argv variable assignments (
NAME=value) - targets as argv targets
This is the key idea: your “units of work” should be make file targets whose
recipes end by touching $@.
Example:
SHELL := /bin/bash
.ONESHELL:
.SHELLFLAGS := -euo pipefail -c
# In decomk's execution model, targets below are files created in the stamp dir.
Block00_base:
echo "base"
touch $@
Block10_common: Block00_base
echo "common"
touch $@
Recommendation: touch $@ last and only on success.
Stamps and invalidation
Why “touch existing stamps”?
Provisioning/bootstrapping often wants semantics closer to: “run once unless explicitly invalidated” than: “re-run when a prerequisite timestamp changes”.
So before running make, decomk updates the mtime of existing (non-hidden)
regular files in the stamp dir, effectively making stamp deletion the main way
to force re-execution.
How to force a step to re-run
Delete its stamp file in the stamp directory, then run again:
rm -f "$DECOMK_HOME/stamps/Block20_go"
decomk run ...
For “rerun everything”, delete the whole stamps directory (a future decomk clean
command will automate this).
Persistent directory layout
By default, state lives under /var/decomk. You can override it with
DECOMK_HOME or decomk -home.
By default, per-run logs are written under /var/log/decomk. You can override
this with DECOMK_LOG_DIR or decomk -log-dir. If /var/log/decomk is not
writable and you did not explicitly override the log dir, decomk falls back to
<DECOMK_HOME>/log.
CLI usage
decomk init [flags]
decomk version
decomk plan [flags] [ARGS...]
decomk run [flags] [ARGS...]
ARGS:
Action variable names (e.g. INSTALL) or literal make targets.
ARGS are required for both `decomk plan` and `decomk run`.
Common flags for plan/run:
-home <abs-path> Override DECOMK_HOME
-log-dir <abs-path> Override DECOMK_LOG_DIR (default /var/log/decomk)
-C <dir> Starting directory (like make -C)
-workspaces <dir> Workspaces root directory to scan (default /workspaces; overrides DECOMK_WORKSPACES_DIR)
-context <key> Override context selection
-config <path> Explicit config file (overrides defaults)
-makefile <path> Explicit Makefile path
-max-expand-depth <n> Macro expansion depth limit (default 64)
-v Verbose output
Flags for init:
-repo-root <path> Repo root where .devcontainer files are written (default: current git repo root)
-conf Image producer mode: scaffold shared conf repo starter files at repo root
-name <string> devcontainer.json "name" value (default: repo basename)
-image <ref> Image consumer mode: final devcontainer image; image producer mode (-conf): Dockerfile FROM base image
-conf-url <url> Image consumer mode: derive image from image-producer/conf repo HTTP(S) URL (optional ?ref=...)
-conf-uri <uri> Image producer mode only: DECOMK_CONF_URI value in devcontainer.json (git:...)
-tool-uri <uri> Image producer mode only: DECOMK_TOOL_URI value in devcontainer.json (go:... or git:...)
-home <abs-path> Image producer mode only: DECOMK_HOME value in devcontainer.json
-log-dir <abs-path> Image producer mode only: DECOMK_LOG_DIR value in devcontainer.json
-remote-user <name> DECOMK_REMOTE_USER value for producer Dockerfile ENV (image producer mode)
-remote-uid <uid> DECOMK_REMOTE_UID value for producer Dockerfile ENV (image producer mode)
-fail-no-boot <value> Image producer mode only: DECOMK_FAIL_NOBOOT value in devcontainer.json (true/false/1/0/yes/no/on/off)
-force Overwrite existing stage-0 files even when they already exist
-f Alias for -force
-no-prompt Do not prompt for unset values
Makefile privilege model
decomk run now requires root and does not do its own sudo fallback logic.
The generated stage-0 hook (.devcontainer/decomk-stage0.sh) handles one
non-interactive re-exec via sudo -n -E before calling decomk run.
decomk intentionally uses a single privilege mode per invocation (it does not split targets into “root phase” and “user phase”); this keeps stamp semantics simple and repeatable.
When you need a user-scoped step (for example: dotfiles, pipx installs, or
other $HOME writes) while make is running as root, explicitly drop
privileges inside the Makefile using runuser (or su). decomk exports:
DECOMK_REMOTE_USER: the dev user (the non-root user decomk expects to own state)DECOMK_MAKE_USER: the effective usermakeis running as (root)
Example pattern:
AS_DEV :=
ifneq ($(DECOMK_MAKE_USER),$(DECOMK_REMOTE_USER))
AS_DEV = runuser -u $(DECOMK_REMOTE_USER) --
endif
install-user-stuff:
$(AS_DEV) ./scripts/install-user-stuff.sh
Devcontainer notes
/var/decomk(state) and/var/log/decomk(logs) should be writable by the dev user (or override withDECOMK_HOME/DECOMK_LOG_DIR).- In a Dockerfile, you typically want:
RUN mkdir -p /var/decomk /var/log/decomk && chown -R $USER:$USER /var/decomk /var/log/decomk
- Export
DECOMK_REMOTE_USERandDECOMK_REMOTE_UIDin the image (for example with DockerfileENV) so stage-0 identity checks are explicit and deterministic. - Alternatively, use a minimal lifecycle hook to run decomk directly; see
examples/devcontainer/decomk-stage0.sh. - That hook performs stage-0 bootstrap by ensuring
decomkis inPATH, syncingDECOMK_CONF_URI, then runningdecomk.
- In a Dockerfile, you typically want:
- The repo’s workspace path is host-dependent; prefer using
${containerWorkspaceFolder}indevcontainer.jsonrather than assuming/workspaces/<repo>. - Canonical scaffold sources are:
cmd/decomk/templates/devcontainer.json.tmpl(full stage-0 template used by examples/selftests)cmd/decomk/templates/consumer.devcontainer.json.tmpl(minimal image-consumer template used bydecomk init)cmd/decomk/templates/decomk-stage0.sh.tmpl- Generated files:
examples/devcontainer/devcontainer.jsonexamples/devcontainer/decomk-stage0.shexamples/decomk-selftest/devpod-local/workspace-template/.devcontainer/devcontainer.jsonexamples/decomk-selftest/devpod-local/workspace-template/.devcontainer/decomk-stage0.sh
- Regenerate with
go generate ./...(ormake generate).
- Companion static example file:
examples/devcontainer/Dockerfile
Self-test harness
- Root convenience targets:
make selftest-devpodmake selftest-codespacesmake selftest-codespaces-clean
- Local DevPod Docker-provider validation lives under
examples/decomk-selftest/.- Default tuple-override check:
examples/decomk-selftest/devpod-local/run.sh
- Explicit tuple-action args:
examples/decomk-selftest/devpod-local/run.sh TUPLE_VERIFY_TOOL TUPLE_VERIFY_CONF TUPLE_CONTEXT_OVERRIDE TUPLE_DEFAULT_SHARED
- The harness publishes temporary tool+config repos over
git://and lets postCreate clone/pull them during stage-0 bootstrap. - Context selection is automatic from workspace repo name (
decomk), with no explicit-contextin harness calls. - Fixture config/make/scripts live under:
examples/decomk-selftest/fixtures/confrepo/
- Default tuple-override check:
- Codespaces parity validation lives under
examples/decomk-selftest/codespaces/.- Run:
examples/decomk-selftest/codespaces/run.sh
- The harness creates a fresh Codespace from the pushed branch under test, auto-builds a fixture config repo inside the Codespace, exports stage-0 URI vars, runs
examples/devcontainer/decomk-stage0.sh postCreate, validates PASS/FAIL markers, runs stamp regression checks, then deletes the Codespace unless--keep-on-failis set. - Local harness artifacts under
/tmp/decomk-codespaces.*are preserved by default for inspection; pass--cleanupto remove them on success. - Diagnostics artifacts are explicit and completion-marked:
diagnostics-summary.txt(step-by-step status)diag-<step>.rc,diag-<step>.stdout.log,diag-<step>.stderr.logdiagnostics.complete(written when artifact collection is finished)
- The selftest devcontainer enables
ghcr.io/devcontainers/features/sshd:1because harness execution depends ongh codespace ssh. - Machine selection auto-resolves from the repository-allowed Codespaces machine list (prefers
basicLinux32gbwhen available); override with:examples/decomk-selftest/codespaces/run.sh --machine <machine-name>
- Optional override for external fixture config source:
examples/decomk-selftest/codespaces/run.sh --conf-uri git:https://github.com/<owner>/<conf-repo>.git
- Codespaces parity harness requires local
HEADto matchorigin/<branch>(commit + push first). - Harness details and prerequisites are documented in:
examples/decomk-selftest/README.md
- Run:
- Remote GCP-provider self-tests are intentionally deferred until a separate move-to-GCP decision is approved.
- Experimental lifecycle-evidence spike (POC, not a canonical decomk example):
examples/phase-eval/README.md
Limitations (current MVP)
- No
status/cleancommands yet. - Config parser is intentionally minimal (single quotes only; whole-line comments only).
- Stage-0 bootstrap expects
gitandgoin the container (decomk-stage0.shinstallsdecomk, syncs repos, and runsdecomk).
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
confrepogen
command
Command confrepogen renders canonical shared conf-repo starter files from decomk's embedded `init -conf` templates.
|
Command confrepogen renders canonical shared conf-repo starter files from decomk's embedded `init -conf` templates. |
|
decomk
command
Command decomk is an isconf-inspired bootstrap wrapper for devcontainers.
|
Command decomk is an isconf-inspired bootstrap wrapper for devcontainers. |
|
stage0gen
command
Command stage0gen renders canonical devcontainer stage-0 files from decomk's embedded templates.
|
Command stage0gen renders canonical devcontainer stage-0 files from decomk's embedded templates. |
|
versiongen
command
Command versiongen renders cmd/decomk/version_generated.go from VERSION.
|
Command versiongen renders cmd/decomk/version_generated.go from VERSION. |
|
Package confrepo centralizes defaults for shared decomk config-repo scaffolding.
|
Package confrepo centralizes defaults for shared decomk config-repo scaffolding. |
|
Package contexts loads and parses decomk.conf-style context definitions.
|
Package contexts loads and parses decomk.conf-style context definitions. |
|
Package expand implements isconf-style macro expansion for decomk.
|
Package expand implements isconf-style macro expansion for decomk. |
|
Package makeexec runs GNU make as a subprocess.
|
Package makeexec runs GNU make as a subprocess. |
|
Package resolve contains small, testable helpers for turning expanded tokens into the argv pieces that decomk passes to make.
|
Package resolve contains small, testable helpers for turning expanded tokens into the argv pieces that decomk passes to make. |
|
Package stage0 renders and writes devcontainer stage-0 bootstrap assets.
|
Package stage0 renders and writes devcontainer stage-0 bootstrap assets. |
|
Package state computes decomk's on-disk layout and provides helpers for managing persistent state (locks, stamps, env exports).
|
Package state computes decomk's on-disk layout and provides helpers for managing persistent state (locks, stamps, env exports). |