@aifabrix/server-setup 1.5.2 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/assets/aifabrix-apply-dev-users.sh +92 -6
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -17,7 +17,9 @@ This is the **one document** you need to get your builder-server **from zero to
|
|
|
17
17
|
**The builder-server Docker image is not on a public registry.** You get it with the **AI Fabrix platform**.
|
|
18
18
|
|
|
19
19
|
1. Install the **AI Fabrix Builder** CLI: `npm install -g @aifabrix/builder`
|
|
20
|
-
2. Install the **AI Fabrix server-setup** CLI (af-server): `npm install -g @aifabrix/server-setup`
|
|
20
|
+
2. Install the **AI Fabrix server-setup** CLI (af-server): `npm install -g @aifabrix/server-setup`
|
|
21
|
+
- If you get **EACCES** (permission denied), use either:
|
|
22
|
+
`sudo npm install -g @aifabrix/server-setup`
|
|
21
23
|
3. Use the Builder CLI to run or deploy the platform (including builder-server). See [AI Fabrix Builder](https://github.com/esystemsdev/aifabrix-builder) and [docs/README.md](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/README.md).
|
|
22
24
|
|
|
23
25
|
Once you have the platform (and the builder-server image), use **af-server** to install that server on your own host.
|
|
@@ -203,7 +205,7 @@ If you SSH as a non-root user, that user must be able to sudo. The script will a
|
|
|
203
205
|
- **Admin user** — Adds the admin user (default `serveradmin`) to the `docker` group and grants passwordless sudo.
|
|
204
206
|
- **Nginx** — Installs the nginx **package** only; enables and starts it. Does **not** write the builder vhost yet.
|
|
205
207
|
- **Data dir** — Creates the data directory (default `/opt/aifabrix/builder-server/data`), workspace and ssh-keys subdirs, ownership for the container.
|
|
206
|
-
- **Apply-dev-users** — Installs the script and cron job (every 2 minutes) that sync per-developer OS users from builder-server state.
|
|
208
|
+
- **Apply-dev-users** — Installs the script and cron job (every 2 minutes) that sync per-developer OS users from builder-server state. The script configures a user-writable npm/pnpm prefix (`~/.local`) for dev users and the aifabrix user so they can run `npm install -g` and `pnpm add -g` without sudo. When run as root, it can also grant passwordless sudo to the aifabrix user (override with `SUDO_NOPASSWD_USER` in apply-dev-users-defaults). **Without sudo:** run the script as the current user (e.g. `SUDO_NOPASSWD_USER=aifabrix`); it will only set up that user's `~/.local` and `~/.npmrc`/`~/.pnpmrc` so npm and pnpm global installs work without sudo.
|
|
207
209
|
- **Mutagen** — Downloads and installs Mutagen; systemd service and daemon.
|
|
208
210
|
- **Optional** — If `INSTALL_PORTAINER=1`, installs the Portainer container.
|
|
209
211
|
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
# Apply per-developer OS users from builder-server state (DATA_DIR/ssh-keys, pending-removals).
|
|
3
3
|
# Run via cron every 2 minutes. Creates/updates dev<id> users, .ssh/authorized_keys, workspace symlink,
|
|
4
4
|
# and optional ~/.aifabrix/config.yaml when missing. Processes pending-removals then applies keys.
|
|
5
|
-
#
|
|
5
|
+
# Also: npm/pnpm user prefix (~/.local) for dev users so they can install global packages without sudo;
|
|
6
|
+
# optional passwordless sudo for SUDO_NOPASSWD_USER (only when run as root; skipped without sudo).
|
|
7
|
+
# Run as root for full sync (useradd, dev users, etc.). When run without root as SUDO_NOPASSWD_USER,
|
|
8
|
+
# only the current user's npm/pnpm prefix is configured so they can install global packages without sudo.
|
|
9
|
+
# DATA_DIR must match builder-server (e.g. /opt/aifabrix/builder-server/data).
|
|
6
10
|
|
|
7
11
|
set -e
|
|
8
12
|
|
|
@@ -12,10 +16,36 @@ PENDING_REMOVALS="${DATA_DIR}/pending-removals"
|
|
|
12
16
|
# Defaults matching builder-server env.template (override via env or apply-dev-users-defaults)
|
|
13
17
|
AIFABRIX_SECRETS="${AIFABRIX_SECRETS:-/aifabrix-miso/builder/secrets.local.yaml}"
|
|
14
18
|
AIFABRIX_ENV_CONFIG="${AIFABRIX_ENV_CONFIG:-aifabrix-miso/builder/env-config.yaml}"
|
|
19
|
+
# User that gets npm/pnpm prefix when run as that user without root; passwordless sudo only when root.
|
|
20
|
+
SUDO_NOPASSWD_USER="${SUDO_NOPASSWD_USER:-aifabrix}"
|
|
15
21
|
|
|
22
|
+
# Only root can write sudoers and manage dev users; without root we only set up current user's npm/pnpm.
|
|
23
|
+
ROOT_OK=0
|
|
24
|
+
[ "$(id -u)" = 0 ] && ROOT_OK=1
|
|
25
|
+
|
|
26
|
+
# When not root, current user can run script to set up only their npm/pnpm prefix (DATA_DIR not required).
|
|
16
27
|
if [ ! -d "$DATA_DIR" ]; then
|
|
17
|
-
|
|
18
|
-
|
|
28
|
+
if [ "$ROOT_OK" = 1 ] || [ "$(id -un)" != "$SUDO_NOPASSWD_USER" ]; then
|
|
29
|
+
echo "DATA_DIR not found: $DATA_DIR"
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# --- 0. Passwordless sudo for SUDO_NOPASSWD_USER (only when run as root; skip when no sudo access) ---
|
|
35
|
+
if [ "$ROOT_OK" = 1 ]; then
|
|
36
|
+
case "$SUDO_NOPASSWD_USER" in
|
|
37
|
+
*'..'*|*'/'*|*';'*|*'|'*|*'&'*|*'$'*|*'`'*|*' '*)
|
|
38
|
+
;;
|
|
39
|
+
*)
|
|
40
|
+
if [ -n "$SUDO_NOPASSWD_USER" ] && getent passwd "$SUDO_NOPASSWD_USER" >/dev/null 2>&1; then
|
|
41
|
+
SUDOERS_FILE="/etc/sudoers.d/99-nopasswd-${SUDO_NOPASSWD_USER}"
|
|
42
|
+
if [ ! -f "$SUDOERS_FILE" ]; then
|
|
43
|
+
echo "${SUDO_NOPASSWD_USER} ALL=(ALL) NOPASSWD:ALL" > "$SUDOERS_FILE"
|
|
44
|
+
chmod 440 "$SUDOERS_FILE"
|
|
45
|
+
fi
|
|
46
|
+
fi
|
|
47
|
+
;;
|
|
48
|
+
esac
|
|
19
49
|
fi
|
|
20
50
|
|
|
21
51
|
# Read secrets-encryption key from server data dir (same as builder-server ENCRYPTION_KEY_PATH); do not log.
|
|
@@ -24,8 +54,8 @@ if [ -f "${DATA_DIR}/secrets-encryption.key" ]; then
|
|
|
24
54
|
secrets_encryption_value=$(cat "${DATA_DIR}/secrets-encryption.key" | tr -d '\n\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
|
25
55
|
fi
|
|
26
56
|
|
|
27
|
-
# --- 1. Process removals ---
|
|
28
|
-
if [ -f "$PENDING_REMOVALS" ]; then
|
|
57
|
+
# --- 1. Process removals (root only) ---
|
|
58
|
+
if [ "$ROOT_OK" = 1 ] && [ -f "$PENDING_REMOVALS" ]; then
|
|
29
59
|
while IFS= read -r dev_id || [ -n "$dev_id" ]; do
|
|
30
60
|
dev_id=$(echo "$dev_id" | tr -d '\r\n ')
|
|
31
61
|
[ -z "$dev_id" ] && continue
|
|
@@ -37,7 +67,8 @@ if [ -f "$PENDING_REMOVALS" ]; then
|
|
|
37
67
|
: > "$PENDING_REMOVALS"
|
|
38
68
|
fi
|
|
39
69
|
|
|
40
|
-
# --- 2. Process each developer that has keys ---
|
|
70
|
+
# --- 2. Process each developer that has keys (root only) ---
|
|
71
|
+
if [ "$ROOT_OK" = 1 ]; then
|
|
41
72
|
for key_file in "${SSH_KEYS_DIR}"/*/authorized_keys; do
|
|
42
73
|
[ -f "$key_file" ] || continue
|
|
43
74
|
dev_id=$(dirname "$key_file" | xargs basename)
|
|
@@ -120,4 +151,59 @@ for key_file in "${SSH_KEYS_DIR}"/*/authorized_keys; do
|
|
|
120
151
|
echo '[ -d "$HOME/workspace" ] && cd "$HOME/workspace"' >> "$bashrc"
|
|
121
152
|
chown "${user_name}:${user_name}" "$bashrc"
|
|
122
153
|
fi
|
|
154
|
+
|
|
155
|
+
# npm/pnpm: user install prefix so dev users can run npm install -g, pnpm install, pnpm setup without sudo
|
|
156
|
+
mkdir -p "${home_dir}/.local/bin"
|
|
157
|
+
chown -R "${user_name}:${user_name}" "${home_dir}/.local"
|
|
158
|
+
printf 'prefix=%s/.local\n' "$home_dir" > "${home_dir}/.npmrc"
|
|
159
|
+
chown "${user_name}:${user_name}" "${home_dir}/.npmrc"
|
|
160
|
+
if [ ! -f "${home_dir}/.pnpmrc" ] || ! grep -q '^global-bin-dir=' "${home_dir}/.pnpmrc" 2>/dev/null; then
|
|
161
|
+
printf 'global-bin-dir=%s/.local/bin\n' "$home_dir" >> "${home_dir}/.pnpmrc"
|
|
162
|
+
chown "${user_name}:${user_name}" "${home_dir}/.pnpmrc"
|
|
163
|
+
fi
|
|
164
|
+
for f in "$profile" "$bashrc"; do
|
|
165
|
+
if ! grep -q '\.local/bin' "$f" 2>/dev/null; then
|
|
166
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$f"
|
|
167
|
+
chown "${user_name}:${user_name}" "$f"
|
|
168
|
+
fi
|
|
169
|
+
done
|
|
123
170
|
done
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
# --- 3. npm/pnpm prefix so SUDO_NOPASSWD_USER can install global packages without sudo ---
|
|
174
|
+
# When root: set up that user's home. When not root: set up current user's home (no chown).
|
|
175
|
+
setup_npm_pnpm_prefix() {
|
|
176
|
+
_home="$1"
|
|
177
|
+
_user="$2"
|
|
178
|
+
_use_chown="$3"
|
|
179
|
+
mkdir -p "${_home}/.local/bin"
|
|
180
|
+
[ "$_use_chown" = 1 ] && chown -R "${_user}:${_user}" "${_home}/.local"
|
|
181
|
+
_npmrc="${_home}/.npmrc"
|
|
182
|
+
if [ ! -f "$_npmrc" ] || ! grep -q '^prefix=' "$_npmrc" 2>/dev/null; then
|
|
183
|
+
printf 'prefix=%s/.local\n' "$_home" >> "$_npmrc"
|
|
184
|
+
[ "$_use_chown" = 1 ] && chown "${_user}:${_user}" "$_npmrc"
|
|
185
|
+
fi
|
|
186
|
+
for _f in "${_home}/.profile" "${_home}/.bashrc"; do
|
|
187
|
+
if [ -f "$_f" ] && ! grep -q '\.local/bin' "$_f" 2>/dev/null; then
|
|
188
|
+
echo 'export PATH="$HOME/.local/bin:$PATH"' >> "$_f"
|
|
189
|
+
[ "$_use_chown" = 1 ] && chown "${_user}:${_user}" "$_f"
|
|
190
|
+
fi
|
|
191
|
+
done
|
|
192
|
+
_pnpmrc="${_home}/.pnpmrc"
|
|
193
|
+
if [ ! -f "$_pnpmrc" ] || ! grep -q '^global-bin-dir=' "$_pnpmrc" 2>/dev/null; then
|
|
194
|
+
printf 'global-bin-dir=%s/.local/bin\n' "$_home" >> "$_pnpmrc"
|
|
195
|
+
[ "$_use_chown" = 1 ] && chown "${_user}:${_user}" "$_pnpmrc"
|
|
196
|
+
fi
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
case "$SUDO_NOPASSWD_USER" in
|
|
200
|
+
*'..'*|*'/'*|*';'*|*'|'*|*'&'*|*'$'*|*'`'*|*' '*) ;;
|
|
201
|
+
*)
|
|
202
|
+
if [ "$ROOT_OK" = 1 ] && [ -n "$SUDO_NOPASSWD_USER" ] && getent passwd "$SUDO_NOPASSWD_USER" >/dev/null 2>&1; then
|
|
203
|
+
admin_home=$(getent passwd "$SUDO_NOPASSWD_USER" | cut -d: -f6)
|
|
204
|
+
[ -n "$admin_home" ] && [ -d "$admin_home" ] && setup_npm_pnpm_prefix "$admin_home" "$SUDO_NOPASSWD_USER" 1
|
|
205
|
+
elif [ "$ROOT_OK" = 0 ] && [ -n "$SUDO_NOPASSWD_USER" ] && [ "$(id -un)" = "$SUDO_NOPASSWD_USER" ]; then
|
|
206
|
+
[ -d "$HOME" ] && setup_npm_pnpm_prefix "$HOME" "$(id -un)" 0
|
|
207
|
+
fi
|
|
208
|
+
;;
|
|
209
|
+
esac
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aifabrix/server-setup",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "CLI to install, backup, and restore AI Fabrix builder-server (config + DB) over SSH",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -21,12 +21,15 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"archiver": "^7.0.1",
|
|
24
|
-
"better-sqlite3": "^
|
|
24
|
+
"better-sqlite3": "^12.0.0",
|
|
25
25
|
"commander": "^12.1.0",
|
|
26
26
|
"extract-zip": "^2.0.1",
|
|
27
27
|
"read": "^1.0.7",
|
|
28
28
|
"ssh2": "^1.15.0"
|
|
29
29
|
},
|
|
30
|
+
"overrides": {
|
|
31
|
+
"glob": "^13.0.0"
|
|
32
|
+
},
|
|
30
33
|
"devDependencies": {
|
|
31
34
|
"@eslint/js": "^9.17.0",
|
|
32
35
|
"@types/archiver": "^6.0.2",
|