Repository navigation
feat: match a customer-configured manifest filename - #2265
pankajastro wants to merge 13 commits into
Conversation
Coverage Report for CI Build 37749960856Coverage increased (+0.06%) to 44.913%Details
Uncovered Changes
Coverage Regressions1 previously-covered line in 1 file lost coverage.
Coverage Stats
💛 - Coveralls |
ASTRO_COSMOS_BOOST_MANIFEST_NAME, when set, replaces manifest.json as the only filename Cosmos Boost pre-deploy discovery matches. A customer whose manifest isn't named manifest.json (and may keep other, differently-purposed manifests in the same directory) had nothing discovered at all; setting this env var to their manifest's actual name picks up exactly that file, wherever it sits in the tree, and leaves every other file untouched. Unset, behavior is unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
ASTRO_COSMOS_BOOST_MANIFEST_NAME now holds a JSON object mapping a directory (relative to the deployed path, "." for its root) to the filename discovery should match there, instead of one name applied to the whole deploy. A single global name couldn't represent different projects using different manifest naming conventions in one deploy; an unlisted directory still falls back to manifest.json. Also rejects malformed JSON and any value that isn't a bare filename (a path there would silently match nothing in findManifests' basename comparison), failing the deploy step instead of stamping nothing silently. Co-Authored-By: Claude <noreply@anthropic.com>
The plugin resolves both the hash sidecar and the slim manifest by directory alone, with no check of which manifest produced them. If a directory legitimately holds more than one valid dbt manifest (e.g. per-schedule manifests read by different DAGs), stamping it for one manifest would risk serving that manifest's slim copy and cache hash to a DAG that actually points at the other. hasSiblingDbtManifest checks, before writing anything, whether another *.json file in the same directory also parses as a valid dbt manifest. When it does, processManifest skips both artifacts for that unit (like a non-dbt manifest, but with a Warning naming the reason) and processProject skips only the slim attachment, since the project's own tree-hash sidecar isn't manifest-specific and stays safe to write. Co-Authored-By: Claude <noreply@anthropic.com>
- effectiveManifestName: drop the len(overrides)==0 fast path - a nil
map lookup already returns ("", false) safely, so it was dead code.
- manifestNameOverrides: filepath.Base("") == "." != "", so the
explicit name == "" check was redundant with the bare-filename check.
- Inline joinWarnings (used at exactly one call site) instead of a
standalone helper.
- Drop TestRunManifestNameOverrideInProjectRootIsSlimmedNotDiscovered:
fully subsumed by TestRunManifestNameOverridePerDirectory's dbt1 case.
- Replace two PreDeploy-level rejection tests (each spinning up a
tempdir to hit an error that happens before any filesystem access)
with one direct TestManifestNameOverrides covering unset/valid/
invalid-JSON/path-valued cases against the parser itself.
- Shrink processManifest's doc comment to a pointer at
hasSiblingDbtManifest instead of restating its rationale.
Co-Authored-By: Claude <noreply@anthropic.com>
No behavior change. Doc comments added across this PR's earlier commits restated what the code already shows; cut each to the non-obvious "why" (or the keying/format convention where that isn't otherwise visible), dropping the rest. Co-Authored-By: Claude <noreply@anthropic.com>
isDbtManifest checked only for metadata.dbt_schema_version's presence, which every dbt artifact carries (run_results.json, catalog.json, sources.json, semantic_manifest.json - not just manifest.json), each under its own schema URL. On main this was harmless: it only ever ran on a file already found by exact filename match. hasSiblingDbtManifest breaks that assumption by scanning every *.json in a directory by content - so a plain "dbt build" (which always writes run_results.json next to manifest.json in target/) would flag the directory ambiguous and silently stop stamping manifest.json for most real projects. Tightened to require the schema URL contain "/manifest/", which every dbt manifest schema does and no other dbt artifact's does (confirmed against schemas.getdbt.com). Also fixed two related error-handling gaps found in the same pass: processProject aborted the whole unit (skipping its always-safe tree-hash sidecar) when the sibling check itself failed to read the directory, instead of just skipping the slim attachment the way an actually-ambiguous directory does; and processManifest promoted that same read failure to a hard Err instead of the same fail-safe Skipped+Warning an ambiguous directory gets. Co-Authored-By: Claude <noreply@anthropic.com>
dir always descends from root (it only ever comes from walking root), so filepath.Rel(root, dir) can't realistically fail here - and even if it did, Rel returns "" on error, which already can't match a real override key (the documented convention is "." for root). The explicit early-return added nothing a plain fallthrough doesn't already do correctly. Co-Authored-By: Claude <noreply@anthropic.com>
The override key is a path relative to the whole deploy root, not to the project's own directory - worth pinning since a dbt project living under dags/ (dags/dbt/<name>/) is a common Astro layout, and it would be easy to assume the key is just the project's own folder name. Co-Authored-By: Claude <noreply@anthropic.com>
34347fa to
c6117a6
Compare
Rewritten from scratch, not incrementally: discovery no longer relies on any customer-configured filename. A file is treated as a manifest when its name contains "manifest" (case-insensitive) and its content validates as one (isDbtManifest) - covering manifest.json, manifest_full.json, manifest_by_schedule.json, etc. with nothing to configure. ASTRO_COSMOS_BOOST_MANIFEST_NAME, Options.ManifestNames, effectiveManifestName, and hasSiblingDbtManifest are all removed. Every manifest found now gets its own slim companion, named after itself (slimNameFor: "manifest_full.json" -> "manifest_full.slim.json", "manifest.json" -> "manifest.slim.json" as before) - so N manifests in one directory produce N slim files, never a single contested one. A project root with multiple manifests slims each in turn. Cleanup matches slim files by their .slim.json suffix instead of one fixed name, so it can find and remove any of them; ownership is still checked per file via its own _generated_by marker. .astro/dbt_metadata.json is untouched - same fixed name, same schema, same writer - so a directory with multiple manifests still gets one sidecar, written by whichever unit's processing finishes last. Co-Authored-By: Claude <noreply@anthropic.com>
bd6e318 to
a82ea27
Compare
|
@kaxil heads up - the approach changed significantly since my earlier replies on this thread, so those are superseded. Instead of a customer-configured env var (name override, then a JSON directory→filename map) plus an ambiguity guard that skipped stamping when a directory held more than one valid manifest, this now discovers manifests automatically: a file is treated as a manifest when its name contains "manifest" (case-insensitive) and its content validates as one - no configuration needed. The bigger change: every manifest found now gets its own slim companion, named after itself ( PR description is updated to match. Would appreciate another look when you have time. |
tatiana
left a comment
There was a problem hiding this comment.
Thanks, @pankajastro , for implementing the original approach we had agreed with.
I left two comments inline, I consider them blockers.
Please, could you confirm if we have a follow-up ticket for the plugin work?
dbt_metadata.json's schema bumps to 2: filtered_manifest (a single pointer) becomes manifests, a map keyed by source filename. Each entry carries that manifest's own content hash and its own slim_manifest pointer, so a directory holding more than one manifest no longer loses every sibling's identity to whichever one wrote last - the exact gap kaxil's and tatiana's review comments both raised. Writing is restructured to make that safe: manifest units still compute (hash + slim bytes) concurrently, but are now written in a second pass grouped by directory, so manifests sharing a directory build one complete map together and write their shared sidecar exactly once, from a single writer - not two goroutines racing the same file. Stress-tested at 300 iterations under real parallelism (GOMAXPROCS=8): zero corrupted or lost-update sidecars. Also along the way: - slimNameFor's suffix trim is now case-insensitive, matching isManifestCandidateName (a manifest named MANIFEST.JSON previously got a malformed MANIFEST.JSON.slim.json). - A manifest candidate in a project root that can't even be read now surfaces a warning instead of vanishing silently (was conflated with "not a dbt manifest"). - Trimmed redundant tests/comments across the branch. Co-Authored-By: Claude <noreply@anthropic.com>
processProject aborted the whole unit (discarding the project's tree hash and any sibling manifest's already-written slim file) if any one candidate's slim write failed. Isolate the failure to that candidate, matching the existing read-error handling in the same loop. Co-Authored-By: Claude <noreply@anthropic.com>
…idate hashProject's tree walk aborted the entire project (CI's real permission enforcement, unlike this sandbox's root user) when a root-level manifest-candidate file couldn't be read. processProject already re-reads and warns on that same file - skip it here instead of failing the walk. Co-Authored-By: Claude <noreply@anthropic.com>
Thanks for the review. Addressed your feedback and created a follow-up issue for plugin work |
|
Overall this rework is a good improvement. I left two inline comments (the schema bump, and a minor stale comment). One more thing that is not in this diff so I could not inline it: the scaffold gitignore at |
…nifests - precompute.go: fix comment claiming doc is never mutated after the slim marshal - hashDocument mutates it right below; the marshal just has to happen first. - scaffold gitignore matched only the default manifest.slim.json, so a custom-named slim file (e.g. manifest_full.slim.json) showed up untracked. Same issue as #2242, broadened the same way. Co-Authored-By: Claude <noreply@anthropic.com>
|
Fixed the scaffold gitignore too - broadened to |
tatiana
left a comment
There was a problem hiding this comment.
Thanks for addressing the feedback, @pankajastro !
Summary
Approach changed since earlier commits/review — replaced the customer-configured env var with automatic discovery. No configuration needed.
Discovery previously matched only files literally named
manifest.json. Now a file is treated as a manifest when its name contains "manifest" (case-insensitive) and its content validates as one - coveringmanifest.json,manifest_full.json,manifest_by_schedule.json, etc. automatically.Every manifest found gets its own slim companion, named after itself:
So a directory with multiple manifests (e.g. per-schedule manifests, each read by a different DAG) gets one slim file per manifest instead of one contested file - no picking a winner, no collision. Three dbt projects with two manifests each produce six slim files, one per manifest.
Sidecar schema bumped to v2 in response to review (a directory can describe more than one manifest now, so a single top-level hash/slim pointer isn't enough).
.astro/dbt_metadata.jsonkeeps its fixed name, butfiltered_manifestis replaced bymanifests, a map keyed by source filename, each with its own hash and slim pointer:{ "schema": 2, "version": {"algo": "sha256-tree-v2", "hash": "…"}, "manifests": { "manifest_full.json": { "version": {"algo": "sha256-manifest-v2", "hash": "…"}, "slim_manifest": {"schema": 1, "path": "manifest_full.slim.json", "version": {...}} }, "manifest_by_schedule.json": { "...": "..." } } }A consumer looks up its own manifest's entry instead of trusting the top-level
version/pointer when a directory has several. The top-levelversionis kept for backward compat and is deterministic (alphabetically-first manifest in the directory).This also closed a real concurrency hazard flagged in review: multiple manifests sharing a directory are hashed/slimmed in parallel, then written in one grouped pass per directory (one writer, no race) - verified with a 300-iteration stress test at real parallelism.
Cleanupmatches a slim file by its.slim.jsonsuffix instead of one fixed name, so it can find and remove any of them; ownership is still checked per file via its own_generated_bymarker before deleting.Also fixed in review: a failure slimming one manifest in a project root no longer aborts the whole unit - the project's tree-hash sidecar and any already-slimmed sibling manifest are no longer discarded.
Follow-up tracked separately: BOSS-738 for the Cosmos Boost plugin repo to consume the new
manifestsmap instead of a single pointer.Testing
go build/go vet/gofmtclean; fullpkg/cosmosboost/...suite passing (golang:1.26 container - no local Go toolchain).TestRunSharedDirectorySidecarDescribesBothManifests(schema v2 map + top-level version determinism across repeated runs),TestRunDoesNotFlagOtherDbtArtifactsAsManifests,TestRunSlimsEveryManifestInProjectRoot,TestRunProjectRootSlimFailureIsolatedToOneManifest,TestRunHandlesMultipleProjectsWithDifferentManifestNames,TestIsManifestCandidateName,TestSlimNameFor,TestCleanupRemovesCustomNamedSlimManifest,TestCleanupKeepsForeignSlimJSONSuffixedFile.GOMAXPROCS=8across 300 iterations: zero corruption or lost updates in the sidecar.EnsureCleanremoves a stale slim file after a manifest is renamed between deploys (no orphaned files left behind).🤖 Generated with Claude Code