返回 CodeWhale
bootstrap-ubuntu.sh
根目录 / scripts / tencent-lighthouse / bootstrap-ubuntu.sh
1 #!/usr/bin/env bash
2 set -euo pipefail
3
4 if [[ "${EUID}" -ne 0 ]]; then
5 echo "Run as root: sudo bash scripts/tencent-lighthouse/bootstrap-ubuntu.sh" >&2
6 exit 1
7 fi
8
9 CODEWHALE_USER="${CODEWHALE_USER:-${DEEPSEEK_USER:-codewhale}}"
10 CODEWHALE_ROOT="${CODEWHALE_ROOT:-${DEEPSEEK_ROOT:-/opt/codewhale}}"
11 WHALEBRO_ROOT="${WHALEBRO_ROOT:-/opt/whalebro}"
12 REPO_URL="${CODEWHALE_REPO_URL:-${DEEPSEEK_REPO_URL:-https://github.com/codewhale-hq/CodeWhale.git}}"
13 WHALEBRO_EXTRA_REPOS="${WHALEBRO_EXTRA_REPOS:-}"
14 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15 SOURCE_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
16 SOURCE_BRANCH="$(git -C "${SOURCE_ROOT}" branch --show-current 2>/dev/null || true)"
17 REPO_BRANCH="${CODEWHALE_REPO_BRANCH:-${DEEPSEEK_REPO_BRANCH:-${SOURCE_BRANCH:-main}}}"
18
19 # SSH_ALLOWED_CIDRS accepts comma/space separated IPv4 /8-/32 or IPv6
20 # /16-/128 CIDRs. Validate the whole list before any host changes. For a
21 # deliberately public SSH endpoint, explicitly set SSH_ALLOW_ANY_SOURCE=1.
22 ssh_cidrs="${SSH_ALLOWED_CIDRS:-}"
23 ssh_cidrs="${ssh_cidrs//,/ }"
24 if [[ -z "${ssh_cidrs//[[:space:]]/}" ]]; then
25 if [[ "${SSH_ALLOW_ANY_SOURCE:-0}" != "1" ]]; then
26 echo "Set SSH_ALLOWED_CIDRS to trusted source CIDRs, or explicitly set SSH_ALLOW_ANY_SOURCE=1. No host changes were made." >&2
27 exit 1
28 fi
29 else
30 command -v python3 >/dev/null || { echo "Python 3 is required to validate SSH source CIDRs. No host changes were made." >&2; exit 1; }
31 for cidr in ${ssh_cidrs}; do
32 if ! python3 -c 'import ipaddress, sys
33 value = sys.argv[1]
34 if "/" not in value or "%" in value:
35 sys.exit(1)
36 try:
37 network = ipaddress.ip_network(value, strict=False)
38 except ValueError:
39 sys.exit(1)
40 sys.exit(0 if (8 if network.version == 4 else 16) <= network.prefixlen else 1)' "${cidr}"; then
41 echo "SSH_ALLOWED_CIDRS entry '${cidr}' is not an IPv4 /8-/32 or IPv6 /16-/128 CIDR. No host changes were made." >&2
42 exit 1
43 fi
44 done
45 fi
46
47 apt-get update
48 apt-get install -y \
49 ca-certificates \
50 curl \
51 git \
52 iproute2 \
53 openssh-client \
54 build-essential \
55 pkg-config \
56 libdbus-1-dev \
57 libssl-dev \
58 nodejs \
59 npm \
60 rsync \
61 tmux \
62 fail2ban \
63 ufw
64
65 node_major="$(node -p "Number(process.versions.node.split('.')[0])")"
66 if (( node_major < 18 )); then
67 echo "Node.js 18+ is required for the phone bridges; install a newer Node.js before running install-services.sh." >&2
68 fi
69
70 if ! id -u "${CODEWHALE_USER}" >/dev/null 2>&1; then
71 useradd --create-home --shell /bin/bash "${CODEWHALE_USER}"
72 fi
73
74 install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${CODEWHALE_ROOT}"
75 install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${CODEWHALE_ROOT}/bridge"
76 install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${CODEWHALE_ROOT}/telegram-bridge"
77 install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${WHALEBRO_ROOT}"
78 install -d -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" "${WHALEBRO_ROOT}/worktrees"
79 install -d -m 0750 -o root -g "${CODEWHALE_USER}" /etc/codewhale
80 install -d -m 0700 -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" /var/lib/codewhale-feishu-bridge
81 install -d -m 0700 -o "${CODEWHALE_USER}" -g "${CODEWHALE_USER}" /var/lib/codewhale-telegram-bridge
82
83 if [[ ! -d "${WHALEBRO_ROOT}/codewhale/.git" ]]; then
84 sudo -u "${CODEWHALE_USER}" git clone --branch "${REPO_BRANCH}" "${REPO_URL}" "${WHALEBRO_ROOT}/codewhale"
85 fi
86
87 for repo_spec in ${WHALEBRO_EXTRA_REPOS}; do
88 repo_name="${repo_spec%%=*}"
89 repo_url="${repo_spec#*=}"
90 if [[ -z "${repo_name}" || -z "${repo_url}" || "${repo_name}" == "${repo_url}" ]]; then
91 echo "Skipping malformed WHALEBRO_EXTRA_REPOS entry: ${repo_spec}" >&2
92 continue
93 fi
94 if [[ ! -d "${WHALEBRO_ROOT}/${repo_name}/.git" ]]; then
95 sudo -u "${CODEWHALE_USER}" git clone "${repo_url}" "${WHALEBRO_ROOT}/${repo_name}" || {
96 echo "Warning: failed to clone optional repo ${repo_name} from ${repo_url}" >&2
97 }
98 fi
99 done
100
101 if [[ ! -f /etc/codewhale/runtime.env ]]; then
102 cat >/etc/codewhale/runtime.env <<'EOF'
103 CODEWHALE_RUNTIME_TOKEN=replace-with-long-random-token
104 CODEWHALE_RUNTIME_PORT=7878
105 CODEWHALE_RUNTIME_WORKERS=2
106 CODEWHALE_PROVIDER=deepseek
107 DEEPSEEK_API_KEY=replace-with-provider-key
108 RUST_LOG=info
109 EOF
110 chown root:"${CODEWHALE_USER}" /etc/codewhale/runtime.env
111 chmod 0640 /etc/codewhale/runtime.env
112 fi
113
114 if [[ ! -f /etc/codewhale/feishu-bridge.env ]]; then
115 cat >/etc/codewhale/feishu-bridge.env <<'EOF'
116 FEISHU_APP_ID=cli_xxxxxxxxxxxxxxxx
117 FEISHU_APP_SECRET=replace-with-app-secret
118 FEISHU_DOMAIN=feishu
119 CODEWHALE_RUNTIME_URL=http://127.0.0.1:7878
120 CODEWHALE_RUNTIME_TOKEN=replace-with-same-token-as-runtime-env
121 CODEWHALE_WORKSPACE=/opt/whalebro
122 CODEWHALE_MODEL=auto
123 CODEWHALE_MODE=agent
124 CODEWHALE_ALLOW_SHELL=true
125 CODEWHALE_TRUST_MODE=false
126 CODEWHALE_AUTO_APPROVE=false
127 CODEWHALE_CHAT_ALLOWLIST=
128 CODEWHALE_ALLOW_UNLISTED=false
129 FEISHU_THREAD_MAP_PATH=/var/lib/codewhale-feishu-bridge/thread-map.json
130 FEISHU_ALLOW_GROUPS=false
131 FEISHU_REQUIRE_PREFIX_IN_GROUP=true
132 FEISHU_GROUP_PREFIX=/cw
133 FEISHU_MAX_REPLY_CHARS=3500
134 CODEWHALE_TURN_TIMEOUT_MS=900000
135 EOF
136 chown root:"${CODEWHALE_USER}" /etc/codewhale/feishu-bridge.env
137 chmod 0640 /etc/codewhale/feishu-bridge.env
138 fi
139
140 # Apply the already validated narrow rules before deleting the broad one.
141 # SSH_ALLOW_ANY_SOURCE=1 is an explicit operator choice, never the default.
142 if [[ -n "${ssh_cidrs//[[:space:]]/}" ]]; then
143 for cidr in ${ssh_cidrs}; do
144 ufw allow from "${cidr}" to any app OpenSSH
145 done
146 ufw delete allow OpenSSH >/dev/null 2>&1 || true
147 else
148 echo "WARNING: SSH is reachable from every source. SSH_ALLOW_ANY_SOURCE=1 explicitly allows this. Set SSH_ALLOWED_CIDRS (e.g. 203.0.113.4/32) and rerun to restrict it." >&2
149 ufw allow OpenSSH
150 fi
151 ufw --force enable
152
153 cat <<EOF
154
155 Base server setup complete.
156
157 Next:
158 1. Install Rust 1.89+ for ${CODEWHALE_USER}; rustup is the usual path.
159 2. Build/install the unified binary:
160 sudo -iu ${CODEWHALE_USER}
161 cd ${WHALEBRO_ROOT}/codewhale
162 cargo install --path crates/cli --locked --force
163 3. Copy integrations/feishu-bridge or integrations/telegram-bridge to ${CODEWHALE_ROOT} and run npm install.
164 4. Edit /etc/codewhale/runtime.env and the selected bridge env file.
165 5. Install systemd units with scripts/tencent-lighthouse/install-services.sh.
166 6. After the env files are edited and services are started, run:
167 sudo bash scripts/tencent-lighthouse/doctor.sh
168
169 EOF
170
170 lines BASH