Repository navigation
feat: one-command install without native builds #434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+696
−433
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
bf87f68
build: use prebuilt @lydell/node-pty for PTY sessions
Waishnav 03baeba
build: replace better-sqlite3 with built-in node:sqlite
Waishnav 72904ab
fix(cli): derive the supported Node range from package engines
Waishnav 70c0456
feat: add one-command installers for macOS, Linux, and Windows
Waishnav 34a5ba4
style: satisfy lint for the node:sqlite migration
Waishnav 5715d89
fix(install): harden launchers against shims and unusual paths
Waishnav 32b15c7
fix(install): keep Homebrew's Node path and default to the beta tag
Waishnav 13a0b48
fix(install): pin the real Node except where upgrades delete it
Waishnav 2c6c92a
fix(install): resolve relative Node directory links from their parent
Waishnav File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| // node:sqlite is a release candidate but still emits an ExperimentalWarning in every process that loads it. | ||
| // Built-ins load while the ES module graph links, before any app code runs, so this filter must be preloaded: | ||
| // run-entrypoint.js imports it first, and the agent daemon receives it through `--import`. | ||
| const emitWarning = process.emitWarning; | ||
|
|
||
| process.emitWarning = function (warning, ...args) { | ||
| // emitWarning(warning, type) and emitWarning(warning, { type }) are both valid call shapes. | ||
| const type = args[0]?.type ?? args[0]; | ||
| const message = String(warning?.message ?? warning); | ||
|
|
||
| if (type === "ExperimentalWarning" && message.startsWith("SQLite")) return; | ||
|
|
||
| return emitWarning.call(process, warning, ...args); | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| # DevSpace installer for Windows. | ||
| # | ||
| # powershell -c "irm https://raw.githubusercontent.com/Waishnav/devspace/main/install/install.ps1 | iex" | ||
| # | ||
| # Mirrors install.sh: uses a compatible Node already on PATH, otherwise downloads a checksum-verified Node 24 LTS | ||
| # for DevSpace alone, installs @waishnav/devspace into a user-owned npm prefix, writes `devspace` launchers that | ||
| # pin that Node, then runs `devspace init`. Rerun it to update. DevSpace itself also needs Git Bash on Windows. | ||
| # | ||
| # Environment overrides: DEVSPACE_VERSION (default: beta), DEVSPACE_INSTALL_DIR, DEVSPACE_BIN_DIR, DEVSPACE_NO_INIT=1, | ||
| # DEVSPACE_NO_MODIFY_PATH=1, DEVSPACE_PACKAGE_SPEC (see install.sh). | ||
|
|
||
| $ErrorActionPreference = "Stop" | ||
| $ProgressPreference = "SilentlyContinue" | ||
|
|
||
| $Package = "@waishnav/devspace" | ||
| # v1.1 is the first release installable without install scripts, and it is still on the beta tag. | ||
| # Switch this to latest once v1.1 ships as stable. | ||
| $Version = if ($env:DEVSPACE_VERSION) { $env:DEVSPACE_VERSION } else { "beta" } | ||
| $PackageSpec = if ($env:DEVSPACE_PACKAGE_SPEC) { $env:DEVSPACE_PACKAGE_SPEC } else { "$Package@$Version" } | ||
| $InstallDir = if ($env:DEVSPACE_INSTALL_DIR) { $env:DEVSPACE_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA "devspace\install" } | ||
| $BinDir = if ($env:DEVSPACE_BIN_DIR) { $env:DEVSPACE_BIN_DIR } else { Join-Path $env:LOCALAPPDATA "devspace\bin" } | ||
| $NodeMajor = 24 | ||
| # Must match package.json engines.node. | ||
| $NodeMin = "22.19.0" | ||
| $NodeMaxMajor = 27 | ||
|
|
||
| function Test-CompatibleNode([string]$Node) { | ||
| $script = 'const [maj,min,patch]=process.versions.node.split(".").map(Number);const [a,b,c]=process.argv[1].split(".").map(Number);const ok=maj>a||(maj===a&&(min>b||(min===b&&patch>=c)));process.exit(ok&&maj<Number(process.argv[2])?0:1)' | ||
| & $Node -e $script $NodeMin $NodeMaxMajor *> $null | ||
| return $LASTEXITCODE -eq 0 | ||
| } | ||
|
|
||
| function Install-Node { | ||
| $arch = switch ($env:PROCESSOR_ARCHITECTURE) { | ||
| "AMD64" { "x64" } | ||
| "ARM64" { "arm64" } | ||
| default { throw "Unsupported CPU architecture: $($env:PROCESSOR_ARCHITECTURE)" } | ||
| } | ||
| $dist = "https://nodejs.org/dist/latest-v$NodeMajor.x" | ||
| $tmp = Join-Path ([IO.Path]::GetTempPath()) ("devspace-node-" + [Guid]::NewGuid()) | ||
| New-Item -ItemType Directory -Path $tmp | Out-Null | ||
|
|
||
| try { | ||
| Write-Host "Downloading Node $NodeMajor LTS for win-$arch..." | ||
| $sums = (Invoke-WebRequest -UseBasicParsing "$dist/SHASUMS256.txt").Content -split "`n" | ||
| $line = $sums | Where-Object { $_ -match "\s(node-v[\d.]+-win-$arch\.zip)$" } | Select-Object -First 1 | ||
| if (-not $line) { throw "No Node $NodeMajor build is published for win-$arch." } | ||
| $expected, $file = $line.Trim() -split "\s+" | ||
| $zip = Join-Path $tmp $file | ||
| Invoke-WebRequest -UseBasicParsing "$dist/$file" -OutFile $zip | ||
| if ((Get-FileHash -Algorithm SHA256 $zip).Hash.ToLower() -ne $expected) { | ||
| throw "Checksum mismatch for $file; refusing to install it." | ||
| } | ||
|
|
||
| Expand-Archive -Path $zip -DestinationPath $tmp | ||
| $nodeDir = Join-Path $InstallDir "node" | ||
| if (Test-Path $nodeDir) { Remove-Item -Recurse -Force $nodeDir } | ||
| New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null | ||
| Move-Item (Join-Path $tmp ($file -replace "\.zip$", "")) $nodeDir | ||
| Write-Host "Installed $($file -replace '\.zip$', '') into $nodeDir" | ||
| return (Join-Path $nodeDir "node.exe") | ||
| } finally { | ||
| Remove-Item -Recurse -Force $tmp -ErrorAction SilentlyContinue | ||
| } | ||
| } | ||
|
|
||
| function Select-Node { | ||
| $managed = Join-Path $InstallDir "node\node.exe" | ||
| if ((Test-Path $managed) -and (Test-CompatibleNode $managed)) { return $managed } | ||
|
|
||
| $system = Get-Command node -ErrorAction SilentlyContinue | ||
| if ($system -and (Test-CompatibleNode $system.Source)) { | ||
| # Pin the real binary, since version managers switch what `node` points to but keep each version on disk. | ||
| # nvm-windows repoints a symlinked directory, which process.execPath may report unresolved, so follow it. | ||
| $dir = Get-Item -LiteralPath (Split-Path $system.Source) | ||
| if ($dir.LinkType -and $dir.Target) { | ||
| $target = @($dir.Target)[0] | ||
| if (-not [IO.Path]::IsPathRooted($target)) { $target = Join-Path (Split-Path $dir.FullName) $target } | ||
| return Join-Path $target (Split-Path $system.Source -Leaf) | ||
| } | ||
| return (& $system.Source -p "process.execPath") | ||
| } | ||
| if ($system) { Write-Host "Found Node $(& $system.Source --version), but DevSpace needs >=$NodeMin <$NodeMaxMajor." } | ||
|
|
||
| return Install-Node | ||
| } | ||
|
|
||
| # cmd.exe reads .cmd files in the console code page, so a launcher cannot embed non-ASCII text literally. | ||
| # Spell paths under the user's profile folders through their environment variables instead, which cmd expands | ||
| # with full Unicode, and keep any literal % from being expanded. | ||
| function ConvertTo-CmdPath([string]$Path) { | ||
| $prefix = "" | ||
| $rest = $Path | ||
| foreach ($name in @("LOCALAPPDATA", "APPDATA", "USERPROFILE")) { | ||
| $root = [Environment]::GetEnvironmentVariable($name) | ||
| if ($root -and $Path.StartsWith($root, [StringComparison]::OrdinalIgnoreCase)) { | ||
| $prefix = "%$name%" | ||
| $rest = $Path.Substring($root.Length) | ||
| break | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| } | ||
| if ($rest -match "[^\x00-\x7F]") { | ||
| throw "Cannot write a launcher for $Path because cmd.exe cannot read its non-ASCII characters. If it is a Node runtime, move that Node to an ASCII path or remove it from PATH so the installer downloads its own; otherwise set DEVSPACE_INSTALL_DIR and DEVSPACE_BIN_DIR to ASCII paths. Then rerun." | ||
| } | ||
| return $prefix + $rest.Replace("%", "%%") | ||
| } | ||
|
|
||
| $Node = Select-Node | ||
| $NodeDir = Split-Path $Node | ||
| $Npm = Join-Path $NodeDir "npm.cmd" | ||
| if (-not (Test-Path $Npm)) { throw "npm was not found next to $Node. Install npm for that Node and rerun." } | ||
|
|
||
| Write-Host "Installing $PackageSpec with Node $(& $Node --version)..." | ||
| $prefix = Join-Path $InstallDir "npm" | ||
| New-Item -ItemType Directory -Path $prefix -Force | Out-Null | ||
| # npm resolves `node` from PATH, so put the selected Node first. | ||
| # DevSpace and its dependencies ship prebuilt binaries, so no install scripts are needed. | ||
| $env:Path = "$NodeDir;$env:Path" | ||
| & $Npm install --global --prefix $prefix --ignore-scripts --no-fund --no-audit --no-update-notifier --loglevel=error $PackageSpec | ||
| if ($LASTEXITCODE -ne 0) { throw "npm install failed." } | ||
|
|
||
| $packageDir = Join-Path $prefix "node_modules\$Package" | ||
| if (-not (Test-Path (Join-Path $packageDir "bin\devspace.js"))) { throw "Install finished but $packageDir\bin\devspace.js is missing." } | ||
| New-Item -ItemType Directory -Path $BinDir -Force | Out-Null | ||
|
|
||
| $cmdNode = ConvertTo-CmdPath $Node | ||
| foreach ($command in @("devspace", "devspace-agentd")) { | ||
| $cmdEntry = ConvertTo-CmdPath (Join-Path $packageDir "bin\$command.js") | ||
| # Pin the Node that installed DevSpace so a version manager switching `node` cannot break it. | ||
| # goto instead of an if (...) block: paths such as "Program Files (x86)" would close the block early. | ||
| $launcher = @( | ||
| "@echo off", | ||
| "rem Generated by the DevSpace installer. Rerun the installer to update.", | ||
| "if not exist `"$cmdNode`" goto missing", | ||
| "`"$cmdNode`" `"$cmdEntry`" %*", | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| "exit /b %errorlevel%", | ||
| ":missing", | ||
| "echo DevSpace's Node runtime is missing: `"$cmdNode`" 1>&2", | ||
| "echo Rerun the DevSpace installer. 1>&2", | ||
| "exit /b 1" | ||
| ) -join "`r`n" | ||
| Set-Content -Path (Join-Path $BinDir "$command.cmd") -Value $launcher -Encoding ASCII | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| } | ||
|
|
||
| $userPath = [Environment]::GetEnvironmentVariable("Path", "User") | ||
| if (($userPath -split ";") -notcontains $BinDir) { | ||
| if ($env:DEVSPACE_NO_MODIFY_PATH -eq "1") { | ||
| Write-Host "Add $BinDir to your PATH to run devspace." | ||
| } else { | ||
| [Environment]::SetEnvironmentVariable("Path", (@($BinDir, $userPath) | Where-Object { $_ }) -join ";", "User") | ||
| Write-Host "Added $BinDir to your user PATH (open a new terminal to pick it up)." | ||
| } | ||
| } | ||
| $env:Path = "$BinDir;$env:Path" | ||
|
|
||
| $devspace = Join-Path $BinDir "devspace.cmd" | ||
| $version = & $devspace --version | ||
| if ($LASTEXITCODE -ne 0) { throw "The installed launcher at $devspace did not run." } | ||
| Write-Host "DevSpace $version is installed at $devspace" | ||
|
|
||
| if ($env:DEVSPACE_NO_INIT -ne "1") { | ||
| Write-Host "" | ||
| & $devspace init | ||
| if ($LASTEXITCODE -ne 0) { Write-Host "Setup did not finish. Run ``devspace init`` to continue." } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,206 @@ | ||
| #!/bin/sh | ||
| # DevSpace installer for macOS, Linux, and WSL. | ||
| # | ||
| # curl -fsSL https://raw.githubusercontent.com/Waishnav/devspace/main/install/install.sh | sh | ||
| # | ||
| # Uses a compatible Node already on PATH, otherwise downloads a checksum-verified Node 24 LTS for DevSpace alone. | ||
| # Installs @waishnav/devspace into a user-owned npm prefix (no sudo), writes `devspace` launchers that pin that | ||
| # Node, then runs `devspace init`. Rerun it to update. | ||
| # | ||
| # Environment overrides: | ||
| # DEVSPACE_VERSION npm version or dist-tag to install (default: beta) | ||
| # DEVSPACE_INSTALL_DIR runtime and package location (default: ~/.local/share/devspace/install) | ||
| # DEVSPACE_BIN_DIR launcher location (default: ~/.local/bin) | ||
| # DEVSPACE_NO_INIT=1 skip running `devspace init` afterwards | ||
| # DEVSPACE_NO_MODIFY_PATH=1 never edit shell profiles | ||
| # DEVSPACE_PACKAGE_SPEC npm spec to install instead, such as a local tarball (for testing) | ||
|
|
||
| set -eu | ||
|
|
||
| PACKAGE="@waishnav/devspace" | ||
| # v1.1 is the first release installable without install scripts, and it is still on the beta tag. | ||
| # Switch this to latest once v1.1 ships as stable. | ||
| VERSION="${DEVSPACE_VERSION:-beta}" | ||
| PACKAGE_SPEC="${DEVSPACE_PACKAGE_SPEC:-$PACKAGE@$VERSION}" | ||
| INSTALL_DIR="${DEVSPACE_INSTALL_DIR:-$HOME/.local/share/devspace/install}" | ||
| BIN_DIR="${DEVSPACE_BIN_DIR:-$HOME/.local/bin}" | ||
| NODE_MAJOR=24 | ||
| # Must match package.json engines.node. | ||
| NODE_MIN="22.19.0" | ||
| NODE_MAX_MAJOR=27 | ||
|
|
||
| info() { printf '%s\n' "$*"; } | ||
| fail() { printf 'error: %s\n' "$*" >&2; exit 1; } | ||
|
|
||
| need() { command -v "$1" >/dev/null 2>&1 || fail "'$1' is required but not installed."; } | ||
|
|
||
| # Single-quotes a value for POSIX sh, so generated files keep paths containing $, `, ", or spaces literal. | ||
| shell_quote() { printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"; } | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
|
|
||
| # Succeeds when the given `node` binary satisfies NODE_MIN <= version < NODE_MAX_MAJOR. | ||
| node_is_compatible() { | ||
| "$1" -e ' | ||
| const [maj, min, patch] = process.versions.node.split(".").map(Number); | ||
| const [wantMaj, wantMin, wantPatch] = process.argv[1].split(".").map(Number); | ||
| const atLeast = maj > wantMaj || (maj === wantMaj && (min > wantMin || (min === wantMin && patch >= wantPatch))); | ||
| process.exit(atLeast && maj < Number(process.argv[2]) ? 0 : 1); | ||
| ' "$NODE_MIN" "$NODE_MAX_MAJOR" >/dev/null 2>&1 | ||
| } | ||
|
|
||
| sha256() { | ||
| if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1 | ||
| elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1 | ||
| else fail "sha256sum or shasum is required to verify the Node download." | ||
| fi | ||
| } | ||
|
|
||
| detect_platform() { | ||
| case "$(uname -s)" in | ||
| Linux) os=linux ;; | ||
| Darwin) os=darwin ;; | ||
| MINGW* | MSYS* | CYGWIN*) fail "Use install.ps1 on Windows, or run this script inside WSL." ;; | ||
| *) fail "Unsupported OS: $(uname -s)" ;; | ||
| esac | ||
|
|
||
| case "$(uname -m)" in | ||
| x86_64 | amd64) arch=x64 ;; | ||
| arm64 | aarch64) arch=arm64 ;; | ||
| *) fail "Unsupported CPU architecture: $(uname -m)" ;; | ||
| esac | ||
|
|
||
| # Rosetta reports x64 on Apple silicon; prefer the native build. | ||
| if [ "$os" = darwin ] && [ "$arch" = x64 ] && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || echo 0)" = 1 ]; then | ||
| arch=arm64 | ||
| fi | ||
|
|
||
| if [ "$os" = linux ] && { [ -f /etc/alpine-release ] || ldd --version 2>&1 | grep -qi musl; }; then | ||
| musl=1 | ||
| else | ||
| musl=0 | ||
| fi | ||
| } | ||
|
|
||
| install_node() { | ||
| [ "$musl" = 0 ] || fail "nodejs.org has no musl build. Install Node $NODE_MIN+ with your package manager (apk add nodejs npm) and rerun." | ||
| need curl | ||
| need tar | ||
|
|
||
| dist="https://nodejs.org/dist/latest-v$NODE_MAJOR.x" | ||
| tmp=$(mktemp -d) | ||
| trap 'rm -rf "$tmp"' EXIT | ||
|
|
||
| info "Downloading Node $NODE_MAJOR LTS for $os-$arch..." | ||
| curl -fsSL "$dist/SHASUMS256.txt" -o "$tmp/SHASUMS256.txt" | ||
| file=$(awk -v suffix="-$os-$arch.tar.gz" 'substr($2, length($2) - length(suffix) + 1) == suffix { print $2; exit }' "$tmp/SHASUMS256.txt") | ||
| [ -n "$file" ] || fail "No Node $NODE_MAJOR build is published for $os-$arch." | ||
| expected=$(awk -v name="$file" '$2 == name { print $1; exit }' "$tmp/SHASUMS256.txt") | ||
|
|
||
| curl -fsSL "$dist/$file" -o "$tmp/$file" | ||
| [ "$(sha256 "$tmp/$file")" = "$expected" ] || fail "Checksum mismatch for $file; refusing to install it." | ||
|
|
||
| rm -rf "$INSTALL_DIR/node" | ||
| mkdir -p "$INSTALL_DIR/node" | ||
| tar -xzf "$tmp/$file" -C "$INSTALL_DIR/node" --strip-components=1 | ||
| NODE="$INSTALL_DIR/node/bin/node" | ||
| info "Installed ${file%.tar.gz} into $INSTALL_DIR/node" | ||
| } | ||
|
|
||
| select_node() { | ||
| if [ -x "$INSTALL_DIR/node/bin/node" ] && node_is_compatible "$INSTALL_DIR/node/bin/node"; then | ||
| NODE="$INSTALL_DIR/node/bin/node" | ||
| elif command -v node >/dev/null 2>&1 && node_is_compatible "$(command -v node)"; then | ||
| # Pin the real binary: version managers (nvm, fnm, mise, asdf, Volta) switch what `node` points to but | ||
| # keep every installed version on disk. Homebrew and Nix delete old versions on upgrade instead, so for | ||
| # them keep the stable link that `command -v` found. | ||
| NODE=$(node -p process.execPath) | ||
| case "$NODE" in | ||
| */Cellar/* | /nix/store/*) NODE=$(command -v node) ;; | ||
| esac | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| else | ||
| if command -v node >/dev/null 2>&1; then | ||
| info "Found Node $(node --version), but DevSpace needs >=$NODE_MIN <$NODE_MAX_MAJOR." | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| fi | ||
| install_node | ||
| fi | ||
|
|
||
| NODE_BIN_DIR=$(dirname "$NODE") | ||
| [ -x "$NODE_BIN_DIR/npm" ] || fail "npm was not found next to $NODE. Install npm for that Node and rerun." | ||
| } | ||
|
|
||
| install_package() { | ||
| info "Installing $PACKAGE_SPEC with Node $("$NODE" --version)..." | ||
| mkdir -p "$INSTALL_DIR/npm" | ||
| # npm's shebang resolves `node` from PATH, so put the selected Node first. | ||
| # DevSpace and its dependencies ship prebuilt binaries, so no install scripts are needed. | ||
| PATH="$NODE_BIN_DIR:$PATH" "$NODE_BIN_DIR/npm" install --global --prefix "$INSTALL_DIR/npm" \ | ||
| --ignore-scripts --no-fund --no-audit --no-update-notifier --loglevel=error "$PACKAGE_SPEC" | ||
| } | ||
|
|
||
| write_launchers() { | ||
| package_dir="$INSTALL_DIR/npm/lib/node_modules/$PACKAGE" | ||
| [ -f "$package_dir/bin/devspace.js" ] || fail "Install finished but $package_dir/bin/devspace.js is missing." | ||
| mkdir -p "$BIN_DIR" | ||
|
|
||
| for command in devspace devspace-agentd; do | ||
| launcher="$BIN_DIR/$command" | ||
| # Pin the Node that installed DevSpace so a version manager switching `node` cannot break it. | ||
| cat >"$launcher" <<EOF | ||
| #!/bin/sh | ||
| # Generated by the DevSpace installer. Rerun the installer to update. | ||
| node=$(shell_quote "$NODE") | ||
| if [ ! -x "\$node" ]; then | ||
| echo "DevSpace's Node runtime (\$node) is missing. Rerun the DevSpace installer." >&2 | ||
| exit 1 | ||
| fi | ||
| exec "\$node" $(shell_quote "$package_dir/bin/$command.js") "\$@" | ||
| EOF | ||
| chmod 755 "$launcher" | ||
| done | ||
| } | ||
|
|
||
| ensure_path() { | ||
| case ":$PATH:" in *":$BIN_DIR:"*) return ;; esac | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
|
|
||
| if [ "${DEVSPACE_NO_MODIFY_PATH:-0}" = 1 ]; then | ||
| info "Add $BIN_DIR to your PATH to run devspace." | ||
| return | ||
| fi | ||
|
|
||
| line="export PATH=$(shell_quote "$BIN_DIR"):\"\$PATH\"" | ||
|
|
||
| case "$(basename "${SHELL:-sh}")" in | ||
| zsh) profile="${ZDOTDIR:-$HOME}/.zshrc" ;; | ||
| bash) if [ "$os" = darwin ]; then profile="$HOME/.bash_profile"; else profile="$HOME/.bashrc"; fi ;; | ||
| fish) profile="$HOME/.config/fish/config.fish" | ||
| # fish single quotes escape only backslash and the quote itself. | ||
| line="fish_add_path '$(printf '%s' "$BIN_DIR" | sed "s/[\\\\']/\\\\&/g")'" ;; | ||
| *) profile="$HOME/.profile" ;; | ||
| esac | ||
|
|
||
| if ! grep -Fqs "$line" "$profile"; then | ||
| mkdir -p "$(dirname "$profile")" | ||
| printf '\n# DevSpace\n%s\n' "$line" >>"$profile" | ||
| info "Added $BIN_DIR to PATH in $profile (open a new shell to pick it up)." | ||
| fi | ||
| } | ||
|
|
||
| run_init() { | ||
| if [ "${DEVSPACE_NO_INIT:-0}" = 1 ]; then return; fi | ||
|
|
||
| # Under `curl | sh` stdin is the script itself, so setup reads answers from the terminal. | ||
| if [ -r /dev/tty ] && [ -w /dev/tty ]; then | ||
| info "" | ||
| "$BIN_DIR/devspace" init </dev/tty >/dev/tty 2>&1 || info "Setup did not finish. Run \`devspace init\` to continue." | ||
| else | ||
| info "No terminal available; run \`devspace init\` to finish setup." | ||
| fi | ||
| } | ||
|
|
||
| detect_platform | ||
| select_node | ||
| install_package | ||
| write_launchers | ||
| ensure_path | ||
| version=$("$BIN_DIR/devspace" --version) || fail "The installed launcher at $BIN_DIR/devspace did not run." | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
|
||
| info "DevSpace $version is installed at $BIN_DIR/devspace" | ||
| run_init | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.