Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
118 changes: 0 additions & 118 deletions .github/workflows/fc-kernels.yml

This file was deleted.

191 changes: 191 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
name: Manual Build & Release

on:
workflow_dispatch:
Comment thread
ValentaTomas marked this conversation as resolved.
inputs:
kernel_versions:
description: 'Comma-separated kernel versions to build (default: kernel_versions.txt)'
required: false
type: string
default: ''
Comment thread
ValentaTomas marked this conversation as resolved.
Outdated
build_amd64:
description: 'Build for amd64 architecture'
required: false
type: boolean
default: true
build_arm64:
description: 'Build for arm64 architecture'
required: false
type: boolean
default: true

permissions:
Comment thread
ValentaTomas marked this conversation as resolved.
contents: write
id-token: write

jobs:
validate:
runs-on: ubuntu-24.04
outputs:
build_matrix: ${{ steps.validate.outputs.build_matrix }}
versions: ${{ steps.validate.outputs.versions }}
has_new_artifacts: ${{ steps.validate.outputs.has_new_artifacts }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.11'

- id: validate
env:
GH_TOKEN: ${{ github.token }}
run: |
python3 scripts/validate.py \
--kernel-versions "${{ inputs.kernel_versions }}" \
--build-amd64 "${{ inputs.build_amd64 }}" \
--build-arm64 "${{ inputs.build_arm64 }}"

build:
needs: validate
if: needs.validate.outputs.has_new_artifacts == 'true'
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.validate.outputs.build_matrix) }}
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4

- name: Build kernel ${{ matrix.version_name }} (${{ matrix.arch }})
env:
VERSION_NAME: ${{ matrix.version_name }}
run: |
target_arch="${{ matrix.arch }}"
[ "$target_arch" = "amd64" ] && target_arch="x86_64"
sudo VERSION_NAME="$VERSION_NAME" ./build.sh "${{ matrix.kernel_version }}" "$target_arch"

- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.version_name }}-${{ matrix.arch }}
path: ./builds
retention-days: 7

publish:
needs: [validate, build]
if: needs.validate.outputs.has_new_artifacts == 'true'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/download-artifact@v4
with:
path: ./builds
merge-multiple: true

- name: Show build artifacts
run: find ./builds -type f | head -50

- name: Create or update releases
env:
GH_TOKEN: ${{ github.token }}
VERSIONS: ${{ needs.validate.outputs.versions }}
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

echo "$VERSIONS" | jq -c '.[]' | while read -r entry; do
version_name=$(echo "$entry" | jq -r '.version_name')
kernel_version=$(echo "$entry" | jq -r '.kernel_version')

if ! gh release view "$version_name" >/dev/null 2>&1; then
if ! git rev-parse "refs/tags/$version_name" >/dev/null 2>&1; then
git tag "$version_name"
git push origin "$version_name"
fi
gh release create "$version_name" \
--title "Kernel $version_name" \
--notes "Linux kernel $kernel_version built from configs at ${{ github.sha }}"
fi

existing=$(gh release view "$version_name" --json assets -q '.assets[].name' || true)

for arch in amd64 arm64; do
local_path="./builds/$version_name/$arch/vmlinux.bin"
[ -f "$local_path" ] || continue

asset="vmlinux-${arch}.bin"
if echo "$existing" | grep -qx "$asset"; then
echo "Release $version_name: $asset exists, skipping"
else
tmp=$(mktemp -d)/$asset
cp "$local_path" "$tmp"
gh release upload "$version_name" "$tmp"
rm -f "$tmp"
fi

# Legacy non-arch-suffixed asset (amd64 only) for backwards compat.
if [ "$arch" = "amd64" ] && ! echo "$existing" | grep -qx "vmlinux.bin"; then
tmp=$(mktemp -d)/vmlinux.bin
cp "$local_path" "$tmp"
gh release upload "$version_name" "$tmp"
rm -f "$tmp"
fi
done
done

deploy:
needs: [validate, publish]
if: needs.validate.outputs.has_new_artifacts == 'true'
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }}

- uses: google-github-actions/setup-gcloud@v2

- name: Download release assets and upload to GCS
env:
GH_TOKEN: ${{ github.token }}
GCP_BUCKET_NAME: ${{ vars.GCP_BUCKET_NAME }}
VERSIONS: ${{ needs.validate.outputs.versions }}
run: |
set -euo pipefail
echo "$VERSIONS" | jq -c '.[]' | while read -r entry; do
version_name=$(echo "$entry" | jq -r '.version_name')

for arch in amd64 arm64; do
asset="vmlinux-${arch}.bin"
Comment thread
ValentaTomas marked this conversation as resolved.
Outdated
dl_dir="./dl/$version_name/$arch"
mkdir -p "$dl_dir"
if ! gh release download "$version_name" \
--repo "${{ github.repository }}" \
--pattern "$asset" \
--output "$dl_dir/vmlinux.bin" 2>/dev/null; then
continue
fi

gcs_path="gs://${GCP_BUCKET_NAME}/kernels/${version_name}/${arch}/vmlinux.bin"
if gcloud storage ls "$gcs_path" >/dev/null 2>&1; then
echo "GCS: $gcs_path exists, skipping"
else
gcloud storage cp "$dl_dir/vmlinux.bin" "$gcs_path"
fi

# Legacy non-arch path (amd64 only) for backwards compat.
if [ "$arch" = "amd64" ]; then
legacy_path="gs://${GCP_BUCKET_NAME}/kernels/${version_name}/vmlinux.bin"
if gcloud storage ls "$legacy_path" >/dev/null 2>&1; then
echo "GCS: $legacy_path exists, skipping"
else
gcloud storage cp "$dl_dir/vmlinux.bin" "$legacy_path"
fi
fi
done
done
55 changes: 37 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,59 @@

## Overview

This project automates the building of custom Linux kernels for Firecracker microVMs, using the same kernel sources as official Firecracker repo and custom configuration files. It supports building specific kernel versions and uploading the resulting binaries to a Google Cloud Storage (GCS) bucket.
This project builds custom Linux kernels for Firecracker microVMs from the same kernel sources as the official Firecracker repo, using the configuration files (and optional patches) that live in this repo.

Each kernel build is identified by a content hash of its inputs (configs + patches), so changing a flag or adding a patch produces a new, traceable artifact:

```
vmlinux-<kernel_version>_<sha256[:7]>
```

## Prerequisites

- Linux environment (for building kernels)

## Building Kernels
## Building locally

1. **Configure kernel versions:**
- Edit `kernel_versions.txt` to specify which kernel versions to build (one per line, e.g., `6.1.102`).
- Place the corresponding config file in `configs/` (e.g., `configs/6.1.102.config`).
- Edit `kernel_versions.txt` to specify which kernel versions to build (one per line, e.g. `6.1.158`).
- Place the corresponding config(s) in `configs/x86_64/<version>.config` and `configs/arm64/<version>.config`.
- (Optional) Drop `*.patch` files into `patches/<version>/` to apply on top of the upstream tree before build.

2. **Build:**
```sh
make build
# or directly
./build.sh
make build # builds all versions in kernel_versions.txt for x86_64
make build-arm64 # same, for arm64
./build.sh 6.1.158 # build a single version (x86_64)
./build.sh 6.1.158 arm64
```
The built kernels will be placed in `builds/vmlinux-<version>/<arch>/vmlinux.bin` where `<arch>` is `amd64` or `arm64` (Go/OCI convention). For x86_64 backward compatibility, a legacy copy is also placed at `builds/vmlinux-<version>/vmlinux.bin`.

## Development Workflow
- On every push, GitHub Actions will automatically build the kernels and save it as an artifact.
Output: `builds/vmlinux-<version>_<hash>/<arch>/vmlinux.bin` where `<arch>` is `amd64` or `arm64` (Go/OCI convention). For x86_64 a legacy copy is also placed at `builds/vmlinux-<version>_<hash>/vmlinux.bin`.

## Releasing

1. Run the **Manual Build & Release** workflow (Actions → Manual Build & Release → Run workflow).
2. The workflow:
- Computes a content hash for each kernel version from its configs and patches.
- Skips arches whose artifact is already present in the matching GitHub release.
- Builds the missing arches, creates/updates the `vmlinux-<version>_<hash>` release, uploads `vmlinux-amd64.bin` / `vmlinux-arm64.bin` (and a legacy `vmlinux.bin` for amd64), and pushes the same files to GCS under `gs://$GCP_BUCKET_NAME/kernels/<version_name>/`.

### Workflow inputs

- `kernel_versions` (optional): comma-separated kernel versions. Defaults to all versions in `kernel_versions.txt`.
- `build_amd64` / `build_arm64` (optional, default `true`): which architectures to build.

## New kernel in E2B's infra
_Note: these steps should give you a new kernel on your self-hosted E2B using https://github.com/e2b-dev/infra_

- Run the release workflow to publish the new kernel build.
- Update `DefaultKernelVersion` in [packages/api/internal/cfg/model.go](https://github.com/e2b-dev/infra/blob/main/packages/api/internal/cfg/model.go) to the new `vmlinux-<version>_<hash>` name.
- Build and deploy `api`.

## Architecture naming

Output directories use Go's `runtime.GOARCH` convention (`amd64`, `arm64`) so they match the infra orchestrator's `TargetArch()` path resolution. The build-time variable `TARGET_ARCH` (`x86_64`, `arm64`) is only used internally for config paths and cross-compilation flags.

## New Kernel in E2B's infra
_Note: these steps should give you new kernel on your self-hosted E2B using https://github.com/e2b-dev/infra_

- Copy the kernel build in your project's object storage under `e2b-*-fc-kernels`
- In [packages/api/internal/cfg/model.go](https://github.com/e2b-dev/infra/blob/main/packages/api/internal/cfg/model.go) update `DefaultKernelVersion`
- Build and deploy `api`

## License

This project is licensed under the Apache License 2.0. See [LICENSE](LICENSE) for details.
This project is licensed under the Apache License 2.0. See [LICENSE](LICENSE) for details.
Loading