flow-flow-flow

[Electron] Electronで外部アプリをキックするときに子プロセスではなく、別プロセスとしてキックしたい

f:id:thegriftsense:20210908231101j:plain

やりたいこと

Electronで作っているアプリで、自動アップデート機能を実装することになったので、アプリから普通にインストーラを起動したら、 イントール時にアプリが閉じてしまうみたいで、親プロセスが死んだ。親プロセスが死ねば必然、子プロセスで起動しているインストール処理も南無阿弥陀仏である。インストール処理は途中で中断されてしまう。 なのでインストーラを子プロセスとしてではなく、別プロセスとしてキックしたい。

調査

ググる

electronでアップデータを配布する簡単な方法 この記事のクライアントのコードにバチっといい感じのコードがあった。

解決策

const { spawn } = require('child_process');

// 外部アプリキック
const child = spawn(path, [], {
  stdio: 'ignore', 
  // 親プロセスが終了しても子プロセスが継続するようにする
  detached: true,
});
// 親プロセスが子プロセスから独立して終了できるようにする
child.unref()

参考

[Electron] electron-builderでビルドした.exeファイルからdefenderアイコン(盾マーク)を外したい

f:id:thegriftsense:20210908000357j:plain

やりたいこと

Electronでビルドした.exeを、自動アップデート処理のためElectronアプリから実行しないといけなくなったけど、 アプリにdefenderアイコンが付いてるせいでwindow.require('child_process').execFile(path)で実行できなかった。 defenderアイコンがついていない.exeは実行できていたので、defenderアイコンをなくせばうまくいくはずだと考えた。

エラー内容

読んでもよくわからないエラー。とにかく.exeを実行できない。

index.js:1 ERROR Error: spawn C:\my-app\exec\installer.exe EACCES
    at Process.ChildProcess._handle.onexit (internal/child_process.js:264)
    at onErrorNT (internal/child_process.js:456)
    at processTicksAndRejections (internal/process/task_queues.js:80)

調査

ググる

エラー内容でググっていたら「ファイルの権限の問題では?」みたいな記事が出てきたのでアクセス権を変えて試してみたけどだめだった。

.exeのアイコンにくっついている盾マークに気づく

そういえば自分がビルドしたアプリにいつからか盾マークがつくようになった。バージョンをさかのぼって盾マークのない.exeをビルドして、その.exeをアプリからwindow.require('child_process').execFile(path)で実行するとちゃんと実行できた。この盾マークのせいか? というかなんで途中からこの盾マークがくっつくようになってるんだ? git checkoutnpm run build を繰り返してどこのコミットから盾マークがつくようになったのか特定する。特定した。URLスキーマからアプリを起動するために入れた処理が原因だった。

原因

URLスキーマからアプリを起動したいという仕様があって、Electron を Custom URL Scheme で起動するこの記事を参考に実装したけど、どうやらこの実装方法の package.json"perMachine": true という記述で盾マークがつくようになるらしい。

"build": {
    ...
    "nsis": {
      "include": "build/installer.nsh",
      "perMachine": true
    },
   ...
}

元凶はわかったとはいえ、この記述はURLスキーマからアプリを起動するために必要な処理なので外すことはできない。詰んだと思いながらも、URLスキーマからアプリを起動する別の方法はないか調べてみると出てきた。しかもElectronのドキュメント。Electron Documentation

解決策

app.setAsDefaultProtocolClient(protocol[, path, args])という関数一行で実装できるらしい。 Electronに関数用意してあるんかーい。ありがとうございます。 原因だったpackage.jsonの記述を消して、Electronが用意してくれている関数でURLスキーマからアプリを開く処理を実装。

app.setAsDefaultProtocolClient('my-protocol');

実際にメインプロセスに記述してビルド。 .exeに盾マークはない。 適当にhtml作って<a src="my-protocol://">アプリ起動テスト</a>書いて起動テスト。 ちゃんとURLスキーマからアプリ起動できたーーーー!

これでURLスキーマからアプリを開ける、盾マークのない.exeができました!

windows defender が嫌い。

参考

Electron Documentation

[Firebase Hosting] Firebase Hosting にデプロイするディレクトリを指定したい

f:id:thegriftsense:20210907230633j:plain

やりたいこと

Firebase Hostingにデプロイするとき、ディレクトリを下記みたいにして view/build をデプロイしたい。

/my-app
  /functions
  /view
    /build

調査

けれど初期設定のまま firebase deploy --only hosting を実行すると、my-app/public がデプロイされる。 このmy-app/publicは firebase.json の hosting の publicで指定されている。

ということは、ただ firebase.json の hosting の設定を変更するだけで良さそうである。

解決

firebase.json の編集

"hosting": {
  "public": "public", // 削除
  "public": "view/build", // 追加
  "ignore": [
    "firebase.json",
    "**/.*",
    "**/node_modules/**"
  ]
}

firebase deploy --only hosting を実行すると、my-app/view/build がデプロイされる。

わーい。終わり!

参考

qiita.com

Cloud Functions のプロジェクトを TypeScript で作成したら `Parsing error: Cannot read file tsconfig.json` というエラーがでた

f:id:thegriftsense:20210906001021j:plain

エラー内容

Parsing error: Cannot read file '/users/yamada/my-app/functions/tsconfig.json'.eslint

tsconfig.tsがルートにないので、ファイルの1行目のimportでエラーが起きる。 原因はVSCodeのESlintの拡張機能らしい。

tsconfig.jsonを読み込むために.vscodeファイルを作成する

ルートディレクトリに.vscodeディレクトリを作成。

.vscode ディレクトリの中に settings.json を作成

{
  "eslint.workingDirectories": [ "./functions" ]
}

tsconfigが読み込まれるようになってエラーが消える。

参考

VS Code ESLint 拡張が Parsing error: Cannot read file エラー config ファイルが読み込めない問題にハマる - かもメモ

[JavaScript] APIからレスポンスでうけっとたファイルデータをローカルのディレクトリに保存したいならbase64してさらにBufferに変換すればよかった

f:id:thegriftsense:20210822225438j:plain

困ったこと

electronでアプリを作っていて、サーバから取得したファイルをwindows pc のAppData/localLow/{appName}に保存する必要がでてきた。自分としてはレスポンスをresponse.blob()として、そのまま fs.writeFileSync(path, value);で保存できると思っていたけど、うまくいかなかった。そこで一個前の案件でも同じような処理があって、その実装を上司がしていたのを思い出してそのソースコードを真似したらちゃんと保存できた。とはいえ上司のコードがなにをしているのかいまいち理解できなかったので調べてまとめておこうともう。

なんかblobをbase64に変換してから保存するらしい

とりあえずコード(自分なりにちょっと変更してる。上司のコードはbase64に変換する処理が関数化されててもっと綺麗)

const response = await fetch('', { method: "GET" });
const blob = await response.blob();
const reader = new FileReader();
reader.onload = () => {
  const base64 = (reader.result as string).split(',')[1];
  const buffer = new Buffer(await this.blobToBase64(v as any), 'base64');
  fs.writeFileSync('./example.txt', buffer);
};
reader.readAsDataURL(blob);

はいはいblobをbase64にしてdata部分を保存するわけですね(自己解決)

ソースコードを見るとレスポンスをblobに変換して、それをさらにbase64に変換してから保存しているらしい。 そういえばこの記事でもreadAdDataURLを使ってたなあ。 でも前はFileオブジェクトをbase64に変換したのであって、今回はBlobオブジェクトの変換。FileオブジェクトとBlobオブジェクトについて自分はほとんどなにも知らないことに思い当たったので、ざっと調べてみるとこんな記述があった。

File オブジェクトは特別な種類の Blob オブジェクトであり、 Blob が利用できる場面ではどこでも利用できます。

へえである。FileオブジェクトはBlobオブジェクトを継承しているみたいな認識でいいのかな?

ところで(reader.result as string).split(',')[1]この部分の処理はなにをしているのだろうか。 と思ったけど、これURLスキームの頭のbata:[<mediatype>][;base64],<data><data>部分だけを取り出してるのか。なるほど。コピペしてるときはなんのことかわからなかったけどいま書いてたら一瞬で理解できてしまった。やったぜ。

ということはつまり、レスポンスで受け取ったファイルをローカルのディレクトリに保存するときは、blobをbase64にしてdata部分をさらにBufferに変換して保存してやればいいということか。おおおお理解できたーーー!

なんかBlobオブジェクトとFileオブジェクトとDataUrlスキーマbase64について少し理解できた気がする。

new Buffer() よりも Buffer.from()の方がいい?

いろんな記事を読んでたら、new Buffer() は脆弱性があるから非推奨だよ! Buffer.from()使おうね! みたいな記事がたくさんあったので、Buffer.from() のほうがいいかも。

おわり。

参考

[GitHubPages] Reactで作成したポートフォリオサイトをGitHubPagesで爆速公開したかった

f:id:thegriftsense:20210818112726j:plain

TypeScriptでreactのプロジェクトを作成する

// {プロジェクト名}の部分は好きなプロジェクト名を入れてください。
$ npx create-react-app {プロジェクト名} --template typescript
// たとえばこんな感じ
$ npx create-react-app my-git-hub-pages --template typescript

GitHubリポジトリを作成する

f:id:thegriftsense:20210818110149p:plain

  • ローカルにリモートリポジトリのurlを登録する

このとき、ディレクトリを先ほど作成したプロジェクトに移動しておく

$ git remote add origin {さっき作成したリモートリポジトリ}

ビルド用のパッケージをインストールする

  • gh-pagesをインストールする
$ npm install gh-pages --save-dev
  • package.jsonにscript追加
"scripts": {
    "deploy": "gh-pages -d build" // このscriptを追加
  },

ビルド&デプロイする

  • ビルドする
$ npm run build
  • デプロイする
$ npm run deploy

確認する

  • 下記のURLにアクセスしてReactのページが表示されればOK

https://{userName}.github.io/

f:id:thegriftsense:20210823111133p:plain

ex) 私のGitHubPagesです

参考資料