# 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 assertions run only on the upstream template repo. Compile # success and lint correctness 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). # CLIs get typechecked instead; live 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 - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.12" - run: pip install pyyaml - run: python tools/lint_skills.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 dependency-review: name: Dependency review if: github.event_name == 'pull_request' runs-on: ubuntu-latest steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - 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: Compile CV example (lualatex) run: | cd cv lualatex -interaction=nonstopmode 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 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: Assert exact page counts (upstream template only) if: github.repository == 'MadsLorentzen/ai-job-search' run: | grep -q 'Output written on main_example.pdf (2 pages' cv/main_example.log \ || { echo '::error::cv/main_example.tex no longer compiles to exactly 2 pages'; exit 1; } grep -q 'Output written on cover_example.pdf (1 page' cover_letters/cover_example.log \ || { echo '::error::cover_letters/cover_example.tex no longer compiles to exactly 1 page'; exit 1; } cli-typecheck: name: Typecheck ${{ matrix.tool }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: tool: - 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 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 '