Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/content/docs/cli/build.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ The `--sandbox` flag always wins over `build.sandbox` when both are present.

A components-only project need not declare a lexicon plugin at all. Lexicons are loaded best-effort here, so a project whose `chant.config.ts` lists none still generates.

`--env <name>` is threaded into the generated pipeline as the environment it deploys, and `--param` / `--params-file` bind build-time parameters the same way they do for a resource build. With `--format json` the result is `{ stages, jobs, yaml }` on stdout. The same stages and jobs are available as graph IR through [`chant graph --components --format ir --projection <lexicon>`](/chant/cli/graph/#--projection-lexicon--the-cipipeline-projection-989).
`--env <name>` is threaded into the generated pipeline as the environment it deploys, and `--param` / `--params-file` bind build-time parameters the same way they do for a resource build. With `--format json` the result is `{ stages, jobs, yaml, env }` on stdout, where `env` is the generator's own resolved environment (present whenever the generator returns one; omitted otherwise) rather than something a consumer has to re-derive by parsing the YAML back. The same stages, jobs and env are available as graph IR through [`chant graph --components --format ir --projection <lexicon>`](/chant/cli/graph/#--projection-lexicon--the-cipipeline-projection-989).

## Build-time parameters

Expand Down
80 changes: 80 additions & 0 deletions packages/core/src/cli/handlers/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,83 @@ describe("runBuild --components --generate (chant #1108 build-time parameters)",
expect(generateComponentsPipelineMock).not.toHaveBeenCalled();
});
});

/**
* chant #2060. `chant build --components --generate --format json` forwarded
* only `{ stages, jobs, yaml }`, dropping `result.env` even though the
* generator (#2046, PR #2050) resolves and returns it. A consumer reading the
* structured output (behold does) had no environment identity except by
* parsing the YAML back, which #2046 was meant to retire.
*/
describe("runBuild --components --generate --format json (chant #2060 env passthrough)", () => {
beforeEach(() => {
generateComponentsPipelineMock.mockReset();
loadChantConfigUpwardMock.mockReset().mockResolvedValue({ config: {} });
});

test("--format json includes result.env alongside stages, jobs and yaml", async () => {
generateComponentsPipelineMock.mockResolvedValue({
success: true,
yaml: "name: chant-components-prod\nenv:\n CHANT_ENV: prod\n",
stages: ["deploy"],
jobs: [{ jobName: "deploy-web", component: "web", stage: "deploy", needs: [] }],
env: "prod",
});
const stdout: string[] = [];
vi.spyOn(console, "log").mockImplementation((s: string) => { stdout.push(s); });

const exit = await runBuild({
args: makeArgs({ format: "json", env: "prod" }),
plugins: [],
serializers: [],
});

expect(exit).toBe(0);
expect(stdout).toHaveLength(1);
const printed = JSON.parse(stdout[0]);
expect(printed).toEqual({
stages: ["deploy"],
jobs: [{ jobName: "deploy-web", component: "web", stage: "deploy", needs: [] }],
yaml: "name: chant-components-prod\nenv:\n CHANT_ENV: prod\n",
env: "prod",
});
vi.restoreAllMocks();
});

test("--format json omits env when the generator's result carries none", async () => {
generateComponentsPipelineMock.mockResolvedValue({
success: true,
yaml: "stages: []",
stages: [],
jobs: [],
});
const stdout: string[] = [];
vi.spyOn(console, "log").mockImplementation((s: string) => { stdout.push(s); });

const exit = await runBuild({ args: makeArgs({ format: "json" }), plugins: [], serializers: [] });

expect(exit).toBe(0);
const printed = JSON.parse(stdout[0]);
expect(printed).toEqual({ stages: [], jobs: [], yaml: "stages: []" });
expect(printed).not.toHaveProperty("env");
vi.restoreAllMocks();
});

test("the default text format is unchanged: env is not forwarded and only the raw yaml is printed", async () => {
generateComponentsPipelineMock.mockResolvedValue({
success: true,
yaml: "name: chant-components-prod\nenv:\n CHANT_ENV: prod\n",
stages: ["deploy"],
jobs: [{ jobName: "deploy-web", component: "web", stage: "deploy", needs: [] }],
env: "prod",
});
const stdout: string[] = [];
vi.spyOn(console, "log").mockImplementation((s: string) => { stdout.push(s); });

const exit = await runBuild({ args: makeArgs({ env: "prod" }), plugins: [], serializers: [] });

expect(exit).toBe(0);
expect(stdout).toEqual(["name: chant-components-prod\nenv:\n CHANT_ENV: prod\n"]);
vi.restoreAllMocks();
});
});
16 changes: 15 additions & 1 deletion packages/core/src/cli/handlers/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,21 @@ async function runGenerateComponents(ctx: CommandContext): Promise<number> {

const yaml = result.yaml ?? "";
if (args.format === "json") {
console.log(JSON.stringify({ stages: result.stages, jobs: result.jobs, yaml }, null, 2));
console.log(
JSON.stringify(
{
stages: result.stages,
jobs: result.jobs,
yaml,
// The environment the generated pipeline deploys (#2046), the
// generator's own resolution, forwarded rather than left for a
// consumer to re-derive by parsing the YAML back (#2060).
...(result.env ? { env: result.env } : {}),
},
null,
2,
),
);
} else if (args.output) {
const outputPath = resolve(args.output);
mkdirSync(dirname(outputPath), { recursive: true });
Expand Down
Loading