Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

インストール先を変更可能にしたい #210

Closed
Hiroshiba opened this issue Sep 8, 2021 · 31 comments
Closed

インストール先を変更可能にしたい #210

Hiroshiba opened this issue Sep 8, 2021 · 31 comments

Comments

@Hiroshiba
Copy link
Member

内容

インストール先を変更したい。

Pros 良くなる点

Cドライブ以外にもインストールできるようになる

Cons 悪くなる点

実装には調査が必要

実現方法

今はelectron-builderのnsisを利用しているけど、設定で変えられたりするのかな・・・?

VOICEVOXのバージョン

0.5.1

@oov
Copy link
Contributor

oov commented Sep 8, 2021

electron-builder は使ったことがないので試してはいませんが、
ドキュメントを読む限りでは allowToChangeInstallationDirectory で変更可能に見えます。

また、perMachine を true にすると Program files 以下へのインストールに変更できそうに読めます。
同じマシン上で複数のユーザーアカウントで別々の場所に VOICEVOX をインストールしなければならない理由はないでしょうし、こちらも有効のほうが良さそうに思えます。

@y-chan
Copy link
Member

y-chan commented Sep 9, 2021

今はかなり簡易的なインストーラーになっていると思います(必要ファイルをダウンロードし、結合して解凍後、フォルダにコピーする形)

electron-builderのnsisでは、インストーラーフレームワーク(?)であるnsisに対して特に制限がかけられているようには見えず、全機能を使えるはずです。
少々インストール手順が複雑化することにはなりそうですが、モダンなUIを用いた、よりインストーラーっぽいものを作っても良いかもです。
https://qiita.com/yuki_nishina/items/10620b20e821291cac07

(nsisについては少々、といったレベルではありますが、知見があるので手が空き次第取り掛かってみたいとは考えています)

@Hiroshiba
Copy link
Member Author

へーー あのよく見かける感じな見た目のダイアログにもできそうですね。
プルリクエスト、いつでも誰でもウェルカムです!

@y-chan
Copy link
Member

y-chan commented Sep 9, 2021

ドキュメントを読む限りでは allowToChangeInstallationDirectory で変更可能に見えます。

image
image

確かに、これだけで出来ましたね

ただ、管理者向けにするとまた2.7GBをダウンロードさせられるのが少しネックかもしれません...

@oov
Copy link
Contributor

oov commented Sep 9, 2021

なるほど、昇格後にやりなおしになってしまうんですね。
ファイルのダウンロード処理を customInit じゃないところで行うか、
あるいは昇格後であることを検出して再ダウンロードを回避できるようにする必要があるのかも知れないですね。

Program files にインストールできるようになると 次へ 連打でインストールしたときに日本語パスが入らなくなるはずなので、
簡単に実現できるなら日本語パスの問題への遭遇率を大きく下げられるかと思ったんですが、
そんなに甘くなさそうな感じでしょうかねぇ。

@HyodaKazuaki
Copy link
Contributor

HyodaKazuaki commented Sep 9, 2021

ただ、管理者向けにするとまた2.7GBをダウンロードさせられるのが少しネックかもしれません...

こちらの件についてですが、electron-builderのテンプレートスクリプトで!insertmacro UAC_RunElevatedが呼び出されていることが原因です。
テンプレートスクリプト側はUACのAdminチェックを行っているので、最初から管理者権限で起動していればUACが呼び出されることはありません。

解決方法としては、build/installer.nshの44行目もしくは45行目に!insertmacro UAC_RunElevatedを差し込めば良いと思います。
ダウンロードが行われるより先に管理者権限を取得しておけば、ダウンロードは1回で済みますし、またTempフォルダもシステム全体のものを利用できるかと思います。

以下build/installer.nsh42行目より

; pre install process
!macro customInit
    !insertmacro defineVariables

    !insertmacro checkDiskSpace

    ; download files

; pre install process
!macro customInit
    !insertmacro defineVariables

    ${ifNot} ${UAC_IsAdmin}
        !insertmacro UAC_RunElevated
    ${endif}

    !insertmacro checkDiskSpace

    ; download files

@y-chan
Copy link
Member

y-chan commented Sep 9, 2021

ファイルのダウンロード処理を customInit じゃないところで行うか

