俺様サーバー構築記 - Subversionをインストールして /etc をバージョン管理@全機

# 序

「[俺様サーバー構築記 - 基本方針](https://qiita.com/katz_engineer/items/dbabcff4e14e9db2b10f)」に基づく作業の続き。[LTS版Linuxカーネルの使用を決心](https://qiita.com/katz_engineer/items/70e6a75581c60426c836)してから、私の手持ちで生きているマシンをすべてフォーマットし直しました。

参考文献1: [俺様サーバー構築記 - ZFSをarchisoに埋め込む(linux-lts)@VirtualBox(UEFI)](https://qiita.com/katz_engineer/items/70e6a75581c60426c836)
参考文献2: [俺様サーバー構築記 - ZFS の上に Arch Linux をインストール; やり直し(Linux-LTS)@ノートパソコン(BIOS)](https://qiita.com/katz_engineer/items/3ec8f7bfd3051e60586b)
参考文献3: [俺様サーバー構築記 - ZFS の上に Arch Linux と無線LANをインストール; やり直し(Linux-LTS)@ノートパソコン(UEFI+wifi)](https://qiita.com/katz_engineer/items/128364a91633f83fd412)
参考文献4: [俺様サーバー構築記 - ZFS の上に Arch Linux をインストール; やり直し(Linux-LTS)@デスクトップパソコン(BIOS)](https://qiita.com/katz_engineer/items/5f02b4ca2d989bb5e862)

折角なので、気になっていた細かい点を修正してしまいます。

まずは ```/etc``` のバージョン管理。Subversion を使うのは相変わらずですが、そのリポジトリはローカルに作成します。ArchLinuxのアップデート事故などで```zfs rollback```した時に、リポジトリも一緒に巻き戻ってくれないと、結構面倒だったのでした。これはまず最初に仕込んでおきたいですな。

参考文献5: [俺様サーバー構築記 - Subversionサーバーの構築@デスクトップパソコン](https://qiita.com/katz_engineer/items/b893316b4d1bb7731e87)
参考文献6: [俺様サーバー構築記 - Subversion による /etc のバージョン管理@ノートパソコン](https://qiita.com/katz_engineer/items/c9afdcb0e2355ed67ede)
参考文献7: [俺様サーバー構築記 - Subversion による /etc のバージョン管理@デスクトップパソコン](https://qiita.com/katz_engineer/items/357e9d8efa71b17deec8)

# 準備

## システムのアップデート

何はともあれスナップショット。これを気軽に実行できるのが zfs （及び btrfs ）の素晴らしい点です。そしてシステム全体のスナップショットを撮っておけば、何かあってもすぐに復旧できます。

ちなみに、システム関連なので対象は ```tank/main``` だけです。```tank/sub0``` 以下については除外します。

```console
# zfs snapshot tank/main@$(date +%Y%m%d_%H%M%S)_before_update
```

心置きなくアップデート。

```console
# pacman -Syy --noprogress
:: Synchronizing package databases...
downloading core.db...
downloading extra.db...
downloading community.db...
downloading archzfs.db...
# pacman -Syu
〈省略; 内容は毎回異なる〉
```

## パッケージインストール前のスナップショット

スナップショットはガンガン撮りまくりましょう！HDDの逼迫は心配無用。内部的には差分管理と同じ形になっているので、そんなに消費しません。それでも問題になったら、その時に古いのを削除すればいいんです。

```console
# zfs snapshot tank/main@$(date +%Y%m%d_%H%M%S)_before_svn
```

スナップショットを確認します。さっきのアップデート前の物も一緒に見てみます。

オプション```-S```（Sは大文字）でスナップショット名の逆順を指定しているのがミソ。名前に日時を付けているので、新しい順に表示されます。説明記事を書いたりエビデンスを揃えたりする場合に便利です。

```console
# zfs list -t snapshot -S name | head -n3
NAME                                          USED  AVAIL     REFER MOUNTPOINT
tank/main@20200705_212415_before_svn          208K      -     1.88G -
tank/main@20200705_211551_before_update      1.98M      -     1.88G -
```

# Subversionインストール

## パッケージ

svnパッケージをインストール。

```console
# pacman -S svn
〈省略〉
# svn --version | head -n2
svn, version 1.14.0 (r1876290)
   compiled Jun 21 2020, 16:29:49 on x86_64-pc-linux-gnu
```

Subversion のバージョンは 1.14.0 ですか。何が変わったんでしょうね。少なくともレッドブックは…役に立たないとは言わないけど、細かい部分でかなり変わったんじゃないかと思うんだけど。思うんだけど！```man svn``` も ```info svn``` も、諸々の話題はレッドブックで見つかるよ〜と書いてある…:sweat:

そして公式ページでも、ユーザーマニュアルはレッドブックでした。英語版ですが。

実験的な機能は使わない方が良さそうだから、まあいいか（バージョンアップで操作性や機能などが頻繁に変わるっぽい）

なお、失敗した場合には、下記コマンドの実行で「svnパッケージのインストール前」に戻ります。何度でもやり直せます。（```zfs list```のオプション```-s```のsは小文字）

```console
# zfs list -t snapshot -o name -s name | tail -n1 | xargs -t zfs rollback
zfs rollback tank/main@20200705_212415_before_svn
# reboot
```

## リポジトリ

今回はとにかく```/etc```のバージョン管理を開始するのが目標ですので、汎用的なプロジェクト用の設定は後日とします。

- 専用ユーザは作成しません。```/etc```バージョン管理はrootが行います。
- 汎用リポジトリの場所も用意しません。

汎用ではなく```/etc```の為のリポジトリの場所は、```/etc/.svn_repo```とします。

当然、svnの管理対象外とする設定を忘れてはなりません。その手順は後述。

```console
# mkdir /etc/.svn_repo
# svnadmin create /etc/.svn_repo
```

# ```/etc```を作業コピーにする

```console
# cd /tmp
# mkdir etc
# svn import etc file:///etc/.svn_repo/ -m "create project"
# rmdir etc
# svn co file:///etc/.svn_repo/ /etc
Checked out revision 0.
```

ちゃんと出来たか確認。```/etc/.svn```ディレクトリが出来ていればOKです。

```console
# ls -dl /etc/.svn
drwxr-xr-x 4 root root 8 Jul  5 23:13 /etc/.svn
```

# バージョン管理

## 最初の一歩

```console
# cd /etc
# svn add *
〈省略〉
# ls *- | xargs -rt svn revert
svn revert group- gshadow- passwd- shadow-
Reverted 'group-'
Reverted 'gshadow-'
Reverted 'passwd-'
Reverted 'shadow-'
```

Subversion管理から外したファイルを確認してみます。

```console
# svn st --no-ignore | grep -E "^(\?|I)"
?       .pwd.lock
?       .svn_repo
?       .updated
?       group-
?       gshadow-
?       pacman.d/gnupg/S.gpg-agent
?       pacman.d/gnupg/S.gpg-agent.browser
?       pacman.d/gnupg/S.gpg-agent.extra
?       pacman.d/gnupg/S.gpg-agent.ssh
I       pacman.d/gnupg/pubring.gpg~
?       passwd-
?       shadow-
```

これらの内で```pacman.d/gnupg/S.gpg-agent*```がなぜ外れたのかがわからなくて調べてみました。

```console
# ls -l pacman.d/gnupg/S.gpg-agent*
srwx------ 1 root root 0 Jun 28 21:07 pacman.d/gnupg/S.gpg-agent
srwx------ 1 root root 0 Jun 28 21:07 pacman.d/gnupg/S.gpg-agent.browser
srwx------ 1 root root 0 Jun 28 21:07 pacman.d/gnupg/S.gpg-agent.extra
srwx------ 1 root root 0 Jun 28 21:07 pacman.d/gnupg/S.gpg-agent.ssh
```

最初が「s」つまりソケットだったのでした。こんなものはバージョン管理対象外！なんですけど、どうしてソケットなんかを```/etc```に置いたんでしょう。確かに、他に置く場所も無いのかも知れないけど。納得いきませんな。

なお```.pwd.lock``` と ```.updated``` については[以前調べました](https://qiita.com/katz_engineer/items/c9afdcb0e2355ed67ede#etc-%E7%AE%A1%E7%90%86%E9%96%8B%E5%A7%8B)。バージョン管理は不要です。どうも更新時に設定されるロックファイル等で、特に後者は ArchLinux に特有のファイルっぽい。という訳で無視します。

参考文献1: [Re: What is /etc/.pwd.lock for ? - debian-user Jul 2005 by thread](https://lists.debian.org/debian-user/2005/07/msg02949.html)
参考文献2: [karol 2014-07-31 00:12:19 - [SOLVED] /etc/.updated? - System Administration - ArchLinux Forums](https://bbs.archlinux.org/viewtopic.php?pid=1441907#p1441907)

さて、最初のcommitです。

```console
# svn ci -m "first commit"
〈省略〉
Committing transaction...
Committed revision 1.
```
## 確認

ログを表示させてみます。

なぜかリビジョンを指定しないと何も表示されないんですが… 原因不明。

（2020/07/11 修正ここから）

どうやら部分的にリビジョンが不一致を起こしているっぽい。知らんけど。
という訳で```svn update```すると直ります。

```console
# svn up
Updating '.':
At revision 1.
# svn log
------------------------------------------------------------------------
r1 | root | 2020-07-05 23:37:52 +0900 (Sun, 05 Jul 2020) | 1 line

first commit
------------------------------------------------------------------------
```

（2020/07/11 修正ここまで）

余談ですが、```--xml```オプションを指定して```xmllint```コマンドと組み合わせれば、色んな値を抽出する事が出来ます。```head```とか```sed```とかを組み合わせてビクビクしながら調整する必要はありません。

参考文献: [俺様サーバー構築記 - Subversionサーバーの構築@デスクトップパソコン](https://qiita.com/katz_engineer/items/b893316b4d1bb7731e87)

```console
# svn log -r0:HEAD --xml | xmllint --xpath "/log/logentry/date/text()" -
2020-07-05T14:37:52.9106912
# svn log -r0:HEAD --xml | xmllint --xpath "string(/log/logentry[1]/@revision)" -
1
```

## 無視ファイル

現時点でSubversion管理下に無いファイルは、すべて無視ファイルとして登録します。

```console
# svn propset svn:ignore -F <(svn st | cut -b9- | grep -v /) .
property 'svn:ignore' set on '.'
# cd pacman.d/gnupg
# svn propset svn:ignore -F <(svn st | cut -b9-) .
```

状態を確認します。

```console
# cd /etc
# svn st
 M      .
 M      pacman.d/gnupg
# svn di
Index: pacman.d/gnupg
=================================================
---pacman.d/gnupg       (revision 1)
+++pacman.d/gnupg       (working copy)

Property changes on: pacman.d/gnupg
_________________________________________________
Added: svn:ignore
## -0,0 +1,4 ##
+S.gpg-agent
+S.gpg-agent.browser
+S.gpg-agent.extra
+S.gpg-agent.ssh
Index: .
=================================================
--- .       (revision 0)
+++ .       (working copy)

Property changes on: .
_________________________________________________
Added: svn:ignore
## -0,0 +1,7 ##
+.pwd.lock
+.svn_repo
+.updated
+group-
+gshadow-
+passwd-
+shadow-
```

これも```/etc```の変更ですのでcommitします。

```console
# svn ci -m"set svn:ignore"
Sending        .
svn: E155011: Commit failed (details follow)
svn: E155011: Directory '/etc' is out of date
svn: E160028: Directory '/' is out of date
```

あれれ、良くわかりませんが変なエラーが… このエラー(out of date)の場合は```svn update```すると直ります。

```console
# svn update
Updating '.':
At revision 1.
# svn ci -m"set svn:ignore"
Sending        .
Sending        pacman.d/gnupg
Committing transaction...
Committed revision 2.
```

# pacman フック

pacmanでパッケージを更新した時に、自動的にcommitされるようにします。パッケージのインストールや削除の際には手動でcommitしましょう。適切なコメントを残す必要もありますので。

## 設定

自動commitの為のシェルスクリプトを作成して、置き場所は```/etc/.svn_repo```にしましょう。

```console:/etc/.svn_repo/autocommit.sh
#!/bin/bash

ETC_DIR="$1"
if [[ -z "$ETC_DIR" ]]; then
  echo "$(basename $0) <dirname>"
  exit 1
fi

exec &> >(tee >(systemd-cat -t etcmgr))

cd "$ETC_DIR"
SVN_STATUS=$(svn st)
if [[ -z "$SVN_STATUS" ]]; then exit; fi

echo "auto commit $ETC_DIR when done pacman command${2+: }$2"
echo "$SVN_STATUS" | grep "^?" | cut -b9- | xargs -I{} find {} -type f -or -type d -or -type l | xargs -r svn add
echo "$SVN_STATUS" | grep "^!" | cut -b9- | xargs -r svn rm
svn ci -m "auto commit when done pacman command${2+: }$2"
```

実行権限を忘れずに。

```console
# chmod u+x /etc/.svn_repo/autocommit.sh
```

そしてpacmanのフックを作成します。

参考文献：[2.2 フック - 2 設定 - pacman - ArchWiki](https://wiki.archlinux.jp/index.php/Pacman#.E3.83.95.E3.83.83.E3.82.AF)

```console:/usr/share/libalpm/hooks/etcmgr-pre-install.hook 
# etcmgr pre-install hook

[Trigger]
# Operation = Install
Operation = Upgrade
# Operation = Remove
Type = File
Target = etc/*

[Action]
Description = etcmgr: pre-transaction commit
When = PreTransaction
Exec = /etc/.svn_repo/autocommit.sh /etc pre-transaction
AbortOnFail
```

```console:/usr/share/libalpm/hooks/etcmgr-post-install.hook 
# etcmgr post-install hook

[Trigger]
# Operation = Install
Operation = Upgrade
# Operation = Remove
Type = File
Target = etc/*

[Action]
Description = etcmgr: post-transaction commit
When = PostTransaction
Exec = /etc/.svn_repo/autocommit.sh /etc post-transaction
```

## 確認

数日経過してから```pacman -Syu```を実行します。そうしないとパッケージが更新されないので。しかし```/etc```の変更を伴う更新って、そう頻繁に発生するのかな？実際に発生した場合の表示は、過去の記事を参照。

参考文献1: [俺様サーバー構築記 - Subversion による /etc のバージョン管理@ノートパソコン](https://qiita.com/katz_engineer/items/c9afdcb0e2355ed67ede)
参考文献2: [俺様サーバー構築記 - Subversion による /etc のバージョン管理@デスクトップパソコン](https://qiita.com/katz_engineer/items/357e9d8efa71b17deec8)

（2020/07/13 修正ここから）

後日、パッケージを更新したら```/etc```が修正されたので、それを記しておきます。

```console
# pacman -Syu
〈省略〉
:: Running pre-transaction hooks...
(1/1) etcmgr: pre-transaction commit
〈省略〉
(5/6) etcmgr: post-transaction commit
auto commit /etc when done pacman command: post-transaction
A         keyutils
Adding         keyutils
Sending        services
Committing transaction...
Committed revision 3.
〈省略〉
# svn log /etc -rhead
Updating '/etc':
At revision 3.
------------------------------------------------------------------------
r3 | root | 2020-07-12 15:26:50 +0900 (Sun, 12 Jul 2020) | 1 line

auto commit when done pacman command: post-transaction
------------------------------------------------------------------------
```

（2020/07/13 修正ここまで）

# 結

これで```/etc```をバージョン管理できるようになりました。個人的な好みでSubversionですが、これからガンガンいきます！
やったね:thumbsup_tone1: