diff --git a/setup/js/create_pull_request.cjs b/setup/js/create_pull_request.cjs index 313840c..6f41585 100644 --- a/setup/js/create_pull_request.cjs +++ b/setup/js/create_pull_request.cjs @@ -33,7 +33,7 @@ const { isStagedMode } = require("./safe_output_helpers.cjs"); const { normalizeCommitSHA } = require("./commit_sha_helpers.cjs"); const { withRetry, RATE_LIMIT_RETRY_CONFIG } = require("./error_recovery.cjs"); const { findAgent, getIssueDetails, assignAgentToIssue } = require("./assign_agent_helpers.cjs"); -const { ensureFullHistoryForBundle, extractBundlePrerequisiteCommits, linearizeRangeAsCommit } = require("./git_helpers.cjs"); +const { ensureFullHistoryForBundle, extractBundlePrerequisiteCommits, isShallowOrSparseCheckout, linearizeRangeAsCommit } = require("./git_helpers.cjs"); const { parseDiffGitHeader: parseDiffGitHeaderPaths, extractDiffGitHeaderEntries } = require("./patch_path_helpers.cjs"); const { resolveAllowedMentionsFromPayload } = require("./resolve_mentions_from_payload.cjs"); const { @@ -211,7 +211,15 @@ async function applyBundleToBranch(bundleFilePath, branchName, originalAgentBran core.warning(`Bundle fetch with ${bundleBranchRef} failed due to ${prerequisiteCommits.length} missing prerequisite commit(s); fetching prerequisites from origin and retrying`); core.info(`Prerequisite commits: ${summarizeListForLog(prerequisiteCommits)}`); core.info(`Fetching ${prerequisiteCommits.length} prerequisite commit(s) from origin`); - await execApi.exec("git", ["fetch", "origin", ...prerequisiteCommits]); + // Use --filter=blob:none only when the local repo is already shallow or sparse — + // in a full clone we already have all blobs and must not convert the repo to a + // partial clone (which would trigger lazy blob fetches on later operations). + const useBlobFilter = await isShallowOrSparseCheckout(execApi); + const prerequisiteFetchArgs = useBlobFilter ? ["fetch", "--filter=blob:none", "origin", ...prerequisiteCommits] : ["fetch", "origin", ...prerequisiteCommits]; + if (useBlobFilter) { + core.info("Using --filter=blob:none for prerequisite fetch (shallow or sparse checkout detected)"); + } + await execApi.exec("git", prerequisiteFetchArgs); core.info("Fetched prerequisite commits from origin successfully"); try { core.info(`Retrying bundle fetch from ${bundleBranchRef} into ${bundleTempRef} after prerequisite recovery`); diff --git a/setup/js/git_helpers.cjs b/setup/js/git_helpers.cjs index b6a6c33..d803db2 100644 --- a/setup/js/git_helpers.cjs +++ b/setup/js/git_helpers.cjs @@ -180,6 +180,44 @@ async function ensureFullHistoryForBundle(execApi, options = {}) { } } +/** + * Return true when the local repository is shallow OR has sparse-checkout enabled. + * + * This is the gate for using `--filter=blob:none` on follow-up fetches (e.g. bundle + * prerequisite recovery). In a full, non-sparse clone the repo already contains all + * blobs for committed history; adding `--filter=blob:none` to a fetch would convert + * it to a partial clone and cause subsequent operations to lazily re-fetch blobs. + * In shallow or sparse checkouts we already accept partial object availability, so + * filtering blobs is consistent and saves bandwidth. + * + * Both probes are best-effort — on any error we return `false` (do not filter), + * which is the safe default that preserves the legacy unfiltered fetch behavior. + * + * @param {{ getExecOutput: Function }} execApi - Exec API to run git commands. + * @param {Object} [options] - Options passed through to exec calls. + * @returns {Promise} + */ +async function isShallowOrSparseCheckout(execApi, options = {}) { + const probeOptions = { ...options, ignoreReturnCode: true }; + try { + const { stdout, exitCode } = await execApi.getExecOutput("git", ["rev-parse", "--is-shallow-repository"], probeOptions); + if (exitCode === 0 && stdout.trim() === "true") { + return true; + } + } catch { + // Fall through to sparse check; if both probes fail, return false (no filter). + } + try { + const { stdout, exitCode } = await execApi.getExecOutput("git", ["config", "--get", "core.sparseCheckout"], probeOptions); + if (exitCode === 0 && stdout.trim().toLowerCase() === "true") { + return true; + } + } catch { + // Fall through. + } + return false; +} + /** * Extract prerequisite commit SHAs from git bundle fetch error output. * @@ -265,5 +303,6 @@ module.exports = { extractBundlePrerequisiteCommits, getGitAuthEnv, hasMergeCommitsInRange, + isShallowOrSparseCheckout, linearizeRangeAsCommit, }; diff --git a/setup/js/model_multipliers.json b/setup/js/model_multipliers.json index fbb31eb..7af7e11 100644 --- a/setup/js/model_multipliers.json +++ b/setup/js/model_multipliers.json @@ -37,13 +37,10 @@ "claude-opus-4.6": 27.0, "claude-3-5-opus": 5.0, "claude-3-opus": 5.0, - "gpt-4o": 0.33, "gpt-4o-2024-05-13": 0.33, "gpt-4o-2024-08-06": 0.33, "gpt-4o-2024-11-20": 0.33, - "gpt-4o-mini": 0.33, "gpt-4o-mini-2024-07-18": 0.33, - "gpt-4.1": 1.0, "gpt-4.1-2025-04-14": 1.0, "gpt-41-copilot": 1.0, "gpt-4.1-mini": 1.0, @@ -86,7 +83,6 @@ "gpt-5.4-2026-03-05": 6.0, "gpt-5.4-mini": 6.0, "gpt-5.4-mini-2026-03-17": 6.0, - "gpt-5.4-nano": 6.0, "gpt-5.4-nano-2026-03-17": 6.0, "gpt-5.4-pro": 6.0, "gpt-5.4-pro-2026-03-05": 6.0, diff --git a/setup/js/push_to_pull_request_branch.cjs b/setup/js/push_to_pull_request_branch.cjs index dbe627a..20d6fb2 100644 --- a/setup/js/push_to_pull_request_branch.cjs +++ b/setup/js/push_to_pull_request_branch.cjs @@ -17,7 +17,7 @@ const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs"); const { checkFileProtection } = require("./manifest_file_helpers.cjs"); const { buildWorkflowRunUrl } = require("./workflow_metadata_helpers.cjs"); const { renderTemplateFromFile, buildProtectedFileList, getPromptPath } = require("./messages_core.cjs"); -const { ensureFullHistoryForBundle, getGitAuthEnv, extractBundlePrerequisiteCommits, linearizeRangeAsCommit } = require("./git_helpers.cjs"); +const { ensureFullHistoryForBundle, getGitAuthEnv, extractBundlePrerequisiteCommits, isShallowOrSparseCheckout, linearizeRangeAsCommit } = require("./git_helpers.cjs"); const { normalizeCommitSHA } = require("./commit_sha_helpers.cjs"); const { findRepoCheckout } = require("./find_repo_checkout.cjs"); const { getThreatDetectedMarker } = require("./threat_detection_warning.cjs"); @@ -742,7 +742,16 @@ async function main(config = {}) { if (prerequisiteCommits.length > 0) { core.warning(`Bundle fetch failed due to ${prerequisiteCommits.length} missing prerequisite commit(s); fetching prerequisites from origin and retrying`); core.info(`Fetching ${prerequisiteCommits.length} prerequisite commit(s) from origin`); - await exec.exec("git", ["fetch", "origin", ...prerequisiteCommits], { env: { ...process.env, ...gitAuthEnv }, ...baseGitOpts }); + // Use --filter=blob:none only when the local repo is already shallow or sparse — + // in a full clone we already have all blobs and must not convert the repo to a + // partial clone (which would trigger lazy blob fetches on later operations). + const prereqGitOpts = { env: { ...process.env, ...gitAuthEnv }, ...baseGitOpts }; + const useBlobFilter = await isShallowOrSparseCheckout(exec, prereqGitOpts); + const prerequisiteFetchArgs = useBlobFilter ? ["fetch", "--filter=blob:none", "origin", ...prerequisiteCommits] : ["fetch", "origin", ...prerequisiteCommits]; + if (useBlobFilter) { + core.info("Using --filter=blob:none for prerequisite fetch (shallow or sparse checkout detected)"); + } + await exec.exec("git", prerequisiteFetchArgs, prereqGitOpts); core.info("Fetched prerequisite commits from origin successfully"); await exec.exec("git", ["fetch", bundleFilePath, bundleFetchRef], baseGitOpts); core.info("Bundle fetch retry succeeded after prerequisite recovery"); diff --git a/setup/sh/install_antigravity_cli.sh b/setup/sh/install_antigravity_cli.sh index 3d5769a..858ffbb 100644 --- a/setup/sh/install_antigravity_cli.sh +++ b/setup/sh/install_antigravity_cli.sh @@ -13,6 +13,7 @@ set +o histexpand # Security features: # - Downloads binary directly from Google Cloud Storage over HTTPS # - Verifies SHA256 checksum against official checksums.txt before installation +# - Warns and skips checksum verification if checksums.txt is unavailable (HTTP 404) # - Fails fast if checksum verification fails # - Fails fast on any curl errors @@ -74,34 +75,51 @@ sha256_hash() { TEMP_DIR=$(mktemp -d) trap 'rm -rf "$TEMP_DIR"' EXIT -# Download checksums file from GCS +# Download checksums file from GCS (if available for this version) echo "Downloading checksums from ${CHECKSUMS_URL}..." -curl -fsSL --retry 3 --retry-delay 5 -o "${TEMP_DIR}/checksums.txt" "${CHECKSUMS_URL}" +if ! CHECKSUMS_DOWNLOAD_STATUS=$(curl -sSL --retry 3 --retry-delay 5 -w "%{http_code}" -o "${TEMP_DIR}/checksums.txt" "${CHECKSUMS_URL}"); then + echo "ERROR: Failed to download checksums.txt due to a network or TLS error" + exit 1 +fi + +VERIFY_CHECKSUM=true +if [ "${CHECKSUMS_DOWNLOAD_STATUS}" = "404" ]; then + echo "WARNING: checksums.txt not found for version ${VERSION}; skipping checksum verification." + rm -f "${TEMP_DIR}/checksums.txt" + VERIFY_CHECKSUM=false +elif [ "${CHECKSUMS_DOWNLOAD_STATUS}" != "200" ]; then + echo "ERROR: Failed to download checksums.txt (HTTP ${CHECKSUMS_DOWNLOAD_STATUS})" + exit 1 +fi # Download binary tarball from GCS over HTTPS echo "Downloading from ${TARBALL_URL}..." curl -fsSL --retry 3 --retry-delay 5 -o "${TEMP_DIR}/${TARBALL_NAME}" "${TARBALL_URL}" -# Verify SHA256 checksum before extracting -echo "Verifying SHA256 checksum for ${TARBALL_NAME}..." -EXPECTED_CHECKSUM=$(awk -v fname="${TARBALL_NAME}" '$2 == fname {print $1; exit}' "${TEMP_DIR}/checksums.txt" | tr 'A-F' 'a-f') +# Verify SHA256 checksum before extracting (when checksums.txt is available) +if [ "${VERIFY_CHECKSUM}" = "true" ]; then + echo "Verifying SHA256 checksum for ${TARBALL_NAME}..." + EXPECTED_CHECKSUM=$(awk -v fname="${TARBALL_NAME}" '$2 == fname {print $1; exit}' "${TEMP_DIR}/checksums.txt" | tr 'A-F' 'a-f') -if [ -z "$EXPECTED_CHECKSUM" ]; then - echo "ERROR: Could not find checksum for ${TARBALL_NAME} in checksums.txt" - exit 1 -fi + if [ -z "$EXPECTED_CHECKSUM" ]; then + echo "ERROR: Could not find checksum for ${TARBALL_NAME} in checksums.txt" + exit 1 + fi -ACTUAL_CHECKSUM=$(sha256_hash "${TEMP_DIR}/${TARBALL_NAME}" | tr 'A-F' 'a-f') + ACTUAL_CHECKSUM=$(sha256_hash "${TEMP_DIR}/${TARBALL_NAME}" | tr 'A-F' 'a-f') -if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then - echo "ERROR: Checksum verification failed!" - echo " Expected: $EXPECTED_CHECKSUM" - echo " Got: $ACTUAL_CHECKSUM" - echo " The downloaded file may be corrupted or tampered with" - exit 1 -fi + if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then + echo "ERROR: Checksum verification failed!" + echo " Expected: $EXPECTED_CHECKSUM" + echo " Got: $ACTUAL_CHECKSUM" + echo " The downloaded file may be corrupted or tampered with" + exit 1 + fi -echo "✓ Checksum verification passed for ${TARBALL_NAME}" + echo "✓ Checksum verification passed for ${TARBALL_NAME}" +else + echo "WARNING: Proceeding without checksum verification for ${TARBALL_NAME}" +fi # Extract and install binary echo "Installing binary to ${INSTALL_DIR}/${BINARY_NAME}..."