Use cases

Migrating from Repolinter to alint

Repolinter, the TODO Group's checker for the files an open-source repository should have, was archived on 6 February 2026. Its last release, v0.12.0, came out in May 2025. This guide maps its 19 rule types and its default ruleset to alint, gives you that ruleset as an alint config and lists the checks that need a different tool.

What changes

Repolinteralint
repolinter.json or .yaml, rules as a map.alint.yml, rules as a list with an id each
A type and glob options per ruleA kind and paths per rule, with include and exclude lists
Axioms (linguist, licensee, packagers, contributor-count) and where:Facts you declare, such as any_file_exists, and when: conditions
nocase on globsCase-sensitive globs; list each spelling, as in {README,readme}*, or give each letter a class, as in [Rr][Ee][Aa][Dd][Mm][Ee]*
Levels error, warning and offLevels error, warning, info and off; a bundled rule's level can be changed by its id
Fixes: create, modify or remove a file26 kinds of fix, applied by alint fix
Output: console, JSON, Markdown8 formats, including SARIF and GitHub annotations
Node.js, plus the Ruby gems linguist and licensee for the axiomsOne static binary

Watch the names and levels. The bundled oss-baseline ruleset (15 rules) has README, LICENSE, SECURITY.md and code of conduct checks, but it accepts fewer file names than Repolinter did, warns about README and LICENSE and only informs about the other two. The translated config in the next section checks Repolinter's file names and makes all four errors, as Repolinter's default ruleset does.

oss-baseline also checks for merge-conflict markers and Unicode bidi controls, which Repolinter's default config doesn't.

A config to start from

Two rules from Repolinter's default ruleset, one of them with a where: condition. Beside them, the same two rules in alint: a fact stands in for linguist's language detection.

repolinter.json
{
  "version": 2,
  "axioms": { "linguist": "language" },
  "rules": {
    "license-file-exists": {
      "level": "error",
      "rule": {
        "type": "file-existence",
        "options": { "globsAny": ["LICENSE*", "COPYING*"], "nocase": true }
      }
    },
    "javascript-package-metadata-exists": {
      "level": "error",
      "where": ["language=javascript"],
      "rule": {
        "type": "file-existence",
        "options": { "globsAny": ["package.json"] }
      }
    }
  }
}
.alint.yml
version: 1
facts:
  - id: has_js
    any_file_exists: ["**/*.js", "**/*.mjs", "**/*.cjs", "**/*.jsx"]

rules:
  # nocase: a class for each letter
  - id: license-file-exists
    kind: file_exists
    paths:
      - "[Ll][Ii][Cc][Ee][Nn][Ss][Ee]*"
      - "[Cc][Oo][Pp][Yy][Ii][Nn][Gg]*"
    root_only: true
    level: error

  - id: javascript-package-metadata-exists
    when: facts.has_js
    kind: file_exists
    paths: package.json
    level: error

The 24 rules the default ruleset turns on, translated, are below and as a file. Delete the rules you didn't use in Repolinter, run alint check and compare the output with your last Repolinter run. There's no converter for a repolinter.json of your own: the two tables below give the alint rule for each Repolinter rule type and default rule.

curl -fsSL -o .alint.yml https://alint.org/downloads/repolinter-default.alint.yml
Repolinter's default ruleset as an alint config .alint.yml, 192 lines
.alint.yml
# Repolinter's default ruleset (v0.12.0) translated to alint: every rule
# it turns on. Two things work differently. alint has no linguist, so a
# fact that looks for a language's files stands in for where: language=...
# And alint's globs are case-sensitive, where many Repolinter rules set
# nocase: add the other spellings your repository uses, or write a
# character class for each letter as the README, LICENSE and code of
# conduct rules below do.
version: 1

extends:
  - alint://bundled/oss-baseline@v1

facts:
  - id: has_js
    any_file_exists: ["**/*.js", "**/*.mjs", "**/*.cjs", "**/*.jsx"]
  - id: has_ruby
    any_file_exists: ["**/*.rb"]
  - id: has_java
    any_file_exists: ["**/*.java"]
  - id: has_python
    any_file_exists: ["**/*.py"]
  - id: has_objective_c
    any_file_exists: ["**/*.m", "**/*.mm"]
  - id: has_swift
    any_file_exists: ["**/*.swift"]
  - id: has_erlang
    any_file_exists: ["**/*.erl", "**/*.hrl"]
  - id: has_elixir
    any_file_exists: ["**/*.ex", "**/*.exs"]
  - id: is_apache_2
    file_content_matches:
      paths: LICENSE
      pattern: 'Apache License,?\s*Version 2'

