From 8b125f319fdb7221fbad57b497a452738b30b088 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:06:02 +0000 Subject: [PATCH 1/3] Initial plan From ea97497de24f7cc89b6214ef66128d530ee3f391 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:13:15 +0000 Subject: [PATCH 2/3] Add comprehensive tests and documentation for custom mounts - Added tests for Claude and Codex engines with custom mounts - Added cross-engine test to verify consistent behavior - Added common tool mount scenarios tests (database clients, cloud CLIs, build tools, container tools, shared libraries, directories) - Enhanced documentation with detailed examples for common use cases - Added security warnings and best practices to documentation Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- docs/src/content/docs/reference/sandbox.md | 90 +++++- pkg/workflow/custom_mounts_test.go | 353 +++++++++++++++++++++ 2 files changed, 439 insertions(+), 4 deletions(-) create mode 100644 pkg/workflow/custom_mounts_test.go diff --git a/docs/src/content/docs/reference/sandbox.md b/docs/src/content/docs/reference/sandbox.md index fd89ddb2aa..77e2d2018c 100644 --- a/docs/src/content/docs/reference/sandbox.md +++ b/docs/src/content/docs/reference/sandbox.md @@ -250,10 +250,92 @@ Mount syntax follows Docker's format: `source:destination:mode` - `destination`: Path inside the container - `mode`: Either `ro` (read-only) or `rw` (read-write) -Custom mounts are useful for: -- Providing access to datasets or configuration files -- Making custom tools available in the container -- Sharing cache directories between host and container +###### Common Use Cases + +**Database Clients:** + +```yaml wrap +sandbox: + agent: + id: awf + mounts: + - "/usr/bin/psql:/usr/bin/psql:ro" + - "/usr/bin/mysql:/usr/bin/mysql:ro" + - "/usr/bin/redis-cli:/usr/bin/redis-cli:ro" + - "/usr/lib/x86_64-linux-gnu/libpq.so.5:/usr/lib/x86_64-linux-gnu/libpq.so.5:ro" +``` + +**Cloud CLI Tools:** + +```yaml wrap +sandbox: + agent: + id: awf + mounts: + - "/usr/local/bin/aws:/usr/local/bin/aws:ro" + - "/usr/local/bin/gcloud:/usr/local/bin/gcloud:ro" + - "/usr/local/bin/az:/usr/local/bin/az:ro" +``` + +**Build Tools:** + +```yaml wrap +sandbox: + agent: + id: awf + mounts: + - "/usr/bin/make:/usr/bin/make:ro" + - "/usr/bin/cmake:/usr/bin/cmake:ro" + - "/usr/bin/gcc:/usr/bin/gcc:ro" +``` + +**Container Tools:** + +```yaml wrap +sandbox: + agent: + id: awf + mounts: + - "/usr/bin/docker:/usr/bin/docker:ro" + - "/usr/bin/kubectl:/usr/bin/kubectl:ro" + - "/usr/bin/helm:/usr/bin/helm:ro" +``` + +**Shared Libraries:** + +```yaml wrap +sandbox: + agent: + id: awf + mounts: + - "/usr/lib/x86_64-linux-gnu/libssl.so.3:/usr/lib/x86_64-linux-gnu/libssl.so.3:ro" + - "/usr/lib/x86_64-linux-gnu/libcrypto.so.3:/usr/lib/x86_64-linux-gnu/libcrypto.so.3:ro" +``` + +**Configuration Directories:** + +```yaml wrap +sandbox: + agent: + id: awf + mounts: + - "/usr/share/ca-certificates:/usr/share/ca-certificates:ro" + - "/etc/ssl/certs:/etc/ssl/certs:ro" +``` + +> [!WARNING] +> Security Considerations +> - Always use read-only (`:ro`) mode unless write access is absolutely necessary +> - Never mount sensitive system directories like `/etc/passwd` or `/root` +> - Docker socket (`/var/run/docker.sock`) mounting is not supported for security reasons +> - Custom mounts are validated for proper syntax but not verified to exist on the host + +> [!TIP] +> Best Practices +> - Mount only the specific binaries and libraries your workflow needs +> - Use absolute paths for both source and destination +> - Group related mounts together for better organization +> - Test mounts on your runner environment before deploying to production | Field | Type | Description | |-------|------|-------------| diff --git a/pkg/workflow/custom_mounts_test.go b/pkg/workflow/custom_mounts_test.go new file mode 100644 index 0000000000..0ae130b7ed --- /dev/null +++ b/pkg/workflow/custom_mounts_test.go @@ -0,0 +1,353 @@ +//go:build !integration + +package workflow + +import ( + "strings" + "testing" +) + +// TestClaudeEngineWithCustomMounts tests that custom mounts are included in AWF command for Claude engine +func TestClaudeEngineWithCustomMounts(t *testing.T) { + t.Run("custom mounts are included in AWF command", func(t *testing.T) { + workflowData := &WorkflowData{ + Name: "test-claude-workflow", + EngineConfig: &EngineConfig{ + ID: "claude", + }, + SandboxConfig: &SandboxConfig{ + Agent: &AgentSandboxConfig{ + ID: "awf", + Mounts: []string{ + "/usr/bin/psql:/usr/bin/psql:ro", + "/usr/lib/postgresql:/usr/lib/postgresql:ro", + }, + }, + }, + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + }, + }, + } + + engine := NewClaudeEngine() + steps := engine.GetExecutionSteps(workflowData, "test.log") + + if len(steps) == 0 { + t.Fatal("Expected at least one execution step") + } + + stepContent := strings.Join(steps[0], "\n") + + // Check that custom mounts are included + if !strings.Contains(stepContent, "--mount /usr/bin/psql:/usr/bin/psql:ro") { + t.Error("Expected command to contain custom mount '--mount /usr/bin/psql:/usr/bin/psql:ro'") + } + + if !strings.Contains(stepContent, "--mount /usr/lib/postgresql:/usr/lib/postgresql:ro") { + t.Error("Expected command to contain custom mount '--mount /usr/lib/postgresql:/usr/lib/postgresql:ro'") + } + + // Verify standard mounts are still present + if !strings.Contains(stepContent, "--mount /tmp:/tmp:rw") { + t.Error("Expected command to still contain standard mount '--mount /tmp:/tmp:rw'") + } + }) + + t.Run("custom mounts are sorted alphabetically", func(t *testing.T) { + workflowData := &WorkflowData{ + Name: "test-claude-workflow", + EngineConfig: &EngineConfig{ + ID: "claude", + }, + SandboxConfig: &SandboxConfig{ + Agent: &AgentSandboxConfig{ + ID: "awf", + Mounts: []string{ + "/var/cache:/cache:rw", + "/etc/ssl:/etc/ssl:ro", + "/usr/local/bin/aws:/usr/local/bin/aws:ro", + }, + }, + }, + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + }, + }, + } + + engine := NewClaudeEngine() + steps := engine.GetExecutionSteps(workflowData, "test.log") + + if len(steps) == 0 { + t.Fatal("Expected at least one execution step") + } + + stepContent := strings.Join(steps[0], "\n") + + // Find positions of mounts to verify sorting + etcPos := strings.Index(stepContent, "--mount /etc/ssl:/etc/ssl:ro") + usrPos := strings.Index(stepContent, "--mount /usr/local/bin/aws:/usr/local/bin/aws:ro") + varPos := strings.Index(stepContent, "--mount /var/cache:/cache:rw") + + // Verify all mounts are present + if etcPos == -1 { + t.Error("Expected to find mount '/etc/ssl:/etc/ssl:ro'") + } + if usrPos == -1 { + t.Error("Expected to find mount '/usr/local/bin/aws:/usr/local/bin/aws:ro'") + } + if varPos == -1 { + t.Error("Expected to find mount '/var/cache:/cache:rw'") + } + + // Verify mounts are in alphabetical order: /etc, /usr, /var + if etcPos != -1 && usrPos != -1 && etcPos >= usrPos { + t.Error("Expected '/etc/ssl:/etc/ssl:ro' to appear before '/usr/local/bin/aws:/usr/local/bin/aws:ro'") + } + if usrPos != -1 && varPos != -1 && usrPos >= varPos { + t.Error("Expected '/usr/local/bin/aws:/usr/local/bin/aws:ro' to appear before '/var/cache:/cache:rw'") + } + }) +} + +// TestCodexEngineWithCustomMounts tests that custom mounts are included in AWF command for Codex engine +func TestCodexEngineWithCustomMounts(t *testing.T) { + t.Run("custom mounts are included in AWF command", func(t *testing.T) { + workflowData := &WorkflowData{ + Name: "test-codex-workflow", + EngineConfig: &EngineConfig{ + ID: "codex", + }, + SandboxConfig: &SandboxConfig{ + Agent: &AgentSandboxConfig{ + ID: "awf", + Mounts: []string{ + "/usr/bin/docker:/usr/bin/docker:ro", + "/usr/bin/kubectl:/usr/bin/kubectl:ro", + }, + }, + }, + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + }, + }, + } + + engine := NewCodexEngine() + steps := engine.GetExecutionSteps(workflowData, "test.log") + + if len(steps) == 0 { + t.Fatal("Expected at least one execution step") + } + + stepContent := strings.Join(steps[0], "\n") + + // Check that custom mounts are included + if !strings.Contains(stepContent, "--mount /usr/bin/docker:/usr/bin/docker:ro") { + t.Error("Expected command to contain custom mount '--mount /usr/bin/docker:/usr/bin/docker:ro'") + } + + if !strings.Contains(stepContent, "--mount /usr/bin/kubectl:/usr/bin/kubectl:ro") { + t.Error("Expected command to contain custom mount '--mount /usr/bin/kubectl:/usr/bin/kubectl:ro'") + } + + // Verify standard mounts are still present + if !strings.Contains(stepContent, "--mount /tmp:/tmp:rw") { + t.Error("Expected command to still contain standard mount '--mount /tmp:/tmp:rw'") + } + }) + + t.Run("no custom mounts when not specified", func(t *testing.T) { + workflowData := &WorkflowData{ + Name: "test-codex-workflow", + EngineConfig: &EngineConfig{ + ID: "codex", + }, + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + }, + }, + } + + engine := NewCodexEngine() + steps := engine.GetExecutionSteps(workflowData, "test.log") + + if len(steps) == 0 { + t.Fatal("Expected at least one execution step") + } + + stepContent := strings.Join(steps[0], "\n") + + // Verify standard mounts are present + if !strings.Contains(stepContent, "--mount /tmp:/tmp:rw") { + t.Error("Expected command to contain standard mount '--mount /tmp:/tmp:rw'") + } + + // Custom mount should not be present + if strings.Contains(stepContent, "--mount /usr/bin/docker:/usr/bin/docker:ro") { + t.Error("Did not expect custom mount in output when not configured") + } + }) +} + +// TestCustomMountsAcrossEngines tests that custom mounts work consistently across all engines +func TestCustomMountsAcrossEngines(t *testing.T) { + testCases := []struct { + name string + engineName string + engine CodingAgentEngine + }{ + { + name: "copilot", + engineName: "copilot", + engine: NewCopilotEngine(), + }, + { + name: "claude", + engineName: "claude", + engine: NewClaudeEngine(), + }, + { + name: "codex", + engineName: "codex", + engine: NewCodexEngine(), + }, + } + + customMounts := []string{ + "/usr/bin/custom-tool:/usr/bin/custom-tool:ro", + "/data/shared:/data:ro", + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + workflowData := &WorkflowData{ + Name: "test-workflow", + EngineConfig: &EngineConfig{ + ID: tc.engineName, + }, + SandboxConfig: &SandboxConfig{ + Agent: &AgentSandboxConfig{ + ID: "awf", + Mounts: customMounts, + }, + }, + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + }, + }, + } + + steps := tc.engine.GetExecutionSteps(workflowData, "test.log") + + if len(steps) == 0 { + t.Fatalf("Expected at least one execution step for %s engine", tc.engineName) + } + + stepContent := strings.Join(steps[0], "\n") + + // Verify both custom mounts are present + for _, mount := range customMounts { + expectedArg := "--mount " + mount + if !strings.Contains(stepContent, expectedArg) { + t.Errorf("%s engine: Expected command to contain '%s'", tc.engineName, expectedArg) + } + } + + // Verify standard mounts are still present + if !strings.Contains(stepContent, "--mount /tmp:/tmp:rw") { + t.Errorf("%s engine: Expected command to contain standard mount '--mount /tmp:/tmp:rw'", tc.engineName) + } + }) + } +} + +// TestMountsSyntaxForCommonTools tests validation for common tool mount scenarios +func TestMountsSyntaxForCommonTools(t *testing.T) { + tests := []struct { + name string + mounts []string + wantErr bool + }{ + { + name: "database client binaries", + mounts: []string{ + "/usr/bin/psql:/usr/bin/psql:ro", + "/usr/bin/mysql:/usr/bin/mysql:ro", + "/usr/bin/redis-cli:/usr/bin/redis-cli:ro", + }, + wantErr: false, + }, + { + name: "cloud CLI tools", + mounts: []string{ + "/usr/local/bin/aws:/usr/local/bin/aws:ro", + "/usr/local/bin/gcloud:/usr/local/bin/gcloud:ro", + "/usr/local/bin/az:/usr/local/bin/az:ro", + }, + wantErr: false, + }, + { + name: "build tools", + mounts: []string{ + "/usr/bin/make:/usr/bin/make:ro", + "/usr/bin/cmake:/usr/bin/cmake:ro", + "/usr/bin/gcc:/usr/bin/gcc:ro", + }, + wantErr: false, + }, + { + name: "container tools", + mounts: []string{ + "/usr/bin/docker:/usr/bin/docker:ro", + "/usr/bin/kubectl:/usr/bin/kubectl:ro", + "/usr/bin/helm:/usr/bin/helm:ro", + }, + wantErr: false, + }, + { + name: "shared libraries", + mounts: []string{ + "/usr/lib/x86_64-linux-gnu/libssl.so.3:/usr/lib/x86_64-linux-gnu/libssl.so.3:ro", + "/usr/lib/x86_64-linux-gnu/libcrypto.so.3:/usr/lib/x86_64-linux-gnu/libcrypto.so.3:ro", + }, + wantErr: false, + }, + { + name: "directories", + mounts: []string{ + "/usr/share/ca-certificates:/usr/share/ca-certificates:ro", + "/etc/ssl/certs:/etc/ssl/certs:ro", + }, + wantErr: false, + }, + { + name: "writable cache directories", + mounts: []string{ + "/var/cache/apt:/var/cache/apt:rw", + "/tmp/build-cache:/cache:rw", + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateMountsSyntax(tt.mounts) + + if tt.wantErr && err == nil { + t.Error("validateMountsSyntax() expected error but got none") + } + + if !tt.wantErr && err != nil { + t.Errorf("validateMountsSyntax() unexpected error: %v", err) + } + }) + } +} From 36edb6359730e4213175afe1aedaae1db3ea18fb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 06:14:50 +0000 Subject: [PATCH 3/3] Add demo workflow for custom binary mounts feature - Created workflow demonstrating database clients, cloud CLIs, build tools, and shared libraries - Verified compilation and mount arguments in compiled workflow - All custom mounts are sorted and applied correctly to AWF command Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/demo-custom-mounts.lock.yml | 475 ++++++++++++++++++ .github/workflows/demo-custom-mounts.md | 61 +++ 2 files changed, 536 insertions(+) create mode 100644 .github/workflows/demo-custom-mounts.lock.yml create mode 100644 .github/workflows/demo-custom-mounts.md diff --git a/.github/workflows/demo-custom-mounts.lock.yml b/.github/workflows/demo-custom-mounts.lock.yml new file mode 100644 index 0000000000..8e34083ac5 --- /dev/null +++ b/.github/workflows/demo-custom-mounts.lock.yml @@ -0,0 +1,475 @@ +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw. DO NOT EDIT. +# +# To update this file, edit the corresponding .md file and run: +# gh aw compile +# For more information: https://github.com/githubnext/gh-aw/blob/main/.github/aw/github-agentic-workflows.md +# +# Demonstrates using custom binary and library mounts with AWF sandbox + +name: "Custom Binary Mounts Demo" +"on": + workflow_dispatch: + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "Custom Binary Mounts Demo" + +jobs: + activation: + runs-on: ubuntu-slim + permissions: + contents: read + outputs: + comment_id: "" + comment_repo: "" + steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false + - name: Setup Scripts + uses: ./actions/setup + with: + destination: /opt/gh-aw/actions + - name: Check workflow file timestamps + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_WORKFLOW_FILE: "demo-custom-mounts.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + contents: read + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}" + outputs: + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false + - name: Setup Scripts + uses: ./actions/setup + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + if: | + github.event.pull_request + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://githubnext.github.io/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.397 + - name: Install awf binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.11.2 + - name: Determine automatic lockdown mode for GitHub MCP server + id: determine-automatic-lockdown + env: + TOKEN_CHECK: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + if: env.TOKEN_CHECK != '' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/githubnext/gh-aw-mcpg:v0.0.84 + - name: Start MCP gateway + id: start-mcp-gateway + env: + GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + MCP_GATEWAY_API_KEY="" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + export MCP_GATEWAY_API_KEY + + # Register API key as secret to mask it from logs + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export GH_AW_ENGINE="copilot" + export GITHUB_PERSONAL_ACCESS_TOKEN="$GITHUB_MCP_SERVER_TOKEN" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e DEBUG="*" -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_PERSONAL_ACCESS_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/githubnext/gh-aw-mcpg:v0.0.84' + + mkdir -p /home/runner/.copilot + cat << MCPCONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "github": { + "type": "http", + "url": "https://api.githubcopilot.com/mcp/", + "headers": { + "Authorization": "Bearer \${GITHUB_PERSONAL_ACCESS_TOKEN}", + "X-MCP-Lockdown": "$([ "$GITHUB_MCP_LOCKDOWN" = "1" ] && echo true || echo false)", + "X-MCP-Readonly": "true", + "X-MCP-Toolsets": "context,repos,issues,pull_requests" + }, + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}" + } + } + MCPCONFIG_EOF + - name: Generate agentic run info + id: generate_aw_info + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const fs = require('fs'); + + const awInfo = { + engine_id: "copilot", + engine_name: "GitHub Copilot CLI", + model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", + version: "", + agent_version: "0.0.397", + workflow_name: "Custom Binary Mounts Demo", + experimental: false, + supports_tools_allowlist: true, + supports_http_transport: true, + run_id: context.runId, + run_number: context.runNumber, + run_attempt: process.env.GITHUB_RUN_ATTEMPT, + repository: context.repo.owner + '/' + context.repo.repo, + ref: context.ref, + sha: context.sha, + actor: context.actor, + event_name: context.eventName, + staged: false, + allowed_domains: ["defaults"], + firewall_enabled: true, + awf_version: "v0.11.2", + awmg_version: "v0.0.84", + steps: { + firewall: "squid" + }, + created_at: new Date().toISOString() + }; + + // Write to /tmp/gh-aw directory to avoid inclusion in PR + const tmpPath = '/tmp/gh-aw/aw_info.json'; + fs.writeFileSync(tmpPath, JSON.stringify(awInfo, null, 2)); + console.log('Generated aw_info.json at:', tmpPath); + console.log(JSON.stringify(awInfo, null, 2)); + + // Set model as output for reuse in other steps/jobs + core.setOutput('model', awInfo.model); + - name: Generate workflow overview + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs'); + await generateWorkflowOverview(core); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + cat << 'PROMPT_EOF' > "$GH_AW_PROMPT" + + PROMPT_EOF + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" >> "$GH_AW_PROMPT" + cat "/opt/gh-aw/prompts/markdown.md" >> "$GH_AW_PROMPT" + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + PROMPT_EOF + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + + PROMPT_EOF + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + # Custom Binary Mounts Demonstration + + This workflow demonstrates how to use custom binary and library mounts to make + specialized tools available inside the AWF sandbox container. + + ## Task + + Please verify that the following custom-mounted binaries are available: + + 1. Check for database clients: + - Run `which psql` to verify PostgreSQL client is available + - Run `which mysql` to verify MySQL client is available + + 2. Check for cloud CLIs: + - Run `which aws` to verify AWS CLI is available + + 3. Check for build tools: + - Run `which make` to verify Make is available + - Run `which gcc` to verify GCC compiler is available + + 4. Check for shared libraries: + - Run `ldconfig -p | grep libssl` to verify SSL library is available + + Display the results in a summary showing which tools are available and which + are missing. If a tool is available, also show its version. + + PROMPT_EOF + - name: Substitute placeholders + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + with: + script: | + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE + } + }); + - name: Interpolate variables and render templates + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 20 + run: | + set -o pipefail + GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS + mkdir -p "$HOME/.cache" + sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --mount /usr/bin/gcc:/usr/bin/gcc:ro --mount /usr/bin/make:/usr/bin/make:ro --mount /usr/bin/mysql:/usr/bin/mysql:ro --mount /usr/bin/psql:/usr/bin/psql:ro --mount /usr/lib/x86_64-linux-gnu/libssl.so.3:/usr/lib/x86_64-linux-gnu/libssl.so.3:ro --mount /usr/local/bin/aws:/usr/local/bin/aws:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \ + -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_COPILOT:+ --model "$GH_AW_MODEL_DETECTION_COPILOT"}' \ + 2>&1 | tee /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_MODEL_DETECTION_COPILOT: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - name: Stop MCP gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload engine output files + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: agent_outputs + path: | + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + if-no-files-found: ignore + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP gateway logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: agent-artifacts + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio.log + if-no-files-found: ignore + diff --git a/.github/workflows/demo-custom-mounts.md b/.github/workflows/demo-custom-mounts.md new file mode 100644 index 0000000000..d8d2528c02 --- /dev/null +++ b/.github/workflows/demo-custom-mounts.md @@ -0,0 +1,61 @@ +--- +name: Custom Binary Mounts Demo +description: Demonstrates using custom binary and library mounts with AWF sandbox +engine: copilot +on: + workflow_dispatch: + +# Configure sandbox with custom tool mounts +sandbox: + agent: + id: awf + mounts: + # Database clients + - "/usr/bin/psql:/usr/bin/psql:ro" + - "/usr/bin/mysql:/usr/bin/mysql:ro" + + # Cloud CLIs + - "/usr/local/bin/aws:/usr/local/bin/aws:ro" + + # Build tools + - "/usr/bin/make:/usr/bin/make:ro" + - "/usr/bin/gcc:/usr/bin/gcc:ro" + + # Shared libraries + - "/usr/lib/x86_64-linux-gnu/libssl.so.3:/usr/lib/x86_64-linux-gnu/libssl.so.3:ro" + +network: + firewall: true + allowed: + - defaults + +tools: + github: + mode: remote +--- + +# Custom Binary Mounts Demonstration + +This workflow demonstrates how to use custom binary and library mounts to make +specialized tools available inside the AWF sandbox container. + +## Task + +Please verify that the following custom-mounted binaries are available: + +1. Check for database clients: + - Run `which psql` to verify PostgreSQL client is available + - Run `which mysql` to verify MySQL client is available + +2. Check for cloud CLIs: + - Run `which aws` to verify AWS CLI is available + +3. Check for build tools: + - Run `which make` to verify Make is available + - Run `which gcc` to verify GCC compiler is available + +4. Check for shared libraries: + - Run `ldconfig -p | grep libssl` to verify SSL library is available + +Display the results in a summary showing which tools are available and which +are missing. If a tool is available, also show its version.