Skip to content

Instantly share code, notes, and snippets.

@gadenbuie
Forked from wch/update-rstudio
Last active March 5, 2026 01:53
Show Gist options
  • Select an option

  • Save gadenbuie/7bb14b245a45e08173e54bc75ec38790 to your computer and use it in GitHub Desktop.

Select an option

Save gadenbuie/7bb14b245a45e08173e54bc75ec38790 to your computer and use it in GitHub Desktop.
Script for updating to latest RStudio daily build
#!/bin/bash
#
# Installs the latest RStudio daily desktop build for OSX/macOS and Ubuntu(amd64)
#
# https://support.rstudio.com/hc/en-us/articles/203842428-Getting-the-newest-RStudio-builds
#
# Source: https://gist.github.com/gadenbuie/7bb14b245a45e08173e54bc75ec38790
set -e
# ==============================================================================
# Color and formatting setup (respects NO_COLOR)
# ==============================================================================
setup_colors() {
if [[ -z "${NO_COLOR:-}" && -t 1 ]]; then
BOLD='\033[1m'
DIM='\033[2m'
RESET='\033[0m'
GREEN='\033[32m'
BLUE='\033[34m'
YELLOW='\033[33m'
CYAN='\033[36m'
RED='\033[31m'
else
BOLD=''
DIM=''
RESET=''
GREEN=''
BLUE=''
YELLOW=''
CYAN=''
RED=''
fi
}
setup_colors
# ==============================================================================
# Output helpers
# ==============================================================================
header() {
echo ""
echo -e "${BOLD}${BLUE}▶ ${1}${RESET}"
}
info() {
echo -e " ${DIM}ℹ${RESET} ${1}"
}
success() {
echo -e " ${GREEN}✔${RESET} ${1}"
}
warn() {
echo -e " ${YELLOW}!${RESET} ${1}"
}
error() {
echo -e " ${RED}✘${RESET} ${1}"
}
step() {
echo -e " ${CYAN}●${RESET} ${1}"
}
clear_last_line() {
if [[ -z "${NO_COLOR:-}" && -t 1 ]]; then
echo -en "\033[A\033[2K\r"
fi
}
step_done() {
clear_last_line
success "$1"
}
step_fail() {
clear_last_line
error "$1"
}
run_cmd() {
if [[ "$VERBOSE" == true ]]; then
"$@"
else
"$@" > /dev/null 2>&1
fi
}
# ==============================================================================
# Help
# ==============================================================================
help() {
echo -e "${BOLD}Usage:${RESET} update-rstudio-daily [command] [options]"
echo ""
echo "Installs the latest RStudio daily or hourly desktop build."
echo ""
echo -e "${BOLD}Commands:${RESET}"
echo " install Install the latest build (default)"
echo " info Show available builds for your platform"
echo ""
echo -e "${BOLD}Options:${RESET}"
echo " --hourly Use hourly builds instead of daily"
echo " -v, --verbose Show command output"
echo " -h, --help Show this help message"
echo ""
echo -e "${BOLD}Supported platforms:${RESET}"
echo " - macOS"
echo " - Ubuntu Linux (jammy/22.04)"
}
# ==============================================================================
# Parse arguments
# ==============================================================================
VERBOSE=false
HOURLY=false
COMMAND="install"
while [[ $# -gt 0 ]]; do
case $1 in
info|install)
COMMAND="$1"
shift
;;
--hourly)
HOURLY=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-h|--help)
help
exit 0
;;
-*)
warn "Unknown option: $1"
echo ""
help
exit 1
;;
*)
warn "Unexpected argument: $1"
echo ""
help
exit 1
;;
esac
done
# ==============================================================================
# Build index helpers
# ==============================================================================
DAILY_INDEX_URL="https://dailies.rstudio.com/rstudio/latest/index.json"
# Get the branch slug (e.g., "Globemaster Allium" -> "globemaster-allium")
get_branch_slug() {
local json=$1
echo "$json" | jq -r '.branch' | tr '[:upper:]' '[:lower:]' | tr ' ' '-'
}
# Fetch the build index JSON (daily or hourly)
fetch_index_json() {
local daily_json
daily_json=$(curl -s "${DAILY_INDEX_URL}")
if [[ "$HOURLY" == true ]]; then
local slug
slug=$(get_branch_slug "$daily_json")
curl -s "https://dailies.rstudio.com/rstudio-hourly/${slug}/index.json"
else
echo "$daily_json"
fi
}
# Extract platform info from index JSON
# Daily structure: .products.electron.platforms.<key>
# Hourly structure: .electron.platforms.<key>
get_platform_info() {
local json=$1
local platform_key=$2
if [[ "$HOURLY" == true ]]; then
echo "$json" | jq -r ".electron.platforms.${platform_key}"
else
echo "$json" | jq -r ".products.electron.platforms.${platform_key}"
fi
}
# List all electron platform keys from the index JSON
get_platform_keys() {
local json=$1
if [[ "$HOURLY" == true ]]; then
echo "$json" | jq -r '.electron.platforms // {} | keys[]'
else
echo "$json" | jq -r '.products.electron.platforms // {} | keys[]'
fi
}
build_label() {
if [[ "$HOURLY" == true ]]; then
echo "hourly"
else
echo "daily"
fi
}
# ==============================================================================
# List available builds
# ==============================================================================
list_builds() {
local label
label=$(build_label)
header "Fetching RStudio ${label} build info"
step "Checking available builds..."
local json
json=$(fetch_index_json)
step_done "Fetched build index"
local platform_filter
if [[ $(uname -s) = "Darwin" ]]; then
platform_filter="macos"
elif [[ -f /etc/issue ]] && grep -q Ubuntu /etc/issue; then
platform_filter="jammy\|noble"
else
error "Unsupported platform"
exit 1
fi
echo ""
printf " ${BOLD}%-24s %-28s %-12s %s${RESET}\n" "PLATFORM" "VERSION" "DATE" "ARCH"
local keys
keys=$(get_platform_keys "$json")
local found=false
while IFS= read -r key; do
# Skip xcopy (installer-less) variants
[[ "$key" == *-xcopy ]] && continue
# Filter to current OS platforms
echo "$key" | grep -q "$platform_filter" || continue
local info
info=$(get_platform_info "$json" "$key")
local version date arch
version=$(echo "$info" | jq -r '.version // "-"')
date=$(echo "$info" | jq -r '.date // "-"' | cut -d' ' -f1)
arch=$(echo "$info" | jq -r '.architecture // "-"')
printf " %-24s %-28s %-12s %s\n" "$key" "$version" "$date" "$arch"
found=true
done <<< "$keys"
if [[ "$found" == false ]]; then
warn "No builds found for your platform"
fi
echo ""
}
# ==============================================================================
# RStudio process management
# ==============================================================================
# Check if RStudio is currently running
is_rstudio_running() {
local platform=$1
if [ "$platform" = "macos" ]; then
pgrep -x "RStudio" > /dev/null
else
pgrep -i rstudio > /dev/null
fi
}
# Send quit signal to RStudio
quit_rstudio() {
local platform=$1
if [ "$platform" = "macos" ]; then
osascript -e 'quit app "RStudio"' > /dev/null 2>&1
else
pkill -TERM -i rstudio
fi
}
# Wait for RStudio process to end
wait_for_rstudio_quit() {
local platform=$1
local max_wait=30
for _ in $(seq 1 $max_wait); do
if ! is_rstudio_running "$platform"; then
return 0
fi
sleep 1
done
return 1
}
# Main function to ensure RStudio is not running before update
ensure_rstudio_not_running() {
local platform=$1
if ! is_rstudio_running "$platform"; then
return 0
fi
echo ""
warn "RStudio is currently running"
echo -en " Do you want to quit RStudio to proceed? ${DIM}(y/n)${RESET} "
read -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
info "Update cancelled"
exit 0
fi
step "Quitting RStudio gracefully..."
quit_rstudio "$platform"
if wait_for_rstudio_quit "$platform"; then
step_done "RStudio has quit"
else
step_fail "RStudio is still running (may have unsaved work)"
info "Please quit RStudio manually and run this script again"
exit 1
fi
}
# ==============================================================================
# macOS installation
# ==============================================================================
install_macos_daily() {
local label
label=$(build_label)
header "Fetching RStudio ${label} build info"
info "Source: dailies.rstudio.com"
step "Checking latest version"
local index_json
index_json=$(fetch_index_json)
local platform_info
platform_info=$(get_platform_info "$index_json" "macos")
RELEASE_URL=$(echo "$platform_info" | jq -r '.link')
if [ "${RELEASE_URL}" == "" ] || [ "${RELEASE_URL}" == "null" ]; then
step_fail "Could not extract ${label} build URL from JSON"
exit 1
fi
step_done "Found latest build"
cd /tmp
TARGET=$(basename "${RELEASE_URL}")
# Volume name mirrors the DMG filename without extension.
VOLUME_NAME=$(basename "${TARGET}" .dmg)
VOLUME_MOUNT="/Volumes/${VOLUME_NAME}"
header "Downloading ${VOLUME_NAME}"
info "URL: ${RELEASE_URL}"
step "Downloading DMG..."
if [[ "$VERBOSE" == true ]]; then
curl -L -o "${TARGET}" "${RELEASE_URL}"
else
curl -sL -o "${TARGET}" "${RELEASE_URL}"
fi
step_done "Download complete"
header "Installing RStudio"
ensure_rstudio_not_running "macos"
step "Mounting disk image"
run_cmd hdiutil attach -quiet "${TARGET}"
step_done "Mounted"
step "Removing previous installation"
rm -rf /Applications/RStudio.app
step_done "Removed old version"
step "Copying to /Applications"
run_cmd cp -R "${VOLUME_MOUNT}/RStudio.app" /Applications
step_done "Installed"
step "Cleaning up"
run_cmd hdiutil detach -quiet "${VOLUME_MOUNT}"
rm "${TARGET}"
step_done "Cleaned up"
header "Done"
success "Installed ${VOLUME_NAME} to /Applications"
echo ""
}
# ==============================================================================
# Ubuntu installation
# ==============================================================================
install_ubuntu_daily() {
local label
label=$(build_label)
header "Fetching RStudio ${label} build info"
info "Source: dailies.rstudio.com"
step "Checking latest version"
local index_json
index_json=$(fetch_index_json)
local platform_info
platform_info=$(get_platform_info "$index_json" "jammy")
URL=$(echo "$platform_info" | jq -r '.link')
if [ "${URL}" == "" ] || [ "${URL}" == "null" ]; then
step_fail "Could not extract ${label} build URL from JSON"
exit 1
fi
step_done "Found latest build"
PACKAGE=$(basename "${URL}")
TARGET="/tmp/${PACKAGE}"
# If previous file exists (from previous partial download, for example),
# remove it.
if [[ -f "${TARGET}" ]]; then
step "Removing existing package file"
rm "${TARGET}"
fi
header "Downloading ${PACKAGE}"
info "URL: ${URL}"
step "Downloading package..."
if [ -x /usr/bin/curl ] ; then
if [[ "$VERBOSE" == true ]]; then
curl -L -o "${TARGET}" "${URL}"
else
curl -sL -o "${TARGET}" "${URL}"
fi
elif [ -x /usr/bin/wget ] ; then
if [[ "$VERBOSE" == true ]]; then
wget -O "${TARGET}" "${URL}"
else
wget -q -O "${TARGET}" "${URL}"
fi
else
step_fail "Cannot find 'curl' or 'wget'"
exit 1
fi
step_done "Download complete"
header "Installing RStudio"
ensure_rstudio_not_running "ubuntu"
LAUNCH=""
if [[ $(whoami) != "root" ]]; then
LAUNCH="sudo"
info "Sudo required for installation"
fi
step "Installing package with dpkg"
if [[ "$VERBOSE" == true ]]; then
${LAUNCH} dpkg -i "${TARGET}"
else
${LAUNCH} dpkg -i "${TARGET}" > /dev/null 2>&1
fi
step_done "Installed"
step "Cleaning up"
rm "${TARGET}"
step_done "Cleaned up"
header "Done"
success "Installed ${PACKAGE}"
echo ""
}
# ==============================================================================
# Main
# ==============================================================================
if [[ "$COMMAND" == "info" ]]; then
list_builds
elif [[ $(uname -s) = "Darwin" ]]; then
install_macos_daily
elif [[ -f /etc/issue ]] && grep -q Ubuntu /etc/issue ; then
install_ubuntu_daily
else
error "Unsupported platform"
info "This script only works on macOS and Ubuntu Linux"
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment