dlrandy / note-issues

2 stars 0 forks source link

electron 13--end #39

Open dlrandy opened 6 years ago

dlrandy commented 6 years ago

debug electron app Chromium dev tool for Renderer Process; MenuItem 的click函数的参数(item, focusedWindow) 在VSCode里调试Main Process; 根目录下创建目录.vscode 加入launch.json,内容是 { "version": "0.2.0", "configurations": [ { "name": "Debug Electron Main Process", "type": "node", "request": "launch", "cwd": "${workspaceRoot}", "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", "program": "${workspaceRoot}/main.js" } ] } debugmenu里add configuration Menu要在app ready的时候加入

也可以是用node-inspector调试mainProcess npm install node-inspector --save-dev npm install node-pre-gyp electron --debug=5858 ELECTRON_RUN_AS_NODE=true path/to/electron.app|exe node_modules/node-inspector/bin/ inspector.js

http://127.0.0.1:8080/debug?ws=127.0.0.1:8080&port=5858 还可以使用Devtron npm install devtron --save-dev

dlrandy commented 6 years ago

在electron里测试Rerender Process,可以在Electron之外测试 也就是测试web 项目那样

测试Main Process则需要electron里测试 使用spectron它是test Runner 还需要Mocha(测试框架)和chai(验证库)

spectron 的describe不要使用箭头函数,若使用了,this.timeout(10000)这句会有问题的

测试的时候 不要打开devTools https://github.com/electron/spectron/issues/174

dlrandy commented 6 years ago
const electron = require('electron');
const Application = require('spectron').Application;
const expect = require('chai').expect;

describe('SPECTRON Example', function(){
  this.timeout(10000)
  global.app = null;

  before(() => {
    app = new Application({
      path: electron,
      args: ['.'],
    })

    return app.start()
      .then(() =>{
        app.client.waitUntilWindowLoaded()
        app.browserWindow.show()
        return app;
      })
  });

  after(() => {
    if (app && app.isRunning()) {
      return app.stop()
    }
  });

  it('should open a window', () => {
    return app.client.waitUntilWindowLoaded()
      .getWindowCount()
      .then(count => {
        expect(count).to.equal(1)
      })
  });

  it('should open a working window', () => {
    return app.client.waitUntilWindowLoaded()
      .browserWindow.isVisible()
      .then(res => {
        expect(res).to.be.true
      })
      .browserWindow.isFocused()
      .then(res => {
        expect(res).to.be.true
      })
      .browserWindow.isMinimized()
      .then(res => {
        expect(res).to.be.false
      })
      .browserWindow.isDevToolsOpened()
      .then(res => {
        expect(res).to.be.false
      })
  });

  it('should open a window to correct size', () => {
    return app.client.waitUntilWindowLoaded()
      .browserWindow.getBounds()
        .then(res => {
          expect(res.width).to.equal(800)
          expect(res.height).to.equal(600)
        })
  });

  it('should call foobar', () => {
    return app.client.waitUntilWindowLoaded()
      .then(() => {
        return app.client.getText('#foobarBtn')
      })
      .then(text => {
        expect(text).to.equal('get foo bar')
        return app.client.click('#foobarBtn')
      })
      .then(() => {
        return app.client.getText('#foobarBtn')
      })
      .then(text => {
        expect(text).to.equal('hello!')
      })
  });

});
dlrandy commented 6 years ago

build 或者发布electron app要使用Electron-Builder 它的作用: 1构建打包app(mac Windows linux) 2、支持app的code sign 3、可以生成自动更新的app

npm install electron-builder --save-dev 按步骤顺序: 他对目录有严格要求: build是用来查找build资源的; dist目标App目录; app【optioal】存放在的app文件,main.js, renderer.js index.html

修改package.JSON scripts: "dist": "build -mwl --x64 --ia32"

MAC build windows: brew install wine --without-x11 //linux上的win实现 brew install mono //.NET frameWork 的实现

Mac build Linux brew install gnu-tar graphicsmagick xz brew install rpm

//如果上面都安装成功使用下面的还行,否则就在特定的平台上执行build吧 "build" : { "appId": "cc.gochen.desktop", "copyright": "Copyright @ 2017 ", "productName": "My Electron App", "electronVersion": "1.7.8", "mac": { "category": "public.app-category.developer-tools" }, "win": { "target": [ "nsis" ] }, "linux": { "target": [ "AppImage", "deb" ] } },

上面的配置会有icon警告,electron-builder在build的时候回去build目录找资源的 Mac:icon.icns; Window: icon.ico; Linux: icon.icns; 2的(4--9) 可以使用 electron-icon-maker ,源图片可以放在新建的目录build-assets里 然后build到build目录 electron-icon-maker -i build-assets/icon.png -o build/ 这里要把生成的ico文件放到build下

配置MacOS DMG Electron-builder可以使用background.png/tif "dmg": { "title": "ConfiGAP Installer", "background": "./build/background.png", "icon": "./build/installer.icns", "iconSize": 128, "contents": [ { "x": 388, "y": 160, "type": "link", "path": "/Applications" }, { "x": 128, "y": 160, "type": "file", "path": "" } ] } "nsis" : { "oneClick": true, "artifactName": "Electron Builder Setup.exe", "installerIcon": "./build/installerIcon.ico", "installerHeaderIcon": "./build/installerHeaderIcon.ico" }

https://www.electron.build/configuration/nsis

dlrandy commented 6 years ago

electron forge

dlrandy commented 6 years ago

自动更新包括三个任务: app的base code;build过程的修改;app的正常发布。

Electron的auto-updater module是Squirrel(github)的接口,Squirrel可以检查新版本,下载并执行实际的升级。不能用在Linux上

MacOS 上的自动更新 首先要在Main Process autoUpdater =electron.autoUpdater This module broadcasts four events: • checking-for-update • update-available • update-not-available • error 这个是自动更新的生命周期

autoUpdater.checkForUpdates()如果接受的response是204就认为没有更新,若是200,并带有一个url属性的JSON,那么就知道是需要更新的了

可以在生命周期的函数里给出反馈

可用的Auto Update Server: nuts;electron-release-server;Squirrel-updates/repease-server 当然可以自己设置server

'use strict'
const fs = require('fs')
const express = require('express')
const path = require('path')
const app = express()
app.get('/updates/latest', function (req, res) {
    const latest = getLatestRelease()
    const clientVersion = req.query.v
    if (clientVersion === latest) {
        res.status(204).end()
    } else {
        let baseURL = getBaseUrl()
        let updateURL = baseURL + '/releases/darwin/' + latest + '/electron.zip'
       res.json({
            url: updateURL,
            name: "My Release Name",
            notes: "These are some release notes",
            pub_date: "2017-04-18T12:29:53+01:00"
}) }
})
let getLatestRelease = () => {
    const dir = __dirname + '/releases/darwin'
    const versionsDesc = fs.readdirSync(dir).filter((file) => {
        const filePath = path.join(dir, file)
        return fs.statSync(filePath).isDirectory()
    }).reverse()
    return versionsDesc[0];
}

let getBaseUrl = () => {
  if (process.env.NODE_ENV === 'development') {
    return 'http://localhost:3000'
  } else {
    return 'http://your-company.com'
  }
}

app.listen(process.env.PORT || 3000, () => {
  console.log(`Express server listening on port ${process.env.PORT || 3000}`)
});
dlrandy commented 6 years ago

签名App 要想AutoUpdate正常工作,需要sign app 会用Keychain Access工具生成证书: Keychain Access ➤ Certificate Assistant ➤ Create a Certificate... We need to now set the CSC_NAME environment variable, so the signing can occur when we build our application. In your terminal and in our application’s active directory, run export CSC_NAME="Certificate Name" https://github.com/electron/electron/blob/master/docs/tutorial/mac-app-store-submission-guide.md

"dist": "build -m"

生成一个更新需要三步: 修改package.json的version,并rebuild app; 新的app上传到server的某个位置; 更新server的url;

dlrandy commented 6 years ago

Windows自动更新依赖delta package和特殊的RELEASEs 文件

npm install electron-builder-squirrel-windows --save-dev 如果目标是32位的PC,继续使用nsis,否则使用squirrel dist": "build -w --x64"

• Electron Demo Setup 1.0.1.exe、发布的安装包 • autoupdatedemo-1.0.1-full.nupkg app的源码 • RELEASES 文本文件有一些app version, checksums

"build": { "appId": "com.ajsoftware.electronapp", "copyright": "Copyright © 2017 Chris Griffith", "productName": "Electron Demo", "electronVersion": "1.4.1", "win": { "target": [ "squirrel" ] , "certificateFile": "./certificate.pfx", "certificatePassword": "your-password", "icon": "./build/icon.ico", "squirrelWindows": { "iconUrl": "http://your-company.com/releases/win/icon.ico", "loadingGif": "./build/loader.gif", "useAppIdAsId": false
Squirrel安装app时候会把安装包放在AppData目录,会基于App ID或者name属性创建一个新目录,AppID会被截取(com.company-name.app-name --》 name),所以使用name

} } 签名Window app openssl genrsa -aes128 -out privateKey.key 2048 openssl req -new -x509 -days 365 -key privateKey.key -out certificate.crt

openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt

if (process.platform === 'darwin') { autoUpdater.setFeedURL(updateUrl) autoUpdater.checkForUpdates() } else { updateUrl = "http://your-company.com/releases/win/" autoUpdater.setFeedURL(updateUrl) autoUpdater.checkForUpdates() }

const electron = require("electron");
// Module to control application life.
const app = electron.app;
const dialog = electron.dialog;

const autoUpdater = electron.autoUpdater;
autoUpdater.addListener("update-available", function(event) {
  console.log("UPdate Available");
  dialog.showMessageBox(
    {
      type: "info",
      title: "Update Available",
      message: "There is an updated version available",
      buttons: ["Update", "Skip"]
    },
    function(index) {
      console.log("index ", index);
    }
  );
});

autoUpdater.addListener("update-downloaded", function(
  event,
  releaseNotes,
  releaseName,
  releaseDate,
  updateURL
) {
  console.log("Update Downloaded");
  console.log("releaseNotes", releaseNotes);
  console.log("releaseNotes", releaseName);
  console.log("releaseNotes", releaseDate);
  console.log("releaseNotes", updateURL);

  dialog.showMessageBox(
    {
      type: "info",
      title: "Update Downloaded",
      message: "Update has downloaded",
      detail: releaseNotes,
      buttons: ["Install", "Skip"]
    },
    function(index) {
      if (index === 0) {
        autoUpdater.quitAndInstall();
      } else {
        autoUpdater.quit();
      }
    }
  );
});

autoUpdater.addListener("error", function(error) {
  console.log("Error");
  console.log(error);
  dialog.showMessageBox({
    type: "warning",
    title: "Update Error",
    message: "An error occurred. " + error,
    buttons: ["OK"]
  });
});
autoUpdater.addListener("checking-for-update", function(event) {
  console.log("releaseNotes", "Checking for Update");
});
autoUpdater.addListener("update-not-available", function(event) {
  console.log("releaseNotes", "Update Not Available");
  dialog.showMessageBox({
    type: "warning",
    title: "No Updates",
    message: "No update available at this time.",
    buttons: ["OK"]
  });
});

let appVersion = app.getVersion();

let updateUrl =
  "https://apress-electron-manager.herokuapp.com/updates/latest?v=" +
  appVersion;

if (process.platform === "darwin") {
  autoUpdater.setFeedURL(updateUrl);
  autoUpdater.checkForUpdates();
}

// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;

const path = require("path");
const url = require("url");

// 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 mainWindow;

function createWindow() {
  // Create the browser window.
  mainWindow = new BrowserWindow({ width: 800, height: 600 });

  // and load the index.html of the app.
  mainWindow.loadURL(
    url.format({
      pathname: path.join(__dirname, "index.html"),
      protocol: "file:",
      slashes: true
    })
  );

  // Open the DevTools.
  // mainWindow.webContents.openDevTools()

  // Emitted when the window is closed.
  mainWindow.on("closed", function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
}

// 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", createWindow);

// Quit when all windows are closed.
app.on("window-all-closed", function() {
  // On OS X 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", function() {
  // On OS X 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 (mainWindow === null) {
    createWindow();
  }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

if (handleSquirrelEvent()) {
  // squirrel event handled and app will exit in 1000ms, so don't do anything else
  return;
}
function handleSquirrelEvent() {
  if (process.argv.length === 1) {
    return false;
  }
  const ChildProcess = require("child_process");
  const path = require("path");
  const appFolder = path.resolve(process.execPath, "..");
  const rootAtomFolder = path.resolve(appFolder, "..");
  const updateDotExe = path.resolve(path.join(rootAtomFolder, "Update.exe"));
  const exeName = path.basename(process.execPath);
  const spawn = function(command, args) {
    let spawnedProcess, error;
    try {
      spawnedProcess = ChildProcess.spawn(command, args, { detached: true });
    } catch (error) {}
    return spawnedProcess;
  };
  const spawnUpdate = function(args) {
    return spawn(updateDotExe, args);
  };
  const squirrelEvent = process.argv[1];
  switch (squirrelEvent) {
    case "--squirrel-install":
    case "--squirrel-updated":
      // Optionally do things such as:
      // - Add your .exe to the PATH
      // - Write to the registry for things like file associations and
      //   explorer context menus
      // Install desktop and start menu shortcuts
      spawnUpdate(["--createShortcut", exeName]);
      setTimeout(app.quit, 1000);
      return true;
    case "--squirrel-uninstall":
      // Undo anything you did in the --squirrel-install and
      // --squirrel-updated handlers
      // Remove desktop and start menu shortcuts
      spawnUpdate(["--removeShortcut", exeName]);
      setTimeout(app.quit, 1000);
      return true;
    case "--squirrel-obsolete":
      // This is called on the outgoing version of your app before
      // we update to the new version - it's the opposite of
      // --squirrel-updated
      app.quit();
      return true;
  }
}
dlrandy commented 6 years ago

electron-builder并不是唯一的build工具: Windows installer module(https://github.com/electron/windows-installer); Electron Forge (https://beta.electronforge.io/);