mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix(incremental): a Terraform input change declines to the full rebuild
A Terraform module's output read can live in one file (outputs.tf: module.net.vpc_id) and the module's source in another (main.tf). When only main.tf changed -- its source pointed elsewhere -- the incremental repair recomputed that file's edges alone, so the read, held by two unchanged files, stayed in the graph although a full reindex no longer has it. Measured on kube-hetzner: 18 stale output reads after moving one module's source. Terraform, OpenTofu and Terragrunt inputs reach past their own file the way Kubernetes bundles do: the files of a module directory resolve each other's references, a module call decides what another file reads, a .tfvars file sets the variables beside it. A change to a .tf, .tofu, .tfvars or .hcl file now declines the repair to the full rebuild, as a kustomization or manifest change already does. The incremental re-index now equals a full index in all 18 Terraform scenarios, the stale-read one included; a Terraform edit costs a full rebuild (fabric: 1.64 -> 1.90 s). A pipeline test moves a module's source and requires the read to go. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
949e875337
commit
246d1a0a4b
2 files changed
+76
No files matched your search
@@ -2516,6 +2516,28 @@ static const char *closure_k8s_decline(cbm_store_t *store, const char *project,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Terraform / OpenTofu / Terragrunt inputs reach past their own file
|
||||
* (pass_terraform.c): the files of a module directory resolve each other's
|
||||
* references, a module call's source decides which outputs another file of
|
||||
* the directory reads, a .tfvars file sets the variables beside it, and a
|
||||
* Terragrunt unit includes and reads other .hcl files. The repair recomputes
|
||||
* the changed files' edges only, so a delta touching any of them -- an edge
|
||||
* held by two unchanged files may hang on it -- declines to the full
|
||||
* rebuild. Returns the reason, or NULL. */
|
||||
static const char *closure_terraform_decline(char **paths, int n) {
|
||||
static const char *const exts[] = {".tf", ".tofu", ".tfvars", ".hcl"};
|
||||
for (int i = 0; i < n; i++) {
|
||||
const char *slash = strrchr(paths[i], '/');
|
||||
const char *dot = strrchr(slash ? slash + SKIP_ONE : paths[i], '.');
|
||||
for (size_t k = 0; dot && k < sizeof(exts) / sizeof(exts[0]); k++) {
|
||||
if (strcmp(dot, exts[k]) == 0) {
|
||||
return "terraform_input_changed";
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The env join (pass_envjoin.c) reads each image's files from its
|
||||
* Dockerfile's Module, stored with the ignore patterns of its context: an
|
||||
* ignore file changing, or a deletion of a file an image copies (which
|
||||
@@ -2737,6 +2759,10 @@ static int closure_try_plan(cbm_pipeline_t *p, cbm_store_t *store, const char *p
|
||||
if (decline) {
|
||||
goto done;
|
||||
}
|
||||
decline = closure_terraform_decline(changed_paths, n_changed + n_deleted);
|
||||
if (decline) {
|
||||
goto done;
|
||||
}
|
||||
decline =
|
||||
closure_stem_mark_decline(project, files, file_count, changed_paths + n_changed, n_deleted);
|
||||
if (decline) {
|
||||
|
||||
@@ -1490,6 +1490,55 @@ TEST(pipeline_incremental_preserves_cross_file_calls) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
static void write_temp_file(const char *dir, const char *name, const char *content);
|
||||
|
||||
/* Regression: a Terraform module's output read lives in one file
|
||||
* (infra/outputs.tf: module.net.vpc_id) and the module's source in another
|
||||
* (infra/main.tf). Pointing the source elsewhere edits main.tf alone; the
|
||||
* repair route recomputed the changed file's edges only, so the read -- held
|
||||
* by two UNCHANGED files, outputs.tf and modules/net/main.tf -- stayed in the
|
||||
* graph, which a full reindex no longer has. A Terraform input change now
|
||||
* declines to the full rebuild (closure_terraform_decline). */
|
||||
TEST(pipeline_incremental_drops_terraform_reads_of_a_moved_module) {
|
||||
if (setup_test_repo() != 0) {
|
||||
FAIL("failed to create temp dir");
|
||||
}
|
||||
write_temp_file(g_tmpdir, "infra/main.tf", "module \"net\" {\n source = \"./modules/net\"\n}\n");
|
||||
write_temp_file(g_tmpdir, "infra/outputs.tf",
|
||||
"output \"net_vpc\" {\n value = module.net.vpc_id\n}\n");
|
||||
write_temp_file(g_tmpdir, "infra/modules/net/main.tf",
|
||||
"output \"vpc_id\" {\n value = \"x\"\n}\n");
|
||||
char db_path[512];
|
||||
snprintf(db_path, sizeof(db_path), "%s/test_incr_tf.db", g_tmpdir);
|
||||
|
||||
cbm_pipeline_t *p1 = cbm_pipeline_new(g_tmpdir, db_path, CBM_MODE_FULL);
|
||||
ASSERT_NOT_NULL(p1);
|
||||
ASSERT_EQ(cbm_pipeline_run(p1), 0);
|
||||
cbm_store_t *s1 = cbm_store_open_path(db_path);
|
||||
ASSERT_NOT_NULL(s1);
|
||||
ASSERT_TRUE(cross_file_edge_exists(s1, cbm_pipeline_project_name(p1), "output.net_vpc",
|
||||
"output.vpc_id", "USAGE"));
|
||||
cbm_store_close(s1);
|
||||
cbm_pipeline_free(p1);
|
||||
|
||||
/* the module's source moves to a directory that does not exist */
|
||||
write_temp_file(g_tmpdir, "infra/main.tf",
|
||||
"module \"net\" {\n source = \"./modules/none\"\n}\n");
|
||||
|
||||
cbm_pipeline_t *p2 = cbm_pipeline_new(g_tmpdir, db_path, CBM_MODE_FULL);
|
||||
ASSERT_NOT_NULL(p2);
|
||||
ASSERT_EQ(cbm_pipeline_run(p2), 0);
|
||||
cbm_store_t *s2 = cbm_store_open_path(db_path);
|
||||
ASSERT_NOT_NULL(s2);
|
||||
ASSERT_FALSE(cross_file_edge_exists(s2, cbm_pipeline_project_name(p2), "output.net_vpc",
|
||||
"output.vpc_id", "USAGE"));
|
||||
cbm_store_close(s2);
|
||||
cbm_pipeline_free(p2);
|
||||
|
||||
teardown_test_repo();
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* TS/JS receiver-aware weak-strategy suppression (#592/#606 direction; Perl
|
||||
* precedent #477). A member call x.foo() whose receiver TYPE the TS-LSP cannot
|
||||
* resolve (a regex literal `re.test()`) must NOT be bound to a same-named
|
||||
@@ -23143,6 +23192,7 @@ SUITE(pipeline) {
|
||||
RUN_TEST(pipeline_nix_scoped_binding_calls_resolve);
|
||||
RUN_TEST(pipeline_hcl_block_reference_resolves_to_its_block);
|
||||
RUN_TEST(pipeline_incremental_preserves_cross_file_calls);
|
||||
RUN_TEST(pipeline_incremental_drops_terraform_reads_of_a_moved_module);
|
||||
RUN_TEST(pipeline_field_named_like_method_keeps_its_reads_and_writes);
|
||||
RUN_TEST(pipeline_objectscript_export_preserves_calls_sequential_parallel);
|
||||
RUN_TEST(pipeline_objectscript_export_incremental_matches_full_relationships);
|
||||
|
||||
Reference in new issue
Block a user