From f9ac19c74cfb0e85187a3d8a6d50faf3574a2651 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 06:33:20 +0800 Subject: [PATCH 01/19] =?UTF-8?q?=E5=88=9B=E5=BB=BA=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build | 134 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 .github/workflows/build diff --git a/.github/workflows/build b/.github/workflows/build new file mode 100644 index 0000000..3067ba8 --- /dev/null +++ b/.github/workflows/build @@ -0,0 +1,134 @@ +name: Build and Upload Artifacts + +on: + # 在推送到 main 分支时触发 + push: + branches: [ main, master ] + # 在创建 Pull Request 时触发 + pull_request: + branches: [ main, master ] + # 允许手动触发 + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # 1. 检出代码 + - name: Checkout code + uses: actions/checkout@v4 + + # 2. 根据项目类型设置环境(以下是常见示例,根据实际项目选择) + + # Java/Maven 项目 + - name: Set up JDK 17 + if: false # 如果是 Maven 项目,改为 true + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + # Node.js 项目 + - name: Set up Node.js + if: false # 如果是 Node 项目,改为 true + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + # Python 项目 + - name: Set up Python + if: false # 如果是 Python 项目,改为 true + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + # .NET 项目 + - name: Set up .NET + if: false # 如果是 .NET 项目,改为 true + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + # 3. 执行构建(根据实际项目修改命令) + + # Maven 构建示例 + - name: Build with Maven + if: false + run: mvn clean package -DskipTests + + # Gradle 构建示例 + - name: Build with Gradle + if: false + run: ./gradlew build + + # Node.js 构建示例 + - name: Build Node.js project + if: false + run: | + npm ci + npm run build + + # Python 构建/打包示例 + - name: Build Python package + if: false + run: | + pip install build + python -m build + + # .NET 构建示例 + - name: Build .NET project + if: false + run: dotnet publish -c Release -o ./publish + + # 通用构建(如果没有特定构建工具,直接打包文件) + - name: General build + run: | + # 创建输出目录 + mkdir -p ./dist + # 这里可以添加自定义构建命令 + # 例如:复制需要的文件到 ./dist 目录 + cp -r ./* ./dist/ 2>/dev/null || true + + # 4. 上传构建产物(核心步骤) + + # 方式一:上传到 GitHub Artifacts(推荐,90天保留期) + - name: Upload Build Artifacts to GitHub + uses: actions/upload-artifact@v4 + with: + # 产物名称,会显示在 Actions 页面的 Artifacts 部分 + name: build-artifacts-${{ github.run_id }} + # 要上传的文件或目录路径(支持通配符) + path: | + target/*.jar + build/libs/*.jar + dist/ + publish/ + *.zip + *.tar.gz + # 保留天数(默认90天,可设置1-90) + retention-days: 30 + # 如果文件不存在是否报错(false表示不报错) + if-no-files-found: warn + + # 方式二:创建 GitHub Release 并上传(适合正式版本) + - name: Create Release and Upload Assets + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + uses: softprops/action-gh-release@v1 + with: + files: | + target/*.jar + build/libs/*.jar + dist/* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # 5. 可选:部署到 GitHub Pages(如果是静态网站) + - name: Deploy to GitHub Pages + if: false # 需要时改为 true + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./dist From 4e1198e190c178fe87755ea38a1edbbced213f09 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 06:34:39 +0800 Subject: [PATCH 02/19] =?UTF-8?q?=E5=88=9B=E5=BB=BA=20build.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 134 ++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..3067ba8 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,134 @@ +name: Build and Upload Artifacts + +on: + # 在推送到 main 分支时触发 + push: + branches: [ main, master ] + # 在创建 Pull Request 时触发 + pull_request: + branches: [ main, master ] + # 允许手动触发 + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # 1. 检出代码 + - name: Checkout code + uses: actions/checkout@v4 + + # 2. 根据项目类型设置环境(以下是常见示例,根据实际项目选择) + + # Java/Maven 项目 + - name: Set up JDK 17 + if: false # 如果是 Maven 项目,改为 true + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + # Node.js 项目 + - name: Set up Node.js + if: false # 如果是 Node 项目,改为 true + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + # Python 项目 + - name: Set up Python + if: false # 如果是 Python 项目,改为 true + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + # .NET 项目 + - name: Set up .NET + if: false # 如果是 .NET 项目,改为 true + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + # 3. 执行构建(根据实际项目修改命令) + + # Maven 构建示例 + - name: Build with Maven + if: false + run: mvn clean package -DskipTests + + # Gradle 构建示例 + - name: Build with Gradle + if: false + run: ./gradlew build + + # Node.js 构建示例 + - name: Build Node.js project + if: false + run: | + npm ci + npm run build + + # Python 构建/打包示例 + - name: Build Python package + if: false + run: | + pip install build + python -m build + + # .NET 构建示例 + - name: Build .NET project + if: false + run: dotnet publish -c Release -o ./publish + + # 通用构建(如果没有特定构建工具,直接打包文件) + - name: General build + run: | + # 创建输出目录 + mkdir -p ./dist + # 这里可以添加自定义构建命令 + # 例如:复制需要的文件到 ./dist 目录 + cp -r ./* ./dist/ 2>/dev/null || true + + # 4. 上传构建产物(核心步骤) + + # 方式一:上传到 GitHub Artifacts(推荐,90天保留期) + - name: Upload Build Artifacts to GitHub + uses: actions/upload-artifact@v4 + with: + # 产物名称,会显示在 Actions 页面的 Artifacts 部分 + name: build-artifacts-${{ github.run_id }} + # 要上传的文件或目录路径(支持通配符) + path: | + target/*.jar + build/libs/*.jar + dist/ + publish/ + *.zip + *.tar.gz + # 保留天数(默认90天,可设置1-90) + retention-days: 30 + # 如果文件不存在是否报错(false表示不报错) + if-no-files-found: warn + + # 方式二:创建 GitHub Release 并上传(适合正式版本) + - name: Create Release and Upload Assets + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + uses: softprops/action-gh-release@v1 + with: + files: | + target/*.jar + build/libs/*.jar + dist/* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # 5. 可选:部署到 GitHub Pages(如果是静态网站) + - name: Deploy to GitHub Pages + if: false # 需要时改为 true + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./dist From b7cba6a864527001659646cd16d63ed5834da982 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 12:34:38 +0800 Subject: [PATCH 03/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20build.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 299 ++++++++++++++++++++++-------------- 1 file changed, 183 insertions(+), 116 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3067ba8..0654e79 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,134 +1,201 @@ -name: Build and Upload Artifacts +name: Build & Release on: - # 在推送到 main 分支时触发 push: - branches: [ main, master ] - # 在创建 Pull Request 时触发 - pull_request: - branches: [ main, master ] - # 允许手动触发 + branches: [main, master] + paths-ignore: + - '**.md' + - '.github/**' + workflow_dispatch: + inputs: + version: + description: '版本号 (如 1.0.0.0)' + required: true + type: string + release: + description: '是否创建 GitHub Release' + required: true + type: boolean + default: true + draft: + description: '是否为草稿 Release' + required: true + type: boolean + default: true + +env: + PLUGIN_NAME: YourPlugin jobs: build: - runs-on: ubuntu-latest + runs-on: windows-2022 + name: Build Plugin + + outputs: + version: ${{ steps.build-step.outputs.version }} steps: - # 1. 检出代码 - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive - # 2. 根据项目类型设置环境(以下是常见示例,根据实际项目选择) - - # Java/Maven 项目 - - name: Set up JDK 17 - if: false # 如果是 Maven 项目,改为 true - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: maven + - name: Setup .NET 8 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' - # Node.js 项目 - - name: Set up Node.js - if: false # 如果是 Node 项目,改为 true - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' + - name: Build & Publish + id: build-step + shell: pwsh + run: | + # 查找项目文件 + $csproj = Get-ChildItem -Path . -Filter "*.csproj" -Recurse | Select-Object -First 1 + if (-not $csproj) { + Write-Error "❌ 未找到 .csproj 文件" + exit 1 + } + Write-Host "📄 项目: $($csproj.FullName)" -ForegroundColor Gray + + # 获取版本号 + if ("${{ github.event.inputs.version }}") { + $version = "${{ github.event.inputs.version }}" + } else { + [xml]$proj = Get-Content $csproj.FullName + $version = $proj.Project.PropertyGroup.Version + } + Write-Host "🔖 版本: $version" -ForegroundColor Cyan + echo "version=$version" >> $env:GITHUB_OUTPUT + + # 清理并创建输出目录 + $OutputPath = "./output" + if (Test-Path $OutputPath) { + Remove-Item $OutputPath -Recurse -Force + } + New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null + + # 还原依赖 + Write-Host "📦 还原 NuGet 包..." -ForegroundColor Yellow + dotnet restore $csproj.FullName + if ($LASTEXITCODE -ne 0) { throw "还原失败" } + + # 构建 + Write-Host "🔨 编译项目..." -ForegroundColor Yellow + dotnet build $csproj.FullName -c Release --no-restore + if ($LASTEXITCODE -ne 0) { throw "构建失败" } + + # 发布(生成 CIPX) + Write-Host "📤 发布项目..." -ForegroundColor Yellow + dotnet publish $csproj.FullName ` + -c Release ` + --no-build ` + -p:CreateCipx=true ` + -o $OutputPath + if ($LASTEXITCODE -ne 0) { throw "发布失败" } + + # 显示结果 + Write-Host "" + Write-Host "✅ 构建完成!产物列表:" -ForegroundColor Green + Get-ChildItem $OutputPath -Recurse | ForEach-Object { + $size = if ($_.Length -lt 1KB) { "$($_.Length) B" } + elseif ($_.Length -lt 1MB) { "{0:F2} KB" -f ($_.Length/1KB) } + else { "{0:F2} MB" -f ($_.Length/1MB) } + Write-Host " 📎 $($_.Name) ($size)" -ForegroundColor Gray + } - # Python 项目 - - name: Set up Python - if: false # 如果是 Python 项目,改为 true - uses: actions/setup-python@v5 - with: - python-version: '3.11' + # 上传 CIPX 文件(必需) + - name: Upload CIPX Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PLUGIN_NAME }}_cipx + path: ./output/*.cipx + if-no-files-found: error + retention-days: 30 - # .NET 项目 - - name: Set up .NET - if: false # 如果是 .NET 项目,改为 true - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.0.x' + # 上传 NuGet 包(可选) + - name: Upload NuPkg Artifact + uses: actions/upload-artifact@v4 + if: ${{ hashFiles('./output/*.nupkg') != '' }} + with: + name: ${{ env.PLUGIN_NAME }}_nupkg + path: ./output/*.nupkg + retention-days: 7 - # 3. 执行构建(根据实际项目修改命令) + release: + runs-on: windows-2022 + name: Create Release + needs: build + if: ${{ github.event.inputs.release == true && github.event_name == 'workflow_dispatch' }} - # Maven 构建示例 - - name: Build with Maven - if: false - run: mvn clean package -DskipTests - - # Gradle 构建示例 - - name: Build with Gradle - if: false - run: ./gradlew build - - # Node.js 构建示例 - - name: Build Node.js project - if: false - run: | - npm ci - npm run build - - # Python 构建/打包示例 - - name: Build Python package - if: false - run: | - pip install build - python -m build - - # .NET 构建示例 - - name: Build .NET project - if: false - run: dotnet publish -c Release -o ./publish - - # 通用构建(如果没有特定构建工具,直接打包文件) - - name: General build - run: | - # 创建输出目录 - mkdir -p ./dist - # 这里可以添加自定义构建命令 - # 例如:复制需要的文件到 ./dist 目录 - cp -r ./* ./dist/ 2>/dev/null || true - - # 4. 上传构建产物(核心步骤) + permissions: + contents: write - # 方式一:上传到 GitHub Artifacts(推荐,90天保留期) - - name: Upload Build Artifacts to GitHub - uses: actions/upload-artifact@v4 - with: - # 产物名称,会显示在 Actions 页面的 Artifacts 部分 - name: build-artifacts-${{ github.run_id }} - # 要上传的文件或目录路径(支持通配符) - path: | - target/*.jar - build/libs/*.jar - dist/ - publish/ - *.zip - *.tar.gz - # 保留天数(默认90天,可设置1-90) - retention-days: 30 - # 如果文件不存在是否报错(false表示不报错) - if-no-files-found: warn + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download Artifacts + uses: actions/download-artifact@v4 + with: + path: ./artifacts + pattern: ${{ env.PLUGIN_NAME }}_* + merge-multiple: true - # 方式二:创建 GitHub Release 并上传(适合正式版本) - - name: Create Release and Upload Assets - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - uses: softprops/action-gh-release@v1 - with: - files: | - target/*.jar - build/libs/*.jar - dist/* - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Generate Release Note + shell: pwsh + run: | + $version = "${{ needs.build.outputs.version }}" + $changelogPath = "./changelog/${version}.md" + + $md5Summary = @" + + ## 文件校验 + + > [!important] + > 下载后请校验文件 MD5 值,确保文件完整性。 + + | 文件名 | MD5 | + |--------|-----| + "@ + + $hashes = [ordered]@{} + + # 计算所有文件的 MD5 + Get-ChildItem ./artifacts -File | ForEach-Object { + $hash = Get-FileHash $_.FullName -Algorithm MD5 + $md5Summary += "`n| $($_.Name) | ``$($hash.Hash)`` |" + $hashes.Add($_.Name, $hash.Hash) + } + + # 添加隐藏元数据 + $json = ConvertTo-Json $hashes -Compress + $md5Summary += "`n`n" + + # 读取或创建变更日志 + if (Test-Path $changelogPath) { + $changelog = Get-Content $changelogPath -Raw + } else { + $changelog = "## 版本 ${version}`n`n- 更新内容详见提交历史" + } + + $fullContent = $changelog + "`n`n" + $md5Summary + $fullContent | Out-File -FilePath "./release-note.md" -Encoding utf8 + + Write-Host "生成的发布说明:" -ForegroundColor Gray + Get-Content "./release-note.md" - # 5. 可选:部署到 GitHub Pages(如果是静态网站) - - name: Deploy to GitHub Pages - if: false # 需要时改为 true - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./dist + - name: Create GitHub Release + uses: ncipollo/release-action@v1 + with: + artifacts: "./artifacts/*" + tag: v${{ needs.build.outputs.version }} + name: ${{ env.PLUGIN_NAME }} v${{ needs.build.outputs.version }} + bodyFile: ./release-note.md + draft: ${{ github.event.inputs.draft }} + prerelease: false + token: ${{ secrets.GITHUB_TOKEN }} + generateReleaseNotes: false \ No newline at end of file From a9e22a75a5376ae0eccb7d3c83a8a033feed22f1 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 12:46:37 +0800 Subject: [PATCH 04/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20build.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 206 ++++++++---------------------------- 1 file changed, 45 insertions(+), 161 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0654e79..07742f8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,201 +1,85 @@ -name: Build & Release +name: Build & Publish on: push: - branches: [main, master] - paths-ignore: - - '**.md' - - '.github/**' - workflow_dispatch: inputs: - version: - description: '版本号 (如 1.0.0.0)' + release_tag: + description: '发布标签' required: true type: string - release: - description: '是否创建 GitHub Release' - required: true - type: boolean - default: true - draft: - description: '是否为草稿 Release' - required: true - type: boolean - default: true - -env: - PLUGIN_NAME: YourPlugin jobs: build: - runs-on: windows-2022 + runs-on: 'windows-2022' name: Build Plugin - - outputs: - version: ${{ steps.build-step.outputs.version }} - steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v3 with: fetch-depth: 0 submodules: recursive + ref: ${{ github.event_name != 'workflow_dispatch' && github.ref || github.event.inputs.release_tag }} - - name: Setup .NET 8 - uses: actions/setup-dotnet@v4 + - name: List files + run: ls + + - name: Install .NET Core + uses: actions/setup-dotnet@v3 with: - dotnet-version: '8.0.x' + dotnet-version: 8.0.x - - name: Build & Publish - id: build-step - shell: pwsh - run: | - # 查找项目文件 - $csproj = Get-ChildItem -Path . -Filter "*.csproj" -Recurse | Select-Object -First 1 - if (-not $csproj) { - Write-Error "❌ 未找到 .csproj 文件" - exit 1 - } - Write-Host "📄 项目: $($csproj.FullName)" -ForegroundColor Gray - - # 获取版本号 - if ("${{ github.event.inputs.version }}") { - $version = "${{ github.event.inputs.version }}" - } else { - [xml]$proj = Get-Content $csproj.FullName - $version = $proj.Project.PropertyGroup.Version - } - Write-Host "🔖 版本: $version" -ForegroundColor Cyan - echo "version=$version" >> $env:GITHUB_OUTPUT - - # 清理并创建输出目录 - $OutputPath = "./output" - if (Test-Path $OutputPath) { - Remove-Item $OutputPath -Recurse -Force - } - New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null - - # 还原依赖 - Write-Host "📦 还原 NuGet 包..." -ForegroundColor Yellow - dotnet restore $csproj.FullName - if ($LASTEXITCODE -ne 0) { throw "还原失败" } - - # 构建 - Write-Host "🔨 编译项目..." -ForegroundColor Yellow - dotnet build $csproj.FullName -c Release --no-restore - if ($LASTEXITCODE -ne 0) { throw "构建失败" } - - # 发布(生成 CIPX) - Write-Host "📤 发布项目..." -ForegroundColor Yellow - dotnet publish $csproj.FullName ` - -c Release ` - --no-build ` - -p:CreateCipx=true ` - -o $OutputPath - if ($LASTEXITCODE -ne 0) { throw "发布失败" } - - # 显示结果 - Write-Host "" - Write-Host "✅ 构建完成!产物列表:" -ForegroundColor Green - Get-ChildItem $OutputPath -Recurse | ForEach-Object { - $size = if ($_.Length -lt 1KB) { "$($_.Length) B" } - elseif ($_.Length -lt 1MB) { "{0:F2} KB" -f ($_.Length/1KB) } - else { "{0:F2} MB" -f ($_.Length/1MB) } - Write-Host " 📎 $($_.Name) ($size)" -ForegroundColor Gray - } + - name: Build + run: 'pwsh -ep bypass ./scripts/build.ps1' - # 上传 CIPX 文件(必需) - - name: Upload CIPX Artifact + - name: Upload Plugin to Artifacts + id: upload-unsigned-artifact uses: actions/upload-artifact@v4 with: - name: ${{ env.PLUGIN_NAME }}_cipx - path: ./output/*.cipx - if-no-files-found: error - retention-days: 30 + name: 'DutyIsland_Plugin' + path: | + ./DutyIsland/cipx/*.cipx + ./DutyIsland/cipx/*.md - # 上传 NuGet 包(可选) - - name: Upload NuPkg Artifact + - name: Upload NuPkg to Artifacts uses: actions/upload-artifact@v4 - if: ${{ hashFiles('./output/*.nupkg') != '' }} with: - name: ${{ env.PLUGIN_NAME }}_nupkg - path: ./output/*.nupkg - retention-days: 7 + name: 'DutyIsland_NuPkg' + path: | + ./**/bin/Release/*.nupkg - release: + publish: runs-on: windows-2022 - name: Create Release - needs: build - if: ${{ github.event.inputs.release == true && github.event_name == 'workflow_dispatch' }} - - permissions: - contents: write - + if: ${{ always() && success('build') && github.event.inputs.release_tag && github.event_name != 'pull_request' }} + needs: [ build ] + concurrency: + group: "publish-public" steps: - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout + uses: actions/checkout@v3 with: fetch-depth: 0 + submodules: recursive + ref: ${{ github.event_name != 'workflow_dispatch' && github.ref || github.event.inputs.release_tag }} - name: Download Artifacts uses: actions/download-artifact@v4 with: - path: ./artifacts - pattern: ${{ env.PLUGIN_NAME }}_* - merge-multiple: true + path: ./out_artifacts + + - name: Combine Artifacts + run: pwsh -ep bypass ./scripts/combine-artifacts.ps1 - name: Generate Release Note - shell: pwsh - run: | - $version = "${{ needs.build.outputs.version }}" - $changelogPath = "./changelog/${version}.md" - - $md5Summary = @" - - ## 文件校验 - - > [!important] - > 下载后请校验文件 MD5 值,确保文件完整性。 - - | 文件名 | MD5 | - |--------|-----| - "@ - - $hashes = [ordered]@{} - - # 计算所有文件的 MD5 - Get-ChildItem ./artifacts -File | ForEach-Object { - $hash = Get-FileHash $_.FullName -Algorithm MD5 - $md5Summary += "`n| $($_.Name) | ``$($hash.Hash)`` |" - $hashes.Add($_.Name, $hash.Hash) - } - - # 添加隐藏元数据 - $json = ConvertTo-Json $hashes -Compress - $md5Summary += "`n`n" - - # 读取或创建变更日志 - if (Test-Path $changelogPath) { - $changelog = Get-Content $changelogPath -Raw - } else { - $changelog = "## 版本 ${version}`n`n- 更新内容详见提交历史" - } - - $fullContent = $changelog + "`n`n" + $md5Summary - $fullContent | Out-File -FilePath "./release-note.md" -Encoding utf8 - - Write-Host "生成的发布说明:" -ForegroundColor Gray - Get-Content "./release-note.md" + env: + tagName: ${{ github.event.inputs.release_tag }} + run: 'pwsh -ep bypass ./scripts/gen-release-note.ps1' - - name: Create GitHub Release + - name: Upload Plugin to release uses: ncipollo/release-action@v1 with: - artifacts: "./artifacts/*" - tag: v${{ needs.build.outputs.version }} - name: ${{ env.PLUGIN_NAME }} v${{ needs.build.outputs.version }} + artifacts: "./out/*.cipx,./out/*.md" + draft: true bodyFile: ./release-note.md - draft: ${{ github.event.inputs.draft }} - prerelease: false token: ${{ secrets.GITHUB_TOKEN }} - generateReleaseNotes: false \ No newline at end of file + tag: ${{ github.event_name != 'workflow_dispatch' && github.ref || github.event.inputs.release_tag }} \ No newline at end of file From 01ae509c038e7b8ef292e810abf1f843ab932855 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 12:49:52 +0800 Subject: [PATCH 05/19] Add build script for DutyIsland project --- scripts/build.ps1 | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 scripts/build.ps1 diff --git a/scripts/build.ps1 b/scripts/build.ps1 new file mode 100644 index 0000000..3bf3e07 --- /dev/null +++ b/scripts/build.ps1 @@ -0,0 +1,2 @@ +Write-Host 开始构建 DutyIsland +dotnet publish -p:CreateCipx=true -c Release From aa5426d05c1e989d5db09638a93099a6a0dbba3d Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 12:50:32 +0800 Subject: [PATCH 06/19] Create combine-artifacts.ps1 --- scripts/combine-artifacts.ps1 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 scripts/combine-artifacts.ps1 diff --git a/scripts/combine-artifacts.ps1 b/scripts/combine-artifacts.ps1 new file mode 100644 index 0000000..5358efa --- /dev/null +++ b/scripts/combine-artifacts.ps1 @@ -0,0 +1,14 @@ +$artifacts = Get-ChildItem -Path ./out_artifacts -Directory + +if ($(Test-Path ./out) -eq $false) { + mkdir out +} + +Write-Host "Coping" -ForegroundColor Gray +foreach ($artifact in $artifacts) { + Get-ChildItem -Path ./out_artifacts/$($artifact.Name) + Copy-Item ./out_artifacts/$($artifact.Name)/* -Destination ./out/ -Recurse -Force +} + +Write-Host "Copy Result" -ForegroundColor Gray +Get-ChildItem -Path ./out/ From 77bd56feae54de76b2898520c63e033a1c6722be Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 12:51:03 +0800 Subject: [PATCH 07/19] Add script to generate release notes with MD5 Generate a release note with MD5 checksums for files. --- scripts/gen-release-note.ps1 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 scripts/gen-release-note.ps1 diff --git a/scripts/gen-release-note.ps1 b/scripts/gen-release-note.ps1 new file mode 100644 index 0000000..375204e --- /dev/null +++ b/scripts/gen-release-note.ps1 @@ -0,0 +1,25 @@ +$md5Summary = " +> [!important] +> 下载时请注意核对文件MD5是否正确。 + +| 文件名 | MD5 | +| --- | --- | +" + +$hashes = [ordered]@{} + +$hash = Get-FileHash ./out/DutyIsland.cipx -Algorithm MD5 +$hashString = $hash.Hash +$md5Summary += "| DutyIsland.cipx | ``${hashString}`` |`n" +$hashes.Add("DutyIsland.cipx", $hashString) + +$json = ConvertTo-Json $hashes -Compress + +$md5Summary += "`n" +$changelog = Get-Content "./changelog/${env:tagName}.md" +$fullContent = $changelog + $md5Summary + +Write-Output $fullContent > "release-note.md" + +Write-Host "Release Note" -ForegroundColor Gray +Write-Host $fullContent -ForegroundColor Gray From e683277bd6f6ee7e17feb9f14d23b7c310da64d1 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 13:00:35 +0800 Subject: [PATCH 08/19] =?UTF-8?q?=E5=88=9B=E5=BB=BA=20b.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/b.yml | 134 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 .github/workflows/b.yml diff --git a/.github/workflows/b.yml b/.github/workflows/b.yml new file mode 100644 index 0000000..3067ba8 --- /dev/null +++ b/.github/workflows/b.yml @@ -0,0 +1,134 @@ +name: Build and Upload Artifacts + +on: + # 在推送到 main 分支时触发 + push: + branches: [ main, master ] + # 在创建 Pull Request 时触发 + pull_request: + branches: [ main, master ] + # 允许手动触发 + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + # 1. 检出代码 + - name: Checkout code + uses: actions/checkout@v4 + + # 2. 根据项目类型设置环境(以下是常见示例,根据实际项目选择) + + # Java/Maven 项目 + - name: Set up JDK 17 + if: false # 如果是 Maven 项目,改为 true + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: maven + + # Node.js 项目 + - name: Set up Node.js + if: false # 如果是 Node 项目,改为 true + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + # Python 项目 + - name: Set up Python + if: false # 如果是 Python 项目,改为 true + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + # .NET 项目 + - name: Set up .NET + if: false # 如果是 .NET 项目,改为 true + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + # 3. 执行构建(根据实际项目修改命令) + + # Maven 构建示例 + - name: Build with Maven + if: false + run: mvn clean package -DskipTests + + # Gradle 构建示例 + - name: Build with Gradle + if: false + run: ./gradlew build + + # Node.js 构建示例 + - name: Build Node.js project + if: false + run: | + npm ci + npm run build + + # Python 构建/打包示例 + - name: Build Python package + if: false + run: | + pip install build + python -m build + + # .NET 构建示例 + - name: Build .NET project + if: false + run: dotnet publish -c Release -o ./publish + + # 通用构建(如果没有特定构建工具,直接打包文件) + - name: General build + run: | + # 创建输出目录 + mkdir -p ./dist + # 这里可以添加自定义构建命令 + # 例如:复制需要的文件到 ./dist 目录 + cp -r ./* ./dist/ 2>/dev/null || true + + # 4. 上传构建产物(核心步骤) + + # 方式一:上传到 GitHub Artifacts(推荐,90天保留期) + - name: Upload Build Artifacts to GitHub + uses: actions/upload-artifact@v4 + with: + # 产物名称,会显示在 Actions 页面的 Artifacts 部分 + name: build-artifacts-${{ github.run_id }} + # 要上传的文件或目录路径(支持通配符) + path: | + target/*.jar + build/libs/*.jar + dist/ + publish/ + *.zip + *.tar.gz + # 保留天数(默认90天,可设置1-90) + retention-days: 30 + # 如果文件不存在是否报错(false表示不报错) + if-no-files-found: warn + + # 方式二:创建 GitHub Release 并上传(适合正式版本) + - name: Create Release and Upload Assets + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') + uses: softprops/action-gh-release@v1 + with: + files: | + target/*.jar + build/libs/*.jar + dist/* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # 5. 可选:部署到 GitHub Pages(如果是静态网站) + - name: Deploy to GitHub Pages + if: false # 需要时改为 true + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./dist From cf127c97e0a4b6babe251e18de9b9e39f22491fe Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 18:31:52 +0800 Subject: [PATCH 09/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20b.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/b.yml | 299 ++++++++++++++++++++++++---------------- 1 file changed, 183 insertions(+), 116 deletions(-) diff --git a/.github/workflows/b.yml b/.github/workflows/b.yml index 3067ba8..78e9916 100644 --- a/.github/workflows/b.yml +++ b/.github/workflows/b.yml @@ -1,134 +1,201 @@ -name: Build and Upload Artifacts +name: Build & Release on: - # 在推送到 main 分支时触发 push: - branches: [ main, master ] - # 在创建 Pull Request 时触发 - pull_request: - branches: [ main, master ] - # 允许手动触发 + branches: [main, master] + paths-ignore: + - '**.md' + - '.github/**' + workflow_dispatch: + inputs: + version: + description: '版本号 (如 1.0.0.0)' + required: true + type: string + release: + description: '是否创建 GitHub Release' + required: true + type: boolean + default: true + draft: + description: '是否为草稿 Release' + required: true + type: boolean + default: true + +env: + PLUGIN_NAME: YourPlugin jobs: build: - runs-on: ubuntu-latest + runs-on: windows-2022 + name: Build Plugin + + outputs: + version: ${{ steps.build-step.outputs.version }} steps: - # 1. 检出代码 - - name: Checkout code - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive - # 2. 根据项目类型设置环境(以下是常见示例,根据实际项目选择) - - # Java/Maven 项目 - - name: Set up JDK 17 - if: false # 如果是 Maven 项目,改为 true - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: maven + - name: Setup .NET 8 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' - # Node.js 项目 - - name: Set up Node.js - if: false # 如果是 Node 项目,改为 true - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' + - name: Build & Publish + id: build-step + shell: pwsh + run: | + # 查找项目文件 + $csproj = Get-ChildItem -Path . -Filter "*.csproj" -Recurse | Select-Object -First 1 + if (-not $csproj) { + Write-Error "❌ 未找到 .csproj 文件" + exit 1 + } + Write-Host "📄 项目: $($csproj.FullName)" -ForegroundColor Gray + + # 获取版本号 + if ("${{ github.event.inputs.version }}") { + $version = "${{ github.event.inputs.version }}" + } else { + [xml]$proj = Get-Content $csproj.FullName + $version = $proj.Project.PropertyGroup.Version + } + Write-Host "🔖 版本: $version" -ForegroundColor Cyan + echo "version=$version" >> $env:GITHUB_OUTPUT + + # 清理并创建输出目录 + $OutputPath = "./output" + if (Test-Path $OutputPath) { + Remove-Item $OutputPath -Recurse -Force + } + New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null + + # 还原依赖 + Write-Host "📦 还原 NuGet 包..." -ForegroundColor Yellow + dotnet restore $csproj.FullName + if ($LASTEXITCODE -ne 0) { throw "还原失败" } + + # 构建 + Write-Host "🔨 编译项目..." -ForegroundColor Yellow + dotnet build $csproj.FullName -c Release --no-restore + if ($LASTEXITCODE -ne 0) { throw "构建失败" } + + # 发布(生成 CIPX) + Write-Host "📤 发布项目..." -ForegroundColor Yellow + dotnet publish $csproj.FullName ` + -c Release ` + --no-build ` + -p:CreateCipx=true ` + -o $OutputPath + if ($LASTEXITCODE -ne 0) { throw "发布失败" } + + # 显示结果 + Write-Host "" + Write-Host "✅ 构建完成!产物列表:" -ForegroundColor Green + Get-ChildItem $OutputPath -Recurse | ForEach-Object { + $size = if ($_.Length -lt 1KB) { "$($_.Length) B" } + elseif ($_.Length -lt 1MB) { "{0:F2} KB" -f ($_.Length/1KB) } + else { "{0:F2} MB" -f ($_.Length/1MB) } + Write-Host " 📎 $($_.Name) ($size)" -ForegroundColor Gray + } - # Python 项目 - - name: Set up Python - if: false # 如果是 Python 项目,改为 true - uses: actions/setup-python@v5 - with: - python-version: '3.11' + # 上传 CIPX 文件(必需) + - name: Upload CIPX Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.PLUGIN_NAME }}_cipx + path: ./output/*.cipx + if-no-files-found: error + retention-days: 30 - # .NET 项目 - - name: Set up .NET - if: false # 如果是 .NET 项目,改为 true - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.0.x' + # 上传 NuGet 包(可选) + - name: Upload NuPkg Artifact + uses: actions/upload-artifact@v4 + if: ${{ hashFiles('./output/*.nupkg') != '' }} + with: + name: ${{ env.PLUGIN_NAME }}_nupkg + path: ./output/*.nupkg + retention-days: 7 - # 3. 执行构建(根据实际项目修改命令) + release: + runs-on: windows-2022 + name: Create Release + needs: build + if: ${{ github.event.inputs.release == true && github.event_name == 'workflow_dispatch' }} - # Maven 构建示例 - - name: Build with Maven - if: false - run: mvn clean package -DskipTests - - # Gradle 构建示例 - - name: Build with Gradle - if: false - run: ./gradlew build - - # Node.js 构建示例 - - name: Build Node.js project - if: false - run: | - npm ci - npm run build - - # Python 构建/打包示例 - - name: Build Python package - if: false - run: | - pip install build - python -m build - - # .NET 构建示例 - - name: Build .NET project - if: false - run: dotnet publish -c Release -o ./publish - - # 通用构建(如果没有特定构建工具,直接打包文件) - - name: General build - run: | - # 创建输出目录 - mkdir -p ./dist - # 这里可以添加自定义构建命令 - # 例如:复制需要的文件到 ./dist 目录 - cp -r ./* ./dist/ 2>/dev/null || true - - # 4. 上传构建产物(核心步骤) + permissions: + contents: write - # 方式一:上传到 GitHub Artifacts(推荐,90天保留期) - - name: Upload Build Artifacts to GitHub - uses: actions/upload-artifact@v4 - with: - # 产物名称,会显示在 Actions 页面的 Artifacts 部分 - name: build-artifacts-${{ github.run_id }} - # 要上传的文件或目录路径(支持通配符) - path: | - target/*.jar - build/libs/*.jar - dist/ - publish/ - *.zip - *.tar.gz - # 保留天数(默认90天,可设置1-90) - retention-days: 30 - # 如果文件不存在是否报错(false表示不报错) - if-no-files-found: warn + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Download Artifacts + uses: actions/download-artifact@v4 + with: + path: ./artifacts + pattern: ${{ env.PLUGIN_NAME }}_* + merge-multiple: true - # 方式二:创建 GitHub Release 并上传(适合正式版本) - - name: Create Release and Upload Assets - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - uses: softprops/action-gh-release@v1 - with: - files: | - target/*.jar - build/libs/*.jar - dist/* - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Generate Release Note + shell: pwsh + run: | + $version = "${{ needs.build.outputs.version }}" + $changelogPath = "./changelog/${version}.md" + + $md5Summary = @" + + ## 文件校验 + + > [!important] + > 下载后请校验文件 MD5 值,确保文件完整性。 + + | 文件名 | MD5 | + |--------|-----| + "@ + + $hashes = [ordered]@{} + + # 计算所有文件的 MD5 + Get-ChildItem ./artifacts -File | ForEach-Object { + $hash = Get-FileHash $_.FullName -Algorithm MD5 + $md5Summary += "`n| $($_.Name) | ``$($hash.Hash)`` |" + $hashes.Add($_.Name, $hash.Hash) + } + + # 添加隐藏元数据 + $json = ConvertTo-Json $hashes -Compress + $md5Summary += "`n`n" + + # 读取或创建变更日志 + if (Test-Path $changelogPath) { + $changelog = Get-Content $changelogPath -Raw + } else { + $changelog = "## 版本 ${version}`n`n- 更新内容详见提交历史" + } + + $fullContent = $changelog + "`n`n" + $md5Summary + $fullContent | Out-File -FilePath "./release-note.md" -Encoding utf8 + + Write-Host "生成的发布说明:" -ForegroundColor Gray + Get-Content "./release-note.md" - # 5. 可选:部署到 GitHub Pages(如果是静态网站) - - name: Deploy to GitHub Pages - if: false # 需要时改为 true - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./dist + - name: Create GitHub Release + uses: ncipollo/release-action@v1 + with: + artifacts: "./artifacts/*" + tag: v${{ needs.build.outputs.version }} + name: ${{ env.PLUGIN_NAME }} v${{ needs.build.outputs.version }} + bodyFile: ./release-note.md + draft: ${{ github.event.inputs.draft }} + prerelease: false + token: ${{ secrets.GITHUB_TOKEN }} + generateReleaseNotes: false From a7057b73788ff043947323180c2d07e89055904b Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 18:45:41 +0800 Subject: [PATCH 10/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20b.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/b.yml | 68 +++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/.github/workflows/b.yml b/.github/workflows/b.yml index 78e9916..e2ca79d 100644 --- a/.github/workflows/b.yml +++ b/.github/workflows/b.yml @@ -25,7 +25,7 @@ on: default: true env: - PLUGIN_NAME: YourPlugin + PLUGIN_NAME: SystemTools jobs: build: @@ -34,6 +34,7 @@ jobs: outputs: version: ${{ steps.build-step.outputs.version }} + artifact_name: ${{ steps.pack-step.outputs.artifact_name }} steps: - name: Checkout code @@ -65,6 +66,10 @@ jobs: } else { [xml]$proj = Get-Content $csproj.FullName $version = $proj.Project.PropertyGroup.Version + if ([string]::IsNullOrWhiteSpace($version)) { + $version = "1.0.0" + Write-Host "⚠️ 未找到版本号,使用默认版本: $version" -ForegroundColor Yellow + } } Write-Host "🔖 版本: $version" -ForegroundColor Cyan echo "version=$version" >> $env:GITHUB_OUTPUT @@ -86,16 +91,15 @@ jobs: dotnet build $csproj.FullName -c Release --no-restore if ($LASTEXITCODE -ne 0) { throw "构建失败" } - # 发布(生成 CIPX) + # 发布 Write-Host "📤 发布项目..." -ForegroundColor Yellow dotnet publish $csproj.FullName ` -c Release ` --no-build ` - -p:CreateCipx=true ` -o $OutputPath if ($LASTEXITCODE -ne 0) { throw "发布失败" } - # 显示结果 + # 显示产物列表 Write-Host "" Write-Host "✅ 构建完成!产物列表:" -ForegroundColor Green Get-ChildItem $OutputPath -Recurse | ForEach-Object { @@ -105,23 +109,42 @@ jobs: Write-Host " 📎 $($_.Name) ($size)" -ForegroundColor Gray } - # 上传 CIPX 文件(必需) - - name: Upload CIPX Artifact + - name: Pack Artifacts + id: pack-step + shell: pwsh + run: | + $version = "${{ steps.build-step.outputs.version }}" + $zipName = "${{ env.PLUGIN_NAME }}_v${version}.zip" + $sourcePath = "./output" + $zipPath = "./${zipName}" + + # 检查输出目录是否存在文件 + $files = Get-ChildItem $sourcePath -Recurse -File + if ($files.Count -eq 0) { + Write-Error "❌ 输出目录为空,没有文件可以打包" + exit 1 + } + + Write-Host "📦 正在打包产物..." -ForegroundColor Yellow + Compress-Archive -Path "$sourcePath\*" -DestinationPath $zipPath -Force + + # 显示压缩包信息 + $zipSize = (Get-Item $zipPath).Length + $sizeStr = if ($zipSize -lt 1MB) { "{0:F2} KB" -f ($zipSize/1KB) } else { "{0:F2} MB" -f ($zipSize/1MB) } + Write-Host "✅ 压缩包创建成功: $zipName ($sizeStr)" -ForegroundColor Green + + # 输出文件名供后续步骤使用 + echo "artifact_name=$zipName" >> $env:GITHUB_OUTPUT + echo "zip_path=$zipPath" >> $env:GITHUB_OUTPUT + + - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: ${{ env.PLUGIN_NAME }}_cipx - path: ./output/*.cipx + name: ${{ steps.pack-step.outputs.artifact_name }} + path: ${{ steps.pack-step.outputs.zip_path }} if-no-files-found: error retention-days: 30 - - # 上传 NuGet 包(可选) - - name: Upload NuPkg Artifact - uses: actions/upload-artifact@v4 - if: ${{ hashFiles('./output/*.nupkg') != '' }} - with: - name: ${{ env.PLUGIN_NAME }}_nupkg - path: ./output/*.nupkg - retention-days: 7 + compression-level: 0 release: runs-on: windows-2022 @@ -138,12 +161,11 @@ jobs: with: fetch-depth: 0 - - name: Download Artifacts + - name: Download Artifact uses: actions/download-artifact@v4 with: + name: ${{ needs.build.outputs.artifact_name }} path: ./artifacts - pattern: ${{ env.PLUGIN_NAME }}_* - merge-multiple: true - name: Generate Release Note shell: pwsh @@ -164,18 +186,15 @@ jobs: $hashes = [ordered]@{} - # 计算所有文件的 MD5 Get-ChildItem ./artifacts -File | ForEach-Object { $hash = Get-FileHash $_.FullName -Algorithm MD5 $md5Summary += "`n| $($_.Name) | ``$($hash.Hash)`` |" $hashes.Add($_.Name, $hash.Hash) } - # 添加隐藏元数据 $json = ConvertTo-Json $hashes -Compress $md5Summary += "`n`n" - # 读取或创建变更日志 if (Test-Path $changelogPath) { $changelog = Get-Content $changelogPath -Raw } else { @@ -184,9 +203,6 @@ jobs: $fullContent = $changelog + "`n`n" + $md5Summary $fullContent | Out-File -FilePath "./release-note.md" -Encoding utf8 - - Write-Host "生成的发布说明:" -ForegroundColor Gray - Get-Content "./release-note.md" - name: Create GitHub Release uses: ncipollo/release-action@v1 From 86c6222f78f13cfd7b55ca13a75f79ea18424315 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 11 Mar 2026 19:22:30 +0800 Subject: [PATCH 11/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20b.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/b.yml | 446 ++++++++++++++++++++++++++-------------- 1 file changed, 294 insertions(+), 152 deletions(-) diff --git a/.github/workflows/b.yml b/.github/workflows/b.yml index e2ca79d..74e7d6d 100644 --- a/.github/workflows/b.yml +++ b/.github/workflows/b.yml @@ -1,217 +1,359 @@ -name: Build & Release +name: Release on: push: - branches: [main, master] - paths-ignore: - - '**.md' - - '.github/**' - + tags: + - '*' + workflow_dispatch: inputs: - version: - description: '版本号 (如 1.0.0.0)' + version_type: + description: '版本类型' required: true - type: string - release: - description: '是否创建 GitHub Release' + default: 'none' + type: choice + options: + - none + - patch + - minor + - major + - build + prerelease: + description: '是否为预发布' required: true - type: boolean default: true + type: boolean draft: - description: '是否为草稿 Release' + description: '是否创建草稿 Release' required: true + default: false type: boolean + create_release: + description: '是否创建 GitHub Release' + required: true default: true + type: boolean + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}-${{ github.sha }} + cancel-in-progress: true env: PLUGIN_NAME: SystemTools jobs: - build: - runs-on: windows-2022 - name: Build Plugin - + prepare: + runs-on: ubuntu-latest outputs: - version: ${{ steps.build-step.outputs.version }} - artifact_name: ${{ steps.pack-step.outputs.artifact_name }} - + tag_name: ${{ steps.get_tag.outputs.tag_name }} + version: ${{ steps.get_tag.outputs.version }} + is_prerelease: ${{ steps.release_type.outputs.is_prerelease }} + is_draft: ${{ steps.release_type.outputs.is_draft }} + changelog: ${{ steps.decode_changelog.outputs.changelog }} + steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - submodules: recursive + fetch-tags: true + + - name: Get version from csproj + id: get_version + shell: bash + run: | + CSPROJ=$(find . -name "*.csproj" -type f | head -1) + if [ -z "$CSPROJ" ]; then + echo "❌ 未找到 .csproj 文件" + exit 1 + fi + echo "📄 找到项目文件: $CSPROJ" + + VERSION=$(grep -oP '\K[^<]+' "$CSPROJ" || echo "") + if [ -z "$VERSION" ]; then + VERSION="1.0.0.0" + fi + + PARTS=$(echo "$VERSION" | tr '.' '\n' | wc -l) + while [ "$PARTS" -lt 4 ]; do + VERSION="${VERSION}.0" + PARTS=$((PARTS + 1)) + done + + echo "🔖 csproj 版本: $VERSION" + echo "current_version=$VERSION" >> $GITHUB_OUTPUT + + - name: Calculate new version + id: get_tag + shell: bash + run: | + if [ "${{ github.event_name }}" = "push" ]; then + TAG_NAME="${GITHUB_REF#refs/tags/}" + echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT + echo "version=$TAG_NAME" >> $GITHUB_OUTPUT + echo "使用推送的标签: $TAG_NAME" + else + CURRENT="${{ steps.get_version.outputs.current_version }}" + VERSION_TYPE="${{ github.event.inputs.version_type }}" + + if [ "$VERSION_TYPE" = "none" ]; then + NEW_VERSION="$CURRENT" + echo "使用 csproj 版本(无修改): $NEW_VERSION" + else + MAJOR=$(echo "$CURRENT" | cut -d. -f1) + MINOR=$(echo "$CURRENT" | cut -d. -f2) + PATCH=$(echo "$CURRENT" | cut -d. -f3) + BUILD=$(echo "$CURRENT" | cut -d. -f4) + + case "$VERSION_TYPE" in + major) + MAJOR=$((MAJOR + 1)) + MINOR=0 + PATCH=0 + BUILD=0 + ;; + minor) + MINOR=$((MINOR + 1)) + PATCH=0 + BUILD=0 + ;; + patch) + PATCH=$((PATCH + 1)) + BUILD=0 + ;; + build) + BUILD=$((BUILD + 1)) + ;; + esac + + NEW_VERSION="$MAJOR.$MINOR.$PATCH.$BUILD" + fi + + echo "tag_name=$NEW_VERSION" >> $GITHUB_OUTPUT + echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT + echo "最终版本: $NEW_VERSION" + fi + + - name: Determine release type + id: release_type + shell: bash + run: | + if [ "${{ github.event_name }}" = "push" ]; then + VERSION="${{ steps.get_tag.outputs.version }}" + BUILD=$(echo "$VERSION" | cut -d. -f4) + if [ "$BUILD" = "0" ]; then + echo "is_prerelease=false" >> $GITHUB_OUTPUT + else + echo "is_prerelease=true" >> $GITHUB_OUTPUT + fi + echo "is_draft=false" >> $GITHUB_OUTPUT + else + echo "is_prerelease=${{ github.event.inputs.prerelease }}" >> $GITHUB_OUTPUT + echo "is_draft=${{ github.event.inputs.draft }}" >> $GITHUB_OUTPUT + fi + + - name: Read changelog + id: read_changelog + shell: bash + run: | + VERSION="${{ steps.get_tag.outputs.version }}" + CHANGELOG_PATH="./changelog/${VERSION}.md" + + if [ -f "$CHANGELOG_PATH" ]; then + # 使用手动编写的变更日志 + CONTENT=$(cat "$CHANGELOG_PATH") + echo "✅ 使用手动变更日志: $CHANGELOG_PATH" + else + # 自动生成变更日志 + echo "⚠️ 未找到 $CHANGELOG_PATH,自动生成" + + LAST_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || git rev-list --max-parents=0 HEAD) + COMMITS=$(git log "$LAST_TAG..HEAD" --pretty=format:"- %s (%h)" --no-merges 2>/dev/null || echo "- 版本更新") + + # 修复:简化格式,只有一个 "## 变更内容",删除重复的提示 + CONTENT=$(printf "%s\n\n*日志由action自动生成*" "$COMMITS") + fi + + CONTENT_B64=$(echo "$CONTENT" | base64 -w 0) + echo "changelog_b64=$CONTENT_B64" >> $GITHUB_OUTPUT + + - name: Decode changelog + id: decode_changelog + shell: bash + run: | + echo "${{ steps.read_changelog.outputs.changelog_b64 }}" | base64 -d > changelog_output.txt + echo "changelog<> $GITHUB_OUTPUT + cat changelog_output.txt >> $GITHUB_OUTPUT + echo "CHANGELOG_EOF" >> $GITHUB_OUTPUT + + build: + needs: prepare + runs-on: windows-2022 + outputs: + cipx_size: ${{ steps.calculate_size.outputs.cipx_size }} + cipx_md5: ${{ steps.calculate_size.outputs.cipx_md5 }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 - name: Setup .NET 8 uses: actions/setup-dotnet@v4 with: dotnet-version: '8.0.x' - - name: Build & Publish - id: build-step + - name: Build and Package + id: build_package shell: pwsh run: | - # 查找项目文件 + $pluginName = "${{ env.PLUGIN_NAME }}" + $version = "${{ needs.prepare.outputs.version }}" + $csproj = Get-ChildItem -Path . -Filter "*.csproj" -Recurse | Select-Object -First 1 - if (-not $csproj) { - Write-Error "❌ 未找到 .csproj 文件" - exit 1 - } - Write-Host "📄 项目: $($csproj.FullName)" -ForegroundColor Gray - - # 获取版本号 - if ("${{ github.event.inputs.version }}") { - $version = "${{ github.event.inputs.version }}" - } else { - [xml]$proj = Get-Content $csproj.FullName - $version = $proj.Project.PropertyGroup.Version - if ([string]::IsNullOrWhiteSpace($version)) { - $version = "1.0.0" - Write-Host "⚠️ 未找到版本号,使用默认版本: $version" -ForegroundColor Yellow - } - } - Write-Host "🔖 版本: $version" -ForegroundColor Cyan - echo "version=$version" >> $env:GITHUB_OUTPUT + if (-not $csproj) { throw "未找到 .csproj 文件" } - # 清理并创建输出目录 $OutputPath = "./output" - if (Test-Path $OutputPath) { - Remove-Item $OutputPath -Recurse -Force - } + if (Test-Path $OutputPath) { Remove-Item $OutputPath -Recurse -Force } New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null - # 还原依赖 - Write-Host "📦 还原 NuGet 包..." -ForegroundColor Yellow dotnet restore $csproj.FullName - if ($LASTEXITCODE -ne 0) { throw "还原失败" } - - # 构建 - Write-Host "🔨 编译项目..." -ForegroundColor Yellow dotnet build $csproj.FullName -c Release --no-restore - if ($LASTEXITCODE -ne 0) { throw "构建失败" } - - # 发布 - Write-Host "📤 发布项目..." -ForegroundColor Yellow - dotnet publish $csproj.FullName ` - -c Release ` - --no-build ` - -o $OutputPath - if ($LASTEXITCODE -ne 0) { throw "发布失败" } - - # 显示产物列表 - Write-Host "" - Write-Host "✅ 构建完成!产物列表:" -ForegroundColor Green - Get-ChildItem $OutputPath -Recurse | ForEach-Object { - $size = if ($_.Length -lt 1KB) { "$($_.Length) B" } - elseif ($_.Length -lt 1MB) { "{0:F2} KB" -f ($_.Length/1KB) } - else { "{0:F2} MB" -f ($_.Length/1MB) } - Write-Host " 📎 $($_.Name) ($size)" -ForegroundColor Gray + + $PublishPath = "$OutputPath/publish" + dotnet publish $csproj.FullName -c Release --no-build -o $PublishPath + + $filesToPack = @( + "$PublishPath/$pluginName.pdb", + "$PublishPath/$pluginName.dll", + "$PublishPath/$pluginName.deps.json", + "$PublishPath/$pluginName.runtimeconfig.json", + "$PublishPath/Lyricify Lite - README.md", + "$PublishPath/README-1.md", + "$PublishPath/README-2.md", + "$PublishPath/jinyongshubiao.ps1", + "$PublishPath/jinyongshubiao.bat", + "$PublishPath/black.html", + "$PublishPath/huifu.bat", + "$PublishPath/$pluginName.pdb", + "$PublishPath/huifu.ps1", + "$PublishPath/icon-1.png", + "$PublishPath/$pluginName.dll", + "$PublishPath/README.md", + "$PublishPath/manifest.yml", + "$PublishPath/icon.png" + ) + + # 移除重复项并过滤不存在的文件 + $filesToPack = $filesToPack | Get-Unique | Where-Object { Test-Path $_ } + + Write-Host "找到的文件:" + foreach ($file in $filesToPack) { + Write-Host " - $file" } + + $PackTemp = "$OutputPath/pack_temp" + New-Item -ItemType Directory -Path $PackTemp -Force | Out-Null + + foreach ($file in $filesToPack) { + if (Test-Path $file) { + Write-Host "复制文件: $file" + Copy-Item $file $PackTemp/ + } else { + Write-Host "警告: 文件不存在 - $file" + } + } + + # 检查是否复制了文件 + if (-not (Get-ChildItem -Path $PackTemp -ErrorAction SilentlyContinue)) { + throw "没有文件被复制到打包目录!" + } + + $cipxName = "$pluginName.$version.cipx" + Compress-Archive -Path "$PackTemp/*" -DestinationPath "$OutputPath/$pluginName.zip" -Force + Move-Item "$OutputPath/$pluginName.zip" "$OutputPath/$cipxName" -Force + + Remove-Item $PublishPath, $PackTemp -Recurse -Force + + echo "cipx_name=$cipxName" >> $env:GITHUB_OUTPUT - - name: Pack Artifacts - id: pack-step + - name: Calculate size and MD5 + id: calculate_size shell: pwsh run: | - $version = "${{ steps.build-step.outputs.version }}" - $zipName = "${{ env.PLUGIN_NAME }}_v${version}.zip" - $sourcePath = "./output" - $zipPath = "./${zipName}" - - # 检查输出目录是否存在文件 - $files = Get-ChildItem $sourcePath -Recurse -File - if ($files.Count -eq 0) { - Write-Error "❌ 输出目录为空,没有文件可以打包" - exit 1 - } - - Write-Host "📦 正在打包产物..." -ForegroundColor Yellow - Compress-Archive -Path "$sourcePath\*" -DestinationPath $zipPath -Force + $cipxName = "${{ steps.build_package.outputs.cipx_name }}" + $cipxPath = "./output/$cipxName" - # 显示压缩包信息 - $zipSize = (Get-Item $zipPath).Length - $sizeStr = if ($zipSize -lt 1MB) { "{0:F2} KB" -f ($zipSize/1KB) } else { "{0:F2} MB" -f ($zipSize/1MB) } - Write-Host "✅ 压缩包创建成功: $zipName ($sizeStr)" -ForegroundColor Green + $size = (Get-Item $cipxPath).Length + $md5 = (Get-FileHash $cipxPath -Algorithm MD5).Hash - # 输出文件名供后续步骤使用 - echo "artifact_name=$zipName" >> $env:GITHUB_OUTPUT - echo "zip_path=$zipPath" >> $env:GITHUB_OUTPUT + echo "cipx_size=$size" >> $env:GITHUB_OUTPUT + echo "cipx_md5=$md5" >> $env:GITHUB_OUTPUT - name: Upload Artifact uses: actions/upload-artifact@v4 with: - name: ${{ steps.pack-step.outputs.artifact_name }} - path: ${{ steps.pack-step.outputs.zip_path }} - if-no-files-found: error - retention-days: 30 - compression-level: 0 + name: ${{ env.PLUGIN_NAME }}-${{ needs.prepare.outputs.version }} + path: ./output/*.cipx release: - runs-on: windows-2022 - name: Create Release - needs: build - if: ${{ github.event.inputs.release == true && github.event_name == 'workflow_dispatch' }} - + needs: [prepare, build] + runs-on: ubuntu-latest + # 只有在 push 事件时,或者手动触发且选择了创建 release 时才执行 + if: github.event_name == 'push' || github.event.inputs.create_release == 'true' permissions: contents: write - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 + steps: - name: Download Artifact uses: actions/download-artifact@v4 with: - name: ${{ needs.build.outputs.artifact_name }} - path: ./artifacts + name: ${{ env.PLUGIN_NAME }}-${{ needs.prepare.outputs.version }} - - name: Generate Release Note - shell: pwsh + - name: Create Release Body + id: create_body + shell: bash run: | - $version = "${{ needs.build.outputs.version }}" - $changelogPath = "./changelog/${version}.md" - - $md5Summary = @" - - ## 文件校验 - - > [!important] - > 下载后请校验文件 MD5 值,确保文件完整性。 + VERSION="${{ needs.prepare.outputs.version }}" + SIZE="${{ needs.build.outputs.cipx_size }}" + MD5="${{ needs.build.outputs.cipx_md5 }}" - | 文件名 | MD5 | - |--------|-----| - "@ + if [ "$SIZE" -lt 1024 ]; then + SIZE_STR="${SIZE} B" + elif [ "$SIZE" -lt 1048576 ]; then + SIZE_STR=$(awk "BEGIN {printf \"%.2f KB\", $SIZE/1024}") + else + SIZE_STR=$(awk "BEGIN {printf \"%.2f MB\", $SIZE/1048576}") + fi - $hashes = [ordered]@{} + CHANGELOG="${{ needs.prepare.outputs.changelog }}" - Get-ChildItem ./artifacts -File | ForEach-Object { - $hash = Get-FileHash $_.FullName -Algorithm MD5 - $md5Summary += "`n| $($_.Name) | ``$($hash.Hash)`` |" - $hashes.Add($_.Name, $hash.Hash) - } - - $json = ConvertTo-Json $hashes -Compress - $md5Summary += "`n`n" - - if (Test-Path $changelogPath) { - $changelog = Get-Content $changelogPath -Raw - } else { - $changelog = "## 版本 ${version}`n`n- 更新内容详见提交历史" - } + # 修复:只在手动生成时添加 "## 变更内容" 标题 + { + echo "## 变更内容" + echo "" + echo "$CHANGELOG" + echo "" + echo "## 文件信息" + echo "" + echo "| 文件名 | 大小 | MD5 |" + echo "|--------|------|-----|" + echo "| ${{ env.PLUGIN_NAME }}.${VERSION}.cipx | ${SIZE_STR} | \`${MD5}\` |" + echo "" + echo "" + } > release_body.txt - $fullContent = $changelog + "`n`n" + $md5Summary - $fullContent | Out-File -FilePath "./release-note.md" -Encoding utf8 + echo "body<> $GITHUB_OUTPUT + cat release_body.txt >> $GITHUB_OUTPUT + echo "BODY_EOF" >> $GITHUB_OUTPUT - name: Create GitHub Release - uses: ncipollo/release-action@v1 + uses: softprops/action-gh-release@v2 with: - artifacts: "./artifacts/*" - tag: v${{ needs.build.outputs.version }} - name: ${{ env.PLUGIN_NAME }} v${{ needs.build.outputs.version }} - bodyFile: ./release-note.md - draft: ${{ github.event.inputs.draft }} - prerelease: false - token: ${{ secrets.GITHUB_TOKEN }} - generateReleaseNotes: false + tag_name: ${{ needs.prepare.outputs.tag_name }} + name: ${{ env.PLUGIN_NAME }} ${{ needs.prepare.outputs.version }} + body: ${{ steps.create_body.outputs.body }} + draft: ${{ needs.prepare.outputs.is_draft }} + prerelease: ${{ needs.prepare.outputs.is_prerelease == 'true' }} + files: '*.cipx' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From d2f6441903243827e65086160873789b00715528 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Wed, 22 Apr 2026 21:01:30 +0800 Subject: [PATCH 12/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20b.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/b.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/b.yml b/.github/workflows/b.yml index 74e7d6d..2ebab46 100644 --- a/.github/workflows/b.yml +++ b/.github/workflows/b.yml @@ -226,7 +226,6 @@ jobs: "$PublishPath/$pluginName.pdb", "$PublishPath/$pluginName.dll", "$PublishPath/$pluginName.deps.json", - "$PublishPath/$pluginName.runtimeconfig.json", "$PublishPath/Lyricify Lite - README.md", "$PublishPath/README-1.md", "$PublishPath/README-2.md", @@ -234,13 +233,12 @@ jobs: "$PublishPath/jinyongshubiao.bat", "$PublishPath/black.html", "$PublishPath/huifu.bat", - "$PublishPath/$pluginName.pdb", "$PublishPath/huifu.ps1", "$PublishPath/icon-1.png", - "$PublishPath/$pluginName.dll", "$PublishPath/README.md", "$PublishPath/manifest.yml", - "$PublishPath/icon.png" + "$PublishPath/icon.png", + "$PublishPath/version.json" ) # 移除重复项并过滤不存在的文件 From b6db48efe80f61ecbd635f2426aba763b7798753 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Tue, 26 May 2026 06:26:59 +0800 Subject: [PATCH 13/19] =?UTF-8?q?=E5=88=A0=E9=99=A4=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build | 134 ---------------------------------------- 1 file changed, 134 deletions(-) delete mode 100644 .github/workflows/build diff --git a/.github/workflows/build b/.github/workflows/build deleted file mode 100644 index 3067ba8..0000000 --- a/.github/workflows/build +++ /dev/null @@ -1,134 +0,0 @@ -name: Build and Upload Artifacts - -on: - # 在推送到 main 分支时触发 - push: - branches: [ main, master ] - # 在创建 Pull Request 时触发 - pull_request: - branches: [ main, master ] - # 允许手动触发 - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - - steps: - # 1. 检出代码 - - name: Checkout code - uses: actions/checkout@v4 - - # 2. 根据项目类型设置环境(以下是常见示例,根据实际项目选择) - - # Java/Maven 项目 - - name: Set up JDK 17 - if: false # 如果是 Maven 项目,改为 true - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: maven - - # Node.js 项目 - - name: Set up Node.js - if: false # 如果是 Node 项目,改为 true - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - # Python 项目 - - name: Set up Python - if: false # 如果是 Python 项目,改为 true - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - # .NET 项目 - - name: Set up .NET - if: false # 如果是 .NET 项目,改为 true - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.0.x' - - # 3. 执行构建(根据实际项目修改命令) - - # Maven 构建示例 - - name: Build with Maven - if: false - run: mvn clean package -DskipTests - - # Gradle 构建示例 - - name: Build with Gradle - if: false - run: ./gradlew build - - # Node.js 构建示例 - - name: Build Node.js project - if: false - run: | - npm ci - npm run build - - # Python 构建/打包示例 - - name: Build Python package - if: false - run: | - pip install build - python -m build - - # .NET 构建示例 - - name: Build .NET project - if: false - run: dotnet publish -c Release -o ./publish - - # 通用构建(如果没有特定构建工具,直接打包文件) - - name: General build - run: | - # 创建输出目录 - mkdir -p ./dist - # 这里可以添加自定义构建命令 - # 例如:复制需要的文件到 ./dist 目录 - cp -r ./* ./dist/ 2>/dev/null || true - - # 4. 上传构建产物(核心步骤) - - # 方式一:上传到 GitHub Artifacts(推荐,90天保留期) - - name: Upload Build Artifacts to GitHub - uses: actions/upload-artifact@v4 - with: - # 产物名称,会显示在 Actions 页面的 Artifacts 部分 - name: build-artifacts-${{ github.run_id }} - # 要上传的文件或目录路径(支持通配符) - path: | - target/*.jar - build/libs/*.jar - dist/ - publish/ - *.zip - *.tar.gz - # 保留天数(默认90天,可设置1-90) - retention-days: 30 - # 如果文件不存在是否报错(false表示不报错) - if-no-files-found: warn - - # 方式二:创建 GitHub Release 并上传(适合正式版本) - - name: Create Release and Upload Assets - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - uses: softprops/action-gh-release@v1 - with: - files: | - target/*.jar - build/libs/*.jar - dist/* - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - # 5. 可选:部署到 GitHub Pages(如果是静态网站) - - name: Deploy to GitHub Pages - if: false # 需要时改为 true - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./dist From d0771ca94b6ecba81fe30fbcc70866114dbf76fb Mon Sep 17 00:00:00 2001 From: YWYdog Date: Tue, 26 May 2026 06:27:13 +0800 Subject: [PATCH 14/19] =?UTF-8?q?=E5=88=A0=E9=99=A4=20b.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/b.yml | 357 ---------------------------------------- 1 file changed, 357 deletions(-) delete mode 100644 .github/workflows/b.yml diff --git a/.github/workflows/b.yml b/.github/workflows/b.yml deleted file mode 100644 index 2ebab46..0000000 --- a/.github/workflows/b.yml +++ /dev/null @@ -1,357 +0,0 @@ -name: Release - -on: - push: - tags: - - '*' - - workflow_dispatch: - inputs: - version_type: - description: '版本类型' - required: true - default: 'none' - type: choice - options: - - none - - patch - - minor - - major - - build - prerelease: - description: '是否为预发布' - required: true - default: true - type: boolean - draft: - description: '是否创建草稿 Release' - required: true - default: false - type: boolean - create_release: - description: '是否创建 GitHub Release' - required: true - default: true - type: boolean - -concurrency: - group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}-${{ github.sha }} - cancel-in-progress: true - -env: - PLUGIN_NAME: SystemTools - -jobs: - prepare: - runs-on: ubuntu-latest - outputs: - tag_name: ${{ steps.get_tag.outputs.tag_name }} - version: ${{ steps.get_tag.outputs.version }} - is_prerelease: ${{ steps.release_type.outputs.is_prerelease }} - is_draft: ${{ steps.release_type.outputs.is_draft }} - changelog: ${{ steps.decode_changelog.outputs.changelog }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Get version from csproj - id: get_version - shell: bash - run: | - CSPROJ=$(find . -name "*.csproj" -type f | head -1) - if [ -z "$CSPROJ" ]; then - echo "❌ 未找到 .csproj 文件" - exit 1 - fi - echo "📄 找到项目文件: $CSPROJ" - - VERSION=$(grep -oP '\K[^<]+' "$CSPROJ" || echo "") - if [ -z "$VERSION" ]; then - VERSION="1.0.0.0" - fi - - PARTS=$(echo "$VERSION" | tr '.' '\n' | wc -l) - while [ "$PARTS" -lt 4 ]; do - VERSION="${VERSION}.0" - PARTS=$((PARTS + 1)) - done - - echo "🔖 csproj 版本: $VERSION" - echo "current_version=$VERSION" >> $GITHUB_OUTPUT - - - name: Calculate new version - id: get_tag - shell: bash - run: | - if [ "${{ github.event_name }}" = "push" ]; then - TAG_NAME="${GITHUB_REF#refs/tags/}" - echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT - echo "version=$TAG_NAME" >> $GITHUB_OUTPUT - echo "使用推送的标签: $TAG_NAME" - else - CURRENT="${{ steps.get_version.outputs.current_version }}" - VERSION_TYPE="${{ github.event.inputs.version_type }}" - - if [ "$VERSION_TYPE" = "none" ]; then - NEW_VERSION="$CURRENT" - echo "使用 csproj 版本(无修改): $NEW_VERSION" - else - MAJOR=$(echo "$CURRENT" | cut -d. -f1) - MINOR=$(echo "$CURRENT" | cut -d. -f2) - PATCH=$(echo "$CURRENT" | cut -d. -f3) - BUILD=$(echo "$CURRENT" | cut -d. -f4) - - case "$VERSION_TYPE" in - major) - MAJOR=$((MAJOR + 1)) - MINOR=0 - PATCH=0 - BUILD=0 - ;; - minor) - MINOR=$((MINOR + 1)) - PATCH=0 - BUILD=0 - ;; - patch) - PATCH=$((PATCH + 1)) - BUILD=0 - ;; - build) - BUILD=$((BUILD + 1)) - ;; - esac - - NEW_VERSION="$MAJOR.$MINOR.$PATCH.$BUILD" - fi - - echo "tag_name=$NEW_VERSION" >> $GITHUB_OUTPUT - echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT - echo "最终版本: $NEW_VERSION" - fi - - - name: Determine release type - id: release_type - shell: bash - run: | - if [ "${{ github.event_name }}" = "push" ]; then - VERSION="${{ steps.get_tag.outputs.version }}" - BUILD=$(echo "$VERSION" | cut -d. -f4) - if [ "$BUILD" = "0" ]; then - echo "is_prerelease=false" >> $GITHUB_OUTPUT - else - echo "is_prerelease=true" >> $GITHUB_OUTPUT - fi - echo "is_draft=false" >> $GITHUB_OUTPUT - else - echo "is_prerelease=${{ github.event.inputs.prerelease }}" >> $GITHUB_OUTPUT - echo "is_draft=${{ github.event.inputs.draft }}" >> $GITHUB_OUTPUT - fi - - - name: Read changelog - id: read_changelog - shell: bash - run: | - VERSION="${{ steps.get_tag.outputs.version }}" - CHANGELOG_PATH="./changelog/${VERSION}.md" - - if [ -f "$CHANGELOG_PATH" ]; then - # 使用手动编写的变更日志 - CONTENT=$(cat "$CHANGELOG_PATH") - echo "✅ 使用手动变更日志: $CHANGELOG_PATH" - else - # 自动生成变更日志 - echo "⚠️ 未找到 $CHANGELOG_PATH,自动生成" - - LAST_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || git rev-list --max-parents=0 HEAD) - COMMITS=$(git log "$LAST_TAG..HEAD" --pretty=format:"- %s (%h)" --no-merges 2>/dev/null || echo "- 版本更新") - - # 修复:简化格式,只有一个 "## 变更内容",删除重复的提示 - CONTENT=$(printf "%s\n\n*日志由action自动生成*" "$COMMITS") - fi - - CONTENT_B64=$(echo "$CONTENT" | base64 -w 0) - echo "changelog_b64=$CONTENT_B64" >> $GITHUB_OUTPUT - - - name: Decode changelog - id: decode_changelog - shell: bash - run: | - echo "${{ steps.read_changelog.outputs.changelog_b64 }}" | base64 -d > changelog_output.txt - echo "changelog<> $GITHUB_OUTPUT - cat changelog_output.txt >> $GITHUB_OUTPUT - echo "CHANGELOG_EOF" >> $GITHUB_OUTPUT - - build: - needs: prepare - runs-on: windows-2022 - outputs: - cipx_size: ${{ steps.calculate_size.outputs.cipx_size }} - cipx_md5: ${{ steps.calculate_size.outputs.cipx_md5 }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup .NET 8 - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.0.x' - - - name: Build and Package - id: build_package - shell: pwsh - run: | - $pluginName = "${{ env.PLUGIN_NAME }}" - $version = "${{ needs.prepare.outputs.version }}" - - $csproj = Get-ChildItem -Path . -Filter "*.csproj" -Recurse | Select-Object -First 1 - if (-not $csproj) { throw "未找到 .csproj 文件" } - - $OutputPath = "./output" - if (Test-Path $OutputPath) { Remove-Item $OutputPath -Recurse -Force } - New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null - - dotnet restore $csproj.FullName - dotnet build $csproj.FullName -c Release --no-restore - - $PublishPath = "$OutputPath/publish" - dotnet publish $csproj.FullName -c Release --no-build -o $PublishPath - - $filesToPack = @( - "$PublishPath/$pluginName.pdb", - "$PublishPath/$pluginName.dll", - "$PublishPath/$pluginName.deps.json", - "$PublishPath/Lyricify Lite - README.md", - "$PublishPath/README-1.md", - "$PublishPath/README-2.md", - "$PublishPath/jinyongshubiao.ps1", - "$PublishPath/jinyongshubiao.bat", - "$PublishPath/black.html", - "$PublishPath/huifu.bat", - "$PublishPath/huifu.ps1", - "$PublishPath/icon-1.png", - "$PublishPath/README.md", - "$PublishPath/manifest.yml", - "$PublishPath/icon.png", - "$PublishPath/version.json" - ) - - # 移除重复项并过滤不存在的文件 - $filesToPack = $filesToPack | Get-Unique | Where-Object { Test-Path $_ } - - Write-Host "找到的文件:" - foreach ($file in $filesToPack) { - Write-Host " - $file" - } - - $PackTemp = "$OutputPath/pack_temp" - New-Item -ItemType Directory -Path $PackTemp -Force | Out-Null - - foreach ($file in $filesToPack) { - if (Test-Path $file) { - Write-Host "复制文件: $file" - Copy-Item $file $PackTemp/ - } else { - Write-Host "警告: 文件不存在 - $file" - } - } - - # 检查是否复制了文件 - if (-not (Get-ChildItem -Path $PackTemp -ErrorAction SilentlyContinue)) { - throw "没有文件被复制到打包目录!" - } - - $cipxName = "$pluginName.$version.cipx" - Compress-Archive -Path "$PackTemp/*" -DestinationPath "$OutputPath/$pluginName.zip" -Force - Move-Item "$OutputPath/$pluginName.zip" "$OutputPath/$cipxName" -Force - - Remove-Item $PublishPath, $PackTemp -Recurse -Force - - echo "cipx_name=$cipxName" >> $env:GITHUB_OUTPUT - - - name: Calculate size and MD5 - id: calculate_size - shell: pwsh - run: | - $cipxName = "${{ steps.build_package.outputs.cipx_name }}" - $cipxPath = "./output/$cipxName" - - $size = (Get-Item $cipxPath).Length - $md5 = (Get-FileHash $cipxPath -Algorithm MD5).Hash - - echo "cipx_size=$size" >> $env:GITHUB_OUTPUT - echo "cipx_md5=$md5" >> $env:GITHUB_OUTPUT - - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: ${{ env.PLUGIN_NAME }}-${{ needs.prepare.outputs.version }} - path: ./output/*.cipx - - release: - needs: [prepare, build] - runs-on: ubuntu-latest - # 只有在 push 事件时,或者手动触发且选择了创建 release 时才执行 - if: github.event_name == 'push' || github.event.inputs.create_release == 'true' - permissions: - contents: write - - steps: - - name: Download Artifact - uses: actions/download-artifact@v4 - with: - name: ${{ env.PLUGIN_NAME }}-${{ needs.prepare.outputs.version }} - - - name: Create Release Body - id: create_body - shell: bash - run: | - VERSION="${{ needs.prepare.outputs.version }}" - SIZE="${{ needs.build.outputs.cipx_size }}" - MD5="${{ needs.build.outputs.cipx_md5 }}" - - if [ "$SIZE" -lt 1024 ]; then - SIZE_STR="${SIZE} B" - elif [ "$SIZE" -lt 1048576 ]; then - SIZE_STR=$(awk "BEGIN {printf \"%.2f KB\", $SIZE/1024}") - else - SIZE_STR=$(awk "BEGIN {printf \"%.2f MB\", $SIZE/1048576}") - fi - - CHANGELOG="${{ needs.prepare.outputs.changelog }}" - - # 修复:只在手动生成时添加 "## 变更内容" 标题 - { - echo "## 变更内容" - echo "" - echo "$CHANGELOG" - echo "" - echo "## 文件信息" - echo "" - echo "| 文件名 | 大小 | MD5 |" - echo "|--------|------|-----|" - echo "| ${{ env.PLUGIN_NAME }}.${VERSION}.cipx | ${SIZE_STR} | \`${MD5}\` |" - echo "" - echo "" - } > release_body.txt - - echo "body<> $GITHUB_OUTPUT - cat release_body.txt >> $GITHUB_OUTPUT - echo "BODY_EOF" >> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ needs.prepare.outputs.tag_name }} - name: ${{ env.PLUGIN_NAME }} ${{ needs.prepare.outputs.version }} - body: ${{ steps.create_body.outputs.body }} - draft: ${{ needs.prepare.outputs.is_draft }} - prerelease: ${{ needs.prepare.outputs.is_prerelease == 'true' }} - files: '*.cipx' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From f9e0ed8538f40d682f57052707202f81e832965d Mon Sep 17 00:00:00 2001 From: YWYdog Date: Tue, 26 May 2026 12:57:09 +0800 Subject: [PATCH 15/19] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=A1=8C=E5=8A=A8?= =?UTF-8?q?=EF=BC=8C=E5=BC=80=E5=85=B3=E8=87=AA=E5=8A=A8=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 可以选择当前方案的自动化,判断当前状态,可以启用,关闭 支持根据当前状态进行切换 --- Actions/ToggleWorkflowAction.cs | 202 +++++++++++++++ Controls/ToggleWorkflowSettingsControl.cs | 257 +++++++++++++++++++ Plugin.cs | 8 +- Settings/ToggleWorkflowSettings.cs | 33 +++ SettingsPage/SystemToolsSettingsViewModel.cs | 3 +- 5 files changed, 500 insertions(+), 3 deletions(-) create mode 100644 Actions/ToggleWorkflowAction.cs create mode 100644 Controls/ToggleWorkflowSettingsControl.cs create mode 100644 Settings/ToggleWorkflowSettings.cs diff --git a/Actions/ToggleWorkflowAction.cs b/Actions/ToggleWorkflowAction.cs new file mode 100644 index 0000000..6a6f6b7 --- /dev/null +++ b/Actions/ToggleWorkflowAction.cs @@ -0,0 +1,202 @@ +using ClassIsland.Core; +using ClassIsland.Core.Abstractions.Automation; +using ClassIsland.Core.Abstractions.Services; +using ClassIsland.Core.Attributes; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; +using SystemTools.Settings; +using ClassIsland.Shared; +using Workflow = ClassIsland.Core.Models.Automation.Workflow; + +namespace SystemTools.Actions; + +/// +/// 切换自动化启用状态的行动 +/// +[ActionInfo("SystemTools.ToggleWorkflow", "开关自动化", "\uE9A8", true)] +public class ToggleWorkflowAction : ActionBase +{ + private static readonly ConcurrentDictionary OriginalStates = new(); + + /// + /// 原始状态快照 + /// + private readonly record struct OriginalStateSnapshot( + string WorkflowName, + int WorkflowIndex, + bool IsEnabled); + + protected override async Task OnInvoke() + { + if (Settings == null) + { + return; + } + + try + { + var automationService = IAppHost.TryGetService(); + if (automationService?.Workflows == null) + { + throw new InvalidOperationException("无法获取自动化服务,请确保 ClassIsland 已正确加载。"); + } + + var targetWorkflow = FindTargetWorkflow(automationService); + if (targetWorkflow == null) + { + throw new InvalidOperationException($"未找到指定的自动化方案: {Settings.TargetWorkflowName}"); + } + + var actionSet = targetWorkflow.ActionSet; + var currentStatus = actionSet.IsEnabled; + + // 如果启用了恢复功能,保存原始状态 + if (IsRevertable && Settings.RevertToOriginal) + { + var snapshot = new OriginalStateSnapshot( + actionSet.Name, + automationService.Workflows.IndexOf(targetWorkflow), + currentStatus); + + OriginalStates[ActionSet.Guid] = snapshot; + } + + // 确定目标状态 + bool targetStatus; + string operationDescription; + + switch (Settings.EnableMode) + { + case true: + targetStatus = true; + operationDescription = "启用"; + break; + case false: + targetStatus = false; + operationDescription = "禁用"; + break; + default: + targetStatus = !currentStatus; + operationDescription = targetStatus ? "启用" : "禁用"; + break; + } + + // 执行状态切换 + if (currentStatus != targetStatus) + { + actionSet.IsEnabled = targetStatus; + automationService.SaveConfig($"通过行动{operationDescription}自动化 \"{actionSet.Name}\""); + } + } + catch (Exception) + { + throw; + } + + await base.OnInvoke(); + } + + protected override async Task OnRevert() + { + if (Settings == null) + { + await base.OnRevert(); + return; + } + + // 检查是否启用了自动恢复 + if (!Settings.RevertToOriginal) + { + await base.OnRevert(); + return; + } + + try + { + var automationService = IAppHost.TryGetService(); + if (automationService?.Workflows == null) + { + throw new InvalidOperationException("无法获取自动化服务。"); + } + + // 尝试获取原始状态 + if (!OriginalStates.TryRemove(ActionSet.Guid, out var snapshot)) + { + await base.OnRevert(); + return; + } + + // 查找目标自动化(优先使用索引,回退到名称) + Workflow? targetWorkflow = null; + + if (snapshot.WorkflowIndex >= 0 && snapshot.WorkflowIndex < automationService.Workflows.Count) + { + var workflowByIndex = automationService.Workflows[snapshot.WorkflowIndex]; + if (workflowByIndex.ActionSet.Name == snapshot.WorkflowName) + { + targetWorkflow = workflowByIndex; + } + } + + if (targetWorkflow == null) + { + targetWorkflow = automationService.Workflows + .FirstOrDefault(w => w.ActionSet.Name == snapshot.WorkflowName); + } + + if (targetWorkflow == null) + { + await base.OnRevert(); + return; + } + + var actionSet = targetWorkflow.ActionSet; + var currentStatus = actionSet.IsEnabled; + var originalStatus = snapshot.IsEnabled; + + if (currentStatus != originalStatus) + { + actionSet.IsEnabled = originalStatus; + automationService.SaveConfig($"通过行动恢复自动化 \"{actionSet.Name}\" 到原始状态({originalStatus})"); + } + } + catch (Exception) + { + throw; + } + + await base.OnRevert(); + } + + /// + /// 查找目标自动化 + /// + private Workflow? FindTargetWorkflow(IAutomationService automationService) + { + Workflow? targetWorkflow = null; + + // 1. 尝试通过索引查找 + if (Settings.TargetWorkflowIndex >= 0 && Settings.TargetWorkflowIndex < automationService.Workflows.Count) + { + targetWorkflow = automationService.Workflows[Settings.TargetWorkflowIndex]; + } + + // 2. 如果索引找不到,尝试通过名称查找 + if (targetWorkflow == null && !string.IsNullOrEmpty(Settings.TargetWorkflowName)) + { + targetWorkflow = automationService.Workflows + .FirstOrDefault(w => w.ActionSet.Name == Settings.TargetWorkflowName); + + if (targetWorkflow != null) + { + // 更新索引以便下次使用 + Settings.TargetWorkflowIndex = automationService.Workflows.IndexOf(targetWorkflow); + } + } + + return targetWorkflow; + } +} diff --git a/Controls/ToggleWorkflowSettingsControl.cs b/Controls/ToggleWorkflowSettingsControl.cs new file mode 100644 index 0000000..74e6ca6 --- /dev/null +++ b/Controls/ToggleWorkflowSettingsControl.cs @@ -0,0 +1,257 @@ +using Avalonia.Controls; +using Avalonia.Layout; +using Avalonia.Media; +using ClassIsland.Core; +using ClassIsland.Core.Abstractions.Controls; +using ClassIsland.Core.Abstractions.Services; +using System; +using System.Collections.ObjectModel; +using System.Linq; +using SystemTools.Settings; +using ClassIsland.Shared; +using Workflow = ClassIsland.Core.Models.Automation.Workflow; + +namespace SystemTools.Controls; + +/// +/// 切换自动化启用状态的设置控件 +/// +public class ToggleWorkflowSettingsControl : ActionSettingsControlBase +{ + private ComboBox _workflowComboBox; + private ComboBox _modeComboBox; + private CheckBox _revertCheckBox; + private ObservableCollection _workflows = []; + private TextBlock _infoTextBlock; + + public ToggleWorkflowSettingsControl() + { + var panel = new StackPanel { Spacing = 10, Margin = new(10) }; + + // 标题 + panel.Children.Add(new TextBlock + { + Text = "切换自动化状态", + FontWeight = FontWeight.Bold, + FontSize = 14 + }); + + // 说明文字 + panel.Children.Add(new TextBlock + { + Text = "选择一个自动化方案并设置要执行的操作。当触发器支持恢复时,可以自动还原状态。", + TextWrapping = TextWrapping.Wrap, + Opacity = 0.8 + }); + + // 自动化选择 + panel.Children.Add(new TextBlock + { + Text = "目标自动化:", + Margin = new(0, 10, 0, 0) + }); + + _workflowComboBox = new ComboBox + { + PlaceholderText = "请选择自动化方案", + HorizontalAlignment = HorizontalAlignment.Stretch + }; + panel.Children.Add(_workflowComboBox); + + // 操作模式选择 + panel.Children.Add(new TextBlock + { + Text = "操作模式:", + Margin = new(0, 10, 0, 0) + }); + + _modeComboBox = new ComboBox + { + HorizontalAlignment = HorizontalAlignment.Stretch + }; + _modeComboBox.Items.Add(new ComboBoxItem { Content = "切换(启用↔禁用)", Tag = null }); + _modeComboBox.Items.Add(new ComboBoxItem { Content = "启用", Tag = true }); + _modeComboBox.Items.Add(new ComboBoxItem { Content = "禁用", Tag = false }); + _modeComboBox.SelectedIndex = 0; + panel.Children.Add(_modeComboBox); + + // 恢复选项 + _revertCheckBox = new CheckBox + { + Content = "触发器恢复时自动还原原状态", + IsChecked = true, + Margin = new(0, 10, 0, 0) + }; + panel.Children.Add(_revertCheckBox); + + // 恢复说明 + panel.Children.Add(new TextBlock + { + Text = "提示:当触发器支持恢复(如\"上课时\"在放学时恢复),勾选此项会自动将自动化恢复到触发前的状态。", + TextWrapping = TextWrapping.Wrap, + FontSize = 12, + Opacity = 0.7, + Margin = new(0, 0, 0, 0) + }); + + // 信息提示区域 + _infoTextBlock = new TextBlock + { + TextWrapping = TextWrapping.Wrap, + Margin = new(0, 10, 0, 0), + IsVisible = false + }; + panel.Children.Add(_infoTextBlock); + + // 刷新按钮 + var refreshButton = new Button + { + Content = "刷新自动化列表", + HorizontalAlignment = HorizontalAlignment.Left, + Margin = new(0, 10, 0, 0) + }; + refreshButton.Click += (_, _) => LoadWorkflows(); + panel.Children.Add(refreshButton); + + Content = panel; + + // 加载自动化列表 + LoadWorkflows(); + } + + protected override void OnInitialized() + { + base.OnInitialized(); + + // 绑定选择变更事件 + _workflowComboBox.SelectionChanged += OnWorkflowSelectionChanged; + _modeComboBox.SelectionChanged += OnModeSelectionChanged; + _revertCheckBox.IsCheckedChanged += OnRevertCheckBoxChanged; + + // 恢复设置值 + RestoreSettings(); + } + + private void LoadWorkflows() + { + try + { + var automationService = IAppHost.TryGetService(); + if (automationService?.Workflows == null) + { + _infoTextBlock.Text = "无法获取自动化服务,请确保 ClassIsland 已正确加载。"; + _infoTextBlock.Foreground = Brushes.Orange; + _infoTextBlock.IsVisible = true; + return; + } + + _workflows = automationService.Workflows; + _workflowComboBox.Items.Clear(); + + foreach (var workflow in _workflows) + { + var actionSet = workflow.ActionSet; + var statusText = actionSet.IsEnabled ? "[已启用]" : "[已禁用]"; + var item = new ComboBoxItem + { + Content = $"{actionSet.Name} {statusText}", + Tag = workflow + }; + _workflowComboBox.Items.Add(item); + } + + if (_workflowComboBox.Items.Count == 0) + { + _workflowComboBox.PlaceholderText = "暂无自动化方案"; + _infoTextBlock.Text = "当前配置文件中没有任何自动化方案,请先创建自动化。"; + _infoTextBlock.Foreground = Brushes.Gray; + _infoTextBlock.IsVisible = true; + } + else + { + _infoTextBlock.IsVisible = false; + } + + // 恢复之前的选择 + RestoreSettings(); + } + catch (Exception ex) + { + _infoTextBlock.Text = $"加载自动化列表失败: {ex.Message}"; + _infoTextBlock.Foreground = Brushes.Red; + _infoTextBlock.IsVisible = true; + } + } + + private void RestoreSettings() + { + if (Settings == null) return; + + // 恢复自动化选择 + if (Settings.TargetWorkflowIndex >= 0 && Settings.TargetWorkflowIndex < _workflowComboBox.Items.Count) + { + _workflowComboBox.SelectedIndex = Settings.TargetWorkflowIndex; + } + else if (!string.IsNullOrEmpty(Settings.TargetWorkflowName)) + { + // 尝试通过名称查找 + for (int i = 0; i < _workflowComboBox.Items.Count; i++) + { + if (_workflowComboBox.Items[i] is ComboBoxItem item && + item.Tag is Workflow workflow && + workflow.ActionSet.Name == Settings.TargetWorkflowName) + { + _workflowComboBox.SelectedIndex = i; + break; + } + } + } + + // 恢复操作模式 + var modeIndex = Settings.EnableMode switch + { + null => 0, // 切换 + true => 1, // 启用 + false => 2 // 禁用 + }; + _modeComboBox.SelectedIndex = modeIndex; + + // 恢复复选框 + _revertCheckBox.IsChecked = Settings.RevertToOriginal; + } + + private void OnWorkflowSelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (_workflowComboBox.SelectedItem is ComboBoxItem item && item.Tag is Workflow workflow) + { + Settings.TargetWorkflowName = workflow.ActionSet.Name; + Settings.TargetWorkflowIndex = _workflowComboBox.SelectedIndex; + + // 更新信息显示 + var status = workflow.ActionSet.IsEnabled ? "已启用" : "已禁用"; + _infoTextBlock.Text = $"当前状态: {status} | 行动组: {workflow.ActionSet.Name}"; + _infoTextBlock.Foreground = workflow.ActionSet.IsEnabled ? Brushes.Green : Brushes.Gray; + _infoTextBlock.IsVisible = true; + } + } + + private void OnModeSelectionChanged(object? sender, SelectionChangedEventArgs e) + { + if (_modeComboBox.SelectedItem is ComboBoxItem item && item.Tag is bool mode) + { + Settings.EnableMode = mode; + } + else + { + Settings.EnableMode = null; // 切换模式 + } + } + + private void OnRevertCheckBoxChanged(object? sender, EventArgs e) + { + if (_revertCheckBox.IsChecked.HasValue) + { + Settings.RevertToOriginal = _revertCheckBox.IsChecked.Value; + } + } +} \ No newline at end of file diff --git a/Plugin.cs b/Plugin.cs index 58ac528..e0bd5b5 100644 --- a/Plugin.cs +++ b/Plugin.cs @@ -1,4 +1,4 @@ -using Avalonia.Controls; +using Avalonia.Controls; using Avalonia.Threading; using AvaloniaEdit.Utils; using ClassIsland.Core; @@ -297,6 +297,8 @@ private void RegisterBaseActions(IServiceCollection services) RegisterActionIfEnabled(services, config, "SystemTools.OpenAppSettings"); RegisterActionIfEnabled(services, config, "SystemTools.OpenProfileEditor"); RegisterActionIfEnabled(services, config, "SystemTools.OpenClassSwapWindow"); + RegisterActionIfEnabled(services, config, + "SystemTools.ToggleWorkflow"); } private void RegisterBaseTriggers(IServiceCollection services) @@ -513,7 +515,7 @@ private void BuildBaseActionTree() if (HasAnyActionEnabled(config, "SystemTools.ClearAllNotifications", "SystemTools.RestartAsAdmin", "SystemTools.LoadTemporaryClassPlan", "SystemTools.OpenAppSettings", - "SystemTools.OpenProfileEditor", "SystemTools.OpenClassSwapWindow")) + "SystemTools.OpenProfileEditor", "SystemTools.OpenClassSwapWindow","SystemTools.ToggleWorkflow")) { IActionService.ActionMenuTree["SystemTools 行动"].Add(new ActionMenuTreeGroup("ClassIsland…", "\uE5CB")); BuildClassIslandMenu(config); @@ -851,6 +853,8 @@ private void BuildClassIslandMenu(MainConfigData config) items.Add(new ActionMenuTreeItem("SystemTools.OpenProfileEditor", "打开档案编辑", "\uE699")); if (config.IsActionEnabled("SystemTools.OpenClassSwapWindow")) items.Add(new ActionMenuTreeItem("SystemTools.OpenClassSwapWindow", "打开换课窗口", "\uE13B")); + if (config.IsActionEnabled("SystemTools.ToggleWorkflow")) + items.Add(new ActionMenuTreeItem("SystemTools.ToggleWorkflow", "开关自动化", "\uE9A8")); if (items.Count > 0) { diff --git a/Settings/ToggleWorkflowSettings.cs b/Settings/ToggleWorkflowSettings.cs new file mode 100644 index 0000000..41c55eb --- /dev/null +++ b/Settings/ToggleWorkflowSettings.cs @@ -0,0 +1,33 @@ +using System.Text.Json.Serialization; + +namespace SystemTools.Settings; + +/// +/// 切换自动化启用状态的设置 +/// +public class ToggleWorkflowSettings +{ + /// + /// 目标自动化的名称(用于显示) + /// + [JsonPropertyName("targetWorkflowName")] + public string TargetWorkflowName { get; set; } = string.Empty; + + /// + /// 目标自动化在列表中的索引 + /// + [JsonPropertyName("targetWorkflowIndex")] + public int TargetWorkflowIndex { get; set; } = -1; + + /// + /// 操作模式:true=启用, false=禁用, null=切换 + /// + [JsonPropertyName("enableMode")] + public bool? EnableMode { get; set; } = null; + + /// + /// 是否在触发器恢复时自动恢复原状态 + /// + [JsonPropertyName("revertToOriginal")] + public bool RevertToOriginal { get; set; } = true; +} diff --git a/SettingsPage/SystemToolsSettingsViewModel.cs b/SettingsPage/SystemToolsSettingsViewModel.cs index 8d480a7..b39978b 100644 --- a/SettingsPage/SystemToolsSettingsViewModel.cs +++ b/SettingsPage/SystemToolsSettingsViewModel.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using System.Net.Http; using System.Security.Cryptography; using Avalonia.Threading; @@ -212,6 +212,7 @@ public void InitializeFeatureItems() ("SystemTools.OpenAppSettings", "打开应用设置", "ClassIsland"), ("SystemTools.OpenProfileEditor", "打开档案编辑", "ClassIsland"), ("SystemTools.OpenClassSwapWindow", "打开换课窗口", "ClassIsland"), + ("SystemTools.ToggleWorkflow", "开关自动化", "ClassIsland"), }; if (Settings.EnableFloatingWindowFeature) From 412fb4e84bb19aee01f51890c2ca8d3eb1b39ffe Mon Sep 17 00:00:00 2001 From: YWYdog Date: Thu, 28 May 2026 06:39:01 +0800 Subject: [PATCH 16/19] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20dotnet-build.yml,?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=89=8B=E5=8A=A8=E8=A7=A6=E5=8F=91=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/dotnet-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/dotnet-build.yml b/.github/workflows/dotnet-build.yml index 2d774e9..6a8648f 100644 --- a/.github/workflows/dotnet-build.yml +++ b/.github/workflows/dotnet-build.yml @@ -1,6 +1,7 @@ name: Build Project on: + workflow_dispatch: push: branches: [ main ] pull_request: From 39a8093baa855448a4d01cc898f23b44bb6d4144 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Thu, 28 May 2026 18:46:00 +0800 Subject: [PATCH 17/19] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Actions/ToggleWorkflowAction.cs | 171 ++++++++++++++------------------ 1 file changed, 73 insertions(+), 98 deletions(-) diff --git a/Actions/ToggleWorkflowAction.cs b/Actions/ToggleWorkflowAction.cs index 6a6f6b7..3dac4fb 100644 --- a/Actions/ToggleWorkflowAction.cs +++ b/Actions/ToggleWorkflowAction.cs @@ -1,179 +1,137 @@ -using ClassIsland.Core; using ClassIsland.Core.Abstractions.Automation; using ClassIsland.Core.Abstractions.Services; using ClassIsland.Core.Attributes; +using ClassIsland.Shared; using Microsoft.Extensions.Logging; using System; using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; using SystemTools.Settings; -using ClassIsland.Shared; using Workflow = ClassIsland.Core.Models.Automation.Workflow; namespace SystemTools.Actions; -/// -/// 切换自动化启用状态的行动 -/// -[ActionInfo("SystemTools.ToggleWorkflow", "开关自动化", "\uE9A8", true)] -public class ToggleWorkflowAction : ActionBase +[ActionInfo("SystemTools.ToggleWorkflow", "开关自动化", "\uE9A8", false)] +public class ToggleWorkflowAction(ILogger logger) : ActionBase { - private static readonly ConcurrentDictionary OriginalStates = new(); + private readonly ILogger _logger = logger; - /// - /// 原始状态快照 - /// - private readonly record struct OriginalStateSnapshot( - string WorkflowName, - int WorkflowIndex, - bool IsEnabled); + private static readonly ConcurrentDictionary PreviousSnapshots = new(); protected override async Task OnInvoke() { - if (Settings == null) - { - return; - } - try { + _logger.LogDebug("ToggleWorkflowAction OnInvoke 开始"); + + if (Settings == null) + { + _logger.LogWarning("设置为空,无法执行"); + return; + } + var automationService = IAppHost.TryGetService(); if (automationService?.Workflows == null) { + _logger.LogError("无法获取自动化服务"); throw new InvalidOperationException("无法获取自动化服务,请确保 ClassIsland 已正确加载。"); } var targetWorkflow = FindTargetWorkflow(automationService); if (targetWorkflow == null) { + _logger.LogWarning("未找到目标自动化: Index={Index}, Name={Name}", + Settings.TargetWorkflowIndex, Settings.TargetWorkflowName); throw new InvalidOperationException($"未找到指定的自动化方案: {Settings.TargetWorkflowName}"); } var actionSet = targetWorkflow.ActionSet; var currentStatus = actionSet.IsEnabled; - // 如果启用了恢复功能,保存原始状态 - if (IsRevertable && Settings.RevertToOriginal) + if (IsRevertable) { - var snapshot = new OriginalStateSnapshot( + PreviousSnapshots[ActionSet.Guid] = new OriginalStateSnapshot( actionSet.Name, automationService.Workflows.IndexOf(targetWorkflow), currentStatus); - - OriginalStates[ActionSet.Guid] = snapshot; + _logger.LogDebug("已保存自动化 \"{WorkflowName}\" 的原始状态快照", actionSet.Name); } - // 确定目标状态 - bool targetStatus; - string operationDescription; + var (targetStatus, operationDescription) = Settings.EnableMode switch + { + true => (true, "启用"), + false => (false, "禁用"), + _ => (!currentStatus, !currentStatus ? "启用" : "禁用") + }; - switch (Settings.EnableMode) + if (currentStatus == targetStatus) { - case true: - targetStatus = true; - operationDescription = "启用"; - break; - case false: - targetStatus = false; - operationDescription = "禁用"; - break; - default: - targetStatus = !currentStatus; - operationDescription = targetStatus ? "启用" : "禁用"; - break; + _logger.LogInformation("自动化 \"{WorkflowName}\" 已经是{Operation}状态,无需操作", + actionSet.Name, operationDescription); } - - // 执行状态切换 - if (currentStatus != targetStatus) + else { + _logger.LogInformation("正在{Operation}自动化 \"{WorkflowName}\" (原始: {OriginalStatus} -> 目标: {TargetStatus})", + operationDescription, actionSet.Name, currentStatus, targetStatus); + actionSet.IsEnabled = targetStatus; automationService.SaveConfig($"通过行动{operationDescription}自动化 \"{actionSet.Name}\""); + + _logger.LogInformation("自动化 \"{WorkflowName}\" 已成功{Operation}", + actionSet.Name, operationDescription); } + + await base.OnInvoke(); + _logger.LogDebug("ToggleWorkflowAction OnInvoke 完成"); } - catch (Exception) + catch (Exception ex) { + _logger.LogError(ex, "ToggleWorkflowAction 执行失败"); throw; } - - await base.OnInvoke(); } protected override async Task OnRevert() { - if (Settings == null) - { - await base.OnRevert(); - return; - } - - // 检查是否启用了自动恢复 - if (!Settings.RevertToOriginal) - { - await base.OnRevert(); - return; - } - try { - var automationService = IAppHost.TryGetService(); - if (automationService?.Workflows == null) - { - throw new InvalidOperationException("无法获取自动化服务。"); - } + await base.OnRevert(); - // 尝试获取原始状态 - if (!OriginalStates.TryRemove(ActionSet.Guid, out var snapshot)) + if (!PreviousSnapshots.TryRemove(ActionSet.Guid, out var snapshot)) { - await base.OnRevert(); + _logger.LogWarning("未找到触发前状态,跳过恢复。ActionSet={ActionSetGuid}", ActionSet.Guid); return; } - // 查找目标自动化(优先使用索引,回退到名称) - Workflow? targetWorkflow = null; - - if (snapshot.WorkflowIndex >= 0 && snapshot.WorkflowIndex < automationService.Workflows.Count) - { - var workflowByIndex = automationService.Workflows[snapshot.WorkflowIndex]; - if (workflowByIndex.ActionSet.Name == snapshot.WorkflowName) - { - targetWorkflow = workflowByIndex; - } - } - - if (targetWorkflow == null) + var automationService = IAppHost.TryGetService(); + if (automationService?.Workflows == null) { - targetWorkflow = automationService.Workflows - .FirstOrDefault(w => w.ActionSet.Name == snapshot.WorkflowName); + _logger.LogError("无法获取自动化服务,恢复失败"); + return; } + var targetWorkflow = FindTargetWorkflow(automationService); if (targetWorkflow == null) { - await base.OnRevert(); + _logger.LogWarning("恢复时未找到目标自动化: {Name}", snapshot.WorkflowName); return; } var actionSet = targetWorkflow.ActionSet; - var currentStatus = actionSet.IsEnabled; - var originalStatus = snapshot.IsEnabled; + actionSet.IsEnabled = snapshot.IsEnabled; + automationService.SaveConfig($"通过行动恢复自动化 \"{actionSet.Name}\" 到原始状态({snapshot.IsEnabled})"); - if (currentStatus != originalStatus) - { - actionSet.IsEnabled = originalStatus; - automationService.SaveConfig($"通过行动恢复自动化 \"{actionSet.Name}\" 到原始状态({originalStatus})"); - } + _logger.LogInformation("已恢复自动化 \"{WorkflowName}\" 为触发前状态。ActionSet={ActionSetGuid}", + actionSet.Name, ActionSet.Guid); } - catch (Exception) + catch (Exception ex) { + _logger.LogError(ex, "ToggleWorkflowAction 恢复失败"); throw; } - - await base.OnRevert(); } - /// - /// 查找目标自动化 - /// private Workflow? FindTargetWorkflow(IAutomationService automationService) { Workflow? targetWorkflow = null; @@ -182,6 +140,8 @@ protected override async Task OnRevert() if (Settings.TargetWorkflowIndex >= 0 && Settings.TargetWorkflowIndex < automationService.Workflows.Count) { targetWorkflow = automationService.Workflows[Settings.TargetWorkflowIndex]; + _logger.LogDebug("通过索引 {Index} 找到自动化: {Name}", + Settings.TargetWorkflowIndex, targetWorkflow.ActionSet.Name); } // 2. 如果索引找不到,尝试通过名称查找 @@ -192,11 +152,26 @@ protected override async Task OnRevert() if (targetWorkflow != null) { - // 更新索引以便下次使用 + _logger.LogDebug("通过名称 \"{Name}\" 找到自动化", Settings.TargetWorkflowName); Settings.TargetWorkflowIndex = automationService.Workflows.IndexOf(targetWorkflow); } + else + { + _logger.LogWarning("通过名称 \"{Name}\" 未找到自动化", Settings.TargetWorkflowName); + } + } + + if (targetWorkflow == null) + { + _logger.LogWarning("未能找到任何匹配的目标自动化 (Index={Index}, Name={Name})", + Settings.TargetWorkflowIndex, Settings.TargetWorkflowName); } return targetWorkflow; } + + private readonly record struct OriginalStateSnapshot( + string WorkflowName, + int WorkflowIndex, + bool IsEnabled); } From b35441834756d38178e98a2899833e59f0ec4af8 Mon Sep 17 00:00:00 2001 From: YWYdog Date: Thu, 28 May 2026 18:54:01 +0800 Subject: [PATCH 18/19] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/build.ps1 | 2 -- scripts/combine-artifacts.ps1 | 14 -------------- scripts/gen-release-note.ps1 | 25 ------------------------- 3 files changed, 41 deletions(-) delete mode 100644 scripts/build.ps1 delete mode 100644 scripts/combine-artifacts.ps1 delete mode 100644 scripts/gen-release-note.ps1 diff --git a/scripts/build.ps1 b/scripts/build.ps1 deleted file mode 100644 index 3bf3e07..0000000 --- a/scripts/build.ps1 +++ /dev/null @@ -1,2 +0,0 @@ -Write-Host 开始构建 DutyIsland -dotnet publish -p:CreateCipx=true -c Release diff --git a/scripts/combine-artifacts.ps1 b/scripts/combine-artifacts.ps1 deleted file mode 100644 index 5358efa..0000000 --- a/scripts/combine-artifacts.ps1 +++ /dev/null @@ -1,14 +0,0 @@ -$artifacts = Get-ChildItem -Path ./out_artifacts -Directory - -if ($(Test-Path ./out) -eq $false) { - mkdir out -} - -Write-Host "Coping" -ForegroundColor Gray -foreach ($artifact in $artifacts) { - Get-ChildItem -Path ./out_artifacts/$($artifact.Name) - Copy-Item ./out_artifacts/$($artifact.Name)/* -Destination ./out/ -Recurse -Force -} - -Write-Host "Copy Result" -ForegroundColor Gray -Get-ChildItem -Path ./out/ diff --git a/scripts/gen-release-note.ps1 b/scripts/gen-release-note.ps1 deleted file mode 100644 index 375204e..0000000 --- a/scripts/gen-release-note.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -$md5Summary = " -> [!important] -> 下载时请注意核对文件MD5是否正确。 - -| 文件名 | MD5 | -| --- | --- | -" - -$hashes = [ordered]@{} - -$hash = Get-FileHash ./out/DutyIsland.cipx -Algorithm MD5 -$hashString = $hash.Hash -$md5Summary += "| DutyIsland.cipx | ``${hashString}`` |`n" -$hashes.Add("DutyIsland.cipx", $hashString) - -$json = ConvertTo-Json $hashes -Compress - -$md5Summary += "`n" -$changelog = Get-Content "./changelog/${env:tagName}.md" -$fullContent = $changelog + $md5Summary - -Write-Output $fullContent > "release-note.md" - -Write-Host "Release Note" -ForegroundColor Gray -Write-Host $fullContent -ForegroundColor Gray From 3423c557628d0a21c605ae2d94ae7f4aeaa9fdef Mon Sep 17 00:00:00 2001 From: YWYdog Date: Thu, 28 May 2026 18:56:19 +0800 Subject: [PATCH 19/19] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build.yml | 85 ------------------------------------- 1 file changed, 85 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 07742f8..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Build & Publish - -on: - push: - workflow_dispatch: - inputs: - release_tag: - description: '发布标签' - required: true - type: string - -jobs: - build: - runs-on: 'windows-2022' - name: Build Plugin - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: recursive - ref: ${{ github.event_name != 'workflow_dispatch' && github.ref || github.event.inputs.release_tag }} - - - name: List files - run: ls - - - name: Install .NET Core - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 8.0.x - - - name: Build - run: 'pwsh -ep bypass ./scripts/build.ps1' - - - name: Upload Plugin to Artifacts - id: upload-unsigned-artifact - uses: actions/upload-artifact@v4 - with: - name: 'DutyIsland_Plugin' - path: | - ./DutyIsland/cipx/*.cipx - ./DutyIsland/cipx/*.md - - - name: Upload NuPkg to Artifacts - uses: actions/upload-artifact@v4 - with: - name: 'DutyIsland_NuPkg' - path: | - ./**/bin/Release/*.nupkg - - publish: - runs-on: windows-2022 - if: ${{ always() && success('build') && github.event.inputs.release_tag && github.event_name != 'pull_request' }} - needs: [ build ] - concurrency: - group: "publish-public" - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: recursive - ref: ${{ github.event_name != 'workflow_dispatch' && github.ref || github.event.inputs.release_tag }} - - - name: Download Artifacts - uses: actions/download-artifact@v4 - with: - path: ./out_artifacts - - - name: Combine Artifacts - run: pwsh -ep bypass ./scripts/combine-artifacts.ps1 - - - name: Generate Release Note - env: - tagName: ${{ github.event.inputs.release_tag }} - run: 'pwsh -ep bypass ./scripts/gen-release-note.ps1' - - - name: Upload Plugin to release - uses: ncipollo/release-action@v1 - with: - artifacts: "./out/*.cipx,./out/*.md" - draft: true - bodyFile: ./release-note.md - token: ${{ secrets.GITHUB_TOKEN }} - tag: ${{ github.event_name != 'workflow_dispatch' && github.ref || github.event.inputs.release_tag }} \ No newline at end of file