act icon indicating copy to clipboard operation
act copied to clipboard

gh command not found

Open GeorgeXCV opened this issue 9 months ago • 1 comments

Bug report info

WARN  ⚠ You are using Apple M-series chip and you have not specified container architecture, you might encounter issues while running act. If so, try running it with '--container-architecture linux/amd64'. ⚠  
[Generate PR Dashboard for Slack/generate_dashboard] 🚀  Start image=catthehacker/ubuntu:act-latest
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[Generate PR Dashboard for Slack/generate_dashboard] using DockerAuthConfig authentication for docker pull
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/***/null"] cmd=[] network="host"
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/***/null"] cmd=[] network="host"
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[Generate PR Dashboard for Slack/generate_dashboard] ⭐ Run Main Generate PR Dashboard for Slack
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/generate_dashboard] user= workdir=
| /var/run/act/workflow/generate_dashboard: line 3: gh: command not found
[Generate PR Dashboard for Slack/generate_dashboard]   ❌  Failure - Main Generate PR Dashboard for Slack
[Generate PR Dashboard for Slack/generate_dashboard] exitcode '127': command not found, please refer to https://github.com/nektos/act/issues/107 for more information
[Generate PR Dashboard for Slack/generate_dashboard] 🏁  Job failed
Error: Job 'generate_dashboard' failed

Command used with act

act -j generate_dashboard --env-file .env -P ubuntu-latest=catthehacker/ubuntu:act-latest

Describe issue

Same error all the time, tried a million docker images, tried adding install to workflow itself and get more errors.

Link to GitHub repository

No response

Workflow content

name: Generate PR Dashboard for Slack

on:
  schedule:
    - cron: '0 9 * * 1-5'  # Run at 9 AM Monday-Friday
  workflow_dispatch:  # Allow manual triggering

