mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-06 20:42:57 +08:00
📦 Chore(custom): update publish workflow
This commit is contained in:
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -41,10 +41,11 @@ body:
|
||||
label: 系统信息 | System Information
|
||||
options:
|
||||
- Windows
|
||||
- Win(arm64)
|
||||
- Mac
|
||||
- Mac(arm64)
|
||||
- Linux
|
||||
- All
|
||||
- Linux(arm64)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
5
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -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~
|
||||
Finally, if you like PicList, give it a star~
|
||||
|
||||
141
.github/workflows/buid_arch.yml
vendored
141
.github/workflows/buid_arch.yml
vendored
@@ -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 }}
|
||||
|
||||
|
||||
@@ -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": [
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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!')
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
99
scripts/upload-to-s3.js
Normal file
99
scripts/upload-to-s3.js
Normal file
@@ -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: '<<stream>>',
|
||||
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()
|
||||
@@ -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<void> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user