Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 35 additions & 26 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,36 +52,38 @@ input.

## Purpose

This repo is the single source of truth for linter configs across all rubykatzen repositories. The goal is identical linting everywhere β€” configs live here and nowhere else.

Baseline owns linter configuration and runtime installation for CI. Consuming
repositories call `lint-shared.yml` and get runtimes, configs, and linter
execution handled automatically. Pre-commit hooks are thin wrappers that expect
tools to already be installed in the developer environment.
Baseline is the centrally managed source of development policy for repositories
across organizations and programming language stacks. Its goal is a
zero-configuration, homogeneous development experience for developers who move
between stacks.

Baseline owns linter configuration and CI runtime installation, GitHub repository
policy, required content fragments, and reusable notification workflows.
Consumers connect only the workflows they need. Checks verify policy but do not
mutate consumer repositories; intentional differences use explicit `skip`
inputs. Pre-commit hooks are thin wrappers that expect tools to already be
installed in the developer environment.

## Repository Structure

- `config/` β€” canonical linter config files
- `config/` β€” canonical linter, GitHub policy, and Embedder configuration
- `hooks/` β€” shell and Python script wrappers for pre-commit (`language: script`)
- `baseline.gemspec` β€” Ruby gem packaging RuboCop and erb_lint configs for local `bundle exec rubocop`
- `baseline.gemspec` β€” Ruby gem packaging RuboCop, erb_lint, and Herb tooling
- `lib/` β€” gem code (`Baseline::VERSION`, install stubs)
- `exe/baseline-install` β€” writes project `.rubocop.yml` and `.erb_lint.yml` stubs
- `.github/actions/lint-*/` β€” composite actions that run installed linters with baseline configs
- `.github/actions/check-embedder/` β€” validates required content fragments in consumer files
- `.github/actions/check-embedder/` β€” validates base and named extra content fragments
- `.github/actions/check-github-config/` β€” validates repository settings and labels
- `.github/actions/detect-linters/` β€” composite action that selects applicable linters from tracked files
- `.github/actions/check-precommit/` β€” composite action: verifies pre-commit hooks match detected CI linters
- `.github/actions/setup-runtimes/` β€” installs Python packages, Ruby, and standalone binaries for requested linters; Python is provided by the runner
- `.github/actions/prepare-telegram-pr-message/` β€” formats pull request and open-PR digest messages
- `.github/actions/prepare-telegram-issue-message/` β€” formats closed-issue messages
- `.github/actions/send-telegram-message/` β€” sends plain-text messages through the Telegram Bot API
- `.github/workflows/lint-shared.yml` β€” reusable workflow exported for consuming repos: setup + lint
- `.github/workflows/embedder-shared.yml` β€” reusable workflow exported for required content validation
- `.github/workflows/notify-telegram-pr-shared.yml` β€” reusable pull request notifications and open-PR digest
- `.github/workflows/notify-telegram-issue-shared.yml` β€” optional reusable issue-closure notifications
- `.github/actions/*telegram*/` β€” prepares and sends Telegram notifications
- `.github/workflows/*-shared.yml` β€” reusable workflows exported to consumers
- `.github/workflows/lint.yml`, `github.yml`, and `embedder.yml` β€” local self-check callers
- `.github/workflows/pr.yml` β€” validates Baseline pull request titles against Conventional Commits
- `.github/workflows/lint.yml` β€” baseline self-lint (uses local `./` references, not `@vX`)
- `.github/workflows/release.yml` β€” maintains the release PR and publishes merged releases
- `.github/workflows/notify-telegram-pr.yml` β€” Baseline's own Telegram pull request notification caller
- `.github/workflows/test.yml` β€” runs the Python test suite
- `.pre-commit-hooks.yaml` β€” hook definitions for pre-commit

## Adding a New Linter
Expand All @@ -104,6 +106,15 @@ Pre-commit hooks expect tools to already be on PATH in the developer environment

## Workflows

`lint-shared.yml` auto-detects applicable linters, installs their CI runtimes,
checks pre-commit synchronization when configured, and runs the canonical
tooling.

`github-shared.yml` verifies repository settings and labels against
`config/github.yml`. `embedder-shared.yml` verifies the base fragment set and
named configurations selected through its `extra` JSON input. Extra fragment
IDs use the `<configuration>/<fragment>` namespace in `skip`.

`notify-telegram-pr-shared.yml` exports pull request notifications and the
open-pull-request digest. Baseline calls it locally through
`notify-telegram-pr.yml`.
Expand All @@ -113,19 +124,16 @@ The caller owns any label or other notification condition; the shared workflow
does not modify issues. Baseline does not call it because this repository does
not use issue notifications.

`lint-shared.yml` is the primary export β€” consuming repos call it via
`uses: rubykatzen/baseline/.github/workflows/lint-shared.yml@VERSION`.

Pre-commit hook pins in `.pre-commit-config.yaml` are updated by Dependabot
(`package-ecosystem: pre-commit` in `.github/dependabot.yml`), not by a custom
workflow.

## Self-linting
## Self-validation

Baseline lints itself through `.github/workflows/lint.yml`, which calls the
shared reusable workflow `.github/workflows/lint-shared.yml` using a local
`./` reference. This ensures the current commit's actions and configs are
validated, not a pinned release.
Baseline calls its lint, GitHub, and Embedder reusable workflows through local
`./` references. This validates the current commit's actions and configurations,
not a pinned release. The Embedder self-check enables the `release-please` extra
configuration.

## Cutting Releases

Expand All @@ -145,7 +153,8 @@ For language-agnostic file types (YAML, Markdown, shell, etc.), always choose th

Priority order: Python > TypeScript > everything else.

This avoids introducing new runtimes into repos that don't already use them.
This minimizes setup by preferring the Python runtime already present on GitHub
runners before introducing another toolchain.

## Disabled Rules

Expand Down
85 changes: 29 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
# Baseline

Baseline simplifies managing repositories across multiple organizations and
programming language stacks. It provides a homogeneous development environment
for language-agnostic developers and a centrally managed baseline for repository
owners.
Baseline gives developers who move between language stacks a zero-configuration,
homogeneous development experience across repositories and organizations.

Connect a workflow and Baseline supplies the policy, configuration, runtimes,
and implementation. Consumer repositories only declare which capabilities they
want and, when necessary, explicit exceptions.
Connect the workflows a repository needs. Baseline detects applicable linters,
supplies their CI runtimes and configurations, and verifies shared policies and
files. Repositories satisfy those policies or declare explicit exceptions.

The examples use the latest
[Baseline release](https://github.com/rubykatzen/baseline/releases) and are
updated automatically with every release. Dependabot keeps the pin current in
consumer repositories.

## Lint automatically
## ⚑ Lint automatically

Create `.github/workflows/lint.yml`:

Expand All @@ -41,7 +39,7 @@ linter on the next run.
When the repository contains `.pre-commit-config.yaml`, Baseline also checks
that its local hooks match the automatically selected CI linters.

## Check GitHub configuration
## βš™οΈ Check GitHub configuration

Create `.github/workflows/github.yml`:

Expand All @@ -65,7 +63,7 @@ Baseline checks the repository settings and labels against
automatic branch deletion, auto-merge, and the canonical label set and colors.
Release Please labels are allowed but optional.

## Check shared repository files
## πŸ“„ Check shared repository files

Create `.github/workflows/embedder.yml`:

Expand Down Expand Up @@ -107,7 +105,7 @@ Optional configurations live under [`config/embedder/`](config/embedder/). The
`release-please` configuration checks the common release configuration, workflow,
and pull request title policy while leaving package-specific publishing local.

## Notify Telegram about pull requests
## πŸ”” Notify Telegram about pull requests

Create `.github/workflows/notify-telegram-pr.yml`:

Expand Down Expand Up @@ -140,7 +138,7 @@ workflow code from the default branch. The shared workflow does not check out
or execute pull request code. Pass the two secrets explicitly rather than using
`secrets: inherit`.

## Notify Telegram about closed issues
## πŸ“¬ Notify Telegram about closed issues

Issue notifications are optional and can use a different Telegram channel from
pull request notifications. Create `.github/workflows/notify-telegram-issue.yml`:
Expand All @@ -167,7 +165,7 @@ The caller decides which closed issues should produce a notification. Change or
remove the `if` condition to match the repository's policy. Baseline only
formats and sends the message; it does not modify the issue or its labels.

## Explicit exceptions
## ⏭️ Explicit exceptions

Automatic policy is the default. When a repository intentionally differs, pass
a JSON array through `skip`:
Expand Down Expand Up @@ -209,7 +207,7 @@ Omit `release-please` from `extra` instead when none of its policy should apply.
Unknown names fail the workflow. A skipped linter must also be absent from the
Baseline entry in `.pre-commit-config.yaml`, keeping local and CI linting equal.

## Local linting
## πŸ’» Local linting

CI runtime installation is automatic. For local pre-commit use, create
`.pre-commit-config.yaml` and keep only the hooks relevant to the repository:
Expand Down Expand Up @@ -238,14 +236,14 @@ Pre-commit hooks are thin wrappers and expect their tools on `PATH`. Install the
Python and standalone tools used by the repository:

```bash
python -m pip install yamllint pymarkdownlnt ruff tombi
python -m pip install pre-commit yamllint pymarkdownlnt ruff tombi
brew install shellcheck actionlint
```

### Ruby projects
### πŸ’Ž Ruby projects

Ruby projects get RuboCop and erb_lint, with Baseline's configuration, from one
gem:
Ruby projects get RuboCop, erb_lint, and Herb, with Baseline's configuration,
from one gem:

```ruby
group :development, :test do
Expand All @@ -264,59 +262,34 @@ the gem. Project-specific existing violations can remain in
`.rubocop_todo.yml` or `.erb_lint_todo.yml`; Baseline continues to catch new
violations.

## Keep Baseline current
## πŸ”„ Keep Baseline current

Add GitHub Actions and pre-commit ecosystems to `.github/dependabot.yml` so
Dependabot updates every Baseline pin:

```yaml
version: 2
updates:
- package-ecosystem: github-actions
directory: /
labels: [dependencies]
commit-message:
prefix: chore
include: scope
schedule:
interval: daily
time: "10:00"
timezone: "Europe/Berlin"
- package-ecosystem: pre-commit
directory: /
labels: [dependencies]
commit-message:
prefix: chore
include: scope
schedule:
interval: daily
time: "10:00"
timezone: "Europe/Berlin"
```
Dependabot keeps workflow references, pre-commit hooks, Python packages, and
Ruby gems current. The Embedder workflow verifies the canonical
`.github/dependabot.yml`, so consumer repositories do not maintain that
configuration independently.

Dependabot opens `chore(deps):` pull requests, which do not request a release by
default. Rename a release-worthy dependency update to `fix(deps):` to request a
patch release.

## Supported linters
## 🧰 Supported linters

| Key | Files | Configuration |
|---|---|---|
| `yamllint` | `*.yml`, `*.yaml` | `config/yamllint.yml` |
| `pymarkdown` | `*.md` | `config/pymarkdown.json` |
| `ruff` | `*.py` | `config/ruff.toml` |
| `pymarkdown` | Markdown | `config/pymarkdown.json` |
| `ruff` | Python source and stubs | `config/ruff.toml` |
| `tombi` | `*.toml` | consumer config or Tombi defaults |
| `shellcheck` | `*.sh` | `config/shellcheck.rc` |
| `actionlint` | `.github/workflows/*.yml` | actionlint defaults |
| `rubocop` | `*.rb` | `config/rubocop.yml` |
| `erb-lint` | `*.erb` | `config/erb_lint.yml` |
| `actionlint` | GitHub Actions workflows | actionlint defaults |
| `rubocop` | Ruby source, Gemfiles, Rakefiles, and gemspecs | `config/rubocop.yml` |
| `erb-lint` | HTML ERB templates | `config/erb_lint.yml` |
| `herb` | HTML and Rails template variants | Herb defaults |

See [LINTERS-DEFAULTS-OVERRIDES.md](LINTERS-DEFAULTS-OVERRIDES.md) for deliberate
deviations from upstream linter defaults.

## Releasing Baseline
## πŸ“ License

Release Please maintains a release pull request from Conventional Commits on
`main`. Merging it creates the version tag and GitHub Release, publishes the
Ruby gem, and updates the floating major and minor tags used by consumers.
Baseline is released under the [MIT License](LICENSE).