Code for final

ふぁいなる向けのコード置き場です。すでにコードじゃないこともいっぱい。

GitHub Actionsを使ってElectron with Vue.jsアプリのWindows/MacOS/Linux向けパッケージを同時に自動リリースするやりかた

Electronアプリを作ってGitHub Actionsを使って自動でビルドしてリリースしたいと思って調べてできたやり方をまとめます。
C#アプリではAppVeyorでやっていましたが、あの頃はGitHub Actionsなんてものはなかったので、今回はGitHub Actionsを使います。

final.hateblo.jp

GitHub Actionsとは

いわゆるCI/CD(継続的インテグレーション/継続的デリバリー)というやつで、Jenkinsみたいなやつです。
ブランチにコードがコミットされたタイミングで自動でビルドして、自動でリリースする仕組みを構築できます。
GitHub ActionsではWindowsMacOSLinux(Ubuntu)の3環境でビルドできます。

ワークフローを作成する

GitHubリポジトリを開くと"Actions"があると思うので、そこをたたいて"New workflow"を選択します。
次の画面でテンプレートを選べますが、残念ながらElectronのテンプレートはないのでスキップします。
スキップは"set up a workflow yourself "を選択します。

自動ビルド&リリースのコードを記述する

AppVeyorは自動リリースする際、設定画面でいろいろするべきことが多かったですが、 GitHub Actionsはコードだけで完結します。
コードはyamlで記述します。
コードもブラウザ上で編集できます。

アプリはVue CLIを使っている必要がありますが、以下のコードを貼り付けるだけです。

name: Build Release Electron

on: [push]

jobs:
  build:

    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [windows-latest, macos-latest, ubuntu-latest]

    steps:
      - name: Set Context
        env:
          GITHUB_CONTEXT: ${{ toJson(github) }}
        run: echo "$GITHUB_CONTEXT"
      - uses: actions/checkout@v1
        with:
          fetch-depth: 1
      - name: Set Node.js Version
        uses: actions/setup-node@v1
        with:
          node-version: 12.x
      - name: Install Yarn
        run: |
          yarn install
      - name: Install Vue CLI
        run: |
          yarn global add @vue/cli
      - name: Build for Windows
        if: matrix.os == 'windows-latest'
        run: |
          yarn run electron:build --windows nsis zip --x64
      - name: Build for MacOS
        if: matrix.os == 'macos-latest'
        run: |
          yarn run electron:build --macos dmg --x64
      - name: Build for Linux
        if: matrix.os == 'ubuntu-latest'
        run: |
          yarn run electron:build --linux AppImage --x64
      - name: Upload Artifacts for Windows Zip
        if: matrix.os == 'windows-latest'
        uses: actions/upload-artifact@v2
        with:
          name: ${{ github.event.repository.name }}-Zip Package
          path: dist_electron/*.zip
      - name: Upload Artifacts for Windows Installer
        if: matrix.os == 'windows-latest'
        uses: actions/upload-artifact@v2
        with:
          name: ${{ github.event.repository.name }}-Windows Installer
          path: dist_electron/*.exe
      - name: Upload Artifacts for MacOS
        if: matrix.os == 'macos-latest'
        uses: actions/upload-artifact@v2
        with:
          name: ${{ github.event.repository.name }}-Dmg Package
          path: dist_electron/*.dmg
      - name: Upload Artifacts for Linux
        if: matrix.os == 'ubuntu-latest'
        uses: actions/upload-artifact@v2
        with:
          name: ${{ github.event.repository.name }}-AppImage Package
          path: dist_electron/*.AppImage
      - name: Release
        uses: softprops/action-gh-release@v1
        if: startsWith(github.ref, 'refs/tags/')
        with:
          files: |
            dist_electron/*.zip
            dist_electron/*.exe
            dist_electron/*.dmg
            dist_electron/*.AppImage
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

ちなみにリポジトリ固有の情報はないのでそのままいけます。
nodeのバージョンのところはアプリに合わせて修正してください。

修正が終わったら右上の"Start commit"ボタンでコミットします。

ビルドをしてみる

ブランチにプッシュするだけで自動でビルドが始まります。
ビルド中は黄色でくるくる回ります。

f:id:finalstream:20200724124431p:plain

ビルドが完了したら緑のチェックマークになります。ビルドを選択するとビルドしたものをダウンロードできます。
ビルドしたものはArtifactsとして登録されています。

f:id:finalstream:20200724130747p:plain

左側にあるbuild(xxxx)を選択すると各プラットフォームごとのビルド結果の詳細を確認することもできます。
エラーが発生した場合はここを確認しましょう。
f:id:finalstream:20200724131030p:plain

リリースをしてみる

ブランチにプッシュしただけではリリースされません。
タグをうつとビルド後にリリースされます。
リリースされるとビルドしたものがAssetsとして登録されています。

f:id:finalstream:20200724131458p:plain

最後に

GitHub ActionはのGitHub機能なのでGitHubの連携設定とか不要で便利ですね。
またクロスプラットフォーム向けのビルドにも使えるので今後使っていきたいと思います。

Electronでユーザーに変更させたい設定と変更させたくない設定を保存/読込するやりかた

Electronでアプリを開発していてアプリ設定を保存することは欠かせないと思います。
アプリ設定といっても、ユーザーに変更させたい設定と変更させたくない内部設定があると思います。
ここではその用途別にやり方をまとめます。

ユーザーに変更させたい設定を保存/読込

ユーザーに変更させたい設定は"electron-store"を使うことで簡単に管理できます。

github.com

electron-storeをインストール

vscodeのターミナルのところで以下のコマンドをたたきます。 typescriptの型定義もはいってるみたいでTypeScriptでも使えます。

npm install electron-store

保存する

コード内の任意のところでインスタンスを生成し、setメソッドでキーと値を指定することで保存できます。 以下の例はアプリ終了時にウインドウの大きさと位置を保存するコードです。

import Store from "electron-store";

win.on("close", () => {
    const store = new Store();
    store.set("window.x", win!.getPosition()[0]);
    store.set("window.y", win!.getPosition()[1]);
    store.set("window.height", win!.getSize()[1]);
    store.set("window.width", win!.getSize()[0]);
});

保存された内容を確認

Windowsだと以下のパスにjson形式で保存されます。
アプリ名はpackage.jsonに定義されているnameの値になります。

C:\Users\[ユーザー名]\AppData\Roaming\[アプリ名]\config.json

保存されているファイルは以下になっています。

{
    "window": {
        "x": 114,
        "y": 335,
        "height": 600,
        "width": 800
    }
}

保存した設定を読込む

コード内の任意のところでインスタンスを生成し、getメソッドでキーを指定することで読み込みできます。
以下の例は保存したウインドウの情報を読み込んでウインドウを生成するコードです。

import Store from "electron-store";

async function createWindow() {
  const store = new Store();
  // Create the browser window.
  win = new BrowserWindow({
    x: store.get("window.x"),
    y: store.get("window.y"),
    width: store.get("window.width", 800),
    height: store.get("window.height", 600),
    webPreferences: {
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      nodeIntegration: !!process.env.ELECTRON_NODE_INTEGRATION,
      //nodeIntegration: true,
    },
    title: AppConfig.AppName,
  });

ユーザーに変更させたくない設定を保存/読込

ユーザーに変更させたくない設定はクラスで定義できます。

設定値を定義する

クラスを以下のように定義します。
staticメンバーとして設定値を定義します。

/**
 * アプリ設定
 */
export default class AppConfig {
  /**
   * アプリ名
   */
  static AppName = "Empty Directory Cleaner";
  /**
   * ロガー設定ファイル
   */
  static LoggerConfigFile = "./log4js.config.json";
}

設定値を使用する

クラスをインポートして参照するだけです。

import AppConfig from "./models/AppConfig";

configure(AppConfig.LoggerConfigFile);

最後に

もっといいやり方があれば更新したいと思います。

Electron with TypeScriptアプリで開発に耐えうるログの出力をする

Electronのログの出力はlog4jsを使えばできることがわかりましたが、サンプル程度に出力する記事しかなかったので実際にアプリを開発して アプリ開発に耐えうるログ出力についてまとめます。

log4jsをいれる

javaを使ったことある方ならおなじみのlog4jそのNode.jsバージョンが"log4js"です。

log4js-node.github.io

今回はTypeScriptで使うので型定義もインストールします。
vscodeのターミナルで以下のコマンドをたたきます。

npm install log4js
npm install @types/log4js

設定ファイルをおく

log4js用の設定ファイル(log4js.config.json)をルート(.gitignoreと同じ場所)に配置します。
一般的なアプリ開発でよく使うレベル別にインフォログ(app.log)とデバッグログ(debug.log)とエラーログ(error.log)の3つのファイルに分けて出力する設定になっています。
ログはルートの"logs"フォルダに出力されます。

ロガーの宣言をいれる

コード中にログ出力処理をいれます。
クラスごとに以下の宣言をいれればメインプロセス、レンダラープロセス問わずどこでも出力できます。
以下の例ではロガー設定ファイルのパスは内部設定から取得しています。

import { configure, getLogger } from "log4js";
import AppConfig from "../models/AppConfig";

configure(AppConfig.LoggerConfigFile); // configure("./log4js.config.json");でもOK
const logger = getLogger();

ロガーを使ってログを出力する

宣言したロガーを使ってソースコード中でログを出力します。
例えば検索処理で検索パラメタをデバッグログで出力する場合は以下のような感じになります。

async search() {
    try {
      logger.debug("Search Directory Path:[ %s ]", this.mainData.searchDirectory);
// ...

メッセージのフォーマットは以下のutil.format()に準拠しているみたいです。
ちなみにメッセージにフォーマットを指定せずに引数に渡した場合は末尾に追加されます。

nodejs.org

出力されるログは以下です。

[2020-06-21 15:59:07.604] [DEBUG] Search Directory Path:[ E:\test ]

スタックトレースを出力する

log4jsではログ出力設定でenableCallStackをtrueにしたらスタックトレースが出力されます。
上記の設定ではerrorレベルのみログ出力フォーマットにスタックトレースの出力情報を追加しているため、 errorレベルでのみスタックトレースが出力されます。

以下のようにディレクトリ削除処理でエラーとなった場合、エラーログを出力するとします。

ipcMain.handle("deleteDirectory", async (event, dirPath) => {
  const succesDirectories: string[] = [];

  await trash(dirPath)
    .then(() => {
      succesDirectories.push(dirPath);
    })
    .catch(() => {
      logger.error("error trash directory.", dirPath);
  });

  return succesDirectories;
});

出力されるログは以下になります。

[2020-06-21 16:19:32.357] [ERROR] error trash directory. E:\test\aaa
d:\finalstream\newdev\edc\dist_electron\index.js 28738 20
    at d:\finalstream\newdev\edc\dist_electron\index.js:28738:20
    at async d:\finalstream\newdev\edc\dist_electron\index.js:28733:9
    at async electron/js2c/browser_init.js:6152:30

・・・???。 そうです。Electronはjavascriptで動いているため、実際動作しているjsファイルの行数が出力されます。
あまり見ても役に立ちそうはないので、スタックトレースは出力しなくてもいいかもですね。

パッケージにログ出力設定ファイルを含める

このままパッケージングしてできたものを実行すると"log4js.config.json"が見つからないというエラーになってしまいます。
なので、electron-builderの設定で"log4js.config.json"をパッケージに含める設定を追加します。

module.exports = {
  // debug
  configureWebpack: {
    devtool: "source-map",
  },
  pluginOptions: {
    electronBuilder: {
      nodeIntegration: true,
      builderOptions: {
        extraFiles: ["log4js.config.json"],  // この設定を追加
      },
    },
  },
};

これだけ知っていればアプリでのログ出力は十分だと思います。
ただ、Electronアプリを開発始めたばかりなのでほかにも必要なことがあれば追記していきたいと思います。