git-commit-guard 0.21.0__tar.gz → 0.22.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.
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.github/workflows/lint-commits.yml +1 -1
- git_commit_guard-0.22.0/.github/workflows/lint-md.yml +68 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.github/workflows/release.yml +0 -3
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/PKG-INFO +18 -12
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/README.md +17 -11
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/action.yml +10 -2
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/docs/index.html +17 -9
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/src/git_commit_guard/__init__.py +19 -2
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/tests/test_git_commit_guard.py +62 -2
- git_commit_guard-0.21.0/.github/workflows/lint-md.yml +0 -26
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.editorconfig +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.github/workflows/coverage-baseline.yml +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.github/workflows/coverage-comment.yml +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.github/workflows/lint-python.yml +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.github/workflows/lint-workflows.yml +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.github/workflows/test.yml +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.gitignore +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.markdownlint.json +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.pre-commit-hooks.yaml +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/.python-version +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/LICENSE +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/cliff.toml +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/docs/commit-guard-icon.svg +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/pyproject.toml +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/ruff.toml +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/tests/__init__.py +0 -0
- {git_commit_guard-0.21.0 → git_commit_guard-0.22.0}/uv.lock +0 -0
|
@@ -22,6 +22,6 @@ jobs:
|
|
|
22
22
|
key: nltk-averaged-perceptron-tagger-punkt
|
|
23
23
|
- name: Lint commits
|
|
24
24
|
# yamllint disable-line rule:line-length
|
|
25
|
-
uses: benner/commit-guard@
|
|
25
|
+
uses: benner/commit-guard@22a3b0fdb044e874250fc6525ff0905b20fa3a62 # v0.21.0
|
|
26
26
|
with:
|
|
27
27
|
range: origin/${{ github.base_ref }}..HEAD
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Lint Markdown
|
|
3
|
+
on: # yamllint disable-line rule:truthy
|
|
4
|
+
pull_request:
|
|
5
|
+
permissions:
|
|
6
|
+
contents: read
|
|
7
|
+
jobs:
|
|
8
|
+
lint-md:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
pull-requests: write
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout code
|
|
15
|
+
# yamllint disable-line rule:line-length
|
|
16
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
17
|
+
with:
|
|
18
|
+
persist-credentials: false
|
|
19
|
+
- name: Lint Markdown files with reviewdog
|
|
20
|
+
# yamllint disable-line rule:line-length
|
|
21
|
+
uses: reviewdog/action-markdownlint@3667398db9118d7e78f7a63d10e26ce454ba5f58 # v0.26.2
|
|
22
|
+
with:
|
|
23
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
24
|
+
reporter: github-pr-review
|
|
25
|
+
level: info
|
|
26
|
+
fail_level: any
|
|
27
|
+
lint-md-rumdl:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
pull-requests: write
|
|
32
|
+
steps:
|
|
33
|
+
- name: Checkout code
|
|
34
|
+
# yamllint disable-line rule:line-length
|
|
35
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
36
|
+
with:
|
|
37
|
+
persist-credentials: false
|
|
38
|
+
- name: Set up uv
|
|
39
|
+
# yamllint disable-line rule:line-length
|
|
40
|
+
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
|
41
|
+
with:
|
|
42
|
+
enable-cache: false
|
|
43
|
+
- name: Set up reviewdog
|
|
44
|
+
# yamllint disable-line rule:line-length
|
|
45
|
+
uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # ratchet:reviewdog/action-setup@v1
|
|
46
|
+
- name: Lint Markdown files with rumdl via reviewdog
|
|
47
|
+
env:
|
|
48
|
+
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
49
|
+
run: |-
|
|
50
|
+
uvx rumdl check . --output-format json-lines > rumdl.jsonl || true
|
|
51
|
+
jq -c '
|
|
52
|
+
{
|
|
53
|
+
source: {name: "rumdl"},
|
|
54
|
+
severity: (.severity | ascii_upcase),
|
|
55
|
+
message: .message,
|
|
56
|
+
location: {
|
|
57
|
+
path: .file,
|
|
58
|
+
range: {start: {line: .line, column: .column}}
|
|
59
|
+
},
|
|
60
|
+
code: {value: .rule}
|
|
61
|
+
}
|
|
62
|
+
' rumdl.jsonl \
|
|
63
|
+
| reviewdog \
|
|
64
|
+
-f=rdjsonl \
|
|
65
|
+
-name=rumdl \
|
|
66
|
+
-reporter=github-pr-review \
|
|
67
|
+
-filter-mode=added \
|
|
68
|
+
-fail-level=any
|
|
@@ -18,9 +18,6 @@ jobs:
|
|
|
18
18
|
with:
|
|
19
19
|
persist-credentials: false
|
|
20
20
|
fetch-depth: 0
|
|
21
|
-
- name: Set up Python
|
|
22
|
-
# yamllint disable-line rule:line-length
|
|
23
|
-
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
24
21
|
- name: Set up uv
|
|
25
22
|
# yamllint disable-line rule:line-length
|
|
26
23
|
uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7.6.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: git-commit-guard
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.22.0
|
|
4
4
|
Summary: Opinionated conventional commit message linter with imperative mood detection
|
|
5
5
|
Project-URL: Homepage, https://github.com/benner/commit-guard
|
|
6
6
|
Project-URL: Repository, https://github.com/benner/commit-guard
|
|
@@ -47,7 +47,9 @@ Opinionated conventional commit message linter with imperative mood detection.
|
|
|
47
47
|
$ commit-guard
|
|
48
48
|
✗ [subject] subject does not match 'type(scope): description': WIP
|
|
49
49
|
✗ [signed-off] missing 'Signed-off-by' trailer — use 'git commit -s'
|
|
50
|
-
✗ [signature]
|
|
50
|
+
✗ [signature] signature could not be verified — commit may be
|
|
51
|
+
unsigned, or signed with a key not uploaded as a
|
|
52
|
+
Signing key on https://github.com/settings/keys
|
|
51
53
|
```
|
|
52
54
|
|
|
53
55
|
## Installation
|
|
@@ -249,8 +251,10 @@ The `signature` check verifies the commit without any local keyring setup:
|
|
|
249
251
|
`{username}@users.noreply.github.com`) — no API call needed.
|
|
250
252
|
3. If neither of the above resolves a username, fall back to searching GitHub
|
|
251
253
|
by the commit author's email.
|
|
252
|
-
4. Fetch the resolved user's public keys from `github.com/{username}.gpg`
|
|
253
|
-
|
|
254
|
+
4. Fetch the resolved user's public keys from `github.com/{username}.gpg`
|
|
255
|
+
(GPG) and the `/users/{username}/ssh_signing_keys` API (SSH keys tagged
|
|
256
|
+
with the **Signing key** role). Auth-only SSH keys are deliberately not
|
|
257
|
+
accepted — this mirrors GitHub's "Verified" badge semantics.
|
|
254
258
|
5. Try GPG verification: import the fetched key into a temporary keyring and
|
|
255
259
|
run `git verify-commit`.
|
|
256
260
|
6. Try SSH verification: write a temporary `allowed_signers` file and run
|
|
@@ -261,7 +265,9 @@ If the author cannot be resolved via either method, or the GitHub API is
|
|
|
261
265
|
unreachable, the check fails with a clear error.
|
|
262
266
|
|
|
263
267
|
For private repositories, set `GITHUB_TOKEN` or `GH_TOKEN` so the Commits API
|
|
264
|
-
can authenticate.
|
|
268
|
+
can authenticate. The official GitHub Action wires the workflow's automatic
|
|
269
|
+
token via the `github-token` input, so no manual `env:` is required; override
|
|
270
|
+
with a PAT only for cross-repo lookups.
|
|
265
271
|
|
|
266
272
|
### Configuration file
|
|
267
273
|
|
|
@@ -310,7 +316,7 @@ COMMIT_GUARD_GIT_TIMEOUT=30 commit-guard --range origin/main..HEAD
|
|
|
310
316
|
In GitHub Actions, set it at the step or job level:
|
|
311
317
|
|
|
312
318
|
```yaml
|
|
313
|
-
- uses: benner/commit-guard@v0.
|
|
319
|
+
- uses: benner/commit-guard@v0.22.0
|
|
314
320
|
env:
|
|
315
321
|
COMMIT_GUARD_GIT_TIMEOUT: 30
|
|
316
322
|
with:
|
|
@@ -394,7 +400,7 @@ steps:
|
|
|
394
400
|
- uses: actions/checkout@v4
|
|
395
401
|
with:
|
|
396
402
|
fetch-depth: 0
|
|
397
|
-
- uses: benner/commit-guard@v0.
|
|
403
|
+
- uses: benner/commit-guard@v0.22.0
|
|
398
404
|
```
|
|
399
405
|
|
|
400
406
|
Check all commits in a pull request:
|
|
@@ -410,7 +416,7 @@ jobs:
|
|
|
410
416
|
- uses: actions/checkout@v4
|
|
411
417
|
with:
|
|
412
418
|
fetch-depth: 0
|
|
413
|
-
- uses: benner/commit-guard@v0.
|
|
419
|
+
- uses: benner/commit-guard@v0.22.0
|
|
414
420
|
with:
|
|
415
421
|
range: ${{ env.PR_BASE }}..${{ env.PR_HEAD }}
|
|
416
422
|
```
|
|
@@ -418,7 +424,7 @@ jobs:
|
|
|
418
424
|
Check a specific commit SHA (mirrors the positional CLI argument):
|
|
419
425
|
|
|
420
426
|
```yaml
|
|
421
|
-
- uses: benner/commit-guard@v0.
|
|
427
|
+
- uses: benner/commit-guard@v0.22.0
|
|
422
428
|
with:
|
|
423
429
|
rev: ${{ github.sha }}
|
|
424
430
|
```
|
|
@@ -436,7 +442,7 @@ jobs:
|
|
|
436
442
|
- uses: actions/checkout@v4
|
|
437
443
|
with:
|
|
438
444
|
fetch-depth: 0
|
|
439
|
-
- uses: benner/commit-guard@v0.
|
|
445
|
+
- uses: benner/commit-guard@v0.22.0
|
|
440
446
|
with:
|
|
441
447
|
range: ${{ env.PR_BASE }}..${{ env.PR_HEAD }}
|
|
442
448
|
disable: signed-off,signature
|
|
@@ -456,7 +462,7 @@ jobs:
|
|
|
456
462
|
When `output-file` is set the action exposes the path as an output:
|
|
457
463
|
|
|
458
464
|
```yaml
|
|
459
|
-
- uses: benner/commit-guard@v0.
|
|
465
|
+
- uses: benner/commit-guard@v0.22.0
|
|
460
466
|
id: cg
|
|
461
467
|
with:
|
|
462
468
|
range: ${{ env.PR_BASE }}..${{ env.PR_HEAD }}
|
|
@@ -472,7 +478,7 @@ Add to your `.pre-commit-config.yaml`:
|
|
|
472
478
|
---
|
|
473
479
|
repos:
|
|
474
480
|
- repo: https://github.com/benner/commit-guard
|
|
475
|
-
rev: v0.
|
|
481
|
+
rev: v0.22.0
|
|
476
482
|
hooks:
|
|
477
483
|
- id: commit-guard
|
|
478
484
|
- id: commit-guard-signature
|
|
@@ -26,7 +26,9 @@ Opinionated conventional commit message linter with imperative mood detection.
|
|
|
26
26
|
$ commit-guard
|
|
27
27
|
✗ [subject] subject does not match 'type(scope): description': WIP
|
|
28
28
|
✗ [signed-off] missing 'Signed-off-by' trailer — use 'git commit -s'
|
|
29
|
-
✗ [signature]
|
|
29
|
+
✗ [signature] signature could not be verified — commit may be
|
|
30
|
+
unsigned, or signed with a key not uploaded as a
|
|
31
|
+
Signing key on https://github.com/settings/keys
|
|
30
32
|
```
|
|
31
33
|
|
|
32
34
|
## Installation
|
|
@@ -228,8 +230,10 @@ The `signature` check verifies the commit without any local keyring setup:
|
|
|
228
230
|
`{username}@users.noreply.github.com`) — no API call needed.
|
|
229
231
|
3. If neither of the above resolves a username, fall back to searching GitHub
|
|
230
232
|
by the commit author's email.
|
|
231
|
-
4. Fetch the resolved user's public keys from `github.com/{username}.gpg`
|
|
232
|
-
|
|
233
|
+
4. Fetch the resolved user's public keys from `github.com/{username}.gpg`
|
|
234
|
+
(GPG) and the `/users/{username}/ssh_signing_keys` API (SSH keys tagged
|
|
235
|
+
with the **Signing key** role). Auth-only SSH keys are deliberately not
|
|
236
|
+
accepted — this mirrors GitHub's "Verified" badge semantics.
|
|
233
237
|
5. Try GPG verification: import the fetched key into a temporary keyring and
|
|
234
238
|
run `git verify-commit`.
|
|
235
239
|
6. Try SSH verification: write a temporary `allowed_signers` file and run
|
|
@@ -240,7 +244,9 @@ If the author cannot be resolved via either method, or the GitHub API is
|
|
|
240
244
|
unreachable, the check fails with a clear error.
|
|
241
245
|
|
|
242
246
|
For private repositories, set `GITHUB_TOKEN` or `GH_TOKEN` so the Commits API
|
|
243
|
-
can authenticate.
|
|
247
|
+
can authenticate. The official GitHub Action wires the workflow's automatic
|
|
248
|
+
token via the `github-token` input, so no manual `env:` is required; override
|
|
249
|
+
with a PAT only for cross-repo lookups.
|
|
244
250
|
|
|
245
251
|
### Configuration file
|
|
246
252
|
|
|
@@ -289,7 +295,7 @@ COMMIT_GUARD_GIT_TIMEOUT=30 commit-guard --range origin/main..HEAD
|
|
|
289
295
|
In GitHub Actions, set it at the step or job level:
|
|
290
296
|
|
|
291
297
|
```yaml
|
|
292
|
-
- uses: benner/commit-guard@v0.
|
|
298
|
+
- uses: benner/commit-guard@v0.22.0
|
|
293
299
|
env:
|
|
294
300
|
COMMIT_GUARD_GIT_TIMEOUT: 30
|
|
295
301
|
with:
|
|
@@ -373,7 +379,7 @@ steps:
|
|
|
373
379
|
- uses: actions/checkout@v4
|
|
374
380
|
with:
|
|
375
381
|
fetch-depth: 0
|
|
376
|
-
- uses: benner/commit-guard@v0.
|
|
382
|
+
- uses: benner/commit-guard@v0.22.0
|
|
377
383
|
```
|
|
378
384
|
|
|
379
385
|
Check all commits in a pull request:
|
|
@@ -389,7 +395,7 @@ jobs:
|
|
|
389
395
|
- uses: actions/checkout@v4
|
|
390
396
|
with:
|
|
391
397
|
fetch-depth: 0
|
|
392
|
-
- uses: benner/commit-guard@v0.
|
|
398
|
+
- uses: benner/commit-guard@v0.22.0
|
|
393
399
|
with:
|
|
394
400
|
range: ${{ env.PR_BASE }}..${{ env.PR_HEAD }}
|
|
395
401
|
```
|
|
@@ -397,7 +403,7 @@ jobs:
|
|
|
397
403
|
Check a specific commit SHA (mirrors the positional CLI argument):
|
|
398
404
|
|
|
399
405
|
```yaml
|
|
400
|
-
- uses: benner/commit-guard@v0.
|
|
406
|
+
- uses: benner/commit-guard@v0.22.0
|
|
401
407
|
with:
|
|
402
408
|
rev: ${{ github.sha }}
|
|
403
409
|
```
|
|
@@ -415,7 +421,7 @@ jobs:
|
|
|
415
421
|
- uses: actions/checkout@v4
|
|
416
422
|
with:
|
|
417
423
|
fetch-depth: 0
|
|
418
|
-
- uses: benner/commit-guard@v0.
|
|
424
|
+
- uses: benner/commit-guard@v0.22.0
|
|
419
425
|
with:
|
|
420
426
|
range: ${{ env.PR_BASE }}..${{ env.PR_HEAD }}
|
|
421
427
|
disable: signed-off,signature
|
|
@@ -435,7 +441,7 @@ jobs:
|
|
|
435
441
|
When `output-file` is set the action exposes the path as an output:
|
|
436
442
|
|
|
437
443
|
```yaml
|
|
438
|
-
- uses: benner/commit-guard@v0.
|
|
444
|
+
- uses: benner/commit-guard@v0.22.0
|
|
439
445
|
id: cg
|
|
440
446
|
with:
|
|
441
447
|
range: ${{ env.PR_BASE }}..${{ env.PR_HEAD }}
|
|
@@ -451,7 +457,7 @@ Add to your `.pre-commit-config.yaml`:
|
|
|
451
457
|
---
|
|
452
458
|
repos:
|
|
453
459
|
- repo: https://github.com/benner/commit-guard
|
|
454
|
-
rev: v0.
|
|
460
|
+
rev: v0.22.0
|
|
455
461
|
hooks:
|
|
456
462
|
- id: commit-guard
|
|
457
463
|
- id: commit-guard-signature
|
|
@@ -39,7 +39,8 @@ inputs:
|
|
|
39
39
|
required: false
|
|
40
40
|
default: 'false'
|
|
41
41
|
no-trailing-chars:
|
|
42
|
-
description: Forbidden trailing characters, comma-separated (default '.', '!',
|
|
42
|
+
description: Forbidden trailing characters, comma-separated (default '.', '!',
|
|
43
|
+
'?', ' ')
|
|
43
44
|
required: false
|
|
44
45
|
allow-empty:
|
|
45
46
|
description: Exit 0 when --range yields no commits
|
|
@@ -58,9 +59,15 @@ inputs:
|
|
|
58
59
|
output-file:
|
|
59
60
|
description: Write JSONL results to this file path (text still goes to stdout)
|
|
60
61
|
required: false
|
|
62
|
+
github-token:
|
|
63
|
+
description: GitHub token for Commits API access (signature check). Defaults to
|
|
64
|
+
the workflow's automatic token; override with a PAT for cross-repo lookups.
|
|
65
|
+
required: false
|
|
66
|
+
default: ${{ github.token }}
|
|
61
67
|
outputs:
|
|
62
68
|
output-file:
|
|
63
|
-
description: Path to the JSONL output file (set only when output-file input is
|
|
69
|
+
description: Path to the JSONL output file (set only when output-file input is
|
|
70
|
+
provided)
|
|
64
71
|
value: ${{ steps.run.outputs.output-file }}
|
|
65
72
|
runs:
|
|
66
73
|
using: composite
|
|
@@ -76,6 +83,7 @@ runs:
|
|
|
76
83
|
- name: Run commit-guard
|
|
77
84
|
id: run
|
|
78
85
|
env:
|
|
86
|
+
GITHUB_TOKEN: ${{ inputs.github-token }}
|
|
79
87
|
CG_REV: ${{ inputs.rev }}
|
|
80
88
|
CG_RANGE: ${{ inputs.range }}
|
|
81
89
|
CG_ENABLE: ${{ inputs.enable }}
|
|
@@ -294,7 +294,9 @@
|
|
|
294
294
|
<pre><code id="hero-code">$ commit-guard
|
|
295
295
|
<span class="c-red">✗</span> <span class="c-dim">[subject]</span> subject does not match 'type(scope): description': WIP
|
|
296
296
|
<span class="c-red">✗</span> <span class="c-dim">[signed-off]</span> missing 'Signed-off-by' trailer — use 'git commit -s'
|
|
297
|
-
<span class="c-red">✗</span> <span class="c-dim">[signature]</span>
|
|
297
|
+
<span class="c-red">✗</span> <span class="c-dim">[signature]</span> signature could not be verified — commit may be
|
|
298
|
+
unsigned, or signed with a key not uploaded as a
|
|
299
|
+
Signing key on https://github.com/settings/keys</code></pre>
|
|
298
300
|
</div>
|
|
299
301
|
<div class="hero-dots">
|
|
300
302
|
<button class="hero-dot" data-i="0"></button>
|
|
@@ -464,8 +466,11 @@ require-subject-pattern = "[A-Z]+-[0-9]+"</code></pre>
|
|
|
464
466
|
<li>If neither of the above resolves a username, fall back to
|
|
465
467
|
searching GitHub by the commit author's email.</li>
|
|
466
468
|
<li>Fetch the resolved user's public keys from
|
|
467
|
-
<code>github.com/{username}.gpg</code> and
|
|
468
|
-
<code
|
|
469
|
+
<code>github.com/{username}.gpg</code> (GPG) and
|
|
470
|
+
<code>/users/{username}/ssh_signing_keys</code> (SSH keys tagged
|
|
471
|
+
with the <strong>Signing key</strong> role). Auth-only SSH keys
|
|
472
|
+
are deliberately not accepted — this mirrors GitHub's
|
|
473
|
+
“Verified” badge semantics.</li>
|
|
469
474
|
<li>Try GPG verification using a temporary keyring.</li>
|
|
470
475
|
<li>Try SSH verification using a temporary <code>allowed_signers</code> file.</li>
|
|
471
476
|
<li>Pass if any key verifies; fail if none do.</li>
|
|
@@ -474,7 +479,10 @@ require-subject-pattern = "[A-Z]+-[0-9]+"</code></pre>
|
|
|
474
479
|
If the author cannot be resolved via either method, or the GitHub API
|
|
475
480
|
is unreachable, the check fails with a clear error. For private
|
|
476
481
|
repositories, set <code>GITHUB_TOKEN</code> or <code>GH_TOKEN</code>
|
|
477
|
-
so the Commits API can authenticate.
|
|
482
|
+
so the Commits API can authenticate. The official GitHub Action wires
|
|
483
|
+
the workflow's automatic token via the <code>github-token</code>
|
|
484
|
+
input, so no manual <code>env:</code> is required; override with a
|
|
485
|
+
PAT only for cross-repo lookups. Disable the
|
|
478
486
|
<code>signature</code> check if GitHub API access is unavailable:
|
|
479
487
|
</p>
|
|
480
488
|
<pre><code class="language-bash">commit-guard --disable signature</code></pre>
|
|
@@ -558,13 +566,13 @@ require-subject-pattern = "[A-Z]+-[0-9]+"</code></pre>
|
|
|
558
566
|
- uses: actions/checkout@v4
|
|
559
567
|
with:
|
|
560
568
|
fetch-depth: 0
|
|
561
|
-
- uses: benner/commit-guard@v0.
|
|
569
|
+
- uses: benner/commit-guard@v0.22.0
|
|
562
570
|
with:
|
|
563
571
|
range: ${{ env.PR_BASE }}..${{ env.PR_HEAD }}
|
|
564
572
|
disable: signed-off,signature</code></pre>
|
|
565
573
|
|
|
566
574
|
<p>Check a specific commit SHA:</p>
|
|
567
|
-
<pre><code class="language-yaml"> - uses: benner/commit-guard@v0.
|
|
575
|
+
<pre><code class="language-yaml"> - uses: benner/commit-guard@v0.22.0
|
|
568
576
|
with:
|
|
569
577
|
rev: ${{ github.sha }}</code></pre>
|
|
570
578
|
|
|
@@ -583,7 +591,7 @@ require-subject-pattern = "[A-Z]+-[0-9]+"</code></pre>
|
|
|
583
591
|
When <code>output-file</code> is set the action exposes the path as
|
|
584
592
|
a step output, making JSONL results available to subsequent steps:
|
|
585
593
|
</p>
|
|
586
|
-
<pre><code class="language-yaml"> - uses: benner/commit-guard@v0.
|
|
594
|
+
<pre><code class="language-yaml"> - uses: benner/commit-guard@v0.22.0
|
|
587
595
|
id: cg
|
|
588
596
|
with:
|
|
589
597
|
range: ${{ env.PR_BASE }}..${{ env.PR_HEAD }}
|
|
@@ -596,7 +604,7 @@ require-subject-pattern = "[A-Z]+-[0-9]+"</code></pre>
|
|
|
596
604
|
<p>Add to <code>.pre-commit-config.yaml</code>:</p>
|
|
597
605
|
<pre><code class="language-yaml">repos:
|
|
598
606
|
- repo: https://github.com/benner/commit-guard
|
|
599
|
-
rev: v0.
|
|
607
|
+
rev: v0.22.0
|
|
600
608
|
hooks:
|
|
601
609
|
- id: commit-guard
|
|
602
610
|
- id: commit-guard-signature</code></pre>
|
|
@@ -618,7 +626,7 @@ require-subject-pattern = "[A-Z]+-[0-9]+"</code></pre>
|
|
|
618
626
|
["$ commit-guard\n ", null],
|
|
619
627
|
["✗", "c-red"], [" [subject]", "c-dim"], [" subject does not match 'type(scope): description': WIP\n ", null],
|
|
620
628
|
["✗", "c-red"], [" [signed-off]", "c-dim"], [" missing 'Signed-off-by' trailer — use 'git commit -s'\n ", null],
|
|
621
|
-
["✗", "c-red"], [" [signature]", "c-dim"], ["
|
|
629
|
+
["✗", "c-red"], [" [signature]", "c-dim"], [" signature could not be verified — commit may be\n unsigned, or signed with a key not uploaded as a\n Signing key on https://github.com/settings/keys", null],
|
|
622
630
|
],
|
|
623
631
|
[
|
|
624
632
|
["$ commit-guard --range origin/main..HEAD\n", null],
|
|
@@ -336,9 +336,21 @@ def _fetch_url(url):
|
|
|
336
336
|
return resp.read().decode()
|
|
337
337
|
|
|
338
338
|
|
|
339
|
+
def _fetch_github_signing_keys(username):
|
|
340
|
+
url = f"https://api.github.com/users/{username}/ssh_signing_keys"
|
|
341
|
+
headers = {"Accept": "application/vnd.github+json"}
|
|
342
|
+
token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
|
|
343
|
+
if token:
|
|
344
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
345
|
+
req = urllib.request.Request(url, headers=headers) # noqa: S310 Audit URL open for permitted schemes
|
|
346
|
+
with urllib.request.urlopen(req, timeout=_git_timeout()) as resp: # noqa: S310 Audit URL open for permitted schemes
|
|
347
|
+
data = json.loads(resp.read())
|
|
348
|
+
return "\n".join(item["key"] for item in data)
|
|
349
|
+
|
|
350
|
+
|
|
339
351
|
def _fetch_github_keys(username):
|
|
340
352
|
gpg = _fetch_url(f"https://github.com/{username}.gpg")
|
|
341
|
-
ssh =
|
|
353
|
+
ssh = _fetch_github_signing_keys(username)
|
|
342
354
|
return gpg.strip(), ssh.strip()
|
|
343
355
|
|
|
344
356
|
|
|
@@ -450,7 +462,12 @@ def check_signature(rev, result):
|
|
|
450
462
|
if _verify_ssh(rev, email, ssh_text):
|
|
451
463
|
result.info("signature type: SSH", check=Check.SIGNATURE)
|
|
452
464
|
return
|
|
453
|
-
result.error(
|
|
465
|
+
result.error(
|
|
466
|
+
"signature could not be verified — commit may be unsigned, "
|
|
467
|
+
"or signed with a key not uploaded as a Signing key on "
|
|
468
|
+
"https://github.com/settings/keys",
|
|
469
|
+
check=Check.SIGNATURE,
|
|
470
|
+
)
|
|
454
471
|
except subprocess.TimeoutExpired:
|
|
455
472
|
result.error(
|
|
456
473
|
"git operation timed out — cannot verify signature",
|
|
@@ -17,6 +17,7 @@ from git_commit_guard import (
|
|
|
17
17
|
_ensure_nltk_data,
|
|
18
18
|
_fetch_github_commit_author,
|
|
19
19
|
_fetch_github_keys,
|
|
20
|
+
_fetch_github_signing_keys,
|
|
20
21
|
_fetch_github_username,
|
|
21
22
|
_fetch_url,
|
|
22
23
|
_get_author_email,
|
|
@@ -735,10 +736,69 @@ class TestFetchGithubCommitAuthor:
|
|
|
735
736
|
assert captured[0].get_header("Authorization") == "Bearer ghtoken"
|
|
736
737
|
|
|
737
738
|
|
|
739
|
+
class TestFetchGithubSigningKeys:
|
|
740
|
+
def _mock_response(self, data):
|
|
741
|
+
mock_resp = MagicMock()
|
|
742
|
+
mock_resp.__enter__ = lambda s: s
|
|
743
|
+
mock_resp.__exit__ = MagicMock(return_value=False)
|
|
744
|
+
mock_resp.read.return_value = json.dumps(data).encode()
|
|
745
|
+
return mock_resp
|
|
746
|
+
|
|
747
|
+
def test_returns_keys_joined_by_newline(self):
|
|
748
|
+
resp = self._mock_response(
|
|
749
|
+
[{"key": "ssh-ed25519 AAAA"}, {"key": "ssh-rsa BBBB"}]
|
|
750
|
+
)
|
|
751
|
+
with patch("git_commit_guard.urllib.request.urlopen", return_value=resp):
|
|
752
|
+
assert (
|
|
753
|
+
_fetch_github_signing_keys("testuser")
|
|
754
|
+
== "ssh-ed25519 AAAA\nssh-rsa BBBB"
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
def test_empty_list_returns_empty_string(self):
|
|
758
|
+
resp = self._mock_response([])
|
|
759
|
+
with patch("git_commit_guard.urllib.request.urlopen", return_value=resp):
|
|
760
|
+
assert _fetch_github_signing_keys("testuser") == ""
|
|
761
|
+
|
|
762
|
+
def test_github_token_sent_in_header(self):
|
|
763
|
+
resp = self._mock_response([])
|
|
764
|
+
captured = []
|
|
765
|
+
|
|
766
|
+
def mock_urlopen(req, **_):
|
|
767
|
+
captured.append(req)
|
|
768
|
+
return resp
|
|
769
|
+
|
|
770
|
+
with (
|
|
771
|
+
patch("git_commit_guard.urllib.request.urlopen", side_effect=mock_urlopen),
|
|
772
|
+
patch.dict("os.environ", {"GITHUB_TOKEN": "mytoken"}, clear=False),
|
|
773
|
+
):
|
|
774
|
+
_fetch_github_signing_keys("testuser")
|
|
775
|
+
assert captured[0].get_header("Authorization") == "Bearer mytoken"
|
|
776
|
+
|
|
777
|
+
def test_gh_token_used_when_github_token_absent(self):
|
|
778
|
+
resp = self._mock_response([])
|
|
779
|
+
captured = []
|
|
780
|
+
|
|
781
|
+
def mock_urlopen(req, **_):
|
|
782
|
+
captured.append(req)
|
|
783
|
+
return resp
|
|
784
|
+
|
|
785
|
+
env = {k: v for k, v in os.environ.items() if k != "GITHUB_TOKEN"}
|
|
786
|
+
env["GH_TOKEN"] = "ghtoken" # noqa: S105 Possible hardcoded password assigned to: "GH_TOKEN"
|
|
787
|
+
with (
|
|
788
|
+
patch("git_commit_guard.urllib.request.urlopen", side_effect=mock_urlopen),
|
|
789
|
+
patch.dict("os.environ", env, clear=True),
|
|
790
|
+
):
|
|
791
|
+
_fetch_github_signing_keys("testuser")
|
|
792
|
+
assert captured[0].get_header("Authorization") == "Bearer ghtoken"
|
|
793
|
+
|
|
794
|
+
|
|
738
795
|
class TestFetchGithubKeys:
|
|
739
796
|
def test_returns_gpg_and_ssh(self):
|
|
740
|
-
with
|
|
741
|
-
"git_commit_guard._fetch_url",
|
|
797
|
+
with (
|
|
798
|
+
patch("git_commit_guard._fetch_url", return_value="GPG KEY\n"),
|
|
799
|
+
patch(
|
|
800
|
+
"git_commit_guard._fetch_github_signing_keys", return_value="SSH KEY\n"
|
|
801
|
+
),
|
|
742
802
|
):
|
|
743
803
|
gpg, ssh = _fetch_github_keys("testuser")
|
|
744
804
|
assert gpg == "GPG KEY"
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: Lint Markdown
|
|
3
|
-
on: # yamllint disable-line rule:truthy
|
|
4
|
-
pull_request:
|
|
5
|
-
permissions:
|
|
6
|
-
contents: read
|
|
7
|
-
jobs:
|
|
8
|
-
lint-md:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
permissions:
|
|
11
|
-
contents: read
|
|
12
|
-
pull-requests: write
|
|
13
|
-
steps:
|
|
14
|
-
- name: Checkout code
|
|
15
|
-
# yamllint disable-line rule:line-length
|
|
16
|
-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
17
|
-
with:
|
|
18
|
-
persist-credentials: false
|
|
19
|
-
- name: Lint Markdown files with reviewdog
|
|
20
|
-
# yamllint disable-line rule:line-length
|
|
21
|
-
uses: reviewdog/action-markdownlint@3667398db9118d7e78f7a63d10e26ce454ba5f58 # v0.26.2
|
|
22
|
-
with:
|
|
23
|
-
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
24
|
-
reporter: github-pr-review
|
|
25
|
-
level: info
|
|
26
|
-
fail_level: any
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|