Skip to content

Importing

llms.txt lists every page for an agent
Optional: hand this page to your coding agentThe steps work by hand too.
Show the whole prompt
Read https://intentius.io/chant/lexicons/docker/importing/.
Import each Compose file and Dockerfile I name with `npx chant import --lexicon docker docker-compose.yml -o src/compose/` and `npx chant import --lexicon docker Dockerfile -o src/<name>/`, then rename each Dockerfile export after its image and point the Compose service at it by reference.
Run `npx chant lint src` and `npx chant build src --lexicon docker -o build/docker-compose.yml`, diff the output and the `Dockerfile.*` files beside it against the originals, and open a pull request with each difference listed.
Never deploy to a real cloud account or shared environment (with `chant run`, `chant components` or a platform's own CLI), approve (a pull request review, `chant approve` or `chant run approve`), override a policy denial (`chant approve policy-override`), or merge; never cut or publish a release (`chant release cut --yes` or `chant release publish`), push or delete a `ci/*` tag (`chant ci tick` pushes them), or change the value of an existing flag; never touch `.chant/allowed_signers` or `chant/lifecycle`.

chant import turns an existing docker-compose.yml or Dockerfile into chant TypeScript.

Terminal window
chant import --lexicon docker docker-compose.yml -o src/compose/

Given:

services:
api:
image: myapp:1.0
ports:
- "8080:8080"
environment:
NODE_ENV: production
volumes:
pgdata: null

-o names a directory; the import writes main.ts in it. This produces src/compose/main.ts:

import { Service, Volume } from "@intentius/chant-lexicon-docker";
export const api = new Service({
image: "myapp:1.0",
ports: ["8080:8080"],
environment: {
NODE_ENV: "production",
},
});
export const pgdata = new Volume({});

Import keeps every field the Compose spec defines for a service, volume, network, config or secret, and x- extension fields. A field the spec does not define is dropped, and the import names it in a warning. A top-level key other than services, volumes, networks, configs and secrets (such as name or include) is not imported and is also named in a warning.

An export name is the compose key in camelCase. When the key is not a TypeScript identifier (db-password, nginx.conf), or two kinds share it, the import wraps the entity in composeKey, so the build writes the original key and references to it by name still match:

export const dbPassword = composeKey("db-password", new DockerSecret({ environment: "DB_PASSWORD" }));
Terminal window
chant import --lexicon docker Dockerfile -o src/web/

A Dockerfile imports as steps, one per instruction, in the order the file has them, so building the import writes the same instructions in the same order. ARG lines before the first FROM become globalArgs. Line continuations and heredocs are kept as written. Comments are dropped.

Given:

Dockerfile
# Build stage
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build \
&& npm prune --omit=dev
FROM node:${NODE_VERSION}-alpine
WORKDIR /app
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]

Produces:

src/web/main.ts
import { Dockerfile } from "@intentius/chant-lexicon-docker";
export const app = new Dockerfile({
globalArgs: ["NODE_VERSION=20"],
stages: [
{
from: "node:${NODE_VERSION}-alpine",
as: "build",
steps: [
{ workdir: "/app" },
{ copy: "package.json package-lock.json ./" },
{ run: "npm ci" },
{ copy: ". ." },
{ run: "npm run build \\\n && npm prune --omit=dev" },
],
},
{
from: "node:${NODE_VERSION}-alpine",
steps: [
{ workdir: "/app" },
{ copy: "--from=build /app/node_modules ./node_modules" },
{ copy: "--from=build /app/dist ./dist" },
{ user: "node" },
{ expose: "3000" },
{ cmd: '["node", "dist/server.js"]' },
],
},
],
});

The export is named app, so it builds Dockerfile.app. Rename the export to choose the file name, and point the Compose service at it by reference, which builds as the file name:

import { app } from "../web/main";
export const web = new Service({
build: { context: ".", dockerfile: app }, // dockerfile: Dockerfile.app
});

Exec-form values such as CMD stay strings; you can change them to lists ({ cmd: ["node", "dist/server.js"] }). See Dockerfiles for the step format.

Import is a one-time migration step, not an ongoing sync. After importing:

  1. Review the generated TypeScript; complex fields may need edits by hand
  2. Run chant lint src/ to catch issues
  3. Run chant build src --lexicon docker -o build/docker-compose.yml and diff the output (and the Dockerfile.* files beside it) against the originals
  4. Delete the original file and commit the TypeScript source
  • extends: (Compose file inheritance) is not supported
  • Dockerfile comments and parser directives (# syntax=..., # escape=...) are dropped
  • MAINTAINER, which has no step, is left as a comment with a warning; use a label step instead
  • Build args with complex shell expressions may need cleanup by hand
  • Environment variable syntax (${VAR}) is kept as written; replace it with env() by hand if you want ===

chant