mirror of
https://github.com/coder/coder.git
synced 2026-11-07 21:29:37 +08:00
Now that we are creating the workspaces as the triggering user, it's more ergonomic to allow the workspace owner to review the task on completion.
186 lines
6.7 KiB
YAML
186 lines
6.7 KiB
YAML
name: AI Triage Automation
|
|
|
|
on:
|
|
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: "traiage"
|
|
type: string
|
|
template_preset:
|
|
description: "Template preset to use"
|
|
required: true
|
|
default: "Default"
|
|
type: string
|
|
prefix:
|
|
description: "Prefix for workspace name"
|
|
required: false
|
|
default: "traiage"
|
|
type: string
|
|
cleanup:
|
|
description: "Cleanup workspace after triage."
|
|
required: false
|
|
default: false
|
|
type: boolean
|
|
|
|
jobs:
|
|
traiage:
|
|
name: Triage GitHub Issue with Claude Code
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
env:
|
|
CODER_URL: ${{ secrets.TRAIAGE_CODER_URL }}
|
|
CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }}
|
|
TEMPLATE_NAME: ${{ inputs.template_name }}
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
actions: write
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
persist-credentials: false
|
|
fetch-depth: 0
|
|
|
|
- name: Extract context key from issue
|
|
id: extract-context
|
|
env:
|
|
ISSUE_URL: ${{ inputs.issue_url }}
|
|
GITHUB_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 }}
|
|
GITHUB_USER_ID: ${{
|
|
(github.event_name == 'workflow_dispatch' && github.actor_id)
|
|
}}
|
|
run: |
|
|
[[ -z "${GITHUB_USER_ID}" || "${GITHUB_USER_ID}" == "null" ]] && echo "No GitHub actor ID found" && exit 1
|
|
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}"
|
|
|
|
# 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 }}
|
|
GITHUB_TOKEN: ${{ github.token }}
|
|
ISSUE_URL: ${{ inputs.issue_url }}
|
|
PREFIX: ${{ inputs.prefix }}
|
|
RUN_ID: ${{ github.run_id }}
|
|
TEMPLATE_PARAMETERS: ${{ secrets.TRAIAGE_TEMPLATE_PARAMETERS }}
|
|
TEMPLATE_PRESET: ${{ inputs.template_preset }}
|
|
run: |
|
|
# Fetch issue description using `gh` CLI
|
|
issue_description=$(gh issue view "${ISSUE_URL}")
|
|
|
|
# Write a prompt to PROMPT_FILE
|
|
PROMPT=$(cat <<EOF
|
|
Analyze the below GitHub issue description, understand the root cause, and make appropriate changes to resolve the issue.
|
|
|
|
ISSUE URL: ${ISSUE_URL}
|
|
ISSUE DESCRIPTION BELOW:
|
|
|
|
${issue_description}
|
|
EOF
|
|
)
|
|
export PROMPT
|
|
|
|
export TASK_NAME="${PREFIX}-${CONTEXT_KEY}-${RUN_ID}"
|
|
echo "Creating task: $TASK_NAME"
|
|
./scripts/traiage.sh create
|
|
coder exp task status "${CODER_USERNAME}/$TASK_NAME" --watch
|
|
echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_OUTPUT}"
|
|
echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_ENV}"
|
|
|
|
- name: Create and upload archive
|
|
id: create-archive
|
|
if: inputs.cleanup
|
|
env:
|
|
BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage"
|
|
run: |
|
|
echo "Creating archive for workspace: $TASK_NAME"
|
|
./scripts/traiage.sh archive
|
|
echo "archive_url=${BUCKET_PREFIX%%/}/$TASK_NAME.tar.gz" >> "${GITHUB_OUTPUT}"
|
|
|
|
- name: Generate a summary of the changes and post a comment on GitHub.
|
|
id: generate-summary
|
|
if: inputs.cleanup
|
|
env:
|
|
ARCHIVE_URL: ${{ steps.create-archive.outputs.archive_url }}
|
|
BUCKET_PREFIX: "gs://coder-traiage-outputs/traiage"
|
|
CONTEXT_KEY: ${{ steps.extract-context.outputs.context_key }}
|
|
GITHUB_TOKEN: ${{ github.token }}
|
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
ISSUE_URL: ${{ inputs.issue_url }}
|
|
TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }}
|
|
run: |
|
|
SUMMARY_FILE=$(mktemp)
|
|
trap 'rm -f "${SUMMARY_FILE}"' EXIT
|
|
AUTO_SUMMARY=$(./scripts/traiage.sh summary)
|
|
{
|
|
echo "## TrAIage Results"
|
|
echo "- **Issue URL:** ${ISSUE_URL}"
|
|
echo "- **Context Key:** ${CONTEXT_KEY}"
|
|
echo "- **Workspace:** ${TASK_NAME}"
|
|
echo "- **Archive URL:** ${ARCHIVE_URL}"
|
|
echo
|
|
echo "${AUTO_SUMMARY}"
|
|
echo
|
|
echo "To fetch the output to your own workspace:"
|
|
echo
|
|
echo '```bash'
|
|
echo "BUCKET_PREFIX=${BUCKET_PREFIX} TASK_NAME=${TASK_NAME} ./scripts/traiage.sh resume"
|
|
echo '```'
|
|
echo
|
|
} >> "${SUMMARY_FILE}"
|
|
|
|
if [[ "${ISSUE_URL}" == "https://github.com/${GITHUB_REPOSITORY}"* ]]; then
|
|
gh issue comment "${ISSUE_URL}" --body-file "${SUMMARY_FILE}" --create-if-none --edit-last
|
|
else
|
|
echo "Skipping comment on other repo."
|
|
fi
|
|
cat "${SUMMARY_FILE}" >> "${GITHUB_STEP_SUMMARY}"
|
|
|
|
- name: Cleanup task
|
|
if: inputs.cleanup && steps.create-task.outputs.TASK_NAME != '' && steps.create-archive.outputs.archive_url != ''
|
|
run: |
|
|
echo "Cleaning up task: $TASK_NAME"
|
|
./scripts/traiage.sh delete || true
|