jobs:
  generate_dashboard:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: read
      issues: read
    
    steps:
      - name: Generate PR Dashboard for Slack
        id: generate_dashboard
        run: |
          # Get all open PRs
          gh pr list --json number,title,author,createdAt,updatedAt,reviewDecision,isDraft,labels,url --limit 100 > prs.json
          
          # Process the PR data to include author's real name
          jq '[ .[] | {
            number: .number,
            title: .title,
            author: .author.login,
            authorName: .author.login,
            reviewDecision: .reviewDecision,
            isDraft: .isDraft,
            createdAt: .createdAt,
            updatedAt: .updatedAt,
            labels: .labels,
            url: .url
          }]' prs.json > processed_prs.json
          
          # Fetch real names for each unique author
          jq -r '.[] | .author.login' prs.json | sort -u > authors.txt
          while read -r author; do
            # Get user info including real name
            user_info=$(gh api users/$author)
            name=$(echo "$user_info" | jq -r '.name // ""')
            
            # If real name exists, update the author name in the processed data
            if [ -n "$name" ] && [ "$name" != "null" ]; then
              jq --arg author "$author" --arg name "$name" '[ .[] | if .author == $author then .authorName = $name else . end ]' processed_prs.json > temp.json
              mv temp.json processed_prs.json
            fi
          done < authors.txt
          
          # Use the processed data for the rest of the script
          mv processed_prs.json prs.json
          
          # Summary statistics
          TOTAL_PRS=$(jq '. | length' prs.json)
          APPROVED_PRS=$(jq '[.[] | select(.reviewDecision == "APPROVED")] | length' prs.json)
          PENDING_PRS=$(jq '[.[] | select(.reviewDecision == "REVIEW_REQUIRED" and .isDraft == false)] | length' prs.json)
          CHANGES_PRS=$(jq '[.[] | select(.reviewDecision == "CHANGES_REQUESTED")] | length' prs.json)
          DRAFT_PRS=$(jq '[.[] | select(.isDraft == true)] | length' prs.json)
          
          # Create Slack message blocks
          cat > slack_payload.json << EOF
          {
            "blocks": [
              {
                "type": "header",
                "text": {
                  "type": "plain_text",
                  "text": "📊 PR Dashboard - $(date '+%A, %B %d')",
                  "emoji": true
                }
              },
              {
                "type": "section",
                "fields": [
                  {
                    "type": "mrkdwn",
                    "text": "*Total PRs:* $TOTAL_PRS"
                  },
                  {
                    "type": "mrkdwn",
                    "text": "*Approved:* $APPROVED_PRS"
                  },
                  {
                    "type": "mrkdwn",
                    "text": "*Needs Review:* $PENDING_PRS"
                  },
                  {
                    "type": "mrkdwn",
                    "text": "*Changes Requested:* $CHANGES_PRS"
                  }
                ]
              },
              {
                "type": "divider"
              }
            ]
          }
          EOF
          
          # Add PRs waiting for review section if there are any
          if [ "$PENDING_PRS" -gt 0 ]; then
            # Add section header
            jq '.blocks += [{
              "type": "section",
              "text": {
                "type": "mrkdwn",
                "text": "*⏳ PRs Waiting for Review*"
              }
            }]' slack_payload.json > temp.json && mv temp.json slack_payload.json
            
            # Get PRs waiting for review, sorted by age
            jq -c '.[] | select(.reviewDecision == "REVIEW_REQUIRED" and .isDraft == false) | {
              number: .number,
              title: .title,
              author: .author,
              authorName: .authorName,
              created: .createdAt,
              url: .url,
              age: ((now - (.createdAt | fromdate)) / 86400) | floor
            }' prs.json | jq -s 'sort_by(-.age)' > pending_prs.json
            
            # Add each PR as a separate section (up to 5)
            jq -c '.[:5] | .[]' pending_prs.json | while read -r pr; do
              PR_NUMBER=$(echo "$pr" | jq -r '.number')
              PR_TITLE=$(echo "$pr" | jq -r '.title')
              PR_AUTHOR=$(echo "$pr" | jq -r '.author')
              PR_AGE=$(echo "$pr" | jq -r '.age')
              PR_URL=$(echo "$pr" | jq -r '.url')
              
              # Add emoji based on age
              AGE_EMOJI="🟢"
              if [ "$PR_AGE" -gt 3 ]; then
                AGE_EMOJI="🟠"
              fi
              if [ "$PR_AGE" -gt 5 ]; then
                AGE_EMOJI="🔴"
              fi
              
              jq '.blocks += [{
                "type": "section",
                "text": {
                  "type": "mrkdwn",
                  "text": "'"$AGE_EMOJI"' <'"$PR_URL"'|#'"$PR_NUMBER"': '"${PR_TITLE//\"/\\\"}"'> by '"$PR_AUTHOR"'\n_Waiting for '"$PR_AGE"' days_"
                }
              }]' slack_payload.json > temp.json && mv temp.json slack_payload.json
            done
            
            # Add divider
            jq '.blocks += [{
              "type": "divider"
            }]' slack_payload.json > temp.json && mv temp.json slack_payload.json
          fi
          
          # Add PRs with changes requested section if there are any
          if [ "$CHANGES_PRS" -gt 0 ]; then
            # Add section header
            jq '.blocks += [{
              "type": "section",
              "text": {
                "type": "mrkdwn",
                "text": "*🔄 PRs with Changes Requested*"
              }
            }]' slack_payload.json > temp.json && mv temp.json slack_payload.json
            
            # Get PRs with changes requested, sorted by age
            jq -c '.[] | select(.reviewDecision == "CHANGES_REQUESTED") | {
              number: .number,
              title: .title,
              author: .author.login,
              updated: .updatedAt,
              url: .url,
              age: ((now - (.updatedAt | fromdate)) / 86400) | floor
            }' prs.json | jq -s 'sort_by(-.age)' > changes_prs.json
            
            # Add each PR as a separate section (up to 5)
            jq -c '.[:5] | .[]' changes_prs.json | while read -r pr; do
              PR_NUMBER=$(echo "$pr" | jq -r '.number')
              PR_TITLE=$(echo "$pr" | jq -r '.title')
              PR_AUTHOR=$(echo "$pr" | jq -r '.author')
              PR_AGE=$(echo "$pr" | jq -r '.age')
              PR_URL=$(echo "$pr" | jq -r '.url')
              
              # Add emoji based on age
              AGE_EMOJI="🟢"
              if [ "$PR_AGE" -gt 3 ]; then
                AGE_EMOJI="🟠"
              fi
              if [ "$PR_AGE" -gt 5 ]; then
                AGE_EMOJI="🔴"
              fi
              
              jq '.blocks += [{
                "type": "section",
                "text": {
                  "type": "mrkdwn",
                  "text": "'"$AGE_EMOJI"' <'"$PR_URL"'|#'"$PR_NUMBER"': '"${PR_TITLE//"/\\\"}\"`"'> by '"$PR_AUTHOR"'\n_Changes requested '"$PR_AGE"' days ago_"
                }
              }]' slack_payload.json > temp.json && mv temp.json slack_payload.json
            done
            
            # Add divider
            jq '.blocks += [{
              "type": "divider"
            }]' slack_payload.json > temp.json && mv temp.json slack_payload.json
          fi
          
          # Add footer with link to all PRs
          jq '.blocks += [{
            "type": "context",
            "elements": [{
              "type": "mrkdwn",
              "text": "<https://github.com/${{ github.repository }}/pulls|View all open PRs> • <https://github.com/${{ github.repository }}/pulls?q=is%3Apr+is%3Aopen+review%3Arequired|Needs review> • <https://github.com/${{ github.repository }}/pulls?q=is%3Apr+is%3Aopen+review%3Achanges_requested|Changes requested>"
            }]
          }]' slack_payload.json > temp.json && mv temp.json slack_payload.json
          
          # Send to Slack
          curl -X POST \
            -H 'Content-type: application/json' \
            --data @slack_payload.json \
            ${{ secrets.SLACK_WEBHOOK_CHANNEL }}
          
          echo "Dashboard sent to #test Slack channel"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_CHANNEL }}

