> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tensorlake.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# CICD & Build Systems

> Execute build steps and run tests in isolated, reproducible environments.

Build systems and CI/CD pipelines often require clean, isolated environments to ensure reproducibility and prevent dependency conflicts. Tensorlake Sandboxes allow you to spin up ephemeral containers on demand, upload source code, run tests, and retrieve artifacts.

This example demonstrates a complete mini-CI pipeline that creates a dummy project, runs tests, and builds a distribution package inside a sandbox.

## TypeScript SDK starter

If your build runner is already in Node.js, the workflow is the same: stream project files into a sandbox, run each CI step, and pull artifacts back out if needed.

```typescript theme={null}
import { readFile, readdir } from "node:fs/promises";
import path from "node:path";
import { Sandbox } from "tensorlake";


async function copyTree(sandbox: Sandbox, localDir: string, remoteDir: string) {
  await sandbox.run("mkdir", { args: ["-p", remoteDir] });

  for (const entry of await readdir(localDir, { withFileTypes: true })) {
    const localPath = path.join(localDir, entry.name);
    const remotePath = path.posix.join(remoteDir, entry.name);

    if (entry.isDirectory()) {
      await copyTree(sandbox, localPath, remotePath);
    } else {
      await sandbox.writeFile(remotePath, await readFile(localPath));
    }
  }
}

async function runStep(
  sandbox: Sandbox,
  name: string,
  command: string,
  args: string[],
) {
  const result = await sandbox.run(command, {
    args,
    workingDir: "/workspace/project",
  });

  if (result.exitCode !== 0) {
    throw new Error(`${name} failed\n${result.stderr}`);
  }
}

const sandbox = await Sandbox.create({ timeoutSecs: 900 });

try {
  await copyTree(sandbox, "./my_cool_project", "/workspace/project");
  await runStep(sandbox, "Install Dependencies", "pip", [
    "install",
    "-r",
    "requirements.txt",
    "--user",
    "--break-system-packages",
  ]);
  await runStep(sandbox, "Run Tests", "python", [
    "-m",
    "pytest",
    "/workspace/project",
  ]);
  await runStep(sandbox, "Build Package", "python", [
    "setup.py",
    "sdist",
    "bdist_wheel",
  ]);
} finally {
  await sandbox.terminate();
  client.close();
}
```

## Example: CI/CD Pipeline

The following script simulates a CI pipeline. It generates a simple Python project, uploads it to a sandbox, installs dependencies, runs `pytest`, and builds a wheel file.

