Compare commits

...

13 Commits

Author SHA1 Message Date
default a233591abd refactor: use gh pr comment --edit-last for updates
Implement Cian's suggestion to use --create-if-none and --edit-last
flags instead of manually tracking comment IDs. This simplifies the
workflow and ensures only one comment is created/updated per PR.

Benefits:
- Simpler code (no need to track COMMENT_ID)
- No need to fetch and preserve original comment
- Guarantees single comment per PR
- Cleaner PR comment history
2025-10-16 13:38:45 +00:00
default 51df125f6b fix: remove task cleanup to preserve task logs
Remove the automatic task deletion step to match traiage workflow
behavior. This allows users to review the task logs after the
workflow completes, since the results are already posted to the
PR comment.
2025-10-15 21:57:28 +00:00
default 06dd555020 fix: use stable task name for documentation checks
Use PR number only (not RUN_ID) for task naming to ensure
consistent task reuse across workflow re-runs. This prevents
mismatched task URLs in PR comments and allows the task to
accumulate conversation history for the same PR.
2025-10-15 21:47:17 +00:00
default 5beb53cdbb feat: add intermediate comment update with task link
- Update comment immediately after task creation
- Shows 'Analysis in progress...' with task link
- Allows users to follow along in real-time
- Three-step progression: Started -> In Progress (with link) -> Complete
2025-10-15 21:40:11 +00:00
default 89cfc5e427 feat: preserve original comment content when updating with results
- Fetch original comment before updating
- Append results below a horizontal divider
- Keeps 'Started' message visible for full timeline
- Add emojis to metadata (⏱️ 📋 🔗) for better visual separation
2025-10-15 21:34:20 +00:00
default 58be1b373c feat: extract only final analysis using delimiter markers
- Add clear delimiter markers (---DOCUMENTATION-ANALYSIS-START/END---) to prompt
- Update script to extract only content between markers
- Provides cleaner output: just the bulleted recommendations or 'no changes needed'
- Removes all the tool calls, debugging, and conversation from PR comment
- Falls back to task link if markers not found
2025-10-15 21:31:20 +00:00
default bfbe7d8dfd feat: add execution time tracking to documentation check
- Track start/end time of task execution
- Calculate and format duration (minutes and seconds)
- Display execution time in final PR comment with clock emoji
- Helps users understand task performance
2025-10-15 21:24:46 +00:00
default 61af44bf1f refactor: simplify prompt to reference PR instead of embedding full diff
- Only pass PR URL to task instead of full diff, title, body, files
- Instructs Claude Code to fetch PR details using GitHub CLI/API
- Prevents token limit issues with large PRs
- Reduces workflow complexity and execution time
- Claude Code in task has full access to GitHub tools anyway
2025-10-15 21:16:14 +00:00
default 5e4cc71133 fix: correct YAML indentation in heredoc blocks
- All heredoc content must be indented to match bash script context
- Prevents YAML parser from interpreting bash script content as YAML keys
- Validates successfully with PyYAML parser
2025-10-15 21:07:37 +00:00
default b0b3decb1b fix: use consistent multi-line string formatting throughout workflow
- Replace heredoc with simple quoted strings for comment bodies
- Ensures proper variable expansion for GITHUB_REPOSITORY and RUN_ID
- Maintains consistency with bash best practices in GitHub Actions
2025-10-15 21:04:18 +00:00
default 2ff5fd713d refactor: align workflow structure with triage pattern
- Add comprehensive 'Determine Inputs' step matching triage workflow
- Extract GitHub user ID lookup to separate step
- Update all step references to use determine-inputs outputs
- Fix heredoc formatting to properly handle backticks and variable expansion
- Maintain PR comment posting flow (initial + update)
2025-10-15 21:02:13 +00:00
default c20d661613 fix: use heredoc syntax for multi-line strings in workflow
Fixes YAML validation errors by using proper heredoc syntax (cat <<EOF)
instead of raw multi-line strings in bash run blocks.
2025-10-15 20:55:59 +00:00
default 26204a6b5d feat: add AI documentation check workflow for pull requests
This workflow automatically analyzes PRs to identify documentation updates needed.

Features:
- Triggers on PR opened/synchronize/reopened events
- Posts initial comment when starting analysis
- Creates Coder task to analyze code changes against existing docs
- Identifies updates needed, deprecated docs, and new docs required
- Updates PR comment with actionable documentation recommendations

