Skip to content

fix: provision bundled claude runtime in release builds#386

Merged
Xoshbin merged 1 commit into
mainfrom
fix/ci-provision-claude-sidecar
Jun 2, 2026
Merged

fix: provision bundled claude runtime in release builds#386
Xoshbin merged 1 commit into
mainfrom
fix/ci-provision-claude-sidecar

Conversation

@Xoshbin
Copy link
Copy Markdown
Owner

@Xoshbin Xoshbin commented Jun 2, 2026

No description provided.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for bundling the claude binary as a sidecar alongside bun and uv, updating the download script, .gitignore, and documentation, while introducing provisioning and live manifest tests. Feedback from the review focuses on preserving the script's offline-friendly and idempotent nature by lazily resolving the Claude version and manifest only when a download is actually required. Additionally, suggestions were made to clean up temporary files created during the download process using unlinkSync.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +179 to +210
async function ensureClaude(platform, version, manifest) {
const isWindowsTarget = platform.platformKey.startsWith('win32-')
const exeExt = isWindowsTarget ? '.exe' : ''
const destName = `claude-${platform.rustTriple}${exeExt}`
const destPath = join(BINARIES_DIR, destName)

if (existsSync(destPath)) {
console.log(` claude (${platform.platformKey}): already exists at binaries/${destName}, skipping`)
return
}

const claudeKey = platform.claudePlatform
const expected = manifest.platforms?.[claudeKey]?.checksum
if (!expected) {
throw new Error(`claude manifest ${version} has no checksum for platform "${claudeKey}"`)
}

const binFile = `claude${exeExt}`
const url = `${CLAUDE_BASE_URL}/${version}/${claudeKey}/${binFile}`
const tmpBin = join(tmpdir(), `${destName}-dl-${Date.now()}`)
console.log(` claude (${platform.platformKey}): downloading ${version}/${claudeKey}...`)
await download(url, tmpBin)

const actual = sha256(tmpBin)
if (actual !== expected) {
throw new Error(`claude (${claudeKey}) checksum mismatch: expected ${expected}, got ${actual}`)
}

copyFileSync(tmpBin, destPath)
if (!isWindowsTarget) chmodSync(destPath, 0o755)
console.log(` claude (${platform.platformKey}): installed to binaries/${destName} (verified)`)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Currently, the Claude version and manifest are resolved eagerly at the top level. This causes the script to make network requests to downloads.claude.ai even if all sidecar binaries already exist locally, breaking the offline-friendly and idempotent nature of the script.

By lazily resolving the Claude version and manifest inside ensureClaude only when a download is actually required, we can avoid unnecessary network requests and allow offline builds when the binaries are already present. Additionally, we can wrap the download in a try...finally block to ensure the temporary binary file is cleaned up.

let claudeVersion = null
let claudeManifest = null

async function ensureClaude(platform) {
  const isWindowsTarget = platform.platformKey.startsWith('win32-')
  const exeExt = isWindowsTarget ? '.exe' : ''
  const destName = `claude-${platform.rustTriple}${exeExt}`
  const destPath = join(BINARIES_DIR, destName)

  if (existsSync(destPath)) {
    console.log(`  claude (${platform.platformKey}): already exists at binaries/${destName}, skipping`)
    return
  }

  if (!claudeVersion) {
    step('Resolving claude runtime version')
    claudeVersion = await resolveClaudeVersion()
    claudeManifest = await fetchClaudeManifest(claudeVersion)
    console.log(`  claude runtime: ${claudeVersion}`)
  }

  const claudeKey = platform.claudePlatform
  const expected = claudeManifest.platforms?.[claudeKey]?.checksum
  if (!expected) {
    throw new Error(`claude manifest ${claudeVersion} has no checksum for platform "${claudeKey}"`)
  }

  const binFile = `claude${exeExt}`
  const url = `${CLAUDE_BASE_URL}/${claudeVersion}/${claudeKey}/${binFile}`
  const tmpBin = join(tmpdir(), `${destName}-dl-${Date.now()}`)
  try {
    console.log(`  claude (${platform.platformKey}): downloading ${claudeVersion}/${claudeKey}...`)
    await download(url, tmpBin)

    const actual = sha256(tmpBin)
    if (actual !== expected) {
      throw new Error(`claude (${claudeKey}) checksum mismatch: expected ${expected}, got ${actual}`)
    }

    copyFileSync(tmpBin, destPath)
    if (!isWindowsTarget) chmodSync(destPath, 0o755)
    console.log(`  claude (${platform.platformKey}): installed to binaries/${destName} (verified)`)
  } finally {
    try {
      if (existsSync(tmpBin)) unlinkSync(tmpBin)
    } catch {}
  }
}

Comment on lines +228 to +231
step('Resolving claude runtime version')
const claudeVersion = await resolveClaudeVersion()
const claudeManifest = await fetchClaudeManifest(claudeVersion)
console.log(` claude runtime: ${claudeVersion}`)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Remove the eager top-level resolution of the Claude version and manifest, as it is now handled lazily inside ensureClaude.

archive: platform.uvArchive,
binaryName: 'uv',
})
await ensureClaude(platform, claudeVersion, claudeManifest)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Update the call to ensureClaude to omit the version and manifest arguments, as they are now resolved lazily.

  await ensureClaude(platform)

*/

import { existsSync, chmodSync, mkdirSync, copyFileSync } from 'node:fs'
import { existsSync, chmodSync, mkdirSync, copyFileSync, readFileSync } from 'node:fs'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Import unlinkSync from node:fs to enable cleanup of temporary files created during the download process.

import { existsSync, chmodSync, mkdirSync, copyFileSync, readFileSync, unlinkSync } from 'node:fs'

Comment on lines +156 to +160
async function downloadText(url, label) {
const tmp = join(tmpdir(), `claude-${label}-${Date.now()}`)
await download(url, tmp)
return readFileSync(tmp, 'utf8')
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Clean up the temporary file created during the download of text files (like the version pointer or manifest) to avoid cluttering the system's temp directory.

async function downloadText(url, label) {
  const tmp = join(tmpdir(), `claude-${label}-${Date.now()}`)
  try {
    await download(url, tmp)
    return readFileSync(tmp, 'utf8')
  } finally {
    try {
      if (existsSync(tmp)) unlinkSync(tmp)
    } catch {}
  }
}

@Xoshbin Xoshbin merged commit c3b8302 into main Jun 2, 2026
1 check passed
@Xoshbin Xoshbin deleted the fix/ci-provision-claude-sidecar branch June 2, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant