diff --git a/.github/workflows/e2b-template.yml b/.github/workflows/e2b-template.yml new file mode 100644 index 00000000..3868f40c --- /dev/null +++ b/.github/workflows/e2b-template.yml @@ -0,0 +1,116 @@ +name: E2B template + +on: + push: + branches: [main] + workflow_dispatch: + pull_request: + paths: + - .github/workflows/e2b-template.yml + - scripts/e2b-template.py + - Dockerfile.hosted + - .dockerignore + - hosted-snapshot/** + - pyproject.toml + - src/** + +permissions: + contents: read + +# Distinct commits get distinct templates; never discard a merge's publication. +# Serialize reruns of the same commit and let sandbox cleanup finish. +concurrency: + group: e2b-template-${{ github.ref }}-${{ github.sha }} + cancel-in-progress: false + +env: + UV_VERSION: "0.12.9" + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Validate publisher inputs without credentials + run: python3 scripts/e2b-template.py --dry-run + + publish: + needs: validate + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + runs-on: ubuntu-latest + timeout-minutes: 120 + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Check required credentials + env: + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} + run: | + if [[ -z "$E2B_API_KEY" ]]; then + echo "::error::Add the E2B_API_KEY repository secret to publish templates." + exit 1 + fi + - uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0 + with: + version: ${{ env.UV_VERSION }} + enable-cache: false + - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + - uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build, publish, and certify + env: + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} + # E2B's builder must also authenticate to pull the private GHCR image. + E2B_REGISTRY_USERNAME: ${{ github.actor }} + E2B_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + uv run --python 3.12 scripts/e2b-template.py \ + --image-repository "ghcr.io/${GITHUB_REPOSITORY,,}/alk-hosted-runtime" \ + --image-tag "${GITHUB_SHA}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \ + --template-name "alk-hosted-${GITHUB_SHA:0:12}" \ + --output dist/e2b-template-release.json + - name: Summarize certified release + run: | + python3 - <<'PY' + import json + import os + from pathlib import Path + + release = json.loads(Path("dist/e2b-template-release.json").read_text()) + with open(os.environ["GITHUB_STEP_SUMMARY"], "a") as summary: + summary.write("## Certified E2B template\n\n") + for label, key in ( + ("ALK commit", "alk_source_revision"), + ("Image", "image_reference"), + ("Template reference", "template_reference"), + ("CPU", "cpu_count"), + ("Memory (MB)", "memory_mb"), + ("Verified disk (GB)", "verified_disk_gb"), + ): + summary.write(f"- {label}: `{release[key]}`\n") + summary.write("\nCertification checks:\n\n") + for check in release["certification_checks"]: + summary.write(f"- {check}\n") + summary.write( + "\nDownload the release artifact for the immutable template reference " + "and full certification metadata.\n" + ) + PY + - name: Save certified release metadata + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: e2b-template-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }} + path: dist/e2b-template-release.json + if-no-files-found: error + retention-days: 90 diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index dd95d639..39240b5d 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -51,3 +51,39 @@ When moving an existing surface: repository as the source path. 5. Update public docs/examples to use `agent-learning-kit`. 6. Only then simplify or hide the older engine-level surface. +# Automatic E2B templates + +The `E2B template` GitHub Actions workflow builds and certifies the hosted runtime +on every push to `main`, including merges. It can also be rerun manually with +**Actions → E2B template → Run workflow**, selecting `main`. Pull requests that +change runtime inputs run credential-free validation only. + +One-time setup: add the E2B team's API key as the repository Actions secret +`E2B_API_KEY`. The workflow uses `GITHUB_TOKEN` with `packages: write` to push to +`ghcr.io/future-agi/agent-learning-kit/alk-hosted-runtime`, and supplies that +short-lived credential to the E2B builder for the image import. No Docker Hub +credential is needed. Organization policy must permit the repository to create +GHCR packages; if the package already exists, grant this repository Actions access +in its package settings. See GitHub's +[Container registry documentation](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry). + +The workflow runs `scripts/e2b-template.py` against the checked-out commit. It builds +`Dockerfile.hosted` for Linux amd64, pushes a uniquely tagged image, imports its +immutable digest into `alk-hosted-<12-character commit SHA>`, and certifies the +capabilities in `hosted-snapshot/catalog.json` in a temporary sandbox. The existing +publisher defaults apply: 4 CPUs, 8192 MB RAM, and at least 10 GB verified disk. +The publisher cleans up its certification sandbox when the check finishes. + +A successful run exposes the immutable `template-name:build-id` reference in the +job summary and saves `e2b-template-release.json` as a 90-day workflow artifact. +That file includes the source commit, image digest, resource sizes, and certification +results. A failed certification fails the job and produces no certified release +artifact; its image/template may already exist, so use only successful releases. +Download the artifact and set the platform's `ALK_E2B_TEMPLATE_REFERENCE` to its +`template_reference` when deploying. This workflow creates the templates; it does +not change platform deployment configuration or move a production alias. + +Each commit has its own concurrency group, so a newer merge does not cancel or +replace an older commit's publication. Reruns of the same commit are serialized. +Reruns create a new image tag and E2B build; always use the immutable reference +from the desired run.