Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ Node 20 action. There is no supported Node 24 upgrade for that dependency;
replacing it requires a separate migration of its authentication and locking
behavior, rather than merely changing an action pin.

## Milestone changelog fetch diagnostics

`milestone-changelog.md` retries PR-list requests up to four times, waiting
15, 30, and 60 seconds between attempts. HTTP 5xx errors and the
`unexpected end of JSON input` diagnostic are retryable. When `gh` succeeds
but returns invalid or empty JSON, validation also retries, even if stderr
contains warnings. Other command failures and valid JSON of the wrong type
fail immediately.

Each failed attempt logs the output filename, command and validation exit codes,
stderr from both tools, and the first 4096 bytes of the response with its total
byte count. Response previews are bounded because PR bodies can be large.
Diagnostic lines are prefixed and legacy `##[` command markers are escaped so
response text is not interpreted as GitHub Actions workflow commands.
Successful requests also replay any `gh` warnings;
their complete JSON is kept in the normal data files rather than printed.

## Auto-sec dependency reconciliation

`auto-sec.md` runs every 12 hours (and on demand). It reconciles open Dependabot
Expand Down
80 changes: 73 additions & 7 deletions .github/workflows/milestone-changelog.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 72 additions & 6 deletions .github/workflows/milestone-changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,68 @@ jobs:
DATA_DIR="/tmp/gh-aw/pr-data"
mkdir -p "$DATA_DIR"

# Run `gh pr list` with bounded retry. The large (--limit 5000)
# GraphQL queries below occasionally hit a transient 502/504 from
# GitHub's GraphQL gateway or get a truncated response body. Retry
# only those transient failures (4 attempts, 15s/30s/60s backoff);
# Invalid JSON after a successful command is retryable even if gh
# printed warnings. Other command errors and valid non-array JSON
# fail immediately. Usage: gh_pr_list_with_retry OUT ARGS...
gh_pr_list_with_retry() {
local out="$1"; shift
local delays=(15 30 60)
local attempt=1
local diagnostics err validation_err
diagnostics=$(mktemp -d)
err="$diagnostics/gh.stderr"
validation_err="$diagnostics/jq.stderr"
while true; do
local status=0 validation_status=0
local transient=false
: > "$validation_err"
if gh pr list "$@" > "$out" 2> "$err"; then
if jq -s -e 'if length == 1 and (.[0] | type == "array") then true else error("Expected exactly one JSON array") end' \
"$out" > /dev/null 2> "$validation_err"; then
tr '\r' '\n' < "$err" | sed 's/##\[/##\\[/g; s/^/gh stderr: /' >&2
rm -f "$err" "$validation_err"
rmdir "$diagnostics"
return 0
else
validation_status=$?
fi
if ! jq -s -e 'length > 0' "$out" > /dev/null 2>&1; then
transient=true
fi
else
status=$?
# Examples: "HTTP 502: Bad Gateway", "unexpected end of JSON input".
if grep -qE 'HTTP 5[0-9][0-9]|unexpected end of JSON input' "$err"; then
transient=true
fi
fi

echo "gh pr list attempt $attempt failed for $out (gh exit=$status, jq exit=$validation_status)" >&2
tr '\r' '\n' < "$err" | sed 's/##\[/##\\[/g; s/^/gh stderr: /' >&2
tr '\r' '\n' < "$validation_err" | sed 's/##\[/##\\[/g; s/^/jq stderr: /' >&2
# PR bodies can be large and contain workflow-command syntax.
# Bound the preview and prefix every line, including CR-delimited lines.
# Legacy "##[command]" markers are recognized even after a prefix.
echo "Response preview (first 4096 bytes of $(wc -c < "$out") bytes):" >&2
head -c 4096 "$out" | tr '\r' '\n' | sed 's/##\[/##\\[/g; s/^/stdout: /' >&2
printf '\n' >&2
if [ "$transient" != true ] || [ "$attempt" -gt "${#delays[@]}" ]; then
rm -f "$err" "$validation_err"
rmdir "$diagnostics"
echo "::error::gh pr list failed after $attempt attempt(s)"
return 1
fi
local delay="${delays[$((attempt - 1))]}"
echo "::warning::gh pr list attempt $attempt failed with a transient error; retrying in ${delay}s"
sleep "$delay"
attempt=$((attempt + 1))
done
}

# 0. Clone the memory branch to read existing state (prs/, changes/,
# feedback-issue.json). The full content is copied to
# $DATA_DIR/memory/$MILESTONE/ so the agent can read it directly.
Expand Down Expand Up @@ -144,11 +206,13 @@ jobs:
rm -f "$CLONE_ERR"

# 1. Fetch ALL merged PRs in milestone, sorted by merge date ascending
gh pr list --repo "$REPO" --state merged --limit 5000 \
gh_pr_list_with_retry "$DATA_DIR/all-milestone-prs.raw.json" \
--repo "$REPO" --state merged --limit 5000 \
--search "milestone:$MILESTONE" \
--json number,title,body,author,mergedBy,mergedAt,labels,additions,deletions,changedFiles \
| jq 'sort_by(.mergedAt)' \
--json number,title,body,author,mergedBy,mergedAt,labels,additions,deletions,changedFiles
jq 'sort_by(.mergedAt)' "$DATA_DIR/all-milestone-prs.raw.json" \
> "$DATA_DIR/all-milestone-prs.json"
rm -f "$DATA_DIR/all-milestone-prs.raw.json"

TOTAL=$(jq length "$DATA_DIR/all-milestone-prs.json")
echo "Total merged PRs in milestone: $TOTAL"
Expand Down Expand Up @@ -226,11 +290,13 @@ jobs:
DOCS_MERGED_RANGE="merged:${MILESTONE_START}..${BATCH_CUTOFF}"
echo "Capping docs PRs to merged on or before $BATCH_CUTOFF (batch is full)"
fi
gh pr list --repo "$DOCS_REPO" --state merged --limit 5000 \
gh_pr_list_with_retry "$DATA_DIR/all-docs-prs.raw.json" \
--repo "$DOCS_REPO" --state merged --limit 5000 \
--search "$DOCS_MERGED_RANGE" \
--json number,title,body,author,mergedBy,mergedAt,labels,additions,deletions,changedFiles,files \
| jq 'sort_by(.mergedAt)' \
--json number,title,body,author,mergedBy,mergedAt,labels,additions,deletions,changedFiles,files
jq 'sort_by(.mergedAt)' "$DATA_DIR/all-docs-prs.raw.json" \
> "$DATA_DIR/all-docs-prs.json"
rm -f "$DATA_DIR/all-docs-prs.raw.json"

DOCS_TOTAL=$(jq length "$DATA_DIR/all-docs-prs.json")
echo "Total docs PRs merged since $MILESTONE_START: $DOCS_TOTAL"
Expand Down
Loading