Skip to content

Instantly share code, notes, and snippets.

@okhsunrog
Created June 26, 2025 20:22
Show Gist options
  • Save okhsunrog/bff4504d990096b8c4ba9afdeb7a2e0d to your computer and use it in GitHub Desktop.
Save okhsunrog/bff4504d990096b8c4ba9afdeb7a2e0d to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -euo pipefail
########################################
# Logging Functions
########################################
log_info() {
printf "[INFO] %s\n" "$1"
}
log_error() {
printf "[ERROR] %s\n" "$1" >&2
}
########################################
# OS Detection
########################################
detect_os() {
local os
os="$(uname)"
if [[ "$os" == "Linux" ]]; then
echo "Linux"
elif [[ "$os" == "Darwin" ]]; then
echo "Darwin"
else
echo "$os"
fi
}
########################################
# Install OS-Specific Dependencies
########################################
install_dependencies() {
local os="$1"
if [[ "$os" == "Linux" ]]; then
SUDO=""
if command -v sudo >/dev/null 2>&1; then
SUDO="sudo"
else
log_error "sudo command not found. Please run this script as root or install sudo."
exit 1
fi
# Check for Arch Linux (pacman)
if command -v pacman >/dev/null 2>&1; then
log_info "Detected Arch Linux. Checking for dependencies..."
local packages_to_install=()
# Essential build tools and libraries. --needed handles them gracefully.
packages_to_install+=("base-devel" "openssl" "systemd")
# Check for the 'clang' COMMAND. Only add the 'clang' package if the command is missing.
# Pacman will handle pulling in 'llvm' as a dependency automatically.
if ! command -v clang &>/dev/null; then
log_info "-> 'clang' command not found. Adding 'clang' to install list."
packages_to_install+=("clang")
else
log_info "-> Found clang: $(clang --version | head -n 1)"
fi
# Check for the 'protoc' COMMAND.
if ! command -v protoc &>/dev/null; then
log_info "-> 'protoc' command not found. Adding 'protobuf' to install list."
packages_to_install+=("protobuf")
else
log_info "-> Found protoc: $(protoc --version)"
fi
# --- THE FAULTY LINE HAS BEEN REMOVED FROM HERE ---
log_info "Attempting to install/update required packages with 'pacman --needed'..."
# The "${packages_to_install[@]}" syntax handles the case where the array is empty.
$SUDO pacman -Syu --noconfirm --needed "${packages_to_install[@]}"
# Check for Debian/Ubuntu (apt-get)
elif command -v apt-get >/dev/null 2>&1; then
log_info "Detected Debian/Ubuntu-based Linux. Updating and installing dependencies..."
$SUDO apt-get update
$SUDO apt-get install -y \
build-essential \
pkg-config \
libudev-dev \
llvm \
libclang-dev \
protobuf-compiler \
libssl-dev
else
log_error "Unsupported Linux distribution. Please install dependencies manually."
log_error "Required dependencies are roughly: a C/C++ compiler (gcc/clang), make, pkg-config, llvm, clang, protobuf, openssl, and libudev."
exit 1
fi
elif [[ "$os" == "Darwin" ]]; then
log_info "Detected macOS. Dependencies are typically handled by Solana/Anchor installers."
log_info "Ensure you have Xcode Command Line Tools installed: 'xcode-select --install'"
else
log_info "Detected an unsupported OS: $os."
fi
echo ""
}
########################################
# Install Rust via rustup
########################################
install_rust() {
if command -v rustc >/dev/null 2>&1; then
log_info "Rust is already installed. Updating..."
rustup update
else
log_info "Installing Rust..."
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
# Source the Rust environment for the current script session
if [[ -f "$HOME/.cargo/env" ]]; then
. "$HOME/.cargo/env"
else
log_error "Could not find Rust environment file to source."
fi
fi
if command -v rustc >/dev/null 2>&1; then
rustc --version
else
log_error "Rust installation failed or was not added to PATH. Please check your ~/.profile or ~/.bashrc."
exit 1
fi
echo ""
}
########################################
# Install Solana CLI
########################################
install_solana_cli() {
# Add Solana to PATH for the current script session
export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
if command -v solana >/dev/null 2>&1; then
log_info "Solana CLI is already installed. Updating..."
# The installer creates 'agave-install', not 'solana-install'
if command -v agave-install >/dev/null 2>&1; then
agave-install update
else
log_error "Cannot find 'agave-install' to update Solana. Please update manually or reinstall."
fi
else
log_info "Installing Solana CLI..."
sh -c "$(curl -sSfL https://release.anza.xyz/stable/install)"
log_info "Solana CLI installation complete."
fi
# The installer modifies shell rc files, but we need the PATH for the current session
export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"
if command -v solana >/dev/null 2>&1; then
solana --version
else
log_error "Solana CLI installation failed or not found in PATH."
log_error "Try adding 'export PATH=\"\$HOME/.local/share/solana/install/active_release/bin:\$PATH\"' to your shell rc file."
exit 1
fi
echo ""
}
########################################
# Install Anchor CLI
########################################
install_anchor_cli() {
# Add cargo-installed binaries to PATH for the current session
export PATH="$HOME/.cargo/bin:$PATH"
if command -v anchor >/dev/null 2>&1; then
log_info "Anchor CLI is already installed. Updating..."
if ! command -v avm >/dev/null 2>&1; then
log_info "AVM is not installed. Installing AVM..."
cargo install --force --git https://github.com/coral-xyz/anchor avm
fi
avm update
else
log_info "Installing Anchor Version Manager (AVM) and Anchor CLI..."
cargo install --git https://github.com/coral-xyz/anchor avm --locked
avm install latest
avm use latest
log_info "Anchor CLI installation complete."
fi
if command -v anchor >/dev/null 2>&1; then
anchor --version
else
log_error "Anchor CLI installation failed or not found in PATH."
log_error "Try adding 'export PATH=\"\$HOME/.cargo/bin:\$PATH\"' to your shell rc file."
exit 1
fi
echo ""
}
########################################
# Install nvm and Node.js
########################################
install_nvm_and_node() {
# Ensure NVM is sourced for the current session if it exists
if [ -s "$HOME/.nvm/nvm.sh" ]; then
export NVM_DIR="$HOME/.nvm"
# shellcheck disable=SC1091
. "$NVM_DIR/nvm.sh"
fi
if ! command -v nvm >/dev/null 2>&1; then
log_info "Installing NVM..."
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
# Source NVM for the current session after installation
export NVM_DIR="$HOME/.nvm"
# shellcheck disable=SC1091
. "$NVM_DIR/nvm.sh"
else
log_info "NVM is already installed."
fi
if ! command -v node >/dev/null 2>&1; then
log_info "Installing latest Node.js via NVM..."
nvm install node # 'node' is an alias for the latest version
nvm alias default node
nvm use default
else
log_info "Node.js is already installed. Checking for updates..."
nvm install node # This will install the latest if not already present
nvm alias default node
fi
if command -v node >/dev/null 2>&1; then
log_info "Using Node.js version: $(node --version)"
else
log_error "Node.js installation failed."
exit 1
fi
echo ""
}
########################################
# Install Yarn
########################################
install_yarn() {
if command -v yarn >/dev/null 2>&1; then
log_info "Yarn is already installed."
else
log_info "Installing Yarn..."
npm install --global yarn
fi
if command -v yarn >/dev/null 2>&1; then
yarn --version
else
log_error "Yarn installation failed."
exit 1
fi
echo ""
}
########################################
# Print Installed Versions
########################################
print_versions() {
echo "----------------------------------------"
log_info "Installation Summary"
echo "----------------------------------------"
# Ensure PATHs are set for version checking
export PATH="$HOME/.cargo/bin:$HOME/.local/share/solana/install/active_release/bin:$PATH"
export NVM_DIR="$HOME/.nvm"
# shellcheck disable=SC1091
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
echo "Rust: $(rustc --version 2>/dev/null || echo 'Not found')"
echo "Solana CLI: $(solana --version 2>/dev/null || echo 'Not found')"
echo "Anchor CLI: $(anchor --version 2>/dev/null || echo 'Not found')"
echo "Node.js: $(node --version 2>/dev/null || echo 'Not found')"
echo "Yarn: $(yarn --version 2>/dev/null || echo 'Not found')"
echo "----------------------------------------"
}
########################################
# Ensure RC files are configured
# NOTE: This only affects future terminal sessions.
########################################
ensure_shell_config() {
local shell_rc=""
case "$SHELL" in
*/zsh) shell_rc="$HOME/.zshrc" ;;
*/bash) shell_rc="$HOME/.bashrc" ;;
*) shell_rc="$HOME/.profile" ;;
esac
log_info "Ensuring shell configuration is up to date in: $shell_rc"
# NVM
if ! grep -q 'export NVM_DIR' "$shell_rc"; then
log_info "-> Adding NVM configuration to $shell_rc"
{
echo ''
echo '# Node Version Manager (NVM)'
echo 'export NVM_DIR="$HOME/.nvm"'
echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm'
echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion'
} >> "$shell_rc"
fi
# Note: rustup and solana installers typically handle their own PATH additions.
# We will just verify and add if missing for robustness.
# Rust (Cargo)
if ! grep -q 'source "$HOME/.cargo/env"' "$shell_rc" && ! grep -q '.cargo/bin' "$shell_rc"; then
log_info "-> Adding Rust (Cargo) to PATH in $shell_rc"
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> "$shell_rc"
fi
# Solana
if ! grep -q 'solana/install/active_release/bin' "$shell_rc"; then
log_info "-> Adding Solana to PATH in $shell_rc"
echo 'export PATH="$HOME/.local/share/solana/install/active_release/bin:$PATH"' >> "$shell_rc"
fi
}
########################################
# Main Execution Flow
########################################
main() {
local os
os=$(detect_os)
install_dependencies "$os"
install_rust
install_solana_cli "$os"
install_anchor_cli
install_nvm_and_node
install_yarn
ensure_shell_config
print_versions
log_info "Setup complete. Please restart your terminal or run 'source ~/${SHELL##*/}' to apply all changes."
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment