commit-check 2.3.0__py3-none-any.whl → 2.4.0__py3-none-any.whl

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.
@@ -0,0 +1,220 @@
1
+ """Configuration merger that combines CLI args, env vars, TOML config, and defaults."""
2
+
3
+ from __future__ import annotations
4
+ import os
5
+ import argparse
6
+ from typing import Dict, Any, Optional, List, Callable, Tuple
7
+
8
+ from commit_check.config import load_config as load_toml_config
9
+ from commit_check import (
10
+ DEFAULT_COMMIT_TYPES,
11
+ DEFAULT_BRANCH_TYPES,
12
+ DEFAULT_BRANCH_NAMES,
13
+ DEFAULT_BOOLEAN_RULES,
14
+ )
15
+
16
+
17
+ def parse_bool(value: Any) -> bool:
18
+ """Parse a boolean value from string, int, or bool.
19
+
20
+ Accepts: true/false, yes/no, 1/0, t/f, y/n (case-insensitive)
21
+ """
22
+ if isinstance(value, bool):
23
+ return value
24
+ if isinstance(value, int):
25
+ return bool(value)
26
+ if isinstance(value, str):
27
+ normalized = value.lower().strip()
28
+ if normalized in ("true", "yes", "1", "t", "y"):
29
+ return True
30
+ if normalized in ("false", "no", "0", "f", "n"):
31
+ return False
32
+ raise ValueError(f"Cannot parse '{value}' as boolean")
33
+ raise TypeError(f"Cannot convert {type(value).__name__} to bool")
34
+
35
+
36
+ def parse_list(value: Any) -> List[str]:
37
+ """Parse a list from comma-separated string or list."""
38
+ if isinstance(value, list):
39
+ return value
40
+ if isinstance(value, str):
41
+ # Split by comma and strip whitespace
42
+ return [item.strip() for item in value.split(",") if item.strip()]
43
+ raise TypeError(f"Cannot convert {type(value).__name__} to list")
44
+
45
+
46
+ def parse_int(value: Any) -> int:
47
+ """Parse an integer value."""
48
+ if isinstance(value, int):
49
+ return value
50
+ if isinstance(value, str):
51
+ try:
52
+ return int(value.strip())
53
+ except ValueError:
54
+ raise ValueError(f"Cannot parse '{value}' as integer")
55
+ raise TypeError(f"Cannot convert {type(value).__name__} to int")
56
+
57
+
58
+ def get_default_config() -> Dict[str, Any]:
59
+ """Get the default configuration with all options."""
60
+ return {
61
+ "commit": {
62
+ "conventional_commits": True,
63
+ "subject_capitalized": DEFAULT_BOOLEAN_RULES["subject_capitalized"],
64
+ "subject_imperative": DEFAULT_BOOLEAN_RULES["subject_imperative"],
65
+ "subject_max_length": 80,
66
+ "subject_min_length": 5,
67
+ "allow_commit_types": DEFAULT_COMMIT_TYPES.copy(),
68
+ "allow_merge_commits": DEFAULT_BOOLEAN_RULES["allow_merge_commits"],
69
+ "allow_revert_commits": DEFAULT_BOOLEAN_RULES["allow_revert_commits"],
70
+ "allow_empty_commits": DEFAULT_BOOLEAN_RULES["allow_empty_commits"],
71
+ "allow_fixup_commits": DEFAULT_BOOLEAN_RULES["allow_fixup_commits"],
72
+ "allow_wip_commits": DEFAULT_BOOLEAN_RULES["allow_wip_commits"],
73
+ "require_body": DEFAULT_BOOLEAN_RULES["require_body"],
74
+ "require_signed_off_by": DEFAULT_BOOLEAN_RULES["require_signed_off_by"],
75
+ "ignore_authors": [],
76
+ },
77
+ "branch": {
78
+ "conventional_branch": True,
79
+ "allow_branch_types": DEFAULT_BRANCH_TYPES.copy(),
80
+ "allow_branch_names": DEFAULT_BRANCH_NAMES.copy(),
81
+ "require_rebase_target": "",
82
+ "ignore_authors": [],
83
+ },
84
+ }
85
+
86
+
87
+ def deep_merge(base: Dict[str, Any], override: Dict[str, Any]) -> None:
88
+ """Deep merge override into base dictionary (modifies base in-place)."""
89
+ for key, value in override.items():
90
+ if key in base and isinstance(base[key], dict) and isinstance(value, dict):
91
+ deep_merge(base[key], value)
92
+ else:
93
+ base[key] = value
94
+
95
+
96
+ class ConfigMerger:
97
+ """Merges configurations from multiple sources with priority: CLI > Env > TOML > Defaults."""
98
+
99
+ # Mapping of environment variable names to config keys
100
+ ENV_VAR_MAPPING: Dict[str, Tuple[str, str, Callable[[Any], Any]]] = {
101
+ # Commit section
102
+ "CCHK_CONVENTIONAL_COMMITS": ("commit", "conventional_commits", parse_bool),
103
+ "CCHK_SUBJECT_CAPITALIZED": ("commit", "subject_capitalized", parse_bool),
104
+ "CCHK_SUBJECT_IMPERATIVE": ("commit", "subject_imperative", parse_bool),
105
+ "CCHK_SUBJECT_MAX_LENGTH": ("commit", "subject_max_length", parse_int),
106
+ "CCHK_SUBJECT_MIN_LENGTH": ("commit", "subject_min_length", parse_int),
107
+ "CCHK_ALLOW_COMMIT_TYPES": ("commit", "allow_commit_types", parse_list),
108
+ "CCHK_ALLOW_MERGE_COMMITS": ("commit", "allow_merge_commits", parse_bool),
109
+ "CCHK_ALLOW_REVERT_COMMITS": ("commit", "allow_revert_commits", parse_bool),
110
+ "CCHK_ALLOW_EMPTY_COMMITS": ("commit", "allow_empty_commits", parse_bool),
111
+ "CCHK_ALLOW_FIXUP_COMMITS": ("commit", "allow_fixup_commits", parse_bool),
112
+ "CCHK_ALLOW_WIP_COMMITS": ("commit", "allow_wip_commits", parse_bool),
113
+ "CCHK_REQUIRE_BODY": ("commit", "require_body", parse_bool),
114
+ "CCHK_REQUIRE_SIGNED_OFF_BY": ("commit", "require_signed_off_by", parse_bool),
115
+ "CCHK_IGNORE_AUTHORS": ("commit", "ignore_authors", parse_list),
116
+ # Branch section
117
+ "CCHK_CONVENTIONAL_BRANCH": ("branch", "conventional_branch", parse_bool),
118
+ "CCHK_ALLOW_BRANCH_TYPES": ("branch", "allow_branch_types", parse_list),
119
+ "CCHK_ALLOW_BRANCH_NAMES": ("branch", "allow_branch_names", parse_list),
120
+ "CCHK_REQUIRE_REBASE_TARGET": ("branch", "require_rebase_target", str),
121
+ "CCHK_BRANCH_IGNORE_AUTHORS": ("branch", "ignore_authors", parse_list),
122
+ }
123
+
124
+ # Mapping of CLI argument names to config keys
125
+ CLI_ARG_MAPPING: Dict[str, Tuple[str, str]] = {
126
+ # Commit section
127
+ "conventional_commits": ("commit", "conventional_commits"),
128
+ "subject_capitalized": ("commit", "subject_capitalized"),
129
+ "subject_imperative": ("commit", "subject_imperative"),
130
+ "subject_max_length": ("commit", "subject_max_length"),
131
+ "subject_min_length": ("commit", "subject_min_length"),
132
+ "allow_commit_types": ("commit", "allow_commit_types"),
133
+ "allow_merge_commits": ("commit", "allow_merge_commits"),
134
+ "allow_revert_commits": ("commit", "allow_revert_commits"),
135
+ "allow_empty_commits": ("commit", "allow_empty_commits"),
136
+ "allow_fixup_commits": ("commit", "allow_fixup_commits"),
137
+ "allow_wip_commits": ("commit", "allow_wip_commits"),
138
+ "require_body": ("commit", "require_body"),
139
+ "require_signed_off_by": ("commit", "require_signed_off_by"),
140
+ "ignore_authors": ("commit", "ignore_authors"),
141
+ # Branch section
142
+ "conventional_branch": ("branch", "conventional_branch"),
143
+ "allow_branch_types": ("branch", "allow_branch_types"),
144
+ "allow_branch_names": ("branch", "allow_branch_names"),
145
+ "require_rebase_target": ("branch", "require_rebase_target"),
146
+ "branch_ignore_authors": ("branch", "ignore_authors"),
147
+ }
148
+
149
+ @staticmethod
150
+ def parse_env_vars() -> Dict[str, Any]:
151
+ """Parse environment variables with CCHK_ prefix into config dict."""
152
+ config: Dict[str, Any] = {"commit": {}, "branch": {}}
153
+
154
+ for env_var, (section, key, parser) in ConfigMerger.ENV_VAR_MAPPING.items():
155
+ value = os.environ.get(env_var)
156
+ if value is not None:
157
+ try:
158
+ parsed_value = parser(value)
159
+ config[section][key] = parsed_value
160
+ except (ValueError, TypeError) as e:
161
+ # Log warning but don't fail - just skip invalid env vars
162
+ print(f"Warning: Invalid value for {env_var}: {e}")
163
+
164
+ # Remove empty sections
165
+ config = {k: v for k, v in config.items() if v}
166
+ return config
167
+
168
+ @staticmethod
169
+ def parse_cli_args(args: argparse.Namespace) -> Dict[str, Any]:
170
+ """Parse CLI arguments into config dict."""
171
+ config: Dict[str, Any] = {"commit": {}, "branch": {}}
172
+
173
+ for arg_name, (section, key) in ConfigMerger.CLI_ARG_MAPPING.items():
174
+ if hasattr(args, arg_name):
175
+ value = getattr(args, arg_name)
176
+ if value is not None:
177
+ config[section][key] = value
178
+
179
+ # Remove empty sections
180
+ config = {k: v for k, v in config.items() if v}
181
+ return config
182
+
183
+ @staticmethod
184
+ def from_all_sources(
185
+ cli_args: argparse.Namespace, config_path: Optional[str] = None
186
+ ) -> Dict[str, Any]:
187
+ """Merge configs from all sources with priority: CLI > Env > TOML > Defaults.
188
+
189
+ Args:
190
+ cli_args: Parsed command line arguments
191
+ config_path: Optional path to TOML config file
192
+
193
+ Returns:
194
+ Merged configuration dictionary
195
+ """
196
+ # 1. Start with defaults
197
+ config = get_default_config()
198
+
199
+ # 2. Merge TOML config (if exists)
200
+ try:
201
+ toml_config = load_toml_config(config_path or "")
202
+ if toml_config:
203
+ deep_merge(config, toml_config)
204
+ except FileNotFoundError:
205
+ # If a specific path was provided and not found, this error is already raised
206
+ # If no path provided and no default files exist, that's fine
207
+ if config_path:
208
+ raise
209
+
210
+ # 3. Merge environment variables
211
+ env_config = ConfigMerger.parse_env_vars()
212
+ if env_config:
213
+ deep_merge(config, env_config)
214
+
215
+ # 4. Merge CLI arguments (highest priority)
216
+ cli_config = ConfigMerger.parse_cli_args(cli_args)
217
+ if cli_config:
218
+ deep_merge(config, cli_config)
219
+
220
+ return config
@@ -70,6 +70,8 @@ IMPERATIVES = {
70
70
  "encapsulate",
71
71
  "encode",
72
72
  "end",
73
+ "enforce",
74
+ "enhance",
73
75
  "ensure",
74
76
  "enumerate",
75
77
  "establish",
commit_check/main.py CHANGED
@@ -5,7 +5,7 @@ import sys
5
5
  import argparse
6
6
  from typing import Optional
7
7
 
8
- from commit_check.config import load_config
8
+ from commit_check.config_merger import ConfigMerger, parse_bool, parse_list, parse_int
9
9
  from commit_check.rule_builder import RuleBuilder
10
10
  from commit_check.engine import ValidationEngine, ValidationContext, ValidationResult
11
11
  from . import __version__
@@ -86,6 +86,160 @@ def _get_parser() -> argparse.ArgumentParser:
86
86
  required=False,
87
87
  )
