first commit
This commit is contained in:
178
scripts/build_install.sh
Normal file
178
scripts/build_install.sh
Normal file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
GO_MOD_FILE="${ROOT_DIR}/go.mod"
|
||||
DEFAULT_GO_VERSION="$(awk '/^go / { print $2; exit }' "${GO_MOD_FILE}")"
|
||||
|
||||
GO_VERSION="${DEFAULT_GO_VERSION}"
|
||||
OUTPUT_PATH="${ROOT_DIR}/api"
|
||||
TOOLS_DIR="${ROOT_DIR}/.build-tools"
|
||||
CACHE_DIR="${ROOT_DIR}/.build-cache"
|
||||
CLEAN_GO="false"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: scripts/build_install.sh [options]
|
||||
|
||||
Options:
|
||||
--go-version <version> Go version to download, default: ${GO_VERSION}
|
||||
--output <path> Build output path, default: ${OUTPUT_PATH}
|
||||
--tools-dir <dir> Toolchain directory, default: ${TOOLS_DIR}
|
||||
--cache-dir <dir> Go cache directory, default: ${CACHE_DIR}
|
||||
--clean-go Re-download the Go toolchain even if it exists
|
||||
-h, --help Show this help
|
||||
|
||||
Examples:
|
||||
bash scripts/build_install.sh
|
||||
bash scripts/build_install.sh --output ./package/api
|
||||
EOF
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--go-version)
|
||||
GO_VERSION="$2"
|
||||
shift 2
|
||||
;;
|
||||
--output)
|
||||
OUTPUT_PATH="$2"
|
||||
shift 2
|
||||
;;
|
||||
--tools-dir)
|
||||
TOOLS_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--cache-dir)
|
||||
CACHE_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--clean-go)
|
||||
CLEAN_GO="true"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "error: unknown option: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "error: required command not found: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
detect_platform() {
|
||||
local uname_os uname_arch
|
||||
uname_os="$(uname -s)"
|
||||
uname_arch="$(uname -m)"
|
||||
|
||||
case "${uname_os}" in
|
||||
Linux) GO_OS="linux" ;;
|
||||
Darwin) GO_OS="darwin" ;;
|
||||
*)
|
||||
echo "error: unsupported operating system: ${uname_os}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case "${uname_arch}" in
|
||||
x86_64|amd64) GO_ARCH="amd64" ;;
|
||||
aarch64|arm64) GO_ARCH="arm64" ;;
|
||||
*)
|
||||
echo "error: unsupported architecture: ${uname_arch}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
download_go() {
|
||||
local archive_name archive_path download_url current_version
|
||||
|
||||
mkdir -p "${TOOLS_DIR}"
|
||||
archive_name="go${GO_VERSION}.${GO_OS}-${GO_ARCH}.tar.gz"
|
||||
archive_path="${TOOLS_DIR}/${archive_name}"
|
||||
GO_ROOT="${TOOLS_DIR}/go"
|
||||
|
||||
if [[ -x "${GO_ROOT}/bin/go" ]]; then
|
||||
current_version="$("${GO_ROOT}/bin/go" version | awk '{print $3}' | sed 's/^go//')"
|
||||
if [[ "${current_version}" != "${GO_VERSION}" ]]; then
|
||||
rm -rf "${GO_ROOT}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CLEAN_GO}" == "true" ]]; then
|
||||
rm -rf "${GO_ROOT}"
|
||||
rm -f "${archive_path}"
|
||||
fi
|
||||
|
||||
if [[ ! -x "${GO_ROOT}/bin/go" ]]; then
|
||||
download_url="https://go.dev/dl/${archive_name}"
|
||||
echo "downloading Go ${GO_VERSION} for ${GO_OS}/${GO_ARCH}..."
|
||||
if [[ ! -f "${archive_path}" ]]; then
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL "${download_url}" -o "${archive_path}"
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -O "${archive_path}" "${download_url}"
|
||||
else
|
||||
echo "error: curl or wget is required to download Go" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf "${GO_ROOT}"
|
||||
tar -C "${TOOLS_DIR}" -xzf "${archive_path}"
|
||||
fi
|
||||
}
|
||||
|
||||
build_binary() {
|
||||
local output_dir
|
||||
|
||||
mkdir -p "${CACHE_DIR}/gocache" "${CACHE_DIR}/gomodcache"
|
||||
output_dir="$(dirname "${OUTPUT_PATH}")"
|
||||
mkdir -p "${output_dir}"
|
||||
|
||||
echo "building ${OUTPUT_PATH}..."
|
||||
(
|
||||
cd "${ROOT_DIR}"
|
||||
export GOROOT="${GO_ROOT}"
|
||||
export PATH="${GO_ROOT}/bin:${PATH}"
|
||||
export GOCACHE="${CACHE_DIR}/gocache"
|
||||
export GOMODCACHE="${CACHE_DIR}/gomodcache"
|
||||
"${GO_ROOT}/bin/go" build -trimpath -o "${OUTPUT_PATH}" ./cmd/api
|
||||
)
|
||||
chmod +x "${OUTPUT_PATH}" || true
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
cat <<EOF
|
||||
|
||||
Build complete.
|
||||
|
||||
Go version: ${GO_VERSION}
|
||||
Toolchain: ${GO_ROOT}
|
||||
Output: ${OUTPUT_PATH}
|
||||
Cache dir: ${CACHE_DIR}
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
require_cmd tar
|
||||
detect_platform
|
||||
download_go
|
||||
build_binary
|
||||
print_summary
|
||||
}
|
||||
|
||||
main "$@"
|
||||
210
scripts/install.sh
Normal file
210
scripts/install.sh
Normal file
@@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="singbox-gopanel"
|
||||
SERVICE_NAME="singbox-gopanel"
|
||||
INSTALL_DIR="/opt/${APP_NAME}"
|
||||
RUN_USER="${SUDO_USER:-${USER:-root}}"
|
||||
SKIP_SERVICE="false"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PACKAGE_DIR="${SCRIPT_DIR}"
|
||||
BIN_NAME="api"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: install.sh [options]
|
||||
|
||||
Options:
|
||||
--install-dir <dir> Install directory, default: ${INSTALL_DIR}
|
||||
--service-name <name> systemd service name, default: ${SERVICE_NAME}
|
||||
--run-user <user> Runtime user, default: ${RUN_USER}
|
||||
--package-dir <dir> Package directory, default: ${PACKAGE_DIR}
|
||||
--skip-service Skip systemd service installation
|
||||
-h, --help Show this help
|
||||
|
||||
Expected package layout:
|
||||
${BIN_NAME}
|
||||
frontend/
|
||||
docs/
|
||||
README.md
|
||||
.env.example (optional)
|
||||
|
||||
Example:
|
||||
sudo bash ./install.sh --install-dir /opt/singbox-gopanel --run-user root
|
||||
EOF
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--install-dir)
|
||||
INSTALL_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--service-name)
|
||||
SERVICE_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--run-user)
|
||||
RUN_USER="$2"
|
||||
shift 2
|
||||
;;
|
||||
--package-dir)
|
||||
PACKAGE_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
--skip-service)
|
||||
SKIP_SERVICE="true"
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "error: unknown option: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
ensure_root() {
|
||||
if [[ "${EUID}" -ne 0 ]]; then
|
||||
echo "error: please run as root or via sudo" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_run_user() {
|
||||
if ! id "${RUN_USER}" >/dev/null 2>&1; then
|
||||
echo "error: run user does not exist: ${RUN_USER}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_package_layout() {
|
||||
if [[ ! -f "${PACKAGE_DIR}/${BIN_NAME}" ]]; then
|
||||
echo "error: binary not found: ${PACKAGE_DIR}/${BIN_NAME}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -d "${PACKAGE_DIR}/frontend" ]]; then
|
||||
echo "error: frontend directory not found: ${PACKAGE_DIR}/frontend" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -d "${PACKAGE_DIR}/docs" ]]; then
|
||||
echo "error: docs directory not found: ${PACKAGE_DIR}/docs" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_dirs() {
|
||||
mkdir -p "${INSTALL_DIR}"
|
||||
mkdir -p "${INSTALL_DIR}/frontend"
|
||||
mkdir -p "${INSTALL_DIR}/docs"
|
||||
}
|
||||
|
||||
copy_runtime_files() {
|
||||
echo "copying package files into ${INSTALL_DIR}..."
|
||||
|
||||
install -m 0755 "${PACKAGE_DIR}/${BIN_NAME}" "${INSTALL_DIR}/${BIN_NAME}"
|
||||
|
||||
rm -rf "${INSTALL_DIR}/frontend"
|
||||
mkdir -p "${INSTALL_DIR}/frontend"
|
||||
cp -R "${PACKAGE_DIR}/frontend/." "${INSTALL_DIR}/frontend/"
|
||||
|
||||
rm -rf "${INSTALL_DIR}/docs"
|
||||
mkdir -p "${INSTALL_DIR}/docs"
|
||||
cp -R "${PACKAGE_DIR}/docs/." "${INSTALL_DIR}/docs/"
|
||||
|
||||
if [[ -f "${PACKAGE_DIR}/README.md" ]]; then
|
||||
cp -f "${PACKAGE_DIR}/README.md" "${INSTALL_DIR}/README.md"
|
||||
fi
|
||||
|
||||
if [[ -f "${PACKAGE_DIR}/.env.example" ]]; then
|
||||
cp -f "${PACKAGE_DIR}/.env.example" "${INSTALL_DIR}/.env.example"
|
||||
fi
|
||||
|
||||
if [[ -f "${PACKAGE_DIR}/.env" && ! -f "${INSTALL_DIR}/.env" ]]; then
|
||||
cp -f "${PACKAGE_DIR}/.env" "${INSTALL_DIR}/.env"
|
||||
fi
|
||||
|
||||
if [[ ! -f "${INSTALL_DIR}/.env" && -f "${INSTALL_DIR}/.env.example" ]]; then
|
||||
cp -f "${INSTALL_DIR}/.env.example" "${INSTALL_DIR}/.env"
|
||||
echo "created ${INSTALL_DIR}/.env from .env.example"
|
||||
fi
|
||||
}
|
||||
|
||||
install_service() {
|
||||
if [[ "${SKIP_SERVICE}" == "true" ]]; then
|
||||
echo "skip service enabled"
|
||||
return
|
||||
fi
|
||||
|
||||
if ! command -v systemctl >/dev/null 2>&1; then
|
||||
echo "systemctl not found, skipping service installation"
|
||||
return
|
||||
fi
|
||||
|
||||
cat >"/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
|
||||
[Unit]
|
||||
Description=SingBox GoPanel API
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${RUN_USER}
|
||||
WorkingDirectory=${INSTALL_DIR}
|
||||
EnvironmentFile=-${INSTALL_DIR}/.env
|
||||
ExecStart=${INSTALL_DIR}/${BIN_NAME}
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
LimitNOFILE=1048576
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable "${SERVICE_NAME}"
|
||||
systemctl restart "${SERVICE_NAME}"
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
cat <<EOF
|
||||
|
||||
Install complete.
|
||||
|
||||
Package dir: ${PACKAGE_DIR}
|
||||
Install directory: ${INSTALL_DIR}
|
||||
Binary: ${INSTALL_DIR}/${BIN_NAME}
|
||||
Frontend: ${INSTALL_DIR}/frontend
|
||||
Docs: ${INSTALL_DIR}/docs
|
||||
Service name: ${SERVICE_NAME}
|
||||
|
||||
Next steps:
|
||||
1. Edit ${INSTALL_DIR}/.env if needed
|
||||
2. Check service status: systemctl status ${SERVICE_NAME}
|
||||
3. Tail logs: journalctl -u ${SERVICE_NAME} -f
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
if [[ ! -f "${PACKAGE_DIR}/${BIN_NAME}" && -f "${SCRIPT_DIR}/../${BIN_NAME}" ]]; then
|
||||
PACKAGE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
fi
|
||||
ensure_root
|
||||
ensure_run_user
|
||||
ensure_package_layout
|
||||
prepare_dirs
|
||||
copy_runtime_files
|
||||
chown -R "${RUN_USER}:${RUN_USER}" "${INSTALL_DIR}" || true
|
||||
chmod +x "${INSTALL_DIR}/${BIN_NAME}" || true
|
||||
install_service
|
||||
print_summary
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user