GitHub Actions and CI

GitHub Actions is used in this project to run automated checks.

These checks are part of continuous integration, often shortened to CI.

What is CI?

Continuous integration means that important project checks run automatically when changes are pushed or proposed in a pull request.

In this guide, CI runs:

  • linting,
  • formatting checks,
  • tests,
  • distribution builds.

The goal is simple:

A pull request should not be merged if the basic project checks are failing.

Why CI matters

Running checks locally is important.

However, local checks depend on the developer remembering to run them.

CI gives the project an additional safety net.

It helps answer questions such as:

  • Does the project work on a clean machine?
  • Did the tests pass outside the developer's local environment?
  • Is the formatting still consistent?
  • Did the pull request break the main branch expectations?

CI does not replace careful review.

It supports review by checking repetitive things automatically.

Local checks and CI checks

The CI workflow should run the same checks that developers run locally.

In this project, the local checks are:

uv run ruff check .
uv run ruff format --check .
uv run rumdl check .
uv run mkdocs build --strict
uv run pytest --cov=text_toolkit --cov-report=term-missing
uv run mypy
uv build --no-sources

The GitHub Actions workflow runs the same commands.

This is important because local development and CI should not behave like two unrelated systems.

A developer should be able to run the same checks locally before opening a pull request.

Workflow file

GitHub Actions workflows are stored in:

.github/workflows/

This project uses:

.github/workflows/ci.yml

The workflow is named:

name: CI

This name appears in the GitHub Actions interface and in pull request checks.

When the workflow runs

The workflow runs on:

on:
  push:
    branches: ["main"]
  pull_request:
    branches: ["main"]

This means CI runs when:

  • code is pushed to main,
  • a pull request targets main.

Running CI on pull requests helps catch problems before merging.

Running CI on main helps confirm that the main branch remains healthy after merges.

The job

The workflow defines one job:

jobs:
  quality:
    name: Quality checks
    runs-on: ubuntu-latest

The job runs on a GitHub-hosted Ubuntu runner.

This gives the project a clean environment for every CI run.

Checkout step

The first step checks out the repository:

- name: Checkout repository
  uses: actions/checkout@v6

Without this step, the workflow would not have access to the project files.

Installing uv

The workflow installs uv:

- name: Install uv
  uses: astral-sh/setup-uv@v8.1.0

This makes the uv command available in the CI environment.

The project uses uv locally, so CI should use it too.

Setting up Python

The workflow installs Python:

- name: Set up Python
  run: uv python install

The project contains a .python-version file, so uv knows which Python version should be used.

This keeps local development and CI aligned.

Installing dependencies

The workflow installs dependencies with:

- name: Install dependencies
  run: uv sync --locked --group dev

The --locked option tells uv to use the existing lockfile.

This is important in CI because the workflow should check the project using the dependency versions recorded in uv.lock.

The --group dev option installs development dependencies such as:

  • pytest,
  • pytest-cov,
  • mkdocs,
  • mypy,
  • ruff,
  • rumdl.

These tools are needed for checks, but they are not runtime dependencies of the package.

Ruff linting

The workflow runs Ruff linting:

- name: Run Ruff linting
  run: uv run ruff check .

This checks the project for selected linting issues.

Examples include:

  • unused imports,
  • import ordering,
  • suspicious code patterns,
  • syntax modernization suggestions.

Formatting check

The workflow checks formatting:

- name: Check formatting
  run: uv run ruff format --check .

This does not modify files.

It only verifies whether files are already formatted correctly.

If formatting is wrong, the command fails.

The developer can fix formatting locally with:

uv run ruff format .

Markdown linting

The workflow checks Markdown structure and consistency:

- name: Lint Markdown
  run: uv run rumdl check .

The command uses the [tool.rumdl] configuration in pyproject.toml. It does not replace the pytest check for local documentation links or human review.

Documentation site build

The workflow builds the MkDocs site with strict validation:

- name: Build documentation site
  run: uv run mkdocs build --strict

The step validates navigation and rendering without publishing the generated site. The site/ output directory remains an ignored local artifact.

Publishing is handled by the separate .github/workflows/pages.yml workflow after relevant changes reach main. Pull requests never receive deployment permissions. See Publishing with GitHub Pages for the build, artifact, environment, and permission flow.

Tests and coverage

The workflow runs tests and prints a coverage report:

- name: Run tests with coverage
  run: uv run pytest --cov=text_toolkit --cov-report=term-missing

This verifies the behavior of the example package while measuring statement and branch coverage. The terminal report shows missing source lines, but the project does not fail the workflow based on a coverage percentage.

At this stage, the tests check:

  • whitespace normalization,
  • word counting,
  • character counting.

Type checking

The workflow runs mypy after the tests:

- name: Run type checking
  run: uv run mypy

The command reads [tool.mypy] from pyproject.toml and checks the configured src/ and tests/ directories in strict mode.

Running the same command locally and in CI prevents type-checking results from depending on a developer remembering an extra manual step.

Distribution build

The workflow builds the package distributions:

- name: Build distributions
  run: uv build --no-sources

This creates a wheel and source distribution. The command verifies that the package metadata and included files are sufficient for a standard build.

The workflow does not publish or upload the generated files.

Why one job?

This project currently uses one job called quality.

That is enough for the current stage.

The job is simple and readable.

Later, a larger project might split checks into separate jobs, for example:

  • linting,
  • type checking,
  • tests,
  • documentation,
  • packaging.

However, splitting too early would make the workflow more complex without much benefit.

Why no cache yet?

Caching can make CI faster.

However, this guide starts with a minimal workflow.

A beginner should first understand:

  • what the workflow does,
  • when it runs,
  • how dependencies are installed,
  • how checks are executed.

Caching can be added later as an optimization.

The first version should prioritize clarity.

Green checks

A green CI check means that the automated checks passed.

It does not mean the pull request is automatically good.

It means the pull request passed the basic mechanical checks.

For this project, that also means the documentation site, wheel, and source distribution were built successfully.

A human review is still needed for questions such as:

  • Is the change useful?
  • Is the design clear?
  • Is the documentation understandable?
  • Is the code easy to maintain?
  • Does the change fit the project direction?

Rule of thumb

CI should protect the main branch.

A good CI workflow should be:

  • easy to understand,
  • easy to run locally in similar form,
  • strict enough to catch common mistakes,
  • simple enough that beginners can read it.

The goal is not to create a complicated automation system.

The goal is to make every pull request safer.

Continue