From 0536a4595985d3b0f76c12a1e014e9350349a0d5 Mon Sep 17 00:00:00 2001 From: Tunglies Date: Mon, 2 Jun 2025 13:57:18 +0800 Subject: [PATCH] feat: add autobuild version support and unify version update scripts - Support autobuild version with timestamp (e.g. 2.3.0+autobuild.2506021530) via `pnpm release-version autobuild` - Sync version to package.json, Cargo.toml, tauri.conf.json - Improve version regex to support build metadata - Remove old release_version.mjs and release-alpha_version.mjs, use release-version.mjs for all - Fix script path issues --- .github/workflows/alpha.yml | 6 +- .github/workflows/autobuild.yml | 524 ++++++++++++++++++++++++++++++ package.json | 7 +- pnpm-lock.yaml | 9 + scripts/release-alpha_version.mjs | 96 ------ scripts/release-version.mjs | 213 ++++++++++++ scripts/release_version.mjs | 197 ----------- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 10 files changed, 756 insertions(+), 302 deletions(-) create mode 100644 .github/workflows/autobuild.yml delete mode 100644 scripts/release-alpha_version.mjs create mode 100644 scripts/release-version.mjs delete mode 100644 scripts/release_version.mjs diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 66b35401..386cceae 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -2,9 +2,9 @@ name: Alpha Build on: workflow_dispatch: - schedule: - # UTC+8 0,6,12,18 - - cron: "0 16,22,4,10 * * *" + # schedule: + # UTC+8 0,6,12,18 + # - cron: "0 16,22,4,10 * * *" permissions: write-all env: CARGO_INCREMENTAL: 0 diff --git a/.github/workflows/autobuild.yml b/.github/workflows/autobuild.yml new file mode 100644 index 00000000..7e42a0ef --- /dev/null +++ b/.github/workflows/autobuild.yml @@ -0,0 +1,524 @@ +name: Auto Build + +on: + workflow_dispatch: + schedule: + # UTC+8 0,6,12,18 + # - cron: "0 16,22,4,10 * * *" +permissions: write-all +env: + CARGO_INCREMENTAL: 0 + RUST_BACKTRACE: short +concurrency: + # only allow per workflow per commit (and not pr) to run at a time + group: "${{ github.workflow }} - ${{ github.head_ref || github.ref }}" + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + check_commit: + runs-on: ubuntu-latest + outputs: + should_run: ${{ steps.check.outputs.should_run }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Check if version changed or src changed + id: check + run: | + # For manual workflow_dispatch, always run + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + echo "should_run=true" >> $GITHUB_OUTPUT + exit 0 + fi + + # Store current version from package.json + CURRENT_VERSION=$(cat package.json | jq -r '.version') + echo "Current version: $CURRENT_VERSION" + + # Get the previous commit's package.json version + git checkout HEAD~1 package.json + PREVIOUS_VERSION=$(cat package.json | jq -r '.version') + echo "Previous version: $PREVIOUS_VERSION" + + # Reset back to current commit + git checkout HEAD package.json + + # Check if version changed + if [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then + echo "Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION" + echo "should_run=true" >> $GITHUB_OUTPUT + exit 0 + fi + + # Check if src or src-tauri directories changed + CURRENT_SRC_HASH=$(git rev-parse HEAD:src) + PREVIOUS_SRC_HASH=$(git rev-parse HEAD~1:src 2>/dev/null || echo "") + CURRENT_TAURI_HASH=$(git rev-parse HEAD:src-tauri 2>/dev/null || echo "") + PREVIOUS_TAURI_HASH=$(git rev-parse HEAD~1:src-tauri 2>/dev/null || echo "") + + echo "Current src hash: $CURRENT_SRC_HASH" + echo "Previous src hash: $PREVIOUS_SRC_HASH" + echo "Current tauri hash: $CURRENT_TAURI_HASH" + echo "Previous tauri hash: $PREVIOUS_TAURI_HASH" + + if [ "$CURRENT_SRC_HASH" != "$PREVIOUS_SRC_HASH" ] || [ "$CURRENT_TAURI_HASH" != "$PREVIOUS_TAURI_HASH" ]; then + echo "Source directories changed" + echo "should_run=true" >> $GITHUB_OUTPUT + else + echo "Version and source directories unchanged" + echo "should_run=false" >> $GITHUB_OUTPUT + fi + + # delete_old_assets: + # needs: check_commit + # if: ${{ needs.check_commit.outputs.should_run == 'true' }} + # runs-on: ubuntu-latest + # steps: + # - name: Delete Old Alpha Release Assets + # uses: actions/github-script@v7 + # with: + # github-token: ${{ secrets.GITHUB_TOKEN }} + # script: | + # const releaseTag = 'alpha'; + + # try { + # // Get the release by tag name + # const { data: release } = await github.rest.repos.getReleaseByTag({ + # owner: context.repo.owner, + # repo: context.repo.repo, + # tag: releaseTag + # }); + + # console.log(`Found release with ID: ${release.id}`); + + # // Delete each asset + # if (release.assets && release.assets.length > 0) { + # console.log(`Deleting ${release.assets.length} assets`); + + # for (const asset of release.assets) { + # console.log(`Deleting asset: ${asset.name} (${asset.id})`); + # await github.rest.repos.deleteReleaseAsset({ + # owner: context.repo.owner, + # repo: context.repo.repo, + # asset_id: asset.id + # }); + # } + + # console.log('All assets deleted successfully'); + # } else { + # console.log('No assets found to delete'); + # } + # } catch (error) { + # if (error.status === 404) { + # console.log('Release not found, nothing to delete'); + # } else { + # console.error('Error:', error); + # throw error; + # } + # } + + update_tag: + name: Update tag + runs-on: ubuntu-latest + needs: check_commit + if: ${{ needs.check_commit.outputs.should_run == 'true' }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Fetch Alpha update logs + id: fetch_alpha_logs + run: | + # Check if UPDATELOG.md exists + if [ -f "UPDATELOG.md" ]; then + # Extract the section starting with ## and containing -alpha until the next ## or end of file + # ALPHA_LOGS=$(awk '/^## .*-alpha/{flag=1; print; next} /^## /{flag=0} flag' UPDATELOG.md) + ALPHA_LOGS=$(awk '/^## v/{if(flag) exit; flag=1} flag' UPDATELOG.md) + + if [ -n "$ALPHA_LOGS" ]; then + echo "Found alpha update logs" + echo "ALPHA_LOGS<> $GITHUB_ENV + echo "$ALPHA_LOGS" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + else + echo "No alpha sections found in UPDATELOG.md" + fi + else + echo "UPDATELOG.md file not found" + fi + shell: bash + + - name: Set Env + run: | + echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV + shell: bash + + - run: | + # 检查 ALPHA_LOGS 是否存在,如果不存在则使用默认消息 + if [ -z "$ALPHA_LOGS" ]; then + echo "No alpha logs found, using default message" + ALPHA_LOGS="More new features are now supported. Check for detailed changelog soon." + else + echo "Using found alpha logs" + fi + + # 生成 release.txt 文件 + cat > release.txt << EOF + $ALPHA_LOGS + + ## 我应该下载哪个版本? + + ### MacOS + - MacOS intel芯片: x64.dmg + - MacOS apple M芯片: aarch64.dmg + + ### Linux + - Linux 64位: amd64.deb/amd64.rpm + - Linux arm64 architecture: arm64.deb/aarch64.rpm + - Linux armv7架构: armhf.deb/armhfp.rpm + + ### Windows (不再支持Win7) + #### 正常版本(推荐) + - 64位: x64-setup.exe + - arm64架构: arm64-setup.exe + #### 便携版问题很多不再提供 + #### 内置Webview2版(体积较大,仅在企业版系统或无法安装webview2时使用) + - 64位: x64_fixed_webview2-setup.exe + - arm64架构: arm64_fixed_webview2-setup.exe + + ### FAQ + + - [常见问题](https://clash-verge-rev.github.io/faq/windows.html) + + ### 稳定机场VPN推荐 + - [狗狗加速](https://verge.dginv.click/#/register?code=oaxsAGo6) + + Created at ${{ env.BUILDTIME }}. + EOF + + - name: Upload Release + uses: softprops/action-gh-release@v2 + with: + tag_name: autobuild + name: "Clash Verge Rev AutoBuild" + body_path: release.txt + prerelease: true + token: ${{ secrets.GITHUB_TOKEN }} + generate_release_notes: true + + autobuild-x86-windows-macos-linux: + name: Autobuild x86 Windows, MacOS and Linux + needs: update_tag + strategy: + fail-fast: false + matrix: + include: + - os: windows-latest + target: x86_64-pc-windows-msvc + - os: windows-latest + target: aarch64-pc-windows-msvc + - os: macos-latest + target: aarch64-apple-darwin + - os: macos-latest + target: x86_64-apple-darwin + - os: ubuntu-22.04 + target: x86_64-unknown-linux-gnu + + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Install Rust Stable + uses: dtolnay/rust-toolchain@stable + + - name: Add Rust Target + run: rustup target add ${{ matrix.target }} + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + cache-all-crates: true + cache-on-failure: true + + - name: Install dependencies (ubuntu only) + if: matrix.os == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y libxslt1.1 libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: "22" + + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + run_install: false + + - name: Pnpm install and check + run: | + pnpm i + pnpm check ${{ matrix.target }} + + - name: Release Autobuild Version + run: pnpm release-version autobuild + + - name: Tauri build + uses: tauri-apps/tauri-action@v0 + env: + NODE_OPTIONS: "--max_old_space_size=4096" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + with: + tagName: alpha + releaseName: "Clash Verge Rev Alpha" + releaseBody: "More new features are now supported." + releaseDraft: false + prerelease: true + tauriScript: pnpm + args: --target ${{ matrix.target }} + + autobuild-arm-linux: + name: Autobuild ARM Linux + needs: update_tag + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-22.04 + target: aarch64-unknown-linux-gnu + arch: arm64 + - os: ubuntu-22.04 + target: armv7-unknown-linux-gnueabihf + arch: armhf + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Install Rust Stable + uses: dtolnay/rust-toolchain@stable + + - name: Add Rust Target + run: rustup target add ${{ matrix.target }} + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + cache-all-crates: true + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Pnpm install and check + run: | + pnpm i + pnpm check ${{ matrix.target }} + + - name: Release Autobuild Version + run: pnpm release-version autobuild + + - name: "Setup for linux" + run: |- + sudo ls -lR /etc/apt/ + + cat > /tmp/sources.list << EOF + deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted + deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted + deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-updates main multiverse universe restricted + deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-backports main multiverse universe restricted + + deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy main multiverse universe restricted + deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main multiverse universe restricted + deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main multiverse universe restricted + deb [arch=armhf,arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main multiverse universe restricted + EOF + + sudo mv /etc/apt/sources.list /etc/apt/sources.list.default + sudo mv /tmp/sources.list /etc/apt/sources.list + + sudo dpkg --add-architecture ${{ matrix.arch }} + sudo apt-get update -y + sudo apt-get -f install -y + + sudo apt-get install -y \ + linux-libc-dev:${{ matrix.arch }} \ + libc6-dev:${{ matrix.arch }} + + sudo apt-get install -y \ + libxslt1.1:${{ matrix.arch }} \ + libwebkit2gtk-4.1-dev:${{ matrix.arch }} \ + libayatana-appindicator3-dev:${{ matrix.arch }} \ + libssl-dev:${{ matrix.arch }} \ + patchelf:${{ matrix.arch }} \ + librsvg2-dev:${{ matrix.arch }} + + - name: "Install aarch64 tools" + if: matrix.target == 'aarch64-unknown-linux-gnu' + run: | + sudo apt install -y \ + gcc-aarch64-linux-gnu \ + g++-aarch64-linux-gnu + + - name: "Install armv7 tools" + if: matrix.target == 'armv7-unknown-linux-gnueabihf' + run: | + sudo apt install -y \ + gcc-arm-linux-gnueabihf \ + g++-arm-linux-gnueabihf + + - name: Build for Linux + run: | + export PKG_CONFIG_ALLOW_CROSS=1 + if [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]; then + export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH + export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/ + elif [ "${{ matrix.target }}" == "armv7-unknown-linux-gnueabihf" ]; then + export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/:$PKG_CONFIG_PATH + export PKG_CONFIG_SYSROOT_DIR=/usr/arm-linux-gnueabihf/ + fi + pnpm build --target ${{ matrix.target }} + env: + NODE_OPTIONS: "--max_old_space_size=4096" + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + + - name: Get Version + run: | + sudo apt-get update + sudo apt-get install jq + echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV + echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV + + - name: Upload Release + uses: softprops/action-gh-release@v2 + with: + tag_name: alpha + name: "Clash Verge Rev Alpha" + prerelease: true + token: ${{ secrets.GITHUB_TOKEN }} + files: | + src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb + src-tauri/target/${{ matrix.target }}/release/bundle/rpm/*.rpm + + autobuild-x86-arm-windows_webview2: + name: Autobuild x86 and ARM Windows with WebView2 + needs: check_commit + if: ${{ needs.check_commit.outputs.should_run == 'true' }} + # needs: update_tag + strategy: + fail-fast: false + matrix: + include: + - os: windows-latest + target: x86_64-pc-windows-msvc + arch: x64 + - os: windows-latest + target: aarch64-pc-windows-msvc + arch: arm64 + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Add Rust Target + run: rustup target add ${{ matrix.target }} + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: src-tauri + cache-all-crates: true + cache-on-failure: true + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: "22" + + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + run_install: false + + - name: Pnpm install and check + run: | + pnpm i + pnpm check ${{ matrix.target }} + + - name: Release Autobuild Version + run: pnpm release-version autobuild + + - name: Download WebView2 Runtime + run: | + invoke-webrequest -uri https://github.com/westinyang/WebView2RuntimeArchive/releases/download/109.0.1518.78/Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -outfile Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab + Expand .\Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${{ matrix.arch }}.cab -F:* ./src-tauri + Remove-Item .\src-tauri\tauri.windows.conf.json + Rename-Item .\src-tauri\webview2.${{ matrix.arch }}.json tauri.windows.conf.json + + - name: Tauri build + id: build + uses: tauri-apps/tauri-action@v0 + env: + NODE_OPTIONS: "--max_old_space_size=4096" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + with: + tauriScript: pnpm + args: --target ${{ matrix.target }} + + - name: Rename + run: | + $files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe" + foreach ($file in $files) { + $newName = $file.Name -replace "-setup\.exe$", "_fixed_webview2-setup.exe" + Rename-Item $file.FullName $newName + } + + $files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*.nsis.zip" + foreach ($file in $files) { + $newName = $file.Name -replace "-setup\.nsis\.zip$", "_fixed_webview2-setup.nsis.zip" + Rename-Item $file.FullName $newName + } + + $files = Get-ChildItem ".\src-tauri\target\${{ matrix.target }}\release\bundle\nsis\*-setup.exe.sig" + foreach ($file in $files) { + $newName = $file.Name -replace "-setup\.exe\.sig$", "_fixed_webview2-setup.exe.sig" + Rename-Item $file.FullName $newName + } + + - name: Upload Release + uses: softprops/action-gh-release@v2 + with: + tag_name: autobuild + name: "Clash Verge Rev AutoBuild" + prerelease: true + token: ${{ secrets.GITHUB_TOKEN }} + files: src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*setup* + + - name: Portable Bundle + run: pnpm portable-fixed-webview2 ${{ matrix.target }} --autobuild + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index b4cb7885..811349ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "clash-verge", - "version": "2.2.4-alpha.2", + "version": "2.3.0", "license": "GPL-3.0-only", "scripts": { "dev": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev", @@ -17,7 +17,7 @@ "portable": "node scripts/portable.mjs", "portable-fixed-webview2": "node scripts/portable-fixed-webview2.mjs", "fix-alpha-version": "node scripts/fix-alpha_version.mjs", - "release-version": "node scripts/release_version.mjs", + "release-version": "node scripts/release-version.mjs", "release-alpha-version": "node scripts/release-alpha_version.mjs", "prepare": "husky", "fmt": "cargo fmt --manifest-path ./src-tauri/Cargo.toml", @@ -89,6 +89,7 @@ "@vitejs/plugin-legacy": "^6.0.2", "@vitejs/plugin-react": "4.4.1", "adm-zip": "^0.5.16", + "commander": "^14.0.0", "cross-env": "^7.0.3", "https-proxy-agent": "^7.0.6", "husky": "^9.1.7", @@ -111,4 +112,4 @@ }, "type": "module", "packageManager": "pnpm@9.13.2" -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a067402..4391a30a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -198,6 +198,9 @@ importers: adm-zip: specifier: ^0.5.16 version: 0.5.16 + commander: + specifier: ^14.0.0 + version: 14.0.0 cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -1812,6 +1815,10 @@ packages: resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -4680,6 +4687,8 @@ snapshots: commander@13.1.0: {} + commander@14.0.0: {} + commander@2.20.3: {} convert-source-map@1.9.0: {} diff --git a/scripts/release-alpha_version.mjs b/scripts/release-alpha_version.mjs deleted file mode 100644 index 7319aa63..00000000 --- a/scripts/release-alpha_version.mjs +++ /dev/null @@ -1,96 +0,0 @@ -import fs from "fs/promises"; -import path from "path"; - -/** - * 更新 package.json 文件中的版本号 - */ -async function updatePackageVersion() { - const _dirname = process.cwd(); - const packageJsonPath = path.join(_dirname, "package.json"); - try { - const data = await fs.readFile(packageJsonPath, "utf8"); - const packageJson = JSON.parse(data); - - let result = packageJson.version; - if (!result.includes("alpha")) { - result = `${result}-alpha`; - } - - console.log("[INFO]: Current package.json version is: ", result); - packageJson.version = result; - await fs.writeFile( - packageJsonPath, - JSON.stringify(packageJson, null, 2), - "utf8", - ); - console.log(`[INFO]: package.json version updated to: ${result}`); - } catch (error) { - console.error("Error updating package.json version:", error); - } -} - -/** - * 更新 Cargo.toml 文件中的版本号 - */ -async function updateCargoVersion() { - const _dirname = process.cwd(); - const cargoTomlPath = path.join(_dirname, "src-tauri", "Cargo.toml"); - try { - const data = await fs.readFile(cargoTomlPath, "utf8"); - const lines = data.split("\n"); - - const updatedLines = lines.map((line) => { - if (line.startsWith("version =")) { - const versionMatch = line.match(/version\s*=\s*"([^"]+)"/); - if (versionMatch && !versionMatch[1].includes("alpha")) { - const newVersion = `${versionMatch[1]}-alpha`; - return line.replace(versionMatch[1], newVersion); - } - } - return line; - }); - - await fs.writeFile(cargoTomlPath, updatedLines.join("\n"), "utf8"); - } catch (error) { - console.error("Error updating Cargo.toml version:", error); - } -} - -/** - * 更新 tauri.conf.json 文件中的版本号 - */ -async function updateTauriConfigVersion() { - const _dirname = process.cwd(); - const tauriConfigPath = path.join(_dirname, "src-tauri", "tauri.conf.json"); - try { - const data = await fs.readFile(tauriConfigPath, "utf8"); - const tauriConfig = JSON.parse(data); - - let version = tauriConfig.version; - if (!version.includes("alpha")) { - version = `${version}-alpha`; - } - - console.log("[INFO]: Current tauri.conf.json version is: ", version); - tauriConfig.version = version; - await fs.writeFile( - tauriConfigPath, - JSON.stringify(tauriConfig, null, 2), - "utf8", - ); - console.log(`[INFO]: tauri.conf.json version updated to: ${version}`); - } catch (error) { - console.error("Error updating tauri.conf.json version:", error); - } -} - -/** - * 主函数,依次更新所有文件的版本号 - */ -async function main() { - await updatePackageVersion(); - await updateCargoVersion(); - await updateTauriConfigVersion(); -} - -main().catch(console.error); diff --git a/scripts/release-version.mjs b/scripts/release-version.mjs new file mode 100644 index 00000000..de463d22 --- /dev/null +++ b/scripts/release-version.mjs @@ -0,0 +1,213 @@ + + +/** + * CLI tool to update version numbers in package.json, src-tauri/Cargo.toml, and src-tauri/tauri.conf.json. + * + * Usage: + * pnpm release-version + * + * can be: + * - A full semver version (e.g., 1.2.3, v1.2.3, 1.2.3-beta, v1.2.3+build) + * - A tag: "alpha", "beta", "rc", or "autobuild" + * - "alpha", "beta", "rc": Appends the tag to the current base version (e.g., 1.2.3-beta) + * - "autobuild": Appends a timestamped autobuild tag (e.g., 1.2.3+autobuild.2406101530) + * + * Examples: + * pnpm release-version 1.2.3 + * pnpm release-version v1.2.3-beta + * pnpm release-version beta + * pnpm release-version autobuild + * + * The script will: + * - Validate and normalize the version argument + * - Update the version field in package.json + * - Update the version field in src-tauri/Cargo.toml + * - Update the version field in src-tauri/tauri.conf.json + * + * Errors are logged and the process exits with code 1 on failure. + */ + +import fs from "fs/promises"; +import path from "path"; +import { program } from "commander"; + +/** + * 生成短时间戳(格式:YYMMDDHHMM) + * @returns {string} + */ +function generateShortTimestamp() { + const now = new Date(); + const year = String(now.getFullYear()).slice(-2); + const month = String(now.getMonth() + 1).padStart(2, "0"); + const day = String(now.getDate()).padStart(2, "0"); + const hours = String(now.getHours()).padStart(2, "0"); + const minutes = String(now.getMinutes()).padStart(2, "0"); + return `${year}${month}${day}${hours}${minutes}`; +} + +/** + * 验证版本号格式 + * @param {string} version + * @returns {boolean} + */ +function isValidVersion(version) { + return /^v?\d+\.\d+\.\d+(-(alpha|beta|rc)(\.\d+)?)?(\+[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*)?$/i.test(version); +} + +/** + * 标准化版本号 + * @param {string} version + * @returns {string} + */ +function normalizeVersion(version) { + return version.startsWith("v") ? version : `v${version}`; +} + +/** + * 提取基础版本号(去掉所有 -tag 和 +build 部分) + * @param {string} version + * @returns {string} + */ +function getBaseVersion(version) { + let base = version.replace(/-(alpha|beta|rc)(\.\d+)?/i, ''); + base = base.replace(/\+[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*/g, ''); + return base; +} + +/** + * 更新 package.json 版本号 + * @param {string} newVersion + */ +async function updatePackageVersion(newVersion) { + const _dirname = process.cwd(); + const packageJsonPath = path.join(_dirname, "package.json"); + try { + const data = await fs.readFile(packageJsonPath, "utf8"); + const packageJson = JSON.parse(data); + + console.log("[INFO]: Current package.json version is: ", packageJson.version); + packageJson.version = newVersion.startsWith("v") ? newVersion.slice(1) : newVersion; + await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), "utf8"); + console.log(`[INFO]: package.json version updated to: ${packageJson.version}`); + } catch (error) { + console.error("Error updating package.json version:", error); + throw error; + } +} + +/** + * 更新 Cargo.toml 版本号 + * @param {string} newVersion + */ +async function updateCargoVersion(newVersion) { + const _dirname = process.cwd(); + const cargoTomlPath = path.join(_dirname, "src-tauri", "Cargo.toml"); + try { + const data = await fs.readFile(cargoTomlPath, "utf8"); + const lines = data.split("\n"); + const versionWithoutV = newVersion.startsWith("v") ? newVersion.slice(1) : newVersion; + const baseVersion = getBaseVersion(versionWithoutV); + + const updatedLines = lines.map((line) => { + if (line.trim().startsWith("version =")) { + return line.replace(/version\s*=\s*"[^"]+"/, `version = "${baseVersion}"`); + } + return line; + }); + + await fs.writeFile(cargoTomlPath, updatedLines.join("\n"), "utf8"); + console.log(`[INFO]: Cargo.toml version updated to: ${baseVersion}`); + } catch (error) { + console.error("Error updating Cargo.toml version:", error); + throw error; + } +} + +/** + * 更新 tauri.conf.json 版本号 + * @param {string} newVersion + */ +async function updateTauriConfigVersion(newVersion) { + const _dirname = process.cwd(); + const tauriConfigPath = path.join(_dirname, "src-tauri", "tauri.conf.json"); + try { + const data = await fs.readFile(tauriConfigPath, "utf8"); + const tauriConfig = JSON.parse(data); + const versionWithoutV = newVersion.startsWith("v") ? newVersion.slice(1) : newVersion; + const baseVersion = getBaseVersion(versionWithoutV); + + console.log("[INFO]: Current tauri.conf.json version is: ", tauriConfig.version); + tauriConfig.version = baseVersion; + await fs.writeFile(tauriConfigPath, JSON.stringify(tauriConfig, null, 2), "utf8"); + console.log(`[INFO]: tauri.conf.json version updated to: ${baseVersion}`); + } catch (error) { + console.error("Error updating tauri.conf.json version:", error); + throw error; + } +} + +/** + * 获取当前版本号 + */ +async function getCurrentVersion() { + const _dirname = process.cwd(); + const packageJsonPath = path.join(_dirname, "package.json"); + try { + const data = await fs.readFile(packageJsonPath, "utf8"); + const packageJson = JSON.parse(data); + return packageJson.version; + } catch (error) { + console.error("Error getting current version:", error); + throw error; + } +} + +/** + * 主函数 + */ +async function main(versionArg) { + if (!versionArg) { + console.error("Error: Version argument is required"); + process.exit(1); + } + + try { + let newVersion; + const validTags = ["alpha", "beta", "rc", "autobuild"]; + + if (validTags.includes(versionArg.toLowerCase())) { + const currentVersion = await getCurrentVersion(); + const baseVersion = getBaseVersion(currentVersion); + + if (versionArg.toLowerCase() === "autobuild") { + const timestamp = generateShortTimestamp(); + newVersion = `${baseVersion}+autobuild.${timestamp}`; + } else { + newVersion = `${baseVersion}-${versionArg.toLowerCase()}`; + } + } else { + if (!isValidVersion(versionArg)) { + console.error("Error: Invalid version format"); + process.exit(1); + } + newVersion = normalizeVersion(versionArg); + } + + console.log(`[INFO]: Updating versions to: ${newVersion}`); + await updatePackageVersion(newVersion); + await updateCargoVersion(newVersion); + await updateTauriConfigVersion(newVersion); + console.log("[SUCCESS]: All version updates completed successfully!"); + } catch (error) { + console.error("[ERROR]: Failed to update versions:", error); + process.exit(1); + } +} + +program + .name("pnpm release-version") + .description("Update project version numbers") + .argument("", "version tag or full version") + .action(main) + .parse(process.argv); + diff --git a/scripts/release_version.mjs b/scripts/release_version.mjs deleted file mode 100644 index f051289f..00000000 --- a/scripts/release_version.mjs +++ /dev/null @@ -1,197 +0,0 @@ -import fs from "fs/promises"; -import path from "path"; -import { program } from "commander"; - -/** - * 验证版本号格式 - * @param {string} version - * @returns {boolean} - */ -function isValidVersion(version) { - return /^v?\d+\.\d+\.\d+(-(alpha|beta|rc)(\.\d+)?)?$/i.test(version); -} - -/** - * 标准化版本号(确保v前缀可选) - * @param {string} version - * @returns {string} - */ -function normalizeVersion(version) { - return version.startsWith("v") ? version : `v${version}`; -} - -/** - * 更新 package.json 文件中的版本号 - * @param {string} newVersion 新版本号 - */ -async function updatePackageVersion(newVersion) { - const _dirname = process.cwd(); - const packageJsonPath = path.join(_dirname, "package.json"); - try { - const data = await fs.readFile(packageJsonPath, "utf8"); - const packageJson = JSON.parse(data); - - console.log( - "[INFO]: Current package.json version is: ", - packageJson.version, - ); - packageJson.version = newVersion.startsWith("v") - ? newVersion.slice(1) - : newVersion; - await fs.writeFile( - packageJsonPath, - JSON.stringify(packageJson, null, 2), - "utf8", - ); - console.log( - `[INFO]: package.json version updated to: ${packageJson.version}`, - ); - return packageJson.version; - } catch (error) { - console.error("Error updating package.json version:", error); - throw error; - } -} - -/** - * 更新 Cargo.toml 文件中的版本号 - * @param {string} newVersion 新版本号 - */ -async function updateCargoVersion(newVersion) { - const _dirname = process.cwd(); - const cargoTomlPath = path.join(_dirname, "src-tauri", "Cargo.toml"); - try { - const data = await fs.readFile(cargoTomlPath, "utf8"); - const lines = data.split("\n"); - - const versionWithoutV = newVersion.startsWith("v") - ? newVersion.slice(1) - : newVersion; - - const updatedLines = lines.map((line) => { - if (line.trim().startsWith("version =")) { - return line.replace( - /version\s*=\s*"[^"]+"/, - `version = "${versionWithoutV}"`, - ); - } - return line; - }); - - await fs.writeFile(cargoTomlPath, updatedLines.join("\n"), "utf8"); - console.log(`[INFO]: Cargo.toml version updated to: ${versionWithoutV}`); - } catch (error) { - console.error("Error updating Cargo.toml version:", error); - throw error; - } -} - -/** - * 更新 tauri.conf.json 文件中的版本号 - * @param {string} newVersion 新版本号 - */ -async function updateTauriConfigVersion(newVersion) { - const _dirname = process.cwd(); - const tauriConfigPath = path.join(_dirname, "src-tauri", "tauri.conf.json"); - try { - const data = await fs.readFile(tauriConfigPath, "utf8"); - const tauriConfig = JSON.parse(data); - - const versionWithoutV = newVersion.startsWith("v") - ? newVersion.slice(1) - : newVersion; - - console.log( - "[INFO]: Current tauri.conf.json version is: ", - tauriConfig.version, - ); - tauriConfig.version = versionWithoutV; - await fs.writeFile( - tauriConfigPath, - JSON.stringify(tauriConfig, null, 2), - "utf8", - ); - console.log( - `[INFO]: tauri.conf.json version updated to: ${versionWithoutV}`, - ); - } catch (error) { - console.error("Error updating tauri.conf.json version:", error); - throw error; - } -} - -/** - * 获取当前版本号(从package.json) - */ -async function getCurrentVersion() { - const _dirname = process.cwd(); - const packageJsonPath = path.join(_dirname, "package.json"); - try { - const data = await fs.readFile(packageJsonPath, "utf8"); - const packageJson = JSON.parse(data); - return packageJson.version; - } catch (error) { - console.error("Error getting current version:", error); - throw error; - } -} - -/** - * 主函数,更新所有文件的版本号 - * @param {string} versionArg 版本参数(可以是标签或完整版本号) - */ -async function main(versionArg) { - if (!versionArg) { - console.error("Error: Version argument is required"); - process.exit(1); - } - - try { - let newVersion; - const validTags = ["alpha", "beta", "rc"]; - - // 判断参数是标签还是完整版本号 - if (validTags.includes(versionArg.toLowerCase())) { - // 标签模式:在当前版本基础上添加标签 - const currentVersion = await getCurrentVersion(); - const baseVersion = currentVersion.replace( - /-(alpha|beta|rc)(\.\d+)?$/i, - "", - ); - newVersion = `${baseVersion}-${versionArg.toLowerCase()}`; - } else { - // 完整版本号模式 - if (!isValidVersion(versionArg)) { - console.error( - "Error: Invalid version format. Expected format: vX.X.X or vX.X.X-tag (e.g. v2.2.3 or v2.2.3-alpha)", - ); - process.exit(1); - } - newVersion = normalizeVersion(versionArg); - } - - console.log(`[INFO]: Updating versions to: ${newVersion}`); - await updatePackageVersion(newVersion); - await updateCargoVersion(newVersion); - await updateTauriConfigVersion(newVersion); - console.log("[SUCCESS]: All version updates completed successfully!"); - } catch (error) { - console.error("[ERROR]: Failed to update versions:", error); - process.exit(1); - } -} - -// Example: -// pnpm release-version 2.2.3-alpha -// 设置命令行界面 -program - .name("pnpm release-version") - .description( - "Update project version numbers. Can add tag (alpha/beta/rc) or set full version (e.g. v2.2.3 or v2.2.3-alpha)", - ) - .argument( - "", - "version tag (alpha/beta/rc) or full version (e.g. v2.2.3 or v2.2.3-alpha)", - ) - .action(main) - .parse(process.argv); diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 2f0c2405..836798de 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1042,7 +1042,7 @@ dependencies = [ [[package]] name = "clash-verge" -version = "2.2.4-alpha" +version = "2.3.0" dependencies = [ "ab_glyph", "aes-gcm", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 28365eef..f8544bda 100755 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clash-verge" -version = "2.2.4-alpha" +version = "2.3.0" description = "clash verge" authors = ["zzzgydi", "wonfen", "MystiPanda"] license = "GPL-3.0-only" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 149aad88..0d52e46b 100755 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,5 +1,5 @@ { - "version": "2.2.4-alpha", + "version": "2.3.0", "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "bundle": { "active": true,