2828 With -Uninstall, deletes the install directory including the test key.
2929 The next install then generates a new key, and therefore a new ID.
3030
31+ . PARAMETER ForceReinstall
32+ Deletes the copy Edge already installed so that the policy installs the
33+ current build from scratch on the next start. The extension ID is unchanged.
34+ Edge's own update check is slow and throttled, so this is the reliable way
35+ to pick up a rebuild.
36+
37+ Edge is terminated first, because it would otherwise rewrite the files being
38+ removed. Any unsaved work in the browser is lost.
39+
3140. PARAMETER InstallRoot
3241 Where the packed crx, the manifests, the test config and the test key are
3342 kept. Defaults to .testinstall in the repository root.
4756param (
4857 [switch ]$Uninstall ,
4958 [switch ]$Purge ,
59+ [switch ]$ForceReinstall ,
5060 [string ]$InstallRoot
5161)
5262
@@ -133,6 +143,35 @@ function Get-ProductionExtensionId {
133143 return $null
134144}
135145
146+ # Edge only re-downloads a force installed extension when the update manifest
147+ # advertises a newer version, so every run gets a version derived from the clock.
148+ # Components have to stay below 65536, hence days and minutes rather than a
149+ # full timestamp.
150+ function Get-TestVersion ([string ]$BaseVersion ) {
151+ $parts = $BaseVersion.Split (' .' )
152+ $major = $parts [0 ]
153+ $minor = if ($parts.Length -gt 1 ) { $parts [1 ] } else { ' 0' }
154+ $now = Get-Date
155+ $days = [int ]($now.Date - [datetime ]' 2020-01-01' ).TotalDays
156+ $minutes = $now.Hour * 60 + $now.Minute
157+ return " $major .$minor .$days .$minutes "
158+ }
159+
160+ # Rewrites just the version member, leaving the rest of the file untouched.
161+ function Set-ManifestVersion ([string ]$ManifestPath , [string ]$Version ) {
162+ $text = [System.IO.File ]::ReadAllText($ManifestPath )
163+ $updated = [regex ]::Replace($text , ' ("version"\s*:\s*")[^"]*(")' , " `$ {1}$Version `$ {2}" )
164+ [System.IO.File ]::WriteAllText($ManifestPath , $updated , (New-Object System.Text.UTF8Encoding($false )))
165+ }
166+
167+ function Get-ManifestVersion ([string ]$ManifestPath ) {
168+ $match = [regex ]::Match([System.IO.File ]::ReadAllText($ManifestPath ), ' "version"\s*:\s*"([^"]*)"' )
169+ if (-not $match.Success ) {
170+ throw " No version found in $ManifestPath "
171+ }
172+ return $match.Groups [1 ].Value
173+ }
174+
136175function Invoke-EdgePack ([string ]$Directory , [string ]$KeyPath ) {
137176 $edge = Get-EdgePath
138177 $produced = " $Directory .crx"
@@ -163,6 +202,59 @@ function Invoke-EdgePack([string]$Directory, [string]$KeyPath) {
163202 return $produced
164203}
165204
205+ function Stop-Edge {
206+ $processes = Get-Process - Name ' msedge' - ErrorAction SilentlyContinue
207+ if (-not $processes ) {
208+ Write-Host ' Edge is not running'
209+ return
210+ }
211+ if (-not $PSCmdlet.ShouldProcess (' msedge' , " Stop $ ( $processes.Count ) process(es)" )) {
212+ return
213+ }
214+
215+ Write-Host " stopping $ ( $processes.Count ) Edge process(es)"
216+ $processes | Stop-Process - Force - ErrorAction SilentlyContinue - WhatIf:$false
217+
218+ $stopwatch = [System.Diagnostics.Stopwatch ]::StartNew()
219+ while (Get-Process - Name ' msedge' - ErrorAction SilentlyContinue) {
220+ if ($stopwatch.Elapsed.TotalSeconds -gt 30 ) {
221+ throw ' Edge is still running after 30 seconds.'
222+ }
223+ Start-Sleep - Milliseconds 250
224+ }
225+ # Give Windows a moment to release the profile files.
226+ Start-Sleep - Milliseconds 500
227+ }
228+
229+ # Edge only replaces a force installed extension when its own update check runs,
230+ # which it delays and throttles. Dropping the installed copy makes the policy
231+ # install the current crx from scratch on the next start, keeping the same ID.
232+ function Remove-InstalledExtension ([string ]$ExtensionId ) {
233+ # Edge would rewrite the files being removed, so it is stopped first.
234+ Stop-Edge
235+
236+ $userData = Join-Path $env: LOCALAPPDATA ' Microsoft\Edge\User Data'
237+ if (-not (Test-Path $userData )) {
238+ Write-Warning " No Edge user data found at $userData "
239+ return
240+ }
241+
242+ $removed = 0
243+ Get-ChildItem $userData - Directory - ErrorAction SilentlyContinue | ForEach-Object {
244+ $installed = Join-Path $_.FullName " Extensions\$ExtensionId "
245+ if (Test-Path $installed ) {
246+ if ($PSCmdlet.ShouldProcess ($installed , ' Delete the installed extension' )) {
247+ Remove-Item - LiteralPath $installed - Recurse - Force - WhatIf:$false
248+ Write-Host " removed from profile $ ( $_.Name ) "
249+ $removed ++
250+ }
251+ }
252+ }
253+ if ($removed -eq 0 ) {
254+ Write-Host ' nothing installed yet'
255+ }
256+ }
257+
166258function Get-ForcelistValueName ([string ]$ExtensionId ) {
167259 if (-not (Test-Path $ForcelistKey )) {
168260 return $null
@@ -275,12 +367,15 @@ if ($Uninstall) {
275367
276368Assert-Elevated
277369
370+ # Always rebuild: reusing a stale edge\dev would silently package the previous
371+ # version of the extension.
372+ Write-Step ' Building the extension'
373+ & (Join-Path $WebExtRoot ' build.bat' ) all
374+ if ($LASTEXITCODE -ne 0 ) {
375+ throw ' Building the extension failed.'
376+ }
278377if (-not (Test-Path $SourceDir )) {
279- Write-Step ' edge\dev is missing, building the extension first'
280- & (Join-Path $WebExtRoot ' build.bat' ) all
281- if ($LASTEXITCODE -ne 0 ) {
282- throw ' Building the extension failed.'
283- }
378+ throw " The extension was not staged: $SourceDir "
284379}
285380
286381New-Item - ItemType Directory - Path $InstallRoot - Force - WhatIf:$false | Out-Null
@@ -306,11 +401,16 @@ if (Test-Path $StageDir) {
306401 Remove-Item - LiteralPath $StageDir - Recurse - Force - WhatIf:$false
307402}
308403Copy-Item $SourceDir - Destination $StageDir - Recurse - Force - WhatIf:$false
404+
405+ $stagedManifest = Join-Path $StageDir ' manifest.json'
406+ $version = Get-TestVersion (Get-ManifestVersion $stagedManifest )
407+ Set-ManifestVersion $stagedManifest $version
408+ Write-Host " version $version "
409+
309410$null = Invoke-EdgePack $StageDir $TestKey
310411Write-Host " $CrxPath "
311412
312413Write-Step ' Writing the update manifest'
313- $version = (Get-Content (Join-Path $SourceDir ' manifest.json' ) - Raw | ConvertFrom-Json ).version
314414$xml = @"
315415<?xml version='1.0' encoding='UTF-8'?>
316416<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
@@ -360,6 +460,11 @@ Write-Host " $TestConfig"
360460
361461# --- registry ---------------------------------------------------------------
362462
463+ if ($ForceReinstall ) {
464+ Write-Step ' Stopping Edge and dropping the copy it installed'
465+ Remove-InstalledExtension $extensionId
466+ }
467+
363468Write-Step ' Registering the ExtensionInstallForcelist policy'
364469$entry = " $extensionId ;$ ( ConvertTo-FileUrl $UpdateXml ) "
365470$valueName = Get-ForcelistValueName $extensionId
@@ -401,9 +506,24 @@ if ($PSCmdlet.ShouldProcess("$OwnKey\Configfile", "Set to $TestConfig")) {
401506# --- done -------------------------------------------------------------------
402507
403508Write-Host ' '
404- Write-Host ' Done. Restart Edge, then check:' - ForegroundColor Green
405- Write-Host " edge://extensions - the extension should be listed as $extensionId "
406- Write-Host ' edge://policy - ExtensionInstallForcelist should show the entry'
509+ Write-Host " Done. Packaged version $version " - ForegroundColor Green
510+ Write-Host ' '
511+ if ($ForceReinstall ) {
512+ Write-Host ' Edge was stopped and its copy removed. Start Edge to install this build.'
513+ Write-Host ' '
514+ }
515+ else {
516+ Write-Host ' Edge keeps the copy it already installed until its own update check'
517+ Write-Host ' runs, which is delayed and throttled. To pick up this build reliably,'
518+ Write-Host ' run (this terminates Edge):'
519+ Write-Host ' .\tools\install-test-extension.ps1 -ForceReinstall'
520+ Write-Host ' '
521+ }
522+ Write-Host ' Then confirm on edge://extensions that the version reads'
523+ Write-Host " $version "
524+ Write-Host ' If it still reads the old version, the running code is the old build.'
525+ Write-Host ' '
526+ Write-Host " edge://policy - ExtensionInstallForcelist should list $extensionId "
407527Write-Host ' '
408528Write-Host ' The native messaging host now runs from the Debug build:'
409529Write-Host " $HostExe "
0 commit comments