Feature: add dist upload to cos & update checkupdate logic

This commit is contained in:
PiEgg 2022-08-14 20:45:16 +08:00
parent dc85123bde
commit c926414839
15 changed files with 252 additions and 50 deletions

View File

@ -51,5 +51,8 @@ jobs:
- name: Build & release app
run: |
yarn release
yarn upload-dist
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
PICGO_ENV_COS_SECRET_ID: ${{ secrets.PICGO_ENV_COS_SECRET_ID }}
PICGO_ENV_COS_SECRET_KEY: ${{ secrets.PICGO_ENV_COS_SECRET_KEY }}

1
.gitignore vendored
View File

@ -18,3 +18,4 @@ docs/dist/
dist_electron/
test.js
.env
scripts/*.yml

View File

@ -27,5 +27,6 @@ install:
build_script:
#- yarn test
- yarn release
- yarn upload-dist
test: false

View File

@ -13,7 +13,8 @@
"postuninstall": "electron-builder install-app-deps",
"cz": "git-cz",
"bump": "bump-version",
"release": "vue-cli-service electron:build --publish always"
"release": "vue-cli-service electron:build --publish always",
"upload-dist": "node ./scripts/upload-dist-to-cos.js"
},
"main": "background.js",
"husky": {
@ -38,9 +39,11 @@
"@picgo/i18n": "^1.0.0",
"@picgo/store": "2.0.1",
"axios": "^0.19.0",
"compare-versions": "^4.1.3",
"core-js": "^3.3.2",
"element-ui": "^2.13.0",
"fs-extra": "^10.0.0",
"js-yaml": "^4.1.0",
"keycode": "^2.2.0",
"lodash-id": "^0.14.0",
"lowdb": "^1.0.0",
@ -59,6 +62,7 @@
"@picgo/bump-version": "^1.1.2",
"@types/fs-extra": "^9.0.13",
"@types/inquirer": "^6.5.0",
"@types/js-yaml": "^4.0.5",
"@types/lowdb": "^1.0.9",
"@types/node": "^16.10.2",
"@types/request-promise-native": "^1.0.17",
@ -75,6 +79,7 @@
"@vue/eslint-config-typescript": "^7.0.0",
"conventional-changelog": "^3.1.18",
"cz-customizable": "^6.2.0",
"dotenv": "^16.0.1",
"electron": "^16.0.6",
"electron-devtools-installer": "^3.2.0",
"eslint": "^7.32.0",

50
scripts/config.js Normal file
View File

@ -0,0 +1,50 @@
// different platform has different format
// macos
const darwin = [{
appNameWithPrefix: 'PicGo-',
ext: '.dmg',
arch: '-arm64',
'version-file': 'latest-mac.yml'
}, {
appNameWithPrefix: 'PicGo-',
ext: '.dmg',
arch: '-x64',
'version-file': 'latest-mac.yml'
}]
const linux = [{
appNameWithPrefix: 'PicGo-',
ext: '.AppImage',
arch: '',
'version-file': 'latest-linux.yml'
}, {
appNameWithPrefix: 'picgo_',
ext: '.snap',
arch: '_amd64',
'version-file': 'latest-linux.yml'
}]
// windows
const win32 = [{
appNameWithPrefix: 'PicGo-Setup-',
ext: '.exe',
arch: '-ia32',
'version-file': 'latest.yml'
}, {
appNameWithPrefix: 'PicGo-Setup-',
ext: '.exe',
arch: '-x64',
'version-file': 'latest.yml'
}, {
appNameWithPrefix: 'PicGo-Setup-',
ext: '.exe',
arch: '', // 32 & 64
'version-file': 'latest.yml'
}]
module.exports = {
darwin,
linux,
win32
}

View File

@ -0,0 +1,103 @@
// upload dist bundled-app to cos
require('dotenv').config()
const crypto = require('crypto')
const fs = require('fs')
const mime = require('mime-types')
const pkg = require('../package.json')
const configList = require('./config')
const axios = require('axios').default
const path = require('path')
const distPath = path.join(__dirname, '../dist_electron')
const BUCKET = 'picgo-1251750343'
const AREA = 'ap-chengdu'
const VERSION = pkg.version
const FILE_PATH = `${VERSION}/`
const SECRET_ID = process.env.PICGO_ENV_COS_SECRET_ID
const SECRET_KEY = process.env.PICGO_ENV_COS_SECRET_KEY
// https://cloud.tencent.com/document/product/436/7778#signature
/**
* @param {string} fileName
* @returns
*/
const generateSignature = (fileName, folder = FILE_PATH) => {
const secretKey = SECRET_KEY
const area = AREA
const bucket = BUCKET
const path = folder
const today = Math.floor(new Date().getTime() / 1000)
const tomorrow = today + 86400
const signTime = `${today};${tomorrow}`
const signKey = crypto.createHmac('sha1', secretKey).update(signTime).digest('hex')
const httpString = `put\n/${path}${fileName}\n\nhost=${bucket}.cos.${area}.myqcloud.com\n`
const sha1edHttpString = crypto.createHash('sha1').update(httpString).digest('hex')
const stringToSign = `sha1\n${signTime}\n${sha1edHttpString}\n`
const signature = crypto.createHmac('sha1', signKey).update(stringToSign).digest('hex')
return {
signature,
signTime
}
}
/**
*
* @param {string} fileName
* @param {Buffer} fileBuffer
* @param {{ signature: string, signTime: string }} signature
* @returns
*/
const getReqOptions = (fileName, fileBuffer, signature, folder = FILE_PATH) => {
return {
method: 'PUT',
url: `http://${BUCKET}.cos.${AREA}.myqcloud.com/${encodeURI(folder)}${encodeURI(fileName)}`,
headers: {
Host: `${BUCKET}.cos.${AREA}.myqcloud.com`,
Authorization: `q-sign-algorithm=sha1&q-ak=${SECRET_ID}&q-sign-time=${signature.signTime}&q-key-time=${signature.signTime}&q-header-list=host&q-url-param-list=&q-signature=${signature.signature}`,
contentType: mime.lookup(fileName),
useAgent: `PicGo;${pkg.version};null;null`
},
maxContentLength: Infinity,
maxBodyLength: Infinity,
data: fileBuffer,
resolveWithFullResponse: true
}
}
const uploadFile = async () => {
try {
const platform = process.platform
if (configList[platform]) {
let versionFileHasUploaded = false
for (const [index, config] of configList[platform].entries()) {
const fileName = `${config.appNameWithPrefix}${VERSION}${config.arch}${config.ext}`
const filePath = path.join(distPath, fileName)
const versionFilePath = path.join(distPath, config['version-file'])
let versionFileName = config['version-file']
if (VERSION.toLocaleLowerCase().includes('beta')) {
versionFileName = versionFileName.replace('.yml', '.beta.yml')
}
// upload dist file
const signature = generateSignature(fileName)
const reqOptions = getReqOptions(fileName, fs.readFileSync(filePath), signature)
console.log('[PicGo Dist] Uploading...', fileName, `${index + 1}/${configList[platform].length}`)
await axios.request(reqOptions)
// upload version file
if (!versionFileHasUploaded) {
const signature = generateSignature(versionFileName, '')
const reqOptions = getReqOptions(versionFileName, fs.readFileSync(versionFilePath), signature, '')
console.log('[PicGo Version File] Uploading...', versionFileName)
await axios.request(reqOptions)
versionFileHasUploaded = true
}
}
} else {
console.warn('platform not supported!', platform)
}
} catch (e) {
console.error(e)
}
}
uploadFile()

View File

@ -26,7 +26,8 @@ import {
PICGO_REMOVE_BY_ID_DB,
PICGO_OPEN_FILE,
PASTE_TEXT,
OPEN_WINDOW
OPEN_WINDOW,
DEFAULT_LOGO
} from '#/events/constants'
import { GalleryDB } from 'apis/core/datastore'
@ -366,6 +367,12 @@ const handleOpenWindow = () => {
})
}
const handleDefaultLogo = () => {
ipcMain.on(DEFAULT_LOGO, (event: IpcMainEvent) => {
event.sender.send(DEFAULT_LOGO, path.join(__static, 'roundLogo.png'))
})
}
export default {
listen () {
handleGetPluginList()
@ -379,6 +386,7 @@ export default {
handleImportLocalPlugin()
handleOpenFile()
handleOpenWindow()
handleDefaultLogo()
},
// TODO: separate to single file
handlePluginUninstall,

View File

@ -1,12 +1,12 @@
import { dialog, shell } from 'electron'
import db from '~/main/apis/core/datastore'
import axios from 'axios'
import pkg from 'root/package.json'
import { lt } from 'semver'
import { T } from '~/universal/i18n'
import { getLatestVersion } from '#/utils/getLatestVersion'
const version = pkg.version
const releaseUrl = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const releaseUrlBackup = 'https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@latest/package.json'
// const releaseUrl = 'https://api.github.com/repos/Molunerfinn/PicGo/releases'
// const releaseUrlBackup = 'https://picgo-1251750343.cos.ap-chengdu.myqcloud.com'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
const checkVersion = async () => {
@ -16,17 +16,10 @@ const checkVersion = async () => {
showTip = true
}
if (showTip) {
let res: any
try {
res = await axios.get(releaseUrl).catch(async () => {
const result = await axios.get(releaseUrlBackup)
return result
})
} catch (err) {
console.log(err)
}
if (res.status === 200) {
const latest = res.data.version || res.data.name
const isCheckBetaUpdate = db.get('settings.checkBetaUpdate') !== false
const res: string = await getLatestVersion(isCheckBetaUpdate)
if (res !== '') {
const latest = res
const result = compareVersion2Update(version, latest)
if (result) {
dialog.showMessageBox({

View File

@ -393,10 +393,9 @@ import {
import { Component, Vue } from 'vue-property-decorator'
import { T, languageList } from '~/universal/i18n'
import { enforceNumber } from '~/universal/utils/common'
// import db from '#/datastore'
const releaseUrl = 'https://api.github.com/repos/Molunerfinn/PicGo/releases/latest'
const releaseUrlBackup = 'https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@latest/package.json'
const downloadUrl = 'https://github.com/Molunerfinn/PicGo/releases/latest'
import { getLatestVersion } from '#/utils/getLatestVersion'
import { compare } from 'compare-versions'
import { STABLE_RELEASE_URL, BETA_RELEASE_URL } from '#/utils/static'
const customLinkRule = (rule: string, value: string, callback: (arg0?: Error) => void) => {
if (!/\$url/.test(value)) {
return callback(new Error(T('TIPS_MUST_CONTAINS_URL')))
@ -635,39 +634,23 @@ export default class extends Vue {
})
}
compareVersion2Update (current: string, latest: string) {
const currentVersion = current.split('.').map(item => parseInt(item))
const latestVersion = latest.split('.').map(item => parseInt(item))
for (let i = 0; i < 3; i++) {
if (currentVersion[i] < latestVersion[i]) {
return true
}
if (currentVersion[i] > latestVersion[i]) {
return false
}
}
return false
compareVersion2Update (current: string, latest: string): boolean {
return compare(current, latest, '<')
}
checkUpdate () {
async checkUpdate () {
this.checkUpdateVisible = true
this.$http.get(releaseUrl)
.then(res => {
this.latestVersion = res.data.name
}).catch(async () => {
this.$http.get(releaseUrlBackup)
.then(res => {
this.latestVersion = res.data.version
}).catch(() => {
this.latestVersion = this.$T('TIPS_NETWORK_ERROR')
})
})
const version = await getLatestVersion(this.form.checkBetaUpdate)
if (version) {
this.latestVersion = version
} else {
this.latestVersion = this.$T('TIPS_NETWORK_ERROR')
}
}
confirmCheckVersion () {
if (this.needUpdate) {
ipcRenderer.send(OPEN_URL, downloadUrl)
ipcRenderer.send(OPEN_URL, this.form.checkBetaUpdate ? BETA_RELEASE_URL : STABLE_RELEASE_URL)
}
this.checkUpdateVisible = false
}

View File

@ -120,7 +120,8 @@ import {
PICGO_CONFIG_PLUGIN,
PICGO_HANDLE_PLUGIN_ING,
PICGO_TOGGLE_PLUGIN,
SHOW_PLUGIN_PAGE_MENU
SHOW_PLUGIN_PAGE_MENU,
DEFAULT_LOGO
} from '#/events/constants'
@Component({
@ -144,7 +145,7 @@ export default class extends Vue {
importLocalPluginToolTip = this.$T('PLUGIN_IMPORT_LOCAL')
id = ''
os = ''
defaultLogo: string = 'this.src="https://cdn.jsdelivr.net/gh/Molunerfinn/PicGo@dev/public/roundLogo.png"'
defaultLogo: string = ''
get npmSearchText () {
return this.searchText.match('picgo-plugin-')
? this.searchText
@ -247,11 +248,19 @@ export default class extends Vue {
this.needReload = true
}
})
ipcRenderer.on(DEFAULT_LOGO, (evt: IpcRendererEvent, logoPath) => {
this.defaultLogo = `this.src="${logoPath.replace(/\\/g, '/')}"`
})
this.getPluginList()
this.getSearchResult = debounce(this.getSearchResult, 50)
this.getDefaultLogo()
this.needReload = await this.getConfig<boolean>('needReload') || false
}
getDefaultLogo () {
ipcRenderer.send(DEFAULT_LOGO)
}
async buildContextMenu (plugin: IPicGoPlugin) {
ipcRenderer.send(SHOW_PLUGIN_PAGE_MENU, plugin)
}
@ -439,6 +448,7 @@ export default class extends Vue {
ipcRenderer.removeAllListeners('uninstallSuccess')
ipcRenderer.removeAllListeners('updateSuccess')
ipcRenderer.removeAllListeners('hideLoading')
ipcRenderer.removeAllListeners(DEFAULT_LOGO)
}
}
</script>

View File

@ -34,3 +34,4 @@ export const SHOW_MAIN_PAGE_DONATION = 'SHOW_MAIN_PAGE_DONATION'
export const FORCE_UPDATE = 'FORCE_UPDATE'
export const CHANGE_LANGUAGE = 'CHANGE_LANGUAGE'
export const OPEN_WINDOW = 'OPEN_WINDOW'
export const DEFAULT_LOGO = 'DEFAULT_LOGO'

View File

@ -0,0 +1,25 @@
import axios from 'axios'
import { RELEASE_URL, RELEASE_URL_BACKUP } from './static'
import yaml from 'js-yaml'
export const getLatestVersion = async (isCheckBetaUpdate: boolean = false) => {
let res: string = ''
try {
res = await axios.get(RELEASE_URL).then(r => {
const list = r.data as IStringKeyMap[]
if (isCheckBetaUpdate) {
const betaList = list.filter(item => item.name.includes('beta'))
return betaList[0].name
}
const normalList = list.filter(item => !item.name.includes('beta'))
return normalList[0].name
}).catch(async () => {
const result = await axios.get(isCheckBetaUpdate ? `${RELEASE_URL_BACKUP}/latest.beta.yml` : `${RELEASE_URL_BACKUP}/latest.yml`)
const r = yaml.load(result.data) as IStringKeyMap
return r.version
})
} catch (err) {
console.log(err)
}
return res
}

View File

@ -1 +1,5 @@
export const CLIPBOARD_IMAGE_FOLDER = 'picgo-clipboard-images'
export const RELEASE_URL = 'https://api.github.com/repos/Molunerfinn/PicGo/releases'
export const RELEASE_URL_BACKUP = 'https://picgo-1251750343.cos.ap-chengdu.myqcloud.com'
export const STABLE_RELEASE_URL = 'https://github.com/Molunerfinn/PicGo/releases/latest'
export const BETA_RELEASE_URL = 'https://github.com/Molunerfinn/PicGo/releases'

View File

@ -81,7 +81,7 @@ const config = {
win: {
icon: 'build/icons/icon.ico',
// eslint-disable-next-line no-template-curly-in-string
artifactName: 'PicGo Setup ${version}-${arch}.exe',
artifactName: 'PicGo-Setup-${version}-${arch}.exe',
target: [{
target: 'nsis',
arch: [

View File

@ -1538,6 +1538,11 @@
"@types/through" "*"
rxjs "^6.4.0"
"@types/js-yaml@^4.0.5":
version "4.0.5"
resolved "https://registry.npmmirror.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138"
integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==
"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7":
version "7.0.9"
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
@ -3907,6 +3912,11 @@ compare-version@^0.1.2:
resolved "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080"
integrity sha1-AWLsLZNR9d3VmpICy6k1NmpyUIA=
compare-versions@^4.1.3:
version "4.1.3"
resolved "https://registry.npmmirror.com/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4"
integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg==
component-emitter@^1.2.1:
version "1.3.0"
resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
@ -5146,6 +5156,11 @@ dotenv-expand@^5.1.0:
resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
dotenv@^16.0.1:
version "16.0.1"
resolved "https://registry.npmmirror.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d"
integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==
dotenv@^8.2.0:
version "8.6.0"
resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
@ -8140,7 +8155,7 @@ js-yaml@^3.13.1:
js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"