Skip to content

Commit

Permalink
chore: add install script
Browse files Browse the repository at this point in the history
Add a cross-platform install script that installs the latest release of
`gptscript`. Also add a github action that publishes that script to
R2, making it publicly available at `https://get.gptscript.ai/install.sh`.

Signed-off-by: Nick Hale <[email protected]>
  • Loading branch information
njhale committed Feb 13, 2024
1 parent 0ccc24b commit 8b4d59a
Show file tree
Hide file tree
Showing 3 changed files with 351 additions and 4 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/push-install.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: push-install
on:
push:
branches:
- main
paths:
- "scripts/install.sh"

jobs:
push-install:
name: push-install
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Copy files to S3
env:
AWS_ENDPOINT_URL: ${{ secrets.R2_ENDPOINT_URL }}
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_KEY }}
AWS_DEFAULT_REGION: auto
run: |
ls -la
aws s3 cp scripts/install.sh s3://${{ secrets.R2_BUCKET }}/install.sh
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,34 @@ OUTPUT:
The artist with the most number of albums in the database is Iron Maiden, with a total
of 21 albums.
```

## Quick Start

1. Install with brew (or from [releases](https://github.com/gptscript-ai/gptscript/releases/latest))
### 1. Install the latest release

#### Homebrew (macOS and Linux)

```shell
brew install gptscript-ai/tap/gptscript
```
2. Get an API key from [OpenAI](https://platform.openai.com/api-keys).

#### Install Script (macOS and Linux):

```shell
curl https://get.gptscript.ai/install.sh | sh
```

#### Manually

Download and install the archive for your platform and architecture from the [release page](https://github.com/gptscript-ai/gptscript/releases).

### 2. Get an API key from [OpenAI](https://platform.openai.com/api-keys).

```shell
export OPENAI_API_KEY="your-api-key"
```
3. Run Hello World:

### 3. Run Hello World

```shell
gptscript https://gptscript.ai/echo.gpt --input "Hello, World!"
```
Expand Down
305 changes: 305 additions & 0 deletions scripts/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
#!/bin/sh
set -e
set -o noglob

# Usage:
# curl ... | ENV_VAR=... sh -
# or
# ENV_VAR=... ./install.sh
#
# Example:
# Installing the most recent release:
# curl ... | sh -
#
# Environment variables:
# - INSTALL_GPTSCRIPT_SKIP_DOWNLOAD
# If set to true will not download gptscript hash or binary.
#
# - INSTALL_GPTSCRIPT_SYMLINK
# If set to 'skip' will not create symlinks, 'force' will overwrite,
# default will symlink if command does not exist in path.
#
# - INSTALL_GPTSCRIPT_BIN_DIR
# Directory to install gptscript binary, links, and uninstall script to, or use
# /usr/local/bin as the default
#
# - INSTALL_GPTSCRIPT_BIN_DIR_READ_ONLY
# If set to true will not write files to INSTALL_GPTSCRIPT_BIN_DIR, forces
# setting INSTALL_GPTSCRIPT_SKIP_DOWNLOAD=true

GITHUB_URL=https://github.com/gptscript-ai/gptscript/releases
DOWNLOADER=
SHA=
ARCH=
SUFFIX=
EXT=
SUDO=sudo

# --- helper functions for logs ---
info() {
echo '[INFO] ' "$@"
}

warn() {
echo '[WARN] ' "$@" >&2
}

fatal() {
echo '[ERROR] ' "$@" >&2
exit 1
}

# --- define needed environment variables ---
setup_env() {
# --- don't use sudo if we are already root ---
if [ $(id -u) -eq 0 ]; then
SUDO=
fi

# --- use binary install directory if defined or create default ---
if [ -n "${INSTALL_GPTSCRIPT_BIN_DIR}" ]; then
BIN_DIR=${INSTALL_GPTSCRIPT_BIN_DIR}
else
# --- use /usr/local/bin if root can write to it, otherwise use /opt/bin if it exists
BIN_DIR=/usr/local/bin
if ! $SUDO sh -c "touch ${BIN_DIR}/gptscript-ro-test && rm -rf ${BIN_DIR}/gptscript-ro-test"; then
if [ -d /opt/bin ]; then
BIN_DIR=/opt/bin
fi
fi
fi

# --- if bin directory is read only skip download ---
if [ "${INSTALL_GPTSCRIPT_BIN_DIR_READ_ONLY}" = true ]; then
INSTALL_GPTSCRIPT_SKIP_DOWNLOAD=true
fi
}

# --- check if skip download environment variable set ---
can_skip_download() {
if [ "${INSTALL_GPTSCRIPT_SKIP_DOWNLOAD}" != true ]; then
return 1
fi
}

# --- verify an executable gptscript binary is installed ---
verify_gptscript_is_executable() {
if [ ! -x ${BIN_DIR}/gptscript ]; then
fatal "Executable gptscript binary not found at ${BIN_DIR}/gptscript"
fi
}

# --- set arch and suffix, fatal if architecture not supported ---
setup_verify_arch() {
if [ -z "$ARCH" ]; then
PLATFORM=$(uname)
EXT=".tar.gz"

case $PLATFORM in
Linux)
PLATFORM="linux"
;;
Darwin)
PLATFORM="macOS"
ARCH=universal
;;
Windows)
PLATFORM="windows"
EXT=".zip"
;;
*)
fatal "Unsupported platform $PLATFORM"
esac
fi

if [ -z "$ARCH" ]; then
ARCH=$(uname -m)

case $ARCH in
amd64)
ARCH=amd64
;;
x86_64)
ARCH=amd64
;;
arm64)
ARCH=arm64
;;
aarch64)
ARCH=arm64
;;
*)
fatal "Unsupported architecture $ARCH"
esac
fi

SUFFIX=-${PLATFORM}-${ARCH}
}

# --- verify existence of network downloader executable ---
verify_downloader() {
# Return failure if it doesn't exist or is no executable
[ -x "$(command -v $1)" ] || return 1

# Set verified executable as our downloader program and return success
DOWNLOADER=$1
return 0
}

verify_sha() {
# Return failure if it doesn't exist or is no executable
[ -x "$(command -v $1)" ] || return 1

# Set verified executable as our sha program and return success
SHA=$1
return 0
}

get_sha() {
if [ "${SHA}" = "shasum" ]; then
$SHA -a 256 $1
else
$SHA $1
fi
}

# --- create temporary directory and cleanup when done ---
setup_tmp() {
TMP_DIR=$(mktemp -d -t gptscript-install.XXXXXXXXXX)
TMP_HASH=${TMP_DIR}/gptscript.hash
TMP_ARCHIVE=${TMP_DIR}/gptscript${EXT}
cleanup() {
code=$?
set +e
trap - EXIT
rm -rf ${TMP_DIR}
exit $code
}
trap cleanup INT EXIT
}

# --- use desired latest gptscript version if defined or find version from channel ---
get_release_version() {
info "Finding latest release"
version_url="${GITHUB_URL}/latest"
case $DOWNLOADER in
curl)
VERSION_GPTSCRIPT=$(curl -w '%{url_effective}' -L -s -S ${version_url} -o /dev/null | sed -e 's|.*/||')
;;
wget)
VERSION_GPTSCRIPT=$(wget -SqO /dev/null ${version_url} 2>&1 | grep -i Location | sed -e 's|.*/||')
;;
*)
fatal "Incorrect downloader executable '$DOWNLOADER'"
;;
esac
info "Using ${VERSION_GPTSCRIPT} as release"
}

# --- download from github url ---
download() {
[ $# -eq 2 ] || fatal 'download needs exactly 2 arguments'

case $DOWNLOADER in
curl)
curl -o $1 -sfL $2
;;
wget)
wget -qO $1 $2
;;
*)
fatal "Incorrect executable '$DOWNLOADER'"
;;
esac

# Abort if download command failed
[ $? -eq 0 ] || fatal 'Download failed'
}

# --- download hash from github url ---
download_hash() {
HASH_URL=${GITHUB_URL}/download/${VERSION_GPTSCRIPT}/checksums.txt
info "Downloading hash ${HASH_URL}"
download ${TMP_HASH} ${HASH_URL}
HASH_EXPECTED=$(grep " gptscript-${VERSION_GPTSCRIPT}${SUFFIX}${EXT}" ${TMP_HASH})
HASH_EXPECTED=${HASH_EXPECTED%%[[:blank:]]*}
}

# --- check hash against installed version ---
installed_hash_matches() {
if [ -x ${BIN_DIR}/gptscript ]; then
HASH_INSTALLED=$(get_sha "${BIN_DIR}/gptscript")
HASH_INSTALLED=${HASH_INSTALLED%%[[:blank:]]*}
if [ "${HASH_EXPECTED}" = "${HASH_INSTALLED}" ]; then
return
fi
fi
return 1
}

# --- download archive from github url ---
download_archive() {
ARCHIVE_URL=${GITHUB_URL}/download/${VERSION_GPTSCRIPT}/gptscript-${VERSION_GPTSCRIPT}${SUFFIX}${EXT}
info "Downloading archive ${ARCHIVE_URL}"
download ${TMP_ARCHIVE} ${ARCHIVE_URL}
}

# --- verify downloaded archive hash ---
verify_archive() {
info "Verifying binary download"
HASH_BIN=$(get_sha $TMP_ARCHIVE)
HASH_BIN=${HASH_BIN%%[[:blank:]]*}
if [ "${HASH_EXPECTED}" != "${HASH_BIN}" ]; then
fatal "Download sha256 does not match ${HASH_EXPECTED}, got ${HASH_BIN}"
fi
}

expand_archive() {
if [ "${EXT}" = ".zip" ]; then
unzip ${TMP_ARCHIVE} -d ${TMP_DIR}
else
tar xzf ${TMP_ARCHIVE} -C ${TMP_DIR}
fi

TMP_BIN=${TMP_DIR}/gptscript
}

# --- setup permissions and move binary to system directory ---
setup_binary() {
chmod 755 ${TMP_BIN}
info "Installing gptscript to ${BIN_DIR}/gptscript"
$SUDO chown root ${TMP_BIN}
$SUDO mv -f ${TMP_BIN} ${BIN_DIR}/gptscript
}

# --- download and verify gptscript ---
download_and_verify() {
if can_skip_download; then
info 'Skipping gptscript download and verify'
verify_gptscript_is_executable
return
fi

setup_verify_arch
verify_downloader curl || verify_downloader wget || fatal 'Can not find curl or wget for downloading files'
verify_sha sha256sum || verify_sha shasum || fatal 'Can not find sha256sum or shasum for verifying files'
setup_tmp
get_release_version
download_hash

if installed_hash_matches; then
info 'Skipping binary download, installed gptscript matches hash'
return
fi

download_archive
verify_archive
expand_archive
setup_binary
}

# --- run the install process --
{
setup_env "$@"
download_and_verify
}

0 comments on commit 8b4d59a

Please sign in to comment.