vue-cli-plugin-electron-builder icon indicating copy to clipboard operation
vue-cli-plugin-electron-builder copied to clipboard

Automatic upgrade failed

Open kf53916 opened this issue 5 years ago • 6 comments

Describe the bug A clear and concise description of what the bug is. Automatic upgrade failed.The installation package has been downloaded to the local computer. After starting the upgrade installation wizard, the installation progress is halfway back, causing the update to fail. If you cancel the upgrade and open the application again, it will automatically update to the latest version. The main.log file prints out: [error] Cannot download differentially, fallback to full download

To Reproduce Steps to reproduce the behavior: The first step is to execute npm run electron: build. The second step is to install the exe file to the local computer。 The third step is to open the program and follow the prompts to download the latest version.

Expected behavior A clear and concise description of what you expected to happen. I want to be able to automatically upgrade successfully

Screenshots If applicable, add screenshots to help explain your problem. The vue.config.js:

const path = require('path')

function resolve (dir) {
  return path.join(__dirname, dir)
}

module.exports = {
	publicPath: './',
	devServer: {
		// can be overwritten by process.env.HOST
		host: '0.0.0.0',  
		port: 8090
	},
	configureWebpack: config => {
		// Configuration applied to all builds
		config.devtool = 'source-map';
	},
	chainWebpack: config => {
		config.resolve.alias
			.set('@', resolve('src'))
			.set('src', resolve('src'))
			.set('common', resolve('src/common'))
			.set('components', resolve('src/components'))
	},
    pluginOptions: {
	    electronBuilder: {
			chainWebpackMainProcess: config => {
				// Chain webpack config for electron main process only
			},
			disableMainProcessTypescript: false,
			mainProcessTypeChecking: true,
			outputDir: "./dist",// 指定运行时输出目录
			builderOptions: {
				"productName": "snps",
				"appId": "com.electron.hit",
				"artifactName": "snps-${version}-${arch}.${ext}",
				"asar": true,
				"copyright": "Copyright©2020",
				"nsis": {
					"artifactName": "snps-${version}-${arch}.${ext}"
				},
				"directories": {
					"output": "build"// 指定打包输出目录
				},
				"dmg": {
					"contents": [
						{
							"x": 410,
							"y": 150,
							"type": "link",
							"path": "/Applications"
						},
						{
							"x": 130,
							"y": 150,
							"type": "file"
						}
					]
				},
				"mac": {
					"icon": "./public/icons/icon.icns",
					"target": ["dmg", "zip"]
				},
				"win": {
					"icon": "./public/icons/app.ico",
					"target": ["nsis", "zip"]
				},
				"linux": {
					"icon": "./public/icons"
				},
				"publish": [// auto updater config
					{
						"provider": "generic",
						"url": "http://127.0.0.1:8080/electron/release/download/",
						"channel": "latest"
					}
				],
				"nsis": {// packager config
					"oneClick": false,// disable one key install
					"allowElevation": true,
					"allowToChangeInstallationDirectory": true,
					"installerIcon": "./public/icons/app.ico",
					"uninstallerIcon": "./public/icons/app.ico",
					"createDesktopShortcut": true,
					"createStartMenuShortcut": true
				}
			}
		}
	}
}

The project.json:

{
  "name": "electron-vue",
  "version": "0.1.0",
  "private": true,
  "author": "test",
  "description": "A electron project",
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "electron:build": "vue-cli-service electron:build",
    "electron:serve": "vue-cli-service electron:serve",
    "postinstall": "electron-builder install-app-deps",
    "postuninstall": "electron-builder install-app-deps"
  },
  "main": "background.js",
  "dependencies": {
  },
  "devDependencies": {
    "@types/node": "^12.12.6",
    "@vue/cli-plugin-router": "^4.1.0",
    "@vue/cli-plugin-typescript": "^4.1.0",
    "@vue/cli-plugin-vuex": "^4.1.0",
    "@vue/cli-service": "^4.1.0",
    "electron": "^7.1.11",
    "electron-log": "^4.0.5",
    "electron-updater": "^4.2.0",
    "stylus": "^0.54.7",
    "stylus-loader": "^3.0.2",
    "typescript": "~3.5.3",
    "vue": "^2.6.10",
    "vue-cli-plugin-electron-builder": "^1.4.4",
    "vue-devtools": "^5.1.3",
    "vue-router": "^3.1.3",
    "vue-template-compiler": "^2.6.10",
    "vuex": "^3.1.2"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions"
  ]
}

Environment (please complete the following information):

  • OS and version: 10.0.17763
  • node version: 11.11.0
  • npm version: 6.7.0
  • yarn version (if used):
  • vue-cli-plugin-electron-builder version : 1.4.4
  • electron version: 7.1.11
  • other vue plugins used:
  • custom config for vcp-electron-builder:
  • (if possible) link to your repo:

Additional context Add any other context about the problem here.

(edit: place snippets in code blocks)

kf53916 avatar Feb 05 '20 15:02 kf53916

It looks like you are using a custom server to host the releases. I will need the source code to the server to reproduce the issue.

nklayman avatar Feb 05 '20 17:02 nklayman

It looks like you are using a custom server to host the releases. I will need the source code to the server to reproduce the issue.

The back-end server I use is nginx, I just need to put the executable file and latest.yml file under the / html/electron/release/download directory, and then start the nginx service.

kf53916 avatar Feb 06 '20 00:02 kf53916

It works fine for me with a custom server, can you please post the code that initiates the update from the main process?

nklayman avatar Feb 06 '20 22:02 nklayman

It works fine for me with a custom server, can you please post the code that initiates the update from the main process?

The background.ts:

'use strict'

import { app, protocol, BrowserWindow, ipcMain, Menu, Tray, globalShortcut } from 'electron'
import { autoUpdater } from "electron-updater"
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
var log = require('electron-log')
const config = require('../config')
const isDevelopment = process.env.NODE_ENV !== 'production'

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win: BrowserWindow | null
let appTray: Tray | null

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true } }])

log.transports.file.level = 'debug'
log.transports.console.level = false
log.transports.console.level = 'silly'

function createWindow () {
	// Create the browser window.
	win = new BrowserWindow({
		width: 800,
		height: 600,
		center: true,
		webPreferences: {
			webSecurity: false,// disable cross domain access
			nodeIntegration: true
		},
		// eslint-disable-next-line no-undef
		icon: `${__dirname}/icons/app.ico`// set title bar left-top icon
	})
	
	// Create system tray
	createTray(win);

	if (process.env.WEBPACK_DEV_SERVER_URL) {
		// Load the url of the dev server if in development mode
		win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string)
		if (!process.env.IS_TEST) win.webContents.openDevTools()
	} else {
		createProtocol('app')
		// Load the index.html when not in development
		win.loadURL('app://./index.html')
	}

	win.on('closed', () => {
		win = null
		appTray = null
	})
	
	win.on('close', (event) => {
		event.preventDefault()
		win && win.hide()
		win && win.setSkipTaskbar(true)
	})
	
	updateHandle();
}

function createTray(win:BrowserWindow) {
	const trayMenu = [
		{
			label: '打开', 
			click: () => {
				win && win.show()// 显示窗口
				win && win.setSkipTaskbar(false)// 任务栏显示
			}
		},
		{label: '关于', click: () => {alert('about')}},
		{label: '退出', click: () => {win && win.destroy();}}
	]
	// eslint-disable-next-line no-undef
	appTray = new Tray(`${__dirname}/icons/app.ico`)
	//图标的上下文菜单
	const contextMenu = Menu.buildFromTemplate(trayMenu)
	//设置此托盘图标的悬停提示内容
	appTray.setToolTip('现货交易系统');
	//设置此图标的上下文菜单
	appTray.setContextMenu(contextMenu)
	//单击右下角小图标显示应用
	appTray.on('click',() => {
		win && win.show()
		win && win.setSkipTaskbar(false)
	})
}

function updateHandle() {
	let message = {
		error: '检查更新出错',
		checking: '正在检查更新……',
		updateAva: '检测到新版本,正在下载……',
		updateNotAva: '现在使用的就是最新版本,不用更新',
	};
	// disabled force updater
	autoUpdater.autoDownload = !1
	autoUpdater.logger = log
        // config.updateUrl=http://127.0.0.1:8080/electron/release/download/
	autoUpdater.setFeedURL(config.updateUrl);
	autoUpdater.on('error', function (error) {
		sendUpdateMessage({cmd: 'error', message: message.error})
	});
	autoUpdater.on('checking-for-update', function () {
		sendUpdateMessage({cmd: 'checking', message: message.checking})
	});
	autoUpdater.on('update-available', function (info) {
		sendUpdateMessage({cmd: 'available', message: message.updateAva, ...info})
	});
	autoUpdater.on('update-not-available', function (info) {
		sendUpdateMessage({cmd: 'unavailable', message: message.updateNotAva})
	});
	// 更新下载进度事件
	autoUpdater.on('download-progress', function (progress) {
		log.info('progress', progress)
		sendUpdateMessage({cmd: 'downloadProgress', message: progress})
	});
	// 更新下载完成事件
	autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
		// 发送是否立即更新事件
		sendUpdateMessage({cmd: 'isUpdateNow'});
		ipcMain.on('isUpdateNow', (e, arg) => {
			log.info("begin updating......");
			//some code here to handle event
			autoUpdater.quitAndInstall();
		});
	});
	ipcMain.on('isDownloadNow', () => {
		log.info('isDownloadNow')
		autoUpdater.downloadUpdate();
	})
	ipcMain.on("checkForUpdate", () => {
		// 执行自动更新检查
		autoUpdater.checkForUpdates();
	})
}

function sendUpdateMessage(message: any) {
  win && win.webContents.send('message', message);
}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (win === null) {
    createWindow()
  }
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
	BrowserWindow.addDevToolsExtension('node_modules/vue-devtools/vender')
  }
  // 在开发环境和生产环境均可通过快捷键打开devTools
  globalShortcut.register('CommandOrControl+Shift+F12', () => {
	win && win.webContents.openDevTools()
  })
  createWindow()
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', data => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}

kf53916 avatar Feb 07 '20 00:02 kf53916

@kf53916

	ipcMain.on("checkForUpdate", () => {
		// 执行自动更新检查
		autoUpdater.checkForUpdates();
	})

Your checkForUpdates() method is manually checked instead of automatically checked, and requires the renderer process to trigger checkForUpdate, whether or not you triggered it. Why don't you direct execution checkForUpdates()

qiufeihong2018 avatar Mar 31 '21 07:03 qiufeihong2018

@kf53916 were you able to resolve this issue? Have you tried updating all deps?

nklayman avatar May 22 '21 19:05 nklayman