Skip to content

Commit b6c9cd9

Browse files
committed
feat: hash-based manual release workflow
Mirror the manual build/release pipeline from e2b-dev/fc-versions, but identify each kernel build by a content hash of its inputs (configs + optional patches). Changing a flag or adding a patch yields a new artifact named vmlinux-<version>_<sha256[:7]>. - scripts/validate.py: resolves version names, computes hashes, builds the matrix and skips arches whose artifact is already in the GitHub release. - build.sh: now accepts <kernel_version> [arch], computes the same version_name (or honors VERSION_NAME), supports patches/<version>/. - .github/workflows/release.yml: workflow_dispatch with validate -> build -> publish (GH release) -> deploy (GCS), skipping work whose artifacts already exist. - Drops the old build-on-every-push workflow; releases are explicit.
1 parent f371098 commit b6c9cd9

5 files changed

Lines changed: 518 additions & 183 deletions

File tree

.github/workflows/fc-kernels.yml

Lines changed: 0 additions & 118 deletions
This file was deleted.

.github/workflows/release.yml

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
name: Manual Build & Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
kernel_versions:
7+
description: 'Comma-separated kernel versions to build (default: kernel_versions.txt)'
8+
required: false
9+
type: string
10+
default: ''
11+
build_amd64:
12+
description: 'Build for amd64 architecture'
13+
required: false
14+
type: boolean
15+
default: true
16+
build_arm64:
17+
description: 'Build for arm64 architecture'
18+
required: false
19+
type: boolean
20+
default: true
21+
22+
permissions:
23+
contents: write
24+
id-token: write
25+
26+
jobs:
27+
validate:
28+
runs-on: ubuntu-24.04
29+
outputs:
30+
build_matrix: ${{ steps.validate.outputs.build_matrix }}
31+
versions: ${{ steps.validate.outputs.versions }}
32+
has_new_artifacts: ${{ steps.validate.outputs.has_new_artifacts }}
33+
steps:
34+
- uses: actions/checkout@v4
35+
36+
- uses: actions/setup-python@v5
37+
with:
38+
python-version: '3.11'
39+
40+
- id: validate
41+
env:
42+
GH_TOKEN: ${{ github.token }}
43+
run: |
44+
python3 scripts/validate.py \
45+
--kernel-versions "${{ inputs.kernel_versions }}" \
46+
--build-amd64 "${{ inputs.build_amd64 }}" \
47+
--build-arm64 "${{ inputs.build_arm64 }}"
48+
49+
build:
50+
needs: validate
51+
if: needs.validate.outputs.has_new_artifacts == 'true'
52+
strategy:
53+
fail-fast: false
54+
matrix: ${{ fromJson(needs.validate.outputs.build_matrix) }}
55+
runs-on: ${{ matrix.runner }}
56+
steps:
57+
- uses: actions/checkout@v4
58+
59+
- name: Build kernel ${{ matrix.version_name }} (${{ matrix.arch }})
60+
env:
61+
VERSION_NAME: ${{ matrix.version_name }}
62+
run: |
63+
target_arch="${{ matrix.arch }}"
64+
[ "$target_arch" = "amd64" ] && target_arch="x86_64"
65+
sudo VERSION_NAME="$VERSION_NAME" ./build.sh "${{ matrix.kernel_version }}" "$target_arch"
66+
67+
- uses: actions/upload-artifact@v4
68+
with:
69+
name: ${{ matrix.version_name }}-${{ matrix.arch }}
70+
path: ./builds/${{ matrix.version_name }}
71+
retention-days: 7
72+
73+
publish:
74+
needs: [validate, build]
75+
if: needs.validate.outputs.has_new_artifacts == 'true'
76+
runs-on: ubuntu-24.04
77+
steps:
78+
- uses: actions/checkout@v4
79+
with:
80+
fetch-depth: 0
81+
82+
- uses: actions/download-artifact@v4
83+
with:
84+
path: ./builds
85+
merge-multiple: true
86+
87+
- name: Show build artifacts
88+
run: find ./builds -type f | head -50
89+
90+
- name: Create or update releases
91+
env:
92+
GH_TOKEN: ${{ github.token }}
93+
VERSIONS: ${{ needs.validate.outputs.versions }}
94+
run: |
95+
set -euo pipefail
96+
git config user.name "github-actions[bot]"
97+
git config user.email "github-actions[bot]@users.noreply.github.com"
98+
99+
echo "$VERSIONS" | jq -c '.[]' | while read -r entry; do
100+
version_name=$(echo "$entry" | jq -r '.version_name')
101+
kernel_version=$(echo "$entry" | jq -r '.kernel_version')
102+
103+
if ! gh release view "$version_name" >/dev/null 2>&1; then
104+
if ! git rev-parse "refs/tags/$version_name" >/dev/null 2>&1; then
105+
git tag "$version_name"
106+
git push origin "$version_name"
107+
fi
108+
gh release create "$version_name" \
109+
--title "Kernel $version_name" \
110+
--notes "Linux kernel $kernel_version built from configs at ${{ github.sha }}"
111+
fi
112+
113+
existing=$(gh release view "$version_name" --json assets -q '.assets[].name' || true)
114+
115+
for arch in amd64 arm64; do
116+
local_path="./builds/$version_name/$arch/vmlinux.bin"
117+
[ -f "$local_path" ] || continue
118+
119+
asset="vmlinux-${arch}.bin"
120+
if echo "$existing" | grep -qx "$asset"; then
121+
echo "Release $version_name: $asset exists, skipping"
122+
else
123+
tmp=$(mktemp -d)/$asset
124+
cp "$local_path" "$tmp"
125+
gh release upload "$version_name" "$tmp"
126+
rm -f "$tmp"
127+
fi
128+
129+
# Legacy non-arch-suffixed asset (amd64 only) for backwards compat.
130+
if [ "$arch" = "amd64" ] && ! echo "$existing" | grep -qx "vmlinux.bin"; then
131+
tmp=$(mktemp -d)/vmlinux.bin
132+
cp "$local_path" "$tmp"
133+
gh release upload "$version_name" "$tmp"
134+
rm -f "$tmp"
135+
fi
136+
done
137+
done
138+
139+
deploy:
140+
needs: [validate, publish]
141+
if: needs.validate.outputs.has_new_artifacts == 'true'
142+
runs-on: ubuntu-24.04
143+
steps:
144+
- uses: actions/checkout@v4
145+
146+
- uses: google-github-actions/auth@v2
147+
with:
148+
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
149+
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT_EMAIL }}
150+
151+
- uses: google-github-actions/setup-gcloud@v2
152+
153+
- name: Download release assets and upload to GCS
154+
env:
155+
GH_TOKEN: ${{ github.token }}
156+
GCP_BUCKET_NAME: ${{ vars.GCP_BUCKET_NAME }}
157+
VERSIONS: ${{ needs.validate.outputs.versions }}
158+
run: |
159+
set -euo pipefail
160+
echo "$VERSIONS" | jq -c '.[]' | while read -r entry; do
161+
version_name=$(echo "$entry" | jq -r '.version_name')
162+
163+
for arch in amd64 arm64; do
164+
asset="vmlinux-${arch}.bin"
165+
dl_dir="./dl/$version_name/$arch"
166+
mkdir -p "$dl_dir"
167+
if ! gh release download "$version_name" \
168+
--repo "${{ github.repository }}" \
169+
--pattern "$asset" \
170+
--output "$dl_dir/vmlinux.bin" 2>/dev/null; then
171+
continue
172+
fi
173+
174+
gcs_path="gs://${GCP_BUCKET_NAME}/kernels/${version_name}/${arch}/vmlinux.bin"
175+
if gcloud storage ls "$gcs_path" >/dev/null 2>&1; then
176+
echo "GCS: $gcs_path exists, skipping"
177+
else
178+
gcloud storage cp "$dl_dir/vmlinux.bin" "$gcs_path"
179+
fi
180+
181+
# Legacy non-arch path (amd64 only) for backwards compat.
182+
if [ "$arch" = "amd64" ]; then
183+
legacy_path="gs://${GCP_BUCKET_NAME}/kernels/${version_name}/vmlinux.bin"
184+
if gcloud storage ls "$legacy_path" >/dev/null 2>&1; then
185+
echo "GCS: $legacy_path exists, skipping"
186+
else
187+
gcloud storage cp "$dl_dir/vmlinux.bin" "$legacy_path"
188+
fi
189+
fi
190+
done
191+
done

