Skip to content

Commit 8f02180

Browse files
authored
fix: build to .js for Sentry sourcemap upload (#700)
The Sentry esbuild plugin requires files with `.js` extension to properly inject debug IDs and upload source maps. This changes the build process to: 1. Build to `dist/craft.js` first 2. Let the Sentry plugin inject debug IDs and upload source maps 3. Rename to `dist/craft` (the final executable) 4. Clean up any remaining source map files
1 parent 373929f commit 8f02180

File tree

1 file changed

+44
-9
lines changed

1 file changed

+44
-9
lines changed

build.mjs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { chmod, readFile, writeFile } from 'fs/promises';
1+
import { chmod, readFile, rename, stat, unlink, writeFile } from 'fs/promises';
22
import esbuild from 'esbuild';
33
import { sentryEsbuildPlugin } from '@sentry/esbuild-plugin';
44

@@ -12,7 +12,7 @@ if (process.env.SENTRY_AUTH_TOKEN) {
1212
project: 'craft',
1313
authToken: process.env.SENTRY_AUTH_TOKEN,
1414
sourcemaps: {
15-
assets: ['dist/craft', 'dist/craft.map'],
15+
assets: ['dist/craft.js', 'dist/craft.js.map'],
1616
filesToDeleteAfterUpload: ['dist/**/*.map'],
1717
},
1818
})
@@ -21,6 +21,7 @@ if (process.env.SENTRY_AUTH_TOKEN) {
2121
console.log('[build] SENTRY_AUTH_TOKEN not found, skipping source map upload');
2222
}
2323

24+
// Build to .js file first so Sentry plugin can properly handle source maps
2425
await esbuild.build({
2526
entryPoints: ['src/index.ts'],
2627
sourcemap: true,
@@ -35,14 +36,48 @@ await esbuild.build({
3536
'process.env.CRAFT_BUILD_SHA': JSON.stringify(process.env.CRAFT_BUILD_SHA),
3637
}),
3738
},
38-
outfile: 'dist/craft',
39+
outfile: 'dist/craft.js',
3940
plugins,
4041
});
4142

42-
// Add shebang if not present and make executable
43-
const content = await readFile('dist/craft', 'utf-8');
44-
const hasShebang = content.startsWith('#!');
45-
if (!hasShebang) {
46-
await writeFile('dist/craft', '#!/usr/bin/env node\n' + content);
43+
// Helper to check if file exists
44+
async function exists(path) {
45+
try {
46+
await stat(path);
47+
return true;
48+
} catch {
49+
return false;
50+
}
51+
}
52+
53+
// Post-build processing with race condition handling for parallel test builds
54+
// Another parallel build may have already processed the file, so all operations
55+
// must gracefully handle ENOENT errors
56+
try {
57+
// Add shebang if not present
58+
const content = await readFile('dist/craft.js', 'utf-8');
59+
const hasShebang = content.startsWith('#!');
60+
if (!hasShebang) {
61+
await writeFile('dist/craft.js', '#!/usr/bin/env node\n' + content);
62+
}
63+
64+
// Rename to final executable name
65+
await rename('dist/craft.js', 'dist/craft');
66+
} catch (err) {
67+
// ENOENT means another parallel build already processed the file
68+
if (err.code !== 'ENOENT') {
69+
throw err;
70+
}
71+
}
72+
73+
// Ensure permissions are set (idempotent)
74+
if (await exists('dist/craft')) {
75+
await chmod('dist/craft', 0o755);
76+
}
77+
78+
// Clean up source map if it wasn't deleted by Sentry plugin
79+
try {
80+
await unlink('dist/craft.js.map');
81+
} catch {
82+
// Source map already deleted by Sentry plugin or doesn't exist
4783
}
48-
await chmod('dist/craft', 0o755);

0 commit comments

Comments
 (0)