README: install and update from object storage
This commit is contained in:
@@ -17,15 +17,11 @@ One command sets up a host. Run it as root; it asks what the host should be
|
|||||||
`/opt/runix`, and wires up systemd.
|
`/opt/runix`, and wires up systemd.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -fsSL https://vcs.astra-dev.online/svesnav/runix/raw/branch/main/install.sh | sudo sh
|
curl -fsSL https://runix-releases.s3-alpha-web.astra-dev.online/install.sh | sudo sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Prefer to pin a version, or your instance still requires sign-in to read raw
|
Binaries are served from object storage, so this needs no account and no
|
||||||
files? Use the release asset instead — it downloads without a login:
|
token. To pin a version, add `--version v0.5.0`.
|
||||||
|
|
||||||
```sh
|
|
||||||
curl -fsSL https://vcs.astra-dev.online/svesnav/runix/releases/download/v0.5.0/install.sh | sudo sh -s -- --role all-in-one
|
|
||||||
```
|
|
||||||
|
|
||||||
### Roles
|
### Roles
|
||||||
|
|
||||||
@@ -56,16 +52,15 @@ services restarted, while your configuration (above all the JWT and
|
|||||||
encryption secrets) is preserved:
|
encryption secrets) is preserved:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -fsSL https://vcs.astra-dev.online/svesnav/runix/raw/branch/main/install.sh | sudo sh -- -y
|
curl -fsSL https://runix-releases.s3-alpha-web.astra-dev.online/install.sh | sudo sh -s -- -y
|
||||||
```
|
```
|
||||||
|
|
||||||
Or update from the UI: an administrator sees an **Updates** page that checks
|
Or update from the UI: an administrator sees an **Updates** page that checks
|
||||||
this repository for the latest release and updates the control plane and each
|
object storage for the latest release and updates the control plane and each
|
||||||
agent with one click. Point a control plane at this repository with:
|
agent with one click. Point a control plane at this repository with:
|
||||||
|
|
||||||
```
|
```
|
||||||
RUNIX_UPDATE_API_BASE=https://vcs.astra-dev.online/api/v1
|
RUNIX_UPDATE_MANIFEST=https://runix-releases.s3-alpha-web.astra-dev.online/latest.json
|
||||||
RUNIX_UPDATE_REPO=svesnav/runix
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Releases
|
## Releases
|
||||||
@@ -78,11 +73,13 @@ Each release publishes, for `linux/amd64` and `linux/arm64`:
|
|||||||
- `SHA256SUMS` — checksums; the installer verifies every download against it
|
- `SHA256SUMS` — checksums; the installer verifies every download against it
|
||||||
|
|
||||||
Binaries are built automatically from source on tag by the CI runner and
|
Binaries are built automatically from source on tag by the CI runner and
|
||||||
published here and to the source repository.
|
published to object storage as well as to this repository. `latest.json`
|
||||||
|
beside them names the newest version and every asset's checksum — that is
|
||||||
|
what the installer and the in-app updater read.
|
||||||
|
|
||||||
## Verify a download by hand
|
## Verify a download by hand
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -fsSLO https://vcs.astra-dev.online/svesnav/runix/releases/download/v0.5.0/runix-server_linux_amd64
|
curl -fsSLO https://runix-releases.s3-alpha-web.astra-dev.online/v0.5.0/runix-server_linux_amd64
|
||||||
curl -fsSL https://vcs.astra-dev.online/svesnav/runix/releases/download/v0.5.0/SHA256SUMS | sha256sum -c --ignore-missing
|
curl -fsSL https://runix-releases.s3-alpha-web.astra-dev.online/v0.5.0/SHA256SUMS | sha256sum -c --ignore-missing
|
||||||
```
|
```
|
||||||
|
|||||||
+59
-18
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Runix installer — the one command that sets a host up.
|
# Runix installer — the one command that sets a host up.
|
||||||
#
|
#
|
||||||
# curl -fsSL https://vcs.astra-dev.online/svesnav/runix/raw/branch/main/install.sh | sudo sh
|
# curl -fsSL https://runix-releases.s3-alpha-web.astra-dev.online/install.sh | sudo sh
|
||||||
#
|
#
|
||||||
# Asks what this host should be (control plane, agent, or both), checks
|
# Asks what this host should be (control plane, agent, or both), checks
|
||||||
# the prerequisites, installs under /opt/runix and wires up systemd.
|
# the prerequisites, installs under /opt/runix and wires up systemd.
|
||||||
@@ -19,9 +19,15 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
PREFIX=${RUNIX_PREFIX:-/opt/runix}
|
PREFIX=${RUNIX_PREFIX:-/opt/runix}
|
||||||
# Releases are served from a Gitea instance by default. Point GITEA at a
|
# Releases are served from object storage by default: it needs no account,
|
||||||
# GitHub-style host by clearing it (RUNIX_GITEA=) and setting a download
|
# so a plain `curl … | sh` works on a fresh box. The layout is
|
||||||
# base, if you mirror the release elsewhere.
|
#
|
||||||
|
# <base>/latest.json names the newest version
|
||||||
|
# <base>/<version>/<asset> the binaries, install.sh and SHA256SUMS
|
||||||
|
#
|
||||||
|
# Clear it (RUNIX_S3=) to fall back to the git forge below.
|
||||||
|
S3_BASE=${RUNIX_S3:-https://runix-releases.s3-alpha-web.astra-dev.online}
|
||||||
|
# The forge is the fallback source, used when S3_BASE is empty.
|
||||||
GITEA=${RUNIX_GITEA:-https://vcs.astra-dev.online}
|
GITEA=${RUNIX_GITEA:-https://vcs.astra-dev.online}
|
||||||
REPO=${RUNIX_REPO:-svesnav/runix}
|
REPO=${RUNIX_REPO:-svesnav/runix}
|
||||||
VERSION=${RUNIX_VERSION:-latest}
|
VERSION=${RUNIX_VERSION:-latest}
|
||||||
@@ -86,8 +92,9 @@ Options:
|
|||||||
--server-binary PATH Install a local control-plane build
|
--server-binary PATH Install a local control-plane build
|
||||||
--agent-binary PATH Install a local agent build
|
--agent-binary PATH Install a local agent build
|
||||||
--version VERSION Release to install (default: latest)
|
--version VERSION Release to install (default: latest)
|
||||||
--repo OWNER/NAME Release repository (default: $REPO)
|
--s3 URL Object store serving releases (default: $S3_BASE)
|
||||||
--gitea URL Gitea instance serving releases (default: $GITEA)
|
--repo OWNER/NAME Release repository, when not using --s3
|
||||||
|
--gitea URL Gitea instance serving releases (fallback source)
|
||||||
--repo-token TOK Read token, if the instance requires sign-in
|
--repo-token TOK Read token, if the instance requires sign-in
|
||||||
--prefix PATH Install root (default: $PREFIX)
|
--prefix PATH Install root (default: $PREFIX)
|
||||||
--no-start Install and configure, but do not start services
|
--no-start Install and configure, but do not start services
|
||||||
@@ -95,7 +102,7 @@ Options:
|
|||||||
-h, --help Show this help
|
-h, --help Show this help
|
||||||
|
|
||||||
Environment equivalents: RUNIX_PREFIX, RUNIX_VERSION, RUNIX_REPO,
|
Environment equivalents: RUNIX_PREFIX, RUNIX_VERSION, RUNIX_REPO,
|
||||||
RUNIX_GITEA, RUNIX_TOKEN, RUNIX_DATABASE_DSN, RUNIX_ADMIN_PASSWORD,
|
RUNIX_S3, RUNIX_GITEA, RUNIX_TOKEN, RUNIX_DATABASE_DSN, RUNIX_ADMIN_PASSWORD,
|
||||||
RUNIX_HTTP_HOST, RUNIX_AGENT_SERVER_URL, RUNIX_AGENT_TOKEN,
|
RUNIX_HTTP_HOST, RUNIX_AGENT_SERVER_URL, RUNIX_AGENT_TOKEN,
|
||||||
RUNIX_POSTGRES_PORT.
|
RUNIX_POSTGRES_PORT.
|
||||||
|
|
||||||
@@ -125,7 +132,8 @@ while [ $# -gt 0 ]; do
|
|||||||
--agent-binary) AGENT_BIN="$2"; shift 2 ;;
|
--agent-binary) AGENT_BIN="$2"; shift 2 ;;
|
||||||
--version) VERSION="$2"; shift 2 ;;
|
--version) VERSION="$2"; shift 2 ;;
|
||||||
--repo) REPO="$2"; shift 2 ;;
|
--repo) REPO="$2"; shift 2 ;;
|
||||||
--gitea) GITEA="$2"; shift 2 ;;
|
--s3) S3_BASE="$2"; shift 2 ;;
|
||||||
|
--gitea) GITEA="$2"; S3_BASE=""; shift 2 ;;
|
||||||
--repo-token|--github-token) DL_TOKEN="$2"; shift 2 ;;
|
--repo-token|--github-token) DL_TOKEN="$2"; shift 2 ;;
|
||||||
--prefix) PREFIX="$2"; shift 2 ;;
|
--prefix) PREFIX="$2"; shift 2 ;;
|
||||||
--no-start) NO_START=1; shift ;;
|
--no-start) NO_START=1; shift ;;
|
||||||
@@ -136,7 +144,9 @@ while [ $# -gt 0 ]; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
if [ -z "$DOWNLOAD_BASE" ]; then
|
if [ -z "$DOWNLOAD_BASE" ]; then
|
||||||
if [ -n "$GITEA" ]; then
|
if [ -n "$S3_BASE" ]; then
|
||||||
|
DOWNLOAD_BASE="$S3_BASE"
|
||||||
|
elif [ -n "$GITEA" ]; then
|
||||||
DOWNLOAD_BASE="$GITEA/$REPO/releases"
|
DOWNLOAD_BASE="$GITEA/$REPO/releases"
|
||||||
else
|
else
|
||||||
DOWNLOAD_BASE="https://github.com/$REPO/releases"
|
DOWNLOAD_BASE="https://github.com/$REPO/releases"
|
||||||
@@ -802,6 +812,16 @@ RESOLVED_TAG=""
|
|||||||
resolve_tag() {
|
resolve_tag() {
|
||||||
if [ "$VERSION" != latest ]; then RESOLVED_TAG="$VERSION"; return 0; fi
|
if [ "$VERSION" != latest ]; then RESOLVED_TAG="$VERSION"; return 0; fi
|
||||||
[ -n "$RESOLVED_TAG" ] && return 0
|
[ -n "$RESOLVED_TAG" ] && return 0
|
||||||
|
# Object storage serves no API, so the newest version is named in a
|
||||||
|
# small manifest published beside the binaries.
|
||||||
|
if [ -n "$S3_BASE" ]; then
|
||||||
|
_man=$(mktemp)
|
||||||
|
if fetch "$S3_BASE/latest.json" "$_man" "application/json"; then
|
||||||
|
RESOLVED_TAG=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$_man" | head -n1)
|
||||||
|
fi
|
||||||
|
rm -f "$_man"
|
||||||
|
[ -n "$RESOLVED_TAG" ] && return 0
|
||||||
|
fi
|
||||||
if [ -n "$GITEA" ]; then
|
if [ -n "$GITEA" ]; then
|
||||||
_loc=$(redirect_of "$DOWNLOAD_BASE/latest" 2>/dev/null || true)
|
_loc=$(redirect_of "$DOWNLOAD_BASE/latest" 2>/dev/null || true)
|
||||||
RESOLVED_TAG=$(printf '%s' "$_loc" | sed -n 's#.*/releases/tag/##p')
|
RESOLVED_TAG=$(printf '%s' "$_loc" | sed -n 's#.*/releases/tag/##p')
|
||||||
@@ -843,19 +863,26 @@ asset_id() {
|
|||||||
|
|
||||||
# try_download NAME DEST — quiet, returns non-zero if the asset is absent.
|
# try_download NAME DEST — quiet, returns non-zero if the asset is absent.
|
||||||
try_download() {
|
try_download() {
|
||||||
_name=$1; _dest=$2
|
# Distinct variable names: POSIX sh has no locals, and resolve_tag
|
||||||
if [ -n "$GITEA" ]; then
|
# below calls fetch, which would otherwise overwrite the _dest we were
|
||||||
|
# handed — sending the binary to the manifest's temp file instead of
|
||||||
|
# where the caller expects it.
|
||||||
|
_tdname=$1; _tddest=$2
|
||||||
|
if [ -n "$S3_BASE" ]; then
|
||||||
|
resolve_tag || return 1
|
||||||
|
fetch "$S3_BASE/$RESOLVED_TAG/$_tdname" "$_tddest" || return 1
|
||||||
|
elif [ -n "$GITEA" ]; then
|
||||||
# Gitea assets download by tag; the token (if any) rides in fetch.
|
# Gitea assets download by tag; the token (if any) rides in fetch.
|
||||||
resolve_tag || return 1
|
resolve_tag || return 1
|
||||||
fetch "$DOWNLOAD_BASE/download/$RESOLVED_TAG/$_name" "$_dest" || return 1
|
fetch "$DOWNLOAD_BASE/download/$RESOLVED_TAG/$_tdname" "$_tddest" || return 1
|
||||||
elif [ -n "$DL_TOKEN" ]; then
|
elif [ -n "$DL_TOKEN" ]; then
|
||||||
_id=$(asset_id "$_name") || return 1
|
_id=$(asset_id "$_tdname") || return 1
|
||||||
fetch "https://api.github.com/repos/$REPO/releases/assets/$_id" \
|
fetch "https://api.github.com/repos/$REPO/releases/assets/$_id" \
|
||||||
"$_dest" "application/octet-stream" || return 1
|
"$_tddest" "application/octet-stream" || return 1
|
||||||
elif [ "$VERSION" = latest ]; then
|
elif [ "$VERSION" = latest ]; then
|
||||||
fetch "$DOWNLOAD_BASE/latest/download/$_name" "$_dest" || return 1
|
fetch "$DOWNLOAD_BASE/latest/download/$_tdname" "$_tddest" || return 1
|
||||||
else
|
else
|
||||||
fetch "$DOWNLOAD_BASE/download/$VERSION/$_name" "$_dest" || return 1
|
fetch "$DOWNLOAD_BASE/download/$VERSION/$_tdname" "$_tddest" || return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,6 +1043,14 @@ install_server() {
|
|||||||
mkdir -p "$PREFIX"
|
mkdir -p "$PREFIX"
|
||||||
install_binary runix-server "$SERVER_BIN"
|
install_binary runix-server "$SERVER_BIN"
|
||||||
|
|
||||||
|
# Applying an update from the UI replaces the binary in place, which
|
||||||
|
# means writing a new file into this directory and renaming it over the
|
||||||
|
# old one — so the service user has to own the directory, not just the
|
||||||
|
# file. Without this the update fails with a bare permission error long
|
||||||
|
# after the download has succeeded.
|
||||||
|
chown "$SERVER_USER" "$BIN_DIR" 2>/dev/null || true
|
||||||
|
chown "$SERVER_USER" "$BIN_DIR/runix-server" 2>/dev/null || true
|
||||||
|
|
||||||
[ "$DB_MODE" = docker ] && provision_postgres
|
[ "$DB_MODE" = docker ] && provision_postgres
|
||||||
[ -n "$DSN" ] || fail "no database configured"
|
[ -n "$DSN" ] || fail "no database configured"
|
||||||
|
|
||||||
@@ -1094,7 +1129,11 @@ NoNewPrivileges=yes
|
|||||||
PrivateTmp=yes
|
PrivateTmp=yes
|
||||||
ProtectSystem=strict
|
ProtectSystem=strict
|
||||||
ProtectHome=yes
|
ProtectHome=yes
|
||||||
ReadWritePaths=$CONFIG_DIR
|
# The binary directory is writable so the control plane can replace its own
|
||||||
|
# binary when an administrator applies an update from the UI. That is the
|
||||||
|
# price of in-app updates: the service can rewrite the file it runs from.
|
||||||
|
# Drop \$BIN_DIR here to forbid it, and upgrade by re-running this script.
|
||||||
|
ReadWritePaths=$CONFIG_DIR $BIN_DIR
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
@@ -1376,7 +1415,9 @@ fi
|
|||||||
if [ "$ROLE" = server ]; then
|
if [ "$ROLE" = server ]; then
|
||||||
echo
|
echo
|
||||||
echo " Add hosts from the UI (Servers → Add server), then run on each:"
|
echo " Add hosts from the UI (Servers → Add server), then run on each:"
|
||||||
if [ -n "$GITEA" ]; then
|
if [ -n "$S3_BASE" ]; then
|
||||||
|
say " curl -fsSL $S3_BASE/install.sh | sudo sh -s -- \\"
|
||||||
|
elif [ -n "$GITEA" ]; then
|
||||||
say " curl -fsSL $GITEA/$REPO/raw/branch/main/install.sh | sudo sh -s -- \\"
|
say " curl -fsSL $GITEA/$REPO/raw/branch/main/install.sh | sudo sh -s -- \\"
|
||||||
else
|
else
|
||||||
say " curl -fsSL $DOWNLOAD_BASE/latest/download/install.sh | sudo sh -s -- \\"
|
say " curl -fsSL $DOWNLOAD_BASE/latest/download/install.sh | sudo sh -s -- \\"
|
||||||
|
|||||||
Reference in New Issue
Block a user