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
51 changes: 49 additions & 2 deletions src/handlers/eval/config-bundle/config-bundle.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
testIO,
} from "../../../testing";
import { createRootHandler } from "../../index";
import type { UpdateConfigurationBundleInput } from "../types";
import type { CreateConfigurationBundleInput, UpdateConfigurationBundleInput } from "../types";

const REGION = "us-west-2";
const COMPONENT_ARN =
Expand Down Expand Up @@ -97,7 +97,7 @@ describe("eval config-bundle command hierarchy", () => {
.find((child) => child.name() === "create")
?.flags()
.map((candidate) => candidate.name),
).toEqual(["name", "components", "kms-key-arn"]);
).toEqual(["name", "components", "branch-name", "commit-message", "kms-key-arn"]);
expect(
configBundle
?.children()
Expand Down Expand Up @@ -164,6 +164,34 @@ describe("eval config-bundle command hierarchy", () => {
});

describe("config-bundle create", () => {
test("passes branch name, commit message, and KMS key", async () => {
const { core, route } = testConfigBundleCommand();

await route([
"eval",
"config-bundle",
"create",
"--name",
"orders-prompt",
"--components",
JSON.stringify(COMPONENTS),
"--branch-name",
"feature/order-routing",
"--commit-message",
"Add order routing configuration",
"--kms-key-arn",
"arn:aws:kms:us-west-2:123456789012:key/initial",
]);

expect(callArgs(core, "createConfigurationBundle")[0]).toEqual({
bundleName: "orders-prompt",
components: COMPONENTS,
branchName: "feature/order-routing",
commitMessage: "Add order routing configuration",
kmsKeyArn: "arn:aws:kms:us-west-2:123456789012:key/initial",
} satisfies CreateConfigurationBundleInput);
});

test("reads components from stdin", async () => {
const { core, route } = testConfigBundleCommand(JSON.stringify(COMPONENTS));

Expand All @@ -182,6 +210,25 @@ describe("config-bundle create", () => {
});
});

test("rejects a branch name that does not match service constraints", async () => {
const { core, route } = testConfigBundleCommand();

await expect(
route([
"eval",
"config-bundle",
"create",
"--name",
"orders-prompt",
"--components",
JSON.stringify(COMPONENTS),
"--branch-name",
"1-invalid",
]),
).rejects.toThrow(/Invalid value for option '--branch-name'/);
expect(core.eval.calls).toHaveLength(0);
});

test.each([
["an empty map", {}],
["a component without configuration", { [COMPONENT_ARN]: {} }],
Expand Down
15 changes: 15 additions & 0 deletions src/handlers/eval/config-bundle/create/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import type { Core } from "../../../types";
import { coreOptsFromCtx } from "../../../utils";
import { resolveConfigurationBundleComponents } from "../components";

const BranchNameSchema = z
.string()
.min(1)
.max(128)
.regex(/^[a-zA-Z][a-zA-Z0-9_/-]{0,127}$/, "Value must match [a-zA-Z][a-zA-Z0-9_/-]");
const CommitMessageSchema = z.string().max(500);

export const createCreateConfigBundleHandler = (core: Core, io: AppIO) =>
createHandler({
name: "create",
Expand All @@ -19,6 +26,12 @@ export const createCreateConfigBundleHandler = (core: Core, io: AppIO) =>
z.string().optional(),
{ sensitive: true },
),
flag("branch-name", "branch name for the initial configuration", BranchNameSchema.optional()),
flag(
"commit-message",
"message describing the initial configuration",
CommitMessageSchema.optional(),
),
flag(
"kms-key-arn",
"customer managed KMS key ARN for component configurations",
Expand All @@ -42,6 +55,8 @@ export const createCreateConfigBundleHandler = (core: Core, io: AppIO) =>
{
bundleName: flags["name"],
components,
branchName: flags["branch-name"],
commitMessage: flags["commit-message"],
kmsKeyArn: flags["kms-key-arn"],
},
coreOptsFromCtx(ctx),
Expand Down
2 changes: 1 addition & 1 deletion src/handlers/eval/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export type RoleScopeWarning = {
export type CreateDatasetInput = CreateDatasetRequest;
export type CreateConfigurationBundleInput = Pick<
CreateConfigurationBundleRequest,
"bundleName" | "components" | "kmsKeyArn"
"bundleName" | "components" | "branchName" | "commitMessage" | "kmsKeyArn"
>;
export type UpdateConfigurationBundleInput = Required<
Pick<UpdateConfigurationBundleRequest, "components" | "commitMessage" | "branchName">
Expand Down
23 changes: 23 additions & 0 deletions src/projectSchemas/config-bundle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, expect, test } from "bun:test";
import { ConfigBundleBranchNameSchema } from "./config-bundle";

describe("ConfigBundleBranchNameSchema", () => {
test.each(["mainline", "feature/order-routing", "release-2026_08"])(
"accepts service-compatible branch name %s",
(branchName) => {
expect(ConfigBundleBranchNameSchema.safeParse(branchName).success).toBe(true);
},
);

test.each(["", "1-mainline", "feature branch", "feature.order"])(
"rejects service-incompatible branch name %s",
(branchName) => {
expect(ConfigBundleBranchNameSchema.safeParse(branchName).success).toBe(false);
},
);

test("enforces the service's 128-character maximum", () => {
expect(ConfigBundleBranchNameSchema.safeParse(`a${"b".repeat(127)}`).success).toBe(true);
expect(ConfigBundleBranchNameSchema.safeParse(`a${"b".repeat(128)}`).success).toBe(false);
});
});
6 changes: 5 additions & 1 deletion src/projectSchemas/config-bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ export const ComponentConfigurationSchema = z.object({
export type ComponentConfiguration = z.infer<typeof ComponentConfigurationSchema>;
export const ComponentConfigurationMapSchema = z.record(z.string(), ComponentConfigurationSchema);
export type ComponentConfigurationMap = z.infer<typeof ComponentConfigurationMapSchema>;
export const ConfigBundleBranchNameSchema = z.string().max(128);
export const ConfigBundleBranchNameSchema = z
.string()
.min(1)
.max(128)
.regex(/^[a-zA-Z][a-zA-Z0-9_/-]{0,127}$/, "Value must match [a-zA-Z][a-zA-Z0-9_/-]");
export const ConfigBundleCommitMessageSchema = z.string().max(500);
export const ConfigBundleSchema = z.object({
name: ConfigBundleNameSchema,
Expand Down
Loading