Skip to content

feat(commands): add /gmail-sync - confirm-before-write status sync fr… #194

feat(commands): add /gmail-sync - confirm-before-write status sync fr…

feat(commands): add /gmail-sync - confirm-before-write status sync fr… #194

Workflow file for this run

# CI for the framework itself: LaTeX smoke compiles, skill/command lint,
# CLI typechecks, and (upstream only) placeholder integrity.
#
# Fork-friendly by design: forks personalize CLAUDE.md, the skill files, and
# cv/main_example.tex via /setup, so the placeholder-integrity job and the
# exact page-count/content assertions run only on the upstream template repo.
# Compile success, lint correctness, and an extractable PDF text layer are
# asserted everywhere.
#
# Deliberately NOT here: live smoke tests of the job-portal CLIs. They hit
# real portals (network-flaky, and the linkedin-search skill is personal-use
# only per its own ToS warning - CI-automated requests would violate that).
# CI runs typechecks and the checked-in fixture/mock test suites only; live
# portal testing stays a local, on-demand step.
#
# Security posture: this template ships pre-approved Claude Code permissions
# and CLI code that every fork user executes, so the security-guards job
# fails PRs that widen settings.json permissions, weaken the personal-data
# gitignore rules, or add package lifecycle scripts; dependency-review flags
# newly introduced vulnerable/malicious dependencies. Honest limit: a PR can
# edit this workflow itself, so these guards catch accidents and casual
# attempts, not a determined author - branch protection with required checks
# and human review of workflow/settings diffs remain the real backstop.
# Actions are pinned to commit SHAs; the token is read-only.
name: CI
on:
push:
branches: [master]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
lint:
name: Lint skills, commands, settings
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- run: pip install pyyaml
- run: python tools/lint_skills.py
- name: Framework version guard (upstream template only)
if: github.repository == 'MadsLorentzen/ai-job-search'
run: python tools/check_framework_version.py
security-guards:
name: Security guards (permissions, gitignore, manifests)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- run: python tools/security_guards.py
python-tests:
name: Python tool tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- run: python -m unittest discover -s tests -t . -v
dependency-review:
name: Dependency review (upstream PRs only)
# Requires the repo's Dependency graph, which forks never inherit and
# which may be disabled upstream - so: upstream PRs only, and the
# graph is probed first. If it is unavailable, the job warns and
# passes instead of hard-failing (the same graceful-skip pattern the
# workflow uses for optional tools). Enabling Dependency graph under
# Settings -> Advanced Security activates the real check.
if: github.event_name == 'pull_request' && github.repository == 'MadsLorentzen/ai-job-search'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Probe Dependency graph availability
id: graph
run: |
code=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${{ github.token }}" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${{ github.repository }}/dependency-graph/sbom")
if [ "$code" = "200" ]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
else
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "::warning::Dependency graph is not enabled on this repository (HTTP $code). Dependency review was skipped - enable Dependency graph under Settings -> Advanced Security to activate this check."
fi
- name: Dependency review
if: steps.graph.outputs.enabled == 'true'
uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0
with:
fail-on-severity: high
latex-smoke:
name: Compile example CV and cover letter
runs-on: ubuntu-latest
container: texlive/texlive:latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install PDF inspection tools
run: |
if ! command -v pdfinfo >/dev/null || ! command -v pdftotext >/dev/null; then
apt-get update
apt-get install -y --no-install-recommends poppler-utils
fi
- name: Compile CV example (lualatex)
run: |
cd cv
lualatex -interaction=nonstopmode -halt-on-error main_example.tex
test -f main_example.pdf
if grep -q '^!' main_example.log; then
echo '::error::lualatex reported errors compiling cv/main_example.tex'
grep -A3 '^!' main_example.log
exit 1
fi
- name: Compile cover letter example (xelatex)
run: |
cd cover_letters
xelatex -interaction=nonstopmode -halt-on-error cover_example.tex
test -f cover_example.pdf
if grep -q '^!' cover_example.log; then
echo '::error::xelatex reported errors compiling cover_letters/cover_example.tex'
grep -A3 '^!' cover_example.log
exit 1
fi
- name: Verify extractable PDF text
run: |
python3 tools/verify_pdf.py cv/main_example.pdf --min-chars 100
python3 tools/verify_pdf.py cover_letters/cover_example.pdf --min-chars 100
- name: Assert stock PDF structure (upstream template only)
if: github.repository == 'MadsLorentzen/ai-job-search'
run: |
python3 tools/verify_pdf.py cv/main_example.pdf \
--pages 2 \
--contains '[your.email@example.com]' \
--contains 'Professional Experience'
python3 tools/verify_pdf.py cover_letters/cover_example.pdf \
--pages 1 \
--contains 'your.email@example.com' \
--contains 'Dear [Hiring Manager / Team]'
cli-checks:
name: CLI checks ${{ matrix.tool }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
tool:
- freehire-search
- jobbank-search
- jobdanmark-search
- jobindex-search
- jobnet-search
- linkedin-search
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
- run: bun install
working-directory: .agents/skills/${{ matrix.tool }}/cli
- run: bun run typecheck
working-directory: .agents/skills/${{ matrix.tool }}/cli
- name: Run fixture/mock tests when present
run: |
if find tests -type f -name '*.test.ts' | grep -q .; then
bun test
else
echo "No Bun tests found for ${{ matrix.tool }}; skipping"
fi
working-directory: .agents/skills/${{ matrix.tool }}/cli
placeholder-integrity:
name: Placeholder integrity (upstream template only)
if: github.repository == 'MadsLorentzen/ai-job-search'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Tracked template files must keep their placeholder tokens
run: |
fail=0
check() {
if ! grep -q "$2" "$1"; then
echo "::error file=$1::expected placeholder token $2 - personal data may have been committed"
fail=1
fi
}
check CLAUDE.md '\[YOUR_NAME\]'
check cv/main_example.tex '\[YOUR_NAME\]'
check cover_letters/cover_example.tex '\[YOUR NAME\]'
check .claude/skills/job-application-assistant/01-candidate-profile.md '<!-- SETUP'
check .claude/skills/job-application-assistant/04-job-evaluation.md '\[YOUR_PRIMARY_SKILLS\]'
exit $fail