omerta 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.
- omerta-0.1.0/LICENSE +21 -0
- omerta-0.1.0/PKG-INFO +62 -0
- omerta-0.1.0/PYPI.md +28 -0
- omerta-0.1.0/pyproject.toml +193 -0
- omerta-0.1.0/pyproject.toml.orig +202 -0
- omerta-0.1.0/src/omerta/__init__.py +5 -0
- omerta-0.1.0/src/omerta/check.py +125 -0
- omerta-0.1.0/src/omerta/cli.py +214 -0
- omerta-0.1.0/src/omerta/config.py +120 -0
- omerta-0.1.0/src/omerta/envfiles.py +153 -0
- omerta-0.1.0/src/omerta/errors.py +11 -0
- omerta-0.1.0/src/omerta/forms.py +98 -0
- omerta-0.1.0/src/omerta/git.py +299 -0
- omerta-0.1.0/src/omerta/history.py +236 -0
- omerta-0.1.0/src/omerta/output.py +147 -0
- omerta-0.1.0/src/omerta/patterns.py +74 -0
- omerta-0.1.0/src/omerta/py.typed +0 -0
- omerta-0.1.0/src/omerta/search.py +186 -0
omerta-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sergii Shcherbak
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
omerta-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: omerta
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Fail when a value of your env files, e.g. an API key in .env, is in git: staged, tracked, or anywhere in the history
|
|
5
|
+
Keywords: secrets,env,dotenv,.env,git,pre-commit,leak,api-key,security,secret-scanning
|
|
6
|
+
Author: Sergii Shcherbak
|
|
7
|
+
Author-email: Sergii Shcherbak <sergii@shcherbak.ai>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
19
|
+
Classifier: Operating System :: MacOS
|
|
20
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
23
|
+
Classifier: Topic :: Software Development :: Version Control :: Git
|
|
24
|
+
Classifier: Environment :: Console
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Dist: hyperscan==0.9.1
|
|
27
|
+
Requires-Dist: python-dotenv==1.2.4
|
|
28
|
+
Requires-Python: >=3.11
|
|
29
|
+
Project-URL: Homepage, https://github.com/shcherbak-ai/omerta
|
|
30
|
+
Project-URL: Repository, https://github.com/shcherbak-ai/omerta
|
|
31
|
+
Project-URL: Issues, https://github.com/shcherbak-ai/omerta/issues
|
|
32
|
+
Project-URL: Changelog, https://github.com/shcherbak-ai/omerta/blob/main/CHANGELOG.md
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# omerta
|
|
36
|
+
|
|
37
|
+
Fail when a value of your env files, e.g. an API key in `.env`, is in git: in the staged changes, in a tracked file, or anywhere in the history.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uvx omerta check --all # every tracked file
|
|
41
|
+
uvx omerta history # every commit, tag and message
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- Matches your actual values, as written and as encoded in URLs, JSON and base64, so it catches secrets of any format; it complements pattern scanners, which find what looks like a key.
|
|
45
|
+
- Reports the file, the line and the variable's name, never the value; a tracked env file is a leak in itself.
|
|
46
|
+
- Runs entirely offline.
|
|
47
|
+
|
|
48
|
+
As a pre-commit hook:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
repos:
|
|
52
|
+
- repo: https://github.com/shcherbak-ai/omerta
|
|
53
|
+
rev: v0.1.0
|
|
54
|
+
hooks:
|
|
55
|
+
- id: omerta
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
With Husky, Lefthook or a plain git hook: `uvx omerta@0.1.0 check`.
|
|
59
|
+
|
|
60
|
+
See the [repository](https://github.com/shcherbak-ai/omerta#readme) for the options, CI and configuration.
|
|
61
|
+
|
|
62
|
+
Licensed under [MIT](https://github.com/shcherbak-ai/omerta/blob/main/LICENSE).
|
omerta-0.1.0/PYPI.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# omerta
|
|
2
|
+
|
|
3
|
+
Fail when a value of your env files, e.g. an API key in `.env`, is in git: in the staged changes, in a tracked file, or anywhere in the history.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
uvx omerta check --all # every tracked file
|
|
7
|
+
uvx omerta history # every commit, tag and message
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
- Matches your actual values, as written and as encoded in URLs, JSON and base64, so it catches secrets of any format; it complements pattern scanners, which find what looks like a key.
|
|
11
|
+
- Reports the file, the line and the variable's name, never the value; a tracked env file is a leak in itself.
|
|
12
|
+
- Runs entirely offline.
|
|
13
|
+
|
|
14
|
+
As a pre-commit hook:
|
|
15
|
+
|
|
16
|
+
```yaml
|
|
17
|
+
repos:
|
|
18
|
+
- repo: https://github.com/shcherbak-ai/omerta
|
|
19
|
+
rev: v0.1.0
|
|
20
|
+
hooks:
|
|
21
|
+
- id: omerta
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
With Husky, Lefthook or a plain git hook: `uvx omerta@0.1.0 check`.
|
|
25
|
+
|
|
26
|
+
See the [repository](https://github.com/shcherbak-ai/omerta#readme) for the options, CI and configuration.
|
|
27
|
+
|
|
28
|
+
Licensed under [MIT](https://github.com/shcherbak-ai/omerta/blob/main/LICENSE).
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "omerta"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Fail when a value of your env files, e.g. an API key in .env, is in git: staged, tracked, or anywhere in the history"
|
|
5
|
+
readme = "PYPI.md"
|
|
6
|
+
requires-python = ">=3.11"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
keywords = [
|
|
10
|
+
"secrets",
|
|
11
|
+
"env",
|
|
12
|
+
"dotenv",
|
|
13
|
+
".env",
|
|
14
|
+
"git",
|
|
15
|
+
"pre-commit",
|
|
16
|
+
"leak",
|
|
17
|
+
"api-key",
|
|
18
|
+
"security",
|
|
19
|
+
"secret-scanning",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 3 - Alpha",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Programming Language :: Python :: 3.14",
|
|
29
|
+
"Programming Language :: Python :: 3.15",
|
|
30
|
+
"Operating System :: POSIX :: Linux",
|
|
31
|
+
"Operating System :: MacOS",
|
|
32
|
+
"Operating System :: Microsoft :: Windows",
|
|
33
|
+
"Topic :: Security",
|
|
34
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
35
|
+
"Topic :: Software Development :: Version Control :: Git",
|
|
36
|
+
"Environment :: Console",
|
|
37
|
+
"Typing :: Typed",
|
|
38
|
+
]
|
|
39
|
+
dependencies = [
|
|
40
|
+
"hyperscan==0.9.1",
|
|
41
|
+
"python-dotenv==1.2.4",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[[project.authors]]
|
|
45
|
+
name = "Sergii Shcherbak"
|
|
46
|
+
email = "sergii@shcherbak.ai"
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "https://github.com/shcherbak-ai/omerta"
|
|
50
|
+
Repository = "https://github.com/shcherbak-ai/omerta"
|
|
51
|
+
Issues = "https://github.com/shcherbak-ai/omerta/issues"
|
|
52
|
+
Changelog = "https://github.com/shcherbak-ai/omerta/blob/main/CHANGELOG.md"
|
|
53
|
+
|
|
54
|
+
[project.scripts]
|
|
55
|
+
omerta = "omerta.cli:main"
|
|
56
|
+
|
|
57
|
+
[dependency-groups]
|
|
58
|
+
dev = [
|
|
59
|
+
"pytest>=8.0",
|
|
60
|
+
"pytest-cov>=6.0",
|
|
61
|
+
"pytest-xdist>=3.6",
|
|
62
|
+
"coverage[toml]>=7.0",
|
|
63
|
+
"tethered>=1.0.0",
|
|
64
|
+
"pre-commit>=4.0",
|
|
65
|
+
"pre-commit-hooks>=6.0",
|
|
66
|
+
"ruff>=0.9",
|
|
67
|
+
"bandit>=1.8",
|
|
68
|
+
"ty>=0.0.33",
|
|
69
|
+
"interrogate>=1.7",
|
|
70
|
+
"vulture>=2.16",
|
|
71
|
+
"deptry>=0.25.1",
|
|
72
|
+
"commitizen>=4.13",
|
|
73
|
+
"invoke>=2.2",
|
|
74
|
+
"licenseal>=1.0.1",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
[tool.uv]
|
|
78
|
+
exclude-newer = "7 days"
|
|
79
|
+
|
|
80
|
+
[tool.uv.exclude-newer-package]
|
|
81
|
+
certifi = "2026-10-20T02:25:00Z"
|
|
82
|
+
virtualenv = "2026-10-22T20:11:00Z"
|
|
83
|
+
|
|
84
|
+
[tool.pytest.ini_options]
|
|
85
|
+
testpaths = ["tests"]
|
|
86
|
+
filterwarnings = ["error"]
|
|
87
|
+
|
|
88
|
+
[tool.coverage.run]
|
|
89
|
+
source = ["omerta"]
|
|
90
|
+
branch = true
|
|
91
|
+
|
|
92
|
+
[tool.coverage.report]
|
|
93
|
+
show_missing = true
|
|
94
|
+
fail_under = 100
|
|
95
|
+
exclude_lines = [
|
|
96
|
+
"pragma: no cover",
|
|
97
|
+
"if TYPE_CHECKING:",
|
|
98
|
+
"if __name__",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
[tool.coverage.paths]
|
|
102
|
+
source = ["src"]
|
|
103
|
+
|
|
104
|
+
[tool.ruff]
|
|
105
|
+
target-version = "py311"
|
|
106
|
+
line-length = 100
|
|
107
|
+
src = [
|
|
108
|
+
"src",
|
|
109
|
+
"tests",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
[tool.ruff.lint]
|
|
113
|
+
select = [
|
|
114
|
+
"E",
|
|
115
|
+
"W",
|
|
116
|
+
"F",
|
|
117
|
+
"I",
|
|
118
|
+
"N",
|
|
119
|
+
"UP",
|
|
120
|
+
"B",
|
|
121
|
+
"S",
|
|
122
|
+
"T20",
|
|
123
|
+
"SIM",
|
|
124
|
+
"FA",
|
|
125
|
+
]
|
|
126
|
+
ignore = ["S101"]
|
|
127
|
+
|
|
128
|
+
[tool.ruff.lint.isort]
|
|
129
|
+
required-imports = ["from __future__ import annotations"]
|
|
130
|
+
known-first-party = ["omerta"]
|
|
131
|
+
|
|
132
|
+
[tool.ruff.lint.per-file-ignores]
|
|
133
|
+
"tests/**/*.py" = [
|
|
134
|
+
"S",
|
|
135
|
+
"T20",
|
|
136
|
+
]
|
|
137
|
+
"scripts/**/*.py" = ["T20"]
|
|
138
|
+
"tasks.py" = ["T20"]
|
|
139
|
+
|
|
140
|
+
[tool.ty.src]
|
|
141
|
+
include = ["src"]
|
|
142
|
+
exclude = ["tests"]
|
|
143
|
+
|
|
144
|
+
[tool.deptry]
|
|
145
|
+
known_first_party = ["omerta"]
|
|
146
|
+
|
|
147
|
+
[tool.deptry.per_rule_ignores]
|
|
148
|
+
DEP004 = ["invoke"]
|
|
149
|
+
|
|
150
|
+
[tool.bandit]
|
|
151
|
+
exclude_dirs = ["tests"]
|
|
152
|
+
skips = ["B101"]
|
|
153
|
+
|
|
154
|
+
[tool.vulture]
|
|
155
|
+
paths = ["src"]
|
|
156
|
+
min_confidence = 60
|
|
157
|
+
ignore_names = ["main"]
|
|
158
|
+
|
|
159
|
+
[tool.interrogate]
|
|
160
|
+
ignore-init-method = true
|
|
161
|
+
ignore-init-module = true
|
|
162
|
+
ignore-magic = true
|
|
163
|
+
ignore-private = true
|
|
164
|
+
ignore-semiprivate = true
|
|
165
|
+
fail-under = 100
|
|
166
|
+
exclude = [
|
|
167
|
+
"tests",
|
|
168
|
+
"node_modules",
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
[tool.commitizen]
|
|
172
|
+
name = "cz_conventional_commits"
|
|
173
|
+
version = "0.1.0"
|
|
174
|
+
version_files = [
|
|
175
|
+
"src/omerta/__init__.py:__version__",
|
|
176
|
+
"pyproject.toml:^version",
|
|
177
|
+
"CITATION.cff:^version:",
|
|
178
|
+
"README.md:omerta@|rev: v",
|
|
179
|
+
"PYPI.md:omerta@|rev: v",
|
|
180
|
+
]
|
|
181
|
+
tag_format = "v$version"
|
|
182
|
+
annotated_tag = true
|
|
183
|
+
major_version_zero = true
|
|
184
|
+
pre_bump_hooks = [
|
|
185
|
+
"uv lock --no-upgrade",
|
|
186
|
+
"git add uv.lock",
|
|
187
|
+
"uv run python scripts/date_release.py",
|
|
188
|
+
"uv run pytest tests/test_changelog.py -q -p no:cacheprovider",
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
[build-system]
|
|
192
|
+
requires = ["uv_build>=0.13.0,<0.14"]
|
|
193
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "omerta"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Fail when a value of your env files, e.g. an API key in .env, is in git: staged, tracked, or anywhere in the history"
|
|
5
|
+
readme = "PYPI.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Sergii Shcherbak", email = "sergii@shcherbak.ai" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
keywords = ["secrets", "env", "dotenv", ".env", "git", "pre-commit", "leak", "api-key", "security", "secret-scanning"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Programming Language :: Python :: 3.14",
|
|
21
|
+
"Programming Language :: Python :: 3.15",
|
|
22
|
+
"Operating System :: POSIX :: Linux",
|
|
23
|
+
"Operating System :: MacOS",
|
|
24
|
+
"Operating System :: Microsoft :: Windows",
|
|
25
|
+
"Topic :: Security",
|
|
26
|
+
"Topic :: Software Development :: Quality Assurance",
|
|
27
|
+
"Topic :: Software Development :: Version Control :: Git",
|
|
28
|
+
"Environment :: Console",
|
|
29
|
+
"Typing :: Typed",
|
|
30
|
+
]
|
|
31
|
+
# Pinned exactly, as uv.lock has them, which `uv lock --check` holds: both see
|
|
32
|
+
# the values of the env files in cleartext, and pre-commit and uvx install from
|
|
33
|
+
# these requirements, not from uv.lock. A release of either reaches users only
|
|
34
|
+
# through a release of omerta, after the cooldown and the advisories check.
|
|
35
|
+
dependencies = [
|
|
36
|
+
# Every form of every value is searched for in one pass over each blob, as
|
|
37
|
+
# literals of one Hyperscan database. Its wheels cover every platform but
|
|
38
|
+
# Windows on ARM.
|
|
39
|
+
"hyperscan==0.9.1",
|
|
40
|
+
# Findings depend on its parser: 1.2.4 parses `KEY= # comment` as empty,
|
|
41
|
+
# and 1.2.3 stopped a quoted value ending in an escaped backslash from
|
|
42
|
+
# swallowing the next lines.
|
|
43
|
+
"python-dotenv==1.2.4",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://github.com/shcherbak-ai/omerta"
|
|
48
|
+
Repository = "https://github.com/shcherbak-ai/omerta"
|
|
49
|
+
Issues = "https://github.com/shcherbak-ai/omerta/issues"
|
|
50
|
+
Changelog = "https://github.com/shcherbak-ai/omerta/blob/main/CHANGELOG.md"
|
|
51
|
+
|
|
52
|
+
[project.scripts]
|
|
53
|
+
omerta = "omerta.cli:main"
|
|
54
|
+
|
|
55
|
+
[dependency-groups]
|
|
56
|
+
dev = [
|
|
57
|
+
"pytest>=8.0",
|
|
58
|
+
"pytest-cov>=6.0",
|
|
59
|
+
"pytest-xdist>=3.6",
|
|
60
|
+
"coverage[toml]>=7.0",
|
|
61
|
+
"tethered>=1.0.0",
|
|
62
|
+
"pre-commit>=4.0",
|
|
63
|
+
"pre-commit-hooks>=6.0",
|
|
64
|
+
"ruff>=0.9",
|
|
65
|
+
"bandit>=1.8",
|
|
66
|
+
"ty>=0.0.33",
|
|
67
|
+
"interrogate>=1.7",
|
|
68
|
+
"vulture>=2.16",
|
|
69
|
+
"deptry>=0.25.1",
|
|
70
|
+
"commitizen>=4.13",
|
|
71
|
+
"invoke>=2.2",
|
|
72
|
+
"licenseal>=1.0.1",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
[tool.uv]
|
|
76
|
+
# A release enters uv.lock only a week after its upload, when a compromised one
|
|
77
|
+
# has been found and removed.
|
|
78
|
+
exclude-newer = "7 days"
|
|
79
|
+
|
|
80
|
+
# Security fixes taken inside the cooldown, up to their uploads: the advisories
|
|
81
|
+
# hook says when an entry is no longer needed.
|
|
82
|
+
[tool.uv.exclude-newer-package]
|
|
83
|
+
# 2026.10.19 drops the root CAs whose intermediate keys were compromised
|
|
84
|
+
# (GHSA-6q48-6c7w-9gw2)
|
|
85
|
+
certifi = "2026-10-20T02:25:00Z"
|
|
86
|
+
# 21.14.13 escapes the --prompt value in the Bash activator (GHSA-cx6f-vjhg-g3cc)
|
|
87
|
+
virtualenv = "2026-10-22T20:11:00Z"
|
|
88
|
+
|
|
89
|
+
[tool.pytest.ini_options]
|
|
90
|
+
testpaths = ["tests"]
|
|
91
|
+
filterwarnings = ["error"]
|
|
92
|
+
|
|
93
|
+
[tool.coverage.run]
|
|
94
|
+
source = ["omerta"]
|
|
95
|
+
branch = true
|
|
96
|
+
|
|
97
|
+
[tool.coverage.report]
|
|
98
|
+
show_missing = true
|
|
99
|
+
fail_under = 100
|
|
100
|
+
exclude_lines = [
|
|
101
|
+
"pragma: no cover",
|
|
102
|
+
"if TYPE_CHECKING:",
|
|
103
|
+
"if __name__",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
[tool.coverage.paths]
|
|
107
|
+
source = ["src"]
|
|
108
|
+
|
|
109
|
+
[tool.ruff]
|
|
110
|
+
target-version = "py311"
|
|
111
|
+
line-length = 100
|
|
112
|
+
src = ["src", "tests"]
|
|
113
|
+
|
|
114
|
+
[tool.ruff.lint]
|
|
115
|
+
select = [
|
|
116
|
+
"E", # pycodestyle errors
|
|
117
|
+
"W", # pycodestyle warnings
|
|
118
|
+
"F", # pyflakes
|
|
119
|
+
"I", # isort
|
|
120
|
+
"N", # pep8-naming
|
|
121
|
+
"UP", # pyupgrade
|
|
122
|
+
"B", # flake8-bugbear
|
|
123
|
+
"S", # flake8-bandit
|
|
124
|
+
"T20", # flake8-print
|
|
125
|
+
"SIM", # flake8-simplify
|
|
126
|
+
"FA", # flake8-future-annotations (FA100: require from __future__ import annotations)
|
|
127
|
+
]
|
|
128
|
+
ignore = [
|
|
129
|
+
"S101", # assert used (fine in tests)
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
[tool.ruff.lint.isort]
|
|
133
|
+
required-imports = ["from __future__ import annotations"]
|
|
134
|
+
known-first-party = ["omerta"]
|
|
135
|
+
|
|
136
|
+
[tool.ruff.lint.per-file-ignores]
|
|
137
|
+
"tests/**/*.py" = ["S", "T20"]
|
|
138
|
+
"scripts/**/*.py" = ["T20"]
|
|
139
|
+
"tasks.py" = ["T20"]
|
|
140
|
+
|
|
141
|
+
[tool.ty.src]
|
|
142
|
+
include = ["src"]
|
|
143
|
+
exclude = ["tests"]
|
|
144
|
+
|
|
145
|
+
[tool.deptry]
|
|
146
|
+
known_first_party = ["omerta"]
|
|
147
|
+
|
|
148
|
+
[tool.deptry.per_rule_ignores]
|
|
149
|
+
DEP004 = [
|
|
150
|
+
"invoke", # tasks.py, the development tasks
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
[tool.bandit]
|
|
154
|
+
exclude_dirs = ["tests"]
|
|
155
|
+
skips = ["B101"]
|
|
156
|
+
|
|
157
|
+
[tool.vulture]
|
|
158
|
+
paths = ["src"]
|
|
159
|
+
min_confidence = 60
|
|
160
|
+
# The command's entry point, which the console script calls.
|
|
161
|
+
ignore_names = ["main"]
|
|
162
|
+
|
|
163
|
+
[tool.interrogate]
|
|
164
|
+
ignore-init-method = true
|
|
165
|
+
ignore-init-module = true
|
|
166
|
+
ignore-magic = true
|
|
167
|
+
ignore-private = true
|
|
168
|
+
ignore-semiprivate = true
|
|
169
|
+
fail-under = 100
|
|
170
|
+
exclude = ["tests", "node_modules"]
|
|
171
|
+
|
|
172
|
+
[tool.commitizen]
|
|
173
|
+
name = "cz_conventional_commits"
|
|
174
|
+
version = "0.1.0"
|
|
175
|
+
# Commitizen replaces the version on each line that the pattern after the colon
|
|
176
|
+
# finds: anchored, so that `cff-version: 1.2.0` (the format of CITATION.cff)
|
|
177
|
+
# keeps its own. The README and the package description pin a release in their
|
|
178
|
+
# examples (`omerta@X.Y.Z`, `rev: vX.Y.Z`), which a bump moves too.
|
|
179
|
+
version_files = [
|
|
180
|
+
"src/omerta/__init__.py:__version__",
|
|
181
|
+
"pyproject.toml:^version",
|
|
182
|
+
"CITATION.cff:^version:",
|
|
183
|
+
"README.md:omerta@|rev: v",
|
|
184
|
+
"PYPI.md:omerta@|rev: v",
|
|
185
|
+
]
|
|
186
|
+
tag_format = "v$version"
|
|
187
|
+
annotated_tag = true
|
|
188
|
+
major_version_zero = true
|
|
189
|
+
# The hooks run after the version fields are rewritten and before the commit,
|
|
190
|
+
# which takes in every changed file (`git commit -a`). scripts/date_release.py
|
|
191
|
+
# names the `## [Unreleased]` section of the changelog after the new version,
|
|
192
|
+
# with the day, and writes the day into CITATION.cff; the last hook checks both.
|
|
193
|
+
pre_bump_hooks = [
|
|
194
|
+
"uv lock --no-upgrade",
|
|
195
|
+
"git add uv.lock",
|
|
196
|
+
"uv run python scripts/date_release.py",
|
|
197
|
+
"uv run pytest tests/test_changelog.py -q -p no:cacheprovider",
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
[build-system]
|
|
201
|
+
requires = ["uv_build>=0.13.0,<0.14"]
|
|
202
|
+
build-backend = "uv_build"
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""``omerta check``: the index content of the staged changes, of given paths, or of every entry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from omerta.errors import OmertaError
|
|
8
|
+
from omerta.git import GITLINK, Entry, Git
|
|
9
|
+
from omerta.output import Finding
|
|
10
|
+
from omerta.patterns import Pattern, is_env_file
|
|
11
|
+
from omerta.search import Matcher, Target, line_of
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def select(git: Git, index: list[Entry], paths: list[str], everything: bool) -> list[Entry]:
|
|
15
|
+
"""The index entries that ``check`` searches.
|
|
16
|
+
|
|
17
|
+
A PATH selects the entry at that path, or with a folder the entries under
|
|
18
|
+
it, matched exactly, as literal pathspecs are. The paths are matched here,
|
|
19
|
+
not given to git, so that none goes on git's command line.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
git: git, in the repository.
|
|
23
|
+
index: Every index entry.
|
|
24
|
+
paths: The PATH arguments, relative to the folder omerta runs in.
|
|
25
|
+
everything: ``--all``: every entry.
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
OmertaError: A path is outside the repository.
|
|
29
|
+
"""
|
|
30
|
+
if everything:
|
|
31
|
+
return index
|
|
32
|
+
if paths:
|
|
33
|
+
selected = [_from_root(git, path) for path in paths]
|
|
34
|
+
return [
|
|
35
|
+
entry
|
|
36
|
+
for entry in index
|
|
37
|
+
if any(
|
|
38
|
+
not chosen or entry.path == chosen or entry.path.startswith(chosen + b"/")
|
|
39
|
+
for chosen in selected
|
|
40
|
+
)
|
|
41
|
+
]
|
|
42
|
+
staged = git.staged_paths()
|
|
43
|
+
return [entry for entry in index if entry.path in staged]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _from_root(git: Git, path: str) -> bytes:
|
|
47
|
+
"""A PATH argument as an index path: from the root, with ``/``; empty for the root."""
|
|
48
|
+
full = os.path.abspath(os.path.join(git.folder, path))
|
|
49
|
+
try:
|
|
50
|
+
relative = os.path.relpath(full, os.path.abspath(git.top))
|
|
51
|
+
except ValueError: # on another drive
|
|
52
|
+
relative = os.pardir
|
|
53
|
+
if relative == os.pardir or relative.startswith(os.pardir + os.sep):
|
|
54
|
+
raise OmertaError(f"{path} is outside the repository")
|
|
55
|
+
return b"" if relative == os.curdir else os.fsencode(relative.replace(os.sep, "/"))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def run(
|
|
59
|
+
git: Git,
|
|
60
|
+
entries: list[Entry],
|
|
61
|
+
targets: list[Target],
|
|
62
|
+
patterns: tuple[Pattern, ...],
|
|
63
|
+
findings: list[Finding],
|
|
64
|
+
) -> None:
|
|
65
|
+
"""Search the entries, adding what is found to ``findings``.
|
|
66
|
+
|
|
67
|
+
An entry that matches the patterns is a tracked env file, and a path that
|
|
68
|
+
holds a value is a finding of its own. A gitlink (a submodule) has no
|
|
69
|
+
content to search. An unmerged entry is searched in each of its stages,
|
|
70
|
+
and a value is reported at its first occurrence in the lowest stage that
|
|
71
|
+
holds it.
|
|
72
|
+
|
|
73
|
+
Raises:
|
|
74
|
+
OmertaError: An entry's object cannot be read.
|
|
75
|
+
"""
|
|
76
|
+
matcher = Matcher(targets)
|
|
77
|
+
for path in sorted({entry.path for entry in entries}):
|
|
78
|
+
for index, hit in matcher.search(path).items():
|
|
79
|
+
findings.append(_found(targets[index], hit.form, path=path, in_path=True))
|
|
80
|
+
entries = sorted(
|
|
81
|
+
(entry for entry in entries if entry.mode != GITLINK),
|
|
82
|
+
key=lambda entry: (entry.path, entry.stage),
|
|
83
|
+
)
|
|
84
|
+
tracked: set[bytes] = set()
|
|
85
|
+
for entry in entries:
|
|
86
|
+
if entry.path not in tracked and is_env_file(entry.path, patterns):
|
|
87
|
+
tracked.add(entry.path)
|
|
88
|
+
findings.append(Finding(path=entry.path))
|
|
89
|
+
if not targets:
|
|
90
|
+
return
|
|
91
|
+
by_oid: dict[str, list[Entry]] = {}
|
|
92
|
+
for entry in entries:
|
|
93
|
+
by_oid.setdefault(entry.oid, []).append(entry)
|
|
94
|
+
found: dict[tuple[bytes, int], tuple[int, Finding]] = {}
|
|
95
|
+
try:
|
|
96
|
+
for oid, _, data in git.objects(list(by_oid)):
|
|
97
|
+
hits = matcher.search(data)
|
|
98
|
+
for entry in by_oid[oid]:
|
|
99
|
+
for index, hit in hits.items():
|
|
100
|
+
key = (entry.path, index)
|
|
101
|
+
if key in found and found[key][0] <= entry.stage:
|
|
102
|
+
continue
|
|
103
|
+
line = line_of(data, hit.position)
|
|
104
|
+
found[key] = (
|
|
105
|
+
entry.stage,
|
|
106
|
+
_found(targets[index], hit.form, path=entry.path, line=line),
|
|
107
|
+
)
|
|
108
|
+
finally:
|
|
109
|
+
# What was found before an error is reported with it.
|
|
110
|
+
findings.extend(finding for _, finding in found.values())
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _found(
|
|
114
|
+
target: Target, form: str, *, path: bytes, line: int | None = None, in_path: bool = False
|
|
115
|
+
) -> Finding:
|
|
116
|
+
"""A finding of a target in an entry: in its content at a line, or in its path."""
|
|
117
|
+
return Finding(
|
|
118
|
+
path=path,
|
|
119
|
+
line=line,
|
|
120
|
+
in_path=in_path,
|
|
121
|
+
variable=target.variable,
|
|
122
|
+
env_file=target.env_file,
|
|
123
|
+
env_order=target.env_order,
|
|
124
|
+
form=form,
|
|
125
|
+
)
|