-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidate.ps1
More file actions
205 lines (168 loc) · 6.13 KB
/
Copy pathvalidate.ps1
File metadata and controls
205 lines (168 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
<#
.SYNOPSIS
Validates repository text hygiene (EOL + encoding) on Windows runners.
.DESCRIPTION
This script is intended to run in CI (GitHub Actions) and fail fast when:
- A tracked file violates the expected line-ending declared by `.gitattributes`.
- Any tracked `.cmd` file is not decodable as GBK (code page 936), or contains a BOM.
- Basic `cmd.exe` invocation fails (sanity check for runner/tooling issues).
Notes:
- Markdown is expected to be LF; batch/text helpers are expected to be CRLF.
- The GBK requirement applies to `.cmd` only: those bytes are echoed to the
Windows console under `chcp 936`, so they must be GBK to render Chinese.
- `.txt` files are opened in an editor (Notepad), not the console, so they are
NOT forced to GBK. `the usage .txt` ships as UTF-8 with BOM, which modern
Notepad detects reliably across locales/Windows versions.
#>
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..')
# Ensure legacy code pages (e.g., 936/GBK) are available on .NET Core/PowerShell 7
[System.Text.Encoding]::RegisterProvider([System.Text.CodePagesEncodingProvider]::Instance)
function Add-Failure {
param(
[string]$Message,
[string]$File = ''
)
$script:Failures += [PSCustomObject]@{
Message = $Message
File = $File
}
}
function Emit-Failures {
if (-not $script:Failures) {
return
}
foreach ($failure in $script:Failures) {
if ($failure.File) {
Write-Host "::error file=$($failure.File)::$($failure.Message)"
}
else {
Write-Host "::error::$($failure.Message)"
}
}
throw "Validation failed."
}
function Get-EolExpectation {
param(
[string]$Path
)
$attr = git -C $repoRoot -c core.quotePath=false check-attr eol -- $Path 2>$null
if (-not $attr) {
return $null
}
if ($attr -match ':\s*eol:\s*(\S+)\s*$') {
$value = $matches[1]
if ($value -eq 'unspecified') {
return $null
}
return $value
}
return $null
}
function Validate-LineEndings {
param(
[string]$Path,
[string]$Expected
)
$bytes = [System.IO.File]::ReadAllBytes($Path)
if (-not $bytes.Length) {
return
}
switch ($Expected.ToLower()) {
'crlf' {
for ($i = 0; $i -lt $bytes.Length; $i++) {
if ($bytes[$i] -eq 0x0A) {
if ($i -eq 0 -or $bytes[$i - 1] -ne 0x0D) {
Add-Failure -File $Path -Message 'Expected CRLF line endings per .gitattributes but found LF.'
return
}
}
if ($bytes[$i] -eq 0x0D -and ($i -eq $bytes.Length - 1 -or $bytes[$i + 1] -ne 0x0A)) {
Add-Failure -File $Path -Message 'Found CR without following LF; normalize to CRLF per .gitattributes.'
return
}
}
}
'lf' {
if ($bytes -contains 0x0D) {
Add-Failure -File $Path -Message 'Expected LF line endings per .gitattributes but found CR characters.'
}
}
}
}
function Validate-Encoding {
param(
[string]$Path
)
$bytes = [System.IO.File]::ReadAllBytes($Path)
if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) {
Add-Failure -File $Path -Message 'Detected UTF-8 BOM. Save the file as GBK/ASCII without BOM.'
return
}
if ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFF -and $bytes[1] -eq 0xFE) {
Add-Failure -File $Path -Message 'Detected UTF-16 LE BOM. Save the file as GBK/ASCII.'
return
}
if ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFE -and $bytes[1] -eq 0xFF) {
Add-Failure -File $Path -Message 'Detected UTF-16 BE BOM. Save the file as GBK/ASCII.'
return
}
$gbk = [System.Text.Encoding]::GetEncoding(936, [System.Text.EncoderFallback]::ExceptionFallback, [System.Text.DecoderFallback]::ExceptionFallback)
try {
$null = $gbk.GetString($bytes)
}
catch {
Add-Failure -File $Path -Message 'Content is not valid GBK/ASCII. Convert the file to GBK (code page 936).'
return
}
$hasNonAscii = $bytes | Where-Object { $_ -gt 0x7F } | ForEach-Object { $true } | Select-Object -First 1
if ($hasNonAscii) {
$utf8Strict = New-Object System.Text.UTF8Encoding($false, $true)
$isUtf8 = $true
try {
$null = $utf8Strict.GetString($bytes)
}
catch {
$isUtf8 = $false
}
if ($isUtf8) {
Add-Failure -File $Path -Message 'File looks UTF-8 encoded. Keep batch/text helpers in GBK (code page 936).'
}
}
}
function Probe-CmdSyntax {
# Keep this probe minimal and robust across runner images.
# Avoid commands that may produce locale-dependent output or huge help text.
$probe = @(
'echo probe-cmd',
'setlocal EnableExtensions & ver >nul'
)
foreach ($command in $probe) {
$process = Start-Process -FilePath 'cmd.exe' -ArgumentList "/c $command" -NoNewWindow -PassThru -Wait
if ($process.ExitCode -ne 0) {
Add-Failure -Message "cmd syntax probe failed: $command (exit $($process.ExitCode))"
}
}
}
$script:Failures = @()
$trackedFiles = (git -C $repoRoot -c core.quotePath=false ls-files -z) -split "`0" | Where-Object { $_ }
foreach ($file in $trackedFiles) {
$expectedEol = Get-EolExpectation -Path $file
if ($expectedEol) {
Validate-LineEndings -Path $file -Expected $expectedEol
}
}
# Only `.cmd` files are echoed to the Windows console (under `chcp 936`), so
# only they are required to be GBK without BOM. `.txt` docs are opened in an
# editor and may be UTF-8 (e.g. the usage .txt ships as UTF-8 BOM for Notepad).
$cmdFiles = (git -C $repoRoot -c core.quotePath=false ls-files -z -- '*.cmd') -split "`0" | Where-Object { $_ }
foreach ($file in $cmdFiles) {
Validate-Encoding -Path $file
}
Probe-CmdSyntax
if (-not $Failures) {
Write-Host 'All checks passed.'
exit 0
}
Emit-Failures