88
88
 
89
+ # Commit configuration options
90
+ parser.add_argument(
91
+ "--conventional-commits",
92
+ type=parse_bool,
93
+ default=None,
94
+ metavar="BOOL",
95
+ help="enforce conventional commits format (true/false)",
96
+ )
97
+
98
+ parser.add_argument(
99
+ "--subject-capitalized",
100
+ type=parse_bool,
101
+ default=None,
102
+ metavar="BOOL",
103
+ help="require subject to start with capital letter (true/false)",
104
+ )
105
+
106
+ parser.add_argument(
107
+ "--subject-imperative",
108
+ type=parse_bool,
109
+ default=None,
110
+ metavar="BOOL",
111
+ help="require subject to use imperative mood (true/false)",
112
+ )
113
+
114
+ parser.add_argument(
115
+ "--subject-max-length",
116
+ type=parse_int,
117
+ default=None,
118
+ metavar="INT",
119
+ help="maximum length of commit subject",
120
+ )
121
+
122
+ parser.add_argument(
123
+ "--subject-min-length",
124
+ type=parse_int,
125
+ default=None,
126
+ metavar="INT",
127
+ help="minimum length of commit subject",
128
+ )
129
+
130
+ parser.add_argument(
131
+ "--allow-commit-types",
132
+ type=parse_list,
133
+ default=None,
134
+ metavar="LIST",
135
+ help="comma-separated list of allowed commit types (e.g., feat,fix,docs)",
136
+ )
137
+
138
+ parser.add_argument(
139
+ "--allow-merge-commits",
140
+ type=parse_bool,
141
+ default=None,
142
+ metavar="BOOL",
143
+ help="allow merge commits (true/false)",
144
+ )
145
+
146
+ parser.add_argument(
147
+ "--allow-revert-commits",
148
+ type=parse_bool,
149
+ default=None,
150
+ metavar="BOOL",
151
+ help="allow revert commits (true/false)",
152
+ )
153
+
154
+ parser.add_argument(
155
+ "--allow-empty-commits",
156
+ type=parse_bool,
157
+ default=None,
158
+ metavar="BOOL",
159
+ help="allow empty commit messages (true/false)",
160
+ )
161
+
162
+ parser.add_argument(
163
+ "--allow-fixup-commits",
164
+ type=parse_bool,
165
+ default=None,
166
+ metavar="BOOL",
167
+ help="allow fixup commits (true/false)",
168
+ )
169
+
170
+ parser.add_argument(
171
+ "--allow-wip-commits",
172
+ type=parse_bool,
173
+ default=None,
174
+ metavar="BOOL",
175
+ help="allow WIP commits (true/false)",
176
+ )
177
+
178
+ parser.add_argument(
179
+ "--require-body",
180
+ type=parse_bool,
181
+ default=None,
182
+ metavar="BOOL",
183
+ help="require commit body (true/false)",
184
+ )
185
+
186
+ parser.add_argument(
187
+ "--require-signed-off-by",
188
+ type=parse_bool,
189
+ default=None,
190
+ metavar="BOOL",
191
+ help="require 'Signed-off-by' trailer (true/false)",
192
+ )
193
+
194
+ parser.add_argument(
195
+ "--ignore-authors",
196
+ type=parse_list,
197
+ default=None,
198
+ metavar="LIST",
199
+ help="comma-separated list of authors to ignore for commit checks",
200
+ )
201
+
202
+ # Branch configuration options
203
+ parser.add_argument(
204
+ "--conventional-branch",
205
+ type=parse_bool,
206
+ default=None,
207
+ metavar="BOOL",
208
+ help="enforce conventional branch naming (true/false)",
209
+ )
210
+
211
+ parser.add_argument(
212
+ "--allow-branch-types",
213
+ type=parse_list,
214
+ default=None,
215
+ metavar="LIST",
216
+ help="comma-separated list of allowed branch types (e.g., feature,bugfix,hotfix)",
217
+ )
218
+
219
+ parser.add_argument(
220
+ "--allow-branch-names",
221
+ type=parse_list,
222
+ default=None,
223
+ metavar="LIST",
224
+ help="comma-separated list of additional allowed branch names",
225
+ )
226
+
227
+ parser.add_argument(
228
+ "--require-rebase-target",
229
+ type=str,
230
+ default=None,
231
+ metavar="BRANCH",
232
+ help="target branch for rebase validation",
233
+ )
234
+
235
+ parser.add_argument(
236
+ "--branch-ignore-authors",
237
+ type=parse_list,
238
+ default=None,
239
+ metavar="LIST",
240
+ help="comma-separated list of authors to ignore for branch checks",
241
+ )
242
+
89
243
  return parser
90
244
 
91
245
 
@@ -135,8 +289,8 @@ def main() -> int:
135
289
  stdin_reader = StdinReader()
136
290
 
137
291
  try:
138
- # Load configuration
139
- config_data = load_config(args.config)
292
+ # Load and merge configuration from all sources: CLI > Env > TOML > Defaults
293
+ config_data = ConfigMerger.from_all_sources(args, args.config)
140
294
 
141
295
  # Build validation rules from config
142
296
  rule_builder = RuleBuilder(config_data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: commit-check
3
- Version: 2.3.0
3
+ Version: 2.4.0
4
4
  Summary: Check commit message formatting, branch naming, commit author, email, and more.
5
5
  Author-email: Xianpeng Shen <xianpeng.shen@gmail.com>
6
6
  License-Expression: MIT
@@ -111,6 +111,12 @@ For more information, see the `docs <https://commit-check.github.io/commit-check
111
111
  Configuration
112
112
  -------------
113
113
 
114
+ Commit Check can be configured in three ways (in order of priority):
115
+
116
+ 1. **Command-line arguments** — Override settings for specific runs
117
+ 2. **Environment variables** — Configure via ``CCHK_*`` environment variables
118
+ 3. **Configuration files** — Use ``cchk.toml`` or ``commit-check.toml``
119
+
114
120
  Use Default Configuration
115
121
  ~~~~~~~~~~~~~~~~~~~~~~~~~
116
122
 
@@ -118,11 +124,38 @@ Use Default Configuration
118
124
 
119
125
  - The default configuration is lenient — it only checks whether commit messages follow the `Conventional Commits <https://www.conventionalcommits.org/en/v1.0.0/#summary>`_ specification and branch names follow the `Conventional Branch <https://conventional-branch.github.io/#summary>`_ convention.
120
126
 
121
- Use Custom Configuration
122
- ~~~~~~~~~~~~~~~~~~~~~~~~
127
+ Use Custom Configuration File
128
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
123
129
 
124
130
  To customize the behavior, create a configuration file named ``cchk.toml`` or ``commit-check.toml`` in your repository's root directory or in the ``.github`` folder, e.g., `cchk.toml <https://github.com/commit-check/commit-check/blob/main/cchk.toml>`_ or ``.github/cchk.toml``.
125
131
 
132
+ Use CLI Arguments or Environment Variables
133
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
134
+
135
+ For one-off checks or CI/CD pipelines, you can configure via CLI arguments or environment variables:
136
+
137
+ .. code-block:: bash
138
+
139
+ # Using CLI arguments
140
+ commit-check --message --subject-imperative=true --subject-max-length=72
141
+
142
+ # Using environment variables
143
+ export CCHK_SUBJECT_IMPERATIVE=true
144
+ export CCHK_SUBJECT_MAX_LENGTH=72
145
+ commit-check --message
146
+
147
+ # In pre-commit hooks (.pre-commit-config.yaml)
148
+ repos:
149
+ - repo: https://github.com/commit-check/commit-check
150
+ rev: v2.3.0
151
+ hooks:
152
+ - id: commit-check
153
+ args:
154
+ - --subject-imperative=false
155
+ - --subject-max-length=100
156
+
157
+ See the `Configuration documentation <https://commit-check.github.io/commit-check/configuration.html>`_ for all available options.
158
+
126
159
  Usage
127
160
  -----
128
161
 
@@ -0,0 +1,15 @@
1
+ commit_check/__init__.py,sha256=6bUw-6fUjCN5qAxe6i4mUztQiAHpjOhyPHsegW1tbBc,1297
2
+ commit_check/config.py,sha256=88zOLW6W2DHQjglxfSR3LQhCXgw-QcfEg118-OaWxIg,1031
3
+ commit_check/config_merger.py,sha256=QwOLi3-pm-1w77aMiBeJzJLKoERDJlzH4IfTyNa421I,9427
4
+ commit_check/engine.py,sha256=O9H39JgqrzWclfVTZYf-tJP8lAhHzGfV7j-CIxAsXWA,19348
5
+ commit_check/imperatives.py,sha256=btNOK7HWYB3-Dc58OdgWnxW0YSHl4Q5oFEiN9jqsByM,3612
6
+ commit_check/main.py,sha256=Hth6h2wrHFnrJro6TKL5AzkTORaUWp-p2xCjgq3v2-4,10977
7
+ commit_check/rule_builder.py,sha256=0UqMQxDiPNgqEjmRTYnyHimCA7vZRZqgqdm7ughp3DY,9205
8
+ commit_check/rules_catalog.py,sha256=FSRsdecwfUteEp3J8lnfehLu4j6owZlWfDzPa1ar1Fg,4288
9
+ commit_check/util.py,sha256=4c9cxbBR3ZBSmYGNmzj87XWWr0zsCV8tmg1Z1yZ3iBI,8501
10
+ commit_check-2.4.0.dist-info/licenses/LICENSE,sha256=VAJ9TE1ov8aUKmeoBRYqciMs0CXag1TeDCoLhwbeQmA,1095
11
+ commit_check-2.4.0.dist-info/METADATA,sha256=f18hat-Ayk0bgUFqQSx7yxS09RVvuTdATDeharpmG-k,10723
12
+ commit_check-2.4.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
13
+ commit_check-2.4.0.dist-info/entry_points.txt,sha256=YMrkFGeub-3-IanV1d9qnopXVaRQM7MA9foXKW9iZ88,86
14
+ commit_check-2.4.0.dist-info/top_level.txt,sha256=Wf46u-ooHBMJNHbhfrBNQw3wC5_m8wt-o_Lfbc4QpRg,13
15
+ commit_check-2.4.0.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- commit_check/__init__.py,sha256=6bUw-6fUjCN5qAxe6i4mUztQiAHpjOhyPHsegW1tbBc,1297
2
- commit_check/config.py,sha256=88zOLW6W2DHQjglxfSR3LQhCXgw-QcfEg118-OaWxIg,1031
3
- commit_check/engine.py,sha256=O9H39JgqrzWclfVTZYf-tJP8lAhHzGfV7j-CIxAsXWA,19348
4
- commit_check/imperatives.py,sha256=b_olcxhoHW2LMHaO9COVlVUdLE-7uifo9WeSY7rPql4,3582
5
- commit_check/main.py,sha256=SSQfN5p5AQoG0G_AFXA2wAoTcOJK8N8m8_gj8hkH-OI,7040
6
- commit_check/rule_builder.py,sha256=0UqMQxDiPNgqEjmRTYnyHimCA7vZRZqgqdm7ughp3DY,9205
7
- commit_check/rules_catalog.py,sha256=FSRsdecwfUteEp3J8lnfehLu4j6owZlWfDzPa1ar1Fg,4288
8
- commit_check/util.py,sha256=4c9cxbBR3ZBSmYGNmzj87XWWr0zsCV8tmg1Z1yZ3iBI,8501
9
- commit_check-2.3.0.dist-info/licenses/LICENSE,sha256=VAJ9TE1ov8aUKmeoBRYqciMs0CXag1TeDCoLhwbeQmA,1095
10
- commit_check-2.3.0.dist-info/METADATA,sha256=TlR6GZ1noth7Nx-brsfWEs9bSur2rRiu4GaE5N2zle4,9540
11
- commit_check-2.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
- commit_check-2.3.0.dist-info/entry_points.txt,sha256=YMrkFGeub-3-IanV1d9qnopXVaRQM7MA9foXKW9iZ88,86
13
- commit_check-2.3.0.dist-info/top_level.txt,sha256=Wf46u-ooHBMJNHbhfrBNQw3wC5_m8wt-o_Lfbc4QpRg,13
14
- commit_check-2.3.0.dist-info/RECORD,,