adrlint 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- adrlint-0.1.0/PKG-INFO +63 -0
- adrlint-0.1.0/README.md +37 -0
- adrlint-0.1.0/adrlint/__init__.py +5 -0
- adrlint-0.1.0/adrlint/cli.py +16 -0
- adrlint-0.1.0/adrlint/downloader.py +252 -0
- adrlint-0.1.0/adrlint.egg-info/PKG-INFO +63 -0
- adrlint-0.1.0/adrlint.egg-info/SOURCES.txt +11 -0
- adrlint-0.1.0/adrlint.egg-info/dependency_links.txt +1 -0
- adrlint-0.1.0/adrlint.egg-info/entry_points.txt +2 -0
- adrlint-0.1.0/adrlint.egg-info/requires.txt +1 -0
- adrlint-0.1.0/adrlint.egg-info/top_level.txt +1 -0
- adrlint-0.1.0/pyproject.toml +43 -0
- adrlint-0.1.0/setup.cfg +4 -0
adrlint-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: adrlint
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A fast, opinionated linter and formatter for MADR architecture decision records
|
|
5
|
+
Author-email: Na'aman Hirschfeld <nhirschfeld@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Goldziher/adrlint
|
|
8
|
+
Project-URL: Repository, https://github.com/Goldziher/adrlint.git
|
|
9
|
+
Project-URL: Issues, https://github.com/Goldziher/adrlint/issues
|
|
10
|
+
Keywords: adr,architecture-decision-record,madr,linter,markdown,cli,rust
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Documentation
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Classifier: Topic :: Text Processing
|
|
22
|
+
Classifier: Environment :: Console
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: certifi>=2026.7.22
|
|
26
|
+
|
|
27
|
+
# adrlint
|
|
28
|
+
|
|
29
|
+
[](https://pypi.org/project/adrlint/)
|
|
30
|
+
[](https://github.com/Goldziher/adrlint/blob/main/LICENSE)
|
|
31
|
+
|
|
32
|
+
A fast Rust CLI that lints and formats [MADR](https://adr.github.io/madr/) architecture decision
|
|
33
|
+
records: structure, numbering, Markdown quality, and immutability of accepted decisions.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install adrlint
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The first run downloads the pre-compiled binary for your platform and verifies it against the release
|
|
42
|
+
checksum manifest. Prebuilt archives exist for Linux (glibc) `x86_64`/`aarch64`, macOS
|
|
43
|
+
`x86_64`/`aarch64`, and Windows `x86_64`.
|
|
44
|
+
|
|
45
|
+
Set `ADRLINT_BINARY` to use an existing executable instead of downloading one.
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
adrlint init # write .adrlint.toml and docs/adr
|
|
51
|
+
adrlint lint # check every ADR
|
|
52
|
+
adrlint new "Use PostgreSQL" # create the next sequential ADR
|
|
53
|
+
adrlint lint --format github # GitHub annotations for CI
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Documentation
|
|
57
|
+
|
|
58
|
+
Rules, configuration, locking and the full CLI reference:
|
|
59
|
+
[https://github.com/Goldziher/adrlint](https://github.com/Goldziher/adrlint)
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT — see [LICENSE](https://github.com/Goldziher/adrlint/blob/main/LICENSE).
|
adrlint-0.1.0/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# adrlint
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/adrlint/)
|
|
4
|
+
[](https://github.com/Goldziher/adrlint/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
A fast Rust CLI that lints and formats [MADR](https://adr.github.io/madr/) architecture decision
|
|
7
|
+
records: structure, numbering, Markdown quality, and immutability of accepted decisions.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install adrlint
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The first run downloads the pre-compiled binary for your platform and verifies it against the release
|
|
16
|
+
checksum manifest. Prebuilt archives exist for Linux (glibc) `x86_64`/`aarch64`, macOS
|
|
17
|
+
`x86_64`/`aarch64`, and Windows `x86_64`.
|
|
18
|
+
|
|
19
|
+
Set `ADRLINT_BINARY` to use an existing executable instead of downloading one.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
adrlint init # write .adrlint.toml and docs/adr
|
|
25
|
+
adrlint lint # check every ADR
|
|
26
|
+
adrlint new "Use PostgreSQL" # create the next sequential ADR
|
|
27
|
+
adrlint lint --format github # GitHub annotations for CI
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
|
|
32
|
+
Rules, configuration, locking and the full CLI reference:
|
|
33
|
+
[https://github.com/Goldziher/adrlint](https://github.com/Goldziher/adrlint)
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
MIT — see [LICENSE](https://github.com/Goldziher/adrlint/blob/main/LICENSE).
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""Locate the adrlint executable, downloading and verifying a release asset when needed.
|
|
2
|
+
|
|
3
|
+
Environment
|
|
4
|
+
-----------
|
|
5
|
+
``ADRLINT_BINARY``
|
|
6
|
+
Absolute path to an already-installed adrlint executable. When set it is used as-is: nothing is
|
|
7
|
+
downloaded and no checksum is checked, so the caller owns the integrity of that file.
|
|
8
|
+
|
|
9
|
+
The downloaded binary is cached under ``~/.cache/adrlint/<version>/``. The directory is private
|
|
10
|
+
(mode 0o700) and sits next to ``<binary>.sha256``, which records the digest of the extracted binary
|
|
11
|
+
and of the archive it came from. A cache hit is only served after both digests are re-checked
|
|
12
|
+
against the file on disk and against the release checksum manifest; otherwise the binary is
|
|
13
|
+
discarded and downloaded again. Archive checksums are always verified before extraction.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import hashlib
|
|
19
|
+
import os
|
|
20
|
+
import platform
|
|
21
|
+
import shutil
|
|
22
|
+
import ssl
|
|
23
|
+
import subprocess
|
|
24
|
+
import sys
|
|
25
|
+
import tarfile
|
|
26
|
+
import tempfile
|
|
27
|
+
import zipfile
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from urllib.error import URLError
|
|
30
|
+
from urllib.request import Request, urlopen
|
|
31
|
+
|
|
32
|
+
import certifi
|
|
33
|
+
|
|
34
|
+
BASE_URL = "https://github.com/Goldziher/adrlint/releases/download"
|
|
35
|
+
|
|
36
|
+
# A release archive is a few megabytes. Refusing anything larger keeps a hostile or broken
|
|
37
|
+
# response from being buffered into memory.
|
|
38
|
+
MAX_DOWNLOAD_BYTES = 100 * 1024 * 1024
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _platform_triple() -> str:
|
|
42
|
+
system = platform.system().lower()
|
|
43
|
+
machine = platform.machine().lower()
|
|
44
|
+
|
|
45
|
+
if system == "windows":
|
|
46
|
+
if machine in {"amd64", "x86_64"}:
|
|
47
|
+
return "x86_64-pc-windows-gnu"
|
|
48
|
+
if machine in {"x86", "i386", "i686"}:
|
|
49
|
+
raise RuntimeError("32-bit Windows is not supported")
|
|
50
|
+
elif system == "linux":
|
|
51
|
+
if machine in {"amd64", "x86_64"}:
|
|
52
|
+
return "x86_64-unknown-linux-gnu"
|
|
53
|
+
if machine in {"aarch64", "arm64"}:
|
|
54
|
+
return "aarch64-unknown-linux-gnu"
|
|
55
|
+
elif system == "darwin":
|
|
56
|
+
if machine in {"amd64", "x86_64"}:
|
|
57
|
+
return "x86_64-apple-darwin"
|
|
58
|
+
if machine in {"aarch64", "arm64"}:
|
|
59
|
+
return "aarch64-apple-darwin"
|
|
60
|
+
|
|
61
|
+
raise RuntimeError(f"Unsupported platform: {system} {machine}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _python_version_to_tag(version: str) -> str:
|
|
65
|
+
if "rc" in version:
|
|
66
|
+
core, suffix = version.split("rc")
|
|
67
|
+
return f"{core}-rc.{suffix}"
|
|
68
|
+
return version
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _asset(version: str) -> tuple[str, str, str]:
|
|
72
|
+
tag = _python_version_to_tag(version)
|
|
73
|
+
triple = _platform_triple()
|
|
74
|
+
ext = "zip" if "windows" in triple else "tar.gz"
|
|
75
|
+
name = f"adrlint-{triple}.{ext}"
|
|
76
|
+
return f"{BASE_URL}/v{tag}/{name}", name, ext
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _get(url: str) -> bytes:
|
|
80
|
+
request = Request(url, headers={"User-Agent": "adrlint-python-wrapper"})
|
|
81
|
+
context = ssl.create_default_context(cafile=certifi.where())
|
|
82
|
+
try:
|
|
83
|
+
with urlopen(request, timeout=30, context=context) as response:
|
|
84
|
+
if response.status != 200:
|
|
85
|
+
raise RuntimeError(f"HTTP {response.status}: {response.reason}")
|
|
86
|
+
declared = response.headers.get("Content-Length")
|
|
87
|
+
if declared is not None and int(declared) > MAX_DOWNLOAD_BYTES:
|
|
88
|
+
raise RuntimeError(f"Refusing to download {url}: {declared} bytes is too large")
|
|
89
|
+
|
|
90
|
+
# Read in bounded chunks so a chunked response cannot buffer past the cap either.
|
|
91
|
+
body = bytearray()
|
|
92
|
+
while chunk := response.read(1024 * 1024):
|
|
93
|
+
body.extend(chunk)
|
|
94
|
+
if len(body) > MAX_DOWNLOAD_BYTES:
|
|
95
|
+
raise RuntimeError(f"Refusing to download {url}: response exceeds {MAX_DOWNLOAD_BYTES} bytes")
|
|
96
|
+
return bytes(body)
|
|
97
|
+
except URLError as exc:
|
|
98
|
+
raise RuntimeError(f"Failed to download {url}: {exc}") from exc
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _digest_for(manifest: str, name: str) -> str | None:
|
|
102
|
+
for line in manifest.splitlines():
|
|
103
|
+
parts = line.split()
|
|
104
|
+
if len(parts) == 2 and parts[1].lstrip("*") == name:
|
|
105
|
+
return parts[0]
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _expected_digest(version: str, asset_name: str) -> str:
|
|
110
|
+
tag = _python_version_to_tag(version)
|
|
111
|
+
manifest_url = f"{BASE_URL}/v{tag}/adrlint_{tag}_checksums.txt"
|
|
112
|
+
digest = _digest_for(_get(manifest_url).decode("utf-8"), asset_name)
|
|
113
|
+
if digest is None:
|
|
114
|
+
raise RuntimeError(f"No checksum published for {asset_name}")
|
|
115
|
+
return digest
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _download(version: str) -> tuple[bytes, str, str, str]:
|
|
119
|
+
url, asset_name, ext = _asset(version)
|
|
120
|
+
archive = _get(url)
|
|
121
|
+
expected = _expected_digest(version, asset_name)
|
|
122
|
+
actual = hashlib.sha256(archive).hexdigest()
|
|
123
|
+
if actual != expected:
|
|
124
|
+
raise RuntimeError(f"Checksum mismatch for {asset_name}: expected {expected}, got {actual}")
|
|
125
|
+
return archive, asset_name, ext, expected
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _extract(archive: Path, ext: str, destination: Path) -> None:
|
|
129
|
+
if ext == "zip":
|
|
130
|
+
with zipfile.ZipFile(archive) as zf:
|
|
131
|
+
for name in zf.namelist():
|
|
132
|
+
if name.endswith("adrlint") or name.endswith("adrlint.exe"):
|
|
133
|
+
with zf.open(name) as src, destination.open("wb") as dst:
|
|
134
|
+
dst.write(src.read())
|
|
135
|
+
return
|
|
136
|
+
else:
|
|
137
|
+
with tarfile.open(archive, "r:gz") as tar:
|
|
138
|
+
for member in tar.getmembers():
|
|
139
|
+
if member.name.endswith("adrlint") or member.name.endswith("adrlint.exe"):
|
|
140
|
+
with tar.extractfile(member) as src, destination.open("wb") as dst:
|
|
141
|
+
dst.write(src.read())
|
|
142
|
+
return
|
|
143
|
+
raise RuntimeError("Binary not found in downloaded archive")
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _cache_dir(version: str) -> Path:
|
|
147
|
+
cache_dir = Path.home() / ".cache" / "adrlint" / version
|
|
148
|
+
cache_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
149
|
+
# mkdir's mode is filtered by the umask and is ignored when the directory already exists.
|
|
150
|
+
try:
|
|
151
|
+
os.chmod(cache_dir, 0o700)
|
|
152
|
+
except OSError:
|
|
153
|
+
pass
|
|
154
|
+
return cache_dir
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _sha256_file(path: Path) -> str:
|
|
158
|
+
digest = hashlib.sha256()
|
|
159
|
+
with path.open("rb") as handle:
|
|
160
|
+
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
|
161
|
+
digest.update(chunk)
|
|
162
|
+
return digest.hexdigest()
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _write_cache_file(destination: Path, source: Path, mode: int) -> None:
|
|
166
|
+
"""Copy ``source`` into ``destination`` without ever writing through an existing path.
|
|
167
|
+
|
|
168
|
+
The content is written to a fresh ``O_EXCL`` file in the destination directory and then
|
|
169
|
+
renamed into place, so a symlink left at ``destination`` is replaced rather than followed.
|
|
170
|
+
"""
|
|
171
|
+
fd, staging = tempfile.mkstemp(dir=str(destination.parent), prefix=f".{destination.name}.")
|
|
172
|
+
try:
|
|
173
|
+
with os.fdopen(fd, "wb") as target, source.open("rb") as origin:
|
|
174
|
+
shutil.copyfileobj(origin, target)
|
|
175
|
+
target.flush()
|
|
176
|
+
os.fsync(target.fileno())
|
|
177
|
+
os.chmod(staging, mode)
|
|
178
|
+
os.replace(staging, destination)
|
|
179
|
+
except BaseException:
|
|
180
|
+
Path(staging).unlink(missing_ok=True)
|
|
181
|
+
raise
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _cache_is_valid(binary_path: Path, record_path: Path, version: str, asset_name: str) -> bool:
|
|
185
|
+
"""Check a cached binary against the record written at install time and the live manifest."""
|
|
186
|
+
if not binary_path.is_file() or not record_path.is_file():
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
published_archive = _expected_digest(version, asset_name)
|
|
190
|
+
record = record_path.read_text(encoding="utf-8")
|
|
191
|
+
if _digest_for(record, asset_name) != published_archive:
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
recorded_binary = _digest_for(record, binary_path.name)
|
|
195
|
+
if recorded_binary is None:
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
return _sha256_file(binary_path) == recorded_binary
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def ensure_binary() -> str:
|
|
202
|
+
"""Ensure the binary is available, downloading if necessary."""
|
|
203
|
+
from . import __version__
|
|
204
|
+
|
|
205
|
+
override = os.getenv("ADRLINT_BINARY")
|
|
206
|
+
if override:
|
|
207
|
+
return override
|
|
208
|
+
|
|
209
|
+
suffix = ".exe" if platform.system().lower() == "windows" else ""
|
|
210
|
+
cache_dir = _cache_dir(__version__)
|
|
211
|
+
binary_path = cache_dir / f"adrlint{suffix}"
|
|
212
|
+
record_path = cache_dir / f"adrlint{suffix}.sha256"
|
|
213
|
+
_, asset_name, _ = _asset(__version__)
|
|
214
|
+
|
|
215
|
+
if binary_path.is_file() and os.access(binary_path, os.X_OK):
|
|
216
|
+
if _cache_is_valid(binary_path, record_path, __version__, asset_name):
|
|
217
|
+
return str(binary_path)
|
|
218
|
+
print("Cached adrlint binary failed verification; reinstalling.", file=sys.stderr)
|
|
219
|
+
|
|
220
|
+
binary_path.unlink(missing_ok=True)
|
|
221
|
+
record_path.unlink(missing_ok=True)
|
|
222
|
+
|
|
223
|
+
print(f"Downloading adrlint binary v{__version__}...", file=sys.stderr)
|
|
224
|
+
archive, asset_name, ext, archive_digest = _download(__version__)
|
|
225
|
+
|
|
226
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
227
|
+
archive_path = Path(tmpdir) / asset_name
|
|
228
|
+
archive_path.write_bytes(archive)
|
|
229
|
+
extracted = Path(tmpdir) / binary_path.name
|
|
230
|
+
_extract(archive_path, ext, extracted)
|
|
231
|
+
|
|
232
|
+
_write_cache_file(binary_path, extracted, 0o755)
|
|
233
|
+
record = f"{_sha256_file(binary_path)} {binary_path.name}\n{archive_digest} {asset_name}\n"
|
|
234
|
+
record_file = Path(tmpdir) / record_path.name
|
|
235
|
+
record_file.write_text(record, encoding="utf-8")
|
|
236
|
+
_write_cache_file(record_path, record_file, 0o600)
|
|
237
|
+
|
|
238
|
+
print("Binary downloaded successfully!", file=sys.stderr)
|
|
239
|
+
return str(binary_path)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def run_adrlint(args: list[str]) -> None:
|
|
243
|
+
"""Run the adrlint binary with the given arguments."""
|
|
244
|
+
binary_path = ensure_binary()
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
result = subprocess.run([binary_path] + args, check=False)
|
|
248
|
+
sys.exit(result.returncode)
|
|
249
|
+
except FileNotFoundError:
|
|
250
|
+
raise RuntimeError(f"Binary not found at {binary_path}")
|
|
251
|
+
except Exception as exc:
|
|
252
|
+
raise RuntimeError(f"Failed to run adrlint: {exc}")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: adrlint
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A fast, opinionated linter and formatter for MADR architecture decision records
|
|
5
|
+
Author-email: Na'aman Hirschfeld <nhirschfeld@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Goldziher/adrlint
|
|
8
|
+
Project-URL: Repository, https://github.com/Goldziher/adrlint.git
|
|
9
|
+
Project-URL: Issues, https://github.com/Goldziher/adrlint/issues
|
|
10
|
+
Keywords: adr,architecture-decision-record,madr,linter,markdown,cli,rust
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Documentation
|
|
20
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
21
|
+
Classifier: Topic :: Text Processing
|
|
22
|
+
Classifier: Environment :: Console
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: certifi>=2026.7.22
|
|
26
|
+
|
|
27
|
+
# adrlint
|
|
28
|
+
|
|
29
|
+
[](https://pypi.org/project/adrlint/)
|
|
30
|
+
[](https://github.com/Goldziher/adrlint/blob/main/LICENSE)
|
|
31
|
+
|
|
32
|
+
A fast Rust CLI that lints and formats [MADR](https://adr.github.io/madr/) architecture decision
|
|
33
|
+
records: structure, numbering, Markdown quality, and immutability of accepted decisions.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install adrlint
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The first run downloads the pre-compiled binary for your platform and verifies it against the release
|
|
42
|
+
checksum manifest. Prebuilt archives exist for Linux (glibc) `x86_64`/`aarch64`, macOS
|
|
43
|
+
`x86_64`/`aarch64`, and Windows `x86_64`.
|
|
44
|
+
|
|
45
|
+
Set `ADRLINT_BINARY` to use an existing executable instead of downloading one.
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
adrlint init # write .adrlint.toml and docs/adr
|
|
51
|
+
adrlint lint # check every ADR
|
|
52
|
+
adrlint new "Use PostgreSQL" # create the next sequential ADR
|
|
53
|
+
adrlint lint --format github # GitHub annotations for CI
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Documentation
|
|
57
|
+
|
|
58
|
+
Rules, configuration, locking and the full CLI reference:
|
|
59
|
+
[https://github.com/Goldziher/adrlint](https://github.com/Goldziher/adrlint)
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
MIT — see [LICENSE](https://github.com/Goldziher/adrlint/blob/main/LICENSE).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
adrlint/__init__.py
|
|
4
|
+
adrlint/cli.py
|
|
5
|
+
adrlint/downloader.py
|
|
6
|
+
adrlint.egg-info/PKG-INFO
|
|
7
|
+
adrlint.egg-info/SOURCES.txt
|
|
8
|
+
adrlint.egg-info/dependency_links.txt
|
|
9
|
+
adrlint.egg-info/entry_points.txt
|
|
10
|
+
adrlint.egg-info/requires.txt
|
|
11
|
+
adrlint.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
certifi>=2026.7.22
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
adrlint
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=84.0.0", "wheel>=0.48.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "adrlint"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A fast, opinionated linter and formatter for MADR architecture decision records"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Na'aman Hirschfeld", email = "nhirschfeld@gmail.com"}
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Topic :: Documentation",
|
|
24
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
25
|
+
"Topic :: Text Processing",
|
|
26
|
+
"Environment :: Console"
|
|
27
|
+
]
|
|
28
|
+
keywords = ["adr", "architecture-decision-record", "madr", "linter", "markdown", "cli", "rust"]
|
|
29
|
+
requires-python = ">=3.10"
|
|
30
|
+
dependencies = [
|
|
31
|
+
"certifi>=2026.7.22"
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://github.com/Goldziher/adrlint"
|
|
36
|
+
Repository = "https://github.com/Goldziher/adrlint.git"
|
|
37
|
+
Issues = "https://github.com/Goldziher/adrlint/issues"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
adrlint = "adrlint.cli:main"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools]
|
|
43
|
+
packages = ["adrlint"]
|
adrlint-0.1.0/setup.cfg
ADDED