# The quickstart as a new user runs it on Windows, from the archive alone, timed step by step: # # pwsh -File wai/quickstart/test/clean-machine.ps1 # # The twin of clean-machine.sh, step for step. A machine with nothing of WAI on it: a fresh # profile (USERPROFILE, HOME, APPDATA, LOCALAPPDATA, TEMP and TMP) and working directory, a PATH # of the system's own directories, no cargo and no repository. The steps are the quickstart's: # 1. check the archive against its .sha256 beside it, and unpack it; # 2. the archive holds what `wai dev` and the quickstart name: bin/ with wai, wai-relay, # wai-room, wai-ingest, wai-remote-host, wai-pack and wai-quantum, each of which runs (the # last four here, the first three in the steps below), and share/wai/web/ and # share/wai/studio/ (Studio's page and its core); # 3. wai hello; 4. wai verify; 5. wai create my-room --template video-room; # 6. wai dev --check in the project: the relay, the room service, the page and WAI Studio at # /_wai/ on one origin, each checked through it. # It prints each step's time, the total, and a JSON line; it exits non-zero when a step fails, # naming it. Downloading the archive is not timed: the archive is given. It needs PowerShell 7 # and runs on any system it runs on (a .tar.gz archive elsewhere than Windows), so its steps can # be checked off Windows too. param([string]$Archive) $ErrorActionPreference = 'Stop' if (-not $Archive) { Get-Content $PSCommandPath | Select-Object -First 19 | ForEach-Object { $_ -replace '^# ?', '' } exit 2 } $Archive = (Resolve-Path $Archive).Path # The system's temporary directory, read before TEMP, TMP and TMPDIR point into the run's own. $SystemTmp = [IO.Path]::GetTempPath() $Root = Join-Path $SystemTmp ("wai-clean-" + [guid]::NewGuid().ToString('N')) $HomeDir = Join-Path $Root 'home' $Tmp = Join-Path $Root 'tmp' $Work = Join-Path $Root 'work' New-Item -ItemType Directory -Force -Path $HomeDir, $Tmp, $Work | Out-Null $env:HOME = $HomeDir $env:USERPROFILE = $HomeDir $env:APPDATA = Join-Path $HomeDir 'AppData\Roaming' $env:LOCALAPPDATA = Join-Path $HomeDir 'AppData\Local' $env:TEMP = $Tmp $env:TMP = $Tmp $env:TMPDIR = $Tmp New-Item -ItemType Directory -Force -Path $env:APPDATA, $env:LOCALAPPDATA | Out-Null $Sep = [IO.Path]::PathSeparator if ($IsWindows) { $System = @("$env:SystemRoot\System32", $env:SystemRoot, "$env:SystemRoot\System32\Wbem", "$env:SystemRoot\System32\WindowsPowerShell\v1.0") -join $Sep } else { $System = '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin' } $env:PATH = $System Set-Location $Work $T0 = [Diagnostics.Stopwatch]::StartNew() $Steps = [ordered]@{} $Failed = '' # Run one step, its output to .log: its time, or FAILED and the log's tail. function Step([string]$Name, [scriptblock]$Do) { $log = Join-Path $Root "$Name.log" $w = [Diagnostics.Stopwatch]::StartNew() $ok = $true try { $global:LASTEXITCODE = 0 & $Do *> $log if ($LASTEXITCODE -ne 0) { $ok = $false } } catch { $_ | Out-File -Append $log $ok = $false } if ($ok) { $script:Steps[$Name] = [math]::Round($w.Elapsed.TotalSeconds, 2).ToString([Globalization.CultureInfo]::InvariantCulture) } else { $script:Steps[$Name] = 'FAILED' $script:Failed = $Name Write-Output "FAIL ${Name}:" if (Test-Path $log) { Get-Content $log -Tail 15 } } } Step 'unpack' { $want = ((Get-Content "$Archive.sha256" -Raw).Trim() -split '\s+')[0].ToLower() $got = (Get-FileHash $Archive -Algorithm SHA256).Hash.ToLower() if ($got -ne $want) { throw "SHA-256 $got, not $want" } if ($Archive.EndsWith('.zip')) { Expand-Archive -Path $Archive -DestinationPath $Work } else { tar -xzf $Archive -C $Work } } $Bin = Get-ChildItem -Path $Work -Directory -Filter 'wai-*' | Select-Object -First 1 | ForEach-Object { Join-Path $_.FullName 'bin' } if ($Bin) { $env:PATH = "$Bin$Sep$env:PATH" } if (-not $Failed) { Step 'contents' { $exe = if ($IsWindows) { '.exe' } else { '' } $missing = @() foreach ($b in 'wai', 'wai-relay', 'wai-room', 'wai-ingest', 'wai-remote-host', 'wai-pack', 'wai-quantum') { if (-not (Test-Path -PathType Leaf (Join-Path $Bin "$b$exe"))) { $missing += "bin/$b$exe" } } foreach ($f in 'share/wai/web/room/index.html', 'share/wai/studio/index.html', 'share/wai/studio/pkg/wai_studio_core_bg.wasm') { if (-not (Test-Path -PathType Leaf (Join-Path (Split-Path $Bin -Parent) $f))) { $missing += $f } } if ($missing) { throw "the archive lacks: $($missing -join ' ')" } wai-quantum --version if ($LASTEXITCODE -ne 0) { throw "wai-quantum --version exits $LASTEXITCODE" } # wai-ingest and wai-pack alone say how they are run and exit 2; wai-remote-host, 1. foreach ($u in @{ n = 'wai-ingest'; c = 2 }, @{ n = 'wai-pack'; c = 2 }, @{ n = 'wai-remote-host'; c = 1 }) { $err = Join-Path $Root "$($u.n).err" $p = Start-Process -FilePath (Join-Path $Bin "$($u.n)$exe") -NoNewWindow -Wait -PassThru -RedirectStandardOutput (Join-Path $Root "$($u.n).out") -RedirectStandardError $err $said = Get-Content $err -Raw if ($p.ExitCode -ne $u.c -or $said -notmatch 'usage' -or $said -notmatch [regex]::Escape("$($u.n) ")) { throw "$($u.n) alone exits $($p.ExitCode), not $($u.c) with its usage" } } } } if (-not $Failed) { Step 'hello' { wai hello } } if (-not $Failed) { Step 'verify' { wai verify wai-hello } } if (-not $Failed) { Step 'create' { wai create my-room --template video-room } } # Every piece held, Studio among them: the archive's share/wai/studio, found beside wai. if (-not $Failed) { Step 'dev-check' { Push-Location my-room; try { wai dev --check --port 0 | Tee-Object -Variable out; if ($LASTEXITCODE -eq 0 -and -not ($out | Select-String -SimpleMatch -Quiet 'ok WAI Studio at /_wai/')) { throw 'wai dev --check did not hold WAI Studio at /_wai/' } } finally { Pop-Location } } } $Total = [math]::Round($T0.Elapsed.TotalSeconds, 2).ToString([Globalization.CultureInfo]::InvariantCulture) $Os = [Runtime.InteropServices.RuntimeInformation]::OSDescription $Arch = [Runtime.InteropServices.RuntimeInformation]::OSArchitecture Write-Output "the quickstart from $(Split-Path $Archive -Leaf) on $Os $Arch, a fresh profile and directory:" foreach ($k in $Steps.Keys) { if ($Steps[$k] -eq 'FAILED') { Write-Output (" {0,-10} FAILED" -f $k) } else { Write-Output (" {0,-10} {1} s" -f $k, $Steps[$k]) } } $devLog = Join-Path $Root 'dev-check.log' if (Test-Path $devLog) { Select-String -Path $devLog -Pattern 'checks held' | ForEach-Object { " $($_.Line)" } } Write-Output (" total $Total s" + $(if ($Failed) { " (stopped at $Failed)" } else { '' })) $stepsOut = [ordered]@{} foreach ($k in $Steps.Keys) { $stepsOut[$k] = $Steps[$k] } [ordered]@{ archive = (Split-Path $Archive -Leaf); total_s = [double]$Total; steps = $stepsOut; ok = (-not $Failed) } | ConvertTo-Json -Compress Set-Location $SystemTmp Remove-Item -Recurse -Force $Root -ErrorAction SilentlyContinue if ($Failed) { exit 1 } exit 0