Skip to content

Commit c411cfe

Browse files
author
JSONbored
committed
fix(ci): normalize committed CRLF to LF and name the failure for what it is
Twenty files were committed with CRLF and the repo had no .gitattributes. The `changes` job runs `git diff --check`, and git's default core.whitespace is `blank-at-eol` with `cr-at-eol` off, so a carriage return at end-of-line counts as trailing whitespace. Any PR adding a line to one of those files failed CI pointing at lines whose visible content is spotless, with nothing in the log saying "CRLF" — it hit #9787 (run 30436384202) on two lines of inline-suggestion-anchor.ts. Convert all twenty to LF via `git add --renormalize`, byte-identical otherwise (`git diff -w` reports only the two new files below), and pin the repo with `* text=auto eol=lf`. `text=auto` rather than a bare `text` is load-bearing: five .ts files embed NUL bytes as untrusted-text and sanitizer fixtures, and auto-detection classifies them binary and passes them through untouched. The whitespace step now names CRLF before falling through to the generic check. .gitattributes stops CRLF entering through `git add`, but checkin filters don't run on blobs written directly (web editor, Contents API) or on a merge of a branch cut earlier, so the diagnostic still has a job. It is scoped to added lines, so it fires on exactly what `git diff --check` already flagged — a relabel, not a new failure mode. Closes #9798
1 parent edbdb58 commit c411cfe

22 files changed

Lines changed: 2077 additions & 2027 deletions

.gitattributes

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Line endings are LF, in the repository and in every working tree.
2+
#
3+
# Without this file, a contributor on a CRLF platform (or an editor that preserved a file's existing
4+
# CRLF convention) could commit CRLF, and the `changes` CI job's `git diff --check` would then report
5+
# every added line as "trailing whitespace" -- git's default core.whitespace is `blank-at-eol` with
6+
# `cr-at-eol` OFF, so a carriage return at end-of-line IS trailing whitespace to it. The failure names
7+
# lines whose visible content is spotless, which is undiagnosable from the CI log alone (#9798).
8+
#
9+
# `text=auto` (not a bare `text`) is deliberate: it leaves git's binary detection in charge, so files
10+
# that embed NUL bytes on purpose -- the untrusted-text and sanitizer fixtures under src/, test/,
11+
# scripts/ and review-enrichment/ -- are classified binary and pass through byte-for-byte. Forcing
12+
# `* text eol=lf` would strip them of that protection and corrupt those fixtures.
13+
* text=auto eol=lf
14+
15+
# Belt-and-braces for formats where a line-ending rewrite would corrupt the file outright. `text=auto`
16+
# above already spares them via NUL detection; these entries mean a format that happens to start with
17+
# printable bytes never depends on that heuristic.
18+
*.png binary
19+
*.ico binary
20+
*.jpg binary
21+
*.jpeg binary
22+
*.gif binary
23+
*.webp binary
24+
*.woff binary
25+
*.woff2 binary
26+
*.pdf binary
27+
*.zip binary
28+
*.gz binary

.github/workflows/ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,28 @@ jobs:
8383
exit 0
8484
fi
8585
git fetch --depth=1 origin "$BASE_SHA"
86+
# `git diff --check` reports a carriage return at end-of-line as "trailing whitespace":
87+
# git's default core.whitespace is `blank-at-eol` with `cr-at-eol` OFF, so a CR really is
88+
# trailing whitespace to it. The result names lines whose visible content is spotless, and
89+
# nothing in the log says "CRLF" -- undiagnosable without cloning the branch and hexdumping
90+
# the file (#9798). Name the real cause first, then fall through to the generic check for
91+
# everything else. .gitattributes (`* text=auto eol=lf`) normalizes CRLF away on `git add`, so
92+
# this can no longer be reached by an ordinary commit -- but checkin filters don't run on blobs
93+
# written directly (the GitHub web editor, the Contents API) or on a merge of a branch cut
94+
# before .gitattributes landed, and those still carry CRLF straight into the diff. Scoped to
95+
# ADDED lines so it fires on exactly what `git diff --check` would have flagged anyway -- this
96+
# relabels the failure, it does not fail anything new.
97+
crlf=$(git diff "$BASE_SHA" HEAD | awk '
98+
/^\+\+\+ / { f = $2; sub(/^b\//, "", f); next }
99+
/^\+/ { if ($0 ~ /\r$/) n[f]++ }
100+
END { for (k in n) printf " %s (%d added line(s) ending in CRLF)\n", k, n[k] }
101+
')
102+
if [ -n "$crlf" ]; then
103+
echo "::error::CRLF line endings in added lines -- this repo is LF-only (see .gitattributes)"
104+
printf '%s\n' "$crlf"
105+
echo "Fix: git add --renormalize <file> && git commit --amend"
106+
exit 1
107+
fi
86108
git diff --check "$BASE_SHA" HEAD
87109
- name: Filter changed paths
88110
id: filter

packages/loopover-engine/src/signals/issue-quality-report.ts

Lines changed: 313 additions & 313 deletions
Large diffs are not rendered by default.
Lines changed: 104 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,104 @@
1-
// Shared binary-file extension inventory for analyzers that classify opaque blobs by path extension.
2-
// asset-weight.ts (size bloat) and provenance.ts (unauditable committed artifacts) must stay in parity —
3-
// a single source list prevents one analyzer from drifting and missing formats the other already flags.
4-
5-
/** Lowercase extensions for genuinely binary assets. Text formats like .svg/.json are excluded. */
6-
export const BINARY_FILE_EXTENSIONS = [
7-
// Images
8-
"png",
9-
"jpg",
10-
"jpeg",
11-
"gif",
12-
"bmp",
13-
"tiff",
14-
"tif",
15-
"ico",
16-
"webp",
17-
"avif",
18-
"heic",
19-
"heif",
20-
// Fonts
21-
"woff",
22-
"woff2",
23-
"ttf",
24-
"otf",
25-
"eot",
26-
// Media
27-
"mp4",
28-
"mov",
29-
"avi",
30-
"webm",
31-
"mkv",
32-
"mp3",
33-
"wav",
34-
"flac",
35-
"ogg",
36-
// Archives / compression
37-
"zip",
38-
"tar",
39-
"gz",
40-
"tgz",
41-
"bz2",
42-
"7z",
43-
"rar",
44-
"xz",
45-
"zst",
46-
"lz4",
47-
"br",
48-
// Documents / design
49-
"pdf",
50-
"psd",
51-
"ai",
52-
"sketch",
53-
"fig",
54-
"xcf",
55-
// Native / compiled
56-
"exe",
57-
"dll",
58-
"so",
59-
"dylib",
60-
"bin",
61-
"dat",
62-
"wasm",
63-
"node",
64-
"jar",
65-
"class",
66-
"pyc",
67-
"pyo",
68-
"pyd",
69-
"o",
70-
"a",
71-
"war",
72-
"ear",
73-
// ML checkpoints
74-
"safetensors",
75-
"gguf",
76-
"onnx",
77-
"pt",
78-
"pth",
79-
"ckpt",
80-
// Scientific / ML data artifacts
81-
"h5",
82-
"hdf5",
83-
"pb",
84-
"npy",
85-
"npz",
86-
"parquet",
87-
"feather",
88-
"arrow",
89-
"orc",
90-
"msgpack",
91-
] as const;
92-
93-
const BINARY_EXT_SET = new Set<string>(BINARY_FILE_EXTENSIONS);
94-
95-
/** True when `ext` (without a leading dot) is a known binary file extension. Case-insensitive. Pure. */
96-
export function isBinaryFileExtension(ext: string): boolean {
97-
return BINARY_EXT_SET.has(ext.toLowerCase());
98-
}
99-
100-
/** Extension-anchored, case-insensitive regex matching any shared binary extension at path end. Pure. */
101-
export const BINARY_EXT_RE = new RegExp(
102-
`\\.(?:${BINARY_FILE_EXTENSIONS.join("|")})$`,
103-
"i",
104-
);
1+
// Shared binary-file extension inventory for analyzers that classify opaque blobs by path extension.
2+
// asset-weight.ts (size bloat) and provenance.ts (unauditable committed artifacts) must stay in parity —
3+
// a single source list prevents one analyzer from drifting and missing formats the other already flags.
4+
5+
/** Lowercase extensions for genuinely binary assets. Text formats like .svg/.json are excluded. */
6+
export const BINARY_FILE_EXTENSIONS = [
7+
// Images
8+
"png",
9+
"jpg",
10+
"jpeg",
11+
"gif",
12+
"bmp",
13+
"tiff",
14+
"tif",
15+
"ico",
16+
"webp",
17+
"avif",
18+
"heic",
19+
"heif",
20+
// Fonts
21+
"woff",
22+
"woff2",
23+
"ttf",
24+
"otf",
25+
"eot",
26+
// Media
27+
"mp4",
28+
"mov",
29+
"avi",
30+
"webm",
31+
"mkv",
32+
"mp3",
33+
"wav",
34+
"flac",
35+
"ogg",
36+
// Archives / compression
37+
"zip",
38+
"tar",
39+
"gz",
40+
"tgz",
41+
"bz2",
42+
"7z",
43+
"rar",
44+
"xz",
45+
"zst",
46+
"lz4",
47+
"br",
48+
// Documents / design
49+
"pdf",
50+
"psd",
51+
"ai",
52+
"sketch",
53+
"fig",
54+
"xcf",
55+
// Native / compiled
56+
"exe",
57+
"dll",
58+
"so",
59+
"dylib",
60+
"bin",
61+
"dat",
62+
"wasm",
63+
"node",
64+
"jar",
65+
"class",
66+
"pyc",
67+
"pyo",
68+
"pyd",
69+
"o",
70+
"a",
71+
"war",
72+
"ear",
73+
// ML checkpoints
74+
"safetensors",
75+
"gguf",
76+
"onnx",
77+
"pt",
78+
"pth",
79+
"ckpt",
80+
// Scientific / ML data artifacts
81+
"h5",
82+
"hdf5",
83+
"pb",
84+
"npy",
85+
"npz",
86+
"parquet",
87+
"feather",
88+
"arrow",
89+
"orc",
90+
"msgpack",
91+
] as const;
92+
93+
const BINARY_EXT_SET = new Set<string>(BINARY_FILE_EXTENSIONS);
94+
95+
/** True when `ext` (without a leading dot) is a known binary file extension. Case-insensitive. Pure. */
96+
export function isBinaryFileExtension(ext: string): boolean {
97+
return BINARY_EXT_SET.has(ext.toLowerCase());
98+
}
99+
100+
/** Extension-anchored, case-insensitive regex matching any shared binary extension at path end. Pure. */
101+
export const BINARY_EXT_RE = new RegExp(
102+
`\\.(?:${BINARY_FILE_EXTENSIONS.join("|")})$`,
103+
"i",
104+
);

0 commit comments

Comments
 (0)