rules:
  # Repolinter's default rules are errors, apart from the source header
  # check, which warns. oss-baseline only informs about a security policy,
  # so raise it.
  - { id: oss-security-policy-exists, level: error }

  # oss-baseline's README, LICENSE and code of conduct rules accept a
  # fixed list of names. Repolinter accepts any README*, LICENSE*,
  # COPYING* or CODE-OF-CONDUCT* file in any case, so these three rules
  # use its globs instead, one character class per letter.
  - { id: oss-readme-exists, level: off }
  - { id: oss-license-exists, level: off }
  - { id: oss-code-of-conduct-exists, level: off }
  - id: readme-file-exists
    kind: file_exists
    paths: "[Rr][Ee][Aa][Dd][Mm][Ee]*"
    root_only: true
    level: error
  - id: license-file-exists
    kind: file_exists
    paths:
      - "[Ll][Ii][Cc][Ee][Nn][Ss][Ee]*"
      - "[Cc][Oo][Pp][Yy][Ii][Nn][Gg]*"
    root_only: true
    level: error
  - id: code-of-conduct-file-exists
    kind: file_exists
    paths:
      - "[Cc][Oo][Dd][Ee][Oo][Ff][Cc][Oo][Nn][Dd][Uu][Cc][Tt]*"
      - "[Cc][Oo][Dd][Ee]-[Oo][Ff]-[Cc][Oo][Nn][Dd][Uu][Cc][Tt]*"
      - "[Cc][Oo][Dd][Ee]_[Oo][Ff]_[Cc][Oo][Nn][Dd][Uu][Cc][Tt]*"
      - "{docs,.github}/[Cc][Oo][Dd][Ee][Oo][Ff][Cc][Oo][Nn][Dd][Uu][Cc][Tt]*"
      - "{docs,.github}/[Cc][Oo][Dd][Ee]-[Oo][Ff]-[Cc][Oo][Nn][Dd][Uu][Cc][Tt]*"
      - "{docs,.github}/[Cc][Oo][Dd][Ee]_[Oo][Ff]_[Cc][Oo][Nn][Dd][Uu][Cc][Tt]*"
    level: error

  - id: contributing-file-exists
    kind: file_exists
    paths: ["CONTRIB*", "docs/CONTRIB*", ".github/CONTRIB*"]
    level: error
  - id: changelog-file-exists
    kind: file_exists
    paths: "CHANGELOG*"
    level: error
  - id: support-file-exists
    kind: file_exists
    paths: ["SUPPORT*", "docs/SUPPORT*", ".github/SUPPORT*"]
    level: error
  - id: readme-references-license
    kind: file_content_matches
    paths: "README*"
    pattern: '(?i)license'
    level: error
  - id: binaries-not-present
    kind: file_absent
    paths:
      include: ["**/*.exe", "**/*.dll"]
      exclude: ["node_modules/**"]
    level: error
  - id: test-directory-exists
    kind: dir_exists
    paths: ["**/test*", "**/specs"]
    level: error
  - id: integrates-with-ci
    kind: file_exists
    paths:
      - ".gitlab-ci.yml"
      - ".travis.yml"
      - "appveyor.yml"
      - ".appveyor.yml"
      - "circle.yml"
      - ".circleci/config.yml"
      - "Jenkinsfile"
      - ".drone.yml"
      - ".github/workflows/*"
      - "azure-pipelines.yml"
    level: error
  - id: code-of-conduct-file-contains-email
    kind: file_content_matches
    paths:
      - "CODEOFCONDUCT*"
      - "CODE-OF-CONDUCT*"
      - "CODE_OF_CONDUCT*"
      - ".github/CODEOFCONDUCT*"
      - ".github/CODE-OF-CONDUCT*"
      - ".github/CODE_OF_CONDUCT*"
    pattern: '(?i).+@.+\..+'
    level: error
  - id: source-license-headers-exist
    kind: file_header
    paths:
      include: ["**/*.js"]
      exclude: ["node_modules/**"]
    lines: 5
    pattern: '(?is)copyright.*license|license.*copyright'
    level: warning
  - id: github-issue-template-exists
    kind: file_exists
    paths: ["ISSUE_TEMPLATE*", ".github/ISSUE_TEMPLATE*", ".github/ISSUE_TEMPLATE/*"]
    level: error
  - id: github-pull-request-template-exists
    kind: file_exists
    paths:
      - "PULL_REQUEST_TEMPLATE*"
      - ".github/PULL_REQUEST_TEMPLATE*"
      - ".github/PULL_REQUEST_TEMPLATE/*"
      - ".github/pull_request_template.md"
      - "docs/pull_request_template.md"
      - "docs/PULL_REQUEST_TEMPLATE*"
      - "pull_request_template.md"
    level: error
  - id: notice-file-exists
    when: facts.is_apache_2
    kind: file_exists
    paths: "NOTICE*"
    level: error

  # One rule per language, where Repolinter had where: language=...
  - id: javascript-package-metadata-exists
    when: facts.has_js
    kind: file_exists
    paths: package.json
    level: error
  - id: ruby-package-metadata-exists
    when: facts.has_ruby
    kind: file_exists
    paths: Gemfile
    level: error
  - id: java-package-metadata-exists
    when: facts.has_java
    kind: file_exists
    paths: ["pom.xml", "build.xml", "build.gradle"]
    level: error
  - id: python-package-metadata-exists
    when: facts.has_python
    kind: file_exists
    paths: ["setup.py", "requirements.txt"]
    level: error
  - id: objective-c-package-metadata-exists
    when: facts.has_objective_c
    kind: file_exists
    paths: ["Cartfile", "Podfile", "*.podspec"]
    level: error
  - id: swift-package-metadata-exists
    when: facts.has_swift
    kind: file_exists
    paths: Package.swift
    level: error
  - id: erlang-package-metadata-exists
    when: facts.has_erlang
    kind: file_exists
    paths: rebar.config
    level: error
  - id: elixir-package-metadata-exists
    when: facts.has_elixir
    kind: file_exists
    paths: mix.exs
    level: error

Two things work differently. A fact that looks for a language's files stands in for linguist's language detection, and alint's globs are case-sensitive where many Repolinter rules weren't: the README, LICENSE and code of conduct rules spell out every case, and for the others, add the spellings your repository uses. The 2 rules Repolinter leaves off, licensee detection and the Best Practices badge, aren't in the file.

Rule types

Full means an alint rule checks the same thing, bundled or written by you. Partial means alint checks part of it. None means no rule kind does it; a command rule or another tool can.

19 rule types: 11 full, 3partial, 5 none.

Repolinter typeCoveragealint
file-existence Fullfile_exists, or dir_exists for directories
file-not-exists Fullfile_absent
directory-existence Fulldir_exists
file-contents Fullfile_content_matches
file-not-contents Fullfile_content_forbidden
file-starts-with Fullfile_header, one regex per rule
file-hash Fullfile_hash, SHA-256 only (Repolinter's default algorithm)
file-hashes-not-exist NoneNo rule kind. A command rule can compare sha256sum output with a denylist.
file-type-exclusion Fullfile_absent with the same globs
large-file Fullfile_max_size
json-schema-passes Fulljson_schema_passes, which also reads YAML, TOML, XML, dotenv, INI, HCL and Java properties files. The schema comes from a file.
file-no-broken-links NoneNo rule kind. markdown_paths_resolve checks backticked file paths in Markdown and doesn't follow links.
apache-notice Fullfile_exists for NOTICE*, or extend compliance/apache-2
license-detectable-by-licensee NoneNo license detection. A command rule can run licensee.
best-practices-badge-present Partialfile_content_matches finds the badge in the README. The minimum-level option calls the badge API, which alint doesn't.
git-grep-commits NoneNo rule kind searches the content of old commits. gitleaks covers secrets.
git-grep-log PartialThe commit rules check every message in a range against a pattern that must match. A list of banned words needs a command rule.
git-list-tree Partialgit_no_denied_paths checks tracked paths, or the paths a pull request changes. It doesn't search older commits.
git-working-tree NoneNo rule kind. A command rule can run git rev-parse.

The default ruleset

default.json has 26 rules, and 2 of them are off unless you turn them on. Each row names what replaces it in alint.

26 rules: 24 full, 1partial, 1 none.

Repolinter ruleCoveragealint
license-file-exists FullA file_exists rule with Repolinter's globs, LICENSE* and COPYING* in any case. oss-baseline's oss-license-exists accepts fewer names.
readme-file-exists FullA file_exists rule for README* in any case. oss-baseline's oss-readme-exists accepts only README.md, README and README.rst.
contributing-file-exists FullA file_exists rule. oss-baseline has no CONTRIBUTING check.
code-of-conduct-file-exists FullA file_exists rule with Repolinter's globs for CODE-OF-CONDUCT* and its spellings. oss-baseline's oss-code-of-conduct-exists accepts only CODE_OF_CONDUCT.md.
changelog-file-exists FullA file_exists rule
security-file-exists Fulloss-security-policy-exists, plus oss-security-policy-non-empty
support-file-exists FullA file_exists rule
readme-references-license FullA file_content_matches rule
binaries-not-present FullA file_absent rule. The hygiene/no-tracked-artifacts ruleset covers build output and caches and has no rule for executables.
test-directory-exists FullA dir_exists rule
integrates-with-ci FullA file_exists rule over the CI config paths
code-of-conduct-file-contains-email FullA file_content_matches rule
source-license-headers-exist FullA file_header rule, or the compliance/apache-2 and compliance/reuse rulesets
github-issue-template-exists FullA file_exists rule
github-pull-request-template-exists FullA file_exists rule
*-package-metadata-existsjavascript, ruby, java, python, objective-c, swift, erlang, elixir FullA file_exists rule gated by a fact that looks for the language's files. The node ruleset only checks once some package.json exists.
license-detectable-by-licenseeoff by default NoneNo license detection (see the rule types)
notice-file-exists Fullapache-2-notice-file-exists in compliance/apache-2, or a rule gated on the license text
best-practices-badge-presentoff by default PartialA file_content_matches rule for the badge

Checks that need another tool

alint doesn't make network requests, except to fetch a hash-pinned extends: URL, and its git rules read the tracked tree or a range of commits.

Broken links
Repolinter's file-no-broken-links renders Markdown and other markup to HTML, requests every absolute link and checks relative links against the files. alint doesn't make requests.
lychee or markdown-link-check, as its own CI step. markdown_paths_resolve catches a narrower problem: backticked file paths in docs such as AGENTS.md that no longer exist.
License detection
alint doesn't identify licenses. If you used where: license=Apache-2.0, gate rules on a fact that matches the license text, as the translated config does.
The compliance/apache-2 ruleset for Apache projects, or licensee run from a command rule for SPDX detection.
Best Practices badge level
A file_content_matches rule can require the badge in the README. Checking the level the project reached needs the badge API.
OpenSSF Scorecard reports on the same area.
Git history
git_no_denied_paths checks the tracked tree, or with since: the paths a pull request changed. The commit rules check messages, subjects, sign-offs, GPG signatures, authors and leftover fixup! commits across a range. None of them searches the content of every past commit.
A command rule around git log, or gitleaks for secrets. Repolinter's git-working-tree check becomes a command rule that runs git rev-parse.
Hash denylists
file_hash pins the hash a file must have, and pair_hash checks hashes against a checksum file. Neither rejects a list of known hashes, as file-hashes-not-exist does.
A command rule that runs sha256sum and compares the result with your list.

What alint adds

  • Rules across files: pairs of files, a required file in every package directory, values that must match between files, and file graphs with layering and cycle checks.
  • Values inside config files, queried with JSONPath in JSON, YAML, TOML, XML, dotenv, .properties, INI and HCL files. See the structured query rules.
  • 22 bundled rulesets, for Rust, Node, Python, Go, Java, .NET and PHP projects, GitHub Actions, monorepos, license compliance and coding-agent files.
  • A baseline: alint baseline records today's findings, and CI fails only on new ones.
  • --changed --base origin/main, which checks only the files a pull request touches, and extensions for VS Code and JetBrains IDEs through alint lsp. A Zed extension is in the alint repository and isn't in Zed's registry yet.

Speed is on the benchmarks page, measured for each release that changes the linter.

Moving CI over

  1. Install alint. The installation page lists curl, Homebrew, npm, PyPI, cargo and Docker.
  2. Write .alint.yml from the translated config, keeping the rules you used.
  3. Run alint check on the same commit as your last Repolinter run and compare the two reports.
  4. Run alint fix --dry-run to see what alint can fix for you, then alint fix to apply it.
  5. If the repository has findings you won't fix yet, record them with alint baseline, commit .alint-baseline.json and add baseline: .alint-baseline.json to .alint.yml. alint check and the Action read the baseline only when the config names it; alint check --baseline FILE also works.
  6. Add the workflow below, then delete the Repolinter step and its config.

The alint step is pinned too. If you extend ci/github-actions, its gha-pin-actions-to-sha rule asks for a commit SHA on every third-party action, alint's included. Pinned by SHA, the Action installs the binary named in version:; the tag page lists each release's commit.

.github/workflows/alint.yml
name: alint
on: [push, pull_request]
permissions:
  contents: read
jobs:
  alint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - uses: asamarts/alint@c08c68be72e0c3a3a02051bd15f4a7d768fa5cbe # v0.18.2
        with:
          version: v0.18.2

The Action prints each finding as an annotation on the pull request. Its docs cover the inputs and SARIF upload. On GitLab or another CI, run alint check --format gitlab (or --format junit) in a job instead.

Try it

Start from the translated default ruleset, or from one line that brings in the README, license, security policy and code of conduct checks:

.alint.yml
version: 1
extends:
  - alint://bundled/oss-baseline@v1

The one line keeps oss-baseline's levels: README and LICENSE are warnings, and the security policy and code of conduct only inform. Install alint and run alint check at the root of the repository.

Next: the oss-baseline ruleset, rule by rule; configuration for facts, when: and overrides; the GitHub Action's inputs; and how alint compares with the tools people use next to it.