# What CI/CD Pipelines Are Implemented for ai-job-search: Complete GitHub Actions Workflow

> Explore the GitHub Actions CI/CD workflow for ai-job-search. This powerful single workflow manages linting, security, testing, vulnerability scanning, and more for efficient development.

- Repository: [Mads Lorentzen/ai-job-search](https://github.com/MadsLorentzen/ai-job-search)
- Tags: how-to-guide
- Published: 2026-07-25

---

**The ai-job-search repository uses a single GitHub Actions workflow defined in [`.github/workflows/ci.yml`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.github/workflows/ci.yml) that orchestrates seven distinct jobs covering linting, security enforcement, Python unit tests, dependency vulnerability scanning, LaTeX document compilation, CLI type-checking, and placeholder token integrity.**

The MadsLorentzen/ai-job-search project automates quality assurance through a comprehensive CI/CD pipeline. This workflow validates code integrity, security posture, and document generation every time changes are pushed to the repository or submitted via pull request.

## Pipeline Architecture and Workflow Location

The entire CI/CD system is centralized in one workflow file: [`.github/workflows/ci.yml`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.github/workflows/ci.yml). This file defines seven independent jobs that run in parallel on GitHub-hosted runners, each targeting a specific quality gate. By consolidating all checks into a single workflow, the repository maintains a unified view of build status while allowing individual job failures to be isolated and debugged independently.

## The Seven CI/CD Jobs Explained

### Lint Job

The **lint** job validates the integrity of skill definitions, command structures, and settings files. It runs on Python 3.12, installs PyYAML, and executes [`tools/lint_skills.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/lint_skills.py). On the upstream repository only, it additionally runs [`tools/check_framework_version.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/check_framework_version.py) to verify that forks align with the current framework version.

### Security Guards Job

The **security-guards** job enforces repository security policies by running [`tools/security_guards.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/security_guards.py). This script audits [`.claude/settings.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.claude/settings.json) for permission scope violations, validates `.gitignore` integrity, and ensures manifest files haven't been tampered with.

### Python Tests Job

The **python-tests** job discovers and executes the unit test suite using Python's built-in unittest framework. The command `python -m unittest discover -s tests -t . -v` runs all tests located in the `tests/` directory, ensuring that utility scripts maintain expected behavior across changes.

### Dependency Review Job

The **dependency-review** job scans newly added dependencies for known vulnerabilities using GitHub's Dependency Graph. It runs only on pull requests targeting the upstream repository and fails the build if any dependency introduces a high-severity vulnerability (`fail-on-severity: high`).

### LaTeX Smoke Tests

The **latex-smoke** job verifies that example CV and cover letter documents compile correctly and produce valid PDFs. It installs `poppler-utils` for PDF analysis, compiles `cv/main_example.tex` with **lualatex**, compiles `cover_letters/cover_example.tex` with **xelatex**, and validates the output using [`tools/verify_pdf.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/verify_pdf.py). Upstream builds additionally assert that placeholder tokens remain present to prevent accidental commits of personal data.

### CLI Checks Job

The **cli-checks** job type-checks portal-specific CLI tools built with TypeScript. It installs **bun**, runs `bun install` and `bun run typecheck` for each CLI located under `.agents/skills/*/cli/`, and executes any [`.test.ts`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.test.ts) files found in those directories.

### Placeholder Integrity Job

The **placeholder-integrity** job ensures that template files retain their placeholder tokens. It greps for required placeholders in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md), CV files, cover letters, and skill configuration files to prevent contributors from accidentally committing personal information.

## Pipeline Triggers and Execution Model

The workflow responds to three distinct events defined at the top of [`ci.yml`](https://github.com/MadsLorentzen/ai-job-search/blob/main/ci.yml):

```yaml
on:
  push:
    branches: [master]
  pull_request:
  workflow_dispatch:

```

- **Push to master**: Automatically validates the main branch after every merge.
- **Pull requests**: Runs the full suite on every PR to catch issues before merging.
- **Workflow dispatch**: Allows manual triggering via the GitHub UI for debugging or release preparation.

## Security Configuration and Permissions

The workflow operates with **read-only** repository permissions (`contents: read`), limiting the blast radius of potential security compromises. All third-party actions are pinned to specific commit SHAs rather than floating version tags, ensuring reproducible builds. The `security-guards` job provides an additional layer of protection by auditing the repository's permission configuration and `.gitignore` rules.

## Local Development and Debugging

You can replicate most CI checks locally to debug failures before pushing. To run the lint job locally:

```bash

# Set up Python environment

python3 -m venv .venv && source .venv/bin/activate
pip install pyyaml

# Execute the lint script

python tools/lint_skills.py

```

To manually trigger the workflow via API (useful for automation scripts):

```bash
curl -X POST \
  -H "Authorization: Bearer <YOUR_GITHUB_TOKEN>" \
  -H "Accept: application/vnd.github+json" \
  https://api.github.com/repos/MadsLorentzen/ai-job-search/actions/workflows/ci.yml/dispatches \
  -d '{"ref":"master"}'

```

Replace `<YOUR_GITHUB_TOKEN>` with a personal access token possessing the **repo** scope.

## Extending the Pipeline

New CI steps can be added by extending the `jobs:` section in [`.github/workflows/ci.yml`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.github/workflows/ci.yml). Because each job runs on an isolated runner, additional verification steps—such as code coverage analysis or Docker image builds—can be introduced without affecting existing checks. To add a new portal CLI to the type-checking matrix, simply create the directory structure `.agents/skills/<new-portal>/cli/` with a [`package.json`](https://github.com/MadsLorentzen/ai-job-search/blob/main/package.json) and TypeScript entry point; the `cli-checks` job will automatically include it.

## Summary

- The ai-job-search CI/CD pipeline is defined entirely in [`.github/workflows/ci.yml`](https://github.com/MadsLorentzen/ai-job-search/blob/main/.github/workflows/ci.yml) and runs on GitHub Actions.
- **Seven specialized jobs** handle linting, security guards, Python tests, dependency reviews, LaTeX compilation, CLI type-checking, and placeholder integrity.
- The workflow triggers on pushes to master, pull requests, and manual workflow dispatch events.
- Security hardening includes read-only permissions, SHA-pinned actions, and automated policy enforcement via [`tools/security_guards.py`](https://github.com/MadsLorentzen/ai-job-search/blob/main/tools/security_guards.py).
- Local debugging is supported by running the underlying Python and bash scripts directly from the `tools/` directory.

## Frequently Asked Questions

### What triggers the CI/CD pipeline in ai-job-search?

The pipeline runs on three events: pushes to the `master` branch, any pull request creation or update, and manual triggers via the GitHub Actions "Workflow dispatch" button. This configuration ensures that both automated merges and proposed changes receive full validation.

### How does the pipeline prevent accidental commits of personal data?

The **placeholder-integrity** job greps for required placeholder tokens in [`CLAUDE.md`](https://github.com/MadsLorentzen/ai-job-search/blob/main/CLAUDE.md), CV files, and cover letters. Additionally, the **latex-smoke** job (on upstream only) asserts that example documents still contain placeholder text, failing the build if personal information has been substituted.

### Can I run the CI checks locally before pushing?

Yes. Most checks can be executed locally by running the underlying scripts. For example, execute `python tools/lint_skills.py` after installing PyYAML, or run `python -m unittest discover -s tests -t . -v` to execute the Python test suite. The LaTeX compilation requires local installations of lualatex and xelatex.

### Why does the workflow use both lualatex and xelatex for LaTeX compilation?

The **latex-smoke** job uses **lualatex** to compile `cv/main_example.tex` and **xelatex** to compile `cover_letters/cover_example.tex`, matching the specific engine requirements of each document template. This ensures that the example files compile correctly regardless of which LaTeX features they utilize.