From 78ccf30737ecdc322ed3ddb6e55e92b00c2e9a4e Mon Sep 17 00:00:00 2001 From: Kuingsmile <96409857+Kuingsmile@users.noreply.github.com> Date: Fri, 16 Jan 2026 11:03:05 +0800 Subject: [PATCH] :package: Chore(custom): update publish workflow --- .github/ISSUE_TEMPLATE/bug_report.yml | 3 +- .github/ISSUE_TEMPLATE/feature_request.yml | 5 +- .github/workflows/buid_arch.yml | 141 ++++++++++++++++----- electron-builder.json | 16 --- package.json | 7 +- scripts/combine-yml.cjs | 43 ++++--- scripts/config.js | 103 ++++++++++++--- scripts/gen-sha256.js | 137 -------------------- scripts/generate-release-notes.sh | 3 + scripts/link.js | 40 ++++-- scripts/upload-to-s3.js | 99 +++++++++++++++ src/main/lifeCycle/autoUpdater.ts | 26 ++-- 12 files changed, 363 insertions(+), 260 deletions(-) delete mode 100644 scripts/gen-sha256.js create mode 100644 scripts/upload-to-s3.js diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 4e039a54..1338a27a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -41,10 +41,11 @@ body: label: 系统信息 | System Information options: - Windows + - Win(arm64) - Mac - Mac(arm64) - Linux - - All + - Linux(arm64) validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 7323ef34..ee4e6dd7 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -41,10 +41,11 @@ body: label: 系统信息 | System Information options: - Windows + - Win(arm64) - Mac - Mac(arm64) - Linux - - All + - Linux(arm64) validations: required: true - type: textarea @@ -58,4 +59,4 @@ body: attributes: value: | 最后,喜欢 PicList 的话不妨给它点个 star~ - Finally, if you like PicList, give it a star~ \ No newline at end of file + Finally, if you like PicList, give it a star~ diff --git a/.github/workflows/buid_arch.yml b/.github/workflows/buid_arch.yml index a4cdfcbb..11ed44c3 100644 --- a/.github/workflows/buid_arch.yml +++ b/.github/workflows/buid_arch.yml @@ -3,6 +3,11 @@ name: Build with architecture on: workflow_dispatch: inputs: + tag_name: + description: 'Tag name to build' + required: false + default: 'preview' + type: string publish_enabled: description: 'Publish artifacts after build?' required: true @@ -34,7 +39,7 @@ on: - ubuntu-24.04-arm-arm64-rpm - All permissions: - contents: read + contents: write env: ELECTRON_OUTPUT_PATH: ./dist_electron @@ -154,10 +159,7 @@ jobs: yarn global add xvfb-maybe if [[ "${{ matrix.format }}" == "zip" || "${{ matrix.format }}" == "7z" ]]; then echo "Target format is ${{ matrix.format }}, downloading all resources..." - yarn run prepare - else - echo "Target format is other, downloading themes only..." - yarn run prepare:themes + yarn run prepare:7za fi echo "Checking resources directory:" ls -alh ./resources/theme || echo "Theme directory not found" @@ -168,22 +170,6 @@ jobs: echo "❌ 7za.exe does not exist" fi - - name: Generate release notes - if: github.event.inputs.build_os == matrix.filter || github.event.inputs.build_os == 'All' - shell: bash - run: | - chmod +x ./scripts/generate-release-notes.sh - ./scripts/generate-release-notes.sh - - - name: Configure electron-builder.json - if: github.event.inputs.build_os == matrix.filter || github.event.inputs.build_os == 'All' - shell: bash - run: | - # Remove publish config if not publishing - if [ "${{ github.event.inputs.publish_enabled }}" == "false" ]; then - jq 'del(.publish)' electron-builder.json > tmp.json && mv tmp.json electron-builder.json - fi - - name: Build & release app if: github.event.inputs.build_os == matrix.filter || github.event.inputs.build_os == 'All' shell: bash @@ -192,17 +178,12 @@ jobs: export CSC_IDENTITY_AUTO_DISCOVERY=false unset CSC_LINK WIN_CSC_LINK CSC_KEY_PASSWORD fi - PUBLISH_ARG="never" - if [ "${{ github.event.inputs.publish_enabled }}" == "true" ]; then - PUBLISH_ARG="always" - fi - echo "Publishing argument: $PUBLISH_ARG" if [[ "${{ matrix.os }}" == windows* ]]; then - yarn run build:win ${{ matrix.format}} --${{ matrix.arch }} --publish $PUBLISH_ARG + yarn run build:win ${{ matrix.format}} --${{ matrix.arch }} --publish never elif [[ "${{ matrix.os }}" == macos* ]]; then - yarn run build:mac ${{ matrix.format}} --${{ matrix.arch }} --publish $PUBLISH_ARG + yarn run build:mac default --${{ matrix.arch }} --publish never elif [[ "${{ matrix.os }}" == ubuntu* ]]; then - yarn run build:linux ${{ matrix.format}} --${{ matrix.arch }} --publish $PUBLISH_ARG + yarn run build:linux ${{ matrix.format}} --${{ matrix.arch }} --publish never else echo "Unsupported OS: ${{ matrix.os }}" exit 1 @@ -210,11 +191,6 @@ jobs: env: USE_SYSTEM_FPM: ${{ matrix.os == 'ubuntu-24.04-arm' && 'true' || 'false' }} GH_TOKEN: ${{ secrets.GH_TOKEN }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - R2_SECRET_ID: ${{ secrets.R2_SECRET_ID }} - R2_SECRET_KEY: ${{ secrets.R2_SECRET_KEY }} - R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} ELECTRON_SKIP_NOTARIZATION: ${{ secrets.ELECTRON_SKIP_NOTARIZATION }} XCODE_APP_LOADER_EMAIL: ${{ secrets.XCODE_APP_LOADER_EMAIL }} XCODE_APP_LOADER_PASSWORD: ${{ secrets.XCODE_APP_LOADER_PASSWORD }} @@ -239,6 +215,7 @@ jobs: dist_electron/*.deb dist_electron/*.rpm dist_electron/*.snap + dist_electron/*.blockmap retention-days: 30 if-no-files-found: 'ignore' @@ -250,3 +227,99 @@ jobs: path: dist_electron/**/*.yml retention-days: 30 if-no-files-found: 'ignore' + + combine-and-upload: + name: Combine and Upload Release + needs: build + 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 + rm -rf node_modules && yarn install + + - name: Generate Release Notes + run: | + chmod +x ./scripts/generate-release-notes.sh + ./scripts/generate-release-notes.sh + + - name: Download All Artifacts + uses: actions/download-artifact@v4 + with: + pattern: '*-artifacts' + path: ./artifacts + + - name: Download 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 + tree ./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: Publish GitHub Dev Release + uses: softprops/action-gh-release@v2 + continue-on-error: true + with: + token: ${{ secrets.GH_TOKEN }} + tag_name: ${{ github.event.inputs.tag_name }} + draft: true + prerelease: ${{ github.event.inputs.publish_enabled == 'false' || github.event.inputs.tag_name == 'preview' }} + body_path: ./release-notes.md + name: ${{ github.event.inputs.tag_name }} + files: | + !artifacts/**/*-unpacked/** + artifacts/**/*.exe + artifacts/**/*.dmg + artifacts/**/*.zip + artifacts/**/*.7z + artifacts/**/*.AppImage + artifacts/**/*.deb + artifacts/**/*.snap + artifacts/**/*.rpm + ./dist_electron/combined/*.yml + artifacts/**/*.tar.gz + + - name: Upload to S3 + run: | + if [ "${{ github.event.inputs.publish_enabled }}" == "false" ]; then + echo "Publishing is disabled. Skipping upload to S3." + node scripts/upload-to-s3.js ./artifacts ./dist_electron/combined + exit 0 + fi + node scripts/upload-to-s3.js ./artifacts ./dist_electron/combined false + env: + R2_SECRET_ID: ${{ secrets.R2_SECRET_ID }} + R2_SECRET_KEY: ${{ secrets.R2_SECRET_KEY }} + R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} + diff --git a/electron-builder.json b/electron-builder.json index 76e5d599..e4435f93 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -14,22 +14,6 @@ "resources/**" ], "files": ["out/**/*", "resources/**", "package.json", "!**/node_modules/typescript{,/**}"], - "publish": [ - { - "provider": "s3", - "bucket": "piclist-dl", - "region": "auto", - "acl": "private", - "endpoint": "https://7ab4ed5cb1f4052a13d3b573876ecf33.r2.cloudflarestorage.com", - "path": "/latest" - }, - { - "provider": "github", - "owner": "Kuingsmile", - "repo": "PicList", - "releaseType": "draft" - } - ], "dmg": { "sign": false, "contents": [ diff --git a/package.json b/package.json index e490ec84..0f1d859a 100644 --- a/package.json +++ b/package.json @@ -35,12 +35,9 @@ "postinstall": "electron-builder install-app-deps", "postuninstall": "electron-builder install-app-deps", "prebuild": "electron-vite build", - "prepare": "node ./scripts/prepare.js --all", - "prepare:7zaip": "node ./scripts/prepare.js --type=7zip", - "prepare:themes": "node ./scripts/prepare.js --type=themes", + "prepare": "node ./scripts/prepare.js --type=themes", + "prepare:7za": "node ./scripts/prepare.js --type=7zip", "preview": "electron-vite preview", - "release": "electron-vite build && electron-builder", - "sha256": "node ./scripts/gen-sha256.js", "test": "vitest", "winget": "node ./scripts/auto-winget.js" }, diff --git a/scripts/combine-yml.cjs b/scripts/combine-yml.cjs index 203f5b86..9157ea2a 100644 --- a/scripts/combine-yml.cjs +++ b/scripts/combine-yml.cjs @@ -99,20 +99,20 @@ function main() { 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 windowsX64Ymls = findYmlInFolder(distPath, 'windows-latest-x64-nsis-yml', 'latest.yml') + const windowsArm64Ymls = findYmlInFolder(distPath, 'windows-11-arm-arm64-nsis-yml', 'latest.yml') + const macX64Ymls = findYmlInFolder(distPath, 'macos-15-intel-x64-dmg-yml', 'latest-mac.yml') + const macArm64Ymls = findYmlInFolder(distPath, 'macos-latest-arm64-dmg-yml', 'latest-mac.yml') + const linuxX64AppImageYmls = findYmlInFolder(distPath, 'ubuntu-latest-x64-AppImage-yml', 'latest-linux.yml') + const linuxArm64AppImageYmls = findYmlInFolder( + distPath, + 'ubuntu-24.04-arm-arm64-AppImage-yml', + 'latest-linux-arm64.yml', + ) + const linuxX64DebYmls = findYmlInFolder(distPath, 'ubuntu-latest-x64-deb-yml', 'latest-linux.yml') + const linuxArm64DebYmls = findYmlInFolder(distPath, 'ubuntu-24.04-arm-arm64-deb-yml', 'latest-linux-arm64.yml') + const linuxX64RpmYmls = findYmlInFolder(distPath, 'ubuntu-latest-x64-rpm-yml', 'latest-linux.yml') + const linuxArm64RpmYmls = findYmlInFolder(distPath, 'ubuntu-24.04-arm-arm64-rpm-yml', 'latest-linux-arm64.yml') const windowsYmls = [...windowsX64Ymls, ...windowsArm64Ymls] if (windowsYmls.length > 0) { @@ -129,17 +129,20 @@ function main() { } else { console.log('\nNo macOS yml files found to combine') } + const linuxX64Ymls = [...linuxX64AppImageYmls, ...linuxX64DebYmls, ...linuxX64RpmYmls] if (linuxX64Ymls.length > 0) { - console.log('\nProcessing Linux x64 yml file (deduplicate only)...') - processSingleYmlFile(linuxX64Ymls[0], path.join(outputDir, 'latest-linux.yml')) + console.log(`\nCombining ${linuxX64Ymls.length} Linux x64 yml files...`) + combineYmlFiles(linuxX64Ymls, path.join(outputDir, 'latest-linux.yml')) } else { - console.log('\nNo Linux x64 yml files found') + console.log('\nNo Linux x64 yml files found to combine') } + + const linuxArm64Ymls = [...linuxArm64AppImageYmls, ...linuxArm64DebYmls, ...linuxArm64RpmYmls] if (linuxArm64Ymls.length > 0) { - console.log('\nProcessing Linux ARM64 yml file (deduplicate only)...') - processSingleYmlFile(linuxArm64Ymls[0], path.join(outputDir, 'latest-linux-arm64.yml')) + console.log(`\nCombining ${linuxArm64Ymls.length} Linux arm64 yml files...`) + combineYmlFiles(linuxArm64Ymls, path.join(outputDir, 'latest-linux-arm64.yml')) } else { - console.log('\nNo Linux ARM64 yml files found') + console.log('\nNo Linux arm64 yml files found to combine') } console.log('\nYML combination and deduplication complete!') diff --git a/scripts/config.js b/scripts/config.js index 26301a32..85597ffc 100644 --- a/scripts/config.js +++ b/scripts/config.js @@ -1,4 +1,8 @@ // different platform has different format +import pkg from '../package.json' with { type: 'json' } +import path from 'path' + +const version = pkg.version // macos const darwin = [ @@ -7,12 +11,28 @@ const darwin = [ ext: '.dmg', arch: '-arm64', 'version-file': 'latest-mac.yml', + path: 'macos-latest-arm64-dmg-artifacts', }, { appNameWithPrefix: 'PicList-', ext: '.dmg', arch: '-x64', 'version-file': 'latest-mac.yml', + path: 'macos-15-intel-x64-dmg-artifacts', + }, + { + appNameWithPrefix: 'PicList-', + ext: '.zip', + arch: '-arm64', + 'version-file': 'latest-mac.yml', + path: 'macos-latest-arm64-dmg-artifacts', + }, + { + appNameWithPrefix: 'PicList-', + ext: '.zip', + arch: '-x64', + 'version-file': 'latest-mac.yml', + path: 'macos-15-intel-x64-dmg-artifacts', }, ] @@ -22,61 +42,106 @@ const linux = [ ext: '.AppImage', arch: '-x86_64', 'version-file': 'latest-linux.yml', + path: 'ubuntu-latest-x64-AppImage-artifacts', }, { appNameWithPrefix: 'PicList-', ext: '.AppImage', arch: '-arm64', - 'version-file': 'latest-linux.yml', - }, - { - appNameWithPrefix: 'PicList-', - ext: '.snap', - arch: '-amd64', - 'version-file': 'latest-linux.yml', + 'version-file': 'latest-linux-arm64.yml', + path: 'ubuntu-24.04-arm-arm64-AppImage-artifacts', }, { appNameWithPrefix: 'PicList-', ext: '.deb', arch: '-amd64', 'version-file': 'latest-linux.yml', + path: 'ubuntu-latest-x64-deb-artifacts', }, { appNameWithPrefix: 'PicList-', ext: '.deb', arch: '-arm64', + 'version-file': 'latest-linux-arm64.yml', + path: 'ubuntu-24.04-arm-arm64-deb-artifacts', + }, + { + appNameWithPrefix: 'PicList-', + ext: '.rpm', + arch: '-x86_64', 'version-file': 'latest-linux.yml', + path: 'ubuntu-latest-x64-rpm-artifacts', + }, + { + appNameWithPrefix: 'PicList-', + ext: '.rpm', + arch: '-aarch64', + 'version-file': 'latest-linux-arm64.yml', + path: 'ubuntu-24.04-arm-arm64-rpm-artifacts', + }, + { + appNameWithPrefix: 'PicList-', + ext: '.snap', + arch: '-amd64', + path: 'ubuntu-latest-x64-snap-artifacts', }, ] // windows const win32 = [ - { - appNameWithPrefix: 'PicList-Setup-', - ext: '.exe', - arch: '-ia32', - 'version-file': 'latest.yml', - }, { appNameWithPrefix: 'PicList-Setup-', ext: '.exe', arch: '-x64', 'version-file': 'latest.yml', - }, - { - appNameWithPrefix: 'PicList-Setup-', - ext: '.exe', - arch: '', // 32 & 64 - 'version-file': 'latest.yml', + path: 'windows-latest-x64-nsis-artifacts', }, { appNameWithPrefix: 'PicList-Setup-', ext: '.exe', arch: '-arm64', 'version-file': 'latest.yml', + path: 'windows-11-arm-arm64-nsis-artifacts', + }, + { + appNameWithPrefix: 'PicList-Setup-', + ext: '.zip', + arch: '-x64-portable', + path: 'windows-latest-x64-zip-artifacts', + }, + { + appNameWithPrefix: 'PicList-Setup-', + ext: '.zip', + arch: '-arm64-portable', + path: 'windows-11-arm-arm64-zip-artifacts', + }, + { + appNameWithPrefix: 'PicList-Setup-', + ext: '.7z', + arch: '-x64-portable', + path: 'windows-latest-x64-7z-artifacts', + }, + { + appNameWithPrefix: 'PicList-Setup-', + ext: '.7z', + arch: '-arm64-portable', + path: 'windows-11-arm-arm64-7z-artifacts', }, ] +export const generateFileName = (platformConfig, version) => { + return `${platformConfig.appNameWithPrefix}${version}${platformConfig.arch}${platformConfig.ext}` +} + +export const fileList = [...darwin, ...linux, ...win32].map(platformConfig => { + const fileName = generateFileName(platformConfig, version) + return { + name: fileName, + path: path.join(platformConfig.path, fileName), + blockMapPath: path.join(platformConfig.path, `${fileName}.blockmap`), + } +}) + export default { darwin, linux, diff --git a/scripts/gen-sha256.js b/scripts/gen-sha256.js deleted file mode 100644 index ac0296f3..00000000 --- a/scripts/gen-sha256.js +++ /dev/null @@ -1,137 +0,0 @@ -import crypto from 'node:crypto' -import path from 'node:path' - -import axios from 'axios' -import fs from 'fs-extra' - -import pkg from '../package.json' with { type: 'json' } -const version = process.argv[2] || pkg.version - -// Configuration -const BASE_URL = `https://github.com/Kuingsmile/PicList/releases/download/v${version}` -const DOWNLOAD_DIR = path.join('R:\\Downloads') -// File information -const files = [ - { - name: 'PicList-x64.dmg', - url: `${BASE_URL}/PicList-${version}-x64.dmg`, - }, - { - name: 'PicList-arm64.dmg', - url: `${BASE_URL}/PicList-${version}-arm64.dmg`, - }, -] - -/** - * Create progress bar string - */ -function getProgressBar(current, total, length = 20) { - const progress = Math.round((current / total) * length) - const percentage = Math.round((current / total) * 100) - const bar = '█'.repeat(progress) + '░'.repeat(length - progress) - return `[${bar}] ${percentage}% (${formatBytes(current)}/${formatBytes(total)})` -} - -/** - * Format bytes to human-readable format - */ -function formatBytes(bytes, decimals = 2) { - if (bytes === 0) return '0 Bytes' - const k = 1024 - const sizes = ['Bytes', 'KB', 'MB', 'GB'] - const i = Math.floor(Math.log(bytes) / Math.log(k)) - return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i] -} - -/** - * Download file and calculate SHA256 hash - */ -async function downloadAndHash(fileInfo) { - const { url, name } = fileInfo - const filePath = path.join(DOWNLOAD_DIR, name) - - console.log(`\nStarting download: ${name} from ${url}`) - - try { - const response = await axios({ - method: 'get', - url, - responseType: 'stream', - }) - - const writer = fs.createWriteStream(filePath) - response.data.pipe(writer) - - const hash = crypto.createHash('sha256') - const progressTotal = parseInt(response.headers['content-length'], 10) - let progressCurrent = 0 - - response.data.on('data', chunk => { - hash.update(chunk) - progressCurrent += chunk.length - process.stdout.write(`\r${name}: ${getProgressBar(progressCurrent, progressTotal)}`) - }) - - await new Promise((resolve, reject) => { - writer.on('finish', resolve) - writer.on('error', reject) - response.data.on('error', reject) - }) - - const hashValue = hash.digest('hex') - console.log(`\n✅ ${name} SHA256: ${hashValue}`) - - // Write hash to a file for reference - await fs.writeFile(`${filePath}.sha256`, hashValue) - console.log(`Hash saved to ${filePath}.sha256`) - - if (process.argv.includes('--keep-files')) { - console.log(`Keeping file: ${filePath}`) - } else { - await fs.remove(filePath) - console.log(`Deleted: ${filePath}`) - } - - return { name, hash: hashValue } - } catch (err) { - console.error(`\n❌ Error processing ${name}: ${err.message}`) - throw err - } -} - -/** - * Main function - */ -async function main() { - console.log(`Generating SHA256 hashes for PicList v${version}`) - console.log(`Download directory: ${DOWNLOAD_DIR}`) - - try { - // Ensure download directory exists - await fs.ensureDir(DOWNLOAD_DIR) - - // Start all downloads concurrently - const results = await Promise.allSettled(files.map(downloadAndHash)) - - // Output summary - console.log('\n===== SUMMARY =====') - results.forEach((result, index) => { - const fileName = files[index].name - if (result.status === 'fulfilled') { - console.log(`✅ ${fileName}: ${result.value.hash}`) - } else { - console.log(`❌ ${fileName}: Failed - ${result.reason.message}`) - } - }) - - // Check if all downloads were successful - if (results.some(r => r.status === 'rejected')) { - process.exit(1) - } - } catch (err) { - console.error(`\nCritical error: ${err.message}`) - process.exit(1) - } -} - -main() diff --git a/scripts/generate-release-notes.sh b/scripts/generate-release-notes.sh index f3ce8208..ae687b91 100644 --- a/scripts/generate-release-notes.sh +++ b/scripts/generate-release-notes.sh @@ -36,6 +36,7 @@ cat > "$OUTPUT_FILE" << EOF ### macOS +- DMG: [**ARM64 (M1-M6)**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/PicList-${VERSION}-arm64.dmg) | [**Intel**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/PicList-${VERSION}-x64.dmg) ### Linux @@ -44,6 +45,8 @@ cat > "$OUTPUT_FILE" << EOF [**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) | [**ARM64**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/Piclist-${VERSION}-arm64.deb) +- rpm: + [**x64**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/Piclist-${VERSION}-x86_64.rpm) | [**ARM64**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/Piclist-${VERSION}-aarch64.rpm) - snap: [**x64**](https://github.com/Kuingsmile/PicList/releases/download/v${VERSION}/Piclist-${VERSION}-amd64.snap) diff --git a/scripts/link.js b/scripts/link.js index fb315cf6..333e76b1 100644 --- a/scripts/link.js +++ b/scripts/link.js @@ -1,6 +1,7 @@ import pkg from '../package.json' with { type: 'json' } + const version = pkg.version -// TODO: use the same name format + const generateURL = (platform, ext, prefix = 'PicList-') => { return `https://release.piclist.cn/latest/${prefix}${version}${platform}${ext}` } @@ -9,18 +10,37 @@ const template = ` ### 加速下载地址 #### MacOS -[PicList-${version}-arm64.dmg](${generateURL('-arm64', '.dmg', 'PicList-')}) -[PicList-${version}-x64.dmg](${generateURL('-x64', '.dmg', 'PicList-')}) -[PicList-${version}-universal.dmg](${generateURL('-universal', '.dmg', 'PicList-')}) + +- DMG: + +[PicList-${version}-x64.dmg](${generateURL('-x64', '.dmg', 'PicList-')}) | [PicList-${version}-arm64.dmg](${generateURL('-arm64', '.dmg', 'PicList-')}) #### Windows -[PicList-Setup-${version}-ia32.exe](${generateURL('-ia32', '.exe', 'PicList-Setup-')}) -[PicList-Setup-${version}-x64.exe](${generateURL('-x64', '.exe', 'PicList-Setup-')}) -[PicList-Setup-${version}-arm64.exe](${generateURL('-arm64', '.exe', 'PicList-Setup-')}) -[PicList-Setup-${version}.exe](${generateURL('', '.exe', 'PicList-Setup-')}) + +- Installer: +[PicList-Setup-${version}-x64.exe](${generateURL('-x64', '.exe', 'PicList-Setup-')}) | [PicList-Setup-${version}-arm64.exe](${generateURL('-arm64', '.exe', 'PicList-Setup-')}) + +- Portable: + +[PicList-Setup-${version}-x64-portable.zip](${generateURL('-x64-portable', '.zip', 'PicList-Setup-')}) | [PicList-Setup-${version}-arm64-portable.zip](${generateURL('-arm64-portable', '.zip', 'PicList-Setup-')}) | [PicList-Setup-${version}-x64-portable.7z](${generateURL('-x64-portable', '.7z', 'PicList-Setup-')}) | [PicList-Setup-${version}-arm64-portable.7z](${generateURL('-arm64-portable', '.7z', 'PicList-Setup-')}) #### Linux -[PicList-${version}.AppImage](${generateURL('', '.AppImage', 'PicList-')}) -[piclist_${version}_amd64.snap](${generateURL('_amd64', '.snap', 'piclist_')})` + +- AppImage: + +[PicList-${version}-x86_64.AppImage](${generateURL('-x86_64', '.AppImage', 'PicList-')}) | [PicList-${version}-arm64.AppImage](${generateURL('-arm64', '.AppImage', 'PicList-')}) + +- Deb: + +[PicList-${version}-amd64.deb](${generateURL('-amd64', '.deb', 'PicList-')}) | [PicList-${version}-arm64.deb](${generateURL('-arm64', '.deb', 'PicList-')}) + +- Rpm: + +[PicList-${version}-x86_64.rpm](${generateURL('-x86_64', '.rpm', 'PicList-')}) | [PicList-${version}-aarch64.rpm](${generateURL('-aarch64', '.rpm', 'PicList-')}) + +- Snap: + +[PicList-${version}-amd64.snap](${generateURL('-amd64', '.snap', 'PicList-')}) +` console.log(template) diff --git a/scripts/upload-to-s3.js b/scripts/upload-to-s3.js new file mode 100644 index 00000000..711af4be --- /dev/null +++ b/scripts/upload-to-s3.js @@ -0,0 +1,99 @@ +import dotenv from 'dotenv' +import { fileList } from './config.js' +import fs from 'fs-extra' +import mime from 'mime' +import path from 'node:path' +import { S3Client } from '@aws-sdk/client-s3' +import { Upload } from '@aws-sdk/lib-storage' + +dotenv.config() + +const bucket = 'piclist-dl' +const folder = `latest/` +const R2_SECRET_ID = process.env.R2_SECRET_ID +const R2_SECRET_KEY = process.env.R2_SECRET_KEY +const R2_ACCOUNT_ID = process.env.R2_ACCOUNT_ID + +console.log('fileList to upload:', JSON.stringify(fileList, null, 2)) + +const ymlFileList = ['latest-mac.yml', 'latest.yml', 'latest-linux.yml', 'latest-linux-arm64.yml'] + +const args = process.argv.slice(2) +const exePath = args[0] || './artifacts' +const ymlPath = args[1] || './dist_electron/combined' +const idDev = args[2] || true + +const S3Options = { + credentials: { + accessKeyId: R2_SECRET_ID, + secretAccessKey: R2_SECRET_KEY, + }, + endpoint: `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, + sslEnabled: true, + region: 'auto', +} + +const uploadFile = async filePath => { + try { + console.log('[INFO]: uploading file:', filePath) + const client = new S3Client(S3Options) + if (fs.existsSync(filePath)) { + const uploadDistToS3 = new Upload({ + client, + params: { + Bucket: bucket, + Key: `${folder}${path.basename(filePath)}`, + Body: fs.createReadStream(filePath), + ContentType: path.basename(filePath).endsWith('.yml') + ? mime.getType(path.basename(filePath)) + : 'application/octet-stream', + }, + }) + uploadDistToS3.on('httpUploadProgress', progress => { + console.log(`[INFO]: ${path.basename(filePath)} - ${progress.loaded}/${progress.total}`) + }) + if (idDev) { + console.log('[DEV]: upload params:', { + Bucket: bucket, + Key: `${folder}${path.basename(filePath)}`, + Body: '<>', + ContentType: path.basename(filePath).endsWith('.yml') + ? mime.getType(path.basename(filePath)) + : 'application/octet-stream', + }) + } else { + await uploadDistToS3.done() + } + } else { + console.warn('[Warn] File not found:', filePath) + } + } catch (e) { + console.error('[ERROR]: upload failed:', e) + } +} + +const upload = async () => { + console.log('[INFO]: Starting upload to S3...') + console.log('[INFO]: upload exePath:', exePath) + for (const file of fileList) { + const installerPath = path.join(exePath, file.path) + await uploadFile(installerPath) + } + + console.log('[INFO]: upload blockmap files...') + for (const file of fileList) { + const installerPath = path.join(exePath, file.blockMapPath) + await uploadFile(installerPath) + } + + for (const ymlFile of ymlFileList) { + const ymlFilePath = path.join(ymlPath, ymlFile) + await uploadFile(ymlFilePath) + } +} + +const main = async () => { + await upload() +} + +main() diff --git a/src/main/lifeCycle/autoUpdater.ts b/src/main/lifeCycle/autoUpdater.ts index 683ea9d9..3754fb5f 100644 --- a/src/main/lifeCycle/autoUpdater.ts +++ b/src/main/lifeCycle/autoUpdater.ts @@ -116,13 +116,9 @@ export async function setupAutoUpdater() { updater.autoUpdater.forceDevUpdateConfig = true updater.autoUpdater.autoDownload = false - updater.autoUpdater.on('update-available', updateAvailableHandler) - updater.autoUpdater.on('download-progress', progressHandler) - updater.autoUpdater.on('update-downloaded', downloadedHandler) - updater.autoUpdater.on('error', err => { logger.error(err) }) @@ -130,18 +126,16 @@ export async function setupAutoUpdater() { } export async function checkUpdateAndNotify(): Promise { - if (isPortable()) { - const url = 'https://release.piclist.cn/latest/latest.yml' - const res = await axios.get(url, { - headers: { 'Content-Type': 'application/octet-stream' }, - responseType: 'text', - }) - const latest = yaml.parseDocument(res.data).toJSON() as unknown as updater.UpdateInfo - const currentVersion = pkg.version - if (latest.version !== currentVersion) { - newVersion = latest.version - updateAvailableHandler(latest) - } + const url = 'https://release.piclist.cn/latest/latest.yml' + const res = await axios.get(url, { + headers: { 'Content-Type': 'application/octet-stream' }, + responseType: 'text', + }) + const latest = yaml.parseDocument(res.data).toJSON() as unknown as updater.UpdateInfo + const currentVersion = pkg.version + if (latest.version !== currentVersion) { + newVersion = latest.version + updateAvailableHandler(latest) } }