俺様サーバー構築記 - Subversionサーバーの構築@デスクトップパソコン

「[俺様サーバー構築記 - 基本方針](https://qiita.com/katz_engineer/items/dbabcff4e14e9db2b10f)」以来構築を続けてきた俺様パソコン環境ですが、[サーバーマシンも基本的なインストールが出来て](https://qiita.com/katz_engineer/items/8722e3d74468786d6f89)、ぼちぼち色んなサービスなどを構築する段階になってきました。

すると各種設定がガンガン必要になり、場合によっては試行錯誤とその記録も必要になりますが、そういう場合は言うまでも無くバージョン管理が有効です。

一般的には git という話になるんだと思いますけども。私は未だ git に慣れない、というかほぼ触った事がありませんので、今回は Subversion サーバーを立てます。って言うかですね、分散以外に git の優位性がわからんのですよ。いずれ GitLab を自力で立てて git フローを勉強するつもりですので、そうしたらまた報告記事を上げます。

# 参考文献

[なおかつ・ArchLinux インストール覚書](https://qiita.com/katz_engineer/items/d3ed613dcf7d04ffc8b7)
[Subversion だけでお手軽にサーバ構築](https://qiita.com/katz_engineer/items/09c6dd4e8bde642d627b)
[2 Svnserve のセットアップ - Subversion - ArchWiki](https://wiki.archlinux.jp/index.php/Subversion#Svnserve_.E3.81.AE.E3.82.BB.E3.83.83.E3.83.88.E3.82.A2.E3.83.83.E3.83.97)
[許可するように設定されていても、SVNにパスワードを保存させることはできません。 - コードログ](https://codeday.me/jp/qa/20190301/345492.html)

# インストール

## パッケージ

何はともあれインストール。その前にスナップショットを撮りましょう。

```console
# zfs snapshot tank/main@$(date +%Y%m%d_%H%M%S)_before_svn
# zfs list -t snapshot -S name | head -n2
NAME                                        USED  AVAIL     REFER  MOUNTPOINT
tank/main@20190629_004305_before_svn        854K      -     2.43G  -
# pacman -S --noconfirm svn 
〈省略〉
# svn --version | head -n2
svn, version 1.12.0 (r1857323)
   compiled Jun  7 2019, 01:40:12 on x86_64-pc-linux-gnu
```

Subversion のバージョンは 1.12.0 ですか。何が変わったんでしょうね。少なくとも[レッドブック](http://svnbook.red-bean.com/index.ja.html)は…役に立たないとは言わないけど、細かい部分でかなり変わったんじゃないかと思うんだけど。思うんだけど！```info svn``` とか読むと、諸々の話題はレッドブックで見つかるよ〜と書いてある…:sweat:

そして[公式ページ](https://subversion.apache.org/)でも、ユーザーマニュアルは[レッドブック](http://svnbook.red-bean.com/)でした。英語版だけど。という事はあんまり変わってないのかな。まあ良いか。

レッドブック以降の追加機能の調査は今後の課題としましょう。

## Subversionサーバ用ユーザ

ユーザは svn とします。

```console
# useradd -Mr -c "Subversion service" -d /srv/svn -s /sbin/nologin svn
```

何だかオプションがごちゃごちゃしてしまいましたが、この結果次のように登録されます。

```console
# grep svn /etc/passwd
svn:x:977:977:Subversion service:/srv/svn:/sbin/nologin
```

ホームディレクトリは ```/srv/svn``` としています。ftp とか http とかもそうなんで、妥当な設定でしょう。

```console
# grep -e ftp -e http /etc/passwd
ftp:x:14:11::/srv/ftp:/sbin/nologin
http:x:33:33::/srv/http:/sbin/nologin
```

svn ユーザのホームディレクトリの扱いとなった ```/srv/svn``` を作り、リポジトリはその下の ```/srv/svn/repo``` とします。

```useradd``` コマンドの ```-m``` オプションを使用しなかったのは、余計なサブディレクトリなどを避ける為です。

```console
# mkdir -p /srv/svn/repo
```

## Subversionサーバの設定

svnserve の設定ファイルと、systemd用の設定ファイルを用意します。

例によって、可能な限り自動化できるコマンドを選んでいます。

```console
# sed -i -e 's:^\(SVNSERVE_ARGS\)=.*$:\1="-r /srv/svn/repo":' /etc/conf.d/svnserve
# SYSTEMD_EDITOR="/usr/bin/cp /dev/stdin" systemctl edit svnserve <<___
> [Service]
> User=svn
> ___
```

```systemctl edit``` のエディタとして ```/usr/bin/cp /dev/stdin``` を指定するのは反則級の暴挙だろうなぁとは思います。しかし反省も後悔もしていません。

なお設定結果の確認には ```systemctl cat``` コマンドを使うと楽でしょう。

```console
# systemctl cat svnserve
# /usr/lib/systemd/system/svnserve.service
[Unit]
Description=Subversion protocol daemon
After=syslog.target network.target

[Service]
Type=forking
EnvironmentFile=/etc/conf.d/svnserve
ExecStart=/usr/bin/svnserve --daemon $SVNSERVE_ARGS

[Install]
WantedBy=multi-user.target

# /etc/systemd/system/svnserve.service.d/override.conf
[Service]
User=svn
```

組み込みのユーザ認証を設定します。ここに記述されるパスワードは平文で、スペースや記号も許されます。とりあえず実験用のユーザとパスワードを一つ。

```console
# cd /srv/svn
# mkdir conf
# cat <<___ >conf/passwd
> [users]
> taro = This is 1 pen.
> ___
```

プライベートの場合は権限の制限は無くて、プロジェクトのディレクトリツリーは全員すべて読み書き可能にする事が多いでしょう。これを各プロジェクト間で共有のファイルとしておきます。そうでない場合はそれなりに設定してください。

```console
# cat <<___ >conf/default-authz
> [/]
> * = rw
> ___
```

リポジトリを用意する為のスクリプトを作ります。滅多にしない操作を忘れないように。

```console
# vi createprj.sh
# cat createprj.sh 
#!/bin/bash
if [[ -z "$1" ]]; then
	echo "usage: $(basename $0) <projectname>"
	exit
fi
umask 002
SVN_HOME=$(dirname $0)
cd $SVN_HOME
svnadmin create repo/$1
ret=$?
test $ret -ne 0 && exit $ret

cat <<-___ >repo/$1/conf/svnserve.conf
	[general]
	anon-access = none
	auth-access = write
	password-db = ../../../conf/passwd
	authz-db = ../../../conf/default-authz
	realm = Project $1
___
chown -R svn:svn repo/$1

echo "Edit svnserve.conf and authz in the directory $SVN_HOME/repo/$1/conf as needed."
echo "The file passwd is in the directory $SVN_HOME/conf, edit it as needed, too."
# chmod 755 createprj.sh
```

ここまで出来たら、ファイルとディレクトリの所有者を svn にしておきます。

```console
# chown -R svn:svn .
```
## Subversionクライアントの基本設定

これは、Subversion に対して昔から良く挙げられる要望への対応です。

```console
# ls -ld /etc/subversion
ls: cannot access '/etc/subversion': No such file or directory
# mkdir /etc/subversion
# cat <<___ >/etc/subversion/config
> [miscellany]
> use-commit-times = yes
> ___
```

これを指定しておくと、checkout や update などの際、該当ファイルに commit 日時が設定されます！操作日時ではなく！

commit された時の該当ファイルの更新日時が再現される訳ではありませんが、それでも結構な改善でしょう。

# 起動

ここまで設定したら Subversion サーバを起動します。

```console
# systemctl enable svnserve
Created symlink /etc/systemd/system/multi-user.target.wants/svnserve.service -> /usr/lib/systemd/system/svnserve.service.
# systemctl start svnserve
```

起動を確認。

```console
# systemctl status svnserve
* svnserve.service - Subversion protocol daemon
   Loaded: loaded (/usr/lib/systemd/system/svnserve.service; disabled; vendor preset: disabled)
  Drop-In: /etc/systemd/system/svnserve.service.d
           `-override.conf
   Active: active (running) since Sat 2019-06-29 19:48:24 JST; 26s ago
  Process: 3158 ExecStart=/usr/bin/svnserve --daemon $SVNSERVE_ARGS (code=exited, status=0/SUCCESS)
 Main PID: 3159 (svnserve)
    Tasks: 1 (limit: 4915)
   Memory: 2.8M
   CGroup: /system.slice/svnserve.service
           `-3159 /usr/bin/svnserve --daemon -r /srv/svn/repo

Jun 29 19:48:23 anju systemd[1]: Starting Subversion protocol daemon...
Jun 29 19:48:24 anju svnserve[3158]: DIGEST-MD5 common mech free
Jun 29 19:48:24 anju systemd[1]: Started Subversion protocol daemon.
# ps aux | grep serve
svn       3159  0.0  0.0  18268   920 ?        Ss   19:48   0:00 /usr/bin/svnserve --daemon -r /srv/svn/repo
root      3270  0.0  0.0   3036  1240 pts/0    S+   19:50   0:00 grep serve
```

ひとまず起動できたようです。

# 確認

実際に操作してみます。

1. 実験用プロジェクト test を作成
2. ホームディレクトリで checkout
3. 適当なテキストファイルを作成して commit
4. テキストファイルを表示して比較
5. 別のディレクトリに checkout して、ファイル更新日時を確認

ユーザは引き続き root のままで、Subversion のアカウントは設定しておいた最初のユーザを使用します。

```console:サーバ側
# /srv/svn/createprj.sh test
Edit svnserve.conf and authz in the directory /srv/svn/repo/test/conf as needed.
The file passwd is in the directory /srv/svn/conf, edit it as needed, too.
# mkdir $HOME/work
# cd $HOME/work
# IFS="$IFS=" read U P <<< $(grep = /srv/svn/conf/passwd | head -n1)
# svn co --username "$U" --password "$P" svn://localhost/test/
Checked out revision 0.
```

素晴らしい事に、最初の ```svn``` コマンドで何も聞かれません。パスワードを保存しますか? Store password unencrypted (yes/no)? と昔は聞かれたものですが。今のバージョンでは何も聞かれません。黙って保存しない方向のようです。

```console
# cd test
# echo "Subversion test" >document.txt
# svn add *
A         document.txt
# svn ci --password "$P" -m "first commit"
Adding         document.txt
Transmitting file data .done
Committing transaction...
Committed revision 1.
# svn cat --password "$P" svn://localhost/test/document.txt
Subversion test
# ls -l --time-style=+"%F %T %z"
total 1
-rw-r--r-- 1 root root 16 2019-07-02 22:40:55 +0900 document.txt
# svn log --password "$P" svn://localhost/test/
------------------------------------------------------------------------
r1 | taro | 2019-07-02 22:41:14 +0900 (Tue, 02 Jul 2019) | 1 line

first commit
------------------------------------------------------------------------
# mkdir $HOME/work2
# cd $HOME/work2
# svn co --password "$P" svn://localhost/test/
A    test/document.txt
Checked out revision 1.
# ls -l --time-style=+"%F %T %z"
total 1
-rw-r--r-- 1 root root 16 2019-07-02 22:41:14 +0900 document.txt
```

最後の、ファイル日時の確認ですが。次のようなコマンドを使用すると楽になります。

```console
# svn log --password="$P" * --xml | xmllint --xpath "/log/logentry/date/text()" -
2019-07-02T13:41:14.299079Z
# date -u -r document.txt +"%FT%T.%6NZ"
2019-07-02T13:41:14.299079Z
```

最後に片付け。テスト用に作成したプロジェクトの、ワーキングディレクトリとリポジトリを削除します。

```console:サーバ側
# cd
# rm -r work work2 /srv/svn/repo/test
```

これでひとまず完了です。やったね:thumbsup_tone1: