feat(commands): add /gmail-sync - confirm-before-write status sync fr… #194
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |