Upload install.sh with huggingface_hub
Browse files- install.sh +177 -0
install.sh
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Install the Lain parser service: one frozen artifact, a private venv and a
|
| 3 |
+
# systemd user unit. Everything lands under --prefix (default
|
| 4 |
+
# ~/.local/share/lain-parser) and ~/.config/systemd/user; no sudo anywhere.
|
| 5 |
+
#
|
| 6 |
+
# bash install.sh # install, enable and start the unit
|
| 7 |
+
# bash install.sh --no-start # install files only
|
| 8 |
+
# bash install.sh --verify # re-check the installed hashes
|
| 9 |
+
#
|
| 10 |
+
# Source of truth for the artifact and the vendored app: this repository.
|
| 11 |
+
set -euo pipefail
|
| 12 |
+
|
| 13 |
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
| 14 |
+
REPO="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
| 15 |
+
ARTIFACT_NAME="e5-s2"
|
| 16 |
+
ARTIFACT_SRC="${REPO}/artifacts/e5-s2"
|
| 17 |
+
ARTIFACT_SHA="d928668e5ed3c8ea4ac69e42f8d11c90f82120e3307dffaf3a02eb801c077e54"
|
| 18 |
+
SNAPSHOT_SRC="${ANIFILEBERT_SNAPSHOT:-${REPO}/artifacts/anifilebert-dapt512-long}"
|
| 19 |
+
SNAPSHOT_FILES=(anifilebert config.json model.safetensors tokenizer_config.json \
|
| 20 |
+
vocab.json vocab.char.json label_schema.json dapt.json)
|
| 21 |
+
PREFIX="${LAIN_PARSER_PREFIX:-${HOME}/.local/share/lain-parser}"
|
| 22 |
+
UNIT_DIR="${HOME}/.config/systemd/user"
|
| 23 |
+
UNIT="lain-parser.service"
|
| 24 |
+
PORT="${LAIN_PARSER_PORT:-9377}"
|
| 25 |
+
HOSTS="${LAIN_PARSER_HOST:-127.0.0.1,172.17.0.1}"
|
| 26 |
+
# The socket lives inside Lain's data dir on purpose: a container that already
|
| 27 |
+
# bind-mounts that directory sees it at /data/parser.sock with no extra mount
|
| 28 |
+
# and no firewall rule (host TCP is firewalled from the bridge on many hosts).
|
| 29 |
+
SOCKET="${LAIN_PARSER_SOCKET:-${HOME}/.local/share/lain/parser.sock}"
|
| 30 |
+
START=1
|
| 31 |
+
VERIFY=0
|
| 32 |
+
FORCE=0
|
| 33 |
+
|
| 34 |
+
while [[ $# -gt 0 ]]; do
|
| 35 |
+
case "$1" in
|
| 36 |
+
--prefix) PREFIX="$2"; shift 2 ;;
|
| 37 |
+
--port) PORT="$2"; shift 2 ;;
|
| 38 |
+
--host) HOSTS="$2"; shift 2 ;;
|
| 39 |
+
--socket) SOCKET="$2"; shift 2 ;;
|
| 40 |
+
--no-start) START=0; shift ;;
|
| 41 |
+
--force) FORCE=1; shift ;;
|
| 42 |
+
--verify) VERIFY=1; shift ;;
|
| 43 |
+
-h|--help) sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
| 44 |
+
*) echo "unknown argument: $1" >&2; exit 2 ;;
|
| 45 |
+
esac
|
| 46 |
+
done
|
| 47 |
+
|
| 48 |
+
step() { printf '\n== %s\n' "$*"; }
|
| 49 |
+
die() { echo "error: $*" >&2; exit 1; }
|
| 50 |
+
|
| 51 |
+
sha_of() { sha256sum "$1" | cut -d' ' -f1; }
|
| 52 |
+
|
| 53 |
+
verify_installed() {
|
| 54 |
+
step "verifying ${PREFIX}"
|
| 55 |
+
local venv="${PREFIX}/venv/bin/python"
|
| 56 |
+
[[ -x "$venv" ]] || die "venv missing under ${PREFIX}"
|
| 57 |
+
local sum installed
|
| 58 |
+
sum="$(sha_of "${PREFIX}/artifact/${ARTIFACT_NAME}/model.int8.pt")"
|
| 59 |
+
[[ "$sum" == "$ARTIFACT_SHA" ]] || die "artifact hash mismatch: ${sum}"
|
| 60 |
+
echo "artifact ${ARTIFACT_NAME} hash ok"
|
| 61 |
+
"$venv" "${PREFIX}/app/serve.py" --check "Example.Show.S01E02.1080p-GROUP.mkv" \
|
| 62 |
+
--artifact "${PREFIX}/artifact/${ARTIFACT_NAME}" --snapshot "${PREFIX}/anifilebert" \
|
| 63 |
+
>/dev/null
|
| 64 |
+
echo "model loads and parses"
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
if [[ "$VERIFY" == 1 ]]; then
|
| 68 |
+
verify_installed
|
| 69 |
+
exit 0
|
| 70 |
+
fi
|
| 71 |
+
|
| 72 |
+
[[ -f "${ARTIFACT_SRC}/model.int8.pt" ]] || die "artifact not found at ${ARTIFACT_SRC}"
|
| 73 |
+
[[ -f "${ARTIFACT_SRC}/config.json" ]] || die "artifact config not found at ${ARTIFACT_SRC}"
|
| 74 |
+
sum="$(sha_of "${ARTIFACT_SRC}/model.int8.pt")"
|
| 75 |
+
[[ "$sum" == "$ARTIFACT_SHA" ]] || die "source artifact hash mismatch: ${sum}"
|
| 76 |
+
[[ -d "$SNAPSHOT_SRC" ]] || die "snapshot not found at ${SNAPSHOT_SRC}"
|
| 77 |
+
|
| 78 |
+
step "installing into ${PREFIX}"
|
| 79 |
+
mkdir -p "${PREFIX}/app/fp" "${PREFIX}/artifact/${ARTIFACT_NAME}" "${PREFIX}/anifilebert"
|
| 80 |
+
cp -f "${SCRIPT_DIR}/app/serve.py" "${PREFIX}/app/serve.py"
|
| 81 |
+
cp -f "${SCRIPT_DIR}"/app/fp/*.py "${PREFIX}/app/fp/"
|
| 82 |
+
cp -f "${SCRIPT_DIR}/requirements.txt" "${PREFIX}/requirements.txt"
|
| 83 |
+
cp -f "${ARTIFACT_SRC}/model.int8.pt" "${ARTIFACT_SRC}/config.json" \
|
| 84 |
+
"${PREFIX}/artifact/${ARTIFACT_NAME}/"
|
| 85 |
+
for entry in "${SNAPSHOT_FILES[@]}"; do
|
| 86 |
+
cp -RL "${SNAPSHOT_SRC}/${entry}" "${PREFIX}/anifilebert/"
|
| 87 |
+
done
|
| 88 |
+
|
| 89 |
+
step "recording hashes"
|
| 90 |
+
{
|
| 91 |
+
echo "# installed $(date -u +%Y-%m-%dT%H:%M:%SZ) by $(id -un)@$(hostname)"
|
| 92 |
+
(cd "$PREFIX" && find app artifact anifilebert requirements.txt -type f \
|
| 93 |
+
-exec sha256sum {} + | sort -k2)
|
| 94 |
+
} > "${PREFIX}/SHA256SUMS"
|
| 95 |
+
echo "wrote ${PREFIX}/SHA256SUMS"
|
| 96 |
+
|
| 97 |
+
step "creating the venv (CPU wheels only)"
|
| 98 |
+
if [[ ! -x "${PREFIX}/venv/bin/python" ]]; then
|
| 99 |
+
if command -v uv >/dev/null 2>&1; then
|
| 100 |
+
uv venv --python "${LAIN_PARSER_PYTHON:-3.12}" "${PREFIX}/venv"
|
| 101 |
+
uv pip install --python "${PREFIX}/venv/bin/python" \
|
| 102 |
+
--index-url https://download.pytorch.org/whl/cpu "torch==2.14.0"
|
| 103 |
+
uv pip install --python "${PREFIX}/venv/bin/python" -r "${PREFIX}/requirements.txt"
|
| 104 |
+
else
|
| 105 |
+
python3 -m venv "${PREFIX}/venv"
|
| 106 |
+
"${PREFIX}/venv/bin/pip" install --upgrade pip >/dev/null
|
| 107 |
+
"${PREFIX}/venv/bin/pip" install --index-url https://download.pytorch.org/whl/cpu \
|
| 108 |
+
"torch==2.14.0"
|
| 109 |
+
"${PREFIX}/venv/bin/pip" install -r "${PREFIX}/requirements.txt"
|
| 110 |
+
fi
|
| 111 |
+
else
|
| 112 |
+
echo "venv already present, keeping it"
|
| 113 |
+
fi
|
| 114 |
+
"${PREFIX}/venv/bin/python" -c "import torch, transformers; print('torch', torch.__version__, '| transformers', transformers.__version__)"
|
| 115 |
+
|
| 116 |
+
step "systemd user unit"
|
| 117 |
+
mkdir -p "${UNIT_DIR}"
|
| 118 |
+
unit_tmp="$(mktemp)"
|
| 119 |
+
cat > "$unit_tmp" <<UNIT
|
| 120 |
+
# Generated by filename-parser packaging/lain-parser/install.sh -- yours to edit.
|
| 121 |
+
[Unit]
|
| 122 |
+
Description=Lain local filename parser (${ARTIFACT_NAME})
|
| 123 |
+
After=network.target docker.service
|
| 124 |
+
|
| 125 |
+
[Service]
|
| 126 |
+
WorkingDirectory=${PREFIX}
|
| 127 |
+
ExecStart=${PREFIX}/venv/bin/python ${PREFIX}/app/serve.py --artifact ${PREFIX}/artifact/${ARTIFACT_NAME} --snapshot ${PREFIX}/anifilebert --host ${HOSTS} --port ${PORT} --unix-socket ${SOCKET}
|
| 128 |
+
Environment=PYTHONUNBUFFERED=1
|
| 129 |
+
Restart=on-failure
|
| 130 |
+
RestartSec=2
|
| 131 |
+
|
| 132 |
+
[Install]
|
| 133 |
+
WantedBy=default.target
|
| 134 |
+
UNIT
|
| 135 |
+
if [[ ! -f "${UNIT_DIR}/${UNIT}" ]]; then
|
| 136 |
+
install -m 0644 "$unit_tmp" "${UNIT_DIR}/${UNIT}"
|
| 137 |
+
echo "wrote ${UNIT_DIR}/${UNIT}"
|
| 138 |
+
elif diff -q "$unit_tmp" "${UNIT_DIR}/${UNIT}" >/dev/null; then
|
| 139 |
+
echo "unit already up to date"
|
| 140 |
+
elif [[ "$FORCE" == 1 ]]; then
|
| 141 |
+
install -m 0644 "$unit_tmp" "${UNIT_DIR}/${UNIT}"
|
| 142 |
+
echo "unit replaced (--force)"
|
| 143 |
+
else
|
| 144 |
+
install -m 0644 "$unit_tmp" "${UNIT_DIR}/${UNIT}.new"
|
| 145 |
+
echo "unit differs: wrote ${UNIT_DIR}/${UNIT}.new for review (or pass --force)"
|
| 146 |
+
fi
|
| 147 |
+
rm -f "$unit_tmp"
|
| 148 |
+
|
| 149 |
+
if [[ "$START" == 1 ]] && command -v systemctl >/dev/null 2>&1; then
|
| 150 |
+
systemctl --user daemon-reload
|
| 151 |
+
systemctl --user enable --now "$UNIT" || true
|
| 152 |
+
if command -v curl >/dev/null 2>&1; then
|
| 153 |
+
ok=""
|
| 154 |
+
for _ in $(seq 1 20); do
|
| 155 |
+
sleep 1
|
| 156 |
+
for addr in ${HOSTS//,/ }; do
|
| 157 |
+
if curl -fsS --max-time 2 "http://${addr}:${PORT}/health" >/dev/null 2>&1; then ok="$addr"; break 2; fi
|
| 158 |
+
done
|
| 159 |
+
done
|
| 160 |
+
if [[ -n "$ok" ]]; then
|
| 161 |
+
curl -fsS --max-time 3 "http://${ok}:${PORT}/health"; echo
|
| 162 |
+
else
|
| 163 |
+
echo "warning: health check did not answer in 20s (see: journalctl --user -u ${UNIT})"
|
| 164 |
+
fi
|
| 165 |
+
fi
|
| 166 |
+
fi
|
| 167 |
+
|
| 168 |
+
step "done"
|
| 169 |
+
cat <<SUMMARY
|
| 170 |
+
prefix ${PREFIX}
|
| 171 |
+
artifact ${ARTIFACT_NAME} (${ARTIFACT_SHA:0:16}…)
|
| 172 |
+
unit ${UNIT_DIR}/${UNIT}
|
| 173 |
+
native URL http://127.0.0.1:${PORT}
|
| 174 |
+
docker URL http://172.17.0.1:${PORT} (or host.docker.internal with host-gateway)
|
| 175 |
+
docker UDS unix:///data/parser.sock (socket at ${SOCKET})
|
| 176 |
+
verify bash install.sh --verify
|
| 177 |
+
SUMMARY
|