From 26204a6b5dbcf442c74e51e47c80de2afb64bfa5 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Oct 2025 20:50:50 +0000 Subject: [PATCH 01/13] 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 --- .github/workflows/documentation-check.yaml | 245 +++++++++++++++++++++ scripts/documentation-check.sh | 142 ++++++++++++ 2 files changed, 387 insertions(+) create mode 100644 .github/workflows/documentation-check.yaml create mode 100755 scripts/documentation-check.sh diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml new file mode 100644 index 0000000000000..26c91603a5aa6 --- /dev/null +++ b/.github/workflows/documentation-check.yaml @@ -0,0 +1,245 @@ +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 PR Number + id: determine-pr + env: + GITHUB_EVENT_NAME: ${{ github.event_name }} + GITHUB_EVENT_PR_NUMBER: ${{ github.event.pull_request.number }} + INPUTS_PR_NUMBER: ${{ inputs.pr_number }} + run: | + if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then + echo "pr_number=${INPUTS_PR_NUMBER}" >> "${GITHUB_OUTPUT}" + elif [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then + echo "pr_number=${GITHUB_EVENT_PR_NUMBER}" >> "${GITHUB_OUTPUT}" + 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_ACTOR: ${{ github.actor }} + run: | + can_push="$(gh api "/repos/${GITHUB_REPOSITORY}/collaborators/${GITHUB_ACTOR}/permission" --jq '.user.permissions.push')" + if [[ "${can_push}" != "true" ]]; then + echo "::error title=Access Denied::${GITHUB_ACTOR} does not have push access to ${GITHUB_REPOSITORY}" + exit 1 + fi + + - name: Post initial comment + id: post-comment + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ steps.determine-pr.outputs.pr_number }} + GITHUB_REPOSITORY: ${{ github.repository }} + RUN_ID: ${{ github.run_id }} + run: | + COMMENT_BODY="🤖 **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}) +" + COMMENT_ID=$(gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" --repo "${GITHUB_REPOSITORY}" | grep -oP 'https://github.com/.*/pull/.*#issuecomment-\K\d+') + echo "comment_id=${COMMENT_ID}" >> "${GITHUB_OUTPUT}" + + - 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_ACTOR: ${{ github.actor }} + run: | + GITHUB_USER_ID=$(gh api "users/${GITHUB_ACTOR}" --jq '.id') + 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-pr.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 'title,body,url,files') + pr_title=$(echo "${pr_json}" | jq -r '.title') + pr_body=$(echo "${pr_json}" | jq -r '.body') + pr_url=$(echo "${pr_json}" | jq -r '.url') + + # Get list of changed files + files_changed=$(echo "${pr_json}" | jq -r '.files[] | " - \(.path) (\(.additions) additions, \(.deletions) deletions)"') + + # Get the actual diff for code changes + pr_diff=$(gh pr diff "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}") + + # Build comprehensive prompt + PROMPT=$(cat <> "${GITHUB_OUTPUT}" + echo "TASK_NAME=${CODER_USERNAME}/${TASK_NAME}" >> "${GITHUB_ENV}" + + - name: Wait for task completion and get results + id: get-results + env: + TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} + run: | + echo "Waiting for task to complete..." + ./scripts/documentation-check.sh wait + + echo "Getting task results..." + TASK_OUTPUT=$(./scripts/documentation-check.sh summary) + + # Save output to file for next step + echo "${TASK_OUTPUT}" > /tmp/task_output.txt + + echo "Task completed successfully" + + - name: Update PR comment with results + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ steps.determine-pr.outputs.pr_number }} + GITHUB_REPOSITORY: ${{ github.repository }} + COMMENT_ID: ${{ steps.post-comment.outputs.comment_id }} + TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} + RUN_ID: ${{ github.run_id }} + run: | + TASK_OUTPUT=$(cat /tmp/task_output.txt) + + COMMENT_BODY="🤖 **Documentation Check Complete** + +${TASK_OUTPUT} + +--- +Task: https://dev.coder.com/tasks/${TASK_NAME} +[View workflow run](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID}) +" + + # Update the existing comment + gh api \ + --method PATCH \ + "/repos/${GITHUB_REPOSITORY}/issues/comments/${COMMENT_ID}" \ + -f body="${COMMENT_BODY}" + + - name: Cleanup task + if: always() + env: + TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} + run: | + if [[ -n "${TASK_NAME}" ]]; then + echo "Cleaning up task: ${TASK_NAME}" + ./scripts/documentation-check.sh delete || true + fi diff --git a/scripts/documentation-check.sh b/scripts/documentation-check.sh new file mode 100755 index 0000000000000..4f007efe46623 --- /dev/null +++ b/scripts/documentation-check.sh @@ -0,0 +1,142 @@ +#!/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 " + 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}") + + # Clean up the output (remove bullet points and tool markers) + last_msg=$(echo "${last_output_msg}" | sed 's/^● //' | sed 's/●//g') + + echo "${last_msg}" +} + +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 "$@" From c20d661613ec4b7fede286d81aa71722fddca181 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Oct 2025 20:55:59 +0000 Subject: [PATCH 02/13] fix: use heredoc syntax for multi-line strings in workflow Fixes YAML validation errors by using proper heredoc syntax (cat <> "${GITHUB_OUTPUT}" @@ -219,14 +221,16 @@ EOF run: | TASK_OUTPUT=$(cat /tmp/task_output.txt) - COMMENT_BODY="🤖 **Documentation Check Complete** + COMMENT_BODY=$(cat < Date: Wed, 15 Oct 2025 21:02:13 +0000 Subject: [PATCH 03/13] 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) --- .github/workflows/documentation-check.yaml | 62 +++++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index 1189c39f76f86..e1e36feff56c7 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -27,17 +27,43 @@ jobs: actions: write steps: - - name: Determine PR Number - id: determine-pr + - 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 @@ -47,11 +73,13 @@ jobs: env: GITHUB_REPOSITORY: ${{ github.repository }} GH_TOKEN: ${{ github.token }} - GITHUB_ACTOR: ${{ github.actor }} + GITHUB_USERNAME: ${{ steps.determine-inputs.outputs.github_username }} + GITHUB_USER_ID: ${{ steps.determine-inputs.outputs.github_user_id }} run: | - can_push="$(gh api "/repos/${GITHUB_REPOSITORY}/collaborators/${GITHUB_ACTOR}/permission" --jq '.user.permissions.push')" + # 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_ACTOR} does not have push access to ${GITHUB_REPOSITORY}" + echo "::error title=Access Denied::${GITHUB_USERNAME} does not have push access to ${GITHUB_REPOSITORY}" exit 1 fi @@ -59,7 +87,7 @@ jobs: id: post-comment env: GH_TOKEN: ${{ github.token }} - PR_NUMBER: ${{ steps.determine-pr.outputs.pr_number }} + PR_NUMBER: ${{ steps.determine-inputs.outputs.pr_number }} GITHUB_REPOSITORY: ${{ github.repository }} RUN_ID: ${{ github.run_id }} run: | @@ -97,10 +125,11 @@ jobs: env: CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }} GH_TOKEN: ${{ github.token }} - GITHUB_ACTOR: ${{ github.actor }} + GITHUB_USER_ID: ${{ steps.determine-inputs.outputs.github_user_id }} run: | - GITHUB_USER_ID=$(gh api "users/${GITHUB_ACTOR}" --jq '.id') - user_json=$(coder users list --github-user-id="${GITHUB_USER_ID}" --output=json) + 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}" @@ -117,7 +146,7 @@ jobs: CODER_USERNAME: ${{ steps.get-coder-username.outputs.coder_username }} GH_TOKEN: ${{ github.token }} GITHUB_REPOSITORY: ${{ github.repository }} - PR_NUMBER: ${{ steps.determine-pr.outputs.pr_number }} + PR_NUMBER: ${{ steps.determine-inputs.outputs.pr_number }} RUN_ID: ${{ github.run_id }} TEMPLATE_NAME: "traiage" TEMPLATE_PRESET: "Default" @@ -135,7 +164,7 @@ jobs: pr_diff=$(gh pr diff "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}") # Build comprehensive prompt - PROMPT=$(cat < Date: Wed, 15 Oct 2025 21:04:18 +0000 Subject: [PATCH 04/13] 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 --- .github/workflows/documentation-check.yaml | 24 +++++++++------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index e1e36feff56c7..cae4ed0f8f745 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -91,14 +91,12 @@ jobs: GITHUB_REPOSITORY: ${{ github.repository }} RUN_ID: ${{ github.run_id }} run: | - COMMENT_BODY=$(cat <<'EOF' - 🤖 **Documentation Check Started** + COMMENT_BODY="🤖 **Documentation Check Started** - Analyzing PR changes to determine if documentation updates are needed... +Analyzing PR changes to determine if documentation updates are needed... - [View workflow run](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID}) - EOF - ) +[View workflow run](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID}) +" COMMENT_ID=$(gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" --repo "${GITHUB_REPOSITORY}" | grep -oP 'https://github.com/.*/pull/.*#issuecomment-\K\d+') echo "comment_id=${COMMENT_ID}" >> "${GITHUB_OUTPUT}" @@ -255,16 +253,14 @@ EOF run: | TASK_OUTPUT=$(cat /tmp/task_output.txt) - COMMENT_BODY=$(cat < Date: Wed, 15 Oct 2025 21:07:37 +0000 Subject: [PATCH 05/13] 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 --- .github/workflows/documentation-check.yaml | 96 +++++++++++----------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index cae4ed0f8f745..de36021d4a621 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -91,12 +91,14 @@ jobs: GITHUB_REPOSITORY: ${{ github.repository }} RUN_ID: ${{ github.run_id }} run: | - COMMENT_BODY="🤖 **Documentation Check Started** + COMMENT_BODY=$(cat <> "${GITHUB_OUTPUT}" @@ -163,58 +165,58 @@ Analyzing PR changes to determine if documentation updates are needed... # Build comprehensive prompt PROMPT=$(cat <<'EOF' -Analyze PR ${pr_url} for documentation updates. + 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: + ## Task Description + You are tasked with analyzing code changes in a pull request and determining what documentation updates are needed. Please: -1. Review the PR description and code changes to understand what was changed -2. Examine the existing documentation in the docs/ directory -3. Identify any of the following needs: - - Updates required to existing documentation - - Documentation that needs to be deprecated - - New documentation that should be added -4. Provide a clear, actionable list of documentation changes needed + 1. Review the PR description and code changes to understand what was changed + 2. Examine the existing documentation in the docs/ directory + 3. Identify any of the following needs: + - Updates required to existing documentation + - Documentation that needs to be deprecated + - New documentation that should be added + 4. Provide a clear, actionable list of documentation changes needed -## PR Details -**Title**: ${pr_title} + ## PR Details + **Title**: ${pr_title} -**Description**: -${pr_body} + **Description**: + ${pr_body} -**Files Changed**: -${files_changed} + **Files Changed**: + ${files_changed} -**Code Diff**: -```diff -${pr_diff} -``` + **Code Diff**: + ```diff + ${pr_diff} + ``` -## Output Format -Please provide your analysis in the following format: + ## Output Format + Please provide your analysis in the following format: -### Documentation Updates Required + ### Documentation Updates Required -#### Updates to Existing Documentation -- [List any existing docs that need updates with specific changes needed] + #### Updates to Existing Documentation + - [List any existing docs that need updates with specific changes needed] -#### Documentation to Deprecate -- [List any docs that should be marked as deprecated or removed] + #### Documentation to Deprecate + - [List any docs that should be marked as deprecated or removed] -#### New Documentation Needed -- [List any new documentation that should be created] + #### New Documentation Needed + - [List any new documentation that should be created] -#### No Changes Needed -- [If no documentation changes are required, explain why] + #### No Changes Needed + - [If no documentation changes are required, explain why] -Be specific and provide file paths and section references where applicable. -EOF + Be specific and provide file paths and section references where applicable. + EOF ) # Expand variables in the prompt PROMPT=$(eval "cat < Date: Wed, 15 Oct 2025 21:16:14 +0000 Subject: [PATCH 06/13] 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 --- .github/workflows/documentation-check.yaml | 37 +++++++--------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index de36021d4a621..f6dbe573941e5 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -152,17 +152,9 @@ jobs: TEMPLATE_PRESET: "Default" run: | # Fetch PR details using `gh` CLI - pr_json=$(gh pr view "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json 'title,body,url,files') - pr_title=$(echo "${pr_json}" | jq -r '.title') - pr_body=$(echo "${pr_json}" | jq -r '.body') + pr_json=$(gh pr view "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json 'url') pr_url=$(echo "${pr_json}" | jq -r '.url') - # Get list of changed files - files_changed=$(echo "${pr_json}" | jq -r '.files[] | " - \(.path) (\(.additions) additions, \(.deletions) deletions)"') - - # Get the actual diff for code changes - pr_diff=$(gh pr diff "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}") - # Build comprehensive prompt PROMPT=$(cat <<'EOF' Analyze PR ${pr_url} for documentation updates. @@ -170,27 +162,20 @@ jobs: ## Task Description You are tasked with analyzing code changes in a pull request and determining what documentation updates are needed. Please: - 1. Review the PR description and code changes to understand what was changed - 2. Examine the existing documentation in the docs/ directory - 3. Identify any of the following needs: + 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 - 4. Provide a clear, actionable list of documentation changes needed - - ## PR Details - **Title**: ${pr_title} - - **Description**: - ${pr_body} - - **Files Changed**: - ${files_changed} + 5. Provide a clear, actionable list of documentation changes needed - **Code Diff**: - ```diff - ${pr_diff} - ``` + ## PR to Analyze + ${pr_url} ## Output Format Please provide your analysis in the following format: From bfbe7d8dfd2d22aee780214dab6271eccfceeb38 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Oct 2025 21:24:46 +0000 Subject: [PATCH 07/13] 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 --- .github/workflows/documentation-check.yaml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index f6dbe573941e5..5184f0add4954 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -218,16 +218,32 @@ jobs: 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" + echo "Task completed successfully in ${DURATION_STR}" - name: Update PR comment with results env: @@ -239,6 +255,7 @@ jobs: RUN_ID: ${{ github.run_id }} run: | TASK_OUTPUT=$(cat /tmp/task_output.txt) + DURATION=$(cat /tmp/task_duration.txt) COMMENT_BODY=$(cat < Date: Wed, 15 Oct 2025 21:31:20 +0000 Subject: [PATCH 08/13] 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 --- .github/workflows/documentation-check.yaml | 45 +++++++++++++--------- scripts/documentation-check.sh | 13 +++++-- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index 5184f0add4954..c0fb4ed7fea80 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -177,24 +177,33 @@ jobs: ## PR to Analyze ${pr_url} - ## Output Format - Please provide your analysis in the following format: - - ### Documentation Updates Required - - #### Updates to Existing Documentation - - [List any existing docs that need updates with specific changes needed] - - #### Documentation to Deprecate - - [List any docs that should be marked as deprecated or removed] - - #### New Documentation Needed - - [List any new documentation that should be created] - - #### No Changes Needed - - [If no documentation changes are required, explain why] - - Be specific and provide file paths and section references where applicable. + ## 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 diff --git a/scripts/documentation-check.sh b/scripts/documentation-check.sh index 4f007efe46623..97ba248de815c 100755 --- a/scripts/documentation-check.sh +++ b/scripts/documentation-check.sh @@ -94,10 +94,15 @@ summary() { # Extract the last output message from the task last_output_msg=$(jq -r 'last(.[] | select(.type=="output")) | .content' <<<"${last_msg_json}") - # Clean up the output (remove bullet points and tool markers) - last_msg=$(echo "${last_output_msg}" | sed 's/^● //' | sed 's/●//g') - - echo "${last_msg}" + # 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() { From 89cfc5e4273be5600cada10a9e9d1b73e013d5c9 Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Oct 2025 21:34:20 +0000 Subject: [PATCH 09/13] feat: preserve original comment content when updating with results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .github/workflows/documentation-check.yaml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index c0fb4ed7fea80..d5f9b444810cd 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -266,15 +266,20 @@ jobs: TASK_OUTPUT=$(cat /tmp/task_output.txt) DURATION=$(cat /tmp/task_duration.txt) + # Fetch the original comment to preserve it + ORIGINAL_COMMENT=$(gh api "/repos/${GITHUB_REPOSITORY}/issues/comments/${COMMENT_ID}" --jq '.body') + COMMENT_BODY=$(cat < Date: Wed, 15 Oct 2025 21:40:11 +0000 Subject: [PATCH 10/13] 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 --- .github/workflows/documentation-check.yaml | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index d5f9b444810cd..27ff7e81b085f 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -222,6 +222,33 @@ jobs: 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 }} + COMMENT_ID: ${{ steps.post-comment.outputs.comment_id }} + GITHUB_REPOSITORY: ${{ github.repository }} + TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} + run: | + # Fetch the original comment + ORIGINAL_COMMENT=$(gh api "/repos/${GITHUB_REPOSITORY}/issues/comments/${COMMENT_ID}" --jq '.body') + + COMMENT_BODY=$(cat < Date: Wed, 15 Oct 2025 21:47:17 +0000 Subject: [PATCH 11/13] 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. --- .github/workflows/documentation-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index 27ff7e81b085f..183fed37de5a1 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -213,7 +213,7 @@ jobs: ") export PROMPT - export TASK_NAME="doccheck-pr-${PR_NUMBER}-${RUN_ID}" + export TASK_NAME="doccheck-pr-${PR_NUMBER}" export CONTEXT_KEY="gh-pr-${PR_NUMBER}" echo "Creating task: ${CODER_USERNAME}/${TASK_NAME}" From 51df125f6b440a1fadd7d4166a54663b1130d2ec Mon Sep 17 00:00:00 2001 From: default Date: Wed, 15 Oct 2025 21:57:28 +0000 Subject: [PATCH 12/13] 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. --- .github/workflows/documentation-check.yaml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index 183fed37de5a1..d77c558b229ca 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -316,12 +316,3 @@ jobs: "/repos/${GITHUB_REPOSITORY}/issues/comments/${COMMENT_ID}" \ -f body="${COMMENT_BODY}" - - name: Cleanup task - if: always() - env: - TASK_NAME: ${{ steps.create-task.outputs.TASK_NAME }} - run: | - if [[ -n "${TASK_NAME}" ]]; then - echo "Cleaning up task: ${TASK_NAME}" - ./scripts/documentation-check.sh delete || true - fi From a233591abd27536f03294b9a56db476490504bb0 Mon Sep 17 00:00:00 2001 From: default Date: Thu, 16 Oct 2025 13:38:45 +0000 Subject: [PATCH 13/13] 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 --- .github/workflows/documentation-check.yaml | 38 +++++++++------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/.github/workflows/documentation-check.yaml b/.github/workflows/documentation-check.yaml index d77c558b229ca..b95d29d4ab3a3 100644 --- a/.github/workflows/documentation-check.yaml +++ b/.github/workflows/documentation-check.yaml @@ -84,7 +84,6 @@ jobs: fi - name: Post initial comment - id: post-comment env: GH_TOKEN: ${{ github.token }} PR_NUMBER: ${{ steps.determine-inputs.outputs.pr_number }} @@ -99,8 +98,7 @@ jobs: [View workflow run](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${RUN_ID}) EOF ) - COMMENT_ID=$(gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" --repo "${GITHUB_REPOSITORY}" | grep -oP 'https://github.com/.*/pull/.*#issuecomment-\K\d+') - echo "comment_id=${COMMENT_ID}" >> "${GITHUB_OUTPUT}" + gh pr comment "${PR_NUMBER}" --body "${COMMENT_BODY}" --repo "${GITHUB_REPOSITORY}" --create-if-none --edit-last - name: Download and install Coder binary shell: bash @@ -225,15 +223,17 @@ jobs: - name: Update comment with task link env: GH_TOKEN: ${{ github.token }} - COMMENT_ID: ${{ steps.post-comment.outputs.comment_id }} + 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: | - # Fetch the original comment - ORIGINAL_COMMENT=$(gh api "/repos/${GITHUB_REPOSITORY}/issues/comments/${COMMENT_ID}" --jq '.body') - COMMENT_BODY=$(cat <