Skip to content

Commit d7fe40d

Browse files
authored
Update harper.yml
1 parent 6db8c1e commit d7fe40d

1 file changed

Lines changed: 48 additions & 144 deletions

File tree

.github/workflows/harper.yml

Lines changed: 48 additions & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ on:
88
type: boolean
99
default: false
1010

11+
env:
12+
ALLOW_FAILURE: ${{ github.event_name == 'workflow_dispatch' && inputs.allow_failure }}
13+
1114
jobs:
1215
spelling:
1316
runs-on: ubuntu-latest
@@ -19,21 +22,37 @@ jobs:
1922
- uses: actions/cache@v4
2023
with:
2124
path: ~/.npm
22-
key: ${{ runner.os }}-npm-cspell-${{ hashFiles('package-lock.json') }}
25+
key: ${{ runner.os }}-npm-cspell
2326
restore-keys: ${{ runner.os }}-npm-cspell-
24-
- run: npm ci
2527
- run: |
26-
npx cspell lint \
28+
npx --yes cspell@9 lint \
2729
"docs/**/*.{md,mdx}" \
2830
"src/pages/**/*.{md,mdx}" \
2931
--config cspell.json \
3032
--no-progress \
3133
--reporter json \
3234
> cspell-report.json || true
33-
- uses: actions/upload-artifact@v4
34-
with:
35-
name: cspell-report
36-
path: cspell-report.json
35+
- run: |
36+
python - <<'PY'
37+
import json
38+
import os
39+
import sys
40+
41+
with open("cspell-report.json", encoding="utf-8") as f:
42+
data = json.load(f)
43+
44+
issues = data.get("issues", [])
45+
for issue in issues:
46+
file = issue.get("uri")
47+
line = issue.get("row")
48+
message = issue.get("message") or f"Unknown word: {issue.get('text')!r}"
49+
print(f"::error file={file},line={line}::{message}")
50+
51+
print(f"{len(issues)} spelling issue(s) found.")
52+
53+
allow_failure = os.environ.get("ALLOW_FAILURE") == "true"
54+
sys.exit(1 if issues and not allow_failure else 0)
55+
PY
3756
3857
grammar:
3958
runs-on: ubuntu-latest
@@ -50,162 +69,47 @@ jobs:
5069
- run: |
5170
command -v harper-cli >/dev/null 2>&1 || \
5271
cargo install --git https://github.com/Automattic/harper \
53-
--rev <PIN_THIS_TO_A_COMMIT_SHA> \
72+
--tag v2.1.0 \
5473
harper-cli --locked
5574
- run: |
5675
find docs src/pages -type f \( -name "*.md" -o -name "*.mdx" \) -print0 | \
5776
xargs -0 harper-cli lint --format json > harper-report.json || true
58-
- uses: actions/upload-artifact@v4
59-
with:
60-
name: harper-report
61-
path: harper-report.json
62-
63-
heading-case:
64-
runs-on: ubuntu-latest
65-
steps:
66-
- uses: actions/checkout@v6
67-
- id: check
68-
continue-on-error: true
69-
run: python check_heading_case.py docs src/pages
70-
- run: echo "${{ steps.check.outcome }}" > heading-case-result.txt
71-
- uses: actions/upload-artifact@v4
72-
with:
73-
name: heading-case-result
74-
path: heading-case-result.txt
75-
76-
aggregate:
77-
needs: [spelling, grammar, heading-case]
78-
runs-on: ubuntu-latest
79-
steps:
80-
- uses: actions/download-artifact@v4
81-
- env:
82-
ALLOW_FAILURE: ${{ github.event_name == 'workflow_dispatch' && inputs.allow_failure }}
83-
run: |
77+
- run: |
8478
python - <<'PY'
8579
import json
8680
import os
8781
import sys
8882
89-
def load_json(path):
90-
try:
91-
with open(path, encoding="utf-8") as f:
92-
return json.load(f)
93-
except FileNotFoundError:
94-
return None
83+
with open("harper-report.json", encoding="utf-8") as f:
84+
reports = json.load(f)
9585
86+
ALLOWED_KINDS = {"Grammar", "Repetition"}
9687
issues = []
9788
98-
cspell_data = load_json("cspell-report/cspell-report.json")
99-
for issue in (cspell_data or {}).get("issues", []):
100-
issues.append(
101-
f"[Spelling] {issue.get('uri')}:{issue.get('row')}: "
102-
f"{issue.get('text')!r} - {issue.get('message', 'unknown word')}"
103-
)
104-
105-
ALLOWED_KINDS = {"Grammar", "Repetition"}
106-
for report in load_json("harper-report/harper-report.json") or []:
89+
for report in reports:
10790
file = report.get("file")
10891
for lint in report.get("lints", []):
109-
kind = lint.get("kind")
110-
if kind not in ALLOWED_KINDS:
92+
if lint.get("kind") not in ALLOWED_KINDS:
11193
continue
112-
issues.append(
113-
f"[{kind}::{lint.get('rule')}] {file}:{lint.get('line')}: "
114-
f"{lint.get('matched_text')!r} - {lint.get('message')}"
115-
)
116-
117-
with open("heading-case-result/heading-case-result.txt", encoding="utf-8") as f:
118-
if f.read().strip() == "failure":
119-
issues.append("[HeadingCase] Title case violations found, see heading-case job log.")
94+
line = lint.get("line")
95+
message = lint.get("message")
96+
print(f"::error file={file},line={line}::{message}")
97+
issues.append(lint)
12098
121-
for item in issues:
122-
print(item)
123-
print(f"\n{len(issues)} issue(s) found.")
99+
print(f"{len(issues)} grammar/repetition issue(s) found.")
124100
125101
allow_failure = os.environ.get("ALLOW_FAILURE") == "true"
126102
sys.exit(1 if issues and not allow_failure else 0)
127-
PY print("Suggested dictionary candidates:")
128-
for word in sorted(words, key=str.lower):
129-
print(word)
130103
PY
131104
132-
- name: Check Selected Harper Rules
133-
run: |
134-
python - <<'PY'
135-
import json
136-
import re
137-
import sys
138-
139-
with open("harper-report.json", encoding="utf-8") as f:
140-
reports = json.load(f)
141-
142-
selected = []
143-
total_lints = 0
144-
145-
IGNORED_RULES = {
146-
("Spelling", "OkToOkay"),
147-
("Spelling", "DisjointPrefixes"),
105+
heading-case:
106+
runs-on: ubuntu-latest
107+
steps:
108+
- uses: actions/checkout@v6
109+
- run: |
110+
python check_heading_case.py docs src/pages || {
111+
if [ "$ALLOW_FAILURE" = "true" ]; then
112+
exit 0
113+
fi
114+
exit 1
148115
}
149-
150-
def should_ignore_spelling_text(text):
151-
if not text:
152-
return False
153-
154-
stripped = text.strip()
155-
156-
# URLs and URL fragments
157-
if stripped.startswith(("http://", "https://", "www.")):
158-
return True
159-
160-
# File paths / command paths
161-
if "/" in stripped:
162-
return True
163-
164-
# Long hashes, IDs, tokens
165-
if len(stripped) >= 16 and any(c.isdigit() for c in stripped):
166-
return True
167-
168-
# Hex-like strings, e.g. d5442a5dc4baadd48b32
169-
if len(stripped) >= 8 and re.fullmatch(r"[a-fA-F0-9]+", stripped):
170-
return True
171-
172-
# Mostly random-looking alphanumeric tokens
173-
if len(stripped) >= 12 and re.fullmatch(r"[A-Za-z0-9_-]+", stripped):
174-
return True
175-
176-
return False
177-
178-
for report in reports:
179-
file = report.get("file")
180-
181-
for lint in report.get("lints", []):
182-
total_lints += 1
183-
184-
kind = lint.get("kind")
185-
rule = lint.get("rule")
186-
line_no = lint.get("line")
187-
message = lint.get("message")
188-
text = lint.get("matched_text")
189-
190-
if (kind, rule) in IGNORED_RULES:
191-
continue
192-
193-
if kind == "Spelling" and should_ignore_spelling_text(text):
194-
continue
195-
196-
if kind in {"Spelling", "Grammar", "Repetition"}:
197-
selected.append(
198-
f"{file}:{line_no}: [{kind}::{rule}] {text!r} - {message}"
199-
)
200-
201-
print(f"Total Harper lint count: {total_lints}")
202-
print(f"Selected lint count: {len(selected)}")
203-
204-
if selected:
205-
print("Selected Harper lints found:")
206-
for item in selected:
207-
print(item)
208-
sys.exit(1)
209-
210-
print("No selected Harper lints found.")
211-
PY

0 commit comments

Comments
 (0)