<# Duenote Windows installer. Duenote ships unsigned, so a browser download is stamped with the Mark-of-the-Web (a Zone.Identifier NTFS alternate data stream). SmartScreen's application-reputation check keys off that mark, which is what produces the "Windows protected your PC" wall on an unsigned installer. Neither curl.exe nor Invoke-WebRequest writes Zone.Identifier, so a build fetched this way arrives unmarked and generally installs without that prompt. This is the same trust decision a user would otherwise make by clicking "More info -> Run anyway"; it is made explicitly here instead. Caveat: this removes the SmartScreen trigger, not every check. Microsoft Defender still scans the binary, and a freshly published unsigned executable with no download history can be flagged on reputation grounds regardless. Signing with an Authenticode certificate remains the real fix. Usage: irm https://download.duenote.app/install.ps1 | iex $env:DUENOTE_VERSION = '0.6.2'; irm https://download.duenote.app/install.ps1 | iex #> $ErrorActionPreference = 'Stop' # Windows PowerShell 5.1 still defaults to TLS 1.0 on older builds, which # Cloudflare refuses. try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { } function Write-Info { param($m) Write-Host "==> $m" -ForegroundColor Cyan } function Write-Warn { param($m) Write-Host "warning: $m" -ForegroundColor Yellow } $BaseUrl = if ($env:DUENOTE_BASE_URL) { $env:DUENOTE_BASE_URL.TrimEnd('/') } else { 'https://download.duenote.app' } if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { Write-Warn 'No native ARM64 build is published; installing the x64 build under emulation.' } # Same version marker the macOS and Linux installers read; written by the # release workflow once every platform has built. $Version = $env:DUENOTE_VERSION if (-not $Version) { Write-Info 'Resolving latest release...' try { $Version = (Invoke-RestMethod -Uri "$BaseUrl/latest.txt" -UseBasicParsing).ToString().Trim() } catch { throw "Could not resolve the latest version. Set `$env:DUENOTE_VERSION = 'x.y.z' and retry." } } # The version lands in a URL path, so refuse anything that is not a plain # version string rather than interpolating whatever the marker file held. if ($Version -notmatch '^[0-9]+(\.[0-9]+)*$') { throw "Unexpected version string: $Version" } if (Get-Process -Name 'Duenote' -ErrorAction SilentlyContinue) { throw 'Duenote is running. Quit it and re-run this installer.' } # maker-squirrel names the artifact "- Setup.exe" -- the # space is literal in the object key and has to be encoded in the URL. $FileName = "Duenote-$Version Setup.exe" $Url = "$BaseUrl/Duenote/win32/x64/" + [uri]::EscapeDataString($FileName) $TmpDir = Join-Path ([IO.Path]::GetTempPath()) ("duenote-" + [Guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null $Installer = Join-Path $TmpDir 'DuenoteSetup.exe' try { Write-Info "Downloading Duenote $Version (x64)..." # curl.exe ships with Windows 10 1803+ and is markedly faster than # Invoke-WebRequest for large files. Neither sets Zone.Identifier. $curl = Get-Command curl.exe -ErrorAction SilentlyContinue if ($curl) { & $curl.Source -fL --progress-bar $Url -o $Installer if ($LASTEXITCODE -ne 0) { throw "Download failed: $Url" } } else { $prev = $ProgressPreference $ProgressPreference = 'SilentlyContinue' # otherwise IWR crawls try { Invoke-WebRequest -Uri $Url -OutFile $Installer -UseBasicParsing } finally { $ProgressPreference = $prev } } if (-not (Test-Path $Installer) -or (Get-Item $Installer).Length -lt 1MB) { throw "Download looks truncated: $Url" } # Belt and braces: clear the mark if anything upstream applied one. Unblock-File -Path $Installer -ErrorAction SilentlyContinue $zone = Get-Item -Path $Installer -Stream 'Zone.Identifier' -ErrorAction SilentlyContinue if ($zone) { Write-Warn 'Mark-of-the-Web is still present; SmartScreen may prompt.' } else { Write-Info 'Downloaded clean -- no Mark-of-the-Web.' } Write-Info 'Running installer...' # Squirrel installs per-user into %LOCALAPPDATA%\Duenote (no admin needed) # and launches the app when it finishes. $proc = Start-Process -FilePath $Installer -PassThru -Wait if ($proc.ExitCode -ne 0) { throw "Installer exited with code $($proc.ExitCode)." } Write-Info 'Done. Duenote is installed in %LOCALAPPDATA%\Duenote.' } finally { Remove-Item -Recurse -Force $TmpDir -ErrorAction SilentlyContinue }