Skip to content

Latest commit

 

History

History
88 lines (69 loc) · 4.7 KB

File metadata and controls

88 lines (69 loc) · 4.7 KB

Agent guide

This repository contains devcontainer features whose install scripts are written in Amber and compiled to bash. The compiled bash is checked in and published to ghcr.io/nicoviii/devcontainer-features/<id>.

Layout

  • lib/*.ab — shared Amber modules (root check, apt handling, download, checksum verification, install helpers). Imported by every feature.
  • src/<id>/ — one directory per feature:
    • install.ab — the install script source of truth
    • install.shgenerated by amber build, do not edit by hand
    • devcontainer-feature.json — feature metadata and options
    • README.mdgenerated by devcontainer features generate-docs, do not edit by hand
    • NOTES.md — optional, hand-written; its content is included in the generated README
    • CHANGELOG.md — hand-written, Keep a Changelog format
  • test/<id>/ — test scenarios (scenarios.json) plus one <scenario-name>.sh per scenario.

Critical invariants

  • Generated files must stay in sync. After editing any .ab file, rebuild; after editing devcontainer-feature.json or NOTES.md, regenerate docs. CI fails if the committed install.sh or README files differ from what a fresh build/generation produces.
  • lib/ changes affect every feature. Amber inlines imports, so editing lib/*.ab requires rebuilding all src/*/install.sh files.
  • Use Amber 0.6.0-alpha (the version CI uses; also pinned in .devcontainer/devcontainer.json). A different compiler version produces different output and trips the CI drift check.

Commands

Run from the repository root (VS Code tasks exist for all of these, see .vscode/tasks.json):

# Check all Amber sources
set -e; for f in $(find ./lib ./src -name '*.ab'); do echo "$f"; amber check "$f"; done

# Rebuild all generated install.sh files
set -e; for f in $(find ./src -name '*.ab'); do echo "$f"; amber build "$f"; done

# Regenerate feature READMEs
(cd ./src && devcontainer features generate-docs -n "NicoVIII/devcontainer-features")

# Test one feature (requires docker; add --filter <scenario substring> to narrow further)
devcontainer features test --skip-autogenerated -f <id>

Amber gotchas

  • Inside $ ... $ command blocks, {expr} interpolates, so literal $ and { must be escaped as \$ and \{.
  • Interpolated values end up unquoted in the compiled bash — avoid passing untrusted or whitespace-containing values into command blocks without quoting them in the command text.
  • A trailing ? propagates failure; main blocks exit on the first failed ? call.

Conventions

  • Feature options are read as uppercase environment variables (option versionread_param("VERSION", ...)).
  • Most features install a tool and have a version option; "latest" is resolved via the GitHub API where supported (oxlint does not support it — see issue #25). The exception is claude-mount, which installs no tool: it declares a bind mounts entry in its devcontainer-feature.json and its install.ab only symlinks the mount into the remote user's home. It has no version option.
  • Test scenarios follow the naming <os>-<variant> for debian/ubuntu × default/latest/specific. The -specific scenarios intentionally pin an older version than the current release so the assertion proves version selection works. Features without a version option (e.g. claude-mount) use plain <os> scenario names.
  • In test scripts, wrap piped assertions for the check helper in bash -c, e.g. check "just version" bash -c "just --version | grep -F '1.50.0'" — a bare pipe would apply to check's output instead of the command.

CI and releases

  • .github/workflows/ci.yml checks/builds Amber, verifies no drift in generated files, then runs per-feature test jobs (via reusable-feature.yml) only for features whose paths changed.
  • Release flow: in src/<id>/, bump version in devcontainer-feature.json, move the Unreleased changelog section to the new version (CI reads the changelog section for the release notes), commit, then push a tag named <id>-v<version>. CI tests, publishes the feature to ghcr.io, and creates a GitHub release.
  • Keeping an Unreleased section in CHANGELOG.md between releases is expected.

Adding a new feature

  1. Copy the structure of an existing feature under src/<id>/ and test/<id>/.
  2. Add the feature to the detect-changes filters and add a per-feature job in ci.yml (copy an existing one), plus the picker entry in .vscode/tasks.json.
  3. Build, regenerate docs, and add test scenarios before tagging a <id>-v0.1.0 release.