Includes:
- .github/workflows/documentation-check.yaml: GitHub Actions workflow
- scripts/documentation-check.sh: Helper script for task management
2025-10-15 20:50:50 +00:00
2 changed files with 457 additions and 0 deletions
+310
View File
@@ -0,0 +1,310 @@
name: AI Documentation Updates Automation
on:
pull_request:
types:
- opened
- synchronize
- reopened
workflow_dispatch:
inputs:
pr_number:
description: "Pull Request number to process"
required: true
type: number
jobs:
documentation-check:
name: Check Documentation Updates 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 }}
permissions:
contents: read
pull-requests: write
actions: write
steps:
- name: Determine Inputs
id: determine-inputs
if: always()
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_EVENT_USER_ID: ${{ github.event.sender.id }}
GITHUB_EVENT_USER_LOGIN: ${{ github.event.sender.login }}
INPUTS_PR_NUMBER: ${{ inputs.pr_number }}
GH_TOKEN: ${{ github.token }}
run: |
# For workflow_dispatch, use the actor who triggered it
# For pull_request events, use the PR 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 PR number: ${INPUTS_PR_NUMBER}"
echo "pr_number=${INPUTS_PR_NUMBER}" >> "${GITHUB_OUTPUT}"
exit 0
elif [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
GITHUB_USER_ID=${GITHUB_EVENT_USER_ID}
echo "Using PR 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 PR number: ${GITHUB_EVENT_PR_NUMBER}"
echo "pr_number=${GITHUB_EVENT_PR_NUMBER}" >> "${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: Post initial comment
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ steps.determine-inputs.outputs.pr_number }}
GITHUB_REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
run: |
COMMENT_BODY=$(cat <<EOF
🤖 **Documentation Check Started**
Analyzing PR changes to determine if documentation updates are needed...
[View workflow run](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID})
EOF
)
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" --repo "${GITHUB_REPOSITORY}" --create-if-none --edit-last
- 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
- name: Create Coder task for documentation analysis
id: create-task
env:
CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }}
GH_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ steps.determine-inputs.outputs.pr_number }}
RUN_ID: ${{ github.run_id }}
TEMPLATE_NAME: "traiage"
TEMPLATE_PRESET: "Default"
run: |
# Fetch PR details using `gh` CLI
pr_json=$(gh pr view "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json 'url')
pr_url=$(echo "${pr_json}" | jq -r '.url')
# Build comprehensive prompt
PROMPT=$(cat <<'EOF'
Analyze PR ${pr_url} for documentation updates.
## Task Description
You are tasked with analyzing code changes in a pull request and determining what documentation updates are needed. Please:
1. Use the GitHub CLI or API to fetch the PR details, including:
- PR title and description
- List of files changed
- The actual code diff
2. Review the PR description and code changes to understand what was changed
3. Examine the existing documentation in the docs/ directory
4. Identify any of the following needs:
- Updates required to existing documentation
- Documentation that needs to be deprecated
- New documentation that should be added
5. Provide a clear, actionable list of documentation changes needed
## PR to Analyze
${pr_url}
## IMPORTANT: Final Output Format
After completing your analysis, you MUST end your response with ONLY the following formatted output between the markers:
---DOCUMENTATION-ANALYSIS-START---
[Your concise bulleted list of recommendations OR "No documentation changes needed"]
---DOCUMENTATION-ANALYSIS-END---
The content between these markers should be:
- A simple bulleted list with links to docs that need updating
- OR a single sentence: "No documentation changes needed - [brief reason]"
- Maximum 10-15 lines
- Use markdown formatting
- Include file paths as links when referencing docs
Example format:
---DOCUMENTATION-ANALYSIS-START---
### Documentation Updates Needed
- **Update** [docs/admin/templates.md](docs/admin/templates.md) - Add new parameter documentation
- **Create** docs/guides/new-feature.md - Document the new feature workflow
- **Deprecate** docs/old-feature.md - Feature has been removed
---DOCUMENTATION-ANALYSIS-END---
OR:
---DOCUMENTATION-ANALYSIS-START---
No documentation changes needed - This PR only contains internal refactoring with no user-facing changes.
---DOCUMENTATION-ANALYSIS-END---
EOF
)
# Expand variables in the prompt
PROMPT=$(eval "cat <<EOF
${PROMPT}
EOF
")
export PROMPT
export TASK_NAME="doccheck-pr-${PR_NUMBER}"
export CONTEXT_KEY="gh-pr-${PR_NUMBER}"
echo "Creating task: ${CODER_USERNAME}/${TASK_NAME}"
./scripts/documentation-check.sh create
echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_OUTPUT}"
echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_ENV}"
- name: Update comment with task link
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ steps.determine-inputs.outputs.pr_number }}
GITHUB_REPOSITORY: ${{ github.repository }}
TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }}
RUN_ID: ${{ github.run_id }}
run: |
COMMENT_BODY=$(cat <<EOF
🤖 **Documentation Check Started**
Analyzing PR changes to determine if documentation updates are needed...
[View workflow run](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID})
---
🔄 **Analysis in progress...**
📋 Task: https://dev.coder.com/tasks/${TASK_NAME}
EOF
)
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" --repo "${GITHUB_REPOSITORY}" --edit-last
- name: Wait for task completion and get results
id: get-results
env:
TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }}
run: |
START_TIME=$(date +%s)
echo "Waiting for task to complete..."
./scripts/documentation-check.sh wait
echo "Getting task results..."
TASK_OUTPUT=$(./scripts/documentation-check.sh summary)
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# Convert to minutes and seconds
MINUTES=$((DURATION / 60))
SECONDS=$((DURATION % 60))
if [ $MINUTES -gt 0 ]; then
DURATION_STR="${MINUTES}m ${SECONDS}s"
else
DURATION_STR="${SECONDS}s"
fi
# Save output to file for next step
echo "${TASK_OUTPUT}" > /tmp/task_output.txt
echo "${DURATION_STR}" > /tmp/task_duration.txt
echo "Task completed successfully in ${DURATION_STR}"
- name: Update PR comment with results
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ steps.determine-inputs.outputs.pr_number }}
GITHUB_REPOSITORY: ${{ github.repository }}
TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }}
RUN_ID: ${{ github.run_id }}
run: |
TASK_OUTPUT=$(cat /tmp/task_output.txt)
DURATION=$(cat /tmp/task_duration.txt)
COMMENT_BODY=$(cat <<EOF
🤖 **Documentation Check Started**
Analyzing PR changes to determine if documentation updates are needed...
[View workflow run](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID})
---
${TASK_OUTPUT}
---
⏱️ Execution time: **${DURATION}**
📋 Task: https://dev.coder.com/tasks/${TASK_NAME}
🔗 [View workflow run](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID})
EOF
)
gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" --repo "${GITHUB_REPOSITORY}" --edit-last
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env bash
SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
# shellcheck source=scripts/lib.sh
source "${SCRIPT_DIR}/lib.sh"
CODER_BIN=${CODER_BIN:-"$(which coder)"}
TEMPDIR=$(mktemp -d)
trap 'rm -rf "${TEMPDIR}"' EXIT
[[ -n ${VERBOSE:-} ]] && set -x
set -euo pipefail
usage() {
echo "Usage: $0 <options>"
echo "Commands:"
echo " create - Create a new documentation check task"
echo " wait - Wait for task to complete"
echo " summary - Get task output summary"
echo " delete - Delete the task"
exit 1
}
create() {
requiredenvs CODER_URL CODER_SESSION_TOKEN CODER_USERNAME TASK_NAME TEMPLATE_NAME TEMPLATE_PRESET PROMPT
# Check if a task already exists
set +e
task_json=$("${CODER_BIN}" \
--url "${CODER_URL}" \
--token "${CODER_SESSION_TOKEN}" \
exp tasks status "${CODER_USERNAME}/${TASK_NAME}" \
--output json 2>/dev/null)
set -e
if [[ "${TASK_NAME}" == $(jq -r '.name' <<<"${task_json}" 2>/dev/null) ]]; then
echo "Task \"${CODER_USERNAME}/${TASK_NAME}\" already exists. Sending prompt to existing task."
prompt
exit 0
fi
"${CODER_BIN}" \
--url "${CODER_URL}" \
--token "${CODER_SESSION_TOKEN}" \
exp tasks create \
--name "${TASK_NAME}" \
--template "${TEMPLATE_NAME}" \
--preset "${TEMPLATE_PRESET}" \
--org coder \
--owner "${CODER_USERNAME}" \
--stdin <<<"${PROMPT}"
exit 0
}
prompt() {
requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME PROMPT
${CODER_BIN} \
--url "${CODER_URL}" \
--token "${CODER_SESSION_TOKEN}" \
exp tasks status "${TASK_NAME}" \
--watch >/dev/null
${CODER_BIN} \
--url "${CODER_URL}" \
--token "${CODER_SESSION_TOKEN}" \
exp tasks send "${TASK_NAME}" \
--stdin \
<<<"${PROMPT}"
}
wait_for_completion() {
requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME
${CODER_BIN} \
--url "${CODER_URL}" \
--token "${CODER_SESSION_TOKEN}" \
exp tasks status "${TASK_NAME}" \
--watch >/dev/null
}
summary() {
requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME
last_msg_json=$(
${CODER_BIN} \
--url "${CODER_URL}" \
--token "${CODER_SESSION_TOKEN}" \
exp tasks logs "${TASK_NAME}" \
--output json
)
# Extract the last output message from the task
last_output_msg=$(jq -r 'last(.[] | select(.type=="output")) | .content' <<<"${last_msg_json}")
# Extract only the content between the documentation analysis markers
if echo "${last_output_msg}" | grep -q "---DOCUMENTATION-ANALYSIS-START---"; then
# Extract content between markers
summary=$(echo "${last_output_msg}" | sed -n '/---DOCUMENTATION-ANALYSIS-START---/,/---DOCUMENTATION-ANALYSIS-END---/p' | sed '1d;$d')
echo "${summary}"
else
# Fallback: if markers not found, return a message
echo "⚠️ Unable to extract documentation analysis. Please check the [task logs](https://dev.coder.com/tasks/${TASK_NAME})."
fi
}
delete() {
requiredenvs CODER_URL CODER_SESSION_TOKEN TASK_NAME
"${CODER_BIN}" \
--url "${CODER_URL}" \
--token "${CODER_SESSION_TOKEN}" \
delete \
"${TASK_NAME}" \
--yes
exit 0
}
main() {
dependencies coder
if [[ $# -eq 0 ]]; then
usage
fi
case "$1" in
create)
create
;;
wait)
wait_for_completion
;;
summary)
summary
;;
delete)
delete
;;
*)
echo "Unknown option: $1"
usage
;;
esac
}
main "$@"