Skip to content

Commit 930816f

Browse files
authored
fix: locate Terraform entrypoint file (coder#16753)
Fixes: coder#16360
1 parent e27953d commit 930816f

File tree

4 files changed

+158
-6
lines changed

4 files changed

+158
-6
lines changed

site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.test.tsx

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ import {
2222
waitForLoaderToBeRemoved,
2323
} from "testHelpers/renderHelpers";
2424
import { server } from "testHelpers/server";
25+
import type { FileTree } from "utils/filetree";
2526
import type { MonacoEditorProps } from "./MonacoEditor";
2627
import { Language } from "./PublishTemplateVersionDialog";
27-
import TemplateVersionEditorPage from "./TemplateVersionEditorPage";
28+
import TemplateVersionEditorPage, {
29+
findEntrypointFile,
30+
} from "./TemplateVersionEditorPage";
2831

2932
const { API } = apiModule;
3033

@@ -409,3 +412,127 @@ function renderEditorPage(queryClient: QueryClient) {
409412
</AppProviders>,
410413
);
411414
}
415+
416+
describe("Find entrypoint", () => {
417+
it("empty tree", () => {
418+
const ft: FileTree = {};
419+
const mainFile = findEntrypointFile(ft);
420+
expect(mainFile).toBeUndefined();
421+
});
422+
it("flat structure, main.tf in root", () => {
423+
const ft: FileTree = {
424+
"aaa.tf": "hello",
425+
"bbb.tf": "world",
426+
"main.tf": "foobar",
427+
"nnn.tf": "foobaz",
428+
};
429+
430+
const mainFile = findEntrypointFile(ft);
431+
expect(mainFile).toBe("main.tf");
432+
});
433+
it("flat structure, no main.tf", () => {
434+
const ft: FileTree = {
435+
"aaa.tf": "hello",
436+
"bbb.tf": "world",
437+
"ccc.tf": "foobaz",
438+
"nnn.tf": "foobaz",
439+
};
440+
441+
const mainFile = findEntrypointFile(ft);
442+
expect(mainFile).toBe("nnn.tf");
443+
});
444+
it("with dirs, single main.tf", () => {
445+
const ft: FileTree = {
446+
"aaa-dir": {
447+
"aaa.tf": "hello",
448+
"bbb.tf": "world",
449+
},
450+
"bbb-dir": {
451+
"aaa.tf": "hello",
452+
"bbb.tf": "world",
453+
},
454+
"main.tf": "foobar",
455+
"nnn.tf": "foobaz",
456+
};
457+
458+
const mainFile = findEntrypointFile(ft);
459+
expect(mainFile).toBe("main.tf");
460+
});
461+
it("with dirs, multiple main.tf's", () => {
462+
const ft: FileTree = {
463+
"aaa-dir": {
464+
"aaa.tf": "hello",
465+
"bbb.tf": "world",
466+
"main.tf": "foobar",
467+
},
468+
"bbb-dir": {
469+
"aaa.tf": "hello",
470+
"bbb.tf": "world",
471+
"main.tf": "foobar",
472+
},
473+
"ccc-dir": {
474+
"aaa.tf": "hello",
475+
"bbb.tf": "world",
476+
},
477+
"main.tf": "foobar",
478+
"nnn.tf": "foobaz",
479+
"zzz-dir": {
480+
"aaa.tf": "hello",
481+
"bbb.tf": "world",
482+
"main.tf": "foobar",
483+
},
484+
};
485+
486+
const mainFile = findEntrypointFile(ft);
487+
expect(mainFile).toBe("main.tf");
488+
});
489+
it("with dirs, multiple main.tf, no main.tf in root", () => {
490+
const ft: FileTree = {
491+
"aaa-dir": {
492+
"aaa.tf": "hello",
493+
"bbb.tf": "world",
494+
"main.tf": "foobar",
495+
},
496+
"bbb-dir": {
497+
"aaa.tf": "hello",
498+
"bbb.tf": "world",
499+
"main.tf": "foobar",
500+
},
501+
"ccc-dir": {
502+
"aaa.tf": "hello",
503+
"bbb.tf": "world",
504+
},
505+
"nnn.tf": "foobaz",
506+
"zzz-dir": {
507+
"aaa.tf": "hello",
508+
"bbb.tf": "world",
509+
"main.tf": "foobar",
510+
},
511+
};
512+
513+
const mainFile = findEntrypointFile(ft);
514+
expect(mainFile).toBe("aaa-dir/main.tf");
515+
});
516+
it("with dirs, multiple main.tf, unordered file tree", () => {
517+
const ft: FileTree = {
518+
"ccc-dir": {
519+
"aaa.tf": "hello",
520+
"bbb.tf": "world",
521+
"main.tf": "foobar",
522+
},
523+
"aaa-dir": {
524+
"aaa.tf": "hello",
525+
"bbb.tf": "world",
526+
"main.tf": "foobar",
527+
},
528+
"zzz-dir": {
529+
"aaa.tf": "hello",
530+
"bbb.tf": "world",
531+
"main.tf": "foobar",
532+
},
533+
};
534+
535+
const mainFile = findEntrypointFile(ft);
536+
expect(mainFile).toBe("aaa-dir/main.tf");
537+
});
538+
});

