Skip to content

Instantly share code, notes, and snippets.

@jmcphers
Last active December 11, 2023 23:26
Show Gist options
  • Save jmcphers/ee24cfc5fce0a65afd2902db5173dd2f to your computer and use it in GitHub Desktop.
Save jmcphers/ee24cfc5fce0a65afd2902db5173dd2f to your computer and use it in GitHub Desktop.
Install Latest Positron Release
#!/usr/bin/env bash
#
# This script will attempt to download and install the most recent release of
# Positron from Github Releases. The script will download the release from
# Github, mount the disk image, and copy the app bundle to /Applications.
#
# Prerequisites:
#
# - The `jq` command-line tool must be installed. If it is not installed, the
# script will attempt to install it using Homebrew.
#
# - The `git` command-line tool must be installed. This is required to fetch
# the Github Personal Access Token (PAT) from the keychain.
#
# - You will need to set a Personal Access Token (Classic) with the 'repo'
# scope. This access token is needed because the Positron repository, which
# stores the releases, is private.
#
# You can create a PAT here:
#
# https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
#
# Usage:
#
# ./update-positron.sh
#
# The first time you run the script, you will be prompted for a username and
# password for 'github.com'. Leave the username blank, and enter the Github
# PAT you created above as the password. The PAT will be stored in the keychain
# and will be used for subsequent runs of the script.
# Check to see if the `jq` command exists
if ! command -v jq >/dev/null 2>&1; then
# No jq? Maybe we can install it with Homebrew. Check to see if Homebrew is
# installed.
if ! command -v brew >/dev/null 2>&1; then
# No jq and no Homebrew? We're out of options.
echo "Error: This script requires the 'jq' command-line tool."
exit 1
else
# Homebrew is installed. Let's try to install jq.
echo "Attempting to install the 'jq' command-line tool..."
brew install jq
if [ $? -ne 0 ]; then
echo "Error: Unable to install the 'jq' command-line tool. Install 'jq' manually and try again."
exit 1
fi
fi
fi
echo "Checking for updates; enter a blank username and your Github PAT as the password if prompted."
# Call `git credential` to get the Github PAT. This will prompt the user for a
# username and password if no credentials are found in the keychain.
read -r -d '' CREDS << EOM
protocol=https
host=github.com
EOM
github_credentials=$(echo "${CREDS}" | git credential fill)
github_username=$(echo "${github_credentials}" | grep username | cut -d= -f2)
github_pat=$(echo "${github_credentials}" | grep password | cut -d= -f2)
# If we didn't get a PAT, exit.
if [ -z "${github_pat}" ]; then
echo "Error: No PAT was entered. Re-run the script and supply a Github Personal Access Token (Classic) with the 'repo' scope when prompted for a password."
exit 1
fi
# Use the Github API to list all the releases.
response=$(curl -s -H "Authorization: token ${github_pat}" "https://api.github.com/repos/posit-dev/positron/releases")
if [ $? -ne 0 ]; then
# Ensure we got some data back.
echo "Error: Unable to fetch data from GitHub API."
exit 1
elif echo "${response}" | grep -q "Bad credentials"; then
# If Github indicates that the credentials are bad, reject the credentials so
# we don't try to use them again.
git credential reject <<EOF
protocol=https
host=github.com
username=${github_username}
password=${github_pat}
EOF
echo "Error: Invalid credentials. Please try again with a valid Personal Access Token."
exit 1
elif echo "${response}" | grep -q "Resource protected by organization SAML enforcement"; then
echo "Error: The posit-dev organization uses SAML enforcement, but the token you supplied isn't authorized on posit-dev. You must grant your Personal Access Token access to this organization. Go here to learn how: https://docs.github.com/articles/authenticating-to-a-github-organization-with-saml-single-sign-on/"
exit 1
fi
# Approve the credentials. This will make them available the next time we try
# to download Positron.
git credential approve <<EOF
protocol=https
host=github.com
username=${github_username}
password=${github_pat}
EOF
# Create a temporary directory and ensure it gets removed when the script exits.
# We'll use this to store the downloaded binary.
tempdir=$(mktemp -d)
trap "rm -rf $tempdir" EXIT
latest_tag=$(echo "${response}" | jq -r '.[0].tag_name')
asset_url=$(echo "${response}" | jq -r '.[0].assets[] | select(.name|match("dmg")) | .url')
filename=$(echo "${response}" | jq -r '.[0].assets[] | select(.name|match("dmg")) | .name')
destination="${tempdir}/${filename}"
echo "Latest release: ${latest_tag}"
echo "Downloading ${filename} from ${asset_url}..."
curl -H "Accept: application/octet-stream" -H "Authorization: token ${github_pat}" "${asset_url}" -L -o "${destination}"
# Ensure we got some data back.
if [ $? -ne 0 ]; then
echo "Error: Unable to download Positron from GitHub API."
exit 1
fi
echo "Installing ${filename}..."
# Compute the volume name for the disk image.
volume_name=$(basename "${filename}" .dmg)
volume_mount="/Volumes/${volume_name}"
# Attach the volume and copy the app bundle to /Applications.
hdiutil attach -quiet "${destination}"
rm -rf /Applications/Positron.app
cp -R "${volume_mount}/Positron.app" /Applications
# Detach the volume.
hdiutil detach -quiet "${volume_mount}"
echo "Successfully installed Positron ${latest_tag}."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment