Files
PEASS-ng/.github/workflows/pr-failure-chack-agent-dispatch.yml
T
04d5bf69bf Auto-merge PR #817 (Chack Agent)
The chack-agent workflows pinned the action to commit 9fd38a06; they now use
carlospolop/chack-agent@master. The steps that install the Codex CLI also
install [email protected].5 so chack's opencode backend is available.
AGENTS.md records both rules.

Co-authored-by: Carlos Polop <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-11-06 13:39:57 +00:00

345 lines
15 KiB
YAML

name: PR Failure Chack-Agent Dispatch
on:
workflow_run:
workflows: ["PR-tests"]
types: [completed]
# Context resolution happens before checkout, including branch-based PR lookup.
env:
GH_REPO: ${{ github.repository }}
jobs:
resolve_pr_context:
if: >-
${{ github.event.workflow_run.conclusion == 'failure' &&
!startsWith(github.event.workflow_run.head_commit.message || '', 'Fix CI failures for PR #') }}
runs-on: ubuntu-latest
permissions:
pull-requests: read
issues: read
outputs:
number: ${{ steps.pr_context.outputs.number }}
author: ${{ steps.pr_context.outputs.author }}
head_repo: ${{ steps.pr_context.outputs.head_repo }}
head_branch: ${{ steps.pr_context.outputs.head_branch }}
should_run: ${{ steps.pr_context.outputs.should_run }}
steps:
- name: Resolve PR context
id: pr_context
env:
PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }}
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
SOURCE_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
GH_TOKEN: ${{ github.token }}
run: |
if [ -z "$PR_NUMBER" ] && [ -n "$HEAD_BRANCH" ]; then
PR_NUMBER="$(gh pr list --state open --head "$HEAD_BRANCH" --json number --jq '.[0].number')"
fi
if [ -z "$PR_NUMBER" ]; then
echo "No pull request found for workflow_run; skipping."
{
echo "number="
echo "author="
echo "head_repo="
echo "head_branch=${HEAD_BRANCH}"
echo "should_run=false"
} >> "$GITHUB_OUTPUT"
exit 0
fi
pr_author=$(gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${PR_NUMBER} \
--jq '.user.login')
pr_state=$(gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${PR_NUMBER} \
--jq '.state')
if [ "$pr_state" != "open" ]; then
echo "PR #${PR_NUMBER} is $pr_state; skipping stale fix."
echo "should_run=false" >> "$GITHUB_OUTPUT"
exit 0
fi
pr_head_repo=$(gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${PR_NUMBER} \
--jq '.head.repo.full_name')
pr_head_branch=$(gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${PR_NUMBER} \
--jq '.head.ref')
pr_head_sha=$(gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${PR_NUMBER} \
--jq '.head.sha')
if [ "$pr_head_sha" != "$SOURCE_HEAD_SHA" ]; then
echo "PR #${PR_NUMBER} moved from failed head $SOURCE_HEAD_SHA to $pr_head_sha; skipping stale fix."
echo "should_run=false" >> "$GITHUB_OUTPUT"
exit 0
fi
pr_labels=$(gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/issues/${PR_NUMBER} \
--jq '.labels[].name')
if echo "$pr_labels" | grep -q "^chack-agent-fix-attempted$"; then
echo "chack-agent fix already attempted for PR #${PR_NUMBER}; skipping."
should_run=false
else
should_run=true
fi
{
echo "number=${PR_NUMBER}"
echo "author=${pr_author}"
echo "head_repo=${pr_head_repo}"
echo "head_branch=${pr_head_branch}"
echo "should_run=${should_run}"
} >> "$GITHUB_OUTPUT"
chack_agent_on_failure:
needs: resolve_pr_context
if: ${{ needs.resolve_pr_context.outputs.author == 'carlospolop' && needs.resolve_pr_context.outputs.should_run == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
actions: write
env:
# chack-agent OTel traces to Phoenix; off when the header secret is absent (e.g. forks).
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: ${{ secrets.PHOENIX_OTLP_HEADERS != '' && 'https://chacks.hacktricks.wiki/v1/traces' || '' }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.PHOENIX_OTLP_HEADERS }}
OTEL_SERVICE_NAME: peass-ng
OTEL_RESOURCE_ATTRIBUTES: openinference.project.name=peass-ng
steps:
- name: Mark fix attempt
env:
PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }}
GH_TOKEN: ${{ github.token }}
run: |
gh api -X POST -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/issues/${PR_NUMBER}/labels \
-f labels[]=chack-agent-fix-attempted
- name: Checkout PR head
uses: actions/checkout@v5
with:
repository: ${{ needs.resolve_pr_context.outputs.head_repo }}
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 0
persist-credentials: true
token: ${{ secrets.CODEX_FIXER_TOKEN || github.token }}
- name: Configure git author
run: |
git config user.name "chack-agent"
git config user.email "[email protected]"
- name: Fetch failure summary
env:
GH_TOKEN: ${{ github.token }}
RUN_ID: ${{ github.event.workflow_run.id }}
run: |
gh api -H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/actions/runs/$RUN_ID/jobs \
--paginate > /tmp/jobs.json
python3 - <<'PY'
import json
data = json.load(open('/tmp/jobs.json'))
lines = []
for job in data.get('jobs', []):
if job.get('conclusion') == 'failure':
lines.append(f"Job: {job.get('name')} (id {job.get('id')})")
lines.append(f"URL: {job.get('html_url')}")
for step in job.get('steps', []):
if step.get('conclusion') == 'failure':
lines.append(f" Step: {step.get('name')}")
lines.append("")
summary = "\n".join(lines).strip() or "No failing job details found."
with open('chack_failure_summary.txt', 'w') as handle:
handle.write(summary)
PY
- name: Create Chack Agent prompt
env:
PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }}
RUN_URL: ${{ github.event.workflow_run.html_url }}
HEAD_BRANCH: ${{ needs.resolve_pr_context.outputs.head_branch }}
run: |
{
echo "You are fixing CI failures for PR #${PR_NUMBER} in ${{ github.repository }}."
echo "The failing workflow run is: ${RUN_URL}"
echo "The PR branch is: ${HEAD_BRANCH}"
echo ""
echo "Failure summary:"
cat chack_failure_summary.txt
echo ""
echo "Please identify the cause, apply a easy, simple and minimal fix, and update files accordingly."
echo "Do not edit or create any .md or .txt files and do not modify build_lists/regexes.yaml."
echo "Run any fast checks you can locally (no network)."
echo "Leave the repo in a state ready to commit as when you finish, it'll be automatically committed and pushed."
} > chack_prompt.txt
- name: Set up Node.js for Codex
uses: actions/setup-node@v5
with:
node-version: "20"
- name: Install Codex and opencode CLIs
run: |
npm install --global @openai/codex@latest
npm i -g [email protected]
codex --version
opencode --version
- name: Run Chack Agent
id: run_chack
# Always run the latest chack-agent; never pin a commit.
uses: carlospolop/chack-agent@master
with:
provider: codex
model_primary: gpt-5.6-sol
main_action: peass-ng-pr-failure
max_turns: 125
sub_action: PR Failure Chack-Agent Dispatch
system_prompt: |
You are Chack Agent, an elite CI-fix engineer.
Diagnose the failing workflow, propose the minimal safe fix, and implement it.
Do not merely describe a problem you can fix. Only leave explanatory text when you
cannot confidently repair and validate the failure or the PR should not be merged.
Never edit any .md or .txt files. Don't create job or summary files of you actions.
Never create or modify build_lists/regexes.yaml.
Run only fast, local checks (no network). Leave the repo ready to commit.
prompt_file: chack_prompt.txt
tools_config_json: "{\"exec_enabled\": true}"
session_config_json: "{\"long_term_memory_enabled\": false}"
agent_config_json: "{\"self_critique_enabled\": false, \"require_task_steps_manager_init_first\": true}"
codex_access_token: ${{ secrets.CODEX_ACCESS_TOKEN }}
openai_api_key: ${{ secrets.OPENAI_API_KEY }}
- name: Commit and push if changed
id: push_fix
env:
TARGET_BRANCH: ${{ needs.resolve_pr_context.outputs.head_branch }}
PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }}
ORIGINAL_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
PUSH_TOKEN: ${{ secrets.CODEX_FIXER_TOKEN }}
WORKFLOW_TOKEN: ${{ github.token }}
run: |
rm -f chack_failure_summary.txt chack_prompt.txt
if [ -n "$(git status --porcelain)" ]; then
git add -A
# Keep all fixer changes, including .github/workflows, and rely on permissioned tokens to push safely.
# Only temporary tool artifacts are filtered out.
git reset -- chack_failure_summary.txt chack_prompt.txt
# Never commit generated or regenerated regex list files from this workflow.
git reset -- build_lists/regexes.yaml || true
# Never allow the agent to commit generated linpeas artifacts.
git reset -- linpeas.sh linpeas_fat.sh || true
while IFS= read -r forbidden_file; do
git reset -- "$forbidden_file" || true
done < <(git diff --name-only --cached | grep -E '(^|/)(linpeas\.sh|linpeas_fat\.sh)$' || true)
while IFS= read -r file; do
case "$file" in
*.txt|*.md)
git reset -- "$file"
;;
esac
done < <(git diff --name-only --cached)
if git diff --cached --name-only | grep -Eq '(^|/)(linpeas\.sh|linpeas_fat\.sh)$'; then
echo "Forbidden generated linpeas files are still staged; skipping push."
echo "pushed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if ! git diff --cached --quiet; then
git commit -m "Fix CI failures for PR #${PR_NUMBER}"
fi
fi
after_head="$(git rev-parse HEAD)"
if [ "$after_head" = "$ORIGINAL_HEAD_SHA" ]; then
echo "No commit produced by Chack Agent for PR #${PR_NUMBER}."
echo "pushed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ -z "$PUSH_TOKEN" ]; then
echo "CODEX_FIXER_TOKEN is required to push and retest Chack Agent fixes."
exit 1
fi
pr_state="$(GH_TOKEN="$WORKFLOW_TOKEN" gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json state --jq .state)"
current_head_sha="$(GH_TOKEN="$WORKFLOW_TOKEN" gh pr view "$PR_NUMBER" --repo "${{ github.repository }}" --json headRefOid --jq .headRefOid)"
if [ "$pr_state" != "OPEN" ] || [ "$current_head_sha" != "$ORIGINAL_HEAD_SHA" ]; then
echo "PR #${PR_NUMBER} is $pr_state at $current_head_sha; skipping stale fix push."
echo "pushed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if ! git push origin HEAD:${TARGET_BRANCH}; then
echo "Push failed (likely token workflow permission limits); leaving run successful without push."
echo "pushed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
retest_url="$(GH_TOKEN="$WORKFLOW_TOKEN" gh workflow run PR-tests.yml --ref "$TARGET_BRANCH")"
retest_id="${retest_url##*/}"
echo "retest_url=$retest_url" >> "$GITHUB_OUTPUT"
retest_conclusion="timed_out"
for attempt in $(seq 1 180); do
run_state="$(GH_TOKEN="$WORKFLOW_TOKEN" gh run view "$retest_id" --json status,conclusion --jq '[.status, (.conclusion // "")] | @tsv' 2>/dev/null || true)"
status="${run_state%%$'\t'*}"
conclusion="${run_state#*$'\t'}"
if [ "$status" = "completed" ]; then
retest_conclusion="$conclusion"
break
fi
sleep 15
done
echo "pushed=true" >> "$GITHUB_OUTPUT"
echo "retest_conclusion=$retest_conclusion" >> "$GITHUB_OUTPUT"
if [ "$retest_conclusion" = "success" ]; then
echo "retest_passed=true" >> "$GITHUB_OUTPUT"
else
echo "retest_passed=false" >> "$GITHUB_OUTPUT"
fi
- name: Dispatch Chack review after a successful retest
if: ${{ steps.push_fix.outputs.retest_passed == 'true' }}
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }}
run: gh workflow run chack-agent-pr-triage.yml -f pr_number="$PR_NUMBER"
- name: Comment only when Chack Agent could not produce a fix
if: ${{ steps.push_fix.outputs.pushed != 'true' && steps.run_chack.outputs.final-message != '' }}
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }}
CHACK_MESSAGE: ${{ steps.run_chack.outputs.final-message }}
with:
github-token: ${{ github.token }}
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
body: process.env.CHACK_MESSAGE,
});
- name: Comment when the Chack CI fix does not pass its retest
if: ${{ steps.push_fix.outputs.pushed == 'true' && steps.push_fix.outputs.retest_passed != 'true' }}
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ needs.resolve_pr_context.outputs.number }}
RETEST_URL: ${{ steps.push_fix.outputs.retest_url }}
RETEST_CONCLUSION: ${{ steps.push_fix.outputs.retest_conclusion }}
with:
github-token: ${{ github.token }}
script: |
const body = `Chack Agent applied a CI fix, but its full retest finished with \`${process.env.RETEST_CONCLUSION}\`. I could not validate this fix for review or merge.\n\nRetest: ${process.env.RETEST_URL}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(process.env.PR_NUMBER),
body,
});