site/src/pages/TemplateVersionEditorPage/TemplateVersionEditorPage.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const TemplateVersionEditorPage: FC = () => {
9090
// File navigation
9191
// It can be undefined when a selected file is deleted
9292
const activePath: string | undefined =
93-
searchParams.get("path") ?? findInitialFile(fileTree ?? {});
93+
searchParams.get("path") ?? findEntrypointFile(fileTree ?? {});
9494
const onActivePathChange = (path: string | undefined) => {
9595
if (path) {
9696
searchParams.set("path", path);
@@ -357,10 +357,33 @@ const publishVersion = async (options: {
357357
return Promise.all(publishActions);
358358
};
359359

360-
const findInitialFile = (fileTree: FileTree): string | undefined => {
360+
const defaultMainTerraformFile = "main.tf";
361+
362+
// findEntrypointFile function locates the entrypoint file to open in the Editor.
363+
// It browses the filetree following these steps:
364+
// 1. If "main.tf" exists in root, return it.
365+
// 2. Traverse through sub-directories.
366+
// 3. If "main.tf" exists in a sub-directory, skip further browsing, and return the path.
367+
// 4. If "main.tf" was not found, return the last reviewed "".tf" file.
368+
export const findEntrypointFile = (fileTree: FileTree): string | undefined => {
361369
let initialFile: string | undefined;
362370

363-
traverse(fileTree, (content, filename, path) => {
371+
if (Object.keys(fileTree).find((key) => key === defaultMainTerraformFile)) {
372+
return defaultMainTerraformFile;
373+
}
374+
375+
let skip = false;
376+
traverse(fileTree, (_, filename, path) => {
377+
if (skip) {
378+
return;
379+
}
380+
381+
if (filename === defaultMainTerraformFile) {
382+
initialFile = path;
383+
skip = true;
384+
return;
385+
}
386+
364387
if (filename.endsWith(".tf")) {
365388
initialFile = path;
366389
}

site/src/utils/filetree.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,6 @@ test("traverse() go trough all the file tree files", () => {
122122
traverse(fileTree, (_content, _filename, fullPath) => {
123123
filePaths.push(fullPath);
124124
});
125-
const expectedFilePaths = ["main.tf", "images", "images/java.Dockerfile"];
125+
const expectedFilePaths = ["images", "images/java.Dockerfile", "main.tf"];
126126
expect(filePaths).toEqual(expectedFilePaths);
127127
});

site/src/utils/filetree.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@ export const traverse = (
9696
) => void,
9797
parent?: string,
9898
) => {
99-
for (const [filename, content] of Object.entries(fileTree)) {
99+
for (const [filename, content] of Object.entries(fileTree).sort(([a], [b]) =>
100+
a.localeCompare(b),
101+
)) {
100102
const fullPath = parent ? `${parent}/${filename}` : filename;
101103
callback(content, filename, fullPath);
102104
if (typeof content === "object") {

0 commit comments

Comments
 (0)