```python theme={null}
import os
import tempfile
import shutil
from dotenv import load_dotenv
from setuptools import setup, find_packages

load_dotenv()
from tensorlake.sandbox import Sandbox

def create_dummy_project(base_dir):
    """
    Helper to create a simple Python project layout locally
    so we have something to build/test.
    """
    project_root = os.path.join(base_dir, "my_cool_project")
    src_dir = os.path.join(project_root, "src", "my_cool_project")
    tests_dir = os.path.join(project_root, "tests")

    os.makedirs(src_dir, exist_ok=True)
    os.makedirs(tests_dir, exist_ok=True)

    # Create setup.py
    with open(os.path.join(project_root, "setup.py"), "w") as f:
        f.write("from setuptools import setup, find_packages\n"
                "setup(name='my_cool_project', version='0.1.0', "
                "package_dir={'': 'src'}, packages=find_packages(where='src'))")

    # Create source code
    with open(os.path.join(src_dir, "__init__.py"), "w") as f:
        f.write("def add(a, b): return a + b")

    # Create test
    with open(os.path.join(tests_dir, "test_logic.py"), "w") as f:
        f.write("from my_cool_project import add\ndef test_add(): assert add(2, 3) == 5")

    # Create requirements.txt
    with open(os.path.join(project_root, "requirements.txt"), "w") as f:
        f.write("pytest\nsetuptools\nwheel\n")

    return project_root

def run_ci_step(sandbox, name, command, working_dir="/workspace/project", env=None):
    """Runs a command in the sandbox, prints the output, and checks for errors."""
    print(f"--- Running Step: {name} ---")
    result = sandbox.run(command[0], command[1:], env=env, working_dir=working_dir)

    print(f"STDOUT:\n{result.stdout}")
    if result.stderr:
        print(f"STDERR:\n{result.stderr}")

    if result.exit_code != 0:
        print(f"❌ Step '{name}' FAILED with exit code {result.exit_code}")
        raise RuntimeError(f"CI step '{name}' failed.")
    else:
        print(f"✅ Step '{name}' PASSED")
    print("-" * (len(name) + 20))


def copy_to_sandbox(sandbox, local_path, remote_path):
    """Recursively copies a local directory to the sandbox."""
    print(f"Copying {local_path} -> {remote_path} ...")
    sandbox.run("mkdir", ["-p", remote_path])

    for root, dirs, files in os.walk(local_path):
        rel_root = os.path.relpath(root, local_path)
        remote_root = remote_path if rel_root == "." else os.path.join(remote_path, rel_root)

        for d in dirs:
            sandbox.run("mkdir", ["-p", os.path.join(remote_root, d)])

        for file in files:
            local_file = os.path.join(root, file)
            remote_file = os.path.join(remote_root, file)
            with open(local_file, "rb") as f:
                sandbox.write_file(remote_file, f.read())

async def main():
    # 1. Setup local dummy project
    temp_dir = tempfile.mkdtemp()
    project_path = create_dummy_project(temp_dir)
    print(f"Dummy project created at: {project_path}")


    try:
        # 2. Create Sandbox
        sandbox = Sandbox.create()
            print("🚀 Sandbox created for CI/CD pipeline.")

            # 3. Upload Code
            copy_to_sandbox(sandbox, project_path, "/workspace/project")

            # 4. Install Dependencies
            run_ci_step(
                sandbox,
                "Install Dependencies",
                ["pip", "install", "-r", "requirements.txt", "--user", "--break-system-packages"],
                working_dir="/workspace/project"
            )

            # 5. Run Tests
            run_ci_step(
                sandbox,
                "Run Tests",
                ["python", "-m", "pytest", "/workspace/project"],
                env={"PYTHONPATH": "/workspace/project/src"},
            )

            # 6. Build Artifacts
            run_ci_step(sandbox, "Build Package", ["python", "setup.py", "sdist", "bdist_wheel"])
            
            print("\n🎉 CI/CD Pipeline finished successfully! 🎉")
    except Exception as e:
        print(f"\n🔥 CI/CD Pipeline FAILED: {e}")
    finally:
        shutil.rmtree(temp_dir)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())
```

## How It Works

1. **Environment Creation**: The script instantiates a fresh sandbox. This ensures no leftover files or environment variables from previous builds affect the current run.
2. **File Injection**: The custom `copy_to_sandbox` function walks the local directory tree and streams files into the sandbox using `sandbox.write_file()`. This simulates the "checkout" phase of a CI pipeline.
3. **Step Execution**: The `run_ci_step` helper function executes shell commands (like `pip` and `pytest`) inside the sandbox using `sandbox.run()`. It captures `stdout`, `stderr`, and exit codes to determine success or failure.
4. **Artifact Generation**: The build step generates `.whl` and `.tar.gz` files inside the sandbox. In a real-world scenario, you would use `sandbox.read_file()` to download these artifacts back to your storage.

## Learn More

<CardGroup cols={2}>
  <Card title="File Operations" icon="folder-open" href="/sandboxes/file-operations">
    Learn how to efficiently move large files and directories in and out of sandboxes.
  </Card>

  <Card title="Process Management" icon="gears" href="/sandboxes/processes">
    Understand how to manage long-running processes and handle exit codes.
  </Card>
</CardGroup>
