Skip to content

Commit 369c404

Browse files
authored
fix: resolve create harness Dockerfile paths from command cwd (#1474)
1 parent 77b54a5 commit 369c404

5 files changed

Lines changed: 59 additions & 2 deletions

File tree

src/cli/commands/create/command.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ function printCreateHarnessSummary(projectName: string, harnessName: string): vo
156156

157157
/** Handle CLI mode for the harness path */
158158
async function handleCreateHarnessCLI(options: CreateOptions): Promise<void> {
159-
const cwd = options.outputDir ?? getWorkingDirectory();
159+
const commandCwd = getWorkingDirectory();
160+
const cwd = options.outputDir ?? commandCwd;
160161
const name = options.name ?? options.projectName;
161162
const projectName = options.projectName ?? name;
162163

@@ -266,6 +267,7 @@ async function handleCreateHarnessCLI(options: CreateOptions): Promise<void> {
266267
additionalParams,
267268
containerUri: containerOption.containerUri,
268269
dockerfilePath: containerOption.dockerfilePath,
270+
dockerfileBaseDir: commandCwd,
269271
skipMemory: options.harnessMemory === false,
270272
maxIterations: options.maxIterations ? Number(options.maxIterations) : undefined,
271273
maxTokens: options.maxTokens ? Number(options.maxTokens) : undefined,

src/cli/commands/create/harness-action.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export interface CreateHarnessProjectOptions {
1919
skipMemory?: boolean;
2020
containerUri?: string;
2121
dockerfilePath?: string;
22+
dockerfileBaseDir?: string;
2223
maxIterations?: number;
2324
maxTokens?: number;
2425
timeoutSeconds?: number;
@@ -69,6 +70,7 @@ export async function createProjectWithHarness(options: CreateHarnessProjectOpti
6970
additionalParams: options.additionalParams,
7071
containerUri: options.containerUri,
7172
dockerfilePath: options.dockerfilePath,
73+
dockerfileBaseDir: options.dockerfileBaseDir ?? options.cwd,
7274
skipMemory: options.skipMemory,
7375
maxIterations: options.maxIterations,
7476
maxTokens: options.maxTokens,

src/cli/primitives/HarnessPrimitive.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ export interface AddHarnessOptions {
153153
/** Harness-level tags, merged with project tags (CFN Tags). */
154154
tags?: Record<string, string>;
155155
withInvokeScript?: boolean;
156+
dockerfileBaseDir?: string;
156157
selectedTools?: string[];
157158
mcpName?: string;
158159
mcpUrl?: string;
@@ -224,9 +225,10 @@ export class HarnessPrimitive extends BasePrimitive<AddHarnessOptions, Removable
224225
let dockerfile: string | undefined;
225226
if (options.dockerfilePath) {
226227
const projectRoot = dirname(configBaseDir);
228+
const dockerfileBaseDir = options.dockerfileBaseDir ?? projectRoot;
227229
const srcPath = isAbsolute(options.dockerfilePath)
228230
? options.dockerfilePath
229-
: resolve(projectRoot, options.dockerfilePath);
231+
: resolve(dockerfileBaseDir, options.dockerfilePath);
230232
try {
231233
await access(srcPath);
232234
} catch {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { ConfigIO } from '../../../lib';
2+
import { createDefaultProjectSpec } from '../../project';
3+
import { HarnessPrimitive } from '../HarnessPrimitive';
4+
import { randomUUID } from 'node:crypto';
5+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
6+
import { tmpdir } from 'node:os';
7+
import { join } from 'node:path';
8+
import { afterEach, describe, expect, it } from 'vitest';
9+
10+
describe('HarnessPrimitive', () => {
11+
const tempDirs: string[] = [];
12+
13+
afterEach(async () => {
14+
await Promise.all(tempDirs.map(dir => rm(dir, { recursive: true, force: true })));
15+
tempDirs.length = 0;
16+
});
17+
18+
it('resolves create-flow Dockerfile paths from the command working directory', async () => {
19+
const commandCwd = join(tmpdir(), `harness-dockerfile-${randomUUID()}`);
20+
tempDirs.push(commandCwd);
21+
const projectRoot = join(commandCwd, 'HarnessProject');
22+
const configBaseDir = join(projectRoot, 'agentcore');
23+
await mkdir(projectRoot, { recursive: true });
24+
25+
const configIO = new ConfigIO({ baseDir: configBaseDir });
26+
await configIO.initializeBaseDir();
27+
await configIO.writeAWSDeploymentTargets([]);
28+
await configIO.writeDeployedState({ targets: {} });
29+
await configIO.writeProjectSpec(createDefaultProjectSpec('HarnessProject'));
30+
31+
await writeFile(join(commandCwd, 'Dockerfile.custom'), 'FROM public.ecr.aws/docker/library/python:3.12\n');
32+
33+
const result = await new HarnessPrimitive().add({
34+
name: 'MyHarness',
35+
modelProvider: 'bedrock',
36+
modelId: 'global.anthropic.claude-sonnet-4-6',
37+
skipMemory: true,
38+
dockerfilePath: './Dockerfile.custom',
39+
dockerfileBaseDir: commandCwd,
40+
configBaseDir,
41+
});
42+
43+
expect(result.success).toBe(true);
44+
const harnessDir = join(projectRoot, 'app', 'MyHarness');
45+
await expect(readFile(join(harnessDir, 'Dockerfile.custom'), 'utf-8')).resolves.toContain('python:3.12');
46+
47+
const harnessSpec = JSON.parse(await readFile(join(harnessDir, 'harness.json'), 'utf-8'));
48+
expect(harnessSpec.dockerfile).toBe('Dockerfile.custom');
49+
});
50+
});

src/cli/tui/screens/create/useCreateFlow.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,7 @@ export function useCreateFlow(cwd: string): CreateFlowState {
624624
...memoryOptions,
625625
containerUri: addHarnessConfig.containerUri,
626626
dockerfilePath: addHarnessConfig.dockerfilePath,
627+
dockerfileBaseDir: cwd,
627628
maxIterations: addHarnessConfig.maxIterations,
628629
maxTokens: addHarnessConfig.maxTokens,
629630
timeoutSeconds: addHarnessConfig.timeoutSeconds,

0 commit comments

Comments
 (0)