Skip to content

Commit e6a9a44

Browse files
committed
feat(discover): add CBM_EXTRA_SKIP_DIRS env var for site-specific skip dirs
Callers can skip extra directory names (comma-separated, whitespace-trimmed) during indexing without a rebuild, checked alongside the hardcoded ALWAYS_SKIP_DIRS in cbm_should_skip_dir. Applies in every mode. The env var is read through cbm_safe_getenv into a local buffer so the value is mt-safe (raw getenv's pointer can be invalidated by a concurrent setenv/putenv) and correct for UTF-8 on Windows. The repro test uses the portable cbm_setenv/cbm_unsetenv wrappers rather than raw POSIX setenv/unsetenv. Covered by a table-driven test that verifies baseline-discovered -> skipped-with-env-set (including a whitespace-padded CSV entry) -> discovered-again-after-unset. Signed-off-by: ChethanUK <chethanuk@outlook.com>
1 parent a5351d3 commit e6a9a44

3 files changed

Lines changed: 100 additions & 1 deletion

File tree

docs/cbmignore.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ a path wins. For directories:
6969
1. **Built-in skip list**`.git`, `node_modules`, `dist`, `target`,
7070
`vendor`, tool caches, etc. (60+ names; the fast/moderate index modes add
7171
more, e.g. `docs`, `examples`, `testdata`). Not overridable from any
72-
ignore file today.
72+
ignore file today. The hardcoded list is intentionally fixed across all
73+
repos, but you can extend it per invocation with the `CBM_EXTRA_SKIP_DIRS`
74+
environment variable — a comma-separated list of extra directory names to
75+
skip in every mode (e.g. `CBM_EXTRA_SKIP_DIRS=".dbt,.terraform-state"`).
76+
Useful for site- or repo-specific artifact directories that don't belong
77+
in the universal list. Whitespace around each name is trimmed.
7378
2. **Repo `.gitignore`**`<repo>/.gitignore` merged with
7479
`<git-common-dir>/info/exclude` (worktree-aware); later patterns win on
7580
conflict. Honored even when the indexed directory is not a git repo root.

src/discover/discover.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,44 @@ static bool resolve_global_excludes_path(char *out, size_t out_sz) {
334334
return false;
335335
}
336336

337+
/* ── Env-var-configurable extra skip dirs ──────────────────────────
338+
* CBM_EXTRA_SKIP_DIRS: comma-separated directory names to skip, on top of
339+
* the hardcoded ALWAYS_SKIP_DIRS above. Lets a caller add repo-specific or
340+
* site-specific artifact dirs (e.g. ".dbt", a Terraform state dir, a prior
341+
* AI-tooling output dir) per invocation, without a rebuild. Applies in
342+
* every mode, same as ALWAYS_SKIP_DIRS. Not cached across calls (the list
343+
* is short and this keeps the function safe to call from any thread). */
344+
static bool str_in_env_csv(const char *s, const char *csv) {
345+
if (!s || !csv || !csv[0]) {
346+
return false;
347+
}
348+
size_t slen = strlen(s);
349+
const char *p = csv;
350+
while (*p) {
351+
const char *comma = strchr(p, ',');
352+
size_t seg_len = comma ? (size_t)(comma - p) : strlen(p);
353+
354+
const char *seg = p;
355+
size_t len = seg_len;
356+
while (len > 0 && isspace((unsigned char)seg[0])) {
357+
seg++;
358+
len--;
359+
}
360+
while (len > 0 && isspace((unsigned char)seg[len - 1])) {
361+
len--;
362+
}
363+
364+
if (len == slen && strncmp(seg, s, len) == 0) {
365+
return true;
366+
}
367+
if (!comma) {
368+
break;
369+
}
370+
p = comma + 1;
371+
}
372+
return false;
373+
}
374+
337375
/* ── Public filter functions ─────────────────────── */
338376

339377
bool cbm_should_skip_dir(const char *dirname, cbm_index_mode_t mode) {
@@ -345,6 +383,16 @@ bool cbm_should_skip_dir(const char *dirname, cbm_index_mode_t mode) {
345383
return true;
346384
}
347385

386+
/* Read through cbm_safe_getenv so the value is copied into a caller-owned
387+
* buffer before use — raw getenv() is mt-unsafe (pointer can be invalidated
388+
* by a concurrent setenv/putenv) and mishandles UTF-8 on Windows. */
389+
char extra_skip[CBM_SZ_1K];
390+
if (str_in_env_csv(dirname,
391+
cbm_safe_getenv("CBM_EXTRA_SKIP_DIRS", extra_skip,
392+
sizeof(extra_skip), NULL))) {
393+
return true;
394+
}
395+
348396
/* Fast discovery applies to both MODERATE and FAST — only FULL keeps everything. */
349397
if (mode != CBM_MODE_FULL) {
350398
if (str_in_list(dirname, FAST_SKIP_DIRS)) {

tests/repro/repro_invariant_discovery_fqn.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,51 @@ TEST(invariant_discovery_always_skip_dirs) {
261261
PASS();
262262
}
263263

264+
/* ── PART A TEST — CBM_EXTRA_SKIP_DIRS env var ─────────────────────────────
265+
*
266+
* Verifies the env-var-configurable extra skip list (discover.c
267+
* cbm_should_skip_dir -> str_in_env_csv): a name NOT in the hardcoded
268+
* ALWAYS_SKIP_DIRS is skipped once listed in CBM_EXTRA_SKIP_DIRS, and goes
269+
* back to being indexed once the env var is cleared — proving the behavior
270+
* is genuinely env-gated, not accidentally hardcoded.
271+
*/
272+
TEST(invariant_discovery_env_extra_skip_dirs) {
273+
const char *custom_dir = "dbt_target_test_fixture";
274+
275+
/* Baseline: not in any hardcoded list, so it must be discovered today. */
276+
cbm_unsetenv("CBM_EXTRA_SKIP_DIRS");
277+
int baseline = check_dir_skipped(custom_dir, CBM_MODE_FULL);
278+
if (baseline < 0) {
279+
printf(" SETUP-ERROR %-32s baseline\n", custom_dir);
280+
ASSERT_EQ(1, 0);
281+
}
282+
if (baseline == 0) {
283+
printf(" UNEXPECTED %-32s already skipped with no env var set\n", custom_dir);
284+
}
285+
ASSERT_GT(baseline, 0);
286+
287+
/* With the env var listing it (alongside noise entries, whitespace-padded
288+
* to also exercise the trimming path), it must now be skipped. */
289+
cbm_setenv("CBM_EXTRA_SKIP_DIRS", ".codegraph, graphify-out ,dbt_target_test_fixture,.wiki", 1);
290+
int with_env = check_dir_skipped(custom_dir, CBM_MODE_FULL);
291+
cbm_unsetenv("CBM_EXTRA_SKIP_DIRS");
292+
293+
if (with_env < 0) {
294+
printf(" SETUP-ERROR %-32s with env var\n", custom_dir);
295+
ASSERT_EQ(1, 0);
296+
}
297+
if (with_env != 0) {
298+
printf(" REGRESSION %-32s not skipped with CBM_EXTRA_SKIP_DIRS set\n", custom_dir);
299+
}
300+
ASSERT_EQ(with_env, 0);
301+
302+
/* And clearing it restores the baseline (not permanently sticky). */
303+
int after_clear = check_dir_skipped(custom_dir, CBM_MODE_FULL);
304+
ASSERT_GT(after_clear, 0);
305+
306+
PASS();
307+
}
308+
264309
/* ── PART A TEST — FAST_SKIP_DIRS table (mode != CBM_MODE_FULL) ────────────
265310
*
266311
* FAST_SKIP_DIRS entries are only skipped when mode != CBM_MODE_FULL.
@@ -794,6 +839,7 @@ SUITE(repro_invariant_discovery_fqn) {
794839
/* Part A — Discovery hygiene */
795840
RUN_TEST(invariant_discovery_control_always_found);
796841
RUN_TEST(invariant_discovery_always_skip_dirs);
842+
RUN_TEST(invariant_discovery_env_extra_skip_dirs);
797843
RUN_TEST(invariant_discovery_fast_skip_dirs);
798844

799845
/* Part B — FQN same-stem distinctness */

0 commit comments

Comments
 (0)