Skip to content

BASH_MAX_TIMEOUT_MS and BASH_DEFAULT_TIMEOUT_MS not taken into consideration #592

Description

@guidocelada

Describe the bug
anthropics/claude-code-action does not pass the BASH_MAX_TIMEOUT_MS and BASH_DEFAULT_TIMEOUT_MS environment variable from the job environment into the Claude Code execution environment. As a result, Bash tool timeouts ignore the value set at the workflow/job level (defaults to 2 minutes).

To Reproduce

  1. Define BASH_MAX_TIMEOUT_MS at the job level (or step env:) to a large value.
  2. Run a Claude Code step that executes a long-running Bash command.
  3. Observe that the command still times out at the default limit rather than using the provided value.

Expected behavior
Claude’s Bash tool should see BASH_MAX_TIMEOUT_MS=900000 in its environment and allow commands to run up to 15 minutes before timing out. Same for BASH_DEFAULT_TIMEOUT_MS

Screenshots
N/A

Workflow yml file

# This action deletes feature toggles from multiple repositories using AI (Claude Code).
# It creates a pull request in each repository to remove the specified feature toggle branch.
# Learn more: https://docs.google.com/document/d/1oX2n-fxIUpPzIyVl_GbLdnD_FHiw06bTjH1IGHcE8N8/edit?tab=t.0

name: Feature Toggle Slayer - Claude Code

on:
  workflow_dispatch:
    inputs:
      repos_json:
        description: |
          A JSON array with repository configurations for feature toggle removal.
          
          Example: 
          [
            { "repo": "Glovo/repo1", "toggle_name": "SHOULD_WORK", "toggle_value_to_delete": false },
            { "repo": "Glovo/repo2", "toggle_name": "SHOULD_SHOW", "toggle_value_to_delete": true }
          ]
        required: true
        type: string

jobs:
  feature-toggle-slayer:

    runs-on: glovo-shared-small-arm
    timeout-minutes: 150  # 2 hours and a half

    permissions:
      contents: write
      pull-requests: write
      id-token: write
      actions: read

    strategy:
      fail-fast: false
      matrix:
        include: ${{ fromJson(inputs.repos_json) }}

    steps:
      - name: Generate GitHub token
        id: generate_github_token
        uses: actions/create-github-app-token@v2
        with:
          app-id: ${{ secrets.GLOVO_GH_ACTIONS_APP_ID }}
          private-key: ${{ secrets.GLOVO_GH_ACTIONS_PRIVATE_KEY }}
          owner: Glovo

      - name: Checkout ${{ matrix.repo }}
        uses: actions/checkout@v4
        with:
          repository: ${{ matrix.repo }}
          token: ${{ steps.generate_github_token.outputs.token }}

      - uses: Glovo/secrets-to-env-action@v1.6
        name: Load Secrets on envs
        id: load-secrets
        with:
          secrets: ${{ toJSON(secrets) }}

      - name: Login to Artifactory docker-hub remote repository
        uses: docker/login-action@v3
        with:
          registry: glovo-docker.artifactory.glovoint.com/
          username: ${{ secrets.ARTIFACTORY_DOCKER_USERNAME }}
          password: ${{ secrets.ARTIFACTORY_DOCKER_PASSWORD }}

      - name: Set up JDK for Gradle
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'
          cache: 'gradle'

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v4
        with:
          gradle-home-cache-cleanup: true
          cache-read-only: false

      - name: Setup environment for Gradle
        run: |
          echo 'GRADLE_OPTS=-Dorg.gradle.jvmargs="-Xmx2g -XX:+UseParallelGC" -Dorg.gradle.daemon=false' >> $GITHUB_ENV
          echo 'CI=true' >> $GITHUB_ENV
          echo 'TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX=glovo-docker.artifactory.glovoint.com/' >> $GITHUB_ENV
          echo 'ORG_GRADLE_PROJECT_artifactory_user=${{ secrets.ARTIFACTORY_USERNAME }}' >> $GITHUB_ENV
          echo 'ORG_GRADLE_PROJECT_artifactory_key=${{ secrets.ARTIFACTORY_PASSWORD }}' >> $GITHUB_ENV

      - name: Ensure gradlew is executable and show version
        run: |
          if [ -f ./gradlew ]; then chmod +x ./gradlew; fi
          ./gradlew --version || true

      - name: Run Claude Code AI to delete feature toggle
        env:
          ANTHROPIC_BASE_URL: ${{ vars.ANTHROPIC_BASE_URL }}
          ANTHROPIC_MODEL: ${{ vars.ANTHROPIC_MODEL }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ steps.generate_github_token.outputs.token }}
          BASH_DEFAULT_TIMEOUT_MS: "7200000" # 120 minutes
          BASH_MAX_TIMEOUT_MS: "7200000" # 120 minutes
        id: claude
        uses: anthropics/claude-code-action@v1
        with:
          github_token: ${{ steps.generate_github_token.outputs.token }}
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          use_commit_signing: true
          claude_args: --model ${{ vars.ANTHROPIC_MODEL }} --allowedTools Bash,Edit,MultiEdit,Read,Task,TodoWrite,Write --permission-mode acceptEdits --verbose
          prompt: |
            Your task is to remove a feature toggle from the codebase. 
            Remove all code that executes when the toggle equals ${{ matrix.toggle_value_to_delete }}, keeping only the opposite branch.
            
            Context:
              Toggle uses FeatureToggleClient#getBooleanValue("${{ matrix.toggle_name }}")
            
            Instructions:
              Find the toggle: Search for all occurrences of ${{ matrix.toggle_name }} in the codebase. Look for conditional statements using this toggle.
              Remove the toggle and keep correct path.
              
              For each conditional checking the toggle, remove the conditional and the ${{ matrix.toggle_value_to_delete }} branch
              Keep only the code that executes when the toggle has the opposite value
              Ensure remaining code is properly indented and integrated
              Do not add any new comments to the code.
            
              I cannot do anything locally, so you must provide exact code changes that do not require any manual intervention from me.
              Do not use sub-agents.
            
            Clean up:
              Remove unused imports, variables, or helper functions only used by the toggle logic.
            
            Verify:
              Confirm all instances of ${{ matrix.toggle_name }} are removed
              Ensure no other toggles were accidentally modified
              Check that code logic remains valid
            
            IMPORTANT: Invariant (must not be violated): You must always validate that the code compiles, tests and 
            integration test pass (usually build test intTest).
            Try to run these only on the modified modules / classes so that we have faster feedback loops. 
            If the commands takes too long, you need to wait for the completion. It's very important that this is considered. 
            If any of these steps fail, fix the issues before completing and then re-verify until all pass.
            Keep output concise: always use `--console=plain --quiet` so only errors are printed.
            On failure, re-run ONLY the failing tests / integration tests.
            After code compiles, test and integration test passes, ensure the modified code is propely formatted by running 
            spotlessApply.

      - name: Extract Claude execution metrics
        id: claude_metrics
        run: |
          set -euo pipefail

          # Find the output file
          if [[ -f "/runner/_work/_temp/claude-execution-output.json" ]]; then
            OUTPUT_FILE="/runner/_work/_temp/claude-execution-output.json"
          else
            printf '%s' '${{ steps.claude.outputs.result }}' > /tmp/claude_output.json
            OUTPUT_FILE="/tmp/claude_output.json"
          fi

          # Extract all metrics with safe defaults
          echo "is_error=$(jq -r '.is_error // false' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "status=$(jq -r '.subtype // .type // "unknown"' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "total_cost=$(jq -r '.total_cost_usd // 0' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "duration_ms=$(jq -r '.duration_ms // 0' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "api_duration_ms=$(jq -r '.duration_api_ms // 0' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "num_turns=$(jq -r '.num_turns // 0' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "input_tokens=$(jq -r '.usage.input_tokens // 0' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "output_tokens=$(jq -r '.usage.output_tokens // 0' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "cache_creation_tokens=$(jq -r '.usage.cache_creation_input_tokens // 0' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "cache_read_tokens=$(jq -r '.usage.cache_read_input_tokens // 0' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "service_tier=$(jq -r '.usage.service_tier // "standard"' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "web_searches=$(jq -r '.usage.server_tool_use.web_search_requests // 0' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"
          echo "session_id=$(jq -r '.session_id // "N/A"' "$OUTPUT_FILE")" >> "$GITHUB_OUTPUT"

          # Calculate total tokens
          TOTAL_TOKENS=$(jq -r '
            (.usage.input_tokens // 0) + 
            (.usage.output_tokens // 0) + 
            (.usage.cache_creation_input_tokens // 0) + 
            (.usage.cache_read_input_tokens // 0)
          ' "$OUTPUT_FILE")
          echo "total_tokens=$TOTAL_TOKENS" >> "$GITHUB_OUTPUT"

          # Calculate cost per 1k tokens
          COST_PER_1K=$(jq -r '
            if (.total_cost_usd // 0) > 0 and 
               ((.usage.input_tokens // 0) + (.usage.output_tokens // 0) + 
                (.usage.cache_creation_input_tokens // 0) + (.usage.cache_read_input_tokens // 0)) > 0
            then 
              ((.total_cost_usd // 0) * 1000) / 
              ((.usage.input_tokens // 0) + (.usage.output_tokens // 0) + 
               (.usage.cache_creation_input_tokens // 0) + (.usage.cache_read_input_tokens // 0))
            else 0 end
          ' "$OUTPUT_FILE")
          echo "cost_per_1k_tokens=$COST_PER_1K" >> "$GITHUB_OUTPUT"

          # Extract the result (multiline, so use EOF delimiter)
          echo "result<<EOF" >> "$GITHUB_OUTPUT"
          jq -r '.result // "No summary available"' "$OUTPUT_FILE" >> "$GITHUB_OUTPUT"
          echo "EOF" >> "$GITHUB_OUTPUT"

      - name: Generate GitHub token as it might be expired
        id: generate_github_token_2
        uses: actions/create-github-app-token@v2
        with:
          app-id: ${{ secrets.GLOVO_GH_ACTIONS_APP_ID }}
          private-key: ${{ secrets.GLOVO_GH_ACTIONS_PRIVATE_KEY }}
          owner: Glovo

      - name: Create PR
        id: cpr
        uses: peter-evans/create-pull-request@v7
        with:
          token: ${{ steps.generate_github_token_2.outputs.token }}
          commit-message: 'Remove feature toggle `${{ matrix.toggle_name }}`'
          committer: Claude Code[bot] <claudio@anthropics.com>
          author: Claude Code[bot] <claudio@anthropics.com>
          title: 'Remove feature toggle `${{ matrix.toggle_name }}`'
          labels: feature-toggle-slayer
          branch: feature-toggle-slayer/claude/${{ matrix.toggle_name }}-removal
          add-paths: '**/src/**'
          body: |
            ## 📝 Summary
            This pull request removes the feature toggle `${{ matrix.toggle_name }}` from the codebase, deleting all code paths where the toggle equals `${{ matrix.toggle_value_to_delete }}`.
            
            > [!WARNING]
            > This pull request was created by Claude Code AI agent using ${{ vars.ANTHROPIC_MODEL }} 🤖 (_[see workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})_), please review carefully and merge or reject as you see fit.
            > *It is your responsability as a developer to monitor the deployment in case you merge the changes*.
            
            ## 🤖 Claude Code Summary
            ${{ steps.claude_metrics.outputs.result }}
                        
            ### 💰 Cost Analysis
            | Metric | Value |
            |--------|--------|
            | **Total Cost** | **${{ steps.claude_metrics.outputs.total_cost }} USD** |
            | **Cost per 1K tokens** | ${{ steps.claude_metrics.outputs.cost_per_1k_tokens }} USD |
            | **Service Tier** | ${{ steps.claude_metrics.outputs.service_tier }} |
            | **Status** | ${{ steps.claude_metrics.outputs.status }} ✅ |
            
            ### 🔢 Token Usage
            | Token Type | Count | Purpose |
            |------------|-------|---------|
            | **Input Tokens** | ${{ steps.claude_metrics.outputs.input_tokens }} | New content processed |
            | **Output Tokens** | ${{ steps.claude_metrics.outputs.output_tokens }} | Generated by Claude |
            | **Cache Creation** | ${{ steps.claude_metrics.outputs.cache_creation_tokens }} | Built context cache |
            | **Cache Read** | ${{ steps.claude_metrics.outputs.cache_read_tokens }} | Read from cache (cheaper) |
            | **Total Tokens** | ${{ steps.claude_metrics.outputs.total_tokens }} | Combined usage |
            
            ### ⚡ Performance
            | Metric | Value |
            |--------|--------|
            | **Total Duration** | ${{ steps.claude_metrics.outputs.duration_ms }}ms |
            | **API Calls Duration** | ${{ steps.claude_metrics.outputs.api_duration_ms }}ms |
            | **Conversation Turns** | ${{ steps.claude_metrics.outputs.num_turns }} |
            | **Web Searches** | ${{ steps.claude_metrics.outputs.web_searches }} |
            
            <details>
            <summary>🔍 Technical Details</summary>
            
            - **Session ID**: `${{ steps.claude_metrics.outputs.session_id }}`
            - **Workflow Run**: [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
            - **Triggered by**: @${{ github.actor }}
            - **Repository**: ${{ github.repository }}
            - **Branch**: `${{ github.ref_name }}`
            
            </details>

      - name: PR URL
        if: steps.cpr.outputs.pull-request-url != ''
        run: echo "PR => ${{ steps.cpr.outputs.pull-request-url }}"

API Provider

LiteLLM

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions