Enrell commited on
Commit
c948f03
·
verified ·
1 Parent(s): 4e76c6f

Upload install.sh with huggingface_hub

Browse files
Files changed (1) hide show
  1. 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