Skip to content

Commit baa5dfd

Browse files
suryaiyer95claude
andcommitted
fix: patch hardcoded __dirname in dbt-tools bundle so altimate-dbt works in published releases
`bun build` replaces `__dirname` with a compile-time constant when bundling `python-bridge` (transitive dep of `@altimateai/dbt-integration`). In CI this bakes `/home/runner/work/...` into the bundle, causing `altimate-dbt build` and all Python-bridge commands to fail with ENOENT on every user's machine. Fix: - Copy `node_python_bridge.py` into `dist/` alongside `index.js` - Post-process the bundle to replace the frozen path with `import.meta.dirname` - Fail the build if the patch pattern isn't found (safety net) - Add CI smoke test to prevent regression Broken since PR #201. Closes #466. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 79c34c5 commit baa5dfd

4 files changed

Lines changed: 74 additions & 5 deletions

File tree

.github/workflows/release.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,25 @@ jobs:
154154
run: bun run build
155155
working-directory: packages/dbt-tools
156156

157+
- name: Smoke test dbt-tools bundle
158+
run: |
159+
# Verify node_python_bridge.py was copied into dist
160+
if [ ! -f packages/dbt-tools/dist/node_python_bridge.py ]; then
161+
echo "::error::node_python_bridge.py missing from dbt-tools dist"
162+
exit 1
163+
fi
164+
# Verify no hardcoded CI runner paths remain in the bundle
165+
if grep -q 'home/runner' packages/dbt-tools/dist/index.js; then
166+
echo "::error::dbt-tools bundle contains hardcoded CI runner path"
167+
exit 1
168+
fi
169+
# Verify __dirname was patched to runtime resolution
170+
if ! grep -q 'import.meta.dirname' packages/dbt-tools/dist/index.js; then
171+
echo "::error::dbt-tools bundle missing import.meta.dirname patch"
172+
exit 1
173+
fi
174+
echo "dbt-tools smoke test passed"
175+
157176
- name: Free disk space for artifact download + npm publish
158177
run: |
159178
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/share/boost

packages/dbt-tools/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"scripts": {
1010
"build": "bun build src/index.ts --outdir dist --target node --format esm && bun run script/copy-python.ts",
1111
"typecheck": "tsc --noEmit",
12-
"test": "bun test test/cli.test.ts test/config.test.ts test/dbt-cli.test.ts test/dbt-resolve.test.ts --timeout 30000",
12+
"test": "bun test test/cli.test.ts test/config.test.ts test/dbt-cli.test.ts test/dbt-resolve.test.ts test/build-integrity.test.ts --timeout 30000",
1313
"test:e2e": "bun test test/e2e/ --timeout 300000"
1414
},
1515
"dependencies": {
Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
1-
import { cpSync } from "fs"
1+
import { cpSync, readFileSync, writeFileSync } from "fs"
22
import { dirname, join } from "path"
33

4+
const dist = join(import.meta.dir, "..", "dist")
5+
6+
// 1. Copy altimate_python_packages
47
const resolved = require.resolve("@altimateai/dbt-integration")
58
const source = join(dirname(resolved), "altimate_python_packages")
6-
const target = join(import.meta.dir, "..", "dist", "altimate_python_packages")
7-
8-
cpSync(source, target, { recursive: true })
9+
cpSync(source, join(dist, "altimate_python_packages"), { recursive: true })
910
console.log(`Copied altimate_python_packages → dist/`)
11+
12+
// 2. Copy node_python_bridge.py into dist so it lives next to index.js
13+
// node_python_bridge.py is shipped in dbt-integration's dist
14+
const bridgePy = join(dirname(resolved), "node_python_bridge.py")
15+
cpSync(bridgePy, join(dist, "node_python_bridge.py"))
16+
console.log(`Copied node_python_bridge.py → dist/`)
17+
18+
// 3. Fix the hardcoded __dirname that bun bakes at compile time.
19+
// Replace it with a runtime resolution so the bridge script is found
20+
// relative to the built index.js, not the CI runner's node_modules.
21+
const indexPath = join(dist, "index.js")
22+
let code = readFileSync(indexPath, "utf8")
23+
const pattern = /var __dirname\s*=\s*"[^"]*python-bridge[^"]*"/
24+
if (pattern.test(code)) {
25+
code = code.replace(pattern, `var __dirname = import.meta.dirname`)
26+
writeFileSync(indexPath, code)
27+
console.log(`Patched __dirname in dist/index.js`)
28+
} else {
29+
console.error(`ERROR: could not find python-bridge __dirname to patch — the bundle format may have changed`)
30+
process.exit(1)
31+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, test, expect, beforeAll } from "bun:test"
2+
import { existsSync, readFileSync } from "fs"
3+
import { join } from "path"
4+
import { $ } from "bun"
5+
6+
const dist = join(import.meta.dir, "../dist")
7+
8+
describe("build integrity", () => {
9+
beforeAll(async () => {
10+
// Rebuild to test the actual build output
11+
await $`bun run build`.cwd(join(import.meta.dir, ".."))
12+
})
13+
14+
test("node_python_bridge.py exists in dist", () => {
15+
expect(existsSync(join(dist, "node_python_bridge.py"))).toBe(true)
16+
})
17+
18+
test("no hardcoded CI runner paths in bundle", () => {
19+
const code = readFileSync(join(dist, "index.js"), "utf8")
20+
expect(code).not.toContain("home/runner")
21+
})
22+
23+
test("__dirname is patched to runtime resolution", () => {
24+
const code = readFileSync(join(dist, "index.js"), "utf8")
25+
expect(code).toContain("import.meta.dirname")
26+
expect(code).not.toMatch(/var __dirname\s*=\s*"\//)
27+
})
28+
})

0 commit comments

Comments
 (0)