diff --git a/docs.json b/docs.json index 4254115..e6e6af5 100644 --- a/docs.json +++ b/docs.json @@ -151,6 +151,12 @@ "docs/commands/background" ] }, + { + "group": "Git", + "pages": [ + "docs/git" + ] + }, { "group": "MCP Gateway", "pages": [ @@ -323,4 +329,4 @@ "permanent": true } ] -} \ No newline at end of file +} diff --git a/docs/git.mdx b/docs/git.mdx new file mode 100644 index 0000000..a0355a0 --- /dev/null +++ b/docs/git.mdx @@ -0,0 +1,421 @@ +--- +title: "Git" +sidebarTitle: Overview +--- + +Use the `sandbox.git` methods to run common git operations inside a sandbox. + +### Authentication & Identity + +#### Passing credentials inline + +For private repositories over HTTP(S), pass `username` and `password` (token) directly to commands that need authentication. A username is required whenever you pass a password/token. + + +```js JavaScript & TypeScript +const repoPath = '/home/user/repo' + +await sandbox.git.push(repoPath, { + username: process.env.GIT_USERNAME, + password: process.env.GIT_TOKEN, +}) + +await sandbox.git.pull(repoPath, { + username: process.env.GIT_USERNAME, + password: process.env.GIT_TOKEN, +}) +``` +```python Python +import os + +repo_path = "/home/user/repo" + +sandbox.git.push( + repo_path, + username=os.environ.get("GIT_USERNAME"), + password=os.environ.get("GIT_TOKEN"), +) + +sandbox.git.pull( + repo_path, + username=os.environ.get("GIT_USERNAME"), + password=os.environ.get("GIT_TOKEN"), +) +``` + + +#### Credential helper (authenticate once) + +To avoid passing credentials on each command, store them in the git credential helper inside the sandbox using `dangerouslyAuthenticate()` / `dangerously_authenticate()`. + + +Stores credentials on disk inside the sandbox. Any process or agent with access to the sandbox can read them. Use only when you understand the risk. + + + +```js JavaScript & TypeScript +// Default (GitHub) +await sandbox.git.dangerouslyAuthenticate({ + username: process.env.GIT_USERNAME, + password: process.env.GIT_TOKEN, +}) + +// Custom host (self-hosted) +await sandbox.git.dangerouslyAuthenticate({ + username: process.env.GIT_USERNAME, + password: process.env.GIT_TOKEN, + host: 'git.example.com', + protocol: 'https', +}) + +// After this, HTTPS git operations use the stored credentials +await sandbox.git.clone('https://git.example.com/org/repo.git', { path: '/home/user/repo' }) +await sandbox.git.push('/home/user/repo') +``` +```python Python +import os + +# Default (GitHub) +sandbox.git.dangerously_authenticate( + username=os.environ.get("GIT_USERNAME"), + password=os.environ.get("GIT_TOKEN"), +) + +# Custom host (self-hosted) +sandbox.git.dangerously_authenticate( + username=os.environ.get("GIT_USERNAME"), + password=os.environ.get("GIT_TOKEN"), + host="git.example.com", + protocol="https", +) + +# After this, HTTPS git operations use the stored credentials +sandbox.git.clone("https://git.example.com/org/repo.git", path="/home/user/repo") +sandbox.git.push("/home/user/repo") +``` + + +#### Keep credentials in the remote URL + +By default, credentials are stripped from the remote URL after cloning. To keep credentials in the remote URL (stored in `.git/config`), set `dangerouslyStoreCredentials` / `dangerously_store_credentials`. + + +Storing credentials in the remote URL persists them in the repo config. Any process or agent with access to the sandbox can read them. Only use this when required. + + + +```js JavaScript & TypeScript +// Default: credentials are stripped from the remote URL +await sandbox.git.clone('https://git.example.com/org/repo.git', { + path: '/home/user/repo', + username: process.env.GIT_USERNAME, + password: process.env.GIT_TOKEN, +}) + +// Keep credentials in the remote URL +await sandbox.git.clone('https://git.example.com/org/repo.git', { + path: '/home/user/repo', + username: process.env.GIT_USERNAME, + password: process.env.GIT_TOKEN, + dangerouslyStoreCredentials: true, +}) +``` +```python Python +import os + +# Default: credentials are stripped from the remote URL +sandbox.git.clone( + "https://git.example.com/org/repo.git", + path="/home/user/repo", + username=os.environ.get("GIT_USERNAME"), + password=os.environ.get("GIT_TOKEN"), +) + +# Keep credentials in the remote URL +sandbox.git.clone( + "https://git.example.com/org/repo.git", + path="/home/user/repo", + username=os.environ.get("GIT_USERNAME"), + password=os.environ.get("GIT_TOKEN"), + dangerously_store_credentials=True, +) +``` + + +#### Configure git identity + +Set the git author name and email for commits. Configure globally or per-repository. + + +```js JavaScript & TypeScript +const repoPath = '/home/user/repo' + +// Global config +await sandbox.git.configureUser('E2B Bot', 'bot@example.com') + +// Repo-local config +await sandbox.git.configureUser('E2B Bot', 'bot@example.com', { + scope: 'local', + path: repoPath +}) +``` +```python Python +repo_path = "/home/user/repo" + +# Global config +sandbox.git.configure_user("E2B Bot", "bot@example.com") + +# Repo-local config +sandbox.git.configure_user( + "E2B Bot", + "bot@example.com", + scope="local", + path=repo_path +) +``` + + +### Clone a repository + +See [Authentication & Identity](#authentication--identity) for how to authenticate with private repositories. + + +```js JavaScript & TypeScript +const repoUrl = 'https://git.example.com/org/repo.git' +const repoPath = '/home/user/repo' + +// Default clone +await sandbox.git.clone(repoUrl, { path: repoPath }) + +// Clone a specific branch +await sandbox.git.clone(repoUrl, { path: repoPath, branch: 'main' }) + +// Shallow clone +await sandbox.git.clone(repoUrl, { path: repoPath, depth: 1 }) +``` +```python Python +repo_url = "https://git.example.com/org/repo.git" +repo_path = "/home/user/repo" + +# Default clone +sandbox.git.clone(repo_url, path=repo_path) + +# Clone a specific branch +sandbox.git.clone(repo_url, path=repo_path, branch="main") + +# Shallow clone +sandbox.git.clone(repo_url, path=repo_path, depth=1) +``` + + +### Check status and branches + +`status()` returns a structured object with branch, ahead/behind, and file status details. `branches()` returns the branch list and the current branch. + + +```js JavaScript & TypeScript +const repoPath = '/home/user/repo' + +const status = await sandbox.git.status(repoPath) +console.log(status.currentBranch, status.ahead, status.behind) +console.log(status.fileStatus) + +const branches = await sandbox.git.branches(repoPath) +console.log(branches.currentBranch) +console.log(branches.branches) +``` +```python Python +repo_path = "/home/user/repo" + +status = sandbox.git.status(repo_path) +print(status.current_branch, status.ahead, status.behind) +print(status.file_status) + +branches = sandbox.git.branches(repo_path) +print(branches.current_branch) +print(branches.branches) +``` + + +### Create and manage branches + + +```js JavaScript & TypeScript +const repoPath = '/home/user/repo' + +// Create and switch to a new branch +await sandbox.git.createBranch(repoPath, 'feature/new-docs') + +// Check out an existing branch +await sandbox.git.checkoutBranch(repoPath, 'main') + +// Delete a branch +await sandbox.git.deleteBranch(repoPath, 'feature/old-docs') + +// Force delete a branch +await sandbox.git.deleteBranch(repoPath, 'feature/stale-docs', { force: true }) +``` +```python Python +repo_path = "/home/user/repo" + +# Create and switch to a new branch +sandbox.git.create_branch(repo_path, "feature/new-docs") + +# Check out an existing branch +sandbox.git.checkout_branch(repo_path, "main") + +# Delete a branch +sandbox.git.delete_branch(repo_path, "feature/old-docs") + +# Force delete a branch +sandbox.git.delete_branch(repo_path, "feature/stale-docs", force=True) +``` + + +### Stage and commit + + +```js JavaScript & TypeScript +const repoPath = '/home/user/repo' + +// Default: stage all changes, commit with repo config +await sandbox.git.add(repoPath) +await sandbox.git.commit(repoPath, 'Initial commit') + +// Stage specific files +await sandbox.git.add(repoPath, { files: ['README.md', 'src/index.ts'] }) + +// Allow empty commit and override author +await sandbox.git.commit(repoPath, 'Docs sync', { + authorName: 'E2B Bot', + authorEmail: 'bot@example.com', + allowEmpty: true, +}) +``` +```python Python +repo_path = "/home/user/repo" + +# Default: stage all changes, commit with repo config +sandbox.git.add(repo_path) +sandbox.git.commit(repo_path, "Initial commit") + +# Stage specific files +sandbox.git.add(repo_path, files=["README.md", "src/index.ts"]) + +# Allow empty commit and override author +sandbox.git.commit( + repo_path, + "Docs sync", + author_name="E2B Bot", + author_email="bot@example.com", + allow_empty=True, +) +``` + + +### Pull and push + +See [Authentication & Identity](#authentication--identity) for how to authenticate with private repositories. + + +```js JavaScript & TypeScript +const repoPath = '/home/user/repo' + +// Default (uses upstream when set) +await sandbox.git.push(repoPath) +await sandbox.git.pull(repoPath) + +// Target a specific remote/branch and set upstream +await sandbox.git.push(repoPath, { + remote: 'origin', + branch: 'main', + setUpstream: true, +}) + +await sandbox.git.pull(repoPath, { + remote: 'origin', + branch: 'main', +}) +``` +```python Python +repo_path = "/home/user/repo" + +# Default (uses upstream when set) +sandbox.git.push(repo_path) +sandbox.git.pull(repo_path) + +# Target a specific remote/branch and set upstream +sandbox.git.push( + repo_path, + remote="origin", + branch="main", + set_upstream=True, +) + +sandbox.git.pull( + repo_path, + remote="origin", + branch="main", +) +``` + + +### Manage remotes + + +```js JavaScript & TypeScript +const repoPath = '/home/user/repo' +const repoUrl = 'https://git.example.com/org/repo.git' + +// Default +await sandbox.git.remoteAdd(repoPath, 'origin', repoUrl) + +// Fetch after adding the remote +await sandbox.git.remoteAdd(repoPath, 'origin', repoUrl, { fetch: true }) + +// Overwrite the remote URL if it already exists +await sandbox.git.remoteAdd(repoPath, 'origin', repoUrl, { overwrite: true }) +``` +```python Python +repo_path = "/home/user/repo" +repo_url = "https://git.example.com/org/repo.git" + +# Default +sandbox.git.remote_add(repo_path, "origin", repo_url) + +# Fetch after adding the remote +sandbox.git.remote_add(repo_path, "origin", repo_url, fetch=True) + +# Overwrite the remote URL if it already exists +sandbox.git.remote_add(repo_path, "origin", repo_url, overwrite=True) +``` + + +### Git config + +Set and get git configuration values. See [Configure git identity](#configure-git-identity) for configuring the commit author. + + +```js JavaScript & TypeScript +const repoPath = '/home/user/repo' + +// Global config +await sandbox.git.setConfig('pull.rebase', 'false') +const rebase = await sandbox.git.getConfig('pull.rebase') + +// Repo-local config +await sandbox.git.setConfig('pull.rebase', 'false', { scope: 'local', path: repoPath }) +const localRebase = await sandbox.git.getConfig('pull.rebase', { scope: 'local', path: repoPath }) +``` +```python Python +repo_path = "/home/user/repo" + +# Global config +sandbox.git.set_config("pull.rebase", "false") +rebase = sandbox.git.get_config("pull.rebase") + +# Repo-local config +sandbox.git.set_config("pull.rebase", "false", scope="local", path=repo_path) +local_rebase = sandbox.git.get_config("pull.rebase", scope="local", path=repo_path) +``` +