From 2d7dd810d6b85779df678269bb5067e89cda66fd Mon Sep 17 00:00:00 2001 From: sarahsanders-docker Date: Mon, 20 Oct 2025 15:52:51 -0400 Subject: [PATCH 1/9] add github sonarqube guide --- .../guides/github-sonarqube-sandbox/_index.md | 51 + .../github-sonarqube-sandbox/customize.md | 88 ++ .../github-sonarqube-sandbox/troubleshoot.md | 63 ++ .../github-sonarqube-sandbox/workflow.md | 911 ++++++++++++++++++ 4 files changed, 1113 insertions(+) create mode 100644 content/guides/github-sonarqube-sandbox/_index.md create mode 100644 content/guides/github-sonarqube-sandbox/customize.md create mode 100644 content/guides/github-sonarqube-sandbox/troubleshoot.md create mode 100644 content/guides/github-sonarqube-sandbox/workflow.md diff --git a/content/guides/github-sonarqube-sandbox/_index.md b/content/guides/github-sonarqube-sandbox/_index.md new file mode 100644 index 000000000000..19547d0386be --- /dev/null +++ b/content/guides/github-sonarqube-sandbox/_index.md @@ -0,0 +1,51 @@ +--- +title: Automate code quality workflows with GitHub and SonarQube in E2B sandboxes +linkTitle: GitHub and SonarQube quality checks +summary: Build AI-powered code quality workflows using E2B sandboxes with Docker's MCP catalog to automate GitHub and SonarQube integration. +description: Learn how to create E2B sandboxes with MCP servers, analyze code quality with SonarQube, and generate quality-gated pull requests using GitHub—all through natural language interactions with Claude. +tags: [devops] +params: + featured: true + time: 40 minutes + image: + resource_links: + - title: E2B Documentation + url: https://e2b.dev/docs + - title: Docker MCP Catalog + url: https://hub.docker.com/mcp + - title: Sandboxes + url: https://docs.docker.com/ai/mcp-catalog-and-toolkit/sandboxes/ +--- + +This guide demonstrates how to build an AI-powered code quality workflow using +[E2B sandboxes](https://e2b.dev/docs) with Docker’s MCP catalog. You’ll create +a system that automatically analyzes code quality issues in GitHub repositories +using SonarQube, then generate pull requests with fixes. + +## What you'll build + +You'll build a Node.js script that spins up an E2B sandbox, connects GitHub +and SonarQube MCP servers, and uses Claude to analyze code quality and propose +improvements. + +## What you'll learn + +In this guide, you'll learn: + +- How to create E2B sandboxes with multiple MCP servers +- How to configure GitHub and SonarQube MCP servers for AI workflows +- How to use Claude CLI inside sandboxes to interact with external tools +- How to build automated code review workflows that create quality-gated +pull requests + +## Why use E2B sandboxes? + +Running this workflow in E2B sandboes provides several advantages over +local execution: + +- Security: AI-generated code runs in isolated containers, protecting your +local environment and credentials +- Zero setup: No need to install SonarQube, GitHub CLI, or manage dependencies +locally +- Scalability: Resource-intensive operations like code scanning run in the +cloud without consuming local resources diff --git a/content/guides/github-sonarqube-sandbox/customize.md b/content/guides/github-sonarqube-sandbox/customize.md new file mode 100644 index 000000000000..d1d408e295ee --- /dev/null +++ b/content/guides/github-sonarqube-sandbox/customize.md @@ -0,0 +1,88 @@ +--- +title: Customize a code quality check workflow +linkTitle: Customize workflow +summary: Adapt your GitHub and SonarQube workflow to focus on specific quality issues, integrate with CI/CD, and set custom thresholds. +description: Learn how to customize prompts for specific quality issues, filter by file patterns, set quality thresholds, and integrate your workflow with GitHub Actions for automated code quality checks. +weight: 20 +--- + +Now that you understand the basics of automating code quality workflows with +GitHub and SonarQube in E2B sandboxes, you can customize the workflow +for your needs. + +## Focus on specific quality issues + +Modify the prompt to prioritize certain issue types: + +```javascript +const prompt = `Using SonarQube and GitHub MCP tools: + +Focus only on: +- Security vulnerabilities (CRITICAL priority) +- Bugs (HIGH priority) +- Skip code smells for this iteration + +Analyze "${repoPath}" and fix the highest priority issues first.`; +``` + +## Integrate with CI/CD + +Add this workflow to GitHub actions to run automatically on pull requests: + +```yaml +name: Automated quality checks +on: + pull_request: + types: [opened, synchronize] + +jobs: + quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '18' + - run: npm install + - run: node 06-quality-gated-pr.js + env: + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} + GITHUB_OWNER: ${{ github.repository_owner }} + GITHUB_REPO: ${{ github.event.repository.name }} + SONARQUBE_ORG: your-org-key +``` + +## Filter by file patterns + +Target specific parts of your codebase: + +```javascript +const prompt = `Analyze code quality but only consider: +- Files in src/**/*.js +- Exclude test files (*.test.js, *.spec.js) +- Exclude build artifacts in dist/ + +Focus on production code only.`; +``` + +## Set quality thresholds + +Define when PRs should be created: + +```javascript +const prompt = `Quality gate thresholds: +- Only create PR if: + * Bug count decreases by at least 1 + * No new security vulnerabilities introduced + * Code coverage doesn't decrease + * Technical debt reduces by at least 15 minutes + +If changes don't meet these thresholds, explain why and skip PR creation.`; +``` + +## Next steps + +Learn how to troubleshoot common issues. diff --git a/content/guides/github-sonarqube-sandbox/troubleshoot.md b/content/guides/github-sonarqube-sandbox/troubleshoot.md new file mode 100644 index 000000000000..d0b4359e4e89 --- /dev/null +++ b/content/guides/github-sonarqube-sandbox/troubleshoot.md @@ -0,0 +1,63 @@ +--- +title: Troubleshoot code quality workflow issues +linkTitle: Troubleshooting +summary: Resolve common issues with E2B sandboxes, MCP server connections, and GitHub/SonarQube integration. +description: Solutions for MCP tools not loading, authentication errors, permission issues, workflow timeouts, and other common problems when building code quality workflows with E2B. +weight: 30 +--- + +This page covers common issues you might encounter when building code quality +workflows with E2B sandboxes and MCP servers, along with their solutions. + +If you're experiencing problems not covered here, check the +[E2B documentation](https://e2b.dev/docs). + +## MCP tools not available + +Issue: Claude reports `I don't have any MCP tools available`. + +Solution: + +1. Verify you're using the authorization header: + + ```plaintext + --header "Authorization: Bearer ${mcpToken}" + ``` + +2. Check you're waiting for MCP initialization: + + ```javascript + await new Promise(resolve => setTimeout(resolve, 1000)); + ``` + +3. Ensure credentials are in both `envs` and `mcp` configuration. +4. Verify your API tokens are valid and have proper scopes. + +## GitHub tools work but SonarQube doesn't + +Issue: GitHub MCP tools load but SonarQube tools don't appear. + +Solution: SonarQube MCP server requires GitHub to be configured simultaneously. +Always include both servers in your sandbox configuration, even if you're only +testing one. + +## Claude can’t access private repositories + +Issue: “I don’t have access to that repository”. + +Solution: + +1. Verify your GitHub token has `repo` scope (not just `public_repo`). +2. Test with a public repository first. +3. Ensure the repository owner and name are correct in your `.env`. + +## Workflow times out or runs too long + +Issue: Workflow doesn’t complete or Claude credits run out. + +Solutions: + +1. Use `timeoutMs: 0` for complex workflows to allow unlimited time. +2. Break complex workflows into smaller, focused tasks. +3. Monitor your Anthropic API credit usage. +4. Add checkpoints in prompts: “After each step, show progress before continuing”. \ No newline at end of file diff --git a/content/guides/github-sonarqube-sandbox/workflow.md b/content/guides/github-sonarqube-sandbox/workflow.md new file mode 100644 index 000000000000..5e708325c5d8 --- /dev/null +++ b/content/guides/github-sonarqube-sandbox/workflow.md @@ -0,0 +1,911 @@ +--- +title: Build a code quality check workflow +linkTitle: Build workflow +summary: Learn to use GitHub and SonarQube MCP servers in E2B sandboxes through progressive examples. +description: Create E2B sandboxes, discover MCP tools, test individual operations, and build complete quality-gated PR workflows. +weight: 10 +--- + +In this section, you'll build a complete code quality automation workflow +step-by-step. You'll start by creating an E2B sandbox with GitHub and +SonarQube MCP servers, then progressively add functionality until you have a +production-ready workflow that analyzes code quality and creates pull requests. + +By working through each step sequentially, you'll learn how MCP servers work, +how to interact with them through Claude, and how to chain operations together +to build powerful automation workflows. + +## Prerequisites + +Before you begin, make sure you have: + +- E2B account with [API access](https://e2b.dev/docs/api-key) +- [Anthropic API key](https://docs.claude.com/en/api/admin-api/apikeys/get-api-key) +- GitHub account with: + - A repository containing code to analyze + - [Personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with `repo` scope +- SonarCloud account with: + - [Organization](https://docs.sonarsource.com/sonarqube-cloud/administering-sonarcloud/resources-structure/organization) created + - [Project configured](https://docs.sonarsource.com/sonarqube-community-build/project-administration/creating-and-importing-projects) for your repository + - [User token](https://docs.sonarsource.com/sonarqube-server/instance-administration/security/administering-tokens) generated +- [Node.js 18+](https://nodejs.org/en/download) installed on your machine + +> [!NOTE] +> +> This guide uses Claude's `--dangerously-skip-permissions` flag to enable +> automated command execution in E2B sandboxes. This flag bypasses permission +> prompts, which is appropriate for isolated container environments like E2B +> where sandboxes are disposable and separate from your local machine. +> +> However, be aware that Claude can execute any commands within the sandbox, +> including accessing files and credentials available in that environment. Only +> use this approach with trusted code and workflows. For more information, +> see [Anthropic's guidance on container security](https://docs.anthropic.com/en/docs/claude-code/devcontainer). + +## Step 1: Set up your project + +1. Create a new directory for your workflow and initialize Node.js: + +```bash +mkdir github-sonarqube-workflow +cd github-sonarqube-workflow +npm init -y +``` + +2. Open `package.json` and configure it for ES modules: + +```json +{ + "name": "github-sonarqube-workflow", + "version": "1.0.0", + "description": "Automated code quality workflow using E2B, GitHub, and SonarQube", + "type": "module", + "main": "quality-workflow.js", + "scripts": { + "start": "node quality-workflow.js" + }, + "keywords": ["e2b", "github", "sonarqube", "mcp", "code-quality"], + "author": "", + "license": "MIT" +} +``` + +3. Install required dependencies: + +```bash +npm install e2b dotenv +``` + +4. Create a `.env` file in your project root: + +```bash +touch .env +``` + +5. Add your API keys and configuration, replacing the placeholders with your + actual credentials: + +```plaintext +E2B_API_KEY=your_e2b_api_key_here +ANTHROPIC_API_KEY=your_anthropic_api_key_here +GITHUB_TOKEN=ghp_your_personal_access_token_here +GITHUB_OWNER=your_github_username +GITHUB_REPO=your_repository_name +SONARQUBE_ORG=your_sonarcloud_org_key +SONARQUBE_TOKEN=your_sonarqube_user_token +SONARQUBE_URL=https://sonarcloud.io +``` + +6. Protect your credentials by adding `.env` to `.gitignore`: + +```bash +echo ".env" >> .gitignore +echo "node_modules/" >> .gitignore +``` + +## Step 2: Create your first sandbox + +Let's start by creating a sandbox and verifying the MCP servers are configured +correctly. + +Create a file named `01-test-connection.js` in your project root: + +```javascript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function testConnection() { + console.log( + "Creating E2B sandbox with GitHub and SonarQube MCP servers...\n", + ); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, + GITHUB_TOKEN: process.env.GITHUB_TOKEN, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG, + token: process.env.SONARQUBE_TOKEN, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + console.log(" Sandbox created successfully!"); + console.log(`MCP Gateway URL: ${mcpUrl}\n`); + + // Wait for MCP initialization + await new Promise((resolve) => setTimeout(resolve, 1000)); + + // Configure Claude to use the MCP gateway + console.log("Connecting Claude CLI to MCP gateway..."); + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + console.log("\n Connection successful! Cleaning up..."); + await sbx.kill(); +} + +testConnection().catch(console.error); +``` + +Run this script to verify your setup: + +```bash +node 01-test-connection.js +``` + +Your output should look similar to the following example: + +```console +Creating E2B sandbox with GitHub and SonarQube MCP servers... + +Sandbox created successfully! +MCP Gateway URL: https://50005-xxxxx.e2b.app/mcp + +Connecting Claude CLI to MCP gateway... +Added HTTP MCP server e2b-mcp-gateway with URL: https://50005-xxxxx.e2b.app/mcp to local config +Headers: { + "Authorization": "Bearer xxxxx-xxxx-xxxx" +} +File modified: /home/user/.claude.json [project: /home/user] + +Connection successful! Cleaning up... +``` + +You've just learned how to create an E2B sandbox with multiple MCP servers +configured. The `betaCreate` method initializes a cloud environment +with Claude CLI and your specified MCP servers. + +## Step 3: Discover available MCP tools + +MCP servers expose tools that Claude can call. The GitHub MCP server provides +repository management tools, while SonarQube provides code analysis tools. +By listing their tools, you know what operations are possible. + +To try listing MCP tools, create `02-list-tools.js`: + +```javascript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function listTools() { + console.log("Creating sandbox...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, + GITHUB_TOKEN: process.env.GITHUB_TOKEN, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG, + token: process.env.SONARQUBE_TOKEN, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + // Wait for MCP initialization + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + console.log("\nDiscovering available MCP tools...\n"); + + const prompt = + "List all MCP tools you have access to. For each tool, show its exact name and a brief description."; + + await sbx.commands.run( + `echo '${prompt}' | claude -p --dangerously-skip-permissions`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + await sbx.kill(); +} + +listTools().catch(console.error); +``` + +Run the script: + +```bash +node 02-list-tools.js +``` + +In the console, you should see a list of MCP tools: + +```console +Creating sandbox... + +Sandbox created +Connecting to MCP gateway... + +Discovering available MCP tools... + +I have access to the following MCP tools: + +**GitHub Tools:** +1. mcp__create_repository - Create a new GitHub repository +2. mcp__list_issues - List issues in a repository +3. mcp__create_issue - Create a new issue +4. mcp__get_file_contents - Get file contents from a repository +5. mcp__create_or_update_file - Create or update files in a repository +6. mcp__create_pull_request - Create a pull request +7. mcp__create_branch - Create a new branch +8. mcp__push_files - Push multiple files in a single commit +... (30+ more GitHub tools) + +**SonarQube Tools:** +1. mcp__get_projects - List projects in organization +2. mcp__get_quality_gate_status - Get quality gate status for a project +3. mcp__list_project_issues - List quality issues in a project +4. mcp__search_issues - Search for specific quality issues +... (SonarQube analysis tools) +``` + +## Step 4: Test GitHub MCP tools + +Let's try testing GitHub using MCP tools. We'll start simple by listing +repository issues. + +Create `03-test-github.js`: + +```javascript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function testGitHub() { + console.log("Creating sandbox...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, + GITHUB_TOKEN: process.env.GITHUB_TOKEN, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN, + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; + + console.log(`\nListing issues in ${repoPath}...\n`); + + const prompt = `Using the GitHub MCP tools, list all open issues in the repository "${repoPath}". Show the issue number, title, and author for each.`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + await sbx.kill(); +} + +testGitHub().catch(console.error); +``` + +Run the script: + +```bash +node 03-test-github.js +``` + +You should see Claude use the GitHub MCP tools to list your repository's issues: + +```console +Creating sandbox... +Connecting to MCP gateway... + +Listing issues in ... + +Here are the first 10 open issues in the repository: + +1. **Issue #23577**: Update README (author: user1) +2. **Issue #23575**: release-notes for Compose v2.40.1 version (author: user2) +3. **Issue #23570**: engine-cli: fix `docker volume prune` output (author: user3) +4. **Issue #23568**: Engdocs update (author: user4) +5. **Issue #23565**: add new section (author: user5) +... (continues with more issues) +``` + +You can now send prompts to Claude and interact with GitHub through +natural language. Claude decides what tool to call based on your prompt. + +## Step 5: Test SonarQube MCP tools + +Let's analyze code quality using SonarQube MCP tools. + +Create `04-test-sonarqube.js`: + +```javascript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function testSonarQube() { + console.log("Creating sandbox...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, + GITHUB_TOKEN: process.env.GITHUB_TOKEN, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG, + token: process.env.SONARQUBE_TOKEN, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + console.log("\nAnalyzing code quality with SonarQube...\n"); + + const prompt = `Using the SonarQube MCP tools: + 1. List all projects in my organization + 2. For the first project, show: + - Quality gate status (pass/fail) + - Number of bugs + - Number of code smells + - Number of security vulnerabilities + 3. List the top 5 most critical issues found`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + await sbx.kill(); +} + +testSonarQube().catch(console.error); +``` + +Run the script: + +```bash +node 04-test-sonarqube.js +``` + +> [!NOTE] +> +> This script may take a few minutes to run. + +You should see Claude output SonarQube analysis results: + +```console +Creating sandbox... + +Analyzing code quality with SonarQube... + +## SonarQube Analysis Results + +### 1. Projects in Your Organization + +Found **1 project**: +- **Project Name**: project-1 +- **Project Key**: project-testing + +### 2. Project Analysis + +... + +### 3. Top 5 Most Critical Issues + +Found 1 total issues (all are code smells with no critical/blocker severity): + +1. **MAJOR Severity** - test.js:2 + - **Rule**: javascript:S1854 + - **Message**: Remove this useless assignment to variable "unusedVariable" + - **Status**: OPEN + +**Summary**: The project is in good health with no bugs or vulnerabilities detected. +``` + +You can now use SonarQube MCP tools to analyze code quality through +natural language. You can retrieve quality metrics, identify issues, +and understand what code needs fixing. + +## Step 6: Create a branch and make code changes + +Now, let's teach Claude to fix code based on quality issues discovered +by SonarQube. + +Create `05-fix-code-issue.js`: + +```javascript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function fixCodeIssue() { + console.log("Creating sandbox...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, + GITHUB_TOKEN: process.env.GITHUB_TOKEN, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG, + token: process.env.SONARQUBE_TOKEN, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; + const branchName = `quality-fix-${Date.now()}`; + + console.log("\nFixing a code quality issue...\n"); + + const prompt = `Using GitHub and SonarQube MCP tools: + + 1. Analyze code quality in repository "${repoPath}" with SonarQube + 2. Find ONE simple issue that can be confidently fixed (like an unused variable or code smell) + 3. Create a new branch called "${branchName}" + 4. Read the file containing the issue using GitHub tools + 5. Fix the issue in the code + 6. Commit the fix to the new branch with a clear commit message + + Important: Only fix issues you're 100% confident about. Explain what you're fixing and why.`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + console.log(`\n Check your repository for branch: ${branchName}`); + + await sbx.kill(); +} + +fixCodeIssue().catch(console.error); +``` + +Run the script: + +```bash +node 05-fix-code-issue.js +``` + +> [!NOTE] +> +> This script may take a few minutes to run. + +Claude will analyze your repository and fix a code quality issue: + +```console +Creating sandbox... + +Fixing a code quality issue... + +## Task Completed Successfully! + +I've successfully analyzed the repository and fixed a code quality issue. Here's what I did: + +### 1. Analysis +Found 1 SonarQube issues in the `` repository: +- **Unused variable** (MINOR severity, line 2) +- Useless assignment (MAJOR severity, line 2) +- For-of loop suggestion (MINOR severity, lines 5-7) + +### 2. Issue Fixed +**Location:** test.js:2 +**Issue:** Unused variable declaration +**SonarQube Rule:** javascript:S1481 +**What was wrong:** The line `const unusedVariable = "this is not used";` declared a variable that was never used anywhere in the function. + +### 3. Why This Fix is Safe +- The variable was only declared and assigned, never referenced +- Removing it has zero impact on functionality +- It eliminates dead code and improves code quality +- This is a textbook example of a code smell that should be removed + +### 4. Changes Made +- Created branch: `quality-fix-xxxxx` +- Removed the unused variable declaration +- Committed with a clear message explaining the fix and referencing the SonarQube rule + +**Commit SHA:** `xxxxxxxxxxxxxxxxxxxxxxxxx` +**Branch:** https://github.com///tree/quality-fix-xxxxx + +The fix is ready for review and can be merged to improve the code quality of the repository! +``` + +You can now use GitHub and SonarQube MCP tools in the same workflow to read +files, make code changes, and commit them. + +## Step 7: Create quality-gated pull requests + +Finally, let's build the complete workflow: analyze quality, fix issues, +and create a PR only if improvements are made. + +Create `06-quality-gated-pr.js`: + +```javascript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function qualityGatedPR() { + console.log("Creating sandbox for quality-gated PR workflow...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, + GITHUB_TOKEN: process.env.GITHUB_TOKEN, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG, + token: process.env.SONARQUBE_TOKEN, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; + const branchName = `quality-improvements-${Date.now()}`; + + console.log("\nRunning quality-gated PR workflow...\n"); + + const prompt = `You are a code quality engineer. Using GitHub and SonarQube MCP tools: + + STEP 1: ANALYSIS + - Get current code quality status from SonarQube for "${repoPath}" + - Record the current number of bugs, code smells, and vulnerabilities + - Identify 1-3 issues that you can confidently fix + + STEP 2: FIX ISSUES + - Create branch "${branchName}" + - For each issue you're fixing: + * Read the file with the issue + * Make the fix + * Commit with a descriptive message + - Only fix issues where you're 100% confident the fix is correct + + STEP 3: VERIFICATION + - After your fixes, check if quality metrics would improve + - Calculate: Would this reduce bugs/smells/vulnerabilities? + + STEP 4: QUALITY GATE + - Only proceed if your changes improve quality + - If quality would not improve, explain why and stop + + STEP 5: CREATE PR (only if quality gate passes) + - Create a pull request from "${branchName}" to main + - Title: "Quality improvements: [describe what you fixed]" + - Description should include: + * What issues you fixed + * Before/after quality metrics + * Why these fixes improve code quality + - Add a comment with detailed SonarQube analysis + + Be thorough and explain your decisions at each step.`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + console.log(`\n Workflow complete! Check ${repoPath} for new pull request.`); + + await sbx.kill(); +} + +qualityGatedPR().catch(console.error); +``` + +Run the script: + +```bash +node 06-quality-gated-pr.js +``` + +> [!NOTE] +> +> This script may take a few minutes to run. + +Claude will run the entire workflow, creating a quality improvement +and opening a PR in GitHub: + +```console +Creating sandbox for quality-gated PR workflow... + +Running quality-gated PR workflow... + +## Complete - Code Quality Improvement Successfully Executed + +### Summary of Work Completed + +**Pull Request Created:** https://github.com///pull/1 + +--- + +### STEP 1: ANALYSIS + +**Current State:** +- **Bugs:** 0 +- **Vulnerabilities:** 0 +- **Code Smells:** 1 +- **Lines of Code:** 12 +- **All Ratings:** A (Reliability, Security, Maintainability) + +**Issues Identified (all in test.js):** +1. xxxxxxxxx - Unused variable declaration (MINOR) + +--- + +### STEP 2: FIX ISSUES + +**Branch Created:** `quality-improvements-xxxxxxxx` + +**Changes Made:** +1. **Removed unused variable** (line 2) - Eliminated dead code that served no purpose +2. **Modernized loop pattern** (lines 5-7) - Converted `for (let i = 0; i < items.length; i++)` to `for (const item of items)` + +**Commit:** xxxxxxxxxx + +--- + +### STEP 3: VERIFICATION + +**Expected Impact:** +- Code Smells: 1 → 0 (100% reduction) +- Bugs: 0 → 0 (maintained) +- Vulnerabilities: 0 → 0 (maintained) +- All quality ratings maintained at A + +--- + +### STEP 4: QUALITY GATE PASSED + +**Decision Criteria Met:** +- ✅ Reduces code smells by 100% +- ✅ No new bugs or vulnerabilities introduced +- ✅ Code is more readable and maintainable +- ✅ Follows modern JavaScript best practices +- ✅ All fixes are low-risk refactorings with no behavioral changes + +--- + +### STEP 5: CREATE PR + +**Pull Request Details:** +- **Number:** #1 +- **Title:** Quality improvements: Remove unused variable and modernize for loop +- **Branch:** quality-improvements-xxxxxxxx → main +- **URL:** https://github.com//pull/1 + +**PR Includes:** +- Comprehensive description with before/after metrics +- Detailed SonarQube analysis comment with issue breakdown +- Code comparison showing improvements +- Quality metrics table + +The pull request is now ready for review and merge! +``` + +You've now built a complete, multi-step workflow with conditional logic. +Claude analyzes quality with SonarQube, makes fixes using GitHub tools, +verifies improvements, and only creates a PR if quality actually improves. + +## Step 8: Add error handling + +Production workflows need error handling. Let's make our workflow more robust. + +Create `07-robust-workflow.js`: + +```javascript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function robustWorkflow() { + let sbx; + + try { + console.log("Creating sandbox...\n"); + + sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, + GITHUB_TOKEN: process.env.GITHUB_TOKEN, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG, + token: process.env.SONARQUBE_TOKEN, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; + + console.log("\nRunning workflow with error handling...\n"); + + const prompt = `Run a quality improvement workflow for "${repoPath}". + + ERROR HANDLING RULES: + 1. If SonarQube is unreachable, explain the error and stop gracefully + 2. If GitHub API fails, retry once, then explain and stop + 3. If no fixable issues are found, explain why and exit (this is not an error) + 4. If file modifications fail, explain which file and why + 5. At each step, check for errors before proceeding + + Run the workflow and handle any errors you encounter professionally.`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + console.log("\n Workflow completed"); + } catch (error) { + console.error("\n Workflow failed:", error.message); + + if (error.message.includes("403")) { + console.error("\n Check your E2B account has MCP gateway access"); + } else if (error.message.includes("401")) { + console.error("\n Check your API tokens are valid"); + } else if (error.message.includes("Credit balance")) { + console.error("\n Check your Anthropic API credit balance"); + } + + process.exit(1); + } finally { + if (sbx) { + console.log("\n Cleaning up sandbox..."); + await sbx.kill(); + } + } +} + +robustWorkflow().catch(console.error); +``` + +Run the script: + +```bash +node 07-robust-workflow.js +``` + +Claude will run the entire workflow, and if it encounters an error, respond +with robust error messaging. + +## Next steps + +In the next section, you'll customize your workflow for your needs. From 8565c7248bf7c3c9691b3713238e70a7353fed1d Mon Sep 17 00:00:00 2001 From: sarahsanders-docker Date: Tue, 21 Oct 2025 10:28:41 -0400 Subject: [PATCH 2/9] update title --- content/guides/github-sonarqube-sandbox/_index.md | 4 ++-- content/guides/github-sonarqube-sandbox/troubleshoot.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/guides/github-sonarqube-sandbox/_index.md b/content/guides/github-sonarqube-sandbox/_index.md index 19547d0386be..0acfc0a35ac5 100644 --- a/content/guides/github-sonarqube-sandbox/_index.md +++ b/content/guides/github-sonarqube-sandbox/_index.md @@ -1,6 +1,6 @@ --- -title: Automate code quality workflows with GitHub and SonarQube in E2B sandboxes -linkTitle: GitHub and SonarQube quality checks +title: How to build an AI-powered code quality workflow with SonarQube and E2B +linkTitle: Build an AI-powered code quality workflow summary: Build AI-powered code quality workflows using E2B sandboxes with Docker's MCP catalog to automate GitHub and SonarQube integration. description: Learn how to create E2B sandboxes with MCP servers, analyze code quality with SonarQube, and generate quality-gated pull requests using GitHub—all through natural language interactions with Claude. tags: [devops] diff --git a/content/guides/github-sonarqube-sandbox/troubleshoot.md b/content/guides/github-sonarqube-sandbox/troubleshoot.md index d0b4359e4e89..2f594e54f1b8 100644 --- a/content/guides/github-sonarqube-sandbox/troubleshoot.md +++ b/content/guides/github-sonarqube-sandbox/troubleshoot.md @@ -1,5 +1,5 @@ --- -title: Troubleshoot code quality workflow issues +title: Troubleshoot code quality workflows linkTitle: Troubleshooting summary: Resolve common issues with E2B sandboxes, MCP server connections, and GitHub/SonarQube integration. description: Solutions for MCP tools not loading, authentication errors, permission issues, workflow timeouts, and other common problems when building code quality workflows with E2B. From f34dc58277a5fa422663f881462ca8eaa5cb57f6 Mon Sep 17 00:00:00 2001 From: sarahsanders-docker Date: Tue, 21 Oct 2025 13:56:38 -0400 Subject: [PATCH 3/9] add callout about claude --- content/guides/github-sonarqube-sandbox/workflow.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/guides/github-sonarqube-sandbox/workflow.md b/content/guides/github-sonarqube-sandbox/workflow.md index 5e708325c5d8..efcea464e7d5 100644 --- a/content/guides/github-sonarqube-sandbox/workflow.md +++ b/content/guides/github-sonarqube-sandbox/workflow.md @@ -21,6 +21,11 @@ Before you begin, make sure you have: - E2B account with [API access](https://e2b.dev/docs/api-key) - [Anthropic API key](https://docs.claude.com/en/api/admin-api/apikeys/get-api-key) + + > [!NOTE] + > + > This example uses Claude CLI which comes pre-installed in E2B sandboxes, but you can adapt the example to work with other AI assistants of your choice. See [E2B's MCP documentation](https://e2b.dev/docs/mcp/quickstart) for alternative connection methods. + - GitHub account with: - A repository containing code to analyze - [Personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with `repo` scope From c22ee8c234c0af477f4b0a1c04f65c53a74417c8 Mon Sep 17 00:00:00 2001 From: sarahsanders-docker Date: Tue, 21 Oct 2025 16:40:51 -0400 Subject: [PATCH 4/9] typescript and python examples --- .../github-sonarqube-sandbox/customize.md | 109 ++- .../github-sonarqube-sandbox/troubleshoot.md | 315 ++++++- .../github-sonarqube-sandbox/workflow.md | 851 ++++++++++++++++-- hugo_stats.json | 5 + 4 files changed, 1169 insertions(+), 111 deletions(-) diff --git a/content/guides/github-sonarqube-sandbox/customize.md b/content/guides/github-sonarqube-sandbox/customize.md index d1d408e295ee..61b22c213434 100644 --- a/content/guides/github-sonarqube-sandbox/customize.md +++ b/content/guides/github-sonarqube-sandbox/customize.md @@ -14,7 +14,10 @@ for your needs. Modify the prompt to prioritize certain issue types: -```javascript +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript const prompt = `Using SonarQube and GitHub MCP tools: Focus only on: @@ -25,9 +28,29 @@ Focus only on: Analyze "${repoPath}" and fix the highest priority issues first.`; ``` +{{< /tab >}} +{{< tab name="Python" >}} + +```python +prompt = f"""Using SonarQube and GitHub MCP tools: + +Focus only on: +- Security vulnerabilities (CRITICAL priority) +- Bugs (HIGH priority) +- Skip code smells for this iteration + +Analyze "{repo_path}" and fix the highest priority issues first.""" +``` + +{{< /tab >}} +{{< /tabs >}} + ## Integrate with CI/CD -Add this workflow to GitHub actions to run automatically on pull requests: +Add this workflow to GitHub Actions to run automatically on pull requests: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} ```yaml name: Automated quality checks @@ -42,9 +65,9 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: '18' + node-version: "18" - run: npm install - - run: node 06-quality-gated-pr.js + - run: npx tsx 06-quality-gated-pr.ts env: E2B_API_KEY: ${{ secrets.E2B_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} @@ -55,11 +78,46 @@ jobs: SONARQUBE_ORG: your-org-key ``` +{{< /tab >}} +{{< tab name="Python" >}} + +```yaml +name: Automated quality checks +on: + pull_request: + types: [opened, synchronize] + +jobs: + quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.8" + - run: pip install e2b python-dotenv + - run: python 06_quality_gated_pr.py + env: + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} + GITHUB_OWNER: ${{ github.repository_owner }} + GITHUB_REPO: ${{ github.event.repository.name }} + SONARQUBE_ORG: your-org-key +``` + +{{< /tab >}} +{{< /tabs >}} + ## Filter by file patterns Target specific parts of your codebase: -```javascript +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript const prompt = `Analyze code quality but only consider: - Files in src/**/*.js - Exclude test files (*.test.js, *.spec.js) @@ -68,21 +126,56 @@ const prompt = `Analyze code quality but only consider: Focus on production code only.`; ``` +{{< /tab >}} +{{< tab name="Python" >}} + +```python +prompt = """Analyze code quality but only consider: +- Files in src/**/*.js +- Exclude test files (*.test.js, *.spec.js) +- Exclude build artifacts in dist/ + +Focus on production code only.""" +``` + +{{< /tab >}} +{{< /tabs >}} + ## Set quality thresholds Define when PRs should be created: -```javascript +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript const prompt = `Quality gate thresholds: - Only create PR if: * Bug count decreases by at least 1 * No new security vulnerabilities introduced - * Code coverage doesn't decrease + * Code coverage does not decrease * Technical debt reduces by at least 15 minutes -If changes don't meet these thresholds, explain why and skip PR creation.`; +If changes do not meet these thresholds, explain why and skip PR creation.`; ``` +{{< /tab >}} +{{< tab name="Python" >}} + +```python +prompt = """Quality gate thresholds: +- Only create PR if: + * Bug count decreases by at least 1 + * No new security vulnerabilities introduced + * Code coverage does not decrease + * Technical debt reduces by at least 15 minutes + +If changes do not meet these thresholds, explain why and skip PR creation.""" +``` + +{{< /tab >}} +{{< /tabs >}} + ## Next steps Learn how to troubleshoot common issues. diff --git a/content/guides/github-sonarqube-sandbox/troubleshoot.md b/content/guides/github-sonarqube-sandbox/troubleshoot.md index 2f594e54f1b8..53629f4f884d 100644 --- a/content/guides/github-sonarqube-sandbox/troubleshoot.md +++ b/content/guides/github-sonarqube-sandbox/troubleshoot.md @@ -20,17 +20,80 @@ Solution: 1. Verify you're using the authorization header: - ```plaintext - --header "Authorization: Bearer ${mcpToken}" - ``` +```plaintext +--header "Authorization: Bearer ${mcpToken}" +``` 2. Check you're waiting for MCP initialization: - ```javascript - await new Promise(resolve => setTimeout(resolve, 1000)); - ``` +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +await new Promise((resolve) => setTimeout(resolve, 1000)); +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +await asyncio.sleep(1) +``` + +{{< /tab >}} +{{< /tabs >}} + +3. Ensure credentials are in both `envs` and `mcp` configuration: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, +}); +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, +) +``` + +{{< /tab >}} +{{< /tabs >}} -3. Ensure credentials are in both `envs` and `mcp` configuration. 4. Verify your API tokens are valid and have proper scopes. ## GitHub tools work but SonarQube doesn't @@ -41,23 +104,249 @@ Solution: SonarQube MCP server requires GitHub to be configured simultaneously. Always include both servers in your sandbox configuration, even if you're only testing one. -## Claude can’t access private repositories +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +// Include both servers even if only using one +const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, +}); +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +# Include both servers even if only using one +sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, +) +``` -Issue: “I don’t have access to that repository”. +{{< /tab >}} +{{< /tabs >}} + +## Claude can't access private repositories + +Issue: "I don't have access to that repository". Solution: 1. Verify your GitHub token has `repo` scope (not just `public_repo`). 2. Test with a public repository first. -3. Ensure the repository owner and name are correct in your `.env`. +3. Ensure the repository owner and name are correct in your `.env`: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```plaintext +GITHUB_OWNER=your_github_username +GITHUB_REPO=your_repository_name +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```plaintext +GITHUB_OWNER=your_github_username +GITHUB_REPO=your_repository_name +``` + +{{< /tab >}} +{{< /tabs >}} ## Workflow times out or runs too long -Issue: Workflow doesn’t complete or Claude credits run out. +Issue: Workflow doesn't complete or Claude credits run out. Solutions: -1. Use `timeoutMs: 0` for complex workflows to allow unlimited time. +1. Use `timeoutMs: 0` (TypeScript) or `timeout_ms=0` (Python) for complex workflows to allow unlimited time: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +await sbx.commands.run( + `echo '${prompt}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, // No timeout + onStdout: console.log, + onStderr: console.log, + }, +); +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout_ms=0, # No timeout + on_stdout=print, + on_stderr=print, +) +``` + +{{< /tab >}} +{{< /tabs >}} + 2. Break complex workflows into smaller, focused tasks. 3. Monitor your Anthropic API credit usage. -4. Add checkpoints in prompts: “After each step, show progress before continuing”. \ No newline at end of file +4. Add checkpoints in prompts: "After each step, show progress before continuing". + +## Sandbox cleanup errors + +Issue: Sandboxes aren't being cleaned up properly, leading to resource exhaustion. + +Solution: Always use proper error handling with cleanup in the `finally` block: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +async function robustWorkflow() { + let sbx: Sandbox | undefined; + + try { + sbx = await Sandbox.betaCreate({ + // ... configuration + }); + + // ... workflow logic + } catch (error) { + console.error("Workflow failed:", error); + process.exit(1); + } finally { + if (sbx) { + console.log("Cleaning up sandbox..."); + await sbx.kill(); + } + } +} +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +async def robust_workflow(): + sbx = None + + try: + sbx = await AsyncSandbox.beta_create( + # ... configuration + ) + + # ... workflow logic + + except Exception as error: + print(f"Workflow failed: {error}") + sys.exit(1) + finally: + if sbx: + print("Cleaning up sandbox...") + await sbx.kill() +``` + +{{< /tab >}} +{{< /tabs >}} + +## Environment variable not loading + +Issue: Script fails with "undefined" or "None" for environment variables. + +Solution: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +1. Ensure `dotenv` is loaded at the top of your file: + +```typescript +import "dotenv/config"; +``` + +2. Verify the `.env` file is in the same directory as your script. + +3. Check variable names match exactly (case-sensitive): + +```typescript +// .env file +GITHUB_TOKEN = ghp_xxxxx; + +// In code +process.env.GITHUB_TOKEN; // Correct +process.env.github_token; // Wrong - case doesn't match +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +1. Ensure `dotenv` is loaded at the top of your file: + +```python +from dotenv import load_dotenv +load_dotenv() +``` + +2. Verify the `.env` file is in the same directory as your script. + +3. Check variable names match exactly (case-sensitive): + +```python +# .env file +GITHUB_TOKEN=ghp_xxxxx + +# In code +os.getenv("GITHUB_TOKEN") # ✓ Correct +os.getenv("github_token") # ✗ Wrong - case doesn't match +``` + +{{< /tab >}} +{{< /tabs >}} + +## SonarQube returns empty results + +Issue: SonarQube analysis returns no projects or issues. + +Solution: + +1. Verify your SonarCloud organization key is correct. +2. Ensure you have at least one project configured in SonarCloud. +3. Check that your SonarQube token has the necessary permissions. +4. Confirm your project has been analyzed at least once in SonarCloud. + +## Next steps + +Explore additional resources for E2B and MCP development. diff --git a/content/guides/github-sonarqube-sandbox/workflow.md b/content/guides/github-sonarqube-sandbox/workflow.md index efcea464e7d5..07c211f53327 100644 --- a/content/guides/github-sonarqube-sandbox/workflow.md +++ b/content/guides/github-sonarqube-sandbox/workflow.md @@ -22,9 +22,9 @@ Before you begin, make sure you have: - E2B account with [API access](https://e2b.dev/docs/api-key) - [Anthropic API key](https://docs.claude.com/en/api/admin-api/apikeys/get-api-key) - > [!NOTE] - > - > This example uses Claude CLI which comes pre-installed in E2B sandboxes, but you can adapt the example to work with other AI assistants of your choice. See [E2B's MCP documentation](https://e2b.dev/docs/mcp/quickstart) for alternative connection methods. + > [!NOTE] + > + > This example uses Claude CLI which comes pre-installed in E2B sandboxes, but you can adapt the example to work with other AI assistants of your choice. See [E2B's MCP documentation](https://e2b.dev/docs/mcp/quickstart) for alternative connection methods. - GitHub account with: - A repository containing code to analyze @@ -33,7 +33,9 @@ Before you begin, make sure you have: - [Organization](https://docs.sonarsource.com/sonarqube-cloud/administering-sonarcloud/resources-structure/organization) created - [Project configured](https://docs.sonarsource.com/sonarqube-community-build/project-administration/creating-and-importing-projects) for your repository - [User token](https://docs.sonarsource.com/sonarqube-server/instance-administration/security/administering-tokens) generated -- [Node.js 18+](https://nodejs.org/en/download) installed on your machine +- Language runtime installed: + - TypeScript: [Node.js 18+](https://nodejs.org/en/download) + - Python: [Python 3.8+](https://www.python.org/downloads/) > [!NOTE] > @@ -49,7 +51,10 @@ Before you begin, make sure you have: ## Step 1: Set up your project -1. Create a new directory for your workflow and initialize Node.js: +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +1. Create a new directory for your workflow and initialize Node.js: ```bash mkdir github-sonarqube-workflow @@ -57,7 +62,7 @@ cd github-sonarqube-workflow npm init -y ``` -2. Open `package.json` and configure it for ES modules: +2. Open `package.json` and configure it for ES modules: ```json { @@ -65,9 +70,9 @@ npm init -y "version": "1.0.0", "description": "Automated code quality workflow using E2B, GitHub, and SonarQube", "type": "module", - "main": "quality-workflow.js", + "main": "quality-workflow.ts", "scripts": { - "start": "node quality-workflow.js" + "start": "tsx quality-workflow.ts" }, "keywords": ["e2b", "github", "sonarqube", "mcp", "code-quality"], "author": "", @@ -75,20 +80,20 @@ npm init -y } ``` -3. Install required dependencies: +3. Install required dependencies: ```bash npm install e2b dotenv +npm install -D typescript tsx @types/node ``` -4. Create a `.env` file in your project root: +4. Create a `.env` file in your project root: ```bash touch .env ``` -5. Add your API keys and configuration, replacing the placeholders with your - actual credentials: +5. Add your API keys and configuration, replacing the placeholders with your actual credentials: ```plaintext E2B_API_KEY=your_e2b_api_key_here @@ -101,21 +106,76 @@ SONARQUBE_TOKEN=your_sonarqube_user_token SONARQUBE_URL=https://sonarcloud.io ``` -6. Protect your credentials by adding `.env` to `.gitignore`: +6. Protect your credentials by adding `.env` to `.gitignore`: ```bash echo ".env" >> .gitignore echo "node_modules/" >> .gitignore ``` +{{< /tab >}} +{{< tab name="Python" >}} + +1. Create a new directory for your workflow: + +```bash +mkdir github-sonarqube-workflow +cd github-sonarqube-workflow +``` + +2. Create a virtual environment and activate it: + +```bash +python3 -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +3. Install required dependencies: + +```bash +pip install e2b python-dotenv +``` + +4. Create a `.env` file in your project root: + +```bash +touch .env +``` + +5. Add your API keys and configuration, replacing the placeholders with your actual credentials: + +```plaintext +E2B_API_KEY=your_e2b_api_key_here +ANTHROPIC_API_KEY=your_anthropic_api_key_here +GITHUB_TOKEN=ghp_your_personal_access_token_here +GITHUB_OWNER=your_github_username +GITHUB_REPO=your_repository_name +SONARQUBE_ORG=your_sonarcloud_org_key +SONARQUBE_TOKEN=your_sonarqube_user_token +SONARQUBE_URL=https://sonarcloud.io +``` + +6. Protect your credentials by adding `.env` to `.gitignore`: + +```bash +echo ".env" >> .gitignore +echo "venv/" >> .gitignore +echo "__pycache__/" >> .gitignore +``` + +{{< /tab >}} +{{< /tabs >}} + ## Step 2: Create your first sandbox -Let's start by creating a sandbox and verifying the MCP servers are configured -correctly. +Let's start by creating a sandbox and verifying the MCP servers are configured correctly. + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} -Create a file named `01-test-connection.js` in your project root: +Create a file named `01-test-connection.ts` in your project root: -```javascript +```typescript import "dotenv/config"; import { Sandbox } from "e2b"; @@ -126,17 +186,17 @@ async function testConnection() { const sbx = await Sandbox.betaCreate({ envs: { - ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, - GITHUB_TOKEN: process.env.GITHUB_TOKEN, - SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { - githubPersonalAccessToken: process.env.GITHUB_TOKEN, + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { - org: process.env.SONARQUBE_ORG, - token: process.env.SONARQUBE_TOKEN, + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, @@ -162,7 +222,7 @@ async function testConnection() { }, ); - console.log("\n Connection successful! Cleaning up..."); + console.log("\nœConnection successful! Cleaning up..."); await sbx.kill(); } @@ -172,15 +232,83 @@ testConnection().catch(console.error); Run this script to verify your setup: ```bash -node 01-test-connection.js +npx tsx 01-test-connection.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create a file named `01_test_connection.py` in your project root: + +```python +import os +import asyncio +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def test_connection(): + print("Creating E2B sandbox with GitHub and SonarQube MCP servers...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + print(" Sandbox created successfully!") + print(f"MCP Gateway URL: {mcp_url}\n") + + # Wait for MCP initialization + await asyncio.sleep(1) + + # Configure Claude to use the MCP gateway + print("Connecting Claude CLI to MCP gateway...") + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print("\n Connection successful! Cleaning up...") + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(test_connection()) +``` + +Run this script to verify your setup: + +```bash +python 01_test_connection.py ``` +{{< /tab >}} +{{< /tabs >}} + Your output should look similar to the following example: -```console +```console {collapse=true} Creating E2B sandbox with GitHub and SonarQube MCP servers... -Sandbox created successfully! +✓ Sandbox created successfully! MCP Gateway URL: https://50005-xxxxx.e2b.app/mcp Connecting Claude CLI to MCP gateway... @@ -190,7 +318,7 @@ Headers: { } File modified: /home/user/.claude.json [project: /home/user] -Connection successful! Cleaning up... +✓ Connection successful! Cleaning up... ``` You've just learned how to create an E2B sandbox with multiple MCP servers @@ -203,9 +331,14 @@ MCP servers expose tools that Claude can call. The GitHub MCP server provides repository management tools, while SonarQube provides code analysis tools. By listing their tools, you know what operations are possible. -To try listing MCP tools, create `02-list-tools.js`: +To try listing MCP tools: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `02-list-tools.ts`: -```javascript +```typescript import "dotenv/config"; import { Sandbox } from "e2b"; @@ -214,17 +347,17 @@ async function listTools() { const sbx = await Sandbox.betaCreate({ envs: { - ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, - GITHUB_TOKEN: process.env.GITHUB_TOKEN, - SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { - githubPersonalAccessToken: process.env.GITHUB_TOKEN, + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { - org: process.env.SONARQUBE_ORG, - token: process.env.SONARQUBE_TOKEN, + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, @@ -260,12 +393,85 @@ listTools().catch(console.error); Run the script: ```bash -node 02-list-tools.js +npx tsx 02-list-tools.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create `02_list_tools.py`: + +```python +import os +import asyncio +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def list_tools(): + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + # Wait for MCP initialization + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print("\nDiscovering available MCP tools...\n") + + prompt = "List all MCP tools you have access to. For each tool, show its exact name and a brief description." + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(list_tools()) +``` + +Run the script: + +```bash +python 02_list_tools.py ``` +{{< /tab >}} +{{< /tabs >}} + In the console, you should see a list of MCP tools: -```console +```console {collapse=true} Creating sandbox... Sandbox created @@ -299,9 +505,12 @@ I have access to the following MCP tools: Let's try testing GitHub using MCP tools. We'll start simple by listing repository issues. -Create `03-test-github.js`: +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `03-test-github.ts`: -```javascript +```typescript import "dotenv/config"; import { Sandbox } from "e2b"; @@ -310,12 +519,12 @@ async function testGitHub() { const sbx = await Sandbox.betaCreate({ envs: { - ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, - GITHUB_TOKEN: process.env.GITHUB_TOKEN, + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, }, mcp: { githubOfficial: { - githubPersonalAccessToken: process.env.GITHUB_TOKEN, + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, }, }); @@ -354,12 +563,80 @@ testGitHub().catch(console.error); Run the script: ```bash -node 03-test-github.js +npx tsx 03-test-github.ts ``` +{{< /tab >}} +{{< tab name="Python" >}} + +Create `03_test_github.py`: + +```python +import os +import asyncio +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def test_github(): + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" + + print(f"\nListing issues in {repo_path}...\n") + + prompt = f'Using the GitHub MCP tools, list all open issues in the repository "{repo_path}". Show the issue number, title, and author for each.' + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(test_github()) +``` + +Run the script: + +```bash +python 03_test_github.py +``` + +{{< /tab >}} +{{< /tabs >}} + You should see Claude use the GitHub MCP tools to list your repository's issues: -```console +```console {collapse=true} Creating sandbox... Connecting to MCP gateway... @@ -382,9 +659,12 @@ natural language. Claude decides what tool to call based on your prompt. Let's analyze code quality using SonarQube MCP tools. -Create `04-test-sonarqube.js`: +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `04-test-sonarqube.ts`: -```javascript +```typescript import "dotenv/config"; import { Sandbox } from "e2b"; @@ -393,17 +673,17 @@ async function testSonarQube() { const sbx = await Sandbox.betaCreate({ envs: { - ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, - GITHUB_TOKEN: process.env.GITHUB_TOKEN, - SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { - githubPersonalAccessToken: process.env.GITHUB_TOKEN, + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { - org: process.env.SONARQUBE_ORG, - token: process.env.SONARQUBE_TOKEN, + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, @@ -448,16 +728,95 @@ testSonarQube().catch(console.error); Run the script: ```bash -node 04-test-sonarqube.js +npx tsx 04-test-sonarqube.ts ``` +{{< /tab >}} +{{< tab name="Python" >}} + +Create `04_test_sonarqube.py`: + +```python +import os +import asyncio +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def test_sonarqube(): + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print("\nAnalyzing code quality with SonarQube...\n") + + prompt = """Using the SonarQube MCP tools: + 1. List all projects in my organization + 2. For the first project, show: + - Quality gate status (pass/fail) + - Number of bugs + - Number of code smells + - Number of security vulnerabilities + 3. List the top 5 most critical issues found""" + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(test_sonarqube()) +``` + +Run the script: + +```bash +python 04_test_sonarqube.py +``` + +{{< /tab >}} +{{< /tabs >}} + > [!NOTE] > > This script may take a few minutes to run. You should see Claude output SonarQube analysis results: -```console +```console {collapse=true} Creating sandbox... Analyzing code quality with SonarQube... @@ -495,9 +854,12 @@ and understand what code needs fixing. Now, let's teach Claude to fix code based on quality issues discovered by SonarQube. -Create `05-fix-code-issue.js`: +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `05-fix-code-issue.ts`: -```javascript +```typescript import "dotenv/config"; import { Sandbox } from "e2b"; @@ -506,17 +868,17 @@ async function fixCodeIssue() { const sbx = await Sandbox.betaCreate({ envs: { - ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, - GITHUB_TOKEN: process.env.GITHUB_TOKEN, - SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { - githubPersonalAccessToken: process.env.GITHUB_TOKEN, + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { - org: process.env.SONARQUBE_ORG, - token: process.env.SONARQUBE_TOKEN, + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, @@ -557,7 +919,7 @@ async function fixCodeIssue() { }, ); - console.log(`\n Check your repository for branch: ${branchName}`); + console.log(`\nœCheck your repository for branch: ${branchName}`); await sbx.kill(); } @@ -568,16 +930,103 @@ fixCodeIssue().catch(console.error); Run the script: ```bash -node 05-fix-code-issue.js +npx tsx 05-fix-code-issue.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create `05_fix_code_issue.py`: + +```python +import os +import asyncio +import time +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def fix_code_issue(): + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" + branch_name = f"quality-fix-{int(time.time() * 1000)}" + + print("\nFixing a code quality issue...\n") + + prompt = f"""Using GitHub and SonarQube MCP tools: + + 1. Analyze code quality in repository "{repo_path}" with SonarQube + 2. Find ONE simple issue that can be confidently fixed (like an unused variable or code smell) + 3. Create a new branch called "{branch_name}" + 4. Read the file containing the issue using GitHub tools + 5. Fix the issue in the code + 6. Commit the fix to the new branch with a clear commit message + + Important: Only fix issues you're 100% confident about. Explain what you're fixing and why.""" + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print(f"\n Check your repository for branch: {branch_name}") + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(fix_code_issue()) ``` +Run the script: + +```bash +python 05_fix_code_issue.py +``` + +{{< /tab >}} +{{< /tabs >}} + > [!NOTE] > > This script may take a few minutes to run. Claude will analyze your repository and fix a code quality issue: -```console +```console {collapse=true} Creating sandbox... Fixing a code quality issue... @@ -623,9 +1072,12 @@ files, make code changes, and commit them. Finally, let's build the complete workflow: analyze quality, fix issues, and create a PR only if improvements are made. -Create `06-quality-gated-pr.js`: +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} -```javascript +Create `06-quality-gated-pr.ts`: + +```typescript import "dotenv/config"; import { Sandbox } from "e2b"; @@ -634,17 +1086,17 @@ async function qualityGatedPR() { const sbx = await Sandbox.betaCreate({ envs: { - ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, - GITHUB_TOKEN: process.env.GITHUB_TOKEN, - SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { - githubPersonalAccessToken: process.env.GITHUB_TOKEN, + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { - org: process.env.SONARQUBE_ORG, - token: process.env.SONARQUBE_TOKEN, + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, @@ -719,9 +1171,119 @@ qualityGatedPR().catch(console.error); Run the script: ```bash -node 06-quality-gated-pr.js +npx tsx 06-quality-gated-pr.ts ``` +{{< /tab >}} +{{< tab name="Python" >}} + +Create `06_quality_gated_pr.py`: + +```python +import os +import asyncio +import time +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def quality_gated_pr(): + print("Creating sandbox for quality-gated PR workflow...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" + branch_name = f"quality-improvements-{int(time.time() * 1000)}" + + print("\nRunning quality-gated PR workflow...\n") + + prompt = f"""You are a code quality engineer. Using GitHub and SonarQube MCP tools: + + STEP 1: ANALYSIS + - Get current code quality status from SonarQube for "{repo_path}" + - Record the current number of bugs, code smells, and vulnerabilities + - Identify 1-3 issues that you can confidently fix + + STEP 2: FIX ISSUES + - Create branch "{branch_name}" + - For each issue you are fixing: + Read the file with the issue + Make the fix + Commit with a descriptive message + - Only fix issues where you are 100 percent confident the fix is correct + + STEP 3: VERIFICATION + - After your fixes, check if quality metrics would improve + - Calculate: Would this reduce bugs/smells/vulnerabilities? + + STEP 4: QUALITY GATE + - Only proceed if your changes improve quality + - If quality would not improve, explain why and stop + + STEP 5: CREATE PR (only if quality gate passes) + - Create a pull request from "{branch_name}" to main + - Title: "Quality improvements: [describe what you fixed]" + - Description should include: + What issues you fixed + Before/after quality metrics + Why these fixes improve code quality + - Add a comment with detailed SonarQube analysis + + Be thorough and explain your decisions at each step.""" + + await sbx.commands.run( + f"echo '{prompt.replace(chr(39), chr(39) + chr(92) + chr(39) + chr(39))}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print(f"\n Workflow complete! Check {repo_path} for new pull request.") + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(quality_gated_pr()) +``` + +Run the script: + +```bash +python 06_quality_gated_pr.py +``` + +{{< /tab >}} +{{< /tabs >}} + > [!NOTE] > > This script may take a few minutes to run. @@ -729,7 +1291,7 @@ node 06-quality-gated-pr.js Claude will run the entire workflow, creating a quality improvement and opening a PR in GitHub: -```console +```console {collapse=true} Creating sandbox for quality-gated PR workflow... Running quality-gated PR workflow... @@ -814,31 +1376,34 @@ verifies improvements, and only creates a PR if quality actually improves. Production workflows need error handling. Let's make our workflow more robust. -Create `07-robust-workflow.js`: +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `07-robust-workflow.ts`: -```javascript +```typescript import "dotenv/config"; import { Sandbox } from "e2b"; async function robustWorkflow() { - let sbx; + let sbx: Sandbox | undefined; try { console.log("Creating sandbox...\n"); sbx = await Sandbox.betaCreate({ envs: { - ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY, - GITHUB_TOKEN: process.env.GITHUB_TOKEN, - SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN, + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { - githubPersonalAccessToken: process.env.GITHUB_TOKEN, + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { - org: process.env.SONARQUBE_ORG, - token: process.env.SONARQUBE_TOKEN, + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, @@ -880,13 +1445,14 @@ async function robustWorkflow() { console.log("\n Workflow completed"); } catch (error) { - console.error("\n Workflow failed:", error.message); + const err = error as Error; + console.error("\n Workflow failed:", err.message); - if (error.message.includes("403")) { + if (err.message.includes("403")) { console.error("\n Check your E2B account has MCP gateway access"); - } else if (error.message.includes("401")) { + } else if (err.message.includes("401")) { console.error("\n Check your API tokens are valid"); - } else if (error.message.includes("Credit balance")) { + } else if (err.message.includes("Credit balance")) { console.error("\n Check your Anthropic API credit balance"); } @@ -905,9 +1471,114 @@ robustWorkflow().catch(console.error); Run the script: ```bash -node 07-robust-workflow.js +npx tsx 07-robust-workflow.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create `07_robust_workflow.py`: + +```python +import os +import asyncio +import sys +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def robust_workflow(): + sbx = None + + try: + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, # Fixed: was timeout_ms + on_stdout=print, + on_stderr=print, + ) + + repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" + + print("\nRunning workflow with error handling...\n") + + prompt = f"""Run a quality improvement workflow for "{repo_path}". + + ERROR HANDLING RULES: + 1. If SonarQube is unreachable, explain the error and stop gracefully + 2. If GitHub API fails, retry once, then explain and stop + 3. If no fixable issues are found, explain why and exit (this is not an error) + 4. If file modifications fail, explain which file and why + 5. At each step, check for errors before proceeding + + Run the workflow and handle any errors you encounter professionally.""" + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print("\n Workflow completed") + + except Exception as error: + print(f"\n✗ Workflow failed: {str(error)}") + + error_msg = str(error) + if "403" in error_msg: + print("\n Check your E2B account has MCP gateway access") + elif "401" in error_msg: + print("\n Check your API tokens are valid") + elif "Credit balance" in error_msg: + print("\n Check your Anthropic API credit balance") + + sys.exit(1) + + finally: + if sbx: + print("\n Cleaning up sandbox...") + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(robust_workflow()) +``` + +Run the script: + +```bash +python 07_robust_workflow.py ``` +{{< /tab >}} +{{< /tabs >}} + Claude will run the entire workflow, and if it encounters an error, respond with robust error messaging. diff --git a/hugo_stats.json b/hugo_stats.json index 35a8c1c8985c..8ed1b0e86691 100644 --- a/hugo_stats.json +++ b/hugo_stats.json @@ -134,14 +134,17 @@ "Specific-version", "Svelte", "Testcontainers-Cloud", + "TypeScript", "Ubuntu", "Ubuntu/Debian", "Unix-pipe", "Updated-Dockerfile", "Use-Docker-Init", "Use-OpenAI", + "Using-Docker-Hardened-Images", "Using-the-CLI", "Using-the-GUI", + "Using-the-official-image", "VS-Code", "Vue", "WSL-2-backend-Arm-Early-Access", @@ -223,6 +226,7 @@ "card-title", "chip", "chroma", + "cl", "cls-1", "cls-2", "col-start-2", @@ -386,6 +390,7 @@ "lg:no-underline", "lg:pb-2", "lg:scale-100", + "line", "link", "links", "lntable", From bba0bed5a4907f3248e4d70805c01a838ef1b4d0 Mon Sep 17 00:00:00 2001 From: sarahsanders-docker Date: Tue, 21 Oct 2025 16:46:49 -0400 Subject: [PATCH 5/9] proofread: headings and fixes --- .../github-sonarqube-sandbox/troubleshoot.md | 6 +----- .../github-sonarqube-sandbox/workflow.md | 18 +++++++++--------- hugo_stats.json | 2 -- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/content/guides/github-sonarqube-sandbox/troubleshoot.md b/content/guides/github-sonarqube-sandbox/troubleshoot.md index 53629f4f884d..4e07789c211c 100644 --- a/content/guides/github-sonarqube-sandbox/troubleshoot.md +++ b/content/guides/github-sonarqube-sandbox/troubleshoot.md @@ -1,6 +1,6 @@ --- title: Troubleshoot code quality workflows -linkTitle: Troubleshooting +linkTitle: Troubleshoot summary: Resolve common issues with E2B sandboxes, MCP server connections, and GitHub/SonarQube integration. description: Solutions for MCP tools not loading, authentication errors, permission issues, workflow timeouts, and other common problems when building code quality workflows with E2B. weight: 30 @@ -346,7 +346,3 @@ Solution: 2. Ensure you have at least one project configured in SonarCloud. 3. Check that your SonarQube token has the necessary permissions. 4. Confirm your project has been analyzed at least once in SonarCloud. - -## Next steps - -Explore additional resources for E2B and MCP development. diff --git a/content/guides/github-sonarqube-sandbox/workflow.md b/content/guides/github-sonarqube-sandbox/workflow.md index 07c211f53327..b0c2c87bc274 100644 --- a/content/guides/github-sonarqube-sandbox/workflow.md +++ b/content/guides/github-sonarqube-sandbox/workflow.md @@ -49,7 +49,7 @@ Before you begin, make sure you have: > use this approach with trusted code and workflows. For more information, > see [Anthropic's guidance on container security](https://docs.anthropic.com/en/docs/claude-code/devcontainer). -## Step 1: Set up your project +## Set up your project {{< tabs group="language" >}} {{< tab name="TypeScript" >}} @@ -166,7 +166,7 @@ echo "__pycache__/" >> .gitignore {{< /tab >}} {{< /tabs >}} -## Step 2: Create your first sandbox +## Step 1: Create your first sandbox Let's start by creating a sandbox and verifying the MCP servers are configured correctly. @@ -325,7 +325,7 @@ You've just learned how to create an E2B sandbox with multiple MCP servers configured. The `betaCreate` method initializes a cloud environment with Claude CLI and your specified MCP servers. -## Step 3: Discover available MCP tools +## Step 2: Discover available MCP tools MCP servers expose tools that Claude can call. The GitHub MCP server provides repository management tools, while SonarQube provides code analysis tools. @@ -500,7 +500,7 @@ I have access to the following MCP tools: ... (SonarQube analysis tools) ``` -## Step 4: Test GitHub MCP tools +## Step 3: Test GitHub MCP tools Let's try testing GitHub using MCP tools. We'll start simple by listing repository issues. @@ -655,7 +655,7 @@ Here are the first 10 open issues in the repository: You can now send prompts to Claude and interact with GitHub through natural language. Claude decides what tool to call based on your prompt. -## Step 5: Test SonarQube MCP tools +## Step 4: Test SonarQube MCP tools Let's analyze code quality using SonarQube MCP tools. @@ -849,7 +849,7 @@ You can now use SonarQube MCP tools to analyze code quality through natural language. You can retrieve quality metrics, identify issues, and understand what code needs fixing. -## Step 6: Create a branch and make code changes +## Step 5: Create a branch and make code changes Now, let's teach Claude to fix code based on quality issues discovered by SonarQube. @@ -1067,7 +1067,7 @@ The fix is ready for review and can be merged to improve the code quality of the You can now use GitHub and SonarQube MCP tools in the same workflow to read files, make code changes, and commit them. -## Step 7: Create quality-gated pull requests +## Step 6: Create quality-gated pull requests Finally, let's build the complete workflow: analyze quality, fix issues, and create a PR only if improvements are made. @@ -1372,9 +1372,9 @@ You've now built a complete, multi-step workflow with conditional logic. Claude analyzes quality with SonarQube, makes fixes using GitHub tools, verifies improvements, and only creates a PR if quality actually improves. -## Step 8: Add error handling +## Step 7: Add error handling -Production workflows need error handling. Let's make our workflow more robust. +Production workflows need error handling. Let's make the workflow more robust. {{< tabs group="language" >}} {{< tab name="TypeScript" >}} diff --git a/hugo_stats.json b/hugo_stats.json index 8ed1b0e86691..4907c59ce48d 100644 --- a/hugo_stats.json +++ b/hugo_stats.json @@ -226,7 +226,6 @@ "card-title", "chip", "chroma", - "cl", "cls-1", "cls-2", "col-start-2", @@ -390,7 +389,6 @@ "lg:no-underline", "lg:pb-2", "lg:scale-100", - "line", "link", "links", "lntable", From 4decec8a84121c22e7a3fb087fd9bb4e2e67c6ac Mon Sep 17 00:00:00 2001 From: sarahsanders-docker Date: Thu, 23 Oct 2025 08:12:29 -0400 Subject: [PATCH 6/9] nits --- content/guides/github-sonarqube-sandbox/_index.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/content/guides/github-sonarqube-sandbox/_index.md b/content/guides/github-sonarqube-sandbox/_index.md index 0acfc0a35ac5..523012e1a52d 100644 --- a/content/guides/github-sonarqube-sandbox/_index.md +++ b/content/guides/github-sonarqube-sandbox/_index.md @@ -24,9 +24,10 @@ using SonarQube, then generate pull requests with fixes. ## What you'll build -You'll build a Node.js script that spins up an E2B sandbox, connects GitHub -and SonarQube MCP servers, and uses Claude to analyze code quality and propose -improvements. +You’ll build a Node.js script that spins up an E2B sandbox, connects GitHub and +SonarQube MCP servers, and uses Claude Code to analyze code quality and propose +improvements. The MCP servers are containerized and run as part of the E2B +sandbox. ## What you'll learn @@ -34,7 +35,7 @@ In this guide, you'll learn: - How to create E2B sandboxes with multiple MCP servers - How to configure GitHub and SonarQube MCP servers for AI workflows -- How to use Claude CLI inside sandboxes to interact with external tools +- How to use Claude Code inside sandboxes to interact with external tools - How to build automated code review workflows that create quality-gated pull requests From 9c72cc461a69f878a4e090894a48820c9c079d48 Mon Sep 17 00:00:00 2001 From: sarahsanders-docker Date: Thu, 23 Oct 2025 08:23:29 -0400 Subject: [PATCH 7/9] fix featured section --- content/guides/admin-user-management/_index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/guides/admin-user-management/_index.md b/content/guides/admin-user-management/_index.md index 746eaed94ed6..5d68916081c6 100644 --- a/content/guides/admin-user-management/_index.md +++ b/content/guides/admin-user-management/_index.md @@ -4,9 +4,9 @@ summary: Simplify user access while ensuring security and efficiency in Docker. description: A guide for managing roles, provisioning users, and optimizing Docker access with tools like SSO and activity logs. tags: [admin] params: - featured: true + featured: false time: 20 minutes - image: + image: resource_links: - title: Overview of Administration in Docker url: /admin/ From 884823f6c9218effd4466dec581f87275e92a1c3 Mon Sep 17 00:00:00 2001 From: sarahsanders-docker Date: Thu, 23 Oct 2025 09:51:29 -0400 Subject: [PATCH 8/9] fixes, add blog,turn off ol lint for now --- .markdownlint.json | 2 +- content/guides/github-sonarqube-sandbox/_index.md | 4 ++++ content/guides/github-sonarqube-sandbox/workflow.md | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.markdownlint.json b/.markdownlint.json index 86037b36a7b8..89cc9d97fc8d 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -13,7 +13,7 @@ "no-space-in-code": true, "no-space-in-links": true, "no-empty-links": true, - "ol-prefix": {"style": "one_or_ordered"}, + "ol-prefix": false, "no-reversed-links": true, "reference-links-images": { "shortcut_syntax": false diff --git a/content/guides/github-sonarqube-sandbox/_index.md b/content/guides/github-sonarqube-sandbox/_index.md index 523012e1a52d..a2e690d7b47c 100644 --- a/content/guides/github-sonarqube-sandbox/_index.md +++ b/content/guides/github-sonarqube-sandbox/_index.md @@ -50,3 +50,7 @@ local environment and credentials locally - Scalability: Resource-intensive operations like code scanning run in the cloud without consuming local resources + +## Learn more + +Read Docker's blog post: [Docker + E2B: Building the Future of Trusted AI](https://www.docker.com/blog/docker-e2b-building-the-future-of-trusted-ai/). diff --git a/content/guides/github-sonarqube-sandbox/workflow.md b/content/guides/github-sonarqube-sandbox/workflow.md index b0c2c87bc274..59e96cab0ff3 100644 --- a/content/guides/github-sonarqube-sandbox/workflow.md +++ b/content/guides/github-sonarqube-sandbox/workflow.md @@ -131,7 +131,6 @@ source venv/bin/activate # On Windows: venv\Scripts\activate ``` 3. Install required dependencies: - ```bash pip install e2b python-dotenv ``` From ffb339082afeb0c47b24f9f542e6b5029b243b0d Mon Sep 17 00:00:00 2001 From: sarahsanders-docker Date: Thu, 23 Oct 2025 09:53:21 -0400 Subject: [PATCH 9/9] remove we --- content/guides/github-sonarqube-sandbox/workflow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/github-sonarqube-sandbox/workflow.md b/content/guides/github-sonarqube-sandbox/workflow.md index 59e96cab0ff3..ccb12dbbd441 100644 --- a/content/guides/github-sonarqube-sandbox/workflow.md +++ b/content/guides/github-sonarqube-sandbox/workflow.md @@ -501,7 +501,7 @@ I have access to the following MCP tools: ## Step 3: Test GitHub MCP tools -Let's try testing GitHub using MCP tools. We'll start simple by listing +Let's try testing GitHub using MCP tools. Start simple by listing repository issues. {{< tabs group="language" >}}