From e744b08c457b581f3e0f15a6c10f76437ec40cd9 Mon Sep 17 00:00:00 2001 From: Kuingsmile <96409857+Kuingsmile@users.noreply.github.com> Date: Tue, 6 Jan 2026 11:37:58 +0800 Subject: [PATCH] :package: Chore(custom): optimize file upload workflow --- .github/workflows/main.yml | 113 +++++++++++++++++++++-- package.json | 2 +- scripts/combine-yml.cjs | 148 ++++++++++++++++++++++++++++++ scripts/config.js | 24 ++++- scripts/generate-release-notes.sh | 2 +- yarn.lock | 2 +- 6 files changed, 275 insertions(+), 16 deletions(-) create mode 100644 scripts/combine-yml.cjs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6884d389..60af5a81 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -70,7 +70,7 @@ jobs: # step2: sign - name: Install the Apple certificates - if: contains(matrix.os, 'macos') && (github.event.inputs.build_os == matrix.platform || github.event.inputs.build_os == 'All') + if: contains(matrix.os_type, 'macos') && (github.event.inputs.build_os == matrix.platform || github.event.inputs.build_os == 'All') run: | CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH @@ -82,9 +82,10 @@ jobs: node-version: "22.x" - name: Install system deps - if: contains(matrix.os, 'linux') && (github.event.inputs.build_os == matrix.platform || github.event.inputs.build_os == 'All') + if: contains(matrix.os_type, 'linux') && (github.event.inputs.build_os == matrix.platform || github.event.inputs.build_os == 'All') run: | sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils libfuse2 + - name: Install FPM if: matrix.os == 'ubuntu-24.04-arm' && (github.event.inputs.build_os == matrix.platform || github.event.inputs.build_os == 'All') run: | @@ -114,10 +115,7 @@ jobs: run: | # Remove publish config if not publishing if [ "${{ github.event.inputs.publish_enabled }}" == "false" ]; then - echo "Publishing disabled, removing publish config..." jq 'del(.publish)' electron-builder.json > tmp.json && mv tmp.json electron-builder.json - echo "modified electron-builder.json:" - cat electron-builder.json fi # Configure architecture based on platform @@ -182,18 +180,25 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ matrix.os_type }}-${{ matrix.platform == 'Windows x64' && 'x64' || matrix.platform == 'Windows ARM64' && 'arm64' || matrix.platform == 'macOS x64' && 'x64' || matrix.platform == 'macOS ARM64' && 'arm64' || matrix.platform == 'Linux x64' && 'x64' || 'arm64' }}-${{ contains(matrix.os, 'windows') && 'executables' || 'packages' }} - path: dist_electron/* + path: | + dist_electron/*.exe + dist_electron/*.dmg + dist_electron/*.zip + dist_electron/*.AppImage + dist_electron/*.deb + dist_electron/*.rpm + dist_electron/*.snap retention-days: 30 - if-no-files-found: ${{ contains(matrix.os, 'linux') && 'ignore' || 'error' }} + if-no-files-found: 'ignore' - - name: Upload update manifests + - name: Upload yml artifacts if: github.event.inputs.build_os == matrix.platform || github.event.inputs.build_os == 'All' uses: actions/upload-artifact@v4 with: name: ${{ matrix.os_type }}-${{ matrix.platform == 'Windows x64' && 'x64' || matrix.platform == 'Windows ARM64' && 'arm64' || matrix.platform == 'macOS x64' && 'x64' || matrix.platform == 'macOS ARM64' && 'arm64' || matrix.platform == 'Linux x64' && 'x64' || 'arm64' }}-yml - path: dist_electron/github/* + path: dist_electron/**/*.yml retention-days: 30 - if-no-files-found: ${{ contains(matrix.os, 'linux') && 'ignore' || 'error' }} + if-no-files-found: 'ignore' - name: Get version if: (github.event.inputs.build_os == matrix.platform || github.event.inputs.build_os == 'All') && matrix.platform == 'Windows x64' && github.event.inputs.publish_enabled == true @@ -214,3 +219,91 @@ jobs: tag_name: v${{ steps.get_version.outputs.version }} name: Release v${{ steps.get_version.outputs.version }} token: ${{ secrets.GH_TOKEN }} + + combine-and-upload-yml: + name: Combine YML files and upload to S3 + needs: release + runs-on: ubuntu-latest + + steps: + - name: Check out git repository + uses: actions/checkout@v6 + + - name: Install Node.js + uses: actions/setup-node@v6 + with: + node-version: "22.x" + + - name: Install dependencies + shell: bash + run: | + yarn config set ignore-engines true + yarn + + - name: Download all yml artifacts + uses: actions/download-artifact@v4 + with: + pattern: '*-yml' + path: ./yml-artifacts + merge-multiple: false + + - name: List downloaded artifacts + run: | + echo "Downloaded artifacts structure:" + find ./yml-artifacts -type f -name "*.yml" + tree ./yml-artifacts + + - name: Combine and deduplicate yml files + run: | + node scripts/combine-yml.cjs ./yml-artifacts ./dist_electron/combined + echo "Combined YML files:" + ls -la ./dist_electron/combined/ + echo "Latest combined YML content:" + cat ./dist_electron/combined/latest.yml + echo "Latest macOS combined YML content:" + cat ./dist_electron/combined/latest-mac.yml + echo "Latest linux combined YML content:" + cat ./dist_electron/combined/latest-linux.yml + echo "Latest linux ARM64 combined YML content:" + cat ./dist_electron/combined/latest-linux-arm64.yml + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + if: github.event.inputs.publish_enabled == 'true' + with: + aws-access-key-id: ${{ secrets.R2_SECRET_ID }} + aws-secret-access-key: ${{ secrets.R2_SECRET_KEY }} + aws-region: us-east-1 + + - name: Upload combined yml files to S3/R2 + if: github.event.inputs.publish_enabled == 'true' + run: | + for file in ./dist_electron/combined/*.yml; do + if [ -f "$file" ]; then + filename=$(basename "$file") + echo "Uploading $filename to S3/R2..." + aws s3 cp "$file" "s3://piclist-dl/latest/$filename" \ + --endpoint-url "https://7ab4ed5cb1f4052a13d3b573876ecf33.r2.cloudflarestorage.com" \ + echo "Uploaded $filename successfully" + fi + done + echo "All yml files uploaded to S3/R2!" + + - name: Get version for release + id: get_version + run: | + VERSION=$(node -p "require('./package.json').version") + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Upload combined yml files to GitHub Release + if: github.event.inputs.publish_enabled == 'true' + uses: softprops/action-gh-release@v2 + with: + draft: true + tag_name: v${{ steps.get_version.outputs.version }} + files: | + ./dist_electron/combined/latest.yml + ./dist_electron/combined/latest-mac.yml + ./dist_electron/combined/latest-linux.yml + ./dist_electron/combined/latest-linux-arm64.yml + token: ${{ secrets.GH_TOKEN }} diff --git a/package.json b/package.json index c2f140e0..ec8e7d05 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,6 @@ "fs-extra": "^11.3.3", "got": "^14.6.6", "hpagent": "^1.2.0", - "js-yaml": "^4.1.1", "lodash-es": "^4.17.22", "marked": "^17.0.1", "mime": "^4.1.0", @@ -119,6 +118,7 @@ "eslint-plugin-unicorn": "^62.0.0", "eslint-plugin-vue": "^10.6.2", "globals": "^16.5.0", + "js-yaml": "^4.1.1", "highlight.js": "^11.11.1", "husky": "^9.1.7", "lucide-vue-next": "^0.562.0", diff --git a/scripts/combine-yml.cjs b/scripts/combine-yml.cjs new file mode 100644 index 00000000..203f5b86 --- /dev/null +++ b/scripts/combine-yml.cjs @@ -0,0 +1,148 @@ +const fs = require('fs') +const path = require('path') +const yaml = require('js-yaml') + +function removeDuplicates(files) { + if (!files || !Array.isArray(files)) return files + + const seen = new Set() + return files.filter(file => { + if (seen.has(file.url)) { + return false + } + seen.add(file.url) + return true + }) +} + +function combineYmlFiles(ymlFiles, outputPath) { + if (ymlFiles.length === 0) { + console.log(`No yml files found for ${outputPath}`) + return + } + + let combinedData = null + + for (const ymlFile of ymlFiles) { + const content = fs.readFileSync(ymlFile, 'utf8') + const data = yaml.load(content) + + if (!combinedData) { + combinedData = data + } else { + if (data.files && Array.isArray(data.files)) { + combinedData.files = [...(combinedData.files || []), ...data.files] + } + + if ( + data.releaseDate && + (!combinedData.releaseDate || new Date(data.releaseDate) > new Date(combinedData.releaseDate)) + ) { + combinedData.releaseDate = data.releaseDate + } + } + } + + if (combinedData && combinedData.files) { + combinedData.files = removeDuplicates(combinedData.files) + } + + const ymlContent = yaml.dump(combinedData, { lineWidth: -1 }) + fs.writeFileSync(outputPath, ymlContent, 'utf8') + console.log(`Created ${outputPath} with ${combinedData?.files?.length || 0} file entries`) +} + +function processSingleYmlFile(ymlFile, outputPath) { + if (!fs.existsSync(ymlFile)) { + console.log(`File not found: ${ymlFile}`) + return + } + + const content = fs.readFileSync(ymlFile, 'utf8') + const data = yaml.load(content) + + if (data && data.files) { + data.files = removeDuplicates(data.files) + } + + const ymlContent = yaml.dump(data, { lineWidth: -1 }) + fs.writeFileSync(outputPath, ymlContent, 'utf8') + console.log(`Processed ${outputPath} with ${data?.files?.length || 0} file entries`) +} + +function findYmlInFolder(basePath, folderPattern, ymlFileName) { + const folders = fs.existsSync(basePath) + ? fs.readdirSync(basePath).filter(f => { + const fullPath = path.join(basePath, f) + return fs.statSync(fullPath).isDirectory() && f.includes(folderPattern) + }) + : [] + + const ymlFiles = [] + for (const folder of folders) { + const ymlPath = path.join(basePath, folder, ymlFileName) + if (fs.existsSync(ymlPath)) { + ymlFiles.push(ymlPath) + } + } + return ymlFiles +} + +function main() { + const distPath = process.argv[2] || './dist_electron' + const outputDir = process.argv[3] || './dist_electron/combined' + + console.log(`Processing yml files from: ${distPath}`) + console.log(`Output directory: ${outputDir}`) + + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }) + } + + const windowsX64Ymls = findYmlInFolder(distPath, 'windows-x64-yml', 'latest.yml') + const windowsArm64Ymls = findYmlInFolder(distPath, 'windows-arm64-yml', 'latest.yml') + const macX64Ymls = findYmlInFolder(distPath, 'macos-x64-yml', 'latest-mac.yml') + const macArm64Ymls = findYmlInFolder(distPath, 'macos-arm64-yml', 'latest-mac.yml') + const linuxX64Ymls = findYmlInFolder(distPath, 'linux-x64-yml', 'latest-linux.yml') + const linuxArm64Ymls = findYmlInFolder(distPath, 'linux-arm64-yml', 'latest-linux-arm64.yml') + + console.log('\nFound yml files:') + console.log(`Windows x64: ${windowsX64Ymls.map(f => path.basename(path.dirname(f))).join(', ')}`) + console.log(`Windows ARM64: ${windowsArm64Ymls.map(f => path.basename(path.dirname(f))).join(', ')}`) + console.log(`macOS x64: ${macX64Ymls.map(f => path.basename(path.dirname(f))).join(', ')}`) + console.log(`macOS ARM64: ${macArm64Ymls.map(f => path.basename(path.dirname(f))).join(', ')}`) + console.log(`Linux x64: ${linuxX64Ymls.map(f => path.basename(path.dirname(f))).join(', ')}`) + console.log(`Linux ARM64: ${linuxArm64Ymls.map(f => path.basename(path.dirname(f))).join(', ')}`) + + const windowsYmls = [...windowsX64Ymls, ...windowsArm64Ymls] + if (windowsYmls.length > 0) { + console.log(`\nCombining ${windowsYmls.length} Windows yml files...`) + combineYmlFiles(windowsYmls, path.join(outputDir, 'latest.yml')) + } else { + console.log('\nNo Windows yml files found to combine') + } + + const macYmls = [...macX64Ymls, ...macArm64Ymls] + if (macYmls.length > 0) { + console.log(`\nCombining ${macYmls.length} macOS yml files...`) + combineYmlFiles(macYmls, path.join(outputDir, 'latest-mac.yml')) + } else { + console.log('\nNo macOS yml files found to combine') + } + if (linuxX64Ymls.length > 0) { + console.log('\nProcessing Linux x64 yml file (deduplicate only)...') + processSingleYmlFile(linuxX64Ymls[0], path.join(outputDir, 'latest-linux.yml')) + } else { + console.log('\nNo Linux x64 yml files found') + } + if (linuxArm64Ymls.length > 0) { + console.log('\nProcessing Linux ARM64 yml file (deduplicate only)...') + processSingleYmlFile(linuxArm64Ymls[0], path.join(outputDir, 'latest-linux-arm64.yml')) + } else { + console.log('\nNo Linux ARM64 yml files found') + } + + console.log('\nYML combination and deduplication complete!') +} + +main() diff --git a/scripts/config.js b/scripts/config.js index ed7c50ab..26301a32 100644 --- a/scripts/config.js +++ b/scripts/config.js @@ -20,13 +20,31 @@ const linux = [ { appNameWithPrefix: 'PicList-', ext: '.AppImage', - arch: '', + arch: '-x86_64', 'version-file': 'latest-linux.yml', }, { - appNameWithPrefix: 'piclist_', + appNameWithPrefix: 'PicList-', + ext: '.AppImage', + arch: '-arm64', + 'version-file': 'latest-linux.yml', + }, + { + appNameWithPrefix: 'PicList-', ext: '.snap', - arch: '_amd64', + arch: '-amd64', + 'version-file': 'latest-linux.yml', + }, + { + appNameWithPrefix: 'PicList-', + ext: '.deb', + arch: '-amd64', + 'version-file': 'latest-linux.yml', + }, + { + appNameWithPrefix: 'PicList-', + ext: '.deb', + arch: '-arm64', 'version-file': 'latest-linux.yml', }, ] diff --git a/scripts/generate-release-notes.sh b/scripts/generate-release-notes.sh index 298cc664..9e7d2ac3 100644 --- a/scripts/generate-release-notes.sh +++ b/scripts/generate-release-notes.sh @@ -37,7 +37,7 @@ cat > "$OUTPUT_FILE" << EOF ### Linux - AppImage: - [**x64**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/PicList-${VERSION}-x64.AppImage) | + [**x64**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/PicList-${VERSION}-x86_64.AppImage) | [**ARM64**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/PicList-${VERSION}-arm64.AppImage) - deb: [**x64**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/Piclist-${VERSION}-amd64.deb) | diff --git a/yarn.lock b/yarn.lock index ce7773cd..8eebb54a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8044,7 +8044,7 @@ got@^11.7.0, got@^11.8.5: p-cancelable "^2.0.0" responselike "^2.0.0" -got@^14.6.5: +got@^14.6.6: version "14.6.6" resolved "https://registry.yarnpkg.com/got/-/got-14.6.6.tgz#5adf7f576f89de8e291fd782e8b277f9bcc8e6c0" integrity sha512-QLV1qeYSo5l13mQzWgP/y0LbMr5Plr5fJilgAIwgnwseproEbtNym8xpLsDzeZ6MWXgNE6kdWGBjdh3zT/Qerg==