add automated review
This commit is contained in:
189
.github/workflows/daily-resume-review.yml
vendored
Normal file
189
.github/workflows/daily-resume-review.yml
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
name: "Daily Resume Review"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "15 7 * * *"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
review:
|
||||
name: "Run AI resume review and open PR"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install nix
|
||||
uses: https://github.com/DeterminateSystems/nix-installer-action@main
|
||||
|
||||
- name: Build resume PDF
|
||||
id: build
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if nix build .#default > build.log 2>&1; then
|
||||
echo "build_status=success" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "build_status=failed" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Prepare review context
|
||||
id: context
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p archive/reviews
|
||||
review_date="$(date +'%Y-%m-%d')"
|
||||
review_path="archive/reviews/${review_date}-automated-review.md"
|
||||
|
||||
python - <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
prompt = Path('.github/prompts/resume-review.prompt.md').read_text(encoding='utf-8')
|
||||
resume = Path('resume.tex').read_text(encoding='utf-8')
|
||||
build_log = Path('build.log').read_text(encoding='utf-8', errors='replace') if Path('build.log').exists() else ''
|
||||
|
||||
payload = {
|
||||
"model": os.getenv("OPENAI_MODEL", "gpt-5"),
|
||||
"input": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": "You are an autonomous resume review agent. Follow the provided prompt exactly."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{
|
||||
"type": "input_text",
|
||||
"text": (
|
||||
"Use the following prompt as your full instruction set:\n\n"
|
||||
f"{prompt}\n\n"
|
||||
"Build status from CI:\n"
|
||||
f"- nix build result: {os.getenv('BUILD_STATUS', 'unknown')}\n\n"
|
||||
"Build log:\n"
|
||||
"```text\n"
|
||||
f"{build_log[-15000:]}\n"
|
||||
"```\n\n"
|
||||
"Resume source:\n"
|
||||
"```tex\n"
|
||||
f"{resume}\n"
|
||||
"```\n"
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Path('review-request.json').write_text(json.dumps(payload), encoding='utf-8')
|
||||
PY
|
||||
|
||||
echo "review_date=${review_date}" >> "$GITHUB_OUTPUT"
|
||||
echo "review_path=${review_path}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
env:
|
||||
BUILD_STATUS: ${{ steps.build.outputs.build_status }}
|
||||
|
||||
- name: Run review agent
|
||||
id: review
|
||||
run: |
|
||||
set -euo pipefail
|
||||
api_base="${OPENAI_BASE_URL:-https://api.openai.com/v1}"
|
||||
curl -sS "${api_base}/responses" \
|
||||
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @review-request.json \
|
||||
> response.json
|
||||
|
||||
python - <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
response = json.loads(Path('response.json').read_text(encoding='utf-8'))
|
||||
text = response.get('output_text', '').strip()
|
||||
|
||||
if not text:
|
||||
chunks = []
|
||||
for item in response.get('output', []):
|
||||
for content in item.get('content', []):
|
||||
if content.get('type') == 'output_text':
|
||||
chunks.append(content.get('text', ''))
|
||||
text = '\n'.join(chunks).strip()
|
||||
|
||||
if not text:
|
||||
raise SystemExit('Agent response did not contain output text.')
|
||||
|
||||
review_path = Path('${{ steps.context.outputs.review_path }}')
|
||||
review_path.write_text(text + '\n', encoding='utf-8')
|
||||
PY
|
||||
|
||||
- name: Check whether review changed
|
||||
id: review_guard
|
||||
run: |
|
||||
set -euo pipefail
|
||||
current_review="${{ steps.context.outputs.review_path }}"
|
||||
previous_review="$(ls -1 archive/reviews/*-automated-review.md 2>/dev/null | grep -vx "$current_review" | sort | tail -n1 || true)"
|
||||
|
||||
if [ -z "$previous_review" ]; then
|
||||
echo "create_pr=true" >> "$GITHUB_OUTPUT"
|
||||
echo "reason=no_previous_review" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if cmp -s "$current_review" "$previous_review"; then
|
||||
rm -f "$current_review"
|
||||
echo "create_pr=false" >> "$GITHUB_OUTPUT"
|
||||
echo "reason=review_unchanged" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "create_pr=true" >> "$GITHUB_OUTPUT"
|
||||
echo "reason=review_changed" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Print guard decision
|
||||
run: |
|
||||
echo "create_pr=${{ steps.review_guard.outputs.create_pr }}"
|
||||
echo "reason=${{ steps.review_guard.outputs.reason }}"
|
||||
|
||||
- name: Write PR body
|
||||
if: steps.review_guard.outputs.create_pr == 'true'
|
||||
run: |
|
||||
cat > pr_body.md <<'EOF'
|
||||
This PR contains the latest automated daily resume review.
|
||||
|
||||
Review file:
|
||||
- ${{ steps.context.outputs.review_path }}
|
||||
|
||||
Generated by `.github/workflows/daily-resume-review.yml` using:
|
||||
- Prompt: `.github/prompts/resume-review.prompt.md`
|
||||
- Source: `resume.tex`
|
||||
EOF
|
||||
|
||||
- name: Create Pull Request
|
||||
id: create-pull-request
|
||||
if: steps.review_guard.outputs.create_pr == 'true'
|
||||
uses: https://nayeonie.com/ahuston-0/create-pull-request@main
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN_FOR_UPDATES }}
|
||||
add-paths: ${{ steps.context.outputs.review_path }}
|
||||
body-path: pr_body.md
|
||||
author: '"github-actions[bot]" <github-actions[bot]@users.noreply.github.com>'
|
||||
title: 'automated: Daily resume review (${{ steps.context.outputs.review_date }})'
|
||||
commit-message: 'automated: Daily resume review (${{ steps.context.outputs.review_date }})'
|
||||
branch: automated/daily-resume-review
|
||||
delete-branch: true
|
||||
pr-labels: |
|
||||
automated
|
||||
review
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
Reference in New Issue
Block a user