Files
dingMCP/distribution/bootstrap-posix.template.sh

190 lines
7.6 KiB
Bash

#!/bin/sh
set -eu
ding_source_bases='{{NODE_SOURCE_BASES_POSIX}}'
ding_installer_urls='{{INSTALLER_URLS_POSIX}}'
ding_installer_sha256='{{INSTALLER_SHA256}}'
ding_public_key_urls='{{PUBLIC_KEY_URLS_POSIX}}'
ding_public_key_sha256='{{PUBLIC_KEY_SHA256}}'
ding_manifest_url='{{MANIFEST_URL}}'
ding_manifest_mirror_urls='{{MANIFEST_MIRROR_URLS_POSIX}}'
ding_temp_dir="$(mktemp -d "${TMPDIR:-/tmp}/dingmcp-install.XXXXXX")"
trap 'rm -rf "$ding_temp_dir"' EXIT HUP INT TERM
ding_progress() {
printf '[dingtalk-approval] %s\n' "$1" >&2
}
ding_hash_file() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | awk '{print $1}'
elif command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$1" | awk '{print $1}'
else
echo '缺少 sha256sum 或 shasum,无法安全安装' >&2
return 1
fi
}
ding_fetch() {
if command -v curl >/dev/null 2>&1; then
if ding_fetch_http="$(curl --fail --location --silent --show-error --connect-timeout 10 --max-time 90 \
--speed-time 20 --speed-limit 1024 \
--write-out '%{http_code}' --output "$2" "$1")"; then
return 0
else
ding_fetch_status=$?
fi
if { [ "$ding_fetch_status" -eq 22 ] && [ "$ding_fetch_http" = '404' ]; } \
|| [ "$ding_fetch_status" -eq 5 ] || [ "$ding_fetch_status" -eq 6 ] \
|| [ "$ding_fetch_status" -eq 7 ] || [ "$ding_fetch_status" -eq 18 ] \
|| [ "$ding_fetch_status" -eq 28 ] || [ "$ding_fetch_status" -eq 35 ] \
|| [ "$ding_fetch_status" -eq 52 ] || [ "$ding_fetch_status" -eq 55 ] \
|| [ "$ding_fetch_status" -eq 56 ]; then
return 75
fi
echo "下载失败且不允许换源(HTTP ${ding_fetch_http:-unknown} / curl $ding_fetch_status):$1" >&2
return 1
elif command -v wget >/dev/null 2>&1; then
ding_wget_log="${2}.wget.log"
if wget --https-only --timeout=90 --server-response --output-document="$2" "$1" 2>"$ding_wget_log"; then
rm -f "$ding_wget_log"
return 0
else
ding_fetch_status=$?
fi
ding_fetch_http="$(awk '/^ HTTP\/[0-9.]+ [0-9][0-9][0-9]/{code=$2} END{print code}' "$ding_wget_log")"
cat "$ding_wget_log" >&2
rm -f "$ding_wget_log"
if [ "$ding_fetch_http" = '404' ] || [ "$ding_fetch_status" -eq 4 ] || [ "$ding_fetch_status" -eq 5 ]; then
return 75
fi
echo "下载失败且不允许换源(HTTP ${ding_fetch_http:-unknown} / wget $ding_fetch_status):$1" >&2
return 1
else
echo '缺少 curl 或 wget,无法下载安装文件' >&2
return 1
fi
}
ding_download_verified() {
ding_download_target="$1"
ding_download_expected="$2"
shift 2
ding_download_succeeded=0
for ding_download_url in "$@"; do
ding_progress "正在下载并校验:$ding_download_url"
rm -f "$ding_download_target"
if ding_fetch "$ding_download_url" "$ding_download_target"; then
ding_download_succeeded=1
ding_download_actual="$(ding_hash_file "$ding_download_target")"
if [ "$ding_download_actual" != "$ding_download_expected" ]; then
echo "下载文件 SHA-256 不匹配,停止安装:$ding_download_url" >&2
return 1
fi
return 0
else
ding_download_status=$?
if [ "$ding_download_status" -ne 75 ]; then return 1; fi
fi
done
if [ "$ding_download_succeeded" -eq 0 ]; then
echo '所有下载源均不可用' >&2
fi
return 1
}
ding_test_node() {
[ -x "$1" ] || return 1
"$1" -e 'const [a,b]=process.versions.node.split(".").map(Number);process.exit(a>22||(a===22&&b>=13)?0:1)' >/dev/null 2>&1
}
case "$(uname -s)" in
Linux) ding_os='linux' ;;
Darwin) ding_os='darwin' ;;
*) echo "不支持的操作系统:$(uname -s)" >&2; exit 1 ;;
esac
case "$(uname -m)" in
x86_64|amd64) ding_arch='x64' ;;
arm64|aarch64) ding_arch='arm64' ;;
*) echo "不支持的 CPU 架构:$(uname -m)" >&2; exit 1 ;;
esac
case "$ding_os-$ding_arch" in
linux-x64) ding_node_file='{{NODE_LINUX_X64_FILE}}'; ding_node_sha256='{{NODE_LINUX_X64_SHA256}}' ;;
linux-arm64) ding_node_file='{{NODE_LINUX_ARM64_FILE}}'; ding_node_sha256='{{NODE_LINUX_ARM64_SHA256}}' ;;
darwin-x64) ding_node_file='{{NODE_DARWIN_X64_FILE}}'; ding_node_sha256='{{NODE_DARWIN_X64_SHA256}}' ;;
darwin-arm64) ding_node_file='{{NODE_DARWIN_ARM64_FILE}}'; ding_node_sha256='{{NODE_DARWIN_ARM64_SHA256}}' ;;
esac
ding_progress "1/4 检查 $ding_os-$ding_arch 的 Node/npm 运行环境"
ding_node_path=''
if command -v node >/dev/null 2>&1 && command -v npm >/dev/null 2>&1 && ding_test_node "$(command -v node)"; then
ding_node_path="$(command -v node)"
ding_progress "2/4 复用兼容的系统 Node"
else
if [ -n "${DINGTALK_APPROVAL_CLIENT_HOME:-}" ]; then
ding_client_root="$DINGTALK_APPROVAL_CLIENT_HOME"
elif [ "$ding_os" = 'darwin' ]; then
ding_client_root="$HOME/Library/Application Support/dingtalk-approval"
else
ding_client_root="${XDG_DATA_HOME:-$HOME/.local/share}/dingtalk-approval"
fi
ding_runtime_name="${ding_node_file%.tar.gz}"
ding_runtime_parent="$ding_client_root/node"
ding_runtime_dir="$ding_runtime_parent/$ding_runtime_name"
ding_node_path="$ding_runtime_dir/bin/node"
ding_npm_cli="$ding_runtime_dir/lib/node_modules/npm/bin/npm-cli.js"
if ! ding_test_node "$ding_node_path" || [ ! -f "$ding_npm_cli" ]; then
ding_progress "2/4 系统 Node 不可用,准备插件私有 Node;下载可能需要一些时间"
mkdir -p "$ding_runtime_parent"
chmod 700 "$ding_client_root" "$ding_runtime_parent" 2>/dev/null || true
ding_runtime_archive="$ding_temp_dir/$ding_node_file"
ding_runtime_urls=''
for ding_source_base in $ding_source_bases; do
ding_runtime_urls="$ding_runtime_urls $ding_source_base$ding_node_file"
done
# The URLs are fixed by the signed release process and contain no whitespace.
# shellcheck disable=SC2086
ding_download_verified "$ding_runtime_archive" "$ding_node_sha256" $ding_runtime_urls
ding_extract_root="$ding_temp_dir/node-extract"
mkdir -p "$ding_extract_root"
tar -xzf "$ding_runtime_archive" -C "$ding_extract_root"
ding_extracted_dir="$ding_extract_root/$ding_runtime_name"
if ! ding_test_node "$ding_extracted_dir/bin/node" || [ ! -f "$ding_extracted_dir/lib/node_modules/npm/bin/npm-cli.js" ]; then
echo '便携 Node 归档缺少可用的 node 或 npm-cli.js' >&2
exit 1
fi
rm -rf "$ding_runtime_dir"
mv "$ding_extracted_dir" "$ding_runtime_dir"
else
ding_progress "2/4 复用已安装的插件私有 Node"
fi
fi
ding_progress '3/4 下载并校验签名安装器与发布公钥'
ding_installer="$ding_temp_dir/installer.mjs"
ding_public_key="$ding_temp_dir/release-signing-public.pem"
ding_download_verified "$ding_installer" "$ding_installer_sha256" $ding_installer_urls
ding_download_verified "$ding_public_key" "$ding_public_key_sha256" $ding_public_key_urls
ding_progress '4/4 执行安装器;最终 JSON 输出到 stdout,阶段进度输出到 stderr'
set -- "$ding_installer" install --manifest-url "$ding_manifest_url"
for ding_manifest_mirror_url in $ding_manifest_mirror_urls; do
set -- "$@" --manifest-mirror-url "$ding_manifest_mirror_url"
done
set -- "$@" --public-key "$ding_public_key" --json
ding_node_proxy_arg=''
if [ -n "${HTTPS_PROXY:-}${https_proxy:-}${HTTP_PROXY:-}${http_proxy:-}${ALL_PROXY:-}${all_proxy:-}" ] \
&& ! printf '%s' "${NODE_OPTIONS:-}" | grep -Eq '(^|[[:space:]])--use-env-proxy($|[[:space:]])' \
&& ! printf '%s' "${NODE_USE_ENV_PROXY:-}" | grep -Eiq '^(1|true)$' \
&& "$ding_node_path" -e 'process.exit(process.allowedNodeEnvironmentFlags.has("--use-env-proxy") ? 0 : 1)'; then
ding_node_proxy_arg='--use-env-proxy'
fi
if [ -n "$ding_node_proxy_arg" ]; then
"$ding_node_path" "$ding_node_proxy_arg" "$@"
else
"$ding_node_path" "$@"
fi