derive-gitlab.ps1
7.25 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
#!/usr/bin/env pwsh
# derive-gitlab.ps1 — Windows 对应版(PowerShell 5.1+ 原生可跑,无外部依赖)
# 与同目录 derive-gitlab.sh 严格等价 — 改动任一份必须同步对方。
#
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File derive-gitlab.ps1 [.env.local 路径]
#
# 派生字段、退出码、回填策略 见 derive-gitlab.sh 头部注释。
[CmdletBinding()]
param([string]$EnvFile = '.env.local')
# 让 Chinese 输出在 cmd / PowerShell 控制台正常显示
try { [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 } catch {}
if (-not (Test-Path -LiteralPath $EnvFile -PathType Leaf)) {
[Console]::Error.WriteLine("derive-gitlab.ps1: env file not found: $EnvFile")
exit 2
}
# ---- 取 origin URL ----
$url = ''
try {
$raw = & git remote get-url origin 2>$null
if ($LASTEXITCODE -eq 0 -and $raw) { $url = ($raw -join '').Trim() }
} catch { $url = '' }
if ([string]::IsNullOrEmpty($url)) {
[Console]::Error.WriteLine("derive-gitlab.ps1: 未配置 origin 远程,GITLAB_* 留给用户手填")
exit 0
}
# ---- 仅支持 http(s) ----
$scheme = $null
if ($url.StartsWith('https://')) { $scheme = 'https' }
elseif ($url.StartsWith('http://')) { $scheme = 'http' }
else {
[Console]::Error.WriteLine("derive-gitlab.ps1: origin 不是 http(s) URL ($url),跳过派生")
exit 0
}
$rest = $url.Substring("${scheme}://".Length)
$slashIdx = $rest.IndexOf('/')
if ($slashIdx -lt 0) {
[Console]::Error.WriteLine("derive-gitlab.ps1: origin URL 缺少路径段 ($url),跳过派生")
exit 0
}
$gitlabHost = $rest.Substring(0, $slashIdx)
$pathRaw = $rest.Substring($slashIdx + 1)
if ($pathRaw.EndsWith('.git')) { $pathRaw = $pathRaw.Substring(0, $pathRaw.Length - 4) }
$repoName = Split-Path -Leaf $pathRaw
$apiUrl = "${scheme}://${gitlabHost}/api/v3"
# ---- 读 .env.local 全文,按原行尾切分 ----
$rawBytes = [System.IO.File]::ReadAllBytes($EnvFile)
$hasBom = ($rawBytes.Length -ge 3 -and $rawBytes[0] -eq 0xEF -and $rawBytes[1] -eq 0xBB -and $rawBytes[2] -eq 0xBF)
$rawText = if ($hasBom) {
[System.Text.Encoding]::UTF8.GetString($rawBytes, 3, $rawBytes.Length - 3)
} else {
[System.Text.Encoding]::UTF8.GetString($rawBytes)
}
$eol = if ($rawText -match "`r`n") { "`r`n" } else { "`n" }
$hadTrailingNewline = $rawText.EndsWith("`n")
$lines = [System.Collections.Generic.List[string]]::new()
foreach ($l in ($rawText -split "`r?`n")) { $lines.Add($l) | Out-Null }
if ($hadTrailingNewline -and $lines.Count -gt 0 -and $lines[$lines.Count - 1] -eq '') {
$lines.RemoveAt($lines.Count - 1)
}
function Get-FieldStripped {
param([string]$Key)
for ($i = 0; $i -lt $script:lines.Count; $i++) {
$ln = $script:lines[$i]
if ($ln.StartsWith("$Key=")) {
$val = $ln.Substring($Key.Length + 1)
# 剥外层单/双引号
if ($val.Length -ge 2) {
$f = $val[0]; $l = $val[$val.Length - 1]
if (($f -eq "'" -and $l -eq "'") -or ($f -eq '"' -and $l -eq '"')) {
$val = $val.Substring(1, $val.Length - 2)
}
}
return @{ Found = $true; Index = $i; Stripped = $val }
}
}
return @{ Found = $false }
}
function Update-Field {
param([string]$Key, [string]$NewVal)
$info = Get-FieldStripped -Key $Key
if (-not $info.Found) {
[Console]::Error.WriteLine(" $Key = (.env.local 中无此行,跳过)")
return
}
$stripped = $info.Stripped
if ([string]::IsNullOrEmpty($stripped) -or $stripped -eq 'TBD(A5 自动补)') {
$script:lines[$info.Index] = "$Key=$NewVal"
$script:modified = $true
Write-Host " $Key = $NewVal [已派生填入]"
} elseif ($stripped -eq $NewVal) {
Write-Host " $Key = $NewVal [已是派生值,无需更新]"
} else {
Write-Host " $Key = $stripped [保留用户手填,未覆盖派生值 $NewVal]"
}
}
function Report-Token {
$info = Get-FieldStripped -Key 'GITLAB_TOKEN'
if (-not $info.Found) {
[Console]::Error.WriteLine(" GITLAB_TOKEN = (.env.local 中无此行,跳过)")
return
}
$stripped = $info.Stripped
if ([string]::IsNullOrEmpty($stripped) -or $stripped.StartsWith('【人工填写:') -or $stripped.StartsWith('TBD')) {
Write-Host " GITLAB_TOKEN = $stripped [待人工填写:GitLab Profile → Account → Private token]"
} else {
$masked = if ($stripped.Length -le 8) {
('*' * $stripped.Length)
} else {
$stripped.Substring(0, 4) + ('*' * ($stripped.Length - 8)) + $stripped.Substring($stripped.Length - 4)
}
Write-Host " GITLAB_TOKEN = $masked [已填入,长度 $($stripped.Length)]"
}
}
$script:modified = $false
Write-Host "derive-gitlab.ps1: 从 origin ($url) 派生 GitLab 凭据:"
Update-Field -Key 'GITLAB_API_URL' -NewVal $apiUrl
# ---- GITLAB_PROJECT_ID:经 GitLab API 解析数字 ID ----
$tokenInfo = Get-FieldStripped -Key 'GITLAB_TOKEN'
$tokenVal = if ($tokenInfo.Found) { $tokenInfo.Stripped } else { '' }
$tokenUsable = $true
if ([string]::IsNullOrEmpty($tokenVal) -or $tokenVal.StartsWith('【人工填写:') -or $tokenVal.StartsWith('TBD')) {
Write-Host " GITLAB_PROJECT_ID = TBD [token 未填,跳过 API 解析;填完 token 后重跑此脚本]"
$tokenUsable = $false
}
if ($tokenUsable) {
# PS 5.1 默认 TLS1.0/1.1,自建 GitLab 通常需要 TLS1.2
try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch {}
$headers = @{ 'PRIVATE-TOKEN' = $tokenVal }
$userOk = $false
try {
$userResp = Invoke-WebRequest -Uri "$apiUrl/user" -Headers $headers -UseBasicParsing -ErrorAction Stop
if ([int]$userResp.StatusCode -eq 200) { $userOk = $true }
else { Write-Host " GITLAB_PROJECT_ID = TBD [token 验证失败 HTTP $([int]$userResp.StatusCode),留 TBD 待人工确认]" }
} catch {
$code = 0
if ($_.Exception.Response) { try { $code = [int]$_.Exception.Response.StatusCode } catch {} }
if ($code -eq 0) { $code = '000' }
Write-Host " GITLAB_PROJECT_ID = TBD [token 验证失败 HTTP $code,留 TBD 待人工确认]"
}
if ($userOk) {
try {
$searchUri = "$apiUrl/projects?search=$([uri]::EscapeDataString($repoName))&simple=true&per_page=50"
$projects = Invoke-RestMethod -Uri $searchUri -Headers $headers -UseBasicParsing -ErrorAction Stop
$match = $projects | Where-Object { $_.path_with_namespace -eq $pathRaw } | Select-Object -First 1
if ($match) {
Update-Field -Key 'GITLAB_PROJECT_ID' -NewVal "$($match.id)"
} else {
Write-Host " GITLAB_PROJECT_ID = TBD [API 未匹配 path_with_namespace=$pathRaw,请到 GitLab 项目设置页查数字 ID 后填入]"
}
} catch {
Write-Host " GITLAB_PROJECT_ID = TBD [API 调用失败:$($_.Exception.Message)]"
}
}
}
Report-Token
# ---- 回写 .env.local(保持 EOL / BOM 状态)----
if ($script:modified) {
$newText = [string]::Join($eol, $lines.ToArray())
if ($hadTrailingNewline) { $newText += $eol }
$encoding = New-Object System.Text.UTF8Encoding($hasBom)
[System.IO.File]::WriteAllText($EnvFile, $newText, $encoding)
}