diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f3e4fcc..373eb75 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -116,12 +116,15 @@ jobs: fi cp deploy/nginx.conf "${ARCHIVE_NAME}/nginx.conf" 2>/dev/null || true tar czf "${ARCHIVE_NAME}.tar.gz" "${ARCHIVE_NAME}" + cp "${ARCHIVE_NAME}.tar.gz" "backupx-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" - name: Upload to GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ env.VERSION }} - files: backupx-${{ env.VERSION }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz + files: | + backupx-${{ env.VERSION }}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz + backupx-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz generate_release_notes: true # ─── Job 3: Docker 多架构 → Docker Hub ─── diff --git a/README.md b/README.md index c344eee..51d5474 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,8 @@ curl -LO https://github.com/Awuqing/BackupX/releases/latest/download/backupx-lin tar xzf backupx-*.tar.gz && cd backupx-* && sudo ./install.sh ``` +For ARM64 hosts, use `backupx-linux-arm64.tar.gz`. The archive contains `backupx`, `web/`, `config.example.yaml`, and `install.sh`; run `install.sh` from the extracted directory. + Open `http://your-server:8340`, create the admin account, then follow the [5-minute Quick Start](https://awuqing.github.io/BackupX/docs/getting-started/quick-start). ## Documentation diff --git a/README.zh-CN.md b/README.zh-CN.md index b2c5363..661a9b0 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -62,6 +62,8 @@ curl -LO https://github.com/Awuqing/BackupX/releases/latest/download/backupx-lin tar xzf backupx-*.tar.gz && cd backupx-* && sudo ./install.sh ``` +ARM64 主机请下载 `backupx-linux-arm64.tar.gz`。预编译包内包含 `backupx`、`web/`、`config.example.yaml` 和 `install.sh`,请在解压后的目录内执行 `install.sh`。 + 打开 `http://your-server:8340`,创建管理员账户,按 [5 分钟快速开始](https://awuqing.github.io/BackupX/zh-Hans/docs/getting-started/quick-start) 完成首次备份。 ## 文档 diff --git a/deploy/install.sh b/deploy/install.sh index 6f0a5f5..99ff81c 100755 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -1,17 +1,25 @@ #!/bin/sh set -eu -PROJECT_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +PROJECT_ROOT=$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd) PREFIX="${PREFIX:-/opt/backupx}" ETC_DIR="${ETC_DIR:-/etc/backupx}" SERVICE_NAME="backupx" APP_USER="backupx" APP_GROUP="backupx" -BIN_SOURCE="${BIN_SOURCE:-$PROJECT_ROOT/server/backupx}" -WEB_SOURCE="${WEB_SOURCE:-$PROJECT_ROOT/web/dist}" -CONFIG_TEMPLATE="${CONFIG_TEMPLATE:-$PROJECT_ROOT/server/config.example.yaml}" +if [ -f "$SCRIPT_DIR/backupx" ] && [ -d "$SCRIPT_DIR/web" ]; then + BIN_SOURCE="${BIN_SOURCE:-$SCRIPT_DIR/backupx}" + WEB_SOURCE="${WEB_SOURCE:-$SCRIPT_DIR/web}" + CONFIG_TEMPLATE="${CONFIG_TEMPLATE:-$SCRIPT_DIR/config.example.yaml}" + NGINX_SOURCE="${NGINX_SOURCE:-$SCRIPT_DIR/nginx.conf}" +else + BIN_SOURCE="${BIN_SOURCE:-$PROJECT_ROOT/server/backupx}" + WEB_SOURCE="${WEB_SOURCE:-$PROJECT_ROOT/web/dist}" + CONFIG_TEMPLATE="${CONFIG_TEMPLATE:-$PROJECT_ROOT/server/config.example.yaml}" + NGINX_SOURCE="${NGINX_SOURCE:-$PROJECT_ROOT/deploy/nginx.conf}" +fi SERVICE_SOURCE="${SERVICE_SOURCE:-$PROJECT_ROOT/deploy/backupx.service}" -NGINX_SOURCE="${NGINX_SOURCE:-$PROJECT_ROOT/deploy/nginx.conf}" if [ "$(id -u)" -ne 0 ]; then echo "请使用 root 或 sudo 执行安装脚本。" >&2 @@ -20,13 +28,20 @@ fi if [ ! -f "$BIN_SOURCE" ]; then echo "未找到后端二进制:$BIN_SOURCE" >&2 - echo "请先执行:cd \"$PROJECT_ROOT/server\" && go build -o backupx ./cmd/backupx" >&2 + echo "源码树安装请先执行:cd \"$PROJECT_ROOT/server\" && go build -o backupx ./cmd/backupx" >&2 + echo "发布包安装请确认当前目录包含 ./backupx、./web 和 ./install.sh。" >&2 exit 1 fi if [ ! -d "$WEB_SOURCE" ]; then echo "未找到前端构建产物:$WEB_SOURCE" >&2 - echo "请先执行:cd \"$PROJECT_ROOT/web\" && npm run build" >&2 + echo "源码树安装请先执行:cd \"$PROJECT_ROOT/web\" && npm run build" >&2 + echo "发布包安装请确认当前目录包含 ./web。" >&2 + exit 1 +fi + +if [ ! -f "$CONFIG_TEMPLATE" ]; then + echo "未找到配置模板:$CONFIG_TEMPLATE" >&2 exit 1 fi @@ -47,11 +62,34 @@ if [ ! -f "$ETC_DIR/config.yaml" ]; then install -m 0640 "$CONFIG_TEMPLATE" "$ETC_DIR/config.yaml" fi -install -m 0644 "$SERVICE_SOURCE" "/etc/systemd/system/$SERVICE_NAME.service" +if [ -f "$SERVICE_SOURCE" ]; then + install -m 0644 "$SERVICE_SOURCE" "/etc/systemd/system/$SERVICE_NAME.service" +else + cat > "/etc/systemd/system/$SERVICE_NAME.service" </dev/null 2>&1; then nginx -t diff --git a/server/internal/installscript/deploy_install_test.go b/server/internal/installscript/deploy_install_test.go new file mode 100644 index 0000000..4c0507a --- /dev/null +++ b/server/internal/installscript/deploy_install_test.go @@ -0,0 +1,41 @@ +package installscript + +import ( + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +func TestDeployInstallScriptSyntax(t *testing.T) { + scriptPath := filepath.Join("..", "..", "..", "deploy", "install.sh") + cmd := exec.Command("sh", "-n", scriptPath) + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("install.sh syntax invalid: %v\n%s", err, output) + } +} + +func TestDeployInstallScriptSupportsReleasePackageLayout(t *testing.T) { + scriptPath := filepath.Join("..", "..", "..", "deploy", "install.sh") + data, err := os.ReadFile(scriptPath) + if err != nil { + t.Fatal(err) + } + script := string(data) + for _, want := range []string{ + `SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)`, + `if [ -f "$SCRIPT_DIR/backupx" ] && [ -d "$SCRIPT_DIR/web" ]; then`, + `BIN_SOURCE="${BIN_SOURCE:-$SCRIPT_DIR/backupx}"`, + `WEB_SOURCE="${WEB_SOURCE:-$SCRIPT_DIR/web}"`, + `CONFIG_TEMPLATE="${CONFIG_TEMPLATE:-$SCRIPT_DIR/config.example.yaml}"`, + `发布包安装请确认当前目录包含 ./backupx、./web 和 ./install.sh。`, + `cat > "/etc/systemd/system/$SERVICE_NAME.service" <