unisonでディレクトリを双方向同期する

この記事は、Infocom Advent Calendar 2022 4日目の記事です。

qiita.com

PCをオフライン環境で使いたいときには必要なファイルをコピーして持っていくことになり、作業中に何らかの更新が発生したら作業後はコピー元に戻しますね。
gitで管理すればいいものもあるしソースはそうしてますけど、パワポExcelとかまあいろんな雑多なファイルをgitに全部入れるわけにもいきません。
とはいえ毎回手動でコピーして戻すとなるとミスもするし、毎回毎回手作業でやるのはうんざりするので自動化を考えました。

  • シェルスクリプト/バッチファイルでコピーコマンドを使う
    • 全部コピーするので単純に遅い
    • 戻すのは手作業
  • rsync
    • 一方向しかできない
    • 両方に修正があると詰む
    • 結局、戻すのは手作業
  • unison
    • 両方の修正を検出してそれなりにいい感じにやってくれる。これだ!

unison

どうやら結構古くからあるUnix系のOSSツールのようです。
私は1995年頃からUnix系OSを使っているのですが知りませんでした。
LinuxMacだけじゃなくWindows用のバイナリもあったのはうれしい。
コンフリクトを検出できる。
SSHでも同期できる。
rsyncのような部分転送になっているということでコピーが高速。ファイル数が多ければそれなりに時間はかかりますが、普段使いの感じだと十分早いなと思います。
お手軽。これだいじ。

www.cis.upenn.edu

alliance.seas.upenn.edu

  • インストール

Macだとbrewのcaskがありました。

$ brew install unison
Warning: Treating unison as a formula. For the cask, use homebrew/cask/unison
🍺  /usr/local/Cellar/unison/2.53.0: 9 files, 3.9MB

Windowsはscoopで入れてます。

> scoop install unison
Installing 'unison' (2.53.0) [64bit] from main bucket
unison-v2.53.0+ocaml-4.12.1+x86_64.windows.zip (29.8 MB) [====================================================] 100%
Checking hash of unison-v2.53.0+ocaml-4.12.1+x86_64.windows.zip ... ok.
Extracting unison-v2.53.0+ocaml-4.12.1+x86_64.windows.zip ... done.
Linking ~\scoop\apps\unison\current => ~\scoop\apps\unison\2.53.0
Creating shim for 'unison'.
Creating shim for 'unison-fsmonitor'.
'unison' (2.53.0) was installed successfully!
Notes
-----
Compiled with same OCaml compiler version 4.12.1

main bucketにあったのでbucket追加しなくても普通に入ると思います。

使ってみる

ということで使ってみます。

  • コマンドで実行

unison root1 root2 で起動します。

$ tree foo bar
foo
├── aaa
│   └── hogehoge.pptx
└── hoge.txt
bar  [error opening dir]

$ unison foo bar
Unison 2.52.1 (ocaml 4.12.0): Contacting server...
Looking for changes
Warning: No archive files were found for these roots, whose canonical names are:
    /Users/gozu/Documents/foo
    /Users/gozu/Documents/bar

Reconciling changes

foo            bar
dir      ---->              [f]

Proceed with propagating updates? [] y
Propagating updates

[BGN] Copying  from /Users/gozu/Documents/foo to /Users/gozu/Documents/bar
[END] Copying

$ tree foo bar
foo
├── aaa
│   └── hogehoge.pptx
└── hoge.txt
bar
├── aaa
│   └── hogehoge.pptx
└── hoge.txt

初回なのでまるっとコピーされました。
相手のディレクトリが無ければ単純に複製ができます。

  • bar/hoge.txt を修正して、同期してみる
$ unison foo bar

foo            bar
         <---- new file   .hoge.txt.un~  [f]
         <---- changed    hoge.txt  [f]
         <---- new file   hoge.txt~  [f]

Proceed with propagating updates? [] y
Propagating updates

[BGN] Copying .hoge.txt.un~ from /Users/gozu/Documents/bar to /Users/gozu/Documents/foo
[END] Copying .hoge.txt.un~
[BGN] Updating file hoge.txt from /Users/gozu/Documents/bar to /Users/gozu/Documents/foo
[END] Updating file hoge.txt
[BGN] Copying hoge.txt~ from /Users/gozu/Documents/bar to /Users/gozu/Documents/foo
[END] Copying hoge.txt~

barのファイルがfooへ同期されています。
が、vimの管理ファイルまで同期されてますね。これは設定で除外できます。

  • 双方で同じファイルを更新してみる
$ unison foo bar

foo            bar
changed  <-?-> changed    hoge.txt  [] d

diff -u '/Users/gozu/Documents/bar/hoge.txt' '/Users/gozu/Documents/foo/hoge.txt'

