[mike@mwxm4]
This commit is contained in:
@@ -28,6 +28,20 @@ version from `main.go`.
|
|||||||
`../t/corpus`, the same corpus `t/select.t` uses, and fails if the two
|
`../t/corpus`, the same corpus `t/select.t` uses, and fails if the two
|
||||||
implementations start disagreeing.
|
implementations start disagreeing.
|
||||||
|
|
||||||
|
## The first one
|
||||||
|
|
||||||
|
`upd --update` needs an upd to run it. `loadupd.sh` fetches that first one -
|
||||||
|
POSIX sh, curl or wget, no upd required:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./loadupd.sh # into the current directory
|
||||||
|
./loadupd.sh ~/bin # into a directory of your choosing
|
||||||
|
```
|
||||||
|
|
||||||
|
It reads the newest release, picks the asset matching `uname -s`/`uname -m`,
|
||||||
|
runs it once before putting it in place, and says if the directory is not in
|
||||||
|
`$PATH`. `$UPD_REPO_URL` points it at a mirror. From there on upd takes over.
|
||||||
|
|
||||||
## Updating itself
|
## Updating itself
|
||||||
|
|
||||||
upd installs itself the same way it installs everything else - same forge
|
upd installs itself the same way it installs everything else - same forge
|
||||||
|
|||||||
Executable
+111
@@ -0,0 +1,111 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# loadupd.sh - fetch the newest upd binary for this machine.
|
||||||
|
#
|
||||||
|
# The one thing upd cannot do for itself: `upd --update` needs an upd to run
|
||||||
|
# it. Everything after this first one is upd's own job.
|
||||||
|
#
|
||||||
|
# ./loadupd.sh # into the current directory
|
||||||
|
# ./loadupd.sh ~/bin # into a directory of your choosing
|
||||||
|
#
|
||||||
|
# Plain POSIX sh, and nothing beyond uname, sed, grep and one of curl or wget:
|
||||||
|
# it has to run on the machines upd exists for, before upd is on them.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REPO=${UPD_REPO_URL:-https://git.micw.org/mike/upd}
|
||||||
|
NAME=upd
|
||||||
|
DEST=${1:-.}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
echo "loadupd: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# https://host/owner/repo -> https://host/api/v1/repos/owner/repo
|
||||||
|
api=$(echo "$REPO" | sed -e 's#/*$##' -e 's#\.git$##' \
|
||||||
|
-e 's#^\(https\{0,1\}://[^/]*\)/\(.*\)$#\1/api/v1/repos/\2#')
|
||||||
|
case "$api" in
|
||||||
|
*/api/v1/repos/*/*) ;;
|
||||||
|
*) die "cannot read $REPO, expected https://host/owner/repo" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
os=$(uname -s | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz')
|
||||||
|
case "$os" in
|
||||||
|
darwin | linux | freebsd) ;;
|
||||||
|
*) die "unsupported system: $(uname -s)" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# The names are Go's, not uname's, because that is what the release assets are
|
||||||
|
# called.
|
||||||
|
case "$(uname -m)" in
|
||||||
|
x86_64 | amd64) arch=amd64 ;;
|
||||||
|
aarch64 | arm64) arch=arm64 ;;
|
||||||
|
i386 | i686) arch=386 ;;
|
||||||
|
armv7* | armv6*) arch=arm ;;
|
||||||
|
*) die "unsupported architecture: $(uname -m)" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
asset="$NAME-$os-$arch"
|
||||||
|
|
||||||
|
# curl on an old machine may be the very thing that cannot speak TLS 1.2 any
|
||||||
|
# more - which is half the reason upd exists - so wget gets its turn before we
|
||||||
|
# give up.
|
||||||
|
fetch() { # url outfile
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
curl -fsSL -o "$2" "$1" && return 0
|
||||||
|
fi
|
||||||
|
if command -v wget >/dev/null 2>&1; then
|
||||||
|
wget -q -O "$2" "$1" && return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp=$(mktemp -d "${TMPDIR:-/tmp}/loadupd.XXXXXX") || die "cannot create a temporary directory"
|
||||||
|
trap 'rm -rf "$tmp"' EXIT INT TERM
|
||||||
|
|
||||||
|
echo "Looking for the newest release of $NAME ..."
|
||||||
|
fetch "$api/releases/latest" "$tmp/release.json" ||
|
||||||
|
die "cannot reach $api
|
||||||
|
If the TLS handshake failed, this machine is too old to fetch it itself -
|
||||||
|
copy $asset over from somewhere else."
|
||||||
|
|
||||||
|
tag=$(sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$tmp/release.json" | head -n 1)
|
||||||
|
[ -n "$tag" ] || die "no release found in $REPO"
|
||||||
|
|
||||||
|
# One asset per line, then pick ours by name. The JSON arrives as one long
|
||||||
|
# line, so the comma is what separates the fields.
|
||||||
|
urls=$(tr ',' '\n' <"$tmp/release.json" |
|
||||||
|
sed -n 's/.*"browser_download_url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
|
||||||
|
url=$(echo "$urls" | grep "/$asset\$" | head -n 1)
|
||||||
|
if [ -z "$url" ]; then
|
||||||
|
die "release $tag has no $asset (only: $(echo "$urls" | sed 's#.*/##' | tr '\n' ' '))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Downloading $asset $tag ..."
|
||||||
|
fetch "$url" "$tmp/$NAME" || die "download failed: $url"
|
||||||
|
chmod 755 "$tmp/$NAME"
|
||||||
|
|
||||||
|
# A truncated file or one built for the wrong platform shows up here, not on
|
||||||
|
# first use.
|
||||||
|
"$tmp/$NAME" --version >/dev/null 2>&1 || die "the downloaded binary does not run"
|
||||||
|
|
||||||
|
[ -d "$DEST" ] || mkdir -p "$DEST" || die "cannot create $DEST"
|
||||||
|
# Spelled out in full: "." says nothing in a message, and even less in the
|
||||||
|
# comparison against $PATH below.
|
||||||
|
dir=$(cd "$DEST" && pwd) || die "cannot use $DEST"
|
||||||
|
target="$dir/$NAME"
|
||||||
|
|
||||||
|
# Via a file alongside, so that replacing an upd that is currently running is
|
||||||
|
# one atomic step and not half a copy.
|
||||||
|
cp "$tmp/$NAME" "$target.new.$$" || die "no write permission in $dir"
|
||||||
|
chmod 755 "$target.new.$$"
|
||||||
|
mv "$target.new.$$" "$target" || {
|
||||||
|
rm -f "$target.new.$$"
|
||||||
|
die "cannot replace $target"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "$NAME $tag installed at $target"
|
||||||
|
case ":$PATH:" in
|
||||||
|
*":$dir:"*) ;;
|
||||||
|
*) echo "Note: $dir is not in \$PATH." ;;
|
||||||
|
esac
|
||||||
|
echo "From here on: $NAME --update"
|
||||||
Reference in New Issue
Block a user