#!/bin/sh # Installs flower. Served publicly, with the downloads Worker's origin and the channel written in, so # a person runs `curl -fsSL
| sh`. It holds nothing secret: it shows a sign-in link (Enter # opens it), the browser hands the person's Cloudflare Access token to a loopback port this script # listens on, it downloads the channel's release with that token, checks its SHA-256 against its # manifest and hands it to the manager it carries, which checks the signature, keeps the token and # installs. On a terminal it draws its progress; anywhere else it prints one plain line per step. # Its body is one function called on the last line, so a download cut short runs nothing. set -eu install_flower() { base='https://flower-worker-installs-downloads.webflow-productivity.workers.dev' channel=stable case $channel in stable) product=Flower command=flower ;; *) product='Flower Next' command=flower-next ;; esac drawn= if [ -t 1 ] && [ -z "${NO_COLOR:-}" ] && [ "${TERM:-dumb}" != dumb ]; then drawn=1; fi esc=$(printf '\033') if [ -n "$drawn" ]; then pink="${esc}[38;5;211m" green="${esc}[38;5;114m" red="${esc}[38;5;203m" dim="${esc}[2m" bold="${esc}[1m" reset="${esc}[0m" else pink='' green='' red='' dim='' bold='' reset='' fi spinner='' reader='' tty_saved='' cleanup() { [ -n "$spinner" ] && kill "$spinner" 2>/dev/null [ -n "$reader" ] && kill "$reader" 2>/dev/null [ -n "$tty_saved" ] && stty "$tty_saved" /dev/null [ -n "$drawn" ] && printf '%s[?25h' "$esc" rm -rf "$work" } frame_at() { case $(($1 % 10)) in 0) printf '⠋' ;; 1) printf '⠙' ;; 2) printf '⠹' ;; 3) printf '⠸' ;; 4) printf '⠼' ;; 5) printf '⠴' ;; 6) printf '⠦' ;; 7) printf '⠧' ;; 8) printf '⠇' ;; *) printf '⠏' ;; esac } line() { printf '\r%s[2K' "$esc"; } done_step() { if [ -n "$drawn" ]; then line printf ' %s✓%s %s\n' "$green" "$reset" "$*" else echo "flower: $*"; fi } spin() { [ -n "$drawn" ] || { echo "flower: $*" return } unspin ( i=0 while :; do line printf ' %s%s%s %s' "$pink" "$(frame_at $i)" "$reset" "$*" i=$((i + 1)) sleep 0.08 done ) & spinner=$! } unspin() { if [ -n "$spinner" ]; then kill "$spinner" 2>/dev/null || true wait "$spinner" 2>/dev/null || true spinner='' line fi } fail() { unspin if [ -n "$drawn" ]; then line printf ' %s✗%s %s\n\n' "$red" "$reset" "$*" >&2 else echo "flower: $*" >&2; fi exit 1 } tilde() { printf '%s' "$1" | sed "s#^$HOME#~#; s# $HOME# ~#g"; } work=$(mktemp -d) trap cleanup EXIT trap 'exit 130' INT TERM [ "$(uname -s)" = Darwin ] || fail "flower runs on macOS only" [ "$(sysctl -n hw.optional.arm64 2>/dev/null)" = 1 ] || fail "flower runs on Apple silicon Macs only" if [ -n "$drawn" ]; then printf '%s[?25l\n' "$esc" for mark in '·' '∘' '❀' '✿'; do printf '\r %s%s%s' "$pink" "$mark" "$reset" sleep 0.07 done printf ' ' word="flower" while [ -n "$word" ]; do rest=${word#?} printf '%s%s%s' "$bold" "${word%"$rest"}" "$reset" word=$rest sleep 0.03 done [ "$channel" = stable ] || printf ' %s· next%s' "$dim" "$reset" printf '\n\n' fi state=$(od -An -N16 -tx1 /dev/urandom | tr -d ' \n') port=$(awk 'BEGIN { srand(); print 49152 + int(rand() * 16000) }') signed_in='Signed in to flower
✿

You are signed in to flower

Close this tab and return to your terminal.

' answer=$(printf 'HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: %s\r\nConnection: close\r\n\r\n%s' "$(printf '%s' "$signed_in" | wc -c | tr -d ' ')" "$signed_in") sign_in="$base/api/sign-in?port=$port&state=$state" if [ -n "$drawn" ]; then printf ' %sSign in with your Webflow account%s\n' "$bold" "$reset" printf ' %s%s]8;;%s%s\\%s%s]8;;%s\\%s\n' "$pink" "$esc" "$sign_in" "$esc" "$sign_in" "$esc" "$esc" "$reset" if [ -r /dev/tty ] && tty_saved=$(stty -g /dev/null); then printf ' %sPress Enter to open it in your browser%s\n\n' "$dim" "$reset" stty -echo /dev/null (read -r _ /dev/null 2>&1 & reader=$! else tty_saved='' printf '\n' fi else echo "flower: sign in at $sign_in" fi spin "Waiting for your browser" token='' while [ -z "$token" ]; do printf '%s' "$answer" | nc -l 127.0.0.1 "$port" >"$work/handed" || fail "could not listen on 127.0.0.1:$port for the sign-in" asked=$(sed -n '1s/^GET \/?\([^ ]*\) HTTP.*/\1/p' "$work/handed" | tr '&' '\n') if printf '%s\n' "$asked" | grep -qx "state=$state"; then token=$(printf '%s\n' "$asked" | sed -n 's/^token=//p') fi done unspin [ -n "$reader" ] && { kill "$reader" 2>/dev/null || true reader='' } [ -n "$tty_saved" ] && { stty "$tty_saved" /dev/null || true tty_saved='' } claims=$(printf '%s' "$token" | cut -d. -f2 | tr '_-' '/+') while [ $((${#claims} % 4)) -ne 0 ]; do claims="$claims="; done email=$(printf '%s' "$claims" | base64 -D 2>/dev/null | plutil -extract email raw -o - - 2>/dev/null || true) done_step "Signed in${email:+ as $email}" spin "Finding the latest $product" pointed=$(curl -sS --connect-timeout 10 --retry 3 -H "cf-access-token: $token" -o "$work/pointer.json" -w '%{http_code}' "$base/api/channels/$channel") || fail "could not reach $base" case $pointed in 200) ;; 404) fail "nothing is published to $channel yet" ;; *) fail "$base answered $pointed for the $channel channel" ;; esac release=$(plutil -extract release raw -o - "$work/pointer.json") curl -fsSL --connect-timeout 10 --retry 3 -H "cf-access-token: $token" -o "$work/manifest.json" "$base/api/releases/$release/manifest" || fail "could not read the manifest of $release" plutil -extract manifest raw -o - "$work/manifest.json" >"$work/release.json" expected=$(plutil -extract sha256 raw -o - "$work/release.json") bytes=$(plutil -extract bytes raw -o - "$work/release.json") megabytes=$(awk -v b="$bytes" 'BEGIN { printf "%.0f", b / 1048576 }') unspin done_step "Found $product $release" curl -fsSL --connect-timeout 10 --retry 3 --speed-limit 1024 --speed-time 30 -H "cf-access-token: $token" \ -o "$work/archive.tar.xz" "$base/api/releases/$release/archive" 2>"$work/download.err" & download=$! if [ -n "$drawn" ]; then i=0 width=28 while kill -0 "$download" 2>/dev/null; do have=$(stat -f %z "$work/archive.tar.xz" 2>/dev/null || echo 0) filled=$((have * width / bytes)) [ "$filled" -gt "$width" ] && filled=$width bar='' n=0 while [ "$n" -lt "$width" ]; do if [ "$n" -lt "$filled" ]; then bar="$bar━"; else bar="$bar$dim─$reset"; fi n=$((n + 1)) done line printf ' %s%s%s Downloading %s%s%s %s%s / %s MB%s' "$pink" "$(frame_at $i)" "$reset" "$pink" "$bar" "$reset" \ "$dim" "$(awk -v b="$have" 'BEGIN { printf "%.0f", b / 1048576 }')" "$megabytes" "$reset" i=$((i + 1)) sleep 0.08 done else echo "flower: downloading $release ($megabytes MB)" fi if wait "$download"; then :; else fail "the download of $release stopped: $(tail -1 "$work/download.err")"; fi [ "$(shasum -a 256 "$work/archive.tar.xz" | awk '{print $1}')" = "$expected" ] || fail "the download of $release does not match its manifest" done_step "Downloaded $megabytes MB and checked it" tar -xf "$work/archive.tar.xz" -C "$work" app/manager/manager.cjs app/node/bin/node FLOWER_ACCESS_TOKEN=$token "$work/app/node/bin/node" "$work/app/manager/manager.cjs" self install \ --channel "$channel" --base "$base" --release "$release" --archive "$work/archive.tar.xz" >"$work/manager.out" 2>&1 & manager=$! said=0 doing="Installing $release" i=0 shown() { total=$(wc -l <"$work/manager.out" | tr -d ' ') while [ "$said" -lt "$total" ]; do said=$((said + 1)) told=$(sed -n "${said}p" "$work/manager.out") told=${told#flower: } case $told in "checking "*) doing="Checking that ${told#checking } runs on this Mac" ;; "installed $release in "*) done_step "Installed in $(tilde "${told#installed "$release" in }")" ;; "added "*" to PATH in "*) path_file=${told#* to PATH in } done_step "Added $command to your PATH in $(tilde "${path_file%%;*}")" ;; "installed $product "* | "replaced $product "* | "$product "*" is in place") done_step "$product is in your Applications folder" ;; *) [ -n "$told" ] && done_step "$(tilde "$told")" ;; esac done } while kill -0 "$manager" 2>/dev/null; do shown if [ -n "$drawn" ]; then line printf ' %s%s%s %s' "$pink" "$(frame_at $i)" "$reset" "$doing" i=$((i + 1)) fi sleep 0.08 done if wait "$manager"; then installed=0; else installed=$?; fi shown [ "$installed" -eq 0 ] || fail "installing $release did not finish" if [ -n "$drawn" ]; then line printf '\n %s✿%s %s%s is ready.%s\n\n' "$pink" "$reset" "$bold" "$product" "$reset" printf ' Open a new terminal and run %s%s%s, or open %s from Applications.\n\n' "$pink" "$command" "$reset" "$product" else echo "flower: $product $release is ready; open a new terminal and run $command" fi } install_flower