最初から管理者権限で立ち上げる場合であっても、これが一番綺麗だと思って、それが出来ればなぁと思ってはいます(そこまでできる目処は今のところたってませんが)

テンプレートスクリプト側はUACのAdminチェックを行っているので、最初から管理者権限で起動していればUACが呼び出されることはありません。

NSISはデフォルトなら管理者権限で立ち上がった気がしたので、何かしら設定はあるんだろうなと思っていたんですが、やはりあったんですね...!
確認してみます...!

@Hiroshiba
Copy link
Member Author

あー、署名されていないものに管理者権限できれば与えたくないって人もいるかも・・・?
あと署名されていないアプリケーションにwindowsが管理者権限くれないかもしれない。(ググっても不明だったので試したほうが早そう)

@y-chan
Copy link
Member

y-chan commented Sep 9, 2021

署名されていないものに管理者権限できれば与えたくないって人もいるかも・・・?

この懸念は割とあるかもですね...

あと署名されていないアプリケーションにwindowsが管理者権限くれないかもしれない。

最近のMacほど堅苦しくはないはずなので、試してみます

@y-chan
Copy link
Member

y-chan commented Sep 9, 2021

署名されていないものでも問題なく起動できますね。

あと、管理者権限を最初から与えると、いきなりインストール先の指定をさせられる感じになりますね。
シンプルな感じでよいとは思うんですが、この手法では未署名アプリケーションに管理者権限問題は解決しようがないですね...

@oov
Copy link
Contributor

oov commented Sep 9, 2021

今日 Twitter 上で「インストーラーじゃなくて zip のほうが扱いやすいのに」という主旨の話題もあったのですが、
それも踏まえるとレジストリを汚さないいわゆる「ポータブルインストーラー」的な動作も将来的に望まれると思うので、
必要になるまで昇格するべきではないだろうな、とは感じています。

@HyodaKazuaki
Copy link
Contributor

まだベータで正式サービスが始まっていないのでなんとも言えないのですが、OSS向けの無料コードサイニングプロジェクトが中心に立ち上がったようです。
https://www.sigstore.dev/

Windows向けバイナリに適用した場合にコードサイニング証明書として同等の効果を持つかは調査できていませんが、これができるのであれば証明書の問題は解決できそうです。

ポータブルインストーラの件は、別途ダウンロード/結合/展開のみをするスクリプト(バイナリ)を配布したほうが良いかと思います。
現時点のインストーラスクリプトに追加するとかなり複雑になるのではないかと懸念しています。

@Hiroshiba
Copy link
Member Author

Hiroshiba commented Sep 9, 2021

なるほどです。管理者権限周りを整理すると

  • 管理者権限が必要なディレクトリにインストールするには、最初から管理者権限が必要
  • しかしユーザー感情的に署名されていないexeを管理者権限で動かしたくない
  • 署名が手に入るかは要調査

という感じでしょうか。
多分署名取得が可能であっても時間はかかると思うので、とりあえず次バージョンに向けては管理者権限不要なインストール先変更方法を目指すのが良いのかなと思いました。

@Hiroshiba
Copy link
Member Author

ポータブル版(zip版)は実際に配って様子見中です。こちらの反応をもとに優先度を決めても良いかもです。
https://twitter.com/hiho_karuta/status/1435985454109659137

@HyodaKazuaki
Copy link
Contributor

先程コメントを投稿したのですが、もう少し追試したかったので一旦取り下げました。

多分署名取得が可能であっても時間はかかると思うので、とりあえず次バージョンに向けては管理者権限不要なインストール先変更方法を目指すのが良いのかなと思いました。

oneClick: falseallowToChangeInstallationDirectory: trueを指定すると、やはり複数ユーザーにインストールする場合は管理者権限が必要なようでした。
また、perMachine: trueを有効にした場合は起動時に管理者権限を要求するようになります。

つまるところ、electron-builderのカスタムスクリプトだけの場合は任意のディレクトリにインストールする場合は管理者権限が必要になりそうです。
となると、以下のような対応方法が考えられます。

前提

  • vue.config.jsoneClick: falseかつallowToChangeInstallationDirectory: trueとしてインストール先を変更可能なインストーラーを作成するように設定する

課題

インストール先を変更しようとすると、管理者権限を要求されることがある。
その際にインストーラーが再起動するため、ユーザーは再度データをダウンロードしないといけなくなってしまう。

対応とゴール

以下のいずれかの方法でダウンロード済みを検知し、スキップするようにする。
インストール先を変更しないユーザーは、管理者権限を要求されずにインストールすることができる。

方法1

build/installer.nshに以下の機能を追加する。

  • ダウンロード対象のファイルがすでにtempディレクトリにあればダウンロードをスキップする

メリット

  • ダウンロード対象のファイルが存在するかチェックするだけなので実装が容易

デメリット

  • 現在のインストーラーの仕組みではダウンロード対象のファイルのチェックサムを取れないため、ダウンロード対象のファイルが破損していると対応が面倒になる
    最後のファイルをダウンロード中にインストーラーを終了すると、途中までのデータで結合しようとするため必ずインストールに失敗する。
    ユーザーが自らtempディレクトリの破損したファイルを削除する必要がある。

方法2

build/installer.nshに以下の機能を追加する。

  • ダウンロード処理の直後に、ダウンロード完了チェック用のlockファイルを作成する(tempディレクトリなど、lockファイルの中身は何でもOK)
  • lockファイルが存在する場合はダウンロードをスキップする
  • インストール完了の直前(おそらくcustomInstall項目が適切)でlockファイルを削除する

メリット

  • ダウンロードが完了したらlockファイルができるため、方法1より破損の影響を受けづらい
    この方法では、最後のファイルをダウンロード中にインストーラーを終了しても再度ダウンロードが行われる。

デメリット

  • 方法1より実装の手間がかかる
  • 絶対にデータの破損による影響を防げるわけではない
    破損したデータがダウンロードされた場合、ダウンロードは正常に終了したとみなされるためlockファイルが生成されてしまう。
    この場合も破損したファイル(もしくはlockファイル)をユーザーに削除して貰う必要がある。

後者の方法では、lockファイルをインストーラと同じディレクトリに置くようにするだけでユーザーの手間を格段に減らすことができます。
ちょっとダサいですけどね。

また、「自分でインストール先を変更するウィンドウを作ればいいんじゃないのか」という意見もあるかと思いますが、そちらは調査不足で確認できていません。

@oov
Copy link
Contributor

oov commented Sep 10, 2021

二通りのアプローチを提案していただきありがとうございます。
現状取れそうな手段が見えやすくとても助かります。

個人的には提案いただいた両方の方法の合わせ技で、
ロックファイルではなくダウンロードした xxxxx.7z.N 自体をインストーラーと同じ場所に保存し、
そのファイルが存在するかどうかをダウンロード処理を行うかどうかの基準にするのがいいのではないかな?
と感じました。
インストール後のダウンロードファイルの自動削除も無しです。

そうすれば全体インストール時だけではなく再インストール時も無駄なダウンロードを抑制できますし、
エンドユーザーも例えばダウンロードフォルダーを見たときに

VOICEVOX.Web.Setup.0.5.1.exe
VOICEVOX.Web.Setup.0.5.1.7z.0
VOICEVOX.Web.Setup.0.5.1.7z.1

のようにファイルが並んでいれば「インストーラーがなんか追加のファイルを落としてきたんだな」と判断しやすいので、
ダウンロードしたファイルをインストール後に自動削除せず残しておいても問題にはなりにくいのではないかと思いました。

また、1の方法でデメリットとして挙げられているダウンロードキャンセル時の問題は、ブラウザーがやっているように

  1. ダウンロード中は 7z.0.parted などの、一時的に違うファイル名にしておく
  2. ダウンロードが完了したら 7z.0 に変える

というアプローチにすると、キャンセル時だけでなくプロセスの強制終了時にも対応できそうです。
インストーラーを再実行すれば parted は上書きされ、成功すればリネームされるので邪魔にもなりませんし、
ユーザーもダウンロードフォルダーのファイルを削除するだけで済むので手間が少ないと思います。


ファイル破損時の処理に関しては、やるとするならば多分こういう流れですよね。

  1. splitResources.js での 7z ファイル分割時、SHA256 辺りのハッシュ値も一緒に計算しておき、インストーラーに持たせる
  2. エンドユーザーによるインストーラーの実行時、ファイルダウンロード後に certutil 辺りでハッシュを求めて検証

ここまでやった上でインストーラーに署名もあれば、破損だけでなく改ざん対策としても良さそうだと思います。

ただ、ダウンロード完了後にファイル名を変更するアプローチを取れば中途半端なファイルは多くの場合弾けるはずなので、
まともな破損や改ざんなどの対策については一旦保留でも良さそうな気がします。
(ダウンロードプラグインが欠けたファイルをダウンロードして完了と誤判断した場合は検出できずに失敗します)

@Hiroshiba
Copy link
Member Author

わかりやすい説明ありがとうございます! ちゃんと追えていなかったのでとても助かりました。

ダウンロード中に容量いっぱいになって破損ファイルができるのは僕自身経験したので、極力避けたいところです。
lockファイルを使う方法もすごく良いですが、個人的にはダウンロード後にrenameする方法がスマートかなと感じました。

renameする方法だとインストール後に7zファイルを消去できないんでしたっけ。
だとしてもDownloadsディレクトリならたしかに放置でも良いなと思いました!(もちろん自動消去だとなお良い)

@oov
Copy link
Contributor

oov commented Sep 10, 2021

7z の消去自体は特に問題なくできると思います。
私が 個人的に 削除しないことにメリットがあり、削除することにデメリットがあるからやるべきではないと考えているだけです。
(あるいはインストール後に「不要になったダウンロード済みファイルを掃除する」のチェックボックスを付ける、とかでもいいです)

簡単に説明すれば、インストーラーでの UX を zip 版以下にするべきではないから、というのが理由です。

zip ならダウンロードを一度だけすれば何度でも再インストール(解凍)できたし、
zip を他のパソコンに移すことでネット環境なしでもインストール(解凍)できました。
現状のインストーラーではこれがどちらも不可能になっていましたが、
7z.0 などを自動削除しないようにすることで、また可能になります。
また、削除したくなったときにユーザーが消すのは容易ですが、勝手に削除されたものを復元することは難しいです。

誰もが快適な回線環境でファイルをダウンロードするわけではないので、
時間をかけてダウンロードしてくれたファイルの扱いについてはもう少し慎重になってもいいのではないかと思っています。

@HyodaKazuaki
Copy link
Contributor

個人的には提案いただいた両方の方法の合わせ技で、
ロックファイルではなくダウンロードした xxxxx.7z.N 自体をインストーラーと同じ場所に保存し、
そのファイルが存在するかどうかをダウンロード処理を行うかどうかの基準にするのがいいのではないかな?
と感じました。

盲点でした。
その方法の方が1ファイルずつ管理できて良さそうですね。
現時点だと「3つのファイルをダウンロードする」という処理1つにまとめられているので、それを3つに分ければ同等の機能を実装できると思います。
https://github.com/Hiroshiba/voicevox/blob/2e0c0c292e54c4484f915d48c7bd7c26a7946f36/build/installer.nsh#L50

これを3つに分けた場合、ダウンロード時に表示されるダウンロードウィンドウが1回から3回表示されることになります。

また、インストーラーと同じ場所に保存するのもいいアイデアだと思います。
現在のインストーラーがtempディレクトリにVOICEVOX.Web.Setup.0.5.1.7z.Nファイルをダウンロードする理由ですが、VOICEVOX.Web.Setup.0.5.1.7z.Nファイルが一時ファイルであると判断したためです。
ですので、インストーラーと同じ場所にダウンロードしても問題ないかと思います。


インストール後のダウンロードファイルの自動削除も無しです。

zip ならダウンロードを一度だけすれば何度でも再インストール(解凍)できたし、
zip を他のパソコンに移すことでネット環境なしでもインストール(解凍)できました。
現状のインストーラーではこれがどちらも不可能になっていましたが、
7z.0 などを自動削除しないようにすることで、また可能になります。
また、削除したくなったときにユーザーが消すのは容易ですが、勝手に削除されたものを復元することは難しいです。

こちらの件ですが、VOICEVOX.Web.Setup.0.5.1.7z.Nファイルを残すという意味であれば、私としては反対です。
VOICEVOX.Web.Setup.0.5.1.7z.NファイルはあくまでもVOICEVOX.Web.Setup.0.5.1.7zの分割データであって、ZIPデータと同等ではありません。
VOICEVOX.Web.Setup.0.5.1.7zがZIPデータに相当します。
ですので、「インストーラーでの UX を zip 版と同等にする」のであれば、VOICEVOX.Web.Setup.0.5.1.7zを削除する/しないを選択可能にするほうが妥当ではないかと考えます。

また、「一度ダウンロードしたインストーラーをローカルでコピーして他のコンピュータにもインストールしたい」のであれば、以下のようにするほうが良いのかと思います。

  • build/installer.nshに、以下の条件にすべて当てはまる場合は処理をすべてスキップする処理を追加する
    • インストーラーと同じ場所にVOICEVOX.Web.Setup.0.5.1.7zが存在する
    • VOICEVOX.Web.Setup.0.5.1.7zのチェックサムがインストーラー内部に記録されているものと同じ

ファイル破損時の処理に関しては、やるとするならば多分こういう流れですよね。

そのとおりです。分割したファイルのハッシュ値をインストーラーで保持しておいて、そこから実行するか勘案するというものです。
ただ、現在のビルドの順番は以下のとおりになっており、インストーラーに VOICEVOX.Web.Setup.0.5.1.7z.N ハッシュ値を埋め込むことができません。

npm run electron:build_pneverコマンドを実行すると:

  1. プロジェクトがビルドされる
  2. 7ZIP形式に圧縮される(VOICEVOX.Web.Setup.0.5.1.7z)
  3. インストーラーが作成される(electron-builderのテンプレートスクリプト+カスタムスクリプト)
  4. build/splitFile.jsVOICEVOX.Web.Setup.0.5.1.7zが分割される

この順番であれば、VOICEVOX.Web.Setup.0.5.1.7zのハッシュ値はカスタムスクリプトでインストーラーに埋め込むことができると思います。

もしVOICEVOX.Web.Setup.0.5.1.7z.Nでハッシュ値を利用したいのであれば、aptなどのチェックサムと同じような仕組みを使うことになるかと思います。

  1. build/splitFile.jsと同じように後処理としてハッシュ値を計算してテキストファイルに書き出す
  2. GitHub Releaseにハッシュ値が記載されたテキストファイルをアップロードする
  3. インストーラーはそのテキストファイルに書かれたハッシュ値とダウンロードされたファイルから得られるハッシュ値を照合する

@Hiroshiba
Copy link
Member Author

@oov
そもそもインストーラー版にしたいのは、github releaseで配布できてメンテがシンプルだからなんですよね・・・。
とはいえUXも犠牲にしたくないので、僕も @HyodaKazuaki さんと同じく、「.0とかは消して.7zは残し、.0.7zがあれば途中から開始する」が良いと思いました。
.7zのチェックサムも、連結後にrenameする処理にすればスキップできるかも?
(毎回チェックサムをスクリプトに含めないといけないのはミス発生しやすそうなので避けたい)

@oov
Copy link
Contributor

oov commented Sep 11, 2021

これを3つに分けた場合、ダウンロード時に表示されるダウンロードウィンドウが1回から3回表示されることになります。

プラグインのドキュメントを見る限りでは /CAPTION を利用するとタイトルバーの表示テキストを変更できそうに読めます。
この部分、現状ですと Inetc plugin - Downloading 10% という前半がエンドユーザーにとって特に意味のないテキストになっているので、
例えば VOICEVOX インストーラー (1/3) - Downloading 10% のように
「何個中の何個目を処理しているか」という進捗状況が把握できるようにすれば、むしろ今よりもユーザーフレンドリーにできそうだと思いました。


インストーラーに VOICEVOX.Web.Setup.0.5.1.7z.Nの ハッシュ値を埋め込むことができません。

なるほど、現状のビルド順序だと 7z ファイルのハッシュ値はインストーラーに埋め込めても、分割済みファイルのハッシュ値は埋め込めないんですね。
将来的にインストーラーに署名できるようになった際にもインストーラー内部で 7z のハッシュ値を持てれば最終的には改ざんは弾けそうですし、
分割ファイルはハッシュ値リストを別にアップロードしておき、インストーラーがダウンロードして検証する動作でも特に問題はなさそうだと思いました。

.7zのチェックサムも、連結後にrenameする処理にすればスキップできるかも?
(毎回チェックサムをスクリプトに含めないといけないのはミス発生しやすそうなので避けたい)

検証自体はスキップはできます。
単純なファイルの欠損などであればエラーも出るかもしれません。
ただ、署名付きインストーラーの話までを視野に入れるとあまりオススメできません。

例えば悪意ある hosts が注入された環境などで偽の GitHub を用意されてしまうと
署名付きインストーラーによって「ヒホさんのお墨付き」で悪意のあるファイルをインストールできてしまい、
犯罪の便利な踏み台として利用される可能性があります。

これを防ぐためには最低でも結合後のファイルの検証が必要で、
検証に必要なハッシュ値は署名されたファイル内部に持つか信用できるソースから入手しなければならず、
埋め込むのがもっともコストが安いだろう、といった感じです。

@HyodaKazuaki さんの記述はカスタムスクリプトに自動で埋め込めるという話だと理解したのですが、
そうではなく手動で 7z のハッシュをコマンドラインとかで求めてスクリプトをアップデートする、という意味でしょうか……?
現状はリリースにあたり人の手による温かみのある手作業がとても多そうですし、ミスもしそうです。


「インストーラーでの UX を zip 版と同等にする」のであれば、VOICEVOX.Web.Setup.0.5.1.7zを削除する/しないを選択可能にするほうが妥当ではないかと考えます。

なるほど、素晴らしいです。
Windows 標準では 7z ファイルを閲覧・展開することができないため結合したとしても zip と完全に同等の UX は得られないのですが、
仰るようにインストール完了後には分割ファイルよりも結合済みファイルを残すほうが遥かに合理的、理想的だと思います。

ただ、最終的にファイルを結合して 7z を残すのだとしても、異常終了時にも問答無用で 7z.0 などを削除する処理が入っていると
「途中で通信エラーが出た」とか「2つ目のダウンロード中に ESC を押して一度中断した」とか、
何らかの理由でダウンロードが完了されなかった場合にやはり無駄な再ダウンロードが発生しますので、 @Hiroshiba さんが仰るように

「.0とかは消して.7zは残し、.0や.7zがあれば途中から開始する」

というのが、回線環境が良くないユーザーにとっても親切だと思います。


というのも踏まえると、ハッシュ値リストのテキストファイルも存在する場合のインストーラーのダウンロード時の大まかな動作としては、

  1. 同じ場所に 7z があるならスクリプト内に保持しているハッシュ値と一致するか検証し、
    一致するならダウンロードも結合も飛ばして展開へ
    一致しないなら 2 へ進む
  2. ハッシュ値一覧テキストをダウンロード
  3. 同じ場所に 7z.N があるならハッシュ値一覧テキストと検証し、
    一致する場合: 次の 7z.N があるなら次のファイルへ、ないなら 4 へ進む
    一致しない場合: 同じ場所に 7z.N ファイルをダウンロードし、検証をやり直す(ここで一致しない時は該当の 7z.N を削除してエラー終了)
  4. すべての 7z.N が揃ったので、同じ場所に 7z として結合し、ハッシュ値を検証
    (ここで一致しない時は 7z を削除してエラー終了、7z7z.N のどちらかのハッシュ値が間違っているはずなので提供側の問題)
  5. 7z.N をすべて削除

のような流れでしょうか。

@HyodaKazuaki
Copy link
Contributor

例えば VOICEVOX インストーラー (1/3) - Downloading 10% のように
「何個中の何個目を処理しているか」という進捗状況が把握できるようにすれば、むしろ今よりもユーザーフレンドリーにできそうだと思いました。

確かにその方がいくつダウンロードするかも明確になるのでわかりやすいですね。良い機能だと思います。


@HyodaKazuaki さんの記述はカスタムスクリプトに自動で埋め込めるという話だと理解したのですが、
そうではなく手動で 7z のハッシュをコマンドラインとかで求めてスクリプトをアップデートする、という意味でしょうか……?
現状はリリースにあたり人の手による温かみのある手作業がとても多そうですし、ミスもしそうです。

前者を想定していましたが、よく考えると難しいかもしれないですね。
後者はおっしゃるとおり手間がかかるのでやめておいたほうが無難です。

たしか、electron-builderは最初から7ZIPファイルのチェックサムを埋め込んでインストーラーを作成していたと思います。
試しに手元で0.5.1のビルドを行ってインストーラーを作って実行していただければチェックサムエラーを確認いただけます。

ただ、electron-builderが提供しているチェックサム機能はカスタムスクリプトの後で呼び出されます。
また、エラーになるとGitHubから存在しない7ZIPファイルをダウンロードしようとするので、そのままではVOICEVOXで使えません。

そこで、テンプレートスクリプトからチェックサムが代入されている変数を探し出し、カスタムスクリプト内でチェックするようにしたほうが良いかと思います。
カスタムスクリプト内でチェックできれば、7z.Nをダウンロードし直すことも可能になります。


ただ、最終的にファイルを結合して 7z を残すのだとしても、異常終了時にも問答無用で 7z.0 などを削除する処理が入っていると
「途中で通信エラーが出た」とか「2つ目のダウンロード中に ESC を押して一度中断した」とか、
何らかの理由でダウンロードが完了されなかった場合にやはり無駄な再ダウンロードが発生しますので、 @Hiroshiba さんが仰るように

「.0とかは消して.7zは残し、.0や.7zがあれば途中から開始する」

というのが、回線環境が良くないユーザーにとっても親切だと思います。

おっしゃるとおりです。7z.Nファイルを削除するのは現在と同じく結合後にすべきだと思います。


というのも踏まえると、ハッシュ値リストのテキストファイルも存在する場合のインストーラーのダウンロード時の大まかな動作としては、
...
のような流れでしょうか。

そうですね、その流れで良いと思います。
処理がかなり大きくなるので、適宜マクロとして機能を切り出したり、複数ファイルに分けて実装するようにしたほうが良さそうです。

@Hiroshiba
Copy link
Member Author

ハッシュ値の照合ですが、どれくらいの優先度だと考えられていますか? @oov @HyodaKazuaki

というのも、ハッシュ値照合はちょっと実装に時間がかかるかもしれなそう?なのと、
キャッシュ機構さえ作ればzip配布を停止できるかもなので、ハッシュ値照合 < zip配布停止の優先度ならキャッシュ機構だけ先に実装して次のバージョンアップに含めるのも手かなと思った次第です。

個人的にはまあ順番に作るという意味で、ハッシュ値照合が付け足せるようにしつつ先にキャッシュ機構作っちゃうのが良いのかなと思ってます。

@oov
Copy link
Contributor

oov commented Sep 11, 2021

今現在のインストーラーが照合せずに動作していることを踏まえると、
ハッシュ値照合自体は欲しいけど、なくても現状より悪化しないだろうということで後回しでも大丈夫だと思います。

また、ハッシュ値の埋め込み手法についても少し調べました。
JavaScript でexeのリソース編集ができるライブラリがあるようなので、
exe ファイルの作成後に JavaScript から exe ファイルを改変して分割後の 7z のハッシュ値を埋め込むこともできそうですし、
NSIS からは System::Call で Windows API 経由で埋め込んだ値を読み込めそうに見えます。
なので、ハッシュ値埋め込みも実験しながらにはなりますが、現状のビルドの流れを大きく変えずに行えそうに思います。

@HyodaKazuaki
Copy link
Contributor

チェックサムはelectron-builderにあるので、喫緊の課題ではないと言えます。

キャッシュ機構さえ作ればzip配布を停止できるかも

キャッシュ機構があればZIP配布しなくていいというのは、インストーラーをダウンローダーとして使うということでしょうか?
であれば、ダウンロード + キャッシュ + 結合のあとに展開処理をスキップすることができてもいいかもしれませんね。

また、ハッシュ値の埋め込み手法についても少し調べました。

なかなか興味深いです。これは7z.Nのチェックサムを行う場合に使うという認識で間違いないでしょうか?
これを用いてチェックサムを実装するのは確かに時間がかかりそうですね...

electon-builderによるチェックサムの実装は以下にあります。
7zのチェックサムだけであれば、ほとんどコピーして使えるかと思います。
https://github.com/electron-userland/electron-builder/blob/28cb86bdcb6dd0b10e75a69ccd34ece6cca1d204/packages/app-builder-lib/templates/nsis/include/installer.nsh#L17-L70

@oov
Copy link
Contributor

oov commented Sep 11, 2021

試しに手元で0.5.1のビルドを行ってインストーラーを作って実行していただければチェックサムエラーを確認いただけます。

確かにチェックサムが一致しないのが原因で突っぱねられて、GitHub に自分で落としに行こうとして失敗するという流れになるまでを確認しました。
electron-builder が元々想定してない形でファイルをダウンロードしているために、ここで失敗してしまった場合それ以降のロジックは壊れているんですね。
試して色々状況を理解しました。

image

electon-builderによるチェックサムの実装は以下にあります。

こちらも確認しました。
今回に限って言えばおそらく ${APP_64_HASH} が該当のハッシュのようですね。
アプリケーションの性質や時代の流れを見て今更 32bit 版とかはありえないかもしれませんが、
下手なことをせずロジックをそのまま持ってきてハッシュ値を得たほうが良さそうな複雑な構造だと感じました。

これは7z.Nのチェックサムを行う場合に使うという認識で間違いないでしょうか?

そうです。
チェックサムの実装を見る限り、ハッシュ値の埋め込みと取得さえ作ってしまえばチェックサムを求める処理自体は流用できそうですね。

しかしちょっとスクリプトを触ってみましたが、想像以上にだいぶ辛いですね……。
言語仕様とかは泣きながら慣れればいいとしても、
インストーラーのスクリプトをもっと短いサイクルで試せるようにならないとトライアンドエラー自体が重荷な感じです。

@HyodaKazuaki
Copy link
Contributor

しかしちょっとスクリプトを触ってみましたが、想像以上にだいぶ辛いですね……。
言語仕様とかは泣きながら慣れればいいとしても、
インストーラーのスクリプトをもっと短いサイクルで試せるようにならないとトライアンドエラー自体が重荷な感じです。

おっしゃるとおり、ビルドに時間がかかりすぎてトライアンドエラーしていられないのが現状です。
現状、ビルドが遅いのは以下の3つが原因だと考えています。

  • プロジェクトがそれなりに大きくビルドに時間がかかる
  • Electronをダウンロードして展開する必要がある
  • VOICEVOXエンジンをコピーして共に7ZIPに圧縮する必要がある

解決策としては、インストーラー用の軽量なElectronプロジェクトを用意し、そこでカスタムスクリプトを試すというのがあります。
また、Electronのダウンロード/展開もキャッシュできるのではないかと思っています。
ただ、環境が変わってしまうので、以下のような問題が発生すると考えられます。

  • 7ZIPの分割数を直前まで決定することができない
  • VOICEVOXプロジェクト特有の問題を事前に確認することができない

electron-builderのカスタムスクリプトである以上は、ビルドしないとインストーラーの挙動を確認できないのは変わらずといったところです。

@oov
Copy link
Contributor

oov commented Sep 15, 2021

少し時間が経ってしまいましたが、NSIS のスクリプトをある程度学んだのでまずはドラフトでPRを作成しました。
内容的にはほぼ完成しているはずなのですが、もう少しだけ寝かせて改善点を探してからレビューに出したいと思います。

分割ファイルのハッシュ値の照合について

後回しにしようと思っていたのですが、どのようなアプローチが取れるか調査する過程で結局実装してしまいました。
また、それを行う過程で分割数なども取得できるようにしていたら、最終的に手動更新が必要な定数などがなくなりました。

分割ファイルのハッシュ値の exe への埋め込みについて

JavaScript から exe のリソースにデータを埋め込む方法を試しそれは成功したのですが、
NSIS 側で行っている exe の CRC チェックに引っかかり、そのままでは上手く行かないことがわかりました。

破損チェックのためのものなので CRC 自体も更新すれば動くでしょうし、起動オプション /NCRC で無視もできるのですが、
今後おそらく証明書なども electron-builder の流儀で行うのでしょうから道を外れすぎるべきではない、と思いまして、
@HyodaKazuaki さんの仰るようにファイルリストを作成し、それを読み込むことで動作するような方向で進めました。

@oov
Copy link
Contributor

oov commented Sep 17, 2021

お待たせいたしました、レビューお願いします。
もし差し支えなければ、NSIS スクリプトに知見をお持ちの @HyodaKazuaki さんにも是非レビューをお願いできればと思っております。

@HyodaKazuaki
Copy link
Contributor

@oov お疲れさまです。わたしもPR確認させていただきます。
PRの規模に圧倒されているので、しばらくお時間をいただくことになりそうです。
以降PR関連のコメントはPRにさせていただきます。

@Hiroshiba
Copy link
Member Author

先ほどプレリリースしたバージョン0.6.0にて、ちゃんと保存先を変えられそうなことが確認できました。
このissueの課題は解決なので、閉じたいと思います。 お疲れさまでした!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants