Skip to content

Commit 4fcf7cb

Browse files
mishushakovclaude
andauthored
feat: sync API specs from infra and belt with Copybara (#1564)
The specs in `spec/` were copied from their source repos by hand and had drifted ~2,400 lines behind infra, so they are now imported with Copybara (`copy.bara.sky`, run in a pinned Docker image by `scripts/fetch-spec.sh`): `make codegen` re-fetches them at the commits pinned in `spec/infra-ref` and `spec/belt-ref` before generating, and the generated-files CI check fails if the tracked copies don't match the pins. Regenerating from the current pins picks up the accumulated spec changes in the generated JS/Python clients (renamed request schemas, `SandboxNetworkConfig`, `SandboxIam` workload identity, `FILE_TYPE_SYMLINK`, access-token auth deprecation, volume path-metadata tweaks). The one handwritten SDK change follows from that: the public `FileType` enums gain a `SYMLINK` member (JS and both Python surfaces) so entries envd reports as symlinks show up in `files.list()` and `getInfo()`/`get_info()` instead of being silently skipped as unknown types. The custom `spec/remove_extra_tags.py` tag-filtering script is replaced by Redocly CLI's `filter-in` decorator (`redocly.yaml`), which produces identical generated JS output; a `filter-out` decorator additionally drops any operation or component schema the upstream specs mark `x-not-implemented: true` (currently the SOCKS5 `SandboxEgressProxyConfig`/`egressProxy` surface, which infra flagged as spec-only); each SDK's bundle now goes to its own gitignored `spec/openapi_generated.<api>.yml` instead of both pipelines overwriting one shared file; Python client models now list fields in spec order instead of alphabetical (mechanical reordering only — construct models with keyword args). Spec fetches try whatever GitHub token is available and fall back to the tracked copies with a warning (the public infra specs also fetch anonymously); in CI a short-lived belt-scoped token is minted from the org-wide Autofixer GitHub App (no new secrets), so fork PRs simply fall back for the belt spec; the CI workflows also cache the Copybara image alongside the codegen image, and the previously ignored `CODEGEN_IMAGE` env is honored by the Makefile. ## Usage ```sh # update the specs: bump a pin, then regenerate echo <infra-commit-sha> > spec/infra-ref make codegen # fetch a single spec without regenerating pnpm fetch:api-spec # spec/openapi.yml from infra pnpm fetch:envd-spec # spec/envd/ from infra pnpm fetch:volume-spec # spec/openapi-volumecontent.yml from belt # try the latest spec without touching the pin E2B_INFRA_REF=main pnpm fetch:api-spec # change which endpoint tags an SDK exposes $EDITOR redocly.yaml && make codegen ``` ```ts // symlinks are now visible in the filesystem API (JS; same shape in Python) const entries = await sandbox.files.list('/home/user') const link = entries.find((e) => e.type === FileType.SYMLINK) console.log(link?.symlinkTarget) ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent ada1744 commit 4fcf7cb

137 files changed

Lines changed: 4489 additions & 2370 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/lucky-donuts-fetch.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'e2b': patch
3+
'@e2b/python-sdk': patch
4+
---
5+
6+
Regenerate API clients from the latest specs, which are now synced with Copybara from their source-of-truth repositories (e2b-dev/infra@e2255f0 for the REST and envd specs, belt for the volume-content spec) instead of being copied by hand. Picks up the latest spec changes: named `SandboxTimeoutRequest`/`SandboxSnapshotRequest`/`SandboxRefreshRequest` request schemas, `SandboxNetworkConfig` and `SandboxIam` workload-identity models, the `FILE_TYPE_SYMLINK` filesystem entry type, and deprecation of access-token auth in favor of API keys. Anything the upstream specs mark `x-not-implemented: true` (currently the SOCKS5 egress-proxy config) is excluded from the generated clients. Generated Python client models now list fields in spec order instead of alphabetical order (the tag filtering moved from a custom script to Redocly CLI); construct them with keyword arguments if you don't already

.changeset/symlink-file-type.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'e2b': minor
3+
'@e2b/python-sdk': minor
4+
---
5+
6+
Add `FileType.SYMLINK` to the sandbox filesystem types. Newer envd versions report symlinks with a dedicated `FILE_TYPE_SYMLINK` entry type; previously the SDKs treated it as unknown, so `files.list()` silently omitted symlink entries and `getInfo()`/`get_info()` returned an `undefined`/`None` type for them. Symlinks now surface as `FileType.SYMLINK` (`'symlink'`) with `symlinkTarget`/`symlink_target` populated, in JS and both sync and async Python.

.github/workflows/codegen_image_cache.yml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
name: Codegen image cache
22

3-
# Seeds the buildkit cache for codegen.Dockerfile from main so pull request
4-
# runs of generated_files.yml warm-start. PR-scoped cache writes are not
5-
# readable by other PRs, so main is the only scope worth writing to.
3+
# Seeds the buildkit caches for codegen.Dockerfile and copybara.Dockerfile
4+
# from main so pull request runs of generated_files.yml warm-start. PR-scoped
5+
# cache writes are not readable by other PRs, so main is the only scope worth
6+
# writing to.
67
on:
78
push:
89
branches: [main]
910
paths:
1011
- 'codegen.Dockerfile'
12+
- 'copybara.Dockerfile'
1113
- '.github/workflows/codegen_image_cache.yml'
1214
workflow_dispatch:
1315

@@ -32,3 +34,11 @@ jobs:
3234
file: codegen.Dockerfile
3335
cache-from: type=gha
3436
cache-to: type=gha,mode=max
37+
38+
- name: Build copybara image and export cache
39+
uses: docker/build-push-action@v6
40+
with:
41+
context: .
42+
file: copybara.Dockerfile
43+
cache-from: type=gha,scope=copybara
44+
cache-to: type=gha,mode=max,scope=copybara

.github/workflows/generated_files.yml

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ jobs:
2525
generated:
2626
- 'spec/**/!(*.md)'
2727
- 'codegen.Dockerfile'
28+
- 'copybara.Dockerfile'
29+
- 'copy.bara.sky'
30+
- 'redocly.yaml'
31+
- 'scripts/fetch-spec.sh'
2832
- 'Makefile'
2933
- 'packages/**/!(*.md)'
3034
- 'pnpm-lock.yaml'
@@ -86,8 +90,43 @@ jobs:
8690
# the repo's 10 GB Actions cache, evicting other caches.
8791
cache-from: type=gha
8892

93+
- name: Build copybara image with caching
94+
uses: docker/build-push-action@v6
95+
with:
96+
context: .
97+
file: copybara.Dockerfile
98+
tags: e2b-copybara:latest
99+
load: true # makes the image available for `docker run`
100+
# Cache is seeded from main by codegen_image_cache.yml (see the
101+
# cache-to note above for why there is no cache-to here).
102+
cache-from: type=gha,scope=copybara
103+
104+
# scripts/fetch-spec.sh needs read access to the private e2b-dev/belt
105+
# repo to fetch the volume-content spec. The default `secrets.GITHUB_TOKEN`
106+
# is scoped to this repository only, so mint a short-lived token from
107+
# the Autofixer GitHub App instead, scoped to every repo the fetches
108+
# touch. Fork PRs have no access to the App secret, so they skip the
109+
# mint, run the public infra fetches with the default token, and fall
110+
# back to the tracked volume-content spec (make fetch-specs warns
111+
# instead of failing).
112+
- name: Generate token for spec repos
113+
id: spec-repos-token
114+
if: github.event.pull_request.head.repo.full_name == github.repository
115+
uses: actions/create-github-app-token@v2
116+
with:
117+
app-id: ${{ vars.AUTOFIXER_APP_ID }}
118+
private-key: ${{ secrets.AUTOFIXER_APP_SECRET }}
119+
owner: e2b-dev
120+
repositories: belt,infra
121+
permission-contents: read
122+
89123
- name: Run codegen
90-
run: CODEGEN_IMAGE=codegen-env:latest make codegen
124+
env:
125+
GITHUB_TOKEN: ${{ steps.spec-repos-token.outputs.token || github.token }}
126+
run: |
127+
CODEGEN_IMAGE=codegen-env:latest \
128+
COPYBARA_IMAGE=e2b-copybara:latest \
129+
make codegen
91130
92131
- name: Check for uncommitted changes
93132
run: |

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,5 +293,8 @@ cython_debug/
293293
sdk_ref/
294294
sdkRefRoutes.json
295295

296-
# SDK Client generated spec
296+
# SDK Client generated specs (per-SDK redocly bundle outputs; the bare
297+
# openapi_generated.yml pattern covers stale copies from older checkouts)
298+
spec/openapi_generated.*.yml
297299
spec/openapi_generated.yml
300+

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Use pnpm for node and uv for python to install and update dependencies.
22
Run `pnpm run format`, `pnpm run lint` and `pnpm run typecheck` before committing changes.
33
When modifying the SDK packages, ensure equivalent changes are applied to both JS as well as sync and async Python implementations.
4-
To re-generate the API client run `make codegen` in the repository root when modifying spec/.
4+
Never edit the API, envd, or volume-content specs in spec/ by hand; they are synced with Copybara from e2b-dev/infra and e2b-dev/belt at the commits pinned in spec/infra-ref and spec/belt-ref (see copy.bara.sky and spec/README.md). To update the specs, bump the pins and re-run `make codegen`, which re-fetches them before generating.
55
Create or update tests covering affected codepaths and run them using `pnpm run test`.
66
Generate a changeset when updating packages/cli, packages/js-sdk, packages/python-sdk with `pnpm changeset` in the repository root.
77
When creating a pull request, add usage examples for user-facing changes to the PR description.

Makefile

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
1+
# Specs are fetched from their source-of-truth repositories (e2b-dev/infra
2+
# and e2b-dev/belt) at the commits pinned in spec/infra-ref and spec/belt-ref.
3+
# To update the specs, bump the pins and re-run `make codegen`.
4+
# When a fetch fails (no GitHub token, no access to the private belt repo),
5+
# fall back to the tracked copy in spec/ with a warning.
6+
.PHONY: fetch-specs
7+
fetch-specs:
8+
@for spec in api-spec envd-spec volume-api-spec; do \
9+
./scripts/fetch-spec.sh $$spec || \
10+
echo "WARNING: could not fetch $$spec; using the tracked copy in spec/"; \
11+
done
12+
13+
# Set CODEGEN_IMAGE to skip the image build and use a prebuilt image instead
14+
# (CI builds it separately with a warm buildkit cache).
115
.PHONY: codegen
2-
codegen:
16+
codegen: fetch-specs
17+
ifeq ($(CODEGEN_IMAGE),)
318
@echo "Building codegen image"
419
docker build -q -t codegen-env . -f codegen.Dockerfile
20+
endif
521
@echo "Generating code"
6-
docker run -v $$PWD:/workspace codegen-env make generate
22+
docker run -v $$PWD:/workspace $(or $(CODEGEN_IMAGE),codegen-env) make generate
723

824
generate: generate-js generate-python
925

codegen.Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ ENV PATH="/go/bin:${PATH}"
2121
# https://github.com/openapi-generators/openapi-python-client/pull/1296
2222
# protoc-gen-py generates the Python envd protobuf-py messages and
2323
# protoc-gen-connectrpc the Connect stubs.
24-
RUN pip install black==26.3.1 pyyaml==6.0.2 e2b-openapi-python-client==0.26.2 datamodel-code-generator==0.34.0 protoc-gen-connectrpc==0.11.1 protoc-gen-py==0.1.1
24+
RUN pip install black==26.3.1 e2b-openapi-python-client==0.26.2 datamodel-code-generator==0.34.0 protoc-gen-connectrpc==0.11.1 protoc-gen-py==0.1.1
2525

2626
# Install Node.js (pinned to match .tool-versions)
2727
ENV NODE_VERSION=22.18.0
@@ -38,6 +38,7 @@ ENV PNPM_VERSION=9.15.5
3838
RUN npm install -g \
3939
pnpm@${PNPM_VERSION} \
4040
@connectrpc/protoc-gen-connect-es@1.6.1 \
41-
@bufbuild/protoc-gen-es@2.6.2
41+
@bufbuild/protoc-gen-es@2.6.2 \
42+
@redocly/cli@2.39.0
4243

4344
CMD ["make", "generate"]

copy.bara.sky

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copybara config that imports API specs from their source-of-truth
2+
# repositories into spec/. The imported files are tracked here but must not
3+
# be edited by hand — `make codegen` re-fetches the infra ones at the commit
4+
# pinned in spec/infra-ref before generating the clients, and the
5+
# generated-files CI check fails if the tracked copies don't match the pin.
6+
# See spec/README.md.
7+
#
8+
# scripts/fetch-spec.sh runs these workflows with `--folder-dir` pointing at
9+
# spec/ itself. Each workflow's destination_files glob declares the spec/
10+
# paths its upstream owns: after a successful fetch, Copybara replaces
11+
# exactly those paths (so files deleted upstream are deleted here too) and
12+
# leaves the repo-owned files in spec/ untouched.
13+
14+
INFRA_REPO = "https://github.com/e2b-dev/infra.git"
15+
BELT_REPO = "https://github.com/e2b-dev/belt.git"
16+
17+
AUTHORING = authoring.pass_thru("E2B <hello@e2b.dev>")
18+
19+
# REST API spec -> spec/openapi.yml
20+
core.workflow(
21+
name = "api-spec",
22+
origin = git.github_origin(
23+
url = INFRA_REPO,
24+
),
25+
destination = folder.destination(),
26+
origin_files = glob(["spec/openapi.yml"]),
27+
destination_files = glob(["openapi.yml"]),
28+
authoring = AUTHORING,
29+
mode = "SQUASH",
30+
transformations = [
31+
core.move("spec/openapi.yml", "openapi.yml"),
32+
],
33+
)
34+
35+
# envd spec (HTTP API + protobufs) -> spec/envd/
36+
core.workflow(
37+
name = "envd-spec",
38+
origin = git.github_origin(
39+
url = INFRA_REPO,
40+
),
41+
destination = folder.destination(),
42+
origin_files = glob(
43+
[
44+
"packages/envd/spec/envd.yaml",
45+
"packages/envd/spec/filesystem/**",
46+
"packages/envd/spec/process/**",
47+
],
48+
# This repo has its own buf generation templates in spec/envd.
49+
exclude = ["packages/envd/spec/buf*.yaml"],
50+
),
51+
destination_files = glob([
52+
"envd/envd.yaml",
53+
"envd/filesystem/**",
54+
"envd/process/**",
55+
]),
56+
authoring = AUTHORING,
57+
mode = "SQUASH",
58+
transformations = [
59+
core.move("packages/envd/spec", "envd"),
60+
],
61+
)
62+
63+
# Volume-content API spec -> spec/openapi-volumecontent.yml
64+
# belt is private, so the fetch authenticates with a GitHub token
65+
# (see scripts/fetch-spec.sh).
66+
core.workflow(
67+
name = "volume-api-spec",
68+
origin = git.github_origin(
69+
url = BELT_REPO,
70+
),
71+
destination = folder.destination(),
72+
origin_files = glob(["packages/volume-content/openapi.yml"]),
73+
destination_files = glob(["openapi-volumecontent.yml"]),
74+
authoring = AUTHORING,
75+
mode = "SQUASH",
76+
transformations = [
77+
core.move("packages/volume-content/openapi.yml", "openapi-volumecontent.yml"),
78+
],
79+
)

copybara.Dockerfile

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM eclipse-temurin:21-jre
2+
3+
RUN apt-get update && apt-get install -y --no-install-recommends git && \
4+
rm -rf /var/lib/apt/lists/*
5+
6+
# gh serves as the git credential helper for private origins; it reads the
7+
# token from GH_TOKEN in the environment.
8+
ENV GH_VERSION=2.96.0
9+
RUN ARCH=$(uname -m) && \
10+
case "$ARCH" in \
11+
x86_64) GH_ARCH="amd64" ;; \
12+
arm64|aarch64) GH_ARCH="arm64" ;; \
13+
*) echo "Unsupported architecture: $ARCH" && exit 1 ;; \
14+
esac && \
15+
curl -fsSL https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_${GH_ARCH}.tar.gz | \
16+
tar -xz --strip-components=2 -C /usr/local/bin gh_${GH_VERSION}_linux_${GH_ARCH}/bin/gh && \
17+
git config --system credential.helper '!gh auth git-credential'
18+
19+
ENV COPYBARA_VERSION=v20260720
20+
ENV COPYBARA_SHA256=e94448c702addc17cfc45d4bbfc8509d458b9a25f4715e3e77207ad570e1075d
21+
RUN curl -fsSL -o /opt/copybara.jar \
22+
https://github.com/google/copybara/releases/download/${COPYBARA_VERSION}/copybara_deploy.jar && \
23+
echo "${COPYBARA_SHA256} /opt/copybara.jar" | sha256sum -c -
24+
25+
WORKDIR /workspace
26+
27+
ENTRYPOINT ["java", "-jar", "/opt/copybara.jar"]

0 commit comments

Comments
 (0)