---
title: Automated testing pipeline
description: Build a testing pipeline that clones Git repositories, installs dependencies, runs tests, and reports results.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> 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.

[Skip to content](#%5Ftop) 

# Automated testing pipeline

**Last reviewed:**  7 months ago 

Build a testing pipeline that clones Git repositories, installs dependencies, runs tests, and reports results.

**Time to complete**: 25 minutes

## Prerequisites

1. Sign up for a [Cloudflare account ↗](https://dash.cloudflare.com/sign-up/workers-and-pages).
2. Install [Node.js ↗](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).

Node.js version manager

Use a Node version manager like [Volta ↗](https://volta.sh/) or [nvm ↗](https://github.com/nvm-sh/nvm) to avoid permission issues and change Node.js versions. [Wrangler](https://developers.cloudflare.com/workers/wrangler/install-and-update/), 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).

## 1\. Create your project

 npm  yarn  pnpm 

```
npm create cloudflare@latest -- test-pipeline --template=cloudflare/sandbox-sdk/examples/minimal
```

```
yarn create cloudflare test-pipeline --template=cloudflare/sandbox-sdk/examples/minimal
```

```
pnpm create cloudflare@latest test-pipeline --template=cloudflare/sandbox-sdk/examples/minimal
```

Terminal window

```

cd test-pipeline


```

## 2\. Build the pipeline

Replace `src/index.ts`:

TypeScript

```

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:

Terminal window

```

npm run dev


```

Test with a repository:

Terminal window

```

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

Terminal window

```

npx wrangler deploy


```

For private repositories, set your GitHub token:

Terminal window

```

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](https://developers.cloudflare.com/sandbox/guides/streaming-output/) \- Add real-time test output
* [Background processes](https://developers.cloudflare.com/sandbox/guides/background-processes/) \- Handle long-running tests
* [Sessions API](https://developers.cloudflare.com/sandbox/api/sessions/) \- Cache dependencies between runs

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/sandbox/","name":"Sandbox SDK"}},{"@type":"ListItem","position":3,"item":{"@id":"/sandbox/tutorials/","name":"Tutorials"}},{"@type":"ListItem","position":4,"item":{"@id":"/sandbox/tutorials/automated-testing-pipeline/","name":"Automated testing pipeline"}}]}
```
