mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
Standardize on using the Write Coder on Coder template for the traiage workflow.
218 lines
9.2 KiB
YAML
218 lines
9.2 KiB
YAML
name: AI Triage Automation
|
||
|
||
on:
|
||
issues:
|
||
types:
|
||
- labeled
|
||
workflow_dispatch:
|
||
inputs:
|
||
issue_url:
|
||
description: "GitHub Issue URL to process"
|
||
required: true
|
||
type: string
|
||
template_name:
|
||
description: "Coder template to use for workspace"
|
||
required: true
|
||
default: "coder"
|
||
type: string
|
||
template_preset:
|
||
description: "Template preset to use"
|
||
required: true
|
||
default: "none"
|
||
type: string
|
||
prefix:
|
||
description: "Prefix for workspace name"
|
||
required: false
|
||
default: "traiage"
|
||
type: string
|
||
|
||
jobs:
|
||
traiage:
|
||
name: Triage GitHub Issue with Claude Code
|
||
runs-on: ubuntu-latest
|
||
if: github.event.label.name == 'traiage' || github.event_name == 'workflow_dispatch'
|
||
timeout-minutes: 30
|
||
env:
|
||
CODER_URL: ${{ secrets.TRAIAGE_CODER_URL }}
|
||
CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }}
|
||
permissions:
|
||
contents: read
|
||
issues: write
|
||
actions: write
|
||
|
||
steps:
|
||
# This is only required for testing locally using nektos/act, so leaving commented out.
|
||
# An alternative is to use a larger or custom image.
|
||
# - name: Install Github CLI
|
||
# id: install-gh
|
||
# run: |
|
||
# (type -p wget >/dev/null || (sudo apt update && sudo apt install wget -y)) \
|
||
# && sudo mkdir -p -m 755 /etc/apt/keyrings \
|
||
# && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
||
# && cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
|
||
# && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
||
# && sudo mkdir -p -m 755 /etc/apt/sources.list.d \
|
||
# && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
|
||
# && sudo apt update \
|
||
# && sudo apt install gh -y
|
||
|
||
- name: Determine Inputs
|
||
id: determine-inputs
|
||
if: always()
|
||
env:
|
||
GITHUB_ACTOR: ${{ github.actor }}
|
||
GITHUB_EVENT_ISSUE_HTML_URL: ${{ github.event.issue.html_url }}
|
||
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
||
GITHUB_EVENT_USER_ID: ${{ github.event.sender.id }}
|
||
GITHUB_EVENT_USER_LOGIN: ${{ github.event.sender.login }}
|
||
INPUTS_ISSUE_URL: ${{ inputs.issue_url }}
|
||
INPUTS_TEMPLATE_NAME: ${{ inputs.template_name || 'coder' }}
|
||
INPUTS_TEMPLATE_PRESET: ${{ inputs.template_preset || 'none'}}
|
||
INPUTS_PREFIX: ${{ inputs.prefix || 'traiage' }}
|
||
GH_TOKEN: ${{ github.token }}
|
||
run: |
|
||
echo "Using template name: ${INPUTS_TEMPLATE_NAME}"
|
||
echo "template_name=${INPUTS_TEMPLATE_NAME}" >> "${GITHUB_OUTPUT}"
|
||
|
||
echo "Using template preset: ${INPUTS_TEMPLATE_PRESET}"
|
||
echo "template_preset=${INPUTS_TEMPLATE_PRESET}" >> "${GITHUB_OUTPUT}"
|
||
|
||
echo "Using prefix: ${INPUTS_PREFIX}"
|
||
echo "prefix=${INPUTS_PREFIX}" >> "${GITHUB_OUTPUT}"
|
||
|
||
# For workflow_dispatch, use the actor who triggered it
|
||
# For issues events, use the issue author.
|
||
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
|
||
if ! GITHUB_USER_ID=$(gh api "users/${GITHUB_ACTOR}" --jq '.id'); then
|
||
echo "::error::Failed to get GitHub user ID for actor ${GITHUB_ACTOR}"
|
||
exit 1
|
||
fi
|
||
echo "Using workflow_dispatch actor: ${GITHUB_ACTOR} (ID: ${GITHUB_USER_ID})"
|
||
echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}"
|
||
echo "github_username=${GITHUB_ACTOR}" >> "${GITHUB_OUTPUT}"
|
||
|
||
echo "Using issue URL: ${INPUTS_ISSUE_URL}"
|
||
echo "issue_url=${INPUTS_ISSUE_URL}" >> "${GITHUB_OUTPUT}"
|
||
|
||
exit 0
|
||
elif [[ "${GITHUB_EVENT_NAME}" == "issues" ]]; then
|
||
GITHUB_USER_ID=${GITHUB_EVENT_USER_ID}
|
||
echo "Using issue author: ${GITHUB_EVENT_USER_LOGIN} (ID: ${GITHUB_USER_ID})"
|
||
echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}"
|
||
echo "github_username=${GITHUB_EVENT_USER_LOGIN}" >> "${GITHUB_OUTPUT}"
|
||
|
||
echo "Using issue URL: ${GITHUB_EVENT_ISSUE_HTML_URL}"
|
||
echo "issue_url=${GITHUB_EVENT_ISSUE_HTML_URL}" >> "${GITHUB_OUTPUT}"
|
||
|
||
exit 0
|
||
else
|
||
echo "::error::Unsupported event type: ${GITHUB_EVENT_NAME}"
|
||
exit 1
|
||
fi
|
||
|
||
- name: Verify push access
|
||
env:
|
||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||
GH_TOKEN: ${{ github.token }}
|
||
GITHUB_USERNAME: ${{ steps.determine-inputs.outputs.github_username }}
|
||
GITHUB_USER_ID: ${{ steps.determine-inputs.outputs.github_user_id }}
|
||
run: |
|
||
# Query the actor’s permission on this repo
|
||
can_push="$(gh api "/repos/${GITHUB_REPOSITORY}/collaborators/${GITHUB_USERNAME}/permission" --jq '.user.permissions.push')"
|
||
if [[ "${can_push}" != "true" ]]; then
|
||
echo "::error title=Access Denied::${GITHUB_USERNAME} does not have push access to ${GITHUB_REPOSITORY}"
|
||
exit 1
|
||
fi
|
||
|
||
- name: Extract context key from issue
|
||
id: extract-context
|
||
env:
|
||
ISSUE_URL: ${{ steps.determine-inputs.outputs.issue_url }}
|
||
GH_TOKEN: ${{ github.token }}
|
||
run: |
|
||
issue_number="$(gh issue view "${ISSUE_URL}" --json number --jq '.number')"
|
||
context_key="gh-${issue_number}"
|
||
echo "context_key=${context_key}" >> "${GITHUB_OUTPUT}"
|
||
echo "CONTEXT_KEY=${context_key}" >> "${GITHUB_ENV}"
|
||
|
||
- name: Download and install Coder binary
|
||
shell: bash
|
||
env:
|
||
CODER_URL: ${{ secrets.TRAIAGE_CODER_URL }}
|
||
run: |
|
||
if [ "${{ runner.arch }}" == "ARM64" ]; then
|
||
ARCH="arm64"
|
||
else
|
||
ARCH="amd64"
|
||
fi
|
||
mkdir -p "${HOME}/.local/bin"
|
||
curl -fsSL --compressed "$CODER_URL/bin/coder-linux-${ARCH}" -o "${HOME}/.local/bin/coder"
|
||
chmod +x "${HOME}/.local/bin/coder"
|
||
export PATH="$HOME/.local/bin:$PATH"
|
||
coder version
|
||
coder whoami
|
||
echo "$HOME/.local/bin" >> "${GITHUB_PATH}"
|
||
|
||
- name: Get Coder username from GitHub actor
|
||
id: get-coder-username
|
||
env:
|
||
CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }}
|
||
GH_TOKEN: ${{ github.token }}
|
||
GITHUB_USER_ID: ${{ steps.determine-inputs.outputs.github_user_id }}
|
||
run: |
|
||
user_json=$(
|
||
coder users list --github-user-id="${GITHUB_USER_ID}" --output=json
|
||
)
|
||
coder_username=$(jq -r 'first | .username' <<< "$user_json")
|
||
[[ -z "${coder_username}" || "${coder_username}" == "null" ]] && echo "No Coder user with GitHub user ID ${GITHUB_USER_ID} found" && exit 1
|
||
echo "coder_username=${coder_username}" >> "${GITHUB_OUTPUT}"
|
||
|
||
- name: Checkout repository
|
||
uses: actions/checkout@v4
|
||
with:
|
||
persist-credentials: false
|
||
fetch-depth: 0
|
||
|
||
# TODO(Cian): this is a good use-case for 'recipes'
|
||
- name: Create Coder task
|
||
id: create-task
|
||
env:
|
||
CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }}
|
||
CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }}
|
||
GH_TOKEN: ${{ github.token }}
|
||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||
ISSUE_URL: ${{ steps.determine-inputs.outputs.issue_url }}
|
||
PREFIX: ${{ steps.determine-inputs.outputs.prefix }}
|
||
RUN_ID: ${{ github.run_id }}
|
||
TEMPLATE_NAME: ${{ steps.determine-inputs.outputs.template_name }}
|
||
TEMPLATE_PARAMETERS: ${{ secrets.TRAIAGE_TEMPLATE_PARAMETERS }}
|
||
TEMPLATE_PRESET: ${{ steps.determine-inputs.outputs.template_preset }}
|
||
run: |
|
||
# Fetch issue description using `gh` CLI
|
||
#shellcheck disable=SC2016 # The template string should not be subject to shell expansion
|
||
issue_description=$(gh issue view "${ISSUE_URL}" \
|
||
--json 'title,body,comments' \
|
||
--template '{{printf "%s\n\n%s\n\nComments:\n" .title .body}}{{range $k, $v := .comments}} - {{index $v.author "login"}}: {{printf "%s\n" $v.body}}{{end}}')
|
||
|
||
# Write a prompt to PROMPT_FILE
|
||
PROMPT=$(cat <<EOF
|
||
Fix ${ISSUE_URL}
|
||
|
||
Analyze the below GitHub issue description, understand the root cause, and make appropriate changes to resolve the issue.
|
||
---
|
||
${issue_description}
|
||
EOF
|
||
)
|
||
export PROMPT
|
||
|
||
export TASK_NAME="${PREFIX}-${CONTEXT_KEY}-${RUN_ID}"
|
||
echo "Creating task: $TASK_NAME"
|
||
./scripts/traiage.sh create
|
||
if [[ "${ISSUE_URL}" == "https://github.com/${GITHUB_REPOSITORY}"* ]]; then
|
||
gh issue comment "${ISSUE_URL}" --body "Task created: https://dev.coder.com/tasks/${CODER_USERNAME}/${TASK_NAME}" --create-if-none --edit-last
|
||
else
|
||
echo "Skipping comment on other repo."
|
||
fi
|
||
echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_OUTPUT}"
|
||
echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_ENV}"
|