Skip to content

Commit a88d811

Browse files
authored
Add GitHub Actions CI/CD use case page (#105)
* docs: add GitHub Actions CI/CD use case page * docs: address review feedback on CI/CD use case page - Remove redundant SDK install section - Add JS/Python toggle for workflow YAML - Merge fragmented code examples into complete review.mjs/review.py files - Use expandable code blocks for one-click copy - Add PR commenting step to the scripts - Adjust prose to be descriptive rather than imperative * docs: add step-by-step walkthrough after review script * docs: address review feedback - trim explanations, update model * docs: remove duplicate How It Works section
1 parent 2fba168 commit a88d811

2 files changed

Lines changed: 283 additions & 1 deletion

File tree

docs.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
{
4646
"group": "Use cases",
4747
"pages": [
48-
"docs/use-cases/computer-use"
48+
"docs/use-cases/computer-use",
49+
"docs/use-cases/ci-cd"
4950
]
5051
},
5152
{

docs/use-cases/ci-cd.mdx

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
---
2+
title: "GitHub Actions CI/CD"
3+
description: "Run AI-powered code review, testing, and validation in secure E2B sandboxes from your GitHub Actions workflows."
4+
icon: "gears"
5+
---
6+
7+
CI/CD pipelines can use AI agents to review pull requests, generate tests, and validate code changes automatically. E2B sandboxes provide the secure, isolated execution environment where these agents can safely clone repositories, run untrusted code, and report results — all triggered by [GitHub Actions](https://docs.github.com/en/actions) on every pull request. Since each run gets a fresh sandbox, malicious or buggy PR code never touches your CI runner.
8+
9+
## GitHub Actions Workflow
10+
11+
The workflow triggers on pull request events and runs a review script. `E2B_API_KEY` and the LLM API key are stored as [GitHub Actions secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions), while the built-in `GITHUB_TOKEN` is available automatically. The `permissions` block grants write access so the script can post PR comments.
12+
13+
<CodeGroup>
14+
```yaml .github/workflows/ai-review.yml (JavaScript)
15+
name: AI Code Review
16+
17+
on:
18+
pull_request:
19+
types: [opened, synchronize]
20+
21+
permissions:
22+
pull-requests: write
23+
24+
jobs:
25+
ai-review:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v4
29+
30+
- name: Set up Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: "20"
34+
35+
- name: Install dependencies
36+
run: npm install e2b openai
37+
38+
- name: Run AI review
39+
env:
40+
E2B_API_KEY: ${{ secrets.E2B_API_KEY }}
41+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
42+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43+
PR_REPO: ${{ github.event.pull_request.head.repo.full_name }}
44+
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
45+
PR_NUMBER: ${{ github.event.pull_request.number }}
46+
GITHUB_REPOSITORY: ${{ github.repository }}
47+
run: node review.mjs
48+
```
49+
```yaml .github/workflows/ai-review.yml (Python)
50+
name: AI Code Review
51+
52+
on:
53+
pull_request:
54+
types: [opened, synchronize]
55+
56+
permissions:
57+
pull-requests: write
58+
59+
jobs:
60+
ai-review:
61+
runs-on: ubuntu-latest
62+
steps:
63+
- uses: actions/checkout@v4
64+
65+
- name: Set up Python
66+
uses: actions/setup-python@v5
67+
with:
68+
python-version: "3.12"
69+
70+
- name: Install dependencies
71+
run: pip install e2b openai
72+
73+
- name: Run AI review
74+
env:
75+
E2B_API_KEY: ${{ secrets.E2B_API_KEY }}
76+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
77+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78+
PR_REPO: ${{ github.event.pull_request.head.repo.full_name }}
79+
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
80+
PR_NUMBER: ${{ github.event.pull_request.number }}
81+
GITHUB_REPOSITORY: ${{ github.repository }}
82+
run: python review.py
83+
```
84+
</CodeGroup>
85+
86+
## Review Script
87+
88+
The workflow calls this script on every PR. It runs five steps inside an E2B sandbox, keeping all untrusted code isolated from the CI runner.
89+
90+
<CodeGroup>
91+
```typescript review.mjs expandable
92+
import { Sandbox, CommandExitError } from 'e2b'
93+
import OpenAI from 'openai'
94+
95+
// --- 1. Create sandbox ---
96+
const sandbox = await Sandbox.create({ timeoutMs: 300_000 })
97+
console.log('Sandbox created:', sandbox.sandboxId)
98+
99+
// --- 2. Clone the PR branch ---
100+
const repoUrl = `https://github.com/${process.env.PR_REPO}.git`
101+
102+
await sandbox.git.clone(repoUrl, {
103+
path: '/home/user/repo',
104+
branch: process.env.PR_BRANCH,
105+
username: 'x-access-token',
106+
password: process.env.GITHUB_TOKEN,
107+
depth: 1,
108+
})
109+
console.log('Repository cloned')
110+
111+
// --- 3. Get the diff and send it to an LLM for review ---
112+
const diffResult = await sandbox.commands.run(
113+
'cd /home/user/repo && git diff origin/main...HEAD'
114+
)
115+
116+
const openai = new OpenAI()
117+
const response = await openai.chat.completions.create({
118+
model: 'gpt-5.2-mini',
119+
messages: [
120+
{
121+
role: 'system',
122+
content:
123+
'You are a senior code reviewer. Analyze the following git diff and provide a concise review with actionable feedback. Focus on bugs, security issues, and code quality.',
124+
},
125+
{
126+
role: 'user',
127+
content: `Review this diff:\n\n${diffResult.stdout}`,
128+
},
129+
],
130+
})
131+
132+
const review = response.choices[0].message.content
133+
console.log('AI Review:', review)
134+
135+
// --- 4. Run the test suite inside the sandbox ---
136+
await sandbox.commands.run('cd /home/user/repo && npm install', {
137+
onStdout: (data) => console.log(data),
138+
onStderr: (data) => console.error(data),
139+
})
140+
141+
try {
142+
await sandbox.commands.run('cd /home/user/repo && npm test', {
143+
onStdout: (data) => console.log(data),
144+
onStderr: (data) => console.error(data),
145+
})
146+
console.log('All tests passed')
147+
} catch (err) {
148+
if (err instanceof CommandExitError) {
149+
console.error('Tests failed with exit code:', err.exitCode)
150+
await sandbox.kill()
151+
process.exit(1)
152+
}
153+
throw err
154+
}
155+
156+
// --- 5. Post results as a PR comment ---
157+
const prNumber = process.env.PR_NUMBER
158+
const repo = process.env.GITHUB_REPOSITORY
159+
160+
await fetch(
161+
`https://api.github.com/repos/${repo}/issues/${prNumber}/comments`,
162+
{
163+
method: 'POST',
164+
headers: {
165+
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
166+
'Content-Type': 'application/json',
167+
},
168+
body: JSON.stringify({
169+
body: `## AI Code Review\n\n${review}`,
170+
}),
171+
}
172+
)
173+
174+
await sandbox.kill()
175+
console.log('Done')
176+
```
177+
```python review.py expandable
178+
import os
179+
import sys
180+
import requests
181+
from e2b import Sandbox, CommandExitException
182+
from openai import OpenAI
183+
184+
# --- 1. Create sandbox ---
185+
sandbox = Sandbox.create(timeout=300)
186+
print(f"Sandbox created: {sandbox.sandbox_id}")
187+
188+
# --- 2. Clone the PR branch ---
189+
repo_url = f"https://github.com/{os.environ['PR_REPO']}.git"
190+
191+
sandbox.git.clone(
192+
repo_url,
193+
path="/home/user/repo",
194+
branch=os.environ["PR_BRANCH"],
195+
username="x-access-token",
196+
password=os.environ["GITHUB_TOKEN"],
197+
depth=1,
198+
)
199+
print("Repository cloned")
200+
201+
# --- 3. Get the diff and send it to an LLM for review ---
202+
diff_result = sandbox.commands.run(
203+
"cd /home/user/repo && git diff origin/main...HEAD"
204+
)
205+
206+
client = OpenAI()
207+
response = client.chat.completions.create(
208+
model="gpt-5.2-mini",
209+
messages=[
210+
{
211+
"role": "system",
212+
"content": "You are a senior code reviewer. Analyze the following git diff and provide a concise review with actionable feedback. Focus on bugs, security issues, and code quality.",
213+
},
214+
{
215+
"role": "user",
216+
"content": f"Review this diff:\n\n{diff_result.stdout}",
217+
},
218+
],
219+
)
220+
221+
review = response.choices[0].message.content
222+
print("AI Review:", review)
223+
224+
# --- 4. Run the test suite inside the sandbox ---
225+
sandbox.commands.run(
226+
"cd /home/user/repo && npm install",
227+
on_stdout=lambda data: print(data),
228+
on_stderr=lambda data: print(data, file=sys.stderr),
229+
)
230+
231+
try:
232+
sandbox.commands.run(
233+
"cd /home/user/repo && npm test",
234+
on_stdout=lambda data: print(data),
235+
on_stderr=lambda data: print(data, file=sys.stderr),
236+
)
237+
print("All tests passed")
238+
except CommandExitException as err:
239+
print(f"Tests failed with exit code: {err.exit_code}", file=sys.stderr)
240+
sandbox.kill()
241+
sys.exit(1)
242+
243+
# --- 5. Post results as a PR comment ---
244+
pr_number = os.environ["PR_NUMBER"]
245+
repo = os.environ["GITHUB_REPOSITORY"]
246+
247+
requests.post(
248+
f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments",
249+
headers={
250+
"Authorization": f"Bearer {os.environ['GITHUB_TOKEN']}",
251+
"Content-Type": "application/json",
252+
},
253+
json={
254+
"body": f"## AI Code Review\n\n{review}",
255+
},
256+
)
257+
258+
sandbox.kill()
259+
print("Done")
260+
```
261+
</CodeGroup>
262+
263+
1. **Create sandbox**`Sandbox.create()` spins up an isolated Linux environment with a 5-minute timeout
264+
2. **Clone the PR**`sandbox.git.clone()` checks out the PR branch using `x-access-token` + `GITHUB_TOKEN` for [authentication](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation)
265+
3. **AI review** — runs `git diff` inside the sandbox, sends the output to an LLM — swap the model for any provider via [Connect LLMs](/docs/quickstart/connect-llms)
266+
4. **Run tests**`commands.run()` streams output in real time and throws on failure (`CommandExitError` / `CommandExitException`)
267+
5. **Post results** — comments the review on the PR via the GitHub REST API, then destroys the sandbox
268+
269+
## Related Guides
270+
271+
<CardGroup cols={3}>
272+
<Card title="Git Integration" icon="code-branch" href="/docs/sandbox/git-integration">
273+
Clone repos, manage branches, and push changes from sandboxes
274+
</Card>
275+
<Card title="Connect LLMs" icon="brain" href="/docs/quickstart/connect-llms">
276+
Integrate AI models with sandboxes using tool calling
277+
</Card>
278+
<Card title="Custom Templates" icon="cube" href="/docs/template/quickstart">
279+
Build reproducible sandbox environments for your pipelines
280+
</Card>
281+
</CardGroup>

0 commit comments

Comments
 (0)