From 8c05ad67e1aebbe44ac715fdba41881db8bba7cb Mon Sep 17 00:00:00 2001 From: Nate Meyer <672246+notnmeyer@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:39:05 -0700 Subject: [PATCH 1/4] powershell script --- install.ps1 | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 install.ps1 diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 0000000..33db6c0 --- /dev/null +++ b/install.ps1 @@ -0,0 +1,88 @@ +# install.ps1 +param( + [string]$Tag = "latest", + [string]$InstallDir = "$env:LOCALAPPDATA\Programs\loops" +) + +$ErrorActionPreference = "Stop" + +$GHRepo = "loops-so/cli" +$GHAssetsUrl = "https://github.com/$GHRepo/releases/download" +$ProjName = "loops_cli" +$BinName = "loops.exe" + +$Arch = switch ($env:PROCESSOR_ARCHITECTURE) { + "AMD64" { "amd64" } + "ARM64" { "arm64" } + "x86" { "386" } + default { throw "Unsupported architecture: $env:PROCESSOR_ARCHITECTURE" } +} + +$AuthHeader = @{} +if ($env:GITHUB_TOKEN) { + $AuthHeader["Authorization"] = "Bearer $env:GITHUB_TOKEN" +} + +function Get-GithubRelease { + param([string]$Repo, [string]$Version) + $url = if ($Version -eq "latest") { + "https://api.github.com/repos/$Repo/releases/latest" + } else { + "https://api.github.com/repos/$Repo/releases/tags/$Version" + } + $response = Invoke-RestMethod -Uri $url -Headers $AuthHeader + return $response.tag_name +} + +function Confirm-Checksum { + param([string]$FilePath, [string]$ChecksumsPath) + $filename = Split-Path $FilePath -Leaf + $line = Get-Content $ChecksumsPath | Where-Object { $_ -match [regex]::Escape($filename) } + if (-not $line) { + throw "Could not find checksum for $filename" + } + $want = ($line -split '\s+')[0].ToLower() + $got = (Get-FileHash -Algorithm SHA256 -Path $FilePath).Hash.ToLower() + if ($want -ne $got) { + throw "Checksum mismatch for $filename`: expected $want, got $got" + } +} + +$release = Get-GithubRelease -Repo $GHRepo -Version $Tag +$versionNoV = $release -replace '^v', '' +$archiveName = "${ProjName}_windows_${Arch}.zip" +$checksumsName = "${ProjName}_${versionNoV}_checksums.txt" +$downloadUrl = "$GHAssetsUrl/$release/$archiveName" +$checksumsUrl = "$GHAssetsUrl/$release/$checksumsName" + +Write-Host "Installing $ProjName $release for windows/$Arch..." + +$tmpDir = Join-Path $env:TEMP ([System.IO.Path]::GetRandomFileName()) +New-Item -ItemType Directory -Path $tmpDir | Out-Null + +try { + Invoke-WebRequest -Uri $downloadUrl -OutFile "$tmpDir\$archiveName" -Headers $AuthHeader + Invoke-WebRequest -Uri $checksumsUrl -OutFile "$tmpDir\$checksumsName" -Headers $AuthHeader + + Confirm-Checksum -FilePath "$tmpDir\$archiveName" -ChecksumsPath "$tmpDir\$checksumsName" + + Expand-Archive -Path "$tmpDir\$archiveName" -DestinationPath $tmpDir -Force + + if (-not (Test-Path $InstallDir)) { + New-Item -ItemType Directory -Path $InstallDir | Out-Null + } + + Copy-Item "$tmpDir\$BinName" "$InstallDir\$BinName" -Force + + $userPath = [System.Environment]::GetEnvironmentVariable("Path", "User") + if ($userPath -notlike "*$InstallDir*") { + [System.Environment]::SetEnvironmentVariable("Path", "$userPath;$InstallDir", "User") + $env:PATH = "$env:PATH;$InstallDir" + Write-Host "Added $InstallDir to your PATH" + } +} finally { + Remove-Item -Recurse -Force $tmpDir -ErrorAction SilentlyContinue +} + +Write-Host "Done!" +Write-Host "Installed to $InstallDir\$BinName" From 09cb6d22ed7e614a155e63549fb62ebe087b9692 Mon Sep 17 00:00:00 2001 From: Nate Meyer <672246+notnmeyer@users.noreply.github.com> Date: Thu, 2 Apr 2026 09:27:12 -0700 Subject: [PATCH 2/4] readme --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ab01e4f..ef38056 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Run `loops --help` to see available commands, or `loops [command] --help` for de brew install loops-so/tap/loops ``` -### Script +### Script for macOS, Linux, Windows via WSL ```bash curl -fsSL --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/loops-so/cli/main/install.sh | bash @@ -29,6 +29,12 @@ curl -fsSL --proto '=https' --tlsv1.2 https://raw.githubusercontent.com/loops-so To install a specific version or to a custom path, append `-s -- ` to `bash` in the command above. The default installation path is `~/.local/bin`. +### Script for Windows PowerShell + +``` +irm https://raw.githubusercontent.com/Loops-so/cli/main/install.ps1 | iex +``` + ## Auth The CLI requires a Loops API key. Get one from [Settings > API](https://app.loops.so/settings?page=api). From 17c4c0095672a3b9ca5d0e77165eb1cbaac06c79 Mon Sep 17 00:00:00 2001 From: Nate Meyer <672246+notnmeyer@users.noreply.github.com> Date: Thu, 2 Apr 2026 09:36:48 -0700 Subject: [PATCH 3/4] also test the pswh script --- .github/workflows/test-install.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 517815c..9750666 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -27,3 +27,19 @@ jobs: else "$INSTALL_DIR/loops" version fi + + test-ps1: + name: install ps1 (windows) + runs-on: windows-latest + steps: + - uses: actions/checkout@v6 + - shell: pwsh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + $InstallDir = "$env:TEMP\loops-test" + .\install.ps1 -InstallDir $InstallDir + - shell: pwsh + run: | + $InstallDir = "$env:TEMP\loops-test" + & "$InstallDir\loops.exe" version From fe57085f5397d2c163d051136376bfb2610c48e6 Mon Sep 17 00:00:00 2001 From: Nate Meyer <672246+notnmeyer@users.noreply.github.com> Date: Thu, 2 Apr 2026 09:41:59 -0700 Subject: [PATCH 4/4] map the architectures to our artifact names --- install.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.ps1 b/install.ps1 index 33db6c0..6da384a 100644 --- a/install.ps1 +++ b/install.ps1 @@ -12,9 +12,9 @@ $ProjName = "loops_cli" $BinName = "loops.exe" $Arch = switch ($env:PROCESSOR_ARCHITECTURE) { - "AMD64" { "amd64" } + "AMD64" { "x86_64" } "ARM64" { "arm64" } - "x86" { "386" } + "x86" { "i386" } default { throw "Unsupported architecture: $env:PROCESSOR_ARCHITECTURE" } }