README.md

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,59 @@
22

33
## Overview
44

5-
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.
5+
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.
6+
7+
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:
8+
9+
```
10+
vmlinux-<kernel_version>_<sha256[:7]>
11+
```
612

713
## Prerequisites
814

915
- Linux environment (for building kernels)
1016

11-
## Building Kernels
17+
## Building locally
1218

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

1724
2. **Build:**
1825
```sh
19-
make build
20-
# or directly
21-
./build.sh
26+
make build # builds all versions in kernel_versions.txt for x86_64
27+
make build-arm64 # same, for arm64
28+
./build.sh 6.1.158 # build a single version (x86_64)
29+
./build.sh 6.1.158 arm64
2230
```
23-
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`.
2431

25-
## Development Workflow
26-
- On every push, GitHub Actions will automatically build the kernels and save it as an artifact.
32+
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`.
33+
34+
## Releasing
35+
36+
1. Run the **Manual Build & Release** workflow (Actions → Manual Build & Release → Run workflow).
37+
2. The workflow:
38+
- Computes a content hash for each kernel version from its configs and patches.
39+
- Skips arches whose artifact is already present in the matching GitHub release.
40+
- 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>/`.
41+
42+
### Workflow inputs
43+
44+
- `kernel_versions` (optional): comma-separated kernel versions. Defaults to all versions in `kernel_versions.txt`.
45+
- `build_amd64` / `build_arm64` (optional, default `true`): which architectures to build.
46+
47+
## New kernel in E2B's infra
48+
_Note: these steps should give you a new kernel on your self-hosted E2B using https://github.com/e2b-dev/infra_
49+
50+
- Run the release workflow to publish the new kernel build.
51+
- 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.
52+
- Build and deploy `api`.
2753

2854
## Architecture naming
2955

3056
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.
3157

32-
## New Kernel in E2B's infra
33-
_Note: these steps should give you new kernel on your self-hosted E2B using https://github.com/e2b-dev/infra_
34-
35-
- Copy the kernel build in your project's object storage under `e2b-*-fc-kernels`
36-
- In [packages/api/internal/cfg/model.go](https://github.com/e2b-dev/infra/blob/main/packages/api/internal/cfg/model.go) update `DefaultKernelVersion`
37-
- Build and deploy `api`
38-
3958
## License
4059

41-
This project is licensed under the Apache License 2.0. See [LICENSE](LICENSE) for details.
60+
This project is licensed under the Apache License 2.0. See [LICENSE](LICENSE) for details.

0 commit comments

Comments
 (0)