From adbc010ce236f2e2eaf56c9a19edb61d43f8b5a8 Mon Sep 17 00:00:00 2001 From: SlimeSB <86453765+SlimeSB@users.noreply.github.com> Date: Mon, 5 Jan 2026 09:59:36 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E6=96=B0CI=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/packer.yml | 90 ++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index 064ff23b5049..d9687099e388 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -73,6 +73,20 @@ jobs: run: echo "timestamp=$(date '+%Y%m%d%H%M%s')" >> $GITHUB_OUTPUT shell: bash + - name: Determine release type and tag + id: determine_release + run: | + if [ "${{ github.event_name }}" == "schedule" ] || [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + echo "tag_name=autobuild" >> $GITHUB_OUTPUT + echo "release_name=汉化资源包-AutoBuild" >> $GITHUB_OUTPUT + echo "prerelease=true" >> $GITHUB_OUTPUT + else + echo "tag_name=Snapshot-${{ steps.create_timestamp.outputs.timestamp }}" >> $GITHUB_OUTPUT + echo "release_name=汉化资源包-Snapshot-${{ steps.create_timestamp.outputs.timestamp }}" >> $GITHUB_OUTPUT + echo "prerelease=false" >> $GITHUB_OUTPUT + fi + shell: bash + # Create the release: https://github.com/actions/create-release - name: Create release id: create_release @@ -80,13 +94,13 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: Snapshot-${{ steps.create_timestamp.outputs.timestamp }} - release_name: 汉化资源包-Snapshot-${{ steps.create_timestamp.outputs.timestamp }} + tag_name: ${{ steps.determine_release.outputs.tag_name }} + release_name: ${{ steps.determine_release.outputs.release_name }} draft: false - prerelease: false + prerelease: ${{ steps.determine_release.outputs.prerelease }} outputs: upload-url: ${{ steps.create_release.outputs.upload_url }} - tag-name: Snapshot-${{ steps.create_timestamp.outputs.timestamp }} + tag-name: ${{ steps.determine_release.outputs.tag_name }} pack: @@ -226,6 +240,74 @@ jobs: } shell: pwsh + clean-old-assets: + name: Clean Old Release Assets + needs: [upload-release-assets] + if: needs.initialize-release.outputs.tag-name == 'autobuild' + runs-on: ubuntu-latest + steps: + - name: Clean old assets from autobuild release + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // Get the autobuild release + const release = await github.rest.repos.getReleaseByTag({ + owner: context.repo.owner, + repo: context.repo.repo, + tag: 'autobuild' + }); + + // Get all assets + const assets = await github.rest.repos.listReleaseAssets({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release.data.id, + per_page: 100 + }); + + console.log(`Found ${assets.data.length} assets in autobuild release`); + + // Group assets by version + const assetsByVersion = {}; + assets.data.forEach(asset => { + // Extract version from asset name + let version = 'unknown'; + if (asset.name.includes('-1.12.2.')) version = '1.12.2'; + else if (asset.name.includes('-1.16.')) version = '1.16'; + else if (asset.name.includes('-1.16-fabric.')) version = '1.16-fabric'; + else if (asset.name.includes('-1.18.')) version = '1.18'; + else if (asset.name.includes('-1.18-fabric.')) version = '1.18-fabric'; + else if (asset.name.includes('-1.19.')) version = '1.19'; + else if (asset.name.includes('-1.20.')) version = '1.20'; + else if (asset.name.includes('-1.20-fabric.')) version = '1.20-fabric'; + else if (asset.name.includes('-1.21.')) version = '1.21'; + else if (asset.name.includes('-1.21-fabric.')) version = '1.21-fabric'; + + if (!assetsByVersion[version]) { + assetsByVersion[version] = []; + } + assetsByVersion[version].push(asset); + }); + + // For each version, keep the latest asset and delete older ones + for (const [version, versionAssets] of Object.entries(assetsByVersion)) { + if (versionAssets.length <= 2) continue; // Keep both zip and md5 for the latest version + + // Sort assets by created_at (newest first) + versionAssets.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); + + // Delete all assets except the first two (zip and md5) + for (let i = 2; i < versionAssets.length; i++) { + console.log(`Deleting old asset: ${versionAssets[i].name}`); + await github.rest.repos.deleteReleaseAsset({ + owner: context.repo.owner, + repo: context.repo.repo, + asset_id: versionAssets[i].id + }); + } + } + update-index: name: Update Version Index (Optional) needs: [pack, initialize-release] From 9bb898349c4ba325d3002ac0da42e0c4220f9e5f Mon Sep 17 00:00:00 2001 From: SlimeSB <86453765+SlimeSB@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:29:02 +0800 Subject: [PATCH 2/8] =?UTF-8?q?=E5=85=88=E5=88=A0=E9=99=A4=E5=90=8E?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/packer.yml | 89 +++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index d9687099e388..96e9b213aef2 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -135,7 +135,7 @@ jobs: id: cache-restore uses: actions/cache/restore@v4 with: - key: ${{ runner.os }}-Packer-${{ hashFiles('source/Packer/**') }} + key: ${{ runner.os }}-Packer-${{ hashFiles('src/Packer/**') }} path: | Packer.exe git2-*.dll @@ -181,7 +181,7 @@ jobs: upload-release-assets: name: Upload Release Assets - needs: [ pack, initialize-release ] + needs: [ pack, initialize-release, pre-upload-cleanup ] runs-on: windows-latest steps: - name: Download all Artifacts @@ -240,13 +240,13 @@ jobs: } shell: pwsh - clean-old-assets: - name: Clean Old Release Assets - needs: [upload-release-assets] + pre-upload-cleanup: + name: Clean Assets Before Upload + needs: [pack, initialize-release] if: needs.initialize-release.outputs.tag-name == 'autobuild' runs-on: ubuntu-latest steps: - - name: Clean old assets from autobuild release + - name: Clean only updated version assets from autobuild release uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} @@ -268,42 +268,59 @@ jobs: console.log(`Found ${assets.data.length} assets in autobuild release`); - // Group assets by version - const assetsByVersion = {}; - assets.data.forEach(asset => { - // Extract version from asset name - let version = 'unknown'; - if (asset.name.includes('-1.12.2.')) version = '1.12.2'; - else if (asset.name.includes('-1.16.')) version = '1.16'; - else if (asset.name.includes('-1.16-fabric.')) version = '1.16-fabric'; - else if (asset.name.includes('-1.18.')) version = '1.18'; - else if (asset.name.includes('-1.18-fabric.')) version = '1.18-fabric'; - else if (asset.name.includes('-1.19.')) version = '1.19'; - else if (asset.name.includes('-1.20.')) version = '1.20'; - else if (asset.name.includes('-1.20-fabric.')) version = '1.20-fabric'; - else if (asset.name.includes('-1.21.')) version = '1.21'; - else if (asset.name.includes('-1.21-fabric.')) version = '1.21-fabric'; - - if (!assetsByVersion[version]) { - assetsByVersion[version] = []; + // Parse pack outputs to get updated versions + const packOutputs = JSON.parse(`${{ toJSON(needs.pack.outputs) }}`); + const updatedVersions = []; + + for (const [key, value] of Object.entries(packOutputs)) { + if (value && key.startsWith('updated_versions_')) { + // Convert from updated_versions_1_12_2 to 1.12.2 + const version = key.replace('updated_versions_', '').replace(/_/g, '.'); + updatedVersions.push(version); } - assetsByVersion[version].push(asset); - }); + } - // For each version, keep the latest asset and delete older ones - for (const [version, versionAssets] of Object.entries(assetsByVersion)) { - if (versionAssets.length <= 2) continue; // Keep both zip and md5 for the latest version + console.log(`Updated versions in this run: ${updatedVersions.join(', ')}`); + + // Delete only assets for updated versions + for (const asset of assets.data) { + let assetVersion = 'unknown'; - // Sort assets by created_at (newest first) - versionAssets.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); + // Determine version from asset name + if (asset.name === 'Minecraft-Modpack.zip') { + assetVersion = '1.12.2'; // Special case for main zip + } else if (asset.name.includes('-1-12-2')) { + assetVersion = '1.12.2'; + } else if (asset.name.includes('-1-16-') && !asset.name.includes('-1-16-fabric.')) { + assetVersion = '1.16'; + } else if (asset.name.includes('-1-16-fabric.')) { + assetVersion = '1.16-fabric'; + } else if (asset.name.includes('-1-18-') && !asset.name.includes('-1-18-fabric.')) { + assetVersion = '1.18'; + } else if (asset.name.includes('-1-18-fabric.')) { + assetVersion = '1.18-fabric'; + } else if (asset.name.includes('-1-19-')) { + assetVersion = '1.19'; + } else if (asset.name.includes('-1-20-') && !asset.name.includes('-1-20-fabric.')) { + assetVersion = '1.20'; + } else if (asset.name.includes('-1-20-fabric.')) { + assetVersion = '1.20-fabric'; + } else if (asset.name.includes('-1-21-') && !asset.name.includes('-1-21-fabric.')) { + assetVersion = '1.21'; + } else if (asset.name.includes('-1-21-fabric.')) { + assetVersion = '1.21-fabric'; + } else if (asset.name.endsWith('.md5')) { + // Handle md5 files directly + assetVersion = asset.name.replace('.md5', ''); + } - // Delete all assets except the first two (zip and md5) - for (let i = 2; i < versionAssets.length; i++) { - console.log(`Deleting old asset: ${versionAssets[i].name}`); + // Check if this asset's version was updated in this run + if (updatedVersions.includes(assetVersion)) { + console.log(`Deleting old asset for updated version ${assetVersion}: ${asset.name}`); await github.rest.repos.deleteReleaseAsset({ owner: context.repo.owner, repo: context.repo.repo, - asset_id: versionAssets[i].id + asset_id: asset.id }); } } @@ -376,7 +393,7 @@ jobs: # 构造程序 - name: Build Uploader - run: dotnet publish .\src\Uploader\Uploader.csproj -o ./ -r win-x64 -p:PublishSingeFile=true + run: dotnet publish .\src\Uploader\Uploader.csproj -o ./ -r win-x64 -p:PublishSingleFile=true # 还原artifact(资源包) - name: Restore Artifacts From 3736c64b0fe7b9efaee71157b8a63977cd52c4d5 Mon Sep 17 00:00:00 2001 From: SlimeSB <86453765+SlimeSB@users.noreply.github.com> Date: Mon, 5 Jan 2026 10:32:17 +0800 Subject: [PATCH 3/8] =?UTF-8?q?autobuild=20exist=20=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/packer.yml | 42 ++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index 96e9b213aef2..34eaf071b150 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -87,9 +87,34 @@ jobs: fi shell: bash - # Create the release: https://github.com/actions/create-release + - name: Check if release exists + id: check_release + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + const release = await github.rest.repos.getReleaseByTag({ + owner: context.repo.owner, + repo: context.repo.repo, + tag: '${{ steps.determine_release.outputs.tag_name }}' + }); + console.log('Release exists:', release.data.id); + return { + exists: true, + id: release.data.id, + upload_url: release.data.upload_url + }; + } catch (error) { + console.log('Release does not exist:', error.message); + return { + exists: false + }; + } + - name: Create release id: create_release + if: steps.check_release.outputs.result == 'null' || fromJSON(steps.check_release.outputs.result).exists == false uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -98,9 +123,22 @@ jobs: release_name: ${{ steps.determine_release.outputs.release_name }} draft: false prerelease: ${{ steps.determine_release.outputs.prerelease }} + + - name: Update release information + id: update_release_info + run: | + if [ "${{ fromJSON(steps.check_release.outputs.result).exists }}" == "true" ]; then + echo "upload-url=${{ fromJSON(steps.check_release.outputs.result).upload_url }}" >> $GITHUB_OUTPUT + echo "release-id=${{ fromJSON(steps.check_release.outputs.result).id }}" >> $GITHUB_OUTPUT + else + echo "upload-url=${{ steps.create_release.outputs.upload_url }}" >> $GITHUB_OUTPUT + echo "release-id=${{ steps.create_release.outputs.id }}" >> $GITHUB_OUTPUT + fi + shell: bash outputs: - upload-url: ${{ steps.create_release.outputs.upload_url }} + upload-url: ${{ steps.update_release_info.outputs.upload-url }} tag-name: ${{ steps.determine_release.outputs.tag_name }} + release-id: ${{ steps.update_release_info.outputs.release-id }} pack: From ec663f44cc6033d6cfa01ca81fce87d26dd0ff40 Mon Sep 17 00:00:00 2001 From: SlimeSB <86453765+SlimeSB@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:54:42 +0800 Subject: [PATCH 4/8] =?UTF-8?q?=E6=8F=8F=E8=BF=B0-=E6=89=93=E5=8C=85?= =?UTF-8?q?=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/packer.yml | 108 ++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index 34eaf071b150..d1180bd4ae8d 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -163,6 +163,17 @@ jobs: updated_versions_1_20_fabric: ${{ steps.collect-updated.outputs.version_1_20_fabric }} updated_versions_1_21: ${{ steps.collect-updated.outputs.version_1_21 }} updated_versions_1_21_fabric: ${{ steps.collect-updated.outputs.version_1_21_fabric }} + # Build times for each version + build_time_1_12_2: ${{ steps.collect-updated.outputs.build_time_1_12_2 }} + build_time_1_16: ${{ steps.collect-updated.outputs.build_time_1_16 }} + build_time_1_16_fabric: ${{ steps.collect-updated.outputs.build_time_1_16_fabric }} + build_time_1_18: ${{ steps.collect-updated.outputs.build_time_1_18 }} + build_time_1_18_fabric: ${{ steps.collect-updated.outputs.build_time_1_18_fabric }} + build_time_1_19: ${{ steps.collect-updated.outputs.build_time_1_19 }} + build_time_1_20: ${{ steps.collect-updated.outputs.build_time_1_20 }} + build_time_1_20_fabric: ${{ steps.collect-updated.outputs.build_time_1_20_fabric }} + build_time_1_21: ${{ steps.collect-updated.outputs.build_time_1_21 }} + build_time_1_21_fabric: ${{ steps.collect-updated.outputs.build_time_1_21_fabric }} steps: - uses: actions/checkout@v2 with: @@ -205,7 +216,7 @@ jobs: ${{ matrix.version }}.md5 if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch' - - name: Collect updated versions + - name: Collect updated versions and build time id: collect-updated run: | if [ "${{ steps.check-changes.outputs.changed }}" == "true" ] || [ "${{ github.event_name }}" == "workflow_dispatch" ]; then @@ -213,6 +224,10 @@ jobs: # 将.转换为_,适配变量名 output_key=$(echo "${{ matrix.version }}" | sed 's/[\.-]/_/g') echo "version_$output_key=${{ matrix.version }}" >> $GITHUB_OUTPUT + + # Record build time for this version + build_time=$(date '+%Y-%m-%d %H:%M:%S') + echo "build_time_$output_key=$build_time" >> $GITHUB_OUTPUT fi shell: bash continue-on-error: true @@ -278,6 +293,97 @@ jobs: } shell: pwsh + update-release-description: + name: Update Release Description with Build Times + needs: [ pack, initialize-release, upload-release-assets ] + if: needs.initialize-release.outputs.tag-name == 'autobuild' + runs-on: ubuntu-latest + steps: + - name: Update release description with build times + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + // Parse pack outputs to get build times + const packOutputs = JSON.parse(`${{ toJSON(needs.pack.outputs) }}`); + const buildTimes = []; + + // Collect all build times + const versions = ["1.12.2", "1.16", "1.16-fabric", "1.18", "1.18-fabric", "1.19", "1.20", "1.20-fabric", "1.21", "1.21-fabric"]; + + for (const version of versions) { + // Convert version to output key format (e.g., 1.12.2 → build_time_1_12_2) + const key = `build_time_${version.replace(/[\.-]/g, '_')}`; + if (packOutputs[key]) { + buildTimes.push({ + version: version, + time: packOutputs[key] + }); + } + } + + // Get existing release to preserve build times of unchanged versions + const existingRelease = await github.rest.repos.getRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: ${{ needs.initialize-release.outputs.release-id }} + }); + + // Parse existing build times from release body + const existingBuildTimes = new Map(); + const existingBody = existingRelease.data.body; + if (existingBody) { + const buildTimeSection = existingBody.match(/### 各个版本打包时间\n\n([\s\S]*?)\n\n###/); + if (buildTimeSection && buildTimeSection[1]) { + const timeLines = buildTimeSection[1].split('\n').filter(line => line.trim()); + timeLines.forEach(line => { + const match = line.match(/^-\s*(.+?):\s*(.+?)\s*UTC$/); + if (match) { + existingBuildTimes.set(match[1], match[2]); + } + }); + } + } + + // Merge existing times with new build times + const allBuildTimes = new Map(existingBuildTimes); + buildTimes.forEach(item => { + allBuildTimes.set(item.version, item.time); + }); + + // Generate release description + let description = "## 汉化资源包-AutoBuild\n\n"; + description += "### 各个版本打包时间\n\n"; + + if (allBuildTimes.size > 0) { + // Sort versions for consistent display + const sortedVersions = Array.from(allBuildTimes.keys()).sort((a, b) => { + // Custom sort to handle version strings properly + const parseVersion = (v) => { + const parts = v.replace('-fabric', '').split('.').map(Number); + return parts[0] * 10000 + parts[1] * 100 + (parts[2] || 0) + (v.includes('fabric') ? 0.5 : 0); + }; + return parseVersion(a) - parseVersion(b); + }); + + sortedVersions.forEach(version => { + description += `- ${version}: ${allBuildTimes.get(version)} UTC\n`; + }); + } else { + description += "本次构建未更新任何版本。\n"; + } + + // Update the release + await github.rest.repos.updateRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: ${{ needs.initialize-release.outputs.release-id }}, + body: description + }); + + console.log('Updated release description with build times'); + console.log('Build times:', buildTimes); + pre-upload-cleanup: name: Clean Assets Before Upload needs: [pack, initialize-release] From 31868b98c3bd99313edfc9300d79bc9c1390c7dd Mon Sep 17 00:00:00 2001 From: SlimeSB <86453765+SlimeSB@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:15:56 +0800 Subject: [PATCH 5/8] =?UTF-8?q?-=20=E5=A4=84=E7=90=86=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/packer.yml | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index d1180bd4ae8d..867fd503b2b9 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -418,8 +418,12 @@ jobs: for (const [key, value] of Object.entries(packOutputs)) { if (value && key.startsWith('updated_versions_')) { - // Convert from updated_versions_1_12_2 to 1.12.2 - const version = key.replace('updated_versions_', '').replace(/_/g, '.'); + // Convert from updated_versions_1_16_fabric to 1.16-fabric + // First remove prefix, then convert underscores that were dots back to dots + // but preserve underscores that were originally hyphens + const versionPart = key.replace('updated_versions_', ''); + // First convert _fabric to -fabric, then convert remaining _ to . + const version = versionPart.replace(/_fabric/g, '-fabric').replace(/_/g, '.'); updatedVersions.push(version); } } @@ -435,23 +439,23 @@ jobs: assetVersion = '1.12.2'; // Special case for main zip } else if (asset.name.includes('-1-12-2')) { assetVersion = '1.12.2'; - } else if (asset.name.includes('-1-16-') && !asset.name.includes('-1-16-fabric.')) { + } else if (asset.name.includes('-1-16-') && !asset.name.includes('-1-16-fabric') && !asset.name.includes('-1-16-Fabric')) { assetVersion = '1.16'; - } else if (asset.name.includes('-1-16-fabric.')) { + } else if (asset.name.includes('-1-16-fabric') || asset.name.includes('-1-16-Fabric')) { assetVersion = '1.16-fabric'; - } else if (asset.name.includes('-1-18-') && !asset.name.includes('-1-18-fabric.')) { + } else if (asset.name.includes('-1-18-') && !asset.name.includes('-1-18-fabric') && !asset.name.includes('-1-18-Fabric')) { assetVersion = '1.18'; - } else if (asset.name.includes('-1-18-fabric.')) { + } else if (asset.name.includes('-1-18-fabric') || asset.name.includes('-1-18-Fabric')) { assetVersion = '1.18-fabric'; } else if (asset.name.includes('-1-19-')) { assetVersion = '1.19'; - } else if (asset.name.includes('-1-20-') && !asset.name.includes('-1-20-fabric.')) { + } else if (asset.name.includes('-1-20-') && !asset.name.includes('-1-20-fabric') && !asset.name.includes('-1-20-Fabric')) { assetVersion = '1.20'; - } else if (asset.name.includes('-1-20-fabric.')) { + } else if (asset.name.includes('-1-20-fabric') || asset.name.includes('-1-20-Fabric')) { assetVersion = '1.20-fabric'; - } else if (asset.name.includes('-1-21-') && !asset.name.includes('-1-21-fabric.')) { + } else if (asset.name.includes('-1-21-') && !asset.name.includes('-1-21-fabric') && !asset.name.includes('-1-21-Fabric')) { assetVersion = '1.21'; - } else if (asset.name.includes('-1-21-fabric.')) { + } else if (asset.name.includes('-1-21-fabric') || asset.name.includes('-1-21-Fabric')) { assetVersion = '1.21-fabric'; } else if (asset.name.endsWith('.md5')) { // Handle md5 files directly From 82add8129cae59bfe444e8b9d2f115bc3715702d Mon Sep 17 00:00:00 2001 From: SlimeSB <86453765+SlimeSB@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:22:06 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E6=B3=A8=E9=87=8A=EF=BC=8C=E5=B9=B2?= =?UTF-8?q?=E6=8E=89=E4=B8=91=E9=99=8B=E7=9A=84else=20if=EF=BC=8C=E5=B9=B2?= =?UTF-8?q?=E6=8E=89=E6=90=9E=E4=B8=8D=E6=87=82=E7=9A=84=E6=89=93=E5=8C=85?= =?UTF-8?q?=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/packer.yml | 171 +++++++++++------------------------ 1 file changed, 51 insertions(+), 120 deletions(-) diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index 867fd503b2b9..28e740a51fe1 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -163,17 +163,6 @@ jobs: updated_versions_1_20_fabric: ${{ steps.collect-updated.outputs.version_1_20_fabric }} updated_versions_1_21: ${{ steps.collect-updated.outputs.version_1_21 }} updated_versions_1_21_fabric: ${{ steps.collect-updated.outputs.version_1_21_fabric }} - # Build times for each version - build_time_1_12_2: ${{ steps.collect-updated.outputs.build_time_1_12_2 }} - build_time_1_16: ${{ steps.collect-updated.outputs.build_time_1_16 }} - build_time_1_16_fabric: ${{ steps.collect-updated.outputs.build_time_1_16_fabric }} - build_time_1_18: ${{ steps.collect-updated.outputs.build_time_1_18 }} - build_time_1_18_fabric: ${{ steps.collect-updated.outputs.build_time_1_18_fabric }} - build_time_1_19: ${{ steps.collect-updated.outputs.build_time_1_19 }} - build_time_1_20: ${{ steps.collect-updated.outputs.build_time_1_20 }} - build_time_1_20_fabric: ${{ steps.collect-updated.outputs.build_time_1_20_fabric }} - build_time_1_21: ${{ steps.collect-updated.outputs.build_time_1_21 }} - build_time_1_21_fabric: ${{ steps.collect-updated.outputs.build_time_1_21_fabric }} steps: - uses: actions/checkout@v2 with: @@ -225,9 +214,7 @@ jobs: output_key=$(echo "${{ matrix.version }}" | sed 's/[\.-]/_/g') echo "version_$output_key=${{ matrix.version }}" >> $GITHUB_OUTPUT - # Record build time for this version - build_time=$(date '+%Y-%m-%d %H:%M:%S') - echo "build_time_$output_key=$build_time" >> $GITHUB_OUTPUT + fi shell: bash continue-on-error: true @@ -294,84 +281,22 @@ jobs: shell: pwsh update-release-description: - name: Update Release Description with Build Times + name: Update Release Description with Build Time needs: [ pack, initialize-release, upload-release-assets ] if: needs.initialize-release.outputs.tag-name == 'autobuild' runs-on: ubuntu-latest steps: - - name: Update release description with build times + - name: Update release description with build time uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - // Parse pack outputs to get build times - const packOutputs = JSON.parse(`${{ toJSON(needs.pack.outputs) }}`); - const buildTimes = []; - - // Collect all build times - const versions = ["1.12.2", "1.16", "1.16-fabric", "1.18", "1.18-fabric", "1.19", "1.20", "1.20-fabric", "1.21", "1.21-fabric"]; - - for (const version of versions) { - // Convert version to output key format (e.g., 1.12.2 → build_time_1_12_2) - const key = `build_time_${version.replace(/[\.-]/g, '_')}`; - if (packOutputs[key]) { - buildTimes.push({ - version: version, - time: packOutputs[key] - }); - } - } - - // Get existing release to preserve build times of unchanged versions - const existingRelease = await github.rest.repos.getRelease({ - owner: context.repo.owner, - repo: context.repo.repo, - release_id: ${{ needs.initialize-release.outputs.release-id }} - }); - - // Parse existing build times from release body - const existingBuildTimes = new Map(); - const existingBody = existingRelease.data.body; - if (existingBody) { - const buildTimeSection = existingBody.match(/### 各个版本打包时间\n\n([\s\S]*?)\n\n###/); - if (buildTimeSection && buildTimeSection[1]) { - const timeLines = buildTimeSection[1].split('\n').filter(line => line.trim()); - timeLines.forEach(line => { - const match = line.match(/^-\s*(.+?):\s*(.+?)\s*UTC$/); - if (match) { - existingBuildTimes.set(match[1], match[2]); - } - }); - } - } + // Generate release description with only the action run time + const currentTime = new Date().toUTCString(); - // Merge existing times with new build times - const allBuildTimes = new Map(existingBuildTimes); - buildTimes.forEach(item => { - allBuildTimes.set(item.version, item.time); - }); - - // Generate release description let description = "## 汉化资源包-AutoBuild\n\n"; - description += "### 各个版本打包时间\n\n"; - - if (allBuildTimes.size > 0) { - // Sort versions for consistent display - const sortedVersions = Array.from(allBuildTimes.keys()).sort((a, b) => { - // Custom sort to handle version strings properly - const parseVersion = (v) => { - const parts = v.replace('-fabric', '').split('.').map(Number); - return parts[0] * 10000 + parts[1] * 100 + (parts[2] || 0) + (v.includes('fabric') ? 0.5 : 0); - }; - return parseVersion(a) - parseVersion(b); - }); - - sortedVersions.forEach(version => { - description += `- ${version}: ${allBuildTimes.get(version)} UTC\n`; - }); - } else { - description += "本次构建未更新任何版本。\n"; - } + description += "### Action 运行时间\n\n"; + description += `- ${currentTime}\n`; // Update the release await github.rest.repos.updateRelease({ @@ -381,8 +306,8 @@ jobs: body: description }); - console.log('Updated release description with build times'); - console.log('Build times:', buildTimes); + console.log('Updated release description with action run time'); + console.log('Action run time:', currentTime); pre-upload-cleanup: name: Clean Assets Before Upload @@ -395,76 +320,82 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | - // Get the autobuild release + // ========================== + // 上传前清理任务:删除autobuild发布中已更新版本的旧资产 + // ========================== + + // 获取autobuild发布版本 const release = await github.rest.repos.getReleaseByTag({ owner: context.repo.owner, repo: context.repo.repo, tag: 'autobuild' }); - // Get all assets + // 获取该发布版本的所有资产 const assets = await github.rest.repos.listReleaseAssets({ owner: context.repo.owner, repo: context.repo.repo, release_id: release.data.id, - per_page: 100 + per_page: 100 // 一次获取最多100个资产 }); - console.log(`Found ${assets.data.length} assets in autobuild release`); + console.log(`在autobuild发布中找到 ${assets.data.length} 个资产`); - // Parse pack outputs to get updated versions + // 解析pack任务输出,获取本次构建中已更新的版本 const packOutputs = JSON.parse(`${{ toJSON(needs.pack.outputs) }}`); const updatedVersions = []; + // 遍历pack任务的所有输出 for (const [key, value] of Object.entries(packOutputs)) { + // 筛选出更新版本的输出项 if (value && key.startsWith('updated_versions_')) { - // Convert from updated_versions_1_16_fabric to 1.16-fabric - // First remove prefix, then convert underscores that were dots back to dots - // but preserve underscores that were originally hyphens + // 将输出键转换为标准版本格式 + // 例如:updated_versions_1_16_fabric -> 1.16-fabric const versionPart = key.replace('updated_versions_', ''); - // First convert _fabric to -fabric, then convert remaining _ to . + // 先将_fabric转换为-fabric,再将剩余的下划线转换为点 const version = versionPart.replace(/_fabric/g, '-fabric').replace(/_/g, '.'); updatedVersions.push(version); } } - console.log(`Updated versions in this run: ${updatedVersions.join(', ')}`); + console.log(`本次运行中已更新的版本:${updatedVersions.join(', ')}`); - // Delete only assets for updated versions + // 遍历所有资产,只删除已更新版本的旧资产 for (const asset of assets.data) { let assetVersion = 'unknown'; - // Determine version from asset name + // 从资产名称中确定版本号(使用正则表达式使代码更简洁) if (asset.name === 'Minecraft-Modpack.zip') { - assetVersion = '1.12.2'; // Special case for main zip - } else if (asset.name.includes('-1-12-2')) { - assetVersion = '1.12.2'; - } else if (asset.name.includes('-1-16-') && !asset.name.includes('-1-16-fabric') && !asset.name.includes('-1-16-Fabric')) { - assetVersion = '1.16'; - } else if (asset.name.includes('-1-16-fabric') || asset.name.includes('-1-16-Fabric')) { - assetVersion = '1.16-fabric'; - } else if (asset.name.includes('-1-18-') && !asset.name.includes('-1-18-fabric') && !asset.name.includes('-1-18-Fabric')) { - assetVersion = '1.18'; - } else if (asset.name.includes('-1-18-fabric') || asset.name.includes('-1-18-Fabric')) { - assetVersion = '1.18-fabric'; - } else if (asset.name.includes('-1-19-')) { - assetVersion = '1.19'; - } else if (asset.name.includes('-1-20-') && !asset.name.includes('-1-20-fabric') && !asset.name.includes('-1-20-Fabric')) { - assetVersion = '1.20'; - } else if (asset.name.includes('-1-20-fabric') || asset.name.includes('-1-20-Fabric')) { - assetVersion = '1.20-fabric'; - } else if (asset.name.includes('-1-21-') && !asset.name.includes('-1-21-fabric') && !asset.name.includes('-1-21-Fabric')) { - assetVersion = '1.21'; - } else if (asset.name.includes('-1-21-fabric') || asset.name.includes('-1-21-Fabric')) { - assetVersion = '1.21-fabric'; + assetVersion = '1.12.2'; // 主zip文件的特殊处理 } else if (asset.name.endsWith('.md5')) { - // Handle md5 files directly + // 直接处理md5文件,去掉扩展名即可得到版本 assetVersion = asset.name.replace('.md5', ''); + } else { + // 使用正则表达式匹配版本模式 + // 匹配格式:-1-XX-XX-fabric 或 -1-XX-XX-Fabric + const versionRegex = /-1-(\d{2})-?(\d+)?-?(fabric|Fabric)?/i; + const match = asset.name.match(versionRegex); + + if (match) { + const major = match[1]; // 主版本号 + const minor = match[2] || ''; // 次版本号(可选) + const platform = match[3] ? 'fabric' : ''; // 平台(如果是fabric版本) + + // 动态构造版本字符串 + if (major === '12' && minor === '2') { + // 特殊处理1.12.2版本 + assetVersion = '1.12.2'; + } else { + // 构造其他版本字符串,如1.16、1.16-fabric等 + assetVersion = `1.${major}${platform ? `-${platform}` : ''}`; + } + } } - // Check if this asset's version was updated in this run + // 检查该资产的版本是否在本次更新的版本列表中 if (updatedVersions.includes(assetVersion)) { - console.log(`Deleting old asset for updated version ${assetVersion}: ${asset.name}`); + console.log(`删除已更新版本 ${assetVersion} 的旧资产:${asset.name}`); + // 删除旧资产 await github.rest.repos.deleteReleaseAsset({ owner: context.repo.owner, repo: context.repo.repo, From 5a68c3a1e7870f35d1d706284e0178587bb7c81a Mon Sep 17 00:00:00 2001 From: SlimeSB <86453765+SlimeSB@users.noreply.github.com> Date: Mon, 5 Jan 2026 13:38:54 +0800 Subject: [PATCH 7/8] =?UTF-8?q?1.12=E5=8C=B9=E9=85=8D=E9=94=99=E8=AF=AF=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/packer.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index 28e740a51fe1..e4235431864a 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -61,7 +61,7 @@ jobs: # # 构造程序 # - name: Build Uploader if not cached # if: steps.cache-uploader.outputs.cache-hit != 'true' - # run: dotnet publish .\src\Uploader\Uploader.csproj -o ./ -r win-x64 -p:PublishSingeFile=true + # run: dotnet publish .\src\Uploader\Uploader.csproj -o ./ -r win-x64 -p:PublishSingleFile=true initialize-release: name: Initialize Release @@ -365,8 +365,10 @@ jobs: let assetVersion = 'unknown'; // 从资产名称中确定版本号(使用正则表达式使代码更简洁) - if (asset.name === 'Minecraft-Modpack.zip') { + if (asset.name === 'Minecraft-Mod-Language-Modpack.zip') { assetVersion = '1.12.2'; // 主zip文件的特殊处理 + } else if (asset.name === 'Minecraft-Mod-Language-Modpack.zip.md5') { + assetVersion = '1.12.2'; // 主zip文件的md5文件特殊处理 } else if (asset.name.endsWith('.md5')) { // 直接处理md5文件,去掉扩展名即可得到版本 assetVersion = asset.name.replace('.md5', ''); From 5d0d33c622a152eae7d855d0ef4636645564f2bb Mon Sep 17 00:00:00 2001 From: dovisutu <40313014+dovisutu@users.noreply.github.com> Date: Mon, 5 Jan 2026 21:28:48 +0800 Subject: [PATCH 8/8] Move logic into Uploader --- .github/workflows/packer.yml | 366 ++--------------------------------- src/Uploader/Program.cs | 194 +++++++++++++------ src/Uploader/Uploader.csproj | 2 + 3 files changed, 145 insertions(+), 417 deletions(-) diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index 13bd47d1ff2c..b1148cba9f38 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -10,7 +10,6 @@ on: - 'projects/**' - jobs: build-packer: name: Build / Cache Packer @@ -63,106 +62,16 @@ jobs: if: steps.cache-uploader.outputs.cache-hit != 'true' run: dotnet publish .\src\Uploader\Uploader.csproj -o ./ -r win-x64 - initialize-release: - name: Initialize Release - runs-on: windows-latest - steps: - - - name: Create timestamp - id: create_timestamp - run: echo "timestamp=$(date '+%Y%m%d%H%M%s')" >> $GITHUB_OUTPUT - shell: bash - - - name: Determine release type and tag - id: determine_release - run: | - if [ "${{ github.event_name }}" == "schedule" ] || [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - echo "tag_name=autobuild" >> $GITHUB_OUTPUT - echo "release_name=汉化资源包-AutoBuild" >> $GITHUB_OUTPUT - echo "prerelease=true" >> $GITHUB_OUTPUT - else - echo "tag_name=Snapshot-${{ steps.create_timestamp.outputs.timestamp }}" >> $GITHUB_OUTPUT - echo "release_name=汉化资源包-Snapshot-${{ steps.create_timestamp.outputs.timestamp }}" >> $GITHUB_OUTPUT - echo "prerelease=false" >> $GITHUB_OUTPUT - fi - shell: bash - - - name: Check if release exists - id: check_release - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - try { - const release = await github.rest.repos.getReleaseByTag({ - owner: context.repo.owner, - repo: context.repo.repo, - tag: '${{ steps.determine_release.outputs.tag_name }}' - }); - console.log('Release exists:', release.data.id); - return { - exists: true, - id: release.data.id, - upload_url: release.data.upload_url - }; - } catch (error) { - console.log('Release does not exist:', error.message); - return { - exists: false - }; - } - - - name: Create release - id: create_release - if: steps.check_release.outputs.result == 'null' || fromJSON(steps.check_release.outputs.result).exists == false - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ steps.determine_release.outputs.tag_name }} - release_name: ${{ steps.determine_release.outputs.release_name }} - draft: false - prerelease: ${{ steps.determine_release.outputs.prerelease }} - - - name: Update release information - id: update_release_info - run: | - if [ "${{ fromJSON(steps.check_release.outputs.result).exists }}" == "true" ]; then - echo "upload-url=${{ fromJSON(steps.check_release.outputs.result).upload_url }}" >> $GITHUB_OUTPUT - echo "release-id=${{ fromJSON(steps.check_release.outputs.result).id }}" >> $GITHUB_OUTPUT - else - echo "upload-url=${{ steps.create_release.outputs.upload_url }}" >> $GITHUB_OUTPUT - echo "release-id=${{ steps.create_release.outputs.id }}" >> $GITHUB_OUTPUT - fi - shell: bash - outputs: - upload-url: ${{ steps.update_release_info.outputs.upload-url }} - tag-name: ${{ steps.determine_release.outputs.tag_name }} - release-id: ${{ steps.update_release_info.outputs.release-id }} - - pack: name: Pack Resources and Upload Artifacts/Releases - needs: [ build-packer, initialize-release ] # 显然,需要存在打包程序,才能打包。 + needs: [ build-packer ] strategy: - fail-fast: false # 把正常的文件先打包了,避免一处错误阻塞整个仓库。 + fail-fast: false matrix: # 版本列表。将对这里的每个版本判断,按需打包。 # 如需添加新版本,在这里添加即可。 version: [ "1.12.2", "1.16", "1.16-fabric", "1.18", "1.18-fabric", "1.19", "1.20", "1.20-fabric", "1.21", "1.21-fabric" ] runs-on: windows-latest - outputs: - # 为每个版本创建独立的输出变量 - updated_versions_1_12_2: ${{ steps.collect-updated.outputs.version_1_12_2 }} - updated_versions_1_16: ${{ steps.collect-updated.outputs.version_1_16 }} - updated_versions_1_16_fabric: ${{ steps.collect-updated.outputs.version_1_16_fabric }} - updated_versions_1_18: ${{ steps.collect-updated.outputs.version_1_18 }} - updated_versions_1_18_fabric: ${{ steps.collect-updated.outputs.version_1_18_fabric }} - updated_versions_1_19: ${{ steps.collect-updated.outputs.version_1_19 }} - updated_versions_1_20: ${{ steps.collect-updated.outputs.version_1_20 }} - updated_versions_1_20_fabric: ${{ steps.collect-updated.outputs.version_1_20_fabric }} - updated_versions_1_21: ${{ steps.collect-updated.outputs.version_1_21 }} - updated_versions_1_21_fabric: ${{ steps.collect-updated.outputs.version_1_21_fabric }} steps: - uses: actions/checkout@v2 with: @@ -177,7 +86,7 @@ jobs: path: | Packer.exe git2-*.dll - fail-on-cache-miss: true # 前一步理应构造过的。如果不命中,肯定有问题,不如直接挂掉。 + fail-on-cache-miss: true # 应由前序保证 - name: Check changed path on ${{ matrix.version }} uses: MarceloPrado/has-changed-path@v1.0 @@ -205,266 +114,13 @@ jobs: ${{ matrix.version }}.md5 if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch' - - name: Collect updated versions and build time - id: collect-updated - run: | - if [ "${{ steps.check-changes.outputs.changed }}" == "true" ] || [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - # Replace all periods and hyphens with underscores - # 将.转换为_,适配变量名 - output_key=$(echo "${{ matrix.version }}" | sed 's/[\.-]/_/g') - echo "version_$output_key=${{ matrix.version }}" >> $GITHUB_OUTPUT - - - fi - shell: bash - continue-on-error: true - - upload-release-assets: - name: Upload Release Assets - needs: [ pack, initialize-release, pre-upload-cleanup ] - runs-on: windows-latest - steps: - - name: Download all Artifacts - uses: actions/download-artifact@v4 - with: - path: artifacts/ - - - name: Upload Release Assets - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Get the upload URL from the previous job - $upload_url = "${{ needs.initialize-release.outputs.upload-url }}" - - # Clean up the URL by removing the template part {?name,label} - $clean_upload_url = $upload_url.Split('{')[0] - - # Iterate through downloaded artifact directories - Get-ChildItem -Path "artifacts" -Directory | ForEach-Object { - $artifact_dir_name = $_.Name - $version_tag = ($artifact_dir_name -split '-Modpack-')[1] - - # Generate the correct asset names - if ($version_tag -eq '1.12.2') { - $zip_asset_name = "Minecraft-Mod-Language-Modpack.zip" - } else { - $formatted_version = $version_tag -replace '\.', '-' - $formatted_version = $formatted_version -replace 'fabric', 'Fabric' - $zip_asset_name = "Minecraft-Mod-Language-Modpack-$formatted_version.zip" - } - - # Build file paths using sub-expression operator - $zip_path = "$(Join-Path -Path $_.FullName -ChildPath ($artifact_dir_name + '.zip'))" - $md5_path = "$(Join-Path -Path $_.FullName -ChildPath ($version_tag + '.md5'))" - - # Build the full URL using the format operator -f - $zip_upload_url = "{0}?name={1}" -f $clean_upload_url, $zip_asset_name - $md5_upload_url = "{0}?name={1}" -f $clean_upload_url, ($version_tag + ".md5") - - # Upload ZIP file - echo "Uploading ZIP: $zip_path as $zip_asset_name" - curl.exe -X POST ` - -H "Authorization: token $env:GITHUB_TOKEN" ` - -H "Content-Type: application/zip" ` - --data-binary "@$zip_path" ` - $zip_upload_url - - # Upload MD5 file - echo "Uploading MD5: $md5_path as $version_tag.md5" - $md5_content = Get-Content -Path "$md5_path" - curl.exe -X POST ` - -H "Authorization: token $env:GITHUB_TOKEN" ` - -H "Content-Type: text/plain" ` - --data-raw "$md5_content" ` - $md5_upload_url - } - shell: pwsh - - update-release-description: - name: Update Release Description with Build Time - needs: [ pack, initialize-release, upload-release-assets ] - if: needs.initialize-release.outputs.tag-name == 'autobuild' - runs-on: ubuntu-latest - steps: - - name: Update release description with build time - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - // Generate release description with only the action run time - const currentTime = new Date().toUTCString(); - - let description = "## 汉化资源包-AutoBuild\n\n"; - description += "### Action 运行时间\n\n"; - description += `- ${currentTime}\n`; - - // Update the release - await github.rest.repos.updateRelease({ - owner: context.repo.owner, - repo: context.repo.repo, - release_id: ${{ needs.initialize-release.outputs.release-id }}, - body: description - }); - - console.log('Updated release description with action run time'); - console.log('Action run time:', currentTime); - - pre-upload-cleanup: - name: Clean Assets Before Upload - needs: [pack, initialize-release] - if: needs.initialize-release.outputs.tag-name == 'autobuild' - runs-on: ubuntu-latest - steps: - - name: Clean only updated version assets from autobuild release - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - // ========================== - // 上传前清理任务:删除autobuild发布中已更新版本的旧资产 - // ========================== - - // 获取autobuild发布版本 - const release = await github.rest.repos.getReleaseByTag({ - owner: context.repo.owner, - repo: context.repo.repo, - tag: 'autobuild' - }); - - // 获取该发布版本的所有资产 - const assets = await github.rest.repos.listReleaseAssets({ - owner: context.repo.owner, - repo: context.repo.repo, - release_id: release.data.id, - per_page: 100 // 一次获取最多100个资产 - }); - - console.log(`在autobuild发布中找到 ${assets.data.length} 个资产`); - - // 解析pack任务输出,获取本次构建中已更新的版本 - const packOutputs = JSON.parse(`${{ toJSON(needs.pack.outputs) }}`); - const updatedVersions = []; - - // 遍历pack任务的所有输出 - for (const [key, value] of Object.entries(packOutputs)) { - // 筛选出更新版本的输出项 - if (value && key.startsWith('updated_versions_')) { - // 将输出键转换为标准版本格式 - // 例如:updated_versions_1_16_fabric -> 1.16-fabric - const versionPart = key.replace('updated_versions_', ''); - // 先将_fabric转换为-fabric,再将剩余的下划线转换为点 - const version = versionPart.replace(/_fabric/g, '-fabric').replace(/_/g, '.'); - updatedVersions.push(version); - } - } - - console.log(`本次运行中已更新的版本:${updatedVersions.join(', ')}`); - - // 遍历所有资产,只删除已更新版本的旧资产 - for (const asset of assets.data) { - let assetVersion = 'unknown'; - - // 从资产名称中确定版本号(使用正则表达式使代码更简洁) - if (asset.name === 'Minecraft-Mod-Language-Modpack.zip') { - assetVersion = '1.12.2'; // 主zip文件的特殊处理 - } else if (asset.name === 'Minecraft-Mod-Language-Modpack.zip.md5') { - assetVersion = '1.12.2'; // 主zip文件的md5文件特殊处理 - } else if (asset.name.endsWith('.md5')) { - // 直接处理md5文件,去掉扩展名即可得到版本 - assetVersion = asset.name.replace('.md5', ''); - } else { - // 使用正则表达式匹配版本模式 - // 匹配格式:-1-XX-XX-fabric 或 -1-XX-XX-Fabric - const versionRegex = /-1-(\d{2})-?(\d+)?-?(fabric|Fabric)?/i; - const match = asset.name.match(versionRegex); - - if (match) { - const major = match[1]; // 主版本号 - const minor = match[2] || ''; // 次版本号(可选) - const platform = match[3] ? 'fabric' : ''; // 平台(如果是fabric版本) - - // 动态构造版本字符串 - if (major === '12' && minor === '2') { - // 特殊处理1.12.2版本 - assetVersion = '1.12.2'; - } else { - // 构造其他版本字符串,如1.16、1.16-fabric等 - assetVersion = `1.${major}${platform ? `-${platform}` : ''}`; - } - } - } - - // 检查该资产的版本是否在本次更新的版本列表中 - if (updatedVersions.includes(assetVersion)) { - console.log(`删除已更新版本 ${assetVersion} 的旧资产:${asset.name}`); - // 删除旧资产 - await github.rest.repos.deleteReleaseAsset({ - owner: context.repo.owner, - repo: context.repo.repo, - asset_id: asset.id - }); - } - } - - update-index: - name: Update Version Index (Optional) - needs: [pack, initialize-release] - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - with: - fetch-depth: 1 - - - name: Set up index branch - run: | - git fetch origin index - git checkout index || git checkout -b index - - - name: Download existing index.json - run: | - if [ ! -f version-index.json ]; then - echo "{}" > version-index.json - fi - - - name: Update index.json - env: - RELEASE_TAG: ${{ needs.initialize-release.outputs.tag-name }} - PACK_OUTPUTS: ${{ toJSON(needs.pack.outputs) }} - run: | - python3 - < + { + var fileExtensionName = _.Extension; // 带点名称,应当为 ".zip" + var fileName = _.Name[0..^fileExtensionName.Length] + .RegulateFileName(); + return (name: fileName + fileExtensionName, file: _); + }); + var md5s = artifactDirectory + .EnumerateFiles("*.md5", SearchOption.AllDirectories) + .Select(_ => (name: _.Name, file: _)); + var files = packs.Concat(md5s); + + Console.WriteLine("待上传的文件数目:{0}", files.Count()); + + IEnumerable tasks = + [ + UploadToServer(host, name, password, files), + UploadSnapshotAssets(client, files), + UpdateAutobuildAssets(client, files) + ]; + await Task.WhenAll(tasks); + } + + async static Task UploadToServer(string host, string username, string password, IEnumerable<(string name, FileInfo file)> files) + { + using var scpClient = new ScpClient(host, port: 22, username, password); scpClient.Connect(); // 与下载服务器建立连接 - + // 确认连接状态 if (scpClient.IsConnected) { @@ -28,59 +73,91 @@ static int Main(string host, string name, string password) else { Log.Error("SCP服务器连接失败"); - return -1; + throw new InvalidOperationException(); } - - // 获取可用的资源包,准备上传 - var artifactDirectory = new DirectoryInfo(Path.Join(Directory.GetCurrentDirectory(), "artifacts")); - var packList = artifactDirectory - .EnumerateFiles("Minecraft-Mod-Language-Modpack-*.zip", SearchOption.AllDirectories); - - Log.Information("检测到的资源包数目:{0}", packList.Count()); + foreach (var (name, file) in files) + { + var destinationName = $"/var/www/html/files/{name}"; + scpClient.Upload(file, destinationName); // 没有async :( + Log.Information(" 写入文件:{0}", destinationName); + } + } - packList.ToList() - .ForEach(_ => + async static Task UploadSnapshotAssets(GitHubClient client, IEnumerable<(string name, FileInfo file)> files) + { + var timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss"); + Log.Information(" 时间戳:{0}", timestamp); + var newRelease = new NewRelease($"Snapshot-{timestamp}") + { + TargetCommitish = Environment.GetEnvironmentVariable("SHA"), + Name = $"汉化资源包-{timestamp}" + }; + var result = await client.Repository.Release.Create(long.Parse(Environment.GetEnvironmentVariable("REPO_ID")!), newRelease); + Log.Information(" 创建 Release"); + foreach (var (name, file) in files) + { + using var fileStream = file.OpenRead(); + var newAsset = new ReleaseAssetUpload( + name, + file.Extension switch { - using var stream = _.OpenRead(); - var md5 = stream.ComputeMD5(); - - // 文件名格式:Minecraft-Mod-Language-Modpack-[dashed-version]-[md5-hash].zip - // 如:Minecraft-Mod-Language-Modpack-1-16-Fabric-0000000000000000.zip - // hash的对象是文件内容,不包括文件名(当然) - // hash应该是全大写 - - var fileExtensionName = _.Extension; // 带点名称,应当为 ".zip" - var fileName = _.Name[0..^fileExtensionName.Length] - .RegulateFileName(); // 无后缀的文件名,应当已修正 - - // 选择性地加上该文件的md5值,以便生成patch - var tweakedName = fileName + "-" + md5; - - var destinationName = $"/var/www/html/files/{fileName + fileExtensionName}"; - var tweakedDestinationName = $"/var/www/html/files/{tweakedName + fileExtensionName}"; - - // 传递不带md5值的最新版本;会覆写已有文件 - scpClient.Upload(_.OpenRead(), destinationName); - Log.Information("向远程服务器写入文件:{0}", destinationName); - - //// 传递带md5值的历史版本,一般不会覆写已有文件 - //scpClient.Upload(_.OpenRead(), tweakedDestinationName); - //Log.Information("向远程服务器写入文件:{0}", tweakedDestinationName); - }); - - // 临时操作 在使用旧md5校验的程序弃用以后需要删除 - var md5List = artifactDirectory - .EnumerateFiles("*.md5", SearchOption.AllDirectories); - md5List.ToList() - .ForEach(_ => + ".zip" => "application/zip", + ".md5" => "text/plain", + _ => throw new ArgumentException($"Unexpected extension: {file.Extension}") + }, + fileStream, + timeout: null); + await client.Repository.Release.UploadAsset(result, newAsset); + Log.Information(" 上传文件:{0}", name); + } + } + + async static Task UpdateAutobuildAssets(GitHubClient client, IEnumerable<(string name, FileInfo file)> files) + { + var repoId = long.Parse(Environment.GetEnvironmentVariable("REPO_ID")!); + var release = await client.Repository.Release.Get(repoId, "autobuild"); + Log.Information(" 获取 autobuild Release"); + + var timestamp = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"); + var desc = new ReleaseUpdate() { - scpClient.Upload(_.OpenRead(), $"/var/www/html/files/{_.Name}"); - Log.Information("向远程服务器写入文件:{0}", $"/var/www/html/files/{_.Name}"); - }); + Body = $""" + ## 汉化资源包 Autobuild + + ### 最后更新时间 + + - {timestamp} + """ + }; + await client.Repository.Release.Edit(repoId, release.Id, desc); + Log.Information(" 更新 Release 简介:时间 {0}", timestamp); - Log.Information("资源包传递完毕"); - return 0; + var assets = release.Assets; + var lookup = assets.Select(_ => (_.Name, _)).ToDictionary(); + foreach (var (name, file) in files) + { + using var fileStream = file.OpenRead(); + + if (lookup.TryGetValue(name, out ReleaseAsset? asset)) + { + await client.Repository.Release.DeleteAsset(repoId, asset.Id); + Log.Information(" 删除旧文件:{0}", name); + } + var newAsset = new ReleaseAssetUpload( + name, + file.Extension switch + { + ".zip" => "application/zip", + ".md5" => "text/plain", + _ => throw new ArgumentException($"Unexpected extension: {file.Extension}") + }, + fileStream, + timeout: null); + await client.Repository.Release.UploadAsset(release, newAsset); + Log.Information(" 上传文件:{0}", name); + + } } public static string RegulateFileName(this string fileName) @@ -98,16 +175,5 @@ string Capitalize(string text) => string.Join("", text[0..1].ToUpper(), text[1..]); } - - /// - /// 计算给定流中全体内容的MD5值。 - /// - /// 被计算的流 - /// - public static string ComputeMD5(this Stream stream) - { - stream.Seek(0, SeekOrigin.Begin); // 确保文件流的位置被重置 - return Convert.ToHexString(MD5.Create().ComputeHash(stream)); - } } } diff --git a/src/Uploader/Uploader.csproj b/src/Uploader/Uploader.csproj index 4efac1109753..3138052add63 100644 --- a/src/Uploader/Uploader.csproj +++ b/src/Uploader/Uploader.csproj @@ -4,10 +4,12 @@ Exe net10.0 true + enable +