Skip to content

Commit 9e8229c

Browse files
committed
feat: add first draft of CLI install script
1 parent 878c6cf commit 9e8229c

File tree

2 files changed

+365
-0
lines changed

2 files changed

+365
-0
lines changed

.github/workflows/build-cli-binaries.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# SPDX-License-Identifier: Apache-2.0
16+
117
name: Build CLI Binaries
218

319
on:

bin/install_cli

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
#!/usr/bin/env bash
2+
# Copyright 2025 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
# SPDX-License-Identifier: Apache-2.0
17+
18+
19+
## <script src="./readability.js"></script>
20+
## <link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/themes/prism-okaidia.min.css" rel="stylesheet" />
21+
## <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/components/prism-core.min.js" data-manual></script>
22+
## <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.16.0/components/prism-bash.min.js"></script>
23+
## <style>body {color: #272822; background-color: #272822; font-size: 0.8em;} </style>
24+
25+
# Configuration variables
26+
DOMAIN="genkit.tools"
27+
TRACKING_ID="UA-XXXXXXXXX-X" # Not used when analytics is commented out
28+
29+
: ==========================================
30+
: Introduction
31+
: ==========================================
32+
33+
# This script allows you to install the latest version of the
34+
# "genkit" command by running:
35+
#
36+
: curl -sL $DOMAIN | bash
37+
#
38+
# If you do not want to use this script, you can manually
39+
# download the latest "genkit" binary.
40+
#
41+
: curl -Lo ./genkit_bin https://$DOMAIN/bin/linux/latest
42+
#
43+
# Alternatively, you can download a specific version.
44+
#
45+
: curl -Lo ./genkit_bin https://$DOMAIN/bin/linux/v1.12.0
46+
#
47+
# Note: On Mac, replace "linux" with "macos" in the URL.
48+
#
49+
# For full details about installation options for the Genkit CLI
50+
# please see our documentation.
51+
# https://firebase.google.com/docs/genkit/
52+
#
53+
# Please report bugs / issues with this script on Github.
54+
# https://github.com/firebase/genkit
55+
#
56+
57+
: ==========================================
58+
: Advanced Usage
59+
: ==========================================
60+
61+
# The behavior of this script can be modified at runtime by passing environmental
62+
# variables to the `bash` process.
63+
#
64+
# For example, passing an argument called arg1 set to true and one called arg2 set
65+
# to false would look like this.
66+
#
67+
: curl -sL $DOMAIN | arg1=true arg2=false bash
68+
#
69+
# These arguments are optional, but be aware that explicitly setting them will help
70+
# ensure consistent behavior if / when defaults are changed.
71+
#
72+
73+
: -----------------------------------------
74+
: Upgrading - default: false
75+
: -----------------------------------------
76+
77+
# By default, this script will not replace an existing "genkit" install.
78+
# If you'd like to upgrade an existing install, set the "upgrade" variable to true.
79+
#
80+
: curl -sL $DOMAIN | upgrade=true bash
81+
#
82+
# This operation could (potentially) break an existing install, so use it with caution.
83+
#
84+
85+
: -----------------------------------------
86+
: Uninstalling - default false
87+
: -----------------------------------------
88+
89+
# You can remove the binary by passing the "uninstall" flag.
90+
#
91+
: curl -sL $DOMAIN | uninstall=true bash
92+
#
93+
# This will remove the binary file and any cached data.
94+
#
95+
96+
: -----------------------------------------
97+
: Analytics - default true
98+
: -----------------------------------------
99+
100+
# This script reports anonymous success / failure analytics.
101+
# You can disable this reporting by setting the "analytics" variable to false.
102+
#
103+
: curl -sL $DOMAIN | analytics=false bash
104+
#
105+
# By default we report all data anonymously and do not collect any information
106+
# except platform type (Darwin, Win, etc) in the case of an unsupported platform
107+
# error.
108+
#
109+
110+
: ==========================================
111+
: Source Code
112+
: ==========================================
113+
114+
# This script contains a large amount of comments so you can understand
115+
# how it interacts with your system. If you're not interested in the
116+
# technical details, you can just run the command above.
117+
118+
# We begin by generating a unique ID for tracking the anonymous session.
119+
CID=$(head -80 /dev/urandom | LC_ALL=c tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
120+
# Credit: https://gist.github.com/earthgecko/3089509
121+
122+
# We can use this CID in all calls to the Google Analytics endpoint via
123+
# this reusable function.
124+
send_analytics_event()
125+
{
126+
# Analytics tracking is currently disabled
127+
# Uncomment the block below to enable analytics
128+
129+
# if [ ! "$analytics" = "false" ]; then
130+
# curl -s https://www.google-analytics.com/collect \
131+
# -d "tid=$TRACKING_ID" \
132+
# -d "t=event" \
133+
# -d "ec=$DOMAIN" \
134+
# -d "ea=$1" \
135+
# -d "v=1" \
136+
# -d "cid=$CID" \
137+
# -o /dev/null
138+
# fi
139+
140+
# For now, just return success
141+
return 0
142+
}
143+
144+
# We send one event to count the number of times this script is ran. At the
145+
# end we also report success / failure, but it's possible that the script
146+
# will crash before we get to that point, so we manually count invocations here.
147+
send_analytics_event start
148+
149+
# We try to detect any existing binaries on $PATH or two common locations.
150+
GENKIT_BINARY=${GENKIT_BINARY:-$(which genkit)}
151+
LOCAL_BINARY="$HOME/.local/bin/genkit"
152+
# For info about why we place the binary at this location, see
153+
# https://unix.stackexchange.com/a/8658
154+
GLOBAL_BINARY="/usr/local/bin/genkit"
155+
if [[ -z "$GENKIT_BINARY" ]]; then
156+
if [ -e "$LOCAL_BINARY" ]; then
157+
GENKIT_BINARY="$LOCAL_BINARY"
158+
elif [ -e "$GLOBAL_BINARY" ]; then
159+
GENKIT_BINARY="$GLOBAL_BINARY"
160+
fi
161+
fi
162+
163+
# If the user asked for us to uninstall genkit, then do so.
164+
if [ "$uninstall" = "true" ]; then
165+
if [[ -z "$GENKIT_BINARY" ]]; then
166+
echo "Cannot detect any Genkit CLI installations."
167+
echo "Please manually remove any \"genkit\" binaries not in \$PATH."
168+
else
169+
# Assuming binary install, skip npm check
170+
echo "-- Removing binary file..."
171+
sudo rm -- "$GENKIT_BINARY"
172+
fi
173+
echo "-- Removing genkit cache..."
174+
rm -rf ~/.cache/genkit
175+
176+
echo "-- genkit has been uninstalled"
177+
echo "-- All Done!"
178+
179+
send_analytics_event uninstall
180+
exit 0
181+
fi
182+
183+
# We need to ensure that we don't mess up an existing "genkit"
184+
# install, so before doing anything we check to see if this system
185+
# has "genkit" installed and if so, we exit out.
186+
echo "-- Checking for existing genkit installation..."
187+
188+
if [[ ! -z "$GENKIT_BINARY" ]]; then
189+
INSTALLED_GENKIT_VERSION=$("$GENKIT_BINARY" --version)
190+
191+
# In the case of a corrupt genkit install, we wont be able to
192+
# retrieve a version number, so to keep the logs correct, we refer to
193+
# your existing install as either the CLI version or as a "corrupt install"
194+
if [[ ! -z "$INSTALLED_GENKIT_VERSION" ]]; then
195+
GENKIT_NICKNAME="genkit@$INSTALLED_GENKIT_VERSION"
196+
else
197+
GENKIT_NICKNAME="a corrupted genkit binary"
198+
fi
199+
200+
# Skip npm check - assume binary install
201+
# If the user didn't pass upgrade=true, then we print the command to do an upgrade and exit
202+
if [ ! "$upgrade" = "true" ]; then
203+
echo "Your machine has $GENKIT_NICKNAME installed."
204+
echo "If you would like to upgrade your install run: curl -sL $DOMAIN | upgrade=true bash"
205+
206+
send_analytics_event already_installed
207+
exit 0
208+
else
209+
# If the user did pass upgrade=true, then we allow the script to continue and overwrite the install.
210+
echo "-- Your machine has $GENKIT_NICKNAME, attempting upgrade..."
211+
212+
send_analytics_event upgrade
213+
fi
214+
fi
215+
216+
echo "-- Checking your machine type..."
217+
218+
# Now we need to detect the platform we're running on (Linux / Mac / Other)
219+
# so we can fetch the correct binary and place it in the correct location
220+
# on the machine.
221+
222+
# We use "tr" to translate the uppercase "uname" output into lowercase
223+
UNAME=$(uname -s | tr '[:upper:]' '[:lower:]')
224+
225+
# Detect architecture
226+
ARCH=$(uname -m)
227+
case "$ARCH" in
228+
x86_64) ARCH_SUFFIX="x64";;
229+
aarch64|arm64) ARCH_SUFFIX="arm64";;
230+
*) ARCH_SUFFIX="x64";; # Default to x64
231+
esac
232+
233+
# Then we map the output to the names used on the Github releases page
234+
case "$UNAME" in
235+
linux*) MACHINE="linux-${ARCH_SUFFIX}";;
236+
darwin*) MACHINE="darwin-${ARCH_SUFFIX}";;
237+
mingw*|msys*|cygwin*) MACHINE="win32-x64";;
238+
esac
239+
240+
# If we never define the $MACHINE variable (because our platform is neither Mac,
241+
# Linux, or Windows), then we can't finish our job, so just log out a helpful message
242+
# and close.
243+
if [[ -z "$MACHINE" ]]; then
244+
echo "Your operating system is not supported, if you think it should be please file a bug."
245+
echo "https://github.com/firebase/genkit/"
246+
echo "-- All done!"
247+
248+
send_analytics_event "missing_platform_${UNAME}_${ARCH}"
249+
exit 0
250+
fi
251+
252+
# We have enough information to generate the binary's download URL.
253+
DOWNLOAD_URL="https://$DOMAIN/bin/$MACHINE/latest"
254+
echo "-- Downloading binary from $DOWNLOAD_URL"
255+
256+
# We use "curl" to download the binary with a flag set to follow redirects
257+
# (Github download URLs redirect to CDNs) and a flag to show a progress bar.
258+
curl -o "/tmp/genkit_standalone.tmp" -L --progress-bar $DOWNLOAD_URL
259+
260+
GENKIT_BINARY=${GENKIT_BINARY:-$GLOBAL_BINARY}
261+
INSTALL_DIR=$(dirname -- "$GENKIT_BINARY")
262+
263+
# We need to ensure that the INSTALL_DIR exists.
264+
# On some platforms like the Windows Subsystem for Linux it may not.
265+
# We created it using a non-destructive mkdir command.
266+
mkdir -p -- "$INSTALL_DIR" 2> /dev/null
267+
268+
# If the directory does not exist or is not writable, we resort to sudo.
269+
sudo=""
270+
if [ ! -w "$INSTALL_DIR" ]; then
271+
sudo="sudo"
272+
fi
273+
274+
$sudo mkdir -p -- "$INSTALL_DIR"
275+
$sudo mv -f "/tmp/genkit_standalone.tmp" "$GENKIT_BINARY"
276+
277+
# Once the download is complete, we mark the binary file as readable
278+
# and executable (+rx).
279+
echo "-- Setting permissions on binary... $GENKIT_BINARY"
280+
$sudo chmod +rx "$GENKIT_BINARY"
281+
282+
# If all went well, the "genkit" binary should be located on our PATH so
283+
# we'll run it once, asking it to print out the version. This is helpful as
284+
# standalone genkit binaries do a small amount of setup on the initial run
285+
# so this not only allows us to make sure we got the right version, but it
286+
# also does the setup so the first time the developer runs the binary, it'll
287+
# be faster.
288+
VERSION=$("$GENKIT_BINARY" --version)
289+
290+
# If no version is detected then clearly the binary failed to install for
291+
# some reason, so we'll log out an error message and report the failure
292+
# to headquarters via an analytics event.
293+
if [[ -z "$VERSION" ]]; then
294+
echo "Something went wrong, genkit has not been installed."
295+
echo "Please file a bug with your system information on Github."
296+
echo "https://github.com/firebase/genkit/"
297+
echo "-- All done!"
298+
299+
send_analytics_event failure
300+
exit 1
301+
fi
302+
303+
# In order for the user to be able to actually run the "genkit" command
304+
# without specifying the absolute location, the INSTALL_DIR path must
305+
# be present inside of the PATH environment variable.
306+
307+
echo "-- Checking your PATH variable..."
308+
if [[ ! ":$PATH:" == *":$INSTALL_DIR:"* ]]; then
309+
echo ""
310+
echo "It looks like $INSTALL_DIR isn't on your PATH."
311+
echo "Please add the following line to either your ~/.profile or ~/.bash_profile, then restart your terminal."
312+
echo ""
313+
echo "PATH=\$PATH:$INSTALL_DIR"
314+
echo ""
315+
echo "For more information about modifying PATHs, see https://unix.stackexchange.com/a/26059"
316+
echo ""
317+
send_analytics_event missing_path
318+
fi
319+
320+
# We also try to upgrade the local binary if it exists.
321+
# This helps prevent having two mismatching versions of "genkit".
322+
if [[ "$GENKIT_BINARY" != "$LOCAL_BINARY" ]] && [ -e "$LOCAL_BINARY" ]; then
323+
echo "-- Upgrading the local binary installation $LOCAL_BINARY..."
324+
cp "$GENKIT_BINARY" "$LOCAL_BINARY" # best effort, okay if it fails.
325+
chmod +x "$LOCAL_BINARY"
326+
fi
327+
328+
# Since we've gotten this far we know everything succeeded. We'll just
329+
# let the developer know everything is ready and take our leave.
330+
echo "-- genkit@$VERSION is now installed"
331+
echo "-- All Done!"
332+
333+
send_analytics_event success
334+
exit 0
335+
336+
# ------------------------------------------
337+
# Notes
338+
# ------------------------------------------
339+
#
340+
# This script contains hidden JavaScript which is used to improve
341+
# readability in the browser (via syntax highlighting, etc), right-click
342+
# and "View source" of this page to see the entire bash script!
343+
#
344+
# You'll also notice that we use the ":" character in the Introduction
345+
# which allows our copy/paste commands to be syntax highlighted, but not
346+
# ran. In bash : is equal to `true` and true can take infinite arguments
347+
# while still returning true. This turns these commands into no-ops so
348+
# when ran as a script, they're totally ignored.
349+
#

0 commit comments

Comments
 (0)