Automated testing pipeline
Last updated Sep 30, 2026 Note
This page documents Sandbox SDK 0.x for existing applications. For new applications, refer to Run tests from a Git repository. To move an existing application to @cloudflare/sandbox 1.0, refer to Migrate from Sandbox SDK 0.x.
Build a testing pipeline that clones Git repositories, installs dependencies, runs tests, and reports results.
Time to complete: 25 minutes
Prerequisites
- Sign up for a Cloudflare account ↗︎.
- Install
Node.js↗︎.
<details>
<summary>
Node.js version manager
</summary>
Use <a href="https://viteplus.dev/guide/env">Vite+ (<code>vp env</code>) ↗︎</a> to install and manage Node.js versions.
For supported Node.js versions, refer to the <a href="https://developers.cloudflare.com/workers/wrangler/install-and-update/#install-wrangler">Wrangler system requirements</a>.
</details>
You'll also need a GitHub repository with tests (public or private with access token).
1. Create your project
npmyarnpnpm
npm create cloudflare@latest -- test-pipeline --template=cloudflare/sandbox-sdk/examples/minimal#v0
yarn create cloudflare test-pipeline --template=cloudflare/sandbox-sdk/examples/minimal#v0
pnpm create cloudflare@latest test-pipeline --template=cloudflare/sandbox-sdk/examples/minimal#v0
cd test-pipeline
2. Build the pipeline
Replace src/index.ts:
import { getSandbox, proxyToSandbox, parseSSEStream, type Sandbox, type ExecEvent } from '@cloudflare/sandbox';
export { Sandbox } from '@cloudflare/sandbox';
interface Env {
Sandbox: DurableObjectNamespace<Sandbox>;
GITHUB_TOKEN?: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const proxyResponse = await proxyToSandbox(request, env);
if (proxyResponse) return proxyResponse;
if (request.method !== 'POST') {
return new Response('POST { "repoUrl": "https://github.com/owner/repo", "branch": "main" }');
}
try {
const { repoUrl, branch } = await request.json();
if (!repoUrl) {
return Response.json({ error: 'repoUrl required' }, { status: 400 });
}
const sandbox = getSandbox(env.Sandbox, `test-${Date.now()}`);
try {
// Clone repository
console.log('Cloning repository...');
let cloneUrl = repoUrl;
if (env.GITHUB_TOKEN && cloneUrl.includes('github.com')) {
cloneUrl = cloneUrl.replace('https://', `https://${env.GITHUB_TOKEN}@`);
}
await sandbox.gitCheckout(cloneUrl, {
...(branch && { branch }),
depth: 1,
targetDir: 'repo'
});
console.log('Repository cloned');
// Detect project type
const projectType = await detectProjectType(sandbox);
console.log(`Detected ${projectType} project`);
// Install dependencies
const installCmd = getInstallCommand(projectType);
if (installCmd) {
console.log('Installing dependencies...');
const installStream = await sandbox.execStream(`cd /workspace/repo && ${installCmd}`);
let installExitCode = 0;
for await (const event of parseSSEStream<ExecEvent>(installStream)) {
if (event.type === 'stdout' || event.type === 'stderr') {
console.log(event.data);
} else if (event.type === 'complete') {
installExitCode = event.exitCode;
}
}
if (installExitCode !== 0) {
return Response.json({
success: false,
error: 'Install failed',
exitCode: installExitCode
});
}
console.log('Dependencies installed');
}
// Run tests
console.log('Running tests...');
const testCmd = getTestCommand(projectType);
const testStream = await sandbox.execStream(`cd /workspace/repo && ${testCmd}`);
let testExitCode = 0;
for await (const event of parseSSEStream<ExecEvent>(testStream)) {
if (event.type === 'stdout' || event.type === 'stderr') {
console.log(event.data);
} else if (event.type === 'complete') {
testExitCode = event.exitCode;
}
}
console.log(`Tests completed with exit code ${testExitCode}`);
return Response.json({
success: testExitCode === 0,
exitCode: testExitCode,
projectType,
message: testExitCode === 0 ? 'All tests passed' : 'Tests failed'
});
} finally {
await sandbox.destroy();
}
} catch (error: any) {
return Response.json({ error: error.message }, { status: 500 });
}
},
};
async function detectProjectType(sandbox: any): Promise<string> {
try {
await sandbox.readFile('/workspace/repo/package.json');
return 'nodejs';
} catch {}
try {
await sandbox.readFile('/workspace/repo/requirements.txt');
return 'python';
} catch {}
try {
await sandbox.readFile('/workspace/repo/go.mod');
return 'go';
} catch {}
return 'unknown';
}
function getInstallCommand(projectType: string): string {
switch (projectType) {
case 'nodejs': return 'npm install';
case 'python': return 'pip install -r requirements.txt || pip install -e .';
case 'go': return 'go mod download';
default: return '';
}
}
function getTestCommand(projectType: string): string {
switch (projectType) {
case 'nodejs': return 'npm test';
case 'python': return 'python -m pytest || python -m unittest discover';
case 'go': return 'go test ./...';
default: return 'echo "Unknown project type"';
}
}
3. Test locally
Start the dev server:
npm run dev
Test with a repository:
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{
"repoUrl": "https://github.com/cloudflare/sandbox-sdk"
}'
You will see progress logs in the wrangler console, and receive a JSON response:
{
"success": true,
"exitCode": 0,
"projectType": "nodejs",
"message": "All tests passed"
}
4. Deploy
npx wrangler deploy
For private repositories, set your GitHub token:
npx wrangler secret put GITHUB_TOKEN
What you built
An automated testing pipeline that:
- Clones Git repositories
- Detects project type (Node.js, Python, Go)
- Installs dependencies automatically
- Runs tests and reports results
Next steps
- Streaming output - Add real-time test output
- Background processes - Handle long-running tests
- Sessions API - Cache dependencies between runs
Was this helpful?
YesNo
On this page
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/sandbox/sdk/tutorials/automated-testing-pipeline/#page","headline":"Automated testing pipeline","description":"Build a Sandbox SDK 0.x testing pipeline that clones Git repositories, installs dependencies, runs tests, and reports results.","url":"https://developers.cloudflare.com/sandbox/sdk/tutorials/automated-testing-pipeline/","inLanguage":"en","image":"https://developers.cloudflare.com/sandbox/sdk/tutorials/automated-testing-pipeline/og.png?v=8bc6b418e93deff4","dateModified":"2026-09-30","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}
description: Clone repositories, analyze code with Claude, and post review comments to GitHub pull requests with the Sandbox SDK 0.x. title: Build a code review bot image: https://developers.cloudflare.com/sandbox/sdk/tutorials/code-review-bot/og.png?v=ec301db0f450ff19
Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/sandbox/llms.txt
Use this file to discover all available pages before exploring further.
Docs