22
33const { spawn } = require ( 'child_process' )
44const fs = require ( 'fs' )
5+ const http = require ( 'http' )
56const https = require ( 'https' )
67const os = require ( 'os' )
78const path = require ( 'path' )
@@ -31,6 +32,70 @@ function createConfig(packageName) {
3132
3233const CONFIG = createConfig ( packageName )
3334
35+ function getPostHogConfig ( ) {
36+ const apiKey =
37+ process . env . CODEBUFF_POSTHOG_API_KEY ||
38+ process . env . NEXT_PUBLIC_POSTHOG_API_KEY
39+ const host =
40+ process . env . CODEBUFF_POSTHOG_HOST ||
41+ process . env . NEXT_PUBLIC_POSTHOG_HOST_URL
42+
43+ if ( ! apiKey || ! host ) {
44+ return null
45+ }
46+
47+ return { apiKey, host }
48+ }
49+
50+ /**
51+ * Track update failure event to PostHog.
52+ * Fire-and-forget - errors are silently ignored.
53+ */
54+ function trackUpdateFailed ( errorMessage , version , context = { } ) {
55+ try {
56+ const posthogConfig = getPostHogConfig ( )
57+ if ( ! posthogConfig ) {
58+ return
59+ }
60+
61+ const payload = JSON . stringify ( {
62+ api_key : posthogConfig . apiKey ,
63+ event : 'cli.update_codebuff_failed' ,
64+ properties : {
65+ distinct_id : `anonymous-${ CONFIG . homeDir } ` ,
66+ error : errorMessage ,
67+ version : version || 'unknown' ,
68+ platform : process . platform ,
69+ arch : process . arch ,
70+ isStaging : true ,
71+ ...context ,
72+ } ,
73+ timestamp : new Date ( ) . toISOString ( ) ,
74+ } )
75+
76+ const parsedUrl = new URL ( `${ posthogConfig . host } /capture/` )
77+ const isHttps = parsedUrl . protocol === 'https:'
78+ const options = {
79+ hostname : parsedUrl . hostname ,
80+ port : parsedUrl . port || ( isHttps ? 443 : 80 ) ,
81+ path : parsedUrl . pathname + parsedUrl . search ,
82+ method : 'POST' ,
83+ headers : {
84+ 'Content-Type' : 'application/json' ,
85+ 'Content-Length' : Buffer . byteLength ( payload ) ,
86+ } ,
87+ }
88+
89+ const transport = isHttps ? https : http
90+ const req = transport . request ( options )
91+ req . on ( 'error' , ( ) => { } ) // Silently ignore errors
92+ req . write ( payload )
93+ req . end ( )
94+ } catch ( e ) {
95+ // Silently ignore any tracking errors
96+ }
97+ }
98+
3499const PLATFORM_TARGETS = {
35100 'linux-x64' : `${ packageName } -linux-x64.tar.gz` ,
36101 'linux-arm64' : `${ packageName } -linux-arm64.tar.gz` ,
@@ -256,7 +321,9 @@ async function downloadBinary(version) {
256321 const fileName = PLATFORM_TARGETS [ platformKey ]
257322
258323 if ( ! fileName ) {
259- throw new Error ( `Unsupported platform: ${ process . platform } ${ process . arch } ` )
324+ const error = new Error ( `Unsupported platform: ${ process . platform } ${ process . arch } ` )
325+ trackUpdateFailed ( error . message , version , { stage : 'platform_check' } )
326+ throw error
260327 }
261328
262329 const downloadUrl = `${
@@ -278,7 +345,9 @@ async function downloadBinary(version) {
278345
279346 if ( res . statusCode !== 200 ) {
280347 fs . rmSync ( CONFIG . tempDownloadDir , { recursive : true } )
281- throw new Error ( `Download failed: HTTP ${ res . statusCode } ` )
348+ const error = new Error ( `Download failed: HTTP ${ res . statusCode } ` )
349+ trackUpdateFailed ( error . message , version , { stage : 'http_download' , statusCode : res . statusCode } )
350+ throw error
282351 }
283352
284353 const totalSize = parseInt ( res . headers [ 'content-length' ] || '0' , 10 )
@@ -318,9 +387,11 @@ async function downloadBinary(version) {
318387 if ( ! fs . existsSync ( tempBinaryPath ) ) {
319388 const files = fs . readdirSync ( CONFIG . tempDownloadDir )
320389 fs . rmSync ( CONFIG . tempDownloadDir , { recursive : true } )
321- throw new Error (
390+ const error = new Error (
322391 `Binary not found after extraction. Expected: ${ CONFIG . binaryName } , Available files: ${ files . join ( ', ' ) } ` ,
323392 )
393+ trackUpdateFailed ( error . message , version , { stage : 'extraction' } )
394+ throw error
324395 }
325396
326397 // Set executable permissions
@@ -334,7 +405,9 @@ async function downloadBinary(version) {
334405
335406 if ( ! smokeTestPassed ) {
336407 fs . rmSync ( CONFIG . tempDownloadDir , { recursive : true } )
337- throw new Error ( 'Downloaded binary failed smoke test (--version check)' )
408+ const error = new Error ( 'Downloaded binary failed smoke test (--version check)' )
409+ trackUpdateFailed ( error . message , version , { stage : 'smoke_test' } )
410+ throw error
338411 }
339412
340413 // Smoke test passed - move binary to final location
0 commit comments