From 906e8328d625fb12e8f87ce230c53f5dda6cf461 Mon Sep 17 00:00:00 2001 From: cita Date: Sun, 1 Mar 2026 16:19:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=E5=92=8C=E5=90=AF=E5=8A=A8=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=EF=BC=8C=E6=94=AF=E6=8C=81=E7=9B=B4=E6=8E=A5=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E9=A2=84=E6=89=93=E5=8C=85=E7=9A=84=20Release=20?= =?UTF-8?q?=E4=BA=A7=E7=89=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 210 ++++++++++++++++++++++++++++++++++ README.md | 36 ++++++ scripts/release/start.bat | 8 ++ scripts/release/start.sh | 5 + 5 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml create mode 100644 scripts/release/start.bat create mode 100644 scripts/release/start.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8072b8b..e78d69e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: publish-docker: runs-on: ubuntu-latest needs: test-and-build - if: github.event_name == 'push' + if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/') timeout-minutes: 30 steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..8d8dcf9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,210 @@ +name: Release + +on: + push: + tags: ['v*'] + workflow_dispatch: + +permissions: + contents: write + packages: write + +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: false + +jobs: + verify: + name: Verify + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test + + - name: Build + run: npm run build + + build-packages: + name: Build Package (${{ matrix.os }}) + needs: verify + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build + run: npm run build + + - name: Prune dev dependencies + run: npm prune --omit=dev + + - name: Assemble archive (Unix) + if: runner.os != 'Windows' + shell: bash + run: | + set -euxo pipefail + ASSET_OS="$(echo "${RUNNER_OS}" | tr '[:upper:]' '[:lower:]')" + ASSET_ARCH="$(echo "${RUNNER_ARCH}" | tr '[:upper:]' '[:lower:]')" + ASSET_NAME="metapi-${GITHUB_REF_NAME}-${ASSET_OS}-${ASSET_ARCH}.tar.gz" + + PACKAGE_DIR="release/metapi" + mkdir -p "$PACKAGE_DIR" + + cp -R dist node_modules drizzle "$PACKAGE_DIR"/ + cp package.json "$PACKAGE_DIR"/ + [ -f .env.example ] && cp .env.example "$PACKAGE_DIR"/ + [ -f LICENSE ] && cp LICENSE "$PACKAGE_DIR"/ + cp scripts/release/start.sh "$PACKAGE_DIR"/start.sh + chmod +x "$PACKAGE_DIR"/start.sh + + tar -czf "release/${ASSET_NAME}" -C release metapi + echo "ASSET_NAME=${ASSET_NAME}" >> "$GITHUB_ENV" + + - name: Assemble archive (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $assetOs = $env:RUNNER_OS.ToLower() + $assetArch = $env:RUNNER_ARCH.ToLower() + $assetName = "metapi-$env:GITHUB_REF_NAME-$assetOs-$assetArch.zip" + + $packageDir = "release\\metapi" + New-Item -ItemType Directory -Path $packageDir -Force | Out-Null + + Copy-Item -Recurse -Force dist $packageDir + Copy-Item -Recurse -Force node_modules $packageDir + Copy-Item -Recurse -Force drizzle $packageDir + Copy-Item -Force package.json $packageDir + if (Test-Path ".env.example") { Copy-Item -Force .env.example $packageDir } + if (Test-Path "LICENSE") { Copy-Item -Force LICENSE $packageDir } + Copy-Item -Force scripts\\release\\start.bat "$packageDir\\start.bat" + + Compress-Archive -Path $packageDir -DestinationPath "release\\$assetName" -Force + "ASSET_NAME=$assetName" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Upload package (Unix) + if: runner.os != 'Windows' + uses: actions/upload-artifact@v4 + with: + name: metapi-${{ github.ref_name }}-${{ runner.os }}-${{ runner.arch }} + path: release/*.tar.gz + if-no-files-found: error + + - name: Upload package (Windows) + if: runner.os == 'Windows' + uses: actions/upload-artifact@v4 + with: + name: metapi-${{ github.ref_name }}-${{ runner.os }}-${{ runner.arch }} + path: release/*.zip + if-no-files-found: error + + publish-release: + name: Publish GitHub Release + needs: build-packages + runs-on: ubuntu-latest + timeout-minutes: 15 + if: startsWith(github.ref, 'refs/tags/') + + steps: + - name: Download package artifacts + uses: actions/download-artifact@v4 + with: + path: release-assets + merge-multiple: true + + - name: Show release files + run: ls -la release-assets + + - name: Upload Release Assets + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + generate_release_notes: true + files: release-assets/* + + publish-docker: + name: Publish Docker Images + needs: build-packages + runs-on: ubuntu-latest + timeout-minutes: 30 + if: startsWith(github.ref, 'refs/tags/') + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Ensure Docker Hub secrets are configured + run: | + if [ -z "${{ secrets.DOCKERHUB_USERNAME }}" ] || [ -z "${{ secrets.DOCKERHUB_TOKEN }}" ]; then + echo "Missing Docker Hub secrets: DOCKERHUB_USERNAME / DOCKERHUB_TOKEN" + exit 1 + fi + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + 1467078763/metapi + ghcr.io/${{ github.repository }} + tags: | + type=ref,event=tag + type=raw,value=latest + + - name: Build and push multi-arch images + uses: docker/build-push-action@v6 + with: + context: . + file: docker/Dockerfile + platforms: linux/amd64,linux/arm64,linux/arm/v7 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/README.md b/README.md index 361259b..9df74b8 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,42 @@ docker run -d --name metapi \ > 如果是非 Compose 场景且你没有显式设置 `AUTH_TOKEN`,默认值为 `change-me-admin-token`(仅用于本地调试)。 > 若你在后台「设置」里修改过管理员令牌,后续登录请使用修改后的新令牌。 +### Release 包启动(Linux / macOS / Windows) + +如果你不想用 Docker,也可以直接下载预打包的 Release 产物运行: + +1. 打开 [Releases](https://github.com/cita-777/metapi/releases) 下载与你系统匹配的压缩包 +2. 解压后进入目录 +3. 设置环境变量并启动 + +Linux / macOS: + +```bash +export AUTH_TOKEN=your-admin-token +export PROXY_TOKEN=your-proxy-sk-token +export PORT=4000 +export DATA_DIR=./data +./start.sh +``` + +Windows(PowerShell): + +```powershell +$env:AUTH_TOKEN="your-admin-token" +$env:PROXY_TOKEN="your-proxy-sk-token" +$env:PORT="4000" +$env:DATA_DIR="./data" +.\start.bat +``` + +`start.sh` / `start.bat` 的作用是: + +- 先执行数据库迁移:`node dist/server/db/migrate.js` +- 再启动服务:`node dist/server/index.js` + +> [!NOTE] +> Release 包依赖本机安装 Node.js(建议 20+)。 + ### 升级 ```bash diff --git a/scripts/release/start.bat b/scripts/release/start.bat new file mode 100644 index 0000000..50bfce6 --- /dev/null +++ b/scripts/release/start.bat @@ -0,0 +1,8 @@ +@echo off +setlocal + +node dist/server/db/migrate.js +if errorlevel 1 exit /b %errorlevel% + +node dist/server/index.js +exit /b %errorlevel% diff --git a/scripts/release/start.sh b/scripts/release/start.sh new file mode 100644 index 0000000..48bd881 --- /dev/null +++ b/scripts/release/start.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +set -eu + +node dist/server/db/migrate.js +exec node dist/server/index.js