返回 CodeWhale
update-user-path.ps1
根目录 / scripts / installer / update-user-path.ps1
1 [CmdletBinding()]
2 param(
3 [ValidateSet('Add', 'Remove')]
4 [string] $Operation,
5
6 [string] $Entry
7 )
8
9 Set-StrictMode -Version Latest
10 $ErrorActionPreference = 'Stop'
11
12 function Get-NormalizedPathEntry {
13 param(
14 [AllowNull()]
15 [AllowEmptyString()]
16 [string] $Value
17 )
18
19 if ($null -eq $Value) {
20 return ''
21 }
22
23 $normalized = $Value.Trim()
24 if (
25 $normalized.Length -ge 2 -and
26 $normalized[0] -eq '"' -and
27 $normalized[$normalized.Length - 1] -eq '"'
28 ) {
29 $normalized = $normalized.Substring(1, $normalized.Length - 2).Trim()
30 }
31
32 return $normalized.TrimEnd([char[]] @('\', '/'))
33 }
34
35 function Test-PathEntryEqual {
36 param(
37 [AllowNull()]
38 [AllowEmptyString()]
39 [string] $Left,
40
41 [AllowNull()]
42 [AllowEmptyString()]
43 [string] $Right
44 )
45
46 $normalizedLeft = Get-NormalizedPathEntry -Value $Left
47 $normalizedRight = Get-NormalizedPathEntry -Value $Right
48
49 if ($normalizedLeft.Length -eq 0 -or $normalizedRight.Length -eq 0) {
50 return $false
51 }
52
53 return [string]::Equals(
54 $normalizedLeft,
55 $normalizedRight,
56 [System.StringComparison]::OrdinalIgnoreCase
57 )
58 }
59
60 function Get-UpdatedUserPath {
61 param(
62 [AllowNull()]
63 [AllowEmptyString()]
64 [string] $Current,
65
66 [Parameter(Mandatory = $true)]
67 [ValidateSet('Add', 'Remove')]
68 [string] $RequestedOperation,
69
70 [Parameter(Mandatory = $true)]
71 [string] $RequestedEntry
72 )
73
74 if ([string]::IsNullOrWhiteSpace((Get-NormalizedPathEntry -Value $RequestedEntry))) {
75 throw 'The PATH entry must not be empty.'
76 }
77
78 if ($null -eq $Current) {
79 $Current = ''
80 }
81
82 $parts = $Current.Split([char[]] @(';'), [System.StringSplitOptions]::None)
83 $containsEntry = $false
84 foreach ($part in $parts) {
85 if (Test-PathEntryEqual -Left $part -Right $RequestedEntry) {
86 $containsEntry = $true
87 break
88 }
89 }
90
91 if ($RequestedOperation -eq 'Add') {
92 if ($containsEntry) {
93 return $Current
94 }
95 if ($Current.Length -eq 0 -or $Current.EndsWith(';')) {
96 return "$Current$RequestedEntry"
97 }
98 return "$Current;$RequestedEntry"
99 }
100
101 if (-not $containsEntry) {
102 return $Current
103 }
104
105 $remaining = New-Object 'System.Collections.Generic.List[string]'
106 foreach ($part in $parts) {
107 if (-not (Test-PathEntryEqual -Left $part -Right $RequestedEntry)) {
108 [void] $remaining.Add($part)
109 }
110 }
111
112 return [string]::Join(';', $remaining)
113 }
114
115 function Set-RegistryPathEntry {
116 param(
117 [Parameter(Mandatory = $true)]
118 [Microsoft.Win32.RegistryKey] $RegistryKey,
119
120 [Parameter(Mandatory = $true)]
121 [ValidateSet('Add', 'Remove')]
122 [string] $RequestedOperation,
123
124 [Parameter(Mandatory = $true)]
125 [string] $RequestedEntry
126 )
127
128 $pathExists = @($RegistryKey.GetValueNames()) -contains 'Path'
129 if ($pathExists) {
130 $valueKind = $RegistryKey.GetValueKind('Path')
131 if (
132 $valueKind -ne [Microsoft.Win32.RegistryValueKind]::String -and
133 $valueKind -ne [Microsoft.Win32.RegistryValueKind]::ExpandString
134 ) {
135 throw "The user PATH has unsupported registry type '$valueKind'."
136 }
137
138 $current = [string] $RegistryKey.GetValue(
139 'Path',
140 $null,
141 [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
142 )
143 if ($null -eq $current) {
144 $current = ''
145 }
146 }
147 else {
148 $current = ''
149 $valueKind = [Microsoft.Win32.RegistryValueKind]::ExpandString
150 }
151
152 $updated = Get-UpdatedUserPath `
153 -Current $current `
154 -RequestedOperation $RequestedOperation `
155 -RequestedEntry $RequestedEntry
156
157 if ([string]::Equals($current, $updated, [System.StringComparison]::Ordinal)) {
158 return $false
159 }
160
161 $RegistryKey.SetValue('Path', $updated, $valueKind)
162 return $true
163 }
164
165 function Set-CodeWhaleUserPath {
166 param(
167 [Parameter(Mandatory = $true)]
168 [ValidateSet('Add', 'Remove')]
169 [string] $RequestedOperation,
170
171 [Parameter(Mandatory = $true)]
172 [string] $RequestedEntry
173 )
174
175 if ([System.Environment]::OSVersion.Platform -ne [System.PlatformID]::Win32NT) {
176 throw 'The CodeWhale user PATH helper only supports Windows.'
177 }
178
179 $environmentKey = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Environment')
180 if ($null -eq $environmentKey) {
181 throw 'Could not open or create the current-user Environment registry key.'
182 }
183
184 try {
185 return Set-RegistryPathEntry `
186 -RegistryKey $environmentKey `
187 -RequestedOperation $RequestedOperation `
188 -RequestedEntry $RequestedEntry
189 }
190 finally {
191 $environmentKey.Close()
192 }
193 }
194
195 if ($MyInvocation.InvocationName -ne '.') {
196 if ([string]::IsNullOrWhiteSpace($Operation)) {
197 throw 'Operation is required.'
198 }
199 if ([string]::IsNullOrWhiteSpace($Entry)) {
200 throw 'Entry is required.'
201 }
202
203 $changed = Set-CodeWhaleUserPath `
204 -RequestedOperation $Operation `
205 -RequestedEntry $Entry
206
207 if ($changed) {
208 Write-Host "User PATH updated: $Operation '$Entry'."
209 }
210 else {
211 Write-Host "User PATH already satisfied: $Operation '$Entry'."
212 }
213 }
214
214 lines Plain Text