feat: add curl | bash installer for Linux/macOS without Homebrew
Adds a GET /install.sh endpoint on the backend (embedded via Go's embed package, so the script stays a real, shellcheck-able .sh file rather than a Go string literal). Auto-detects OS/arch (including Termux on Android via $PREFIX) and always resolves the latest GitHub release via the /releases/latest/download/ redirect, so unlike the winget/brew/AUR manifests it needs no per-release maintenance. Verified end-to-end in an ephemeral Ubuntu container (both the normal /usr/local/bin path and the Termux $PREFIX path), and locally against a running backend instance. Documented alongside the existing winget/Homebrew instructions in README.md, the release notes template, the web CLI page, and the per-product CliHandoff card. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
151f70d67f
commit
5eec0351e7
7 changed files with 99 additions and 0 deletions
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -1,2 +1,3 @@
|
|||
aur/msdl-bin/PKGBUILD text eol=lf
|
||||
aur/msdl-bin/.SRCINFO text eol=lf
|
||||
backend/install.sh text eol=lf
|
||||
|
|
|
|||
6
.github/workflows/cli-release.yml
vendored
6
.github/workflows/cli-release.yml
vendored
|
|
@ -73,6 +73,12 @@ jobs:
|
|||
brew install msdl
|
||||
```
|
||||
|
||||
**macOS/Linux (no Homebrew):**
|
||||
```bash
|
||||
curl -fsSL https://api.msdl.tech-latest.com/install.sh | bash
|
||||
```
|
||||
Auto-detects OS/arch (including Termux on Android) and installs the latest release.
|
||||
|
||||
**Linux/macOS (direct download):**
|
||||
```bash
|
||||
# Linux x86_64
|
||||
|
|
|
|||
|
|
@ -69,6 +69,12 @@ brew tap starkSV/msdl
|
|||
brew install msdl
|
||||
```
|
||||
|
||||
**macOS / Linux (no Homebrew):**
|
||||
```bash
|
||||
curl -fsSL https://api.msdl.tech-latest.com/install.sh | bash
|
||||
```
|
||||
Auto-detects OS/arch (including Termux on Android) and installs the latest release.
|
||||
|
||||
**Direct download:** Grab the latest binary from [GitHub Releases](https://github.com/starkSV/windows-iso-downloader/releases/latest) and rename it:
|
||||
|
||||
| Platform | File | Rename to |
|
||||
|
|
|
|||
48
backend/install.sh
Normal file
48
backend/install.sh
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env bash
|
||||
# Installs the msdl CLI. Usage: curl -fsSL https://api.msdl.tech-latest.com/install.sh | bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO="starkSV/windows-iso-downloader"
|
||||
|
||||
os="$(uname -s)"
|
||||
arch="$(uname -m)"
|
||||
|
||||
case "$os" in
|
||||
Linux) platform="linux" ;;
|
||||
Darwin) platform="darwin" ;;
|
||||
*)
|
||||
echo "msdl: unsupported OS: $os (Windows users: winget install starkSV.msdl)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$arch" in
|
||||
x86_64|amd64) goarch="amd64" ;;
|
||||
aarch64|arm64) goarch="arm64" ;;
|
||||
*)
|
||||
echo "msdl: unsupported architecture: $arch" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
asset="msdl-${platform}-${goarch}"
|
||||
url="https://github.com/${REPO}/releases/latest/download/${asset}"
|
||||
|
||||
tmp="$(mktemp)"
|
||||
echo "Downloading ${asset}..."
|
||||
curl -fsSL "$url" -o "$tmp"
|
||||
chmod +x "$tmp"
|
||||
|
||||
if [ -n "${PREFIX:-}" ] && [ -d "${PREFIX}/bin" ]; then
|
||||
# Termux (or similar $PREFIX-based environment) -- no sudo available or needed
|
||||
mv "$tmp" "${PREFIX}/bin/msdl"
|
||||
echo "Installed to ${PREFIX}/bin/msdl"
|
||||
elif [ -w /usr/local/bin ]; then
|
||||
mv "$tmp" /usr/local/bin/msdl
|
||||
echo "Installed to /usr/local/bin/msdl"
|
||||
else
|
||||
sudo mv "$tmp" /usr/local/bin/msdl
|
||||
echo "Installed to /usr/local/bin/msdl"
|
||||
fi
|
||||
|
||||
echo "Run 'msdl --help' to get started."
|
||||
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html"
|
||||
|
|
@ -27,6 +28,9 @@ import (
|
|||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
//go:embed install.sh
|
||||
var installScript []byte
|
||||
|
||||
// Package-level rand source — avoids global mutex contention under concurrent load.
|
||||
// Go 1.20+ auto-seeds the global rand, but a local source is faster at high QPS.
|
||||
var rng = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
|
@ -1701,6 +1705,20 @@ func handleCLIVersion(w http.ResponseWriter, r *http.Request) {
|
|||
json.NewEncoder(w).Encode(map[string]string{"latest": latestCLIVersion})
|
||||
}
|
||||
|
||||
// --- /install.sh endpoint ---
|
||||
|
||||
// handleInstallScript serves the Linux/macOS install script embedded at
|
||||
// build time from install.sh. Auto-detects OS/arch and always resolves the
|
||||
// latest GitHub release, so it needs no per-release maintenance.
|
||||
func handleInstallScript(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/x-sh; charset=utf-8")
|
||||
w.Write(installScript)
|
||||
}
|
||||
|
||||
// --- /metrics endpoint ---
|
||||
|
||||
// loadTelemetryFromRedis reads all telemetry counters from Redis.
|
||||
|
|
@ -1865,6 +1883,7 @@ func main() {
|
|||
mux.HandleFunc("/metrics", handleMetrics)
|
||||
mux.HandleFunc("/telemetry", handleTelemetry)
|
||||
mux.HandleFunc("/cli/version", handleCLIVersion)
|
||||
mux.HandleFunc("/install.sh", handleInstallScript)
|
||||
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(`{"status":"ok"}`))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const RELEASES_URL = 'https://minxl.ink/msdl-github-release'
|
|||
function InstallSteps() {
|
||||
const [copied, setCopied] = useState(false)
|
||||
const [brewCopied, setBrewCopied] = useState(false)
|
||||
const [curlCopied, setCurlCopied] = useState(false)
|
||||
|
||||
function handleCopy() {
|
||||
navigator.clipboard.writeText('winget install starkSV.msdl')
|
||||
|
|
@ -29,6 +30,12 @@ function InstallSteps() {
|
|||
setTimeout(() => setBrewCopied(false), 2000)
|
||||
}
|
||||
|
||||
function handleCurlCopy() {
|
||||
navigator.clipboard.writeText('curl -fsSL https://api.msdl.tech-latest.com/install.sh | bash')
|
||||
setCurlCopied(true)
|
||||
setTimeout(() => setCurlCopied(false), 2000)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<p className="text-[10px] font-mono font-semibold uppercase tracking-widest text-zinc-600">1. Install</p>
|
||||
|
|
@ -56,6 +63,12 @@ function InstallSteps() {
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between gap-2 rounded-lg bg-white/4 border border-white/7 px-3 py-2">
|
||||
<code className="text-[11px] font-mono text-zinc-400 truncate">curl -fsSL .../install.sh | bash</code>
|
||||
<button onClick={handleCurlCopy} className="flex-shrink-0 text-zinc-500 hover:text-white transition-colors">
|
||||
{curlCopied ? <Check size={12} className="text-green-400" /> : <Copy size={12} />}
|
||||
</button>
|
||||
</div>
|
||||
<a
|
||||
href={RELEASES_URL}
|
||||
target="_blank"
|
||||
|
|
|
|||
|
|
@ -116,6 +116,12 @@ export default function CliPage() {
|
|||
<CodeBlock code="brew tap starkSV/msdl && brew install msdl" />
|
||||
</div>
|
||||
|
||||
{/* No Homebrew — curl installer */}
|
||||
<div className="rounded-xl border border-white/7 bg-[#111113] p-4 space-y-2">
|
||||
<p className="text-[12px] text-zinc-400">No Homebrew? One-line installer (auto-detects OS/arch, incl. Termux):</p>
|
||||
<CodeBlock code="curl -fsSL https://api.msdl.tech-latest.com/install.sh | bash" />
|
||||
</div>
|
||||
|
||||
{/* Manual / other platforms */}
|
||||
<div className="rounded-xl border border-white/7 bg-[#111113] p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue