  smoke-installer-windows-x86_64:
    name: smoke the published installer (windows-x86_64)
    needs: publish
    if: startsWith(github.ref, 'refs/tags/')
    # A plain runner, deliberately: no SQLCipher from any package manager,
    # no setup-raku, nothing this repository put there. Whatever the
    # bundle needs, the bundle has to carry -- and this, together with its
    # macOS and Linux siblings above, is the only coverage that tests that
    # claim against the artefact a user will actually download.
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v6

      - name: Download the published windows-x86_64 bundle
        env:
          GH_TOKEN: {% raw %}${{ github.token }}{% endraw %}
        run: |
          $ErrorActionPreference = 'Stop'
          New-Item -ItemType Directory -Path published -Force | Out-Null
          # ariza packages every platform's bundle as .tar.gz, Windows
          # included -- there is no `.zip` lane to fall back to -- so this
          # is the archive and its digest sidecar, the same pattern the
          # macOS and Linux jobs use above.
          gh release download $env:GITHUB_REF_NAME --repo $env:GITHUB_REPOSITORY --pattern '{{ app_exec }}-*-windows-x86_64.*' --dir published
          if ($LASTEXITCODE -ne 0) { throw "gh release download failed (exit $LASTEXITCODE)" }
          Get-ChildItem published

      - name: Install it with this repository's own install.ps1
        run: |
          $ErrorActionPreference = 'Stop'
          # The committed script, not a raw.githubusercontent URL: this has
          # to work for the first release, before that URL serves anything,
          # and it is the script this tag actually ships.
          $archives = @(Get-ChildItem published\*.tar.gz)
          if ($archives.Count -ne 1) {
              throw "expected exactly one .tar.gz in published/, found $($archives.Count)"
          }
          pwsh -File .\install.ps1 -Url $archives[0].FullName
          if ($LASTEXITCODE -ne 0) { throw "install.ps1 failed (exit $LASTEXITCODE)" }

      - name: Run the installed launcher with a minimal environment
        run: |
          $ErrorActionPreference = 'Stop'
          $bin = Join-Path $env:LOCALAPPDATA '{{ app_display }}\current\bin'
          $exe = Join-Path $bin '{{ app_exec }}.cmd'
          if (-not (Test-Path -LiteralPath $exe)) {
              throw "install.ps1 did not leave $exe behind"
          }

          # PowerShell has no `env -i`: a fresh process is built by hand
          # instead, with its environment cleared and PATH trimmed to the
          # system directory plus the install's own bin, so the bundle
          # cannot pass by borrowing a toolchain this runner happens to
          # have lying around. `pwsh` is resolved before the environment
          # is touched, because the child cannot find it once PATH is gone.
          $pwsh = (Get-Command pwsh).Source
          $psi = [System.Diagnostics.ProcessStartInfo]::new()
          $psi.FileName = $pwsh
          $psi.Arguments = "-NoProfile -NonInteractive -Command & '$exe' --version"
          $psi.UseShellExecute = $false
          $psi.RedirectStandardOutput = $true
          $psi.RedirectStandardError = $true
          $psi.EnvironmentVariables.Clear()
          $psi.EnvironmentVariables['PATH'] = "$env:SystemRoot\System32;$bin"
          $psi.EnvironmentVariables['USERPROFILE'] = $env:USERPROFILE
          $psi.EnvironmentVariables['LOCALAPPDATA'] = $env:LOCALAPPDATA
          $psi.EnvironmentVariables['TEMP'] = $env:TEMP
          # Windows has mandatory ambient environment that `env -i` on
          # POSIX has no analogue for, and stripping it does not make the
          # test cleaner — it makes the OS itself malfunction. Launching
          # any .cmd resolves the interpreter through ComSpec, so without
          # it the batch launcher dies with ERROR_MOD_NOT_FOUND ("The
          # specified module could not be found") before the launcher
          # runs a line — which is exactly how the first published
          # Windows installer smoke failed against a perfectly good
          # bundle. SystemRoot and windir are how cmd and half of Win32
          # find the OS; PATHEXT is how `moneymoor` resolves to .exe/.cmd
          # at all; SystemDrive is cheap insurance of the same kind.
          # These five belong to Windows, not to the runner's toolchain,
          # so keeping them does not weaken the isolation the cleared
          # environment exists to provide.
          $psi.EnvironmentVariables['SystemRoot'] = $env:SystemRoot
          $psi.EnvironmentVariables['windir'] = $env:windir
          $psi.EnvironmentVariables['ComSpec'] = $env:ComSpec
          $psi.EnvironmentVariables['PATHEXT'] = $env:PATHEXT
          $psi.EnvironmentVariables['SystemDrive'] = $env:SystemDrive

          $proc = [System.Diagnostics.Process]::Start($psi)
          $out = $proc.StandardOutput.ReadToEnd()
          $err = $proc.StandardError.ReadToEnd()
          $proc.WaitForExit()
          Write-Host $out
          if ($err) { Write-Host $err }
          if ($proc.ExitCode -ne 0) {
              throw "$exe --version exited $($proc.ExitCode)"
          }

          # The same launch through the compiled runner, when the bundle
          # ships one. The .cmd above proves the script path; this proves
          # the entry point PATHEXT actually hands a user who types the
          # bare command — and unlike the .cmd it involves no cmd.exe and
          # no ComSpec, so the two smokes fail independently.
          $native = Join-Path $bin '{{ app_exec }}.exe'
          if (Test-Path -LiteralPath $native) {
              $psi.Arguments = "-NoProfile -NonInteractive -Command & '$native' --version"
              $proc = [System.Diagnostics.Process]::Start($psi)
              $out = $proc.StandardOutput.ReadToEnd()
              $err = $proc.StandardError.ReadToEnd()
              $proc.WaitForExit()
              Write-Host $out
              if ($err) { Write-Host $err }
              if ($proc.ExitCode -ne 0) {
                  throw "$native --version exited $($proc.ExitCode)"
              }
          }