Relevant log output

WARN  ⚠ You are using Apple M-series chip and you have not specified container architecture, you might encounter issues while running act. If so, try running it with '--container-architecture linux/amd64'. ⚠  
[Generate PR Dashboard for Slack/generate_dashboard] 🚀  Start image=catthehacker/ubuntu:act-latest
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[Generate PR Dashboard for Slack/generate_dashboard] using DockerAuthConfig authentication for docker pull
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/***/null"] cmd=[] network="host"
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/***/null"] cmd=[] network="host"
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[Generate PR Dashboard for Slack/generate_dashboard] ⭐ Run Main Generate PR Dashboard for Slack
[Generate PR Dashboard for Slack/generate_dashboard]   🐳  docker exec cmd=[bash --noprofile --norc -e -o pipefail /var/run/act/workflow/generate_dashboard] user= workdir=
| /var/run/act/workflow/generate_dashboard: line 3: gh: command not found
[Generate PR Dashboard for Slack/generate_dashboard]   ❌  Failure - Main Generate PR Dashboard for Slack
[Generate PR Dashboard for Slack/generate_dashboard] exitcode '127': command not found, please refer to https://github.com/nektos/act/issues/107 for more information
[Generate PR Dashboard for Slack/generate_dashboard] 🏁  Job failed
Error: Job 'generate_dashboard' failed

Additional information

No response

GeorgeXCV avatar Mar 17 '25 05:03 GeorgeXCV

act images don't include some very basic tools, including gh, jq, gcloud cli, etc. You can pull the much larger catthehacker images, or install just what you need on local images you build yourself. I did that here: https://github.com/nektos/act/issues/1799

mcascone avatar Apr 10 '25 16:04 mcascone