Automated testing pipeline
Build a testing pipeline that clones Git repositories, installs dependencies, runs tests, and reports results.
Time to complete: 25 minutes
- Sign up for a Cloudflare account ↗.
 - Install 
Node.js↗. 
Node.js version manager
 Use a Node version manager like Volta ↗ or nvm ↗ to avoid permission issues and change Node.js versions. Wrangler, discussed later in this guide, requires a Node version of 16.17.0 or later.
You'll also need a GitHub repository with tests (public or private with access token).
npm create cloudflare@latest -- test-pipeline --template=cloudflare/sandbox-sdk/examples/minimalyarn create cloudflare test-pipeline --template=cloudflare/sandbox-sdk/examples/minimalpnpm create cloudflare@latest test-pipeline --template=cloudflare/sandbox-sdk/examples/minimalcd test-pipelineReplace 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"';  }}Start the dev server:
npm run devTest 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"}npx wrangler deployFor private repositories, set your GitHub token:
npx wrangler secret put GITHUB_TOKENAn automated testing pipeline that:
- Clones Git repositories
 - Detects project type (Node.js, Python, Go)
 - Installs dependencies automatically
 - Runs tests and reports results
 
- Streaming output - Add real-time test output
 - Background processes - Handle long-running tests
 - Sessions API - Cache dependencies between runs
 
Was this helpful?
- Resources
 - API
 - New to Cloudflare?
 - Directory
 - Sponsorships
 - Open Source
 
- Support
 - Help Center
 - System Status
 - Compliance
 - GDPR
 
- Company
 - cloudflare.com
 - Our team
 - Careers
 
- © 2025 Cloudflare, Inc.
 - Privacy Policy
 - Terms of Use
 - Report Security Issues
 - Trademark