add automated review
All checks were successful
Build and Release Resume PDF / date-fetch (push) Successful in 16s
Check flake.lock / Check health of `flake.lock` (push) Successful in 12s
Check Nix flake / Perform Nix flake checks (push) Successful in 59s
Build and Release Resume PDF / build (push) Successful in 1m27s
All checks were successful
Build and Release Resume PDF / date-fetch (push) Successful in 16s
Check flake.lock / Check health of `flake.lock` (push) Successful in 12s
Check Nix flake / Perform Nix flake checks (push) Successful in 59s
Build and Release Resume PDF / build (push) Successful in 1m27s
This commit is contained in:
2
.github/prompts/resume-review.prompt.md
vendored
2
.github/prompts/resume-review.prompt.md
vendored
@@ -29,7 +29,7 @@ If the build produces errors, include them as a finding under **Formatting & Lay
|
||||
Clean up any stale preview files, then convert the first page of the built PDF to a PNG and view it:
|
||||
|
||||
```
|
||||
rm -f resume-preview*.png
|
||||
rm -f resume-preview*.png || true # ignore if no previews exist
|
||||
pdftoppm -r 150 -png resume.pdf resume-preview
|
||||
```
|
||||
|
||||
|
||||
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
|
||||
42
resume.tex
42
resume.tex
@@ -249,26 +249,26 @@ to improve reliability in navigation}
|
||||
|
||||
% Certifications moved to Technical Skills & Certifications section above
|
||||
|
||||
%\section{Projects}
|
||||
%\resumeSubHeadingListStart
|
||||
%\resumeProjectHeading{SwitchForward}{Jun. 2020 -- Aug. 2020}
|
||||
%\resumeItemListStart
|
||||
%\resumeItem{A \textbf{Python}-based Telegram bot to send stock
|
||||
% updates for the Nintendo Switch during a supply shortage}
|
||||
%\resumeItem{Used the Gmail API to receive and parse emails from a
|
||||
% Google Group tracking Nintendo Switch stock}
|
||||
%\resumeItem{Sent updates to a Telegram announcements channel used by
|
||||
% \textbf{5-10} users}
|
||||
%\resumeItemListEnd
|
||||
%
|
||||
%\resumeProjectHeading{Autonomous Robot}{Aug. 2018 -- Dec. 2018}
|
||||
%\resumeItemListStart
|
||||
%\resumeItem{An \textbf{Arduino}-based robot designed to navigate
|
||||
% through a maze}
|
||||
%\resumeItem{Primarily worked on pathplanning and control in a dynamic setting}
|
||||
%\resumeItem{Implemented basic error-correction to account for drift
|
||||
% during navigation}
|
||||
%\resumeItemListEnd
|
||||
\section{Projects}
|
||||
\resumeSubHeadingListStart
|
||||
\resumeProjectHeading{SwitchForward}{Jun. 2020 -- Aug. 2020}
|
||||
\resumeItemListStart
|
||||
\resumeItem{A \textbf{Python}-based Telegram bot to send stock
|
||||
updates for the Nintendo Switch during a supply shortage}
|
||||
\resumeItem{Used the Gmail API to receive and parse emails from a
|
||||
Google Group tracking Nintendo Switch stock}
|
||||
\resumeItem{Sent updates to a Telegram announcements channel used by
|
||||
\textbf{5-10} users}
|
||||
\resumeItemListEnd
|
||||
|
||||
\resumeProjectHeading{Autonomous Robot}{Aug. 2018 -- Dec. 2018}
|
||||
\resumeItemListStart
|
||||
\resumeItem{An \textbf{Arduino}-based robot designed to navigate
|
||||
through a maze}
|
||||
\resumeItem{Primarily worked on pathplanning and control in a dynamic setting}
|
||||
\resumeItem{Implemented basic error-correction to account for drift
|
||||
during navigation}
|
||||
\resumeItemListEnd
|
||||
|
||||
% Removing this project as it is not as relevant to the software
|
||||
% engineering positions I am applying for
|
||||
@@ -285,6 +285,6 @@ to improve reliability in navigation}
|
||||
% resulting data}
|
||||
%\resumeItemListEnd
|
||||
|
||||
%\resumeSubHeadingListEnd
|
||||
\resumeSubHeadingListEnd
|
||||
|
||||
\end{document}
|
||||
|
||||
Reference in New Issue
Block a user