Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions setup/js/create_pull_request.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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`);
Expand Down
39 changes: 39 additions & 0 deletions setup/js/git_helpers.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean>}
*/
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;
}
Comment on lines +210 to +214
Comment on lines +210 to +214
} catch {
// Fall through.
}
return false;
}

/**
* Extract prerequisite commit SHAs from git bundle fetch error output.
*
Expand Down Expand Up @@ -265,5 +303,6 @@ module.exports = {
extractBundlePrerequisiteCommits,
getGitAuthEnv,
hasMergeCommitsInRange,
isShallowOrSparseCheckout,
linearizeRangeAsCommit,
};
4 changes: 0 additions & 4 deletions setup/js/model_multipliers.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Comment on lines 40 to 44
Comment on lines 40 to 44
"gpt-41-copilot": 1.0,
"gpt-4.1-mini": 1.0,
Expand Down Expand Up @@ -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,
Expand Down
13 changes: 11 additions & 2 deletions setup/js/push_to_pull_request_branch.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down
54 changes: 36 additions & 18 deletions setup/sh/install_antigravity_cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Comment on lines +85 to +93
Comment on lines +85 to +93

# 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}..."
Expand Down
Loading