fix dcrup installers and installation docs

This commit is contained in:
2026-10-11 15:04:16 +08:00
parent a890f7e6eb
commit f17e4c6642
3 changed files with 573 additions and 505 deletions

View File

@@ -1,4 +1,4 @@
#Requires -Version 5.1
#Requires -Version 5.1
$ErrorActionPreference = 'Stop'
$DcrupHome = if ($env:DCRUP_HOME) { $env:DCRUP_HOME } else { Join-Path $env:USERPROFILE '.dcr' }
@@ -6,6 +6,7 @@ $DcrupBin = if ($env:DCRUP_BIN) { $env:DCRUP_BIN } else { Join-Path $DcrupHom
$DcrupTc = Join-Path $DcrupHome 'toolchains'
$DcrupMeta = Join-Path $DcrupHome 'meta'
$RepoUrl = if ($env:DCRUP_REPO) { $env:DCRUP_REPO } else { 'https://github.com/dexoron/dcr' }
$InstallUrl = if ($env:DCRUP_INSTALL_URL) { $env:DCRUP_INSTALL_URL } else { 'https://ps1.dcr-tool.ru' }
$DcrupMirror = if ($env:DCRUP_MIRROR) { $env:DCRUP_MIRROR } else { 'auto' }
$ApiLatest = 'https://api.github.com/repos/dexoron/dcr/releases/latest'
$ApiAll = 'https://api.github.com/repos/dexoron/dcr/releases'
@@ -18,7 +19,7 @@ function Die($m) { Write-Host "[error] $m" -ForegroundColor Red; exit 1 }
function Show-Usage {
@'
dcrup — install and switch DCR versions (Windows)
dcrup - install and switch DCR versions (Windows)
Usage:
dcrup install <spec> [--build|--release] [--force] [--mirror auto|cf|gh|sf|ya]
@@ -33,10 +34,10 @@ Usage:
Spec:
stable | dev | night
VERSION → VERSION@stable (e.g. 0.8.2)
VERSION в†’ VERSION@stable (e.g. 0.8.2)
VERSION@stable
VERSION@dev
night → always build from branch "dev" HEAD
night в†’ always build from branch "dev" HEAD
Modes:
--release download prebuilt binary (default for stable/dev)
@@ -44,7 +45,7 @@ Modes:
night always --build (prebuilt not available)
Mirrors (prebuilt download only):
--mirror auto try cf → gh → sf → ya, first successful download wins (default)
--mirror auto try cf в†’ gh в†’ sf в†’ ya, first successful download wins (default)
--mirror cf download from cdn-global.dcr-tool.ru
--mirror gh download from github.com/dexoron/dcr
--mirror sf download from sourceforge.net/projects/dcr-tool
@@ -130,7 +131,19 @@ function Link-Default([string]$id) {
$dst = Join-Path $DcrupBin 'dcr.exe'
Copy-Item $src $dst -Force
Write-Active $id
Ok "default → $id ($dst)"
Ok "default в†’ $id ($dst)"
}
function Resolve-InstalledId([string]$spec) {
Parse-Spec $spec
if ($script:SpecChannel -eq 'night' -or $script:SpecFloating) {
$item = Get-ChildItem $DcrupTc -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -like "$($script:SpecChannel)-*" } |
Sort-Object Name | Select-Object -Last 1
if ($item) { return $item.Name }
return $null
}
return (Toolchain-Id)
}
function Set-Mirror([string]$value) {
@@ -162,7 +175,7 @@ function Download-FromMirrors([string]$tag, [string]$asset, [string]$destFile) {
$tmp = "$destFile.download"
foreach ($m in (Get-MirrorsToTry)) {
$url = Get-MirrorUrl $m $tag $asset
Info "trying mirror $m …"
Info "trying mirror $m …"
Remove-Item $tmp -ErrorAction SilentlyContinue
try {
Invoke-WebRequest -Uri $url -OutFile $tmp -UserAgent 'dcrup'
@@ -210,10 +223,10 @@ function Install-Prebuilt([switch]$Force) {
}
Ensure-Dirs
New-Item -ItemType Directory -Force -Path $dest | Out-Null
Info "downloading $assetName (mirror=$($script:DcrupMirror)) …"
Info "downloading $assetName (mirror=$($script:DcrupMirror)) …"
$mirror = Download-FromMirrors $tag $assetName $bin
if (-not $mirror) { Die "no mirror reachable for $assetName (try: --mirror cf|gh|sf|ya)" }
Info "mirror → $mirror"
Info "mirror в†’ $mirror"
@"
channel=$($script:SpecChannel)
version=$ver
@@ -235,7 +248,7 @@ function Install-Build([switch]$Force) {
try {
$src = Join-Path $tmp 'src'
if ($script:SpecChannel -eq 'night') {
Info "cloning dev for night …"
Info "cloning dev for night …"
git clone --depth 1 --branch dev $RepoUrl $src 2>$null
if ($LASTEXITCODE -ne 0) { git clone --depth 1 $RepoUrl $src | Out-Null }
$sha = (git -C $src rev-parse --short HEAD).Trim()
@@ -246,7 +259,7 @@ function Install-Build([switch]$Force) {
$tag = $rel.tag_name
$ver = Normalize-Version $tag
$id = Toolchain-Id $ver
Info "cloning $tag …"
Info "cloning $tag …"
git clone --depth 1 --branch $tag $RepoUrl $src 2>$null
if ($LASTEXITCODE -ne 0) {
git clone --depth 1 $RepoUrl $src | Out-Null
@@ -258,7 +271,7 @@ function Install-Build([switch]$Force) {
$tag = "v$($script:SpecVersion)"
$ver = $script:SpecVersion
$id = Toolchain-Id
Info "cloning $tag …"
Info "cloning $tag …"
git clone --depth 1 --branch $tag $RepoUrl $src 2>$null
if ($LASTEXITCODE -ne 0) {
git clone --depth 1 $RepoUrl $src | Out-Null
@@ -277,7 +290,7 @@ function Install-Build([switch]$Force) {
}
$triple = Get-Target
Info "building release (features: $Features) …"
Info "building release (features: $Features) …"
Push-Location $src
cargo build --release --features $Features --target $triple
if ($LASTEXITCODE -ne 0) { Die 'cargo build failed' }
@@ -379,10 +392,22 @@ function Cmd-Show {
function Cmd-SelfInstall {
Ensure-Dirs
if ([string]::IsNullOrWhiteSpace($PSCommandPath) -or -not (Test-Path $PSCommandPath)) {
# With `irm URL | iex`, PowerShell evaluates text and there is no
# source path. Download the canonical installer for self-install.
$tmp = Join-Path $env:TEMP ("dcrup-" + [guid]::NewGuid().ToString('N') + '.ps1')
try {
Invoke-WebRequest -Uri $InstallUrl -OutFile $tmp -UseBasicParsing -UserAgent 'dcrup'
Copy-Item $tmp (Join-Path $DcrupBin 'dcrup.ps1') -Force
} finally {
Remove-Item $tmp -Force -ErrorAction SilentlyContinue
}
} else {
Copy-Item $PSCommandPath (Join-Path $DcrupBin 'dcrup.ps1') -Force
}
Install-CmdShim
Ensure-UserPath
Ok "dcrup installed to $DcrupBin (use: dcrup …)"
Ok "dcrup installed to $DcrupBin (use: dcrup …)"
}
$cmd = if ($args.Count -ge 1) { $args[0] } else { 'help' }
@@ -417,7 +442,7 @@ switch ($cmd) {
if ($a -like 'night-*') { Cmd-Install @('night', '--build', '--force') }
elseif ($a -like 'stable-*') { Cmd-Install @('stable', '--force') }
elseif ($a -like 'dev-*') { Cmd-Install @('dev', '--force') }
else { Ok "pinned $a — install a newer version explicitly" }
else { Ok "pinned $a - install a newer version explicitly" }
}
'uninstall' {
if ($rest -contains '--all') {
@@ -426,9 +451,21 @@ switch ($cmd) {
Remove-Item (Active-Path) -ErrorAction SilentlyContinue
Ok 'removed all'
} else {
Die 'use: dcrup uninstall --all (or install a replacement first)'
if ($rest.Count -ne 1) { Die 'usage: dcrup uninstall <spec>|--all' }
$id = Resolve-InstalledId $rest[0]
if (-not $id) { Die "not installed: $($rest[0])" }
$dir = Join-Path $DcrupTc $id
if (-not (Test-Path $dir)) { Die "not installed: $($rest[0])" }
Remove-Item -Recurse -Force $dir
if ((Read-Active) -eq $id) {
Remove-Item (Join-Path $DcrupBin 'dcr.exe') -ErrorAction SilentlyContinue
Remove-Item (Active-Path) -ErrorAction SilentlyContinue
Warn 'removed active toolchain; install or select another default'
}
Ok "removed $id"
}
}
'self-install' { Cmd-SelfInstall }
default { Die "unknown command: $cmd" }
}

104
dcrup.sh Executable file → Normal file
View File

@@ -6,6 +6,7 @@ DCRUP_BIN="${DCRUP_BIN:-$DCRUP_HOME/bin}"
DCRUP_TC="${DCRUP_HOME}/toolchains"
DCRUP_META="${DCRUP_HOME}/meta"
REPO_URL="${DCRUP_REPO:-https://github.com/dexoron/dcr}"
DCRUP_INSTALL_URL="${DCRUP_INSTALL_URL:-https://sh.dcr-tool.ru}"
DCRUP_MIRROR="${DCRUP_MIRROR:-auto}"
API_LATEST="https://api.github.com/repos/dexoron/dcr/releases/latest"
API_ALL="https://api.github.com/repos/dexoron/dcr/releases"
@@ -20,7 +21,10 @@ NC='\033[0m'
log() { printf "${CYAN}[dcrup]${NC} %s\n" "$*"; }
ok() { printf "${GREEN}[ok]${NC} %s\n" "$*"; }
warn() { printf "${YELLOW}[warn]${NC} %s\n" "$*"; }
die() { printf "${RED}[error]${NC} %s\n" "$*" >&2; exit 1; }
die() {
printf "${RED}[error]${NC} %s\n" "$*" >&2
exit 1
}
usage() {
cat <<'EOF'
@@ -79,7 +83,7 @@ need_cmd() {
detect_linux_libc() {
local pref="${DCRUP_LIBC:-gnu}"
case "$pref" in
gnu|glibc) echo "gnu" ;;
gnu | glibc) echo "gnu" ;;
musl) echo "musl" ;;
auto)
if command -v ldd >/dev/null 2>&1; then
@@ -115,7 +119,7 @@ detect_triple() {
echo "x86_64-unknown-linux-gnu"
fi
;;
Linux:aarch64|Linux:arm64)
Linux:aarch64 | Linux:arm64)
libc="$(detect_linux_libc)"
if [[ "$libc" == "musl" ]]; then
echo "aarch64-unknown-linux-musl"
@@ -123,7 +127,7 @@ detect_triple() {
echo "aarch64-unknown-linux-gnu"
fi
;;
Linux:i686|Linux:i386)
Linux:i686 | Linux:i386)
libc="$(detect_linux_libc)"
if [[ "$libc" == "musl" ]]; then
echo "i686-unknown-linux-musl"
@@ -131,7 +135,7 @@ detect_triple() {
echo "i686-unknown-linux-gnu"
fi
;;
Linux:armv7l|Linux:armv7)
Linux:armv7l | Linux:armv7)
libc="$(detect_linux_libc)"
if [[ "$libc" == "musl" ]]; then
echo "armv7-unknown-linux-musleabihf"
@@ -148,16 +152,16 @@ detect_triple() {
fi
;;
Darwin:x86_64) echo "x86_64-apple-darwin" ;;
Darwin:arm64|Darwin:aarch64) echo "aarch64-apple-darwin" ;;
FreeBSD:x86_64|FreeBSD:amd64) echo "x86_64-unknown-freebsd" ;;
FreeBSD:aarch64|FreeBSD:arm64) echo "aarch64-unknown-freebsd" ;;
OpenBSD:x86_64|OpenBSD:amd64) echo "x86_64-unknown-openbsd" ;;
NetBSD:x86_64|NetBSD:amd64) echo "x86_64-unknown-netbsd" ;;
MINGW*|MSYS*|CYGWIN*)
Darwin:arm64 | Darwin:aarch64) echo "aarch64-apple-darwin" ;;
FreeBSD:x86_64 | FreeBSD:amd64) echo "x86_64-unknown-freebsd" ;;
FreeBSD:aarch64 | FreeBSD:arm64) echo "aarch64-unknown-freebsd" ;;
OpenBSD:x86_64 | OpenBSD:amd64) echo "x86_64-unknown-openbsd" ;;
NetBSD:x86_64 | NetBSD:amd64) echo "x86_64-unknown-netbsd" ;;
MINGW* | MSYS* | CYGWIN*)
case "$arch" in
x86_64|amd64) echo "x86_64-pc-windows-gnu" ;;
aarch64|arm64) echo "aarch64-pc-windows-gnullvm" ;;
i686|i386) echo "i686-pc-windows-gnu" ;;
x86_64 | amd64) echo "x86_64-pc-windows-gnu" ;;
aarch64 | arm64) echo "aarch64-pc-windows-gnullvm" ;;
i686 | i386) echo "i686-pc-windows-gnu" ;;
*) die "unsupported Windows arch under MSYS: $arch" ;;
esac
;;
@@ -180,7 +184,7 @@ parse_spec() {
case "$raw" in
"") die "missing version/channel spec" ;;
stable|dev|night)
stable | dev | night)
SPEC_CHANNEL="$raw"
SPEC_VERSION=""
SPEC_FLOATING=1
@@ -189,7 +193,7 @@ parse_spec() {
SPEC_VERSION="$(normalize_version "${raw%@*}")"
SPEC_CHANNEL="${raw#*@}"
case "$SPEC_CHANNEL" in
stable|dev) ;;
stable | dev) ;;
night) die "cannot pin a version on night (use: dcrup install night)" ;;
*) die "unknown channel in spec: $SPEC_CHANNEL (use stable|dev)" ;;
esac
@@ -235,6 +239,8 @@ link_default() {
local id="$1"
local src="$DCRUP_TC/$id/dcr"
[[ -x "$src" ]] || die "toolchain not installed: $id"
# Resolve the source so a relative DCRUP_HOME cannot create a broken link.
src="$(cd "$(dirname "$src")" && pwd)/$(basename "$src")"
ln -sfn "$src" "$DCRUP_BIN/dcr"
write_active "$id"
ok "default → $id ($DCRUP_BIN/dcr)"
@@ -251,7 +257,7 @@ json_tag() {
set_mirror() {
case "$1" in
auto|cf|gh|sf|ya) DCRUP_MIRROR="$1" ;;
auto | cf | gh | sf | ya) DCRUP_MIRROR="$1" ;;
*) die "invalid --mirror: $1 (use auto|cf|gh|sf|ya)" ;;
esac
}
@@ -272,7 +278,7 @@ mirror_chain() { echo "cf gh sf ya"; }
mirrors_to_try() {
case "$DCRUP_MIRROR" in
auto) mirror_chain ;;
cf|gh|sf|ya) echo "$DCRUP_MIRROR" ;;
cf | gh | sf | ya) echo "$DCRUP_MIRROR" ;;
*) die "invalid mirror: $DCRUP_MIRROR (use auto|cf|gh|sf|ya)" ;;
esac
}
@@ -361,8 +367,8 @@ install_prebuilt() {
mkdir -p "$dest"
log "downloading $asset (mirror=$DCRUP_MIRROR) …"
download_from_mirrors "$tag" "$asset" "$dest" \
|| die "no mirror reachable for $asset (try: --mirror cf|gh|sf|ya)"
download_from_mirrors "$tag" "$asset" "$dest" ||
die "no mirror reachable for $asset (try: --mirror cf|gh|sf|ya)"
mirror="$USED_MIRROR"
log "mirror → $mirror"
chmod +x "$dest/dcr"
@@ -390,8 +396,8 @@ install_build() {
if [[ "$SPEC_CHANNEL" == "night" ]]; then
log "cloning $REPO_URL (branch dev) for night …"
git clone --depth 1 --branch dev "$REPO_URL" "$tmp/src" 2>/dev/null \
|| git clone --depth 1 "$REPO_URL" "$tmp/src"
git clone --depth 1 --branch dev "$REPO_URL" "$tmp/src" 2>/dev/null ||
git clone --depth 1 "$REPO_URL" "$tmp/src"
sha="$(git -C "$tmp/src" rev-parse --short HEAD)"
version="night"
id="$(toolchain_id "$sha")"
@@ -403,8 +409,8 @@ install_build() {
version="$(normalize_version "$tag")"
id="$(toolchain_id "$version")"
log "cloning $REPO_URL @ $tag …"
git clone --depth 1 --branch "$tag" "$REPO_URL" "$tmp/src" 2>/dev/null \
|| {
git clone --depth 1 --branch "$tag" "$REPO_URL" "$tmp/src" 2>/dev/null ||
{
git clone --depth 1 "$REPO_URL" "$tmp/src"
git -C "$tmp/src" fetch --depth 1 origin "refs/tags/${tag}:refs/tags/${tag}"
git -C "$tmp/src" checkout "$tag"
@@ -415,8 +421,8 @@ install_build() {
id="$(toolchain_id)"
local tag="v${SPEC_VERSION}"
log "cloning $REPO_URL @ $tag …"
git clone --depth 1 --branch "$tag" "$REPO_URL" "$tmp/src" 2>/dev/null \
|| {
git clone --depth 1 --branch "$tag" "$REPO_URL" "$tmp/src" 2>/dev/null ||
{
git clone --depth 1 "$REPO_URL" "$tmp/src"
git -C "$tmp/src" fetch --depth 1 origin "refs/tags/${tag}:refs/tags/${tag}"
git -C "$tmp/src" checkout "$tag"
@@ -453,9 +459,18 @@ cmd_install() {
local libc_cli=""
while [[ $# -gt 0 ]]; do
case "$1" in
--build) mode="build"; shift ;;
--release) mode="release"; shift ;;
--force) FORCE=1; shift ;;
--build)
mode="build"
shift
;;
--release)
mode="release"
shift
;;
--force)
FORCE=1
shift
;;
--libc)
[[ $# -ge 2 ]] || die "--libc needs gnu|musl|auto"
libc_cli="$2"
@@ -474,7 +489,10 @@ cmd_install() {
set_mirror "${1#--mirror=}"
shift
;;
-h|--help) usage; exit 0 ;;
-h | --help)
usage
exit 0
;;
-*) die "unknown flag: $1" ;;
*)
[[ -z "$spec" ]] || die "unexpected argument: $1"
@@ -489,7 +507,7 @@ cmd_install() {
if [[ -n "$libc_cli" ]]; then
case "$libc_cli" in
gnu|glibc|musl|auto) DCRUP_LIBC="$libc_cli" ;;
gnu | glibc | musl | auto) DCRUP_LIBC="$libc_cli" ;;
*) die "invalid --libc: $libc_cli (use gnu|musl|auto)" ;;
esac
fi
@@ -534,12 +552,12 @@ cmd_update() {
log "updating night (rebuild HEAD) …"
FORCE=1 cmd_install night --build --force
;;
stable-*|dev-*)
stable-* | dev-*)
local ch="${active%%-*}"
log "updating floating channel $ch …"
FORCE=1 cmd_install "$ch" --force
;;
*-stable|*-dev)
*-stable | *-dev)
ok "pinned toolchain $active — not floating; install a newer version explicitly"
;;
*)
@@ -633,14 +651,28 @@ cmd_self_install() {
local dest="$DCRUP_BIN"
while [[ $# -gt 0 ]]; do
case "$1" in
--to) dest="$2"; shift 2 ;;
--to)
dest="$2"
shift 2
;;
*) die "usage: dcrup self-install [--to DIR]" ;;
esac
done
ensure_dirs
mkdir -p "$dest"
local self
self="$(readlink -f "$0" 2>/dev/null || realpath "$0" 2>/dev/null || echo "$0")"
self="$(readlink -f "$0" 2>/dev/null || realpath "$0" 2>/dev/null || true)"
if [[ -z "$self" || ! -f "$self" ]]; then
# When invoked as `curl ... | sh`, the shell has no script file to copy.
# Fetch the same trusted installer URL and install that exact script.
need_cmd curl
self="$(mktemp "${TMPDIR:-/tmp}/dcrup.XXXXXX")"
curl -fsSL --proto '=https' --tlsv1.2 "$DCRUP_INSTALL_URL" -o "$self" || {
rm -f "$self"
die "unable to download installer for self-install"
}
trap 'rm -f "$self"' EXIT
fi
cp "$self" "$dest/dcrup"
chmod +x "$dest/dcrup"
ok "dcrup installed to $dest/dcrup"
@@ -652,7 +684,7 @@ main() {
local cmd="${1:-}"
shift || true
case "$cmd" in
""|-h|--help|help) usage ;;
"" | -h | --help | help) usage ;;
install) cmd_install "$@" ;;
default) cmd_default "$@" ;;
update) cmd_update "$@" ;;

View File

@@ -11,7 +11,7 @@ Install and switch DCR versions (stable / dev / night, pin `0.8.2`, optional bui
### Linux / macOS / BSD / Windows (bash)
```sh
curl -fsSL https://sh.dcr-tool.ru | sh -s -- self-install
curl --proto '=https' --tlsv1.2 -sSf https://sh.dcr-tool.ru | sh -s -- self-install
export PATH="$HOME/.dcr/bin:$PATH"
dcrup install stable
```
@@ -19,17 +19,14 @@ dcrup install stable
Non-interactive install of DCR in one shot:
```sh
curl -fsSL https://sh.dcr-tool.ru | sh -s -- install stable
curl --proto '=https' --tlsv1.2 -sSf https://sh.dcr-tool.ru | sh -s -- install stable
export PATH="$HOME/.dcr/bin:$PATH"
```
### Windows (PowerShell)
```powershell
irm https://ps1.dcr-tool.ru | iex
# or download then run:
# irm https://ps1.dcr-tool.ru -OutFile dcrup.ps1
# powershell -File .\dcrup.ps1 self-install
& ([scriptblock]::Create((irm https://ps1.dcr-tool.ru))) self-install
# $env:Path += ";$env:USERPROFILE\.dcr\bin"
dcrup install stable
```
@@ -58,6 +55,8 @@ dcrup update
dcrup list
dcrup show
dcrup which
dcrup uninstall 0.8.2 # remove one installed version
dcrup uninstall --all # remove all installed versions
```
Layout: `~/.dcr/toolchains/<id>/dcr` and `~/.dcr/bin/dcr` → active version.