Skip to content

Repository files navigation

Baseline

Baseline gives developers who move between language stacks a zero-configuration, homogeneous development experience across repositories and organizations.

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 and are updated automatically with every release. Dependabot keeps the pin current in consumer repositories.

Lint automatically

Create .github/workflows/lint.yml:

name: Linton:
push:
branches: ["main"]pull_request:
jobs:
lint:
uses: rubykatzen/baseline/.github/workflows/lint-shared.yml@v0.17.2

That is enough to get CI linting. Baseline inspects the tracked files, selects every applicable linter, installs its runtime, and runs it with the canonical configuration. Adding a new supported file type automatically enables its 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

Create .github/workflows/github.yml:

name: GitHubon:
push:
branches: ["main"]pull_request:
jobs:
github:
permissions:
contents: readissues: readuses: rubykatzen/baseline/.github/workflows/github-shared.yml@v0.17.2

Baseline checks the repository settings and labels against config/github.yml. This includes squash-only merging, automatic branch deletion, auto-merge, and the canonical label set and colors. Release Please labels are allowed but optional.

The caller grants both permissions because a reusable workflow can reduce its caller's GITHUB_TOKEN permissions, but cannot elevate them.

Check shared repository files

Create .github/workflows/embedder.yml:

name: Embedderon:
push:
branches: ["main"]pull_request:
jobs:
embedder:
uses: rubykatzen/baseline/.github/workflows/embedder-shared.yml@v0.17.2

Baseline checks that the repository contains every fragment declared in config/embedder.yml, including the shared Dependabot configuration and agent instructions. A failure reports all differences, not only the first missing fragment.

Repositories using an optional shared policy can add its Embedder configuration:

jobs:
embedder:
uses: rubykatzen/baseline/.github/workflows/embedder-shared.yml@v0.17.2with:
extra: '["release-please"]'

Optional configurations live under 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

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

name: Notify Telegram PRon:
pull_request_target:
types: [opened, ready_for_review, reopened, closed]schedule:
- cron: "0 10 * * *"workflow_dispatch:
jobs:
notify:
uses: rubykatzen/baseline/.github/workflows/notify-telegram-pr-shared.yml@v0.17.2with:
telegram-chat-id: ${{ vars.TELEGRAM_CHAT_ID }}secrets:
telegram-bot-token: ${{ secrets.TELEGRAM_BOT_TOKEN }}

Baseline sends notifications for opened, reopened, ready, and merged pull requests, plus a daily digest of up to ten open non-draft pull requests. Empty digests do not send a message.

pull_request_target keeps Telegram secrets available while loading trusted workflow code from the default branch. The shared workflow does not check out or execute pull request code. Store TELEGRAM_CHAT_ID as an Actions variable, keep TELEGRAM_BOT_TOKEN as a secret, and pass both explicitly rather than using secrets: inherit.

Notify Telegram about releases

Create .github/workflows/notify-telegram-release.yml:

name: Notify Telegram releaseon:
release:
types: [published]jobs:
notify:
uses: rubykatzen/baseline/.github/workflows/notify-telegram-release-shared.yml@v0.17.2with:
telegram-chat-id: ${{ vars.TELEGRAM_CHAT_ID }}secrets:
telegram-bot-token: ${{ secrets.TELEGRAM_BOT_TOKEN }}

Baseline sends one notification when a release or prerelease is published. The release tag links to the GitHub Release. Pass the chat ID as an Actions variable and the bot token as a secret.

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:

name: Notify Telegram issueon:
issues:
types: [closed]jobs:
notify:
if: contains(github.event.issue.labels.*.name, 'notify')uses: rubykatzen/baseline/.github/workflows/notify-telegram-issue-shared.yml@v0.17.2with:
telegram-chat-id: ${{ vars.TELEGRAM_ISSUE_CHAT_ID }}secrets:
telegram-bot-token: ${{ secrets.TELEGRAM_BOT_TOKEN }}

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

Automatic policy is the default. When a repository intentionally differs, pass a JSON array through skip:

jobs:
lint:
uses: rubykatzen/baseline/.github/workflows/lint-shared.yml@v0.17.2with:
skip: '["rubocop", "herb"]'

The GitHub and Embedder workflows use the same convention:

with:
skip: '["labels"]'
with:
skip: '["message-prefix"]'

Fragments from an optional Embedder configuration use the <configuration>/<fragment> namespace and can be skipped independently:

with:
extra: '["release-please"]'skip: '["release-please/include-component-in-tag"]'

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

CI runtime installation is automatic. For local pre-commit use, create .pre-commit-config.yaml and keep only the hooks relevant to the repository:

repos:
- repo: https://github.com/rubykatzen/baselinerev: v0.17.2hooks:
- id: yamllint
- id: pymarkdown
- id: ruff
- id: tombi
- id: shellcheck
- id: actionlint
- id: rubocop
- id: erb-lint
- id: herb

Pre-commit hooks are thin wrappers and expect their tools on PATH. Install the Python and standalone tools used by the repository:

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

Ruby projects

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

group:development,:testdogem"rubykatzen-baseline",require: falseend

After bundle install, create the project config stubs:

bundle exec baseline-install

The generated .rubocop.yml and .erb_lint.yml inherit the configs shipped in 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

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

KeyFilesConfiguration
yamllint*.yml, *.yamlconfig/yamllint.yml
pymarkdownMarkdownconfig/pymarkdown.json
ruffPython source and stubsconfig/ruff.toml
tombi*.tomlconsumer config or Tombi defaults
shellcheck*.shconfig/shellcheck.rc
actionlintGitHub Actions workflowsactionlint defaults
rubocopRuby source, Gemfiles, Rakefiles, and gemspecsconfig/rubocop.yml
erb-lintHTML ERB templatesconfig/erb_lint.yml
herbHTML and Rails template variantsHerb defaults

See LINTERS-DEFAULTS-OVERRIDES.md for deliberate deviations from upstream linter defaults.

License

Baseline is released under the MIT License.

About

A zero-configuration, homogeneous development baseline across GitHub repositories and language stacks.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages