Skip to content
Closed
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
82 changes: 82 additions & 0 deletions scripts/migrate-gcs-arch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/bin/bash
# Copies flat-path firecracker binaries into amd64/ subdirectories in GCS.
#
# Before ARM64 support was added, builds were uploaded as:
# {version_name}/firecracker
#
# The arch-aware layout expects:
# {version_name}/amd64/firecracker
#
# This script finds flat-path binaries and copies them into the amd64/ subdir
# so the orchestrator's arch-based path resolution can find them.
#
# Usage:
# ./scripts/migrate-gcs-arch.sh <bucket-or-path> # dry-run: show what would be copied
# ./scripts/migrate-gcs-arch.sh <bucket-or-path> --apply # copy flat -> amd64/
#
# Examples:
# ./scripts/migrate-gcs-arch.sh gs://e2b-staging-fc-versions
# ./scripts/migrate-gcs-arch.sh gs://e2b-prod-public-builds/firecrackers --apply

set -euo pipefail

GCS_PATH="${1:?Usage: $0 <bucket-or-path> [--apply]}"
shift

APPLY=false
for arg in "$@"; do
case "$arg" in
--apply) APPLY=true ;;
*) echo "Unknown flag: $arg"; exit 1 ;;
esac
done

# Normalize: strip trailing slash
GCS_PATH="${GCS_PATH%/}"

echo "Scanning ${GCS_PATH} for flat-path firecracker binaries..."
echo ""

# Match only flat-path binaries: {version}/firecracker
# The single * does NOT match path separators, so this excludes
# {version}/{arch}/firecracker.
objects=$(gsutil ls "${GCS_PATH}/*/firecracker" 2>/dev/null || true)

if [[ -z "$objects" ]]; then
echo "No flat-path firecracker binaries found in ${GCS_PATH}"
exit 0
fi

copied=0
skipped=0
while IFS= read -r src; do
[[ -z "$src" ]] && continue

# Insert /amd64 before the filename:
# .../v1.10.1/firecracker -> .../v1.10.1/amd64/firecracker
dst="${src%/firecracker}/amd64/firecracker"

# Skip if destination already exists
if gsutil -q stat "$dst" 2>/dev/null; then
echo " SKIP $dst (already exists)"
((skipped++)) || true
continue
fi

if [[ "$APPLY" == true ]]; then
echo " COPY $src"
echo " -> $dst"
gsutil cp "$src" "$dst"
else
echo " [dry-run] $src"
echo " -> $dst"
fi
((copied++)) || true
done <<< "$objects"

echo ""
echo "Total: $copied to copy, $skipped skipped (already exist)"
if [[ "$APPLY" != true && "$copied" -gt 0 ]]; then
echo ""
echo "This was a dry run. Add --apply to actually copy."
fi
112 changes: 112 additions & 0 deletions scripts/upload-release-to-gcs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# Uploads firecracker-{amd64,arm64} assets from a fc-versions GitHub release
# to GCS at:
# <bucket>/<version_name>/<arch>/firecracker
# <bucket>/<version_name>/firecracker (legacy amd64 copy)
#
# Existing objects are never overwritten.
#
# Usage:
# ./scripts/upload-release-to-gcs.sh --tag <tag> --bucket <bucket> [--dry-run] [--repo <repo>]
#
# Options:
# --tag <tag> Release tag / version name (e.g. v1.14.1_af9c995).
# --bucket <bucket> Target bucket (with optional path prefix), e.g.
# my-bucket
# my-bucket/firecrackers
# gs://my-bucket/firecrackers
# --repo <repo> GitHub repo (default: e2b-dev/fc-versions).
# --dry-run Print what would be uploaded without writing.
# -h, --help Show this help.

set -euo pipefail

REPO="e2b-dev/fc-versions"
TAG=""
BUCKET=""
DRY_RUN=false

usage() { sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'; exit "${1:-0}"; }

while [[ $# -gt 0 ]]; do
case "$1" in
--tag) TAG="${2:?--tag needs a value}"; shift 2 ;;
--bucket) BUCKET="${2:?--bucket needs a value}"; shift 2 ;;
--repo) REPO="${2:?--repo needs a value}"; shift 2 ;;
--dry-run) DRY_RUN=true; shift ;;
-h|--help) usage 0 ;;
*) echo "Unknown argument: $1" >&2; usage 1 ;;
esac
done

[[ -n "$TAG" ]] || { echo "ERROR: --tag is required" >&2; usage 1; }
[[ -n "$BUCKET" ]] || { echo "ERROR: --bucket is required" >&2; usage 1; }

command -v gh >/dev/null || { echo "ERROR: gh CLI not found" >&2; exit 1; }
command -v gcloud >/dev/null || { echo "ERROR: gcloud CLI not found" >&2; exit 1; }

BUCKET="${BUCKET#gs://}"
BUCKET="${BUCKET%/}"
BUCKET_URI="gs://${BUCKET}"

if ! gh release view "$TAG" --repo "$REPO" >/dev/null 2>&1; then
echo "ERROR: release '$TAG' not found in $REPO" >&2
exit 1
fi

echo "Release: $TAG"
echo "Target: ${BUCKET_URI}"
$DRY_RUN && echo "Mode: dry-run"

ASSETS=()
while IFS= read -r line || [[ -n "$line" ]]; do
[[ -n "$line" ]] && ASSETS+=("$line")
done < <(gh release view "$TAG" --repo "$REPO" --json assets \
--jq '.assets[].name')

if [[ "${#ASSETS[@]}" -eq 0 ]]; then
echo "ERROR: release $TAG has no assets" >&2
exit 1
fi

TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT

uploaded=0
skipped=0
for asset in "${ASSETS[@]}"; do
if [[ "$asset" =~ ^firecracker-(amd64|arm64)$ ]]; then
arch="${BASH_REMATCH[1]}"
dst="${BUCKET_URI}/${TAG}/${arch}/firecracker"
elif [[ "$asset" == "firecracker" ]]; then
dst="${BUCKET_URI}/${TAG}/firecracker"
else
continue
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Upload script missing legacy flat-path copy for amd64

High Severity

The script's header comment documents that it uploads to both <bucket>/<version_name>/<arch>/firecracker and <bucket>/<version_name>/firecracker (legacy amd64 copy). However, when handling a firecracker-amd64 asset, the code only uploads to the arch-specific path and never creates the legacy flat-path copy. build.sh explicitly creates both paths for amd64 with a comment noting "so existing production nodes that expect the old layout keep working." New releases lacking a bare firecracker asset will have no legacy path, breaking backward compatibility.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0cd7a29. Configure here.


if gcloud storage ls "$dst" >/dev/null 2>&1; then
echo " EXISTS $dst"
skipped=$((skipped + 1))
continue
fi

if $DRY_RUN; then
echo " WOULD $asset -> $dst"
uploaded=$((uploaded + 1))
continue
fi

echo " UPLOAD $asset -> $dst"
gh release download "$TAG" --repo "$REPO" \
--pattern "$asset" --dir "$TMP_DIR" --clobber >/dev/null
gcloud storage cp "$TMP_DIR/$asset" "$dst"
rm -f "$TMP_DIR/$asset"
uploaded=$((uploaded + 1))
done

echo ""
if $DRY_RUN; then
echo "Dry run complete. Would upload: $uploaded, already in GCS: $skipped."
else
echo "Done. Uploaded: $uploaded, already in GCS: $skipped."
fi
Loading