Skip to content

Commit 8b4d59a

Browse files
committed
chore: add install script
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]>
1 parent 0ccc24b commit 8b4d59a

File tree

3 files changed

+351
-4
lines changed

3 files changed

+351
-4
lines changed

.github/workflows/push-install.yaml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: push-install
2+
on:
3+
push:
4+
branches:
5+
- main
6+
paths:
7+
- "scripts/install.sh"
8+
9+
jobs:
10+
push-install:
11+
name: push-install
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: checkout
15+
uses: actions/checkout@v3
16+
with:
17+
fetch-depth: 1
18+
- name: Copy files to S3
19+
env:
20+
AWS_ENDPOINT_URL: ${{ secrets.R2_ENDPOINT_URL }}
21+
AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY }}
22+
AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_KEY }}
23+
AWS_DEFAULT_REGION: auto
24+
run: |
25+
ls -la
26+
aws s3 cp scripts/install.sh s3://${{ secrets.R2_BUCKET }}/install.sh

README.md

+20-4
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,34 @@ OUTPUT:
3131
The artist with the most number of albums in the database is Iron Maiden, with a total
3232
of 21 albums.
3333
```
34-
3534
## Quick Start
3635

37-
1. Install with brew (or from [releases](https://github.com/gptscript-ai/gptscript/releases/latest))
36+
### 1. Install the latest release
37+
38+
#### Homebrew (macOS and Linux)
39+
3840
```shell
3941
brew install gptscript-ai/tap/gptscript
4042
```
41-
2. Get an API key from [OpenAI](https://platform.openai.com/api-keys).
43+
44+
#### Install Script (macOS and Linux):
45+
46+
```shell
47+
curl https://get.gptscript.ai/install.sh | sh
48+
```
49+
50+
#### Manually
51+
52+
Download and install the archive for your platform and architecture from the [release page](https://github.com/gptscript-ai/gptscript/releases).
53+
54+
### 2. Get an API key from [OpenAI](https://platform.openai.com/api-keys).
55+
4256
```shell
4357
export OPENAI_API_KEY="your-api-key"
4458
```
45-
3. Run Hello World:
59+
60+
### 3. Run Hello World
61+
4662
```shell
4763
gptscript https://gptscript.ai/echo.gpt --input "Hello, World!"
4864
```

scripts/install.sh

+305
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
#!/bin/sh
2+
set -e
3+
set -o noglob
4+
5+
# Usage:
6+
# curl ... | ENV_VAR=... sh -
7+
# or
8+
# ENV_VAR=... ./install.sh
9+
#
10+
# Example:
11+
# Installing the most recent release:
12+
# curl ... | sh -
13+
#
14+
# Environment variables:
15+
# - INSTALL_GPTSCRIPT_SKIP_DOWNLOAD
16+
# If set to true will not download gptscript hash or binary.
17+
#
18+
# - INSTALL_GPTSCRIPT_SYMLINK
19+
# If set to 'skip' will not create symlinks, 'force' will overwrite,
20+
# default will symlink if command does not exist in path.
21+
#
22+
# - INSTALL_GPTSCRIPT_BIN_DIR
23+
# Directory to install gptscript binary, links, and uninstall script to, or use
24+
# /usr/local/bin as the default
25+
#
26+
# - INSTALL_GPTSCRIPT_BIN_DIR_READ_ONLY
27+
# If set to true will not write files to INSTALL_GPTSCRIPT_BIN_DIR, forces
28+
# setting INSTALL_GPTSCRIPT_SKIP_DOWNLOAD=true
29+
30+
GITHUB_URL=https://github.com/gptscript-ai/gptscript/releases
31+
DOWNLOADER=
32+
SHA=
33+
ARCH=
34+
SUFFIX=
35+
EXT=
36+
SUDO=sudo
37+
38+
# --- helper functions for logs ---
39+
info() {
40+
echo '[INFO] ' "$@"
41+
}
42+
43+
warn() {
44+
echo '[WARN] ' "$@" >&2
45+
}
46+
47+
fatal() {
48+
echo '[ERROR] ' "$@" >&2
49+
exit 1
50+
}
51+
52+
# --- define needed environment variables ---
53+
setup_env() {
54+
# --- don't use sudo if we are already root ---
55+
if [ $(id -u) -eq 0 ]; then
56+
SUDO=
57+
fi
58+
59+
# --- use binary install directory if defined or create default ---
60+
if [ -n "${INSTALL_GPTSCRIPT_BIN_DIR}" ]; then
61+
BIN_DIR=${INSTALL_GPTSCRIPT_BIN_DIR}
62+
else
63+
# --- use /usr/local/bin if root can write to it, otherwise use /opt/bin if it exists
64+
BIN_DIR=/usr/local/bin
65+
if ! $SUDO sh -c "touch ${BIN_DIR}/gptscript-ro-test && rm -rf ${BIN_DIR}/gptscript-ro-test"; then
66+
if [ -d /opt/bin ]; then
67+
BIN_DIR=/opt/bin
68+
fi
69+
fi
70+
fi
71+
72+
# --- if bin directory is read only skip download ---
73+
if [ "${INSTALL_GPTSCRIPT_BIN_DIR_READ_ONLY}" = true ]; then
74+
INSTALL_GPTSCRIPT_SKIP_DOWNLOAD=true
75+
fi
76+
}
77+
78+
# --- check if skip download environment variable set ---
79+
can_skip_download() {
80+
if [ "${INSTALL_GPTSCRIPT_SKIP_DOWNLOAD}" != true ]; then
81+
return 1
82+
fi
83+
}
84+
85+
# --- verify an executable gptscript binary is installed ---
86+
verify_gptscript_is_executable() {
87+
if [ ! -x ${BIN_DIR}/gptscript ]; then
88+
fatal "Executable gptscript binary not found at ${BIN_DIR}/gptscript"
89+
fi
90+
}
91+
92+
# --- set arch and suffix, fatal if architecture not supported ---
93+
setup_verify_arch() {
94+
if [ -z "$ARCH" ]; then
95+
PLATFORM=$(uname)
96+
EXT=".tar.gz"
97+
98+
case $PLATFORM in
99+
Linux)
100+
PLATFORM="linux"
101+
;;
102+
Darwin)
103+
PLATFORM="macOS"
104+
ARCH=universal
105+
;;
106+
Windows)
107+
PLATFORM="windows"
108+
EXT=".zip"
109+
;;
110+
*)
111+
fatal "Unsupported platform $PLATFORM"
112+
esac
113+
fi
114+
115+
if [ -z "$ARCH" ]; then
116+
ARCH=$(uname -m)
117+
118+
case $ARCH in
119+
amd64)
120+
ARCH=amd64
121+
;;
122+
x86_64)
123+
ARCH=amd64
124+
;;
125+
arm64)
126+
ARCH=arm64
127+
;;
128+
aarch64)
129+
ARCH=arm64
130+
;;
131+
*)
132+
fatal "Unsupported architecture $ARCH"
133+
esac
134+
fi
135+
136+
SUFFIX=-${PLATFORM}-${ARCH}
137+
}
138+
139+
# --- verify existence of network downloader executable ---
140+
verify_downloader() {
141+
# Return failure if it doesn't exist or is no executable
142+
[ -x "$(command -v $1)" ] || return 1
143+
144+
# Set verified executable as our downloader program and return success
145+
DOWNLOADER=$1
146+
return 0
147+
}
148+
149+
verify_sha() {
150+
# Return failure if it doesn't exist or is no executable
151+
[ -x "$(command -v $1)" ] || return 1
152+
153+
# Set verified executable as our sha program and return success
154+
SHA=$1
155+
return 0
156+
}
157+
158+
get_sha() {
159+
if [ "${SHA}" = "shasum" ]; then
160+
$SHA -a 256 $1
161+
else
162+
$SHA $1
163+
fi
164+
}
165+
166+
# --- create temporary directory and cleanup when done ---
167+
setup_tmp() {
168+
TMP_DIR=$(mktemp -d -t gptscript-install.XXXXXXXXXX)
169+
TMP_HASH=${TMP_DIR}/gptscript.hash
170+
TMP_ARCHIVE=${TMP_DIR}/gptscript${EXT}
171+
cleanup() {
172+
code=$?
173+
set +e
174+
trap - EXIT
175+
rm -rf ${TMP_DIR}
176+
exit $code
177+
}
178+
trap cleanup INT EXIT
179+
}
180+
181+
# --- use desired latest gptscript version if defined or find version from channel ---
182+
get_release_version() {
183+
info "Finding latest release"
184+
version_url="${GITHUB_URL}/latest"
185+
case $DOWNLOADER in
186+
curl)
187+
VERSION_GPTSCRIPT=$(curl -w '%{url_effective}' -L -s -S ${version_url} -o /dev/null | sed -e 's|.*/||')
188+
;;
189+
wget)
190+
VERSION_GPTSCRIPT=$(wget -SqO /dev/null ${version_url} 2>&1 | grep -i Location | sed -e 's|.*/||')
191+
;;
192+
*)
193+
fatal "Incorrect downloader executable '$DOWNLOADER'"
194+
;;
195+
esac
196+
info "Using ${VERSION_GPTSCRIPT} as release"
197+
}
198+
199+
# --- download from github url ---
200+
download() {
201+
[ $# -eq 2 ] || fatal 'download needs exactly 2 arguments'
202+
203+
case $DOWNLOADER in
204+
curl)
205+
curl -o $1 -sfL $2
206+
;;
207+
wget)
208+
wget -qO $1 $2
209+
;;
210+
*)
211+
fatal "Incorrect executable '$DOWNLOADER'"
212+
;;
213+
esac
214+
215+
# Abort if download command failed
216+
[ $? -eq 0 ] || fatal 'Download failed'
217+
}
218+
219+
# --- download hash from github url ---
220+
download_hash() {
221+
HASH_URL=${GITHUB_URL}/download/${VERSION_GPTSCRIPT}/checksums.txt
222+
info "Downloading hash ${HASH_URL}"
223+
download ${TMP_HASH} ${HASH_URL}
224+
HASH_EXPECTED=$(grep " gptscript-${VERSION_GPTSCRIPT}${SUFFIX}${EXT}" ${TMP_HASH})
225+
HASH_EXPECTED=${HASH_EXPECTED%%[[:blank:]]*}
226+
}
227+
228+
# --- check hash against installed version ---
229+
installed_hash_matches() {
230+
if [ -x ${BIN_DIR}/gptscript ]; then
231+
HASH_INSTALLED=$(get_sha "${BIN_DIR}/gptscript")
232+
HASH_INSTALLED=${HASH_INSTALLED%%[[:blank:]]*}
233+
if [ "${HASH_EXPECTED}" = "${HASH_INSTALLED}" ]; then
234+
return
235+
fi
236+
fi
237+
return 1
238+
}
239+
240+
# --- download archive from github url ---
241+
download_archive() {
242+
ARCHIVE_URL=${GITHUB_URL}/download/${VERSION_GPTSCRIPT}/gptscript-${VERSION_GPTSCRIPT}${SUFFIX}${EXT}
243+
info "Downloading archive ${ARCHIVE_URL}"
244+
download ${TMP_ARCHIVE} ${ARCHIVE_URL}
245+
}
246+
247+
# --- verify downloaded archive hash ---
248+
verify_archive() {
249+
info "Verifying binary download"
250+
HASH_BIN=$(get_sha $TMP_ARCHIVE)
251+
HASH_BIN=${HASH_BIN%%[[:blank:]]*}
252+
if [ "${HASH_EXPECTED}" != "${HASH_BIN}" ]; then
253+
fatal "Download sha256 does not match ${HASH_EXPECTED}, got ${HASH_BIN}"
254+
fi
255+
}
256+
257+
expand_archive() {
258+
if [ "${EXT}" = ".zip" ]; then
259+
unzip ${TMP_ARCHIVE} -d ${TMP_DIR}
260+
else
261+
tar xzf ${TMP_ARCHIVE} -C ${TMP_DIR}
262+
fi
263+
264+
TMP_BIN=${TMP_DIR}/gptscript
265+
}
266+
267+
# --- setup permissions and move binary to system directory ---
268+
setup_binary() {
269+
chmod 755 ${TMP_BIN}
270+
info "Installing gptscript to ${BIN_DIR}/gptscript"
271+
$SUDO chown root ${TMP_BIN}
272+
$SUDO mv -f ${TMP_BIN} ${BIN_DIR}/gptscript
273+
}
274+
275+
# --- download and verify gptscript ---
276+
download_and_verify() {
277+
if can_skip_download; then
278+
info 'Skipping gptscript download and verify'
279+
verify_gptscript_is_executable
280+
return
281+
fi
282+
283+
setup_verify_arch
284+
verify_downloader curl || verify_downloader wget || fatal 'Can not find curl or wget for downloading files'
285+
verify_sha sha256sum || verify_sha shasum || fatal 'Can not find sha256sum or shasum for verifying files'
286+
setup_tmp
287+
get_release_version
288+
download_hash
289+
290+
if installed_hash_matches; then
291+
info 'Skipping binary download, installed gptscript matches hash'
292+
return
293+
fi
294+
295+
download_archive
296+
verify_archive
297+
expand_archive
298+
setup_binary
299+
}
300+
301+
# --- run the install process --
302+
{
303+
setup_env "$@"
304+
download_and_verify
305+
}

0 commit comments

Comments
 (0)