--- /Users/gozu/Documents/bar/hoge.txt  2022-12-02 20:07:40.000000000 +0900
+++ /Users/gozu/Documents/foo/hoge.txt  2022-12-02 20:07:45.000000000 +0900
@@ -1,3 +1 @@
 hello unison.
-hello unison.
-hello unison.

changed  <-?-> changed    hoge.txt  [] x
foo          : changed file       modified on 2022-12-02 at 20:07:45  size 14        rw-r--r--
bar          : changed file       modified on 2022-12-02 at 20:07:40  size 42        rw-r--r--
changed  ====> changed    hoge.txt  [] >

Proceed with propagating updates? [] y
Propagating updates

[BGN] Updating file hoge.txt from /Users/gozu/Documents/foo to /Users/gozu/Documents/bar
[END] Updating file hoge.txt

修正したファイルがテキストファイルなのでdiffを取ることができました。
あとはファイルの日付やサイズを確認したり。
どちらを採用するか > で指定しています。
インタラクティブなコマンドは

atmarkit.itmedia.co.jp

このくらいでなんとかなるかな。

よく使う設定を定義できる

確かにいいんですが、これだとあんまり便利になってませんね。
いらないファイルを除外したり、新しいファイルを採用して欲しいし、いちいち指定せずとも全部自動でやって欲しい。
なので、設定ファイルを書いて自動実行できるようにします。
Macだと ~/.unison ディレクトリを作って、拡張子がprfのファイルを作ります。
Windowsでは C:\Users\アカウント名\.unison です)
default.prf は定義名を指定せずにunisonを実行したときに使われます。
unison 定義名 とすると ~/.unison/定義名.prf が使われます。

私の設定ファイルは以下のようになっています。

includeで読み込むための基本的な設定(common.prf)をしておくと、コピーする対象ごとに設定ファイルを作ることができ、最小の設定ファイルにできます。

$ cat ~/.unison/default.prf
# Unison preferences file
# https://88171.net/unison-manual-ja
# https://www.seas.upenn.edu/~bcpierce/unison/download/releases/stable/unison-manual.html

# 自動実行
batch = true

# ownerも同期する
owner = true

# タイムスタンプをコピーする(ディレクトリはできない)
times = true

# permissionを設定しない
perms = 0

# chmodしない perms=0と組み合わせる
dontchmod = true

# 新しいファイルを優先
prefer = newer

# 新規作成ファイルはユーザに同期如何を問わない
auto = true

# ファイル更新日時による更新有無の判定(Windowsでは更新日時が変わらないことがあるのでたまにfalseにするとよいらしい)
fastcheck = true

# エラー以外の出力停止をしない
silent = false

# 無視するディレクトリやファイル名を指定する
ignore = Name .DS_Store
ignore = Name Icon?
ignore = Name {.*.swp,*.*~}
ignore = Name {*.gsheet,*.gslides,*.gdoc}
$ cat ~/.unison/foo.prf
# Include the contents of the file common
include common

# 同期する対象のルートパスの定義
root = /Users/gozu/Documents/foo/
root = /Volumes/bar/foo/

# バックアップ
backup = Name *
maxbackups = 5
backupprefix = $VERSION.
backupdir = /Volumes/nas/backup/unison/foo

実行は、 unison foo と打つだけです。
こうして設定を分けておく事で、重要性に応じてバックアップの世代数や取るファイルの種類、ディレクトリを変えることができます。

設定のオプションやサンプルが色々載っているので、

https://www.seas.upenn.edu/~bcpierce/unison/download/releases/stable/unison-manual.html

を、見てやってます。DeepLを使うと驚くほどわかりやすく訳してくれます。

使い道

MacWindowsNASなどプラットフォームを越えて個人的なディレクトリを同期するのにはとても便利です。
ファイルを消すときはやっぱり注意が必要。どっちを消しても消える事になるので、rsyncの--deleteオプションより危険な事もあると思う。 更新する人が不特定多数なところで使うのちょっと躊躇しますね。
でも、ログやバックアップがいい感じに取れるので運用次第で使えるのかも?

双方向で同期出来て普通のコピーより早いし差分更新でマルチプラットフォームとなればもっと使われてもいいのになーと思ってこのブログを書きながら何となくググっていたらdocker-syncというツールで使われているというのが分かった。コンテナの中のファイルとホスト側のファイルを同期させるもののようです。
なるほど。いかにもなユースケースですね。

VMWare FusionのWin10をWin11へ上げる

まだ無料アップグレードができるのでやってみる。
Windows 11のシステム要件として64GB以上のシステムDISKとTPMが引っかかったのでまずはこちらを解決しよう。
以下のサイトを参考に実施してみます。

softantenna.com

パーティション拡張する

仮想マシンだから簡単簡単、と思った意外と面倒だった。
VMWare側で拡張は簡単。
設定 > ハードディスク > ディスクサイズ を64GBにするだけ。

ところが起動したら、ブート領域/Cドライブ/回復パーティション/追加領域 になっていて拡張できない…

回復パーティションは設定画面からは消せないので以下のサイトを見ながらやってみる。

pc-karuma.net

C:\WINDOWS\system32>diskpart

DISKPART> list disk

  ディスク      状態           サイズ   空き   ダイナ GPT
  ###                                          ミック
  ------------  -------------  -------  -------  ---  ---
  ディスク 0    オンライン            64 GB    14 GB
  ディスク 1    オンライン            50 GB  1024 KB        *

DISKPART> select disk 0

ディスク 0 が選択されました。

DISKPART> list partition

  Partition ###  Type                Size     Offset
  -------------  ------------------  -------  -------
  Partition 1    プライマリ              579 MB  1024 KB
  Partition 2    プライマリ               48 GB   580 MB
  Partition 3    回復                 553 MB    49 GB

DISKPART> select partition 3

パーティション 3 が選択されました。

DISKPART> list partition

  Partition ###  Type                Size     Offset
  -------------  ------------------  -------  -------
  Partition 1    プライマリ              579 MB  1024 KB
  Partition 2    プライマリ               48 GB   580 MB
* Partition 3    回復                 553 MB    49 GB

DISKPART> delete partition override

DiskPart は選択されたパーティションを正常に削除しました。

DISKPART> list partition

  Partition ###  Type                Size     Offset
  -------------  ------------------  -------  -------
  Partition 1    プライマリ              579 MB  1024 KB
  Partition 2    プライマリ               48 GB   580 MB

DISKPART>

これで消えました。(Windows 11を入れたら復活させるかな)
システムパーティションを拡張できるようになったので全部まとめてしまう。

仮想マシンTPMを追加する

DISKは無事拡張できたので残りはTPMです。 手順としては、

  1. 起動ディスクをMBRからUEFIブートへ変更
  2. VMWareの設定でファームウエアをUEFIへ変更
  3. VMWareの設定でUEFIセキュアブートを有効化にチェック
  4. VMWareの設定で暗号化を有効にするをチェック
  5. Windows 11 インストール

という感じです。

起動ディスクをレガシーBIOSからUEFIへ変更する

VMWareの設定ではレガシーBIOSと出ていますがいわゆる昔ながらのMBRレコードから起動するように仮想マシンを作っていたのでこれをUEFIにするにはディスクをGPTへ変更しなければなりません。
この辺の操作は危険なのでスナップショットを取ってから作業を行います。
仮想マシンを起動したら管理者モードでコマンドプロンプトを起動して、mbr2gptコマンドで実施します。

まず確認ですが早速エラー。

C:\WINDOWS\system32>mbr2gpt /validate /disk:0
ERROR: MBR2GPT can only be used from the Windows Preinstallation Environment. Use /allowFullOS to override.

C:\WINDOWS\system32>mbr2gpt /validate /disk:0 /allowFullOS
MBR2GPT: Attempting to validate disk 0
MBR2GPT: Retrieving layout of disk
MBR2GPT: Validating layout, disk sector size is: 512 bytes
Cannot find OS partition(s) for disk 0

answers.microsoft.com

www.diskpart.com

ググってみるとどうやらWindowsのバージョンアップを行った際に残ったBCD構成ファイル内のゴミがいけないとのこと。消してみましょう。

まず不可視のブートパーティションをいじれるようにします。

C:\WINDOWS\system32>diskpart

DISKPART> select disk 0

ディスク 0 が選択されました。

DISKPART> list partition

  Partition ###  Type                Size     Offset
  -------------  ------------------  -------  -------
  Partition 1    プライマリ              579 MB  1024 KB
  Partition 2    プライマリ               63 GB   580 MB

DISKPART> select partition 2

パーティション 2 が選択されました。

DISKPART> assign letter=w:

仮想ディスク サービス エラー:
現在のブート ボリュームまたはページ ファイル ボリュームに対して、
ドライブ文字を割り当てたり、割り当てを解除することはできません。


DISKPART> select partition 1

パーティション 1 が選択されました。

DISKPART> assign letter=b:

DiskPart はドライブ文字またはマウント ポイントを正常に割り当てました。

DISKPART> exit

DiskPart を終了しています...

元記事ではシステムパーティションをWドライブに退避して作業を行なっていましたが、Cドライブから変えられませんでした。
ブートパーティションはBドライブに変えられたのでこのまま作業を続行します。

C:\WINDOWS\system32>dir /a b:
 ドライブ B のボリューム ラベルは システムで予約済み です
 ボリューム シリアル番号は 3CA5-6110 です

 B:\ のディレクトリ

2022/03/21  22:26    <DIR>          Boot
2022/03/21  21:42           413,880 bootmgr
2019/12/07  18:08                 1 BOOTNXT
2021/06/02  14:50             8,192 BOOTSECT.BAK
2020/12/25  11:02    <DIR>          System Volume Information
               3 個のファイル             422,073 バイト
               2 個のディレクトリ     572,878,848 バイトの空き領域

ブートパーティション見えてますね。

bcdeditコマンドでunknownになっているidentifierを探します。
今回は {1cea4e0a-4655-11eb-b8e8-86cd67d75de6} ですね。

C:\WINDOWS\system32>bcdedit /store B:\boot\bcd /enum all

Windows ブート マネージャー
--------------------------------
identifier              {bootmgr}
device                  partition=B:
description             Windows Boot Manager
locale                  ja-JP
inherit                 {globalsettings}
default                 {default}
resumeobject            {1cea4e07-4655-11eb-b8e8-86cd67d75de6}
displayorder            {default}
toolsdisplayorder       {memdiag}
timeout                 30

Windows ブート ローダー
--------------------------------
identifier              {default}
device                  partition=C:
path                    \WINDOWS\system32\winload.exe
description             Windows 10
locale                  ja-JP
inherit                 {bootloadersettings}
recoverysequence        {1cea4e0a-4655-11eb-b8e8-86cd67d75de6}
displaymessageoverride  Recovery
recoveryenabled         Yes
allowedinmemorysettings 0x15000075
osdevice                partition=C:
systemroot              \WINDOWS
resumeobject            {1cea4e07-4655-11eb-b8e8-86cd67d75de6}
nx                      OptIn
bootmenupolicy          Standard

Windows ブート ローダー
--------------------------------
identifier              {1cea4e0a-4655-11eb-b8e8-86cd67d75de6}
device                  ramdisk=[unknown]\Recovery\WindowsRE\Winre.wim,{1cea4e0b-4655-11eb-b8e8-86cd67d75de6}
path                    \windows\system32\winload.exe
description             Windows Recovery Environment
locale                  ja-JP
inherit                 {bootloadersettings}
displaymessage          Recovery
osdevice                ramdisk=[unknown]\Recovery\WindowsRE\Winre.wim,{1cea4e0b-4655-11eb-b8e8-86cd67d75de6}
systemroot              \windows
nx                      OptIn
bootmenupolicy          Standard
winpe                   Yes

休止状態からの再開
--------------------------------
identifier              {1cea4e07-4655-11eb-b8e8-86cd67d75de6}
device                  partition=C:
path                    \WINDOWS\system32\winresume.exe
description             Windows Resume Application
locale                  ja-JP
inherit                 {resumeloadersettings}
recoverysequence        {1cea4e0a-4655-11eb-b8e8-86cd67d75de6}
recoveryenabled         Yes
allowedinmemorysettings 0x15000075
filedevice              partition=C:
filepath                \hiberfil.sys
bootmenupolicy          Standard
debugoptionenabled      No

Windows メモリ テスター
--------------------------------
identifier              {memdiag}
device                  partition=B:
path                    \boot\memtest.exe
description             Windows メモリ診断ツール
locale                  ja-JP
inherit                 {globalsettings}
badmemoryaccess         Yes

EMS 設定
--------------------------------
identifier              {emssettings}
bootems                 No

デバッガー設定
--------------------------------
identifier              {dbgsettings}
debugtype               Local

RAM 不良
--------------------------------
identifier              {badmemory}

グローバル設定
--------------------------------
identifier              {globalsettings}
inherit                 {dbgsettings}
                        {emssettings}
                        {badmemory}

ブート ローダー設定
--------------------------------
identifier              {bootloadersettings}
inherit                 {globalsettings}
                        {hypervisorsettings}

ハイパーバイザー設定
-------------------
identifier              {hypervisorsettings}
hypervisordebugtype     Serial
hypervisordebugport     1
hypervisorbaudrate      115200

再開ローダー設定
--------------------------------
identifier              {resumeloadersettings}
inherit                 {globalsettings}

こいつを消します。

C:\WINDOWS\system32>bcdedit /store B:\boot\bcd /delete {1cea4e0a-4655-11eb-b8e8-86cd67d75de6}
この操作を正しく終了しました。


C:\WINDOWS\system32>bcdedit /store B:\boot\bcd /enum all

Windows ブート マネージャー
--------------------------------
identifier              {bootmgr}
device                  partition=B:
description             Windows Boot Manager
locale                  ja-JP
inherit                 {globalsettings}
default                 {default}
resumeobject            {1cea4e07-4655-11eb-b8e8-86cd67d75de6}
displayorder            {default}
toolsdisplayorder       {memdiag}
timeout                 30

Windows ブート ローダー
--------------------------------
identifier              {default}
device                  partition=C:
path                    \WINDOWS\system32\winload.exe
description             Windows 10
locale                  ja-JP
inherit                 {bootloadersettings}
displaymessageoverride  Recovery
recoveryenabled         Yes
allowedinmemorysettings 0x15000075
osdevice                partition=C:
systemroot              \WINDOWS
resumeobject            {1cea4e07-4655-11eb-b8e8-86cd67d75de6}
nx                      OptIn
bootmenupolicy          Standard

休止状態からの再開
--------------------------------
identifier              {1cea4e07-4655-11eb-b8e8-86cd67d75de6}
device                  partition=C:
path                    \WINDOWS\system32\winresume.exe
description             Windows Resume Application
locale                  ja-JP
inherit                 {resumeloadersettings}
recoveryenabled         Yes
allowedinmemorysettings 0x15000075
filedevice              partition=C:
filepath                \hiberfil.sys
bootmenupolicy          Standard
debugoptionenabled      No

Windows メモリ テスター
--------------------------------
identifier              {memdiag}
device                  partition=B:
path                    \boot\memtest.exe
description             Windows メモリ診断ツール
locale                  ja-JP
inherit                 {globalsettings}
badmemoryaccess         Yes

EMS 設定
--------------------------------
identifier              {emssettings}
bootems                 No

デバッガー設定
--------------------------------
identifier              {dbgsettings}
debugtype               Local

RAM 不良
--------------------------------
identifier              {badmemory}

グローバル設定
--------------------------------
identifier              {globalsettings}
inherit                 {dbgsettings}
                        {emssettings}
                        {badmemory}

ブート ローダー設定
--------------------------------
identifier              {bootloadersettings}
inherit                 {globalsettings}
                        {hypervisorsettings}

ハイパーバイザー設定
-------------------
identifier              {hypervisorsettings}
hypervisordebugtype     Serial
hypervisordebugport     1
hypervisorbaudrate      115200

再開ローダー設定
--------------------------------
identifier              {resumeloadersettings}
inherit                 {globalsettings}

もうmbr2gptを一回実行してみます。

C:\WINDOWS\system32>mbr2gpt /validate /disk:0 /allowFullOS
MBR2GPT: Attempting to validate disk 0
MBR2GPT: Retrieving layout of disk
MBR2GPT: Validating layout, disk sector size is: 512 bytes
MBR2GPT: Validation completed successfully

うまくいったようです。

ではGPTへ変換してみましょう。

C:\WINDOWS\system32>mbr2gpt /convert /disk:0 /allowFullOS

MBR2GPT will now attempt to convert disk 0.
If conversion is successful the disk can only be booted in GPT mode.
These changes cannot be undone!

MBR2GPT: Attempting to convert disk 0
MBR2GPT: Retrieving layout of disk
MBR2GPT: Validating layout, disk sector size is: 512 bytes
MBR2GPT: Trying to shrink the OS partition
MBR2GPT: Creating the EFI system partition
MBR2GPT: Installing the new boot files
MBR2GPT: Performing the layout conversion
MBR2GPT: Migrating default boot entry
MBR2GPT: Fixing drive letter mapping
MBR2GPT: Conversion completed successfully
Call WinReReapir to repair WinRE
MBR2GPT: Failed to update ReAgent.xml, please try to  manually disable and enable WinRE.
MBR2GPT: Before the new system can boot properly you need to switch the firmware to boot to UEFI mode!

回復パーティションを消したから怒られてるみたいだけど、一応GPTにはできたっぽいですね。
シャットダウンしてVMWareの設定を変えます。

VMWareの設定を変更する

VMWare Fusionの設定をレガシーBIOSUEFI

セキュアブートにする前に起動して確認します。

うまくいったのでシャットダウンしてからセキュアブートにします。

お次は、暗号化を有効にする にチェックです。
これをしないとTPMを追加できません。
DISK容量が足りないと暗号化できません。

最後にTPMモジュール追加です。

バイスを追加するだけですが、元には戻せないと言われます。

Windows 11のインストール

正常性のチェックは通るのに、WindowsUpdateの画面を開くとシステム要件を満たしてないと言われたので、WindowsUpdateしてみましたが変わらず。

でも、Windows11 インストール アシスタントを実行してみたところインストールできました!

追記

Windows 11にしたら自動的に回復パーティションが作成されていました。
作り直す必要なかったですね。
さらにEFIシステムパーティションというものも追加されています。
BドライブにしていたのはGUIからドライブレターの削除ができました。

1Password 8に上げてSSHキーを管理出来るようにする

1Password 8で見た目が変わったり機能が増えたりしていますが、自分が一番おっと思ったのはSSHキーを管理出来るという所ですね。
というわけで、メモがてら記事にしていこうと思います。

インストール

いま(2022/7/9)時点でまだMac AppStoreには8はきていないのでダウンロードします。

1password.com

1Password Installer.app を起動すれば自動的にアップデートされます。

だいぶ見た目が変わってます。(ダークモードに対応したのね)

設定

まずは設定を有効にします。
1Password > 環境設定 > 開発者
を開きます。

デフォルトだと何もチェックがついてない状態ですね。

SSHエージェントを使用する にチェックします。
1Password CLI用の生体認証ロック解除op というコマンドを使うようなのだがとりあえず今は使わないのでそのままです。

1PasswordがSSHキー名をディスクに保存出来るようにします を許可します。
FingerPrintを表示されても分からんしね。

~/.ssh/config に表示されているスニペットを追加します。(xxxxxxxxxxのところのパスは環境によって違うようです)

Host *
    IdentityAgent "~/Library/Group Containers/xxxxxxxxxx.com.1password/t/agent.sock"

Remember key approval が、until 1Password locks でも until 1Password quits でもどちらにしろロックしたらTouch IDが出てくるので、自動ロックの間隔を調整してもいいんじゃないでしょうか。
デフォルトの10分は自分には厳しすぎます。
1Password > 環境設定 > セキュリティ > 自動ロック
で、変更しちゃいます。

既存のキーを使ってSSHする

ラズパイへのSSHキーを1Passwordで管理してみます。
キーを作るときにEd25519かRSA(2048ビット以上)にしていない場合は作り直す必要があります。
お勧めはEd25519らしいです。

とりあえず既にあるRSAの鍵をインポートします。
+新規アイテム > SSHキー で登録します。

秘密鍵を追加 からキーファイルを読み込む でもいいし、ラズパイからわざわざ持ってこなこなくても クリップボードから鍵を張り付け で登録出来ます。
既存のキーならローカルにあるかもしれないですが、クリップボード貼り付けができるのは地味に便利。
パスフレーズが設定されていれば入力画面が出てきます。

キーが登録出来ました。

それではログインしてみます。
既存のキーなので ~/.ssh/authorized_keys は設定済みの想定です。

Touch IDでログイン出来ました!

ssh-keygenを使わずに1Passwordで新しいキーを作る事も出来ます。
その場合、秘密鍵をどこにもコピーせずに公開鍵をauthorized_keysに登録するだけなのでちょっと安心ですね。

Git Cloneできるようにする

個人ではGitHub、仕事ではBitbucketを使っているので両方試してみます。

GitHub

まずGitHubから。
Settings > SSH and GPG keys へ行きます。

New SSH keyを押します。

Titleをクリックすると、1Passwordのブラウザ拡張が入っていれば SSHキーの作成 が出ます。

作成と入力を押すとキーが作成されて1Passwordへ登録され、GitHubの画面に反映されますので、Add SSH key を押して登録します。

それではCloneしてみます。
まずはCLIで。

git clone git@github.com:gozuk16/test.git
Cloning into 'test'...
remote: Enumerating objects: 35, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 35 (delta 5), reused 14 (delta 5), pack-reused 21
Receiving objects: 100% (35/35), 5.04 KiB | 1.26 MiB/s, done.
Resolving deltas: 100% (9/9), done.

出来ました。

次に自分が使ってるGitクライアントのForkでやってみます。
CloneするときのプロトコルhttpsからSSHに切り替えます。

1PasswordがTouch IDの画面を出してくれるのでピッと触ればCloneできます。

うーん便利!

ちなみに、GitHubは2023年末までにTwo-factor authenticationを設定せよというわけで2FAも1Passwordで設定しています。
Sign inの時に1Passwordでパスワードを入力すると遷移先ではtokenが自動でセットされるので便利です。(まあいいという事にしといて...)

Bitbucket

基本的には同じです。
Personal settings > SSH 鍵 > 鍵を追加 でGitHubと同じように出来ます。

その他

  • いまのところ保管庫はデフォルトで作られるPersonalじゃないとダメみたいです

増えてくるとちょっと分かりにくくなるので、別の保管庫も何れ使えるようになるといいな。

  • TailscaleにSSHの接続管理をしてくれる機能が追加されたようです

tailscale.com

codezine.jp

TailscaleのエージェントにSSHを内包してるのかな?
sshdを立てる必要すらないという事で、結構すごいなーと思った。
こうなると1Passwordとか言ってる次元じゃないよね。
調べようと思ってるけど、いまのところ会社でTailscale使えないので何となく先送りしてる。。。

iTerm2のプロファイルごとに表示する仮想デスクトップ(スペース)を切り替える

自分はMacの仮想デスクトップを常時2つ使っていてデスクトップ1を通常業務に、デスクトップ2はコード書き用にして、iTerm2のプロファイルも通常業務用とコード書き用に分けています。
iTerm2を起動したら任意のスペースに表示するっぽい設定があるのにうまく出来ていませんでした。
改めて試してみたらできちゃったのでメモしておきます。

まずスペースの設定は、 Preferences > Profiles > Window > Space です。
多分そうだろうと思っていたけどここで間違いないです。
ここで表示したいスペースを選びます。
自分だったら通常業務用の desktop1 プロファイルは Space 1に、コード書き用の desktop2 プロファイルは Space 2 に設定します。

ここで、スペースを選ぶと注意喚起のメッセージが出ます。

これを無視していたのがよくなかった…
ここに書いてあるように、システム環境設定で ^ 1 とか ^ 2 を有効にしないといけません。

システム環境設定 > キーボード > ショートカット > Mission Control で、 デスクトップ1へ切り替え とか デスクトップ2 へ切り替えと出ているのを必要な分だけチェックします。

最後にDockのアイコンから設定を確認します。
iTerm2アイコン右クリック > オプション > 割り当て先 です。
デフォルトで なし になってると思いますが、自分はうまくいかなくていじってたので なし に戻します。

これで、プロファイル毎に表示するデスクトップを固定することが出来ました!

gopsutilで温度情報を取得する(OS別)

この記事は、Infocom Advent Calendar 2021 7日目の記事です。

qiita.com

あまり業務とは関係ないのですが、shirou/gopsutilを使ってサーバ情報を表示するアプリを作っている過程で、各種温度情報が取れるのが分かりました。 ただし、MacLinuxWindowsでそれぞれ違っていて、試行錯誤したので記録に残しておきます。

今年のアドベントカレンダーは全然埋まってないので、皆さん頑張ってw

Mac

開発をしているのがMacなのでまずはMacでとれるようにします。
個人で使っているMacbook Air(Intel) Mojaveです。

package main

import (
    "fmt"
    "os"

    "github.com/shirou/gopsutil/v3/host"
)

func main() {
    h1, h2, h3, _ := host.PlatformInformation()
    fmt.Println(h1, h2, h3)
    t, err := host.SensorsTemperatures()
    if err != nil {
        fmt.Println(err.Error())
        os.Exit(1)
    }
    for _, v := range t {
        fmt.Printf("sensorKey: %s, temperature: %2.0f, sensorHigh: %2.0f, sensorCritical: %2.0f\n", v.SensorKey, v.Temperature, v.High, v.Critical)
    }
}

実行してみます。

$ go run main.go
darwin Standalone Workstation 11.5.1
sensorKey: TA0P, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TA1P, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TC0D, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TC0H, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TC0P, temperature: 58, sensorHigh:  0, sensorCritical:  0
sensorKey: TB0T, temperature: 36, sensorHigh:  0, sensorCritical:  0
sensorKey: TB1T, temperature: 36, sensorHigh:  0, sensorCritical:  0
sensorKey: TB2T, temperature: 32, sensorHigh:  0, sensorCritical:  0
sensorKey: TB3T, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TG0D, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TG0H, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TG0P, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TH0P, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TM0S, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TM0P, temperature: 54, sensorHigh:  0, sensorCritical:  0
sensorKey: TN0H, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TN0D, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TN0P, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TI0P, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TI1P, temperature:  0, sensorHigh:  0, sensorCritical:  0
sensorKey: TW0P, temperature: 49, sensorHigh:  0, sensorCritical:  0

TA0Pとかは、smc_darwin.hで定義されているようです。

https://github.com/shirou/gopsutil/blob/master/v3/host/smc_darwin.h

#define AMBIENT_AIR_0          "TA0P"
#define AMBIENT_AIR_1          "TA1P"
#define CPU_0_DIODE            "TC0D"
#define CPU_0_HEATSINK         "TC0H"
#define CPU_0_PROXIMITY        "TC0P"
#define ENCLOSURE_BASE_0       "TB0T"
#define ENCLOSURE_BASE_1       "TB1T"
#define ENCLOSURE_BASE_2       "TB2T"
#define ENCLOSURE_BASE_3       "TB3T"
#define GPU_0_DIODE            "TG0D"
#define GPU_0_HEATSINK         "TG0H"
#define GPU_0_PROXIMITY        "TG0P"
#define HARD_DRIVE_BAY         "TH0P"
#define MEMORY_SLOT_0          "TM0S"
#define MEMORY_SLOTS_PROXIMITY "TM0P"
#define NORTHBRIDGE            "TN0H"
#define NORTHBRIDGE_DIODE      "TN0D"
#define NORTHBRIDGE_PROXIMITY  "TN0P"
#define THUNDERBOLT_0          "TI0P"
#define THUNDERBOLT_1          "TI1P"
#define WIRELESS_MODULE        "TW0P"

CPU関係では、TC0DとTC0Hは取れておらず、TC0Pだけ取れているようです。
PROXIMITYは近い・近接的な意味のようなのでCPU温度を見るならとりあえずTC0Pでよいのかなと思いました。

Windows

お仕事ではWindowsサーバを使うのでこちらも取得してみます。

とりあえず同じコードを実行してみます。

C:\home\gozu\go\src\github.com\gozuk16\cputemp>go run main.go
Microsoft Windows 10 Pro Standalone Workstation 10.0.19042 Build 19042
例外が発生しました。 (アクセスは拒否されました )
exit status 1

どうやらWindowsでは管理者権限が必要みたいですね。
管理者権限で実行してみます。

C:\home\gozu\go\src\github.com\gozuk16\cputemp>go run main.go
Microsoft Windows 10 Pro Standalone Workstation 10.0.19043 Build 19043
sensorKey: ACPI\ThermalZone\THM0_0, temperature: 40, sensorHigh:  0, sensorCritical:  0

ACPI\ThermalZone\THM0_0というのが1つだけ取れました。

これはWMIの情報のようです。wmicで確認のため同じものを取得してみます。 (この場合も管理者権限が必要です)

> wmic /namespace:\\root\wmi PATH MSAcpi_ThermalZoneTemperature get CurrentTemperature
Temperature
CurrentTemperature
3132

3132度???
いやいやそんなわけない。
これはケルビンという値を10倍したものが取れるものらしい。
ケルビンとは絶対温度の事で、摂氏を求めるには「ケルビン - 273.16」とすればよい
つまり、摂氏に直すと 3132 / 10 - 273.16 = 40.04 となります。
どうやらプログラムで取った値は正しそうですね。

管理者権限が必要なのは面倒くさいなぁ。

Linux

VMじゃない実機のLinuxだとラズパイがあるのでやってみました。

Raspberry Pi OS 64bit版にしてあるのですが、どうやらaptで入るGoは1.11という古いバージョンなのでダウンロードして入れます。

$ wget https://golang.org/dl/go1.16.7.linux-arm64.tar.gz
$ sudo tar -C /usr/local -xzf go1.16.7.linux-arm64.tar.gz
$ vi .bashrc
# ↓を追加
export PATH=$PATH:/usr/local/go/bin
$ . .bashrc
$ go version
go version go1.16.7 linux/arm64

同じものを実行します。

$ go run main.go
raspbian debian 10.10
sensorKey: cpu_thermal, temperature: 62 sensorHigh:  0, sensorCritical:  0

cpu_thermalというのが1つ取れました。
確認のためコマンドでたたいてみます。

$ cat /sys/class/thermal/thermal_zone0/temp
62322

62322度な訳は無いw
これは1000倍で取れるそうなのであってます。

docker composeでInfluxDBの起動エラー

久しぶりにInfluxDBのdockerを起動したら動かなくなってた。

$ docker compose up
[+] Running 2/2
 ⠿ Network influxdb_default       Created                                                                                                        0.0s
 ⠿ Container influxdb_influxdb_1  Created                                                                                                        0.0s
Attaching to influxdb_1
influxdb_1  | chown: changing ownership of '/var/lib/influxdb2': Permission denied
influxdb_1  | chown: changing ownership of '/var/lib/influxdb2/engine': Permission denied
influxdb_1 exited with code 1

ぐぐったらこれが引っかかった。

github.com

user を追加したら動いた。

version: '3'
services:
  influxdb:
    image: influxdb
    ports:
      - "8086:8086"
    volumes:
      - ./influxdb/data:/var/lib/influxdb
      - ./influxdb/config:/etc/influxdb2
      - ./influxdb/scripts:/docker-entrypoint-initdb.d
    environment:
      - DOCKER_INFLUXDB_INIT_MODE=setup
      - DOCKER_INFLUXDB_INIT_USERNAME=ユーザ名
      - DOCKER_INFLUXDB_INIT_PASSWORD=パスワード
      - DOCKER_INFLUXDB_INIT_ORG=組織を指定
      - DOCKER_INFLUXDB_INIT_BUCKET=バケットを指定
      - DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=トークンをここに書くよ
    user: "501:501"

Karabiner-elementsでMacのPowerPointの行末移動をctrl+eで出来るようにする

MacPowerPointでCtrl+eと打つと「文字列中央揃え」になるんですよね。
これ間違いすぎてストレスたまりすぎたのでKarabinerで行末移動になるようにしました。

frontmost_application_if でオフィス系ソフトとブラウザを指定していますが、 オフィス系ソフトとChromeは上手くいっていて、Firefox、Edge、Safariがまだ上手くいってません。
うーんなんだろう。

    {
      "description": "Left Control + e to End",
      "manipulators": [
        {
          "type": "basic",
          "from": {
            "key_code": "e",
            "modifiers": {
              "mandatory": [
                "control"
              ]
            }
          },
          "to": [
            {
              "key_code": "end"
            }
          ],
      "conditions": [
            {
              "type": "frontmost_application_if",
              "bundle_identifiers": [
                "^com\\.microsoft\\.Excel$",
                "^com\\.microsoft\\.Powerpoint$",
                "^com\\.microsoft\\.Word$",
                "^org\\.mozilla\\.firefox$",
                "^org\\.google\\.Chrome$",
                "^com\\.microsoft\\.edgemac$",
                "^com\\.apple\\.Safari$"
              ]
            }
      ]
        }
      ]
    }