Xcode: 個人開発用にredmineを導入する
個人で開発をしています。
バグや追加機能一覧を管理したいと思って、redmineを使うことにしました。[1]
経緯や作業手順を忘れることになると思うので、メモしておきます。
やりたいこと
やりたいこととしては、以下のようなものになります。
- バグや機能をチケットとして一覧管理する
- 修正内容(リポジトリへのコミット内容)と、チケットを紐付ける
- チケットのステータスを管理する(テストが終わったとか)
- プロジェクト横断でチケットを俯瞰できるように
欲しい機能とか思いつくことか気になった不具合とかが色々あって、自分の性格的に思いついたらすぐ着手しがちです。そのうえ個人開発だと自由度が高すぎて抑制が効かなくなって来ました。
チケットとして書いておけば、いったんは頭から追い出して安心できるし、後で戻ることもできます。
redmineのインストールとか
素のredmineを直接Macにインストールしようかとも思って、以下の記事を見てやってみたのですが、途中で断念しました。
上記の記事は、rubyやgemのバージョンに依存するので、バージョンを切り替えられるようローカルに環境を作る、というようなストーリーです。やってみると最後のApache+passengerというところで詰まりました。
詳しく調べる前に断念したのであれですが、httpd.confへpassengerのLoadModule(自分のホームディレクトリ配下のファイル)を追加するとhttpdの起動に失敗するようになってしまったので、アクセス権の問題ではないかと推測しています。自分のホームディレクトリは700で、そのために755とかにするのは何となく(たぶん実害はないのでしょうが)ヤだなと感じたというのが大きいのですが、他にも「ここでこういうつまづきがあるとするとこの先大変そう」とかもあって考え直しました。
dockerを使う
個人なのでDocker Desktop for Macを使う方向で考え直しました。
「個人なので」というのは、個人〜小規模事業者でない場合はDocker for Macが有償利用のみになるからです。
Docker Personal (Docker Desktop for Mac)は以下からサインアップすればダウンロードできます。
ダウンロードした.dmgをマウントして、アプリアイコンをApplicationsフォルダにドラッグ&ドロップするだけです。
Docker Desktopをアイコンから起動すると諸々の処理が行われて、docker-composeとかもインストールされた状態になります。
それ以降のredmineのインストール作業は以下の記事を参考にしました。
ただ、この記事の状態だとXcodeにコミットした内容とチケットとを紐付ける、というようなことができないので、いくつか変更をしました。
続く。
-
いまどきのチケット管理とか懸案管理とかトラッキングシステムってどんなのがあるのかな?と調べたら、10年(下手したら15年?)ぐらい前とそんなに状況が変わってなくてびっくりしました。 ↩︎
GitHubとかにリポジトリを用意していると別のやり方があるのかも知れませんが、redmine標準ではredmineのインストールされているサーバと同じサーバにあるgitのbareリポジトリが連携対象となります。
個人開発なので、GitHubで毎回どうのこうのというのも一手間ありそうです。[1]
dockerのredmineとローカルのgitリポジトリを連携させる
連携させるために必要なことは、以下の通りになります。
- docker内からローカルのフォルダ/ファイルを見えるようにする
- ローカルにbareリポジトリを作る
- docker内のredmineから、ローカルのbareリポジトリを参照する
- Xcodeからコミットしたらbareリポジトリにそれが入るようにする
docker内からローカルのフォルダ/ファイルを見えるようにする
(Docker Desktop for Macのバージョン:4.20.1)
Docker Desktop for Macのデフォルトは、Mac側の/Users, /Volumes, /private, /tmp, /var/foldersを、docker側から見えるようにすることができるようになっています。
追加する場合は、ウィンドウのタイトルバーの右端にあるギヤマーク→Resources→File shareingで「/path/to/exported/directory」の部分にMac側のパスを入力→Enterで追加できます。
私の場合、/opt/dockerというフォルダをMac側に作り、それを追加しました。
/opt/dockerのパーミッションやオーナーなどにも気をつけてください。
そのうえで
の記事のdocker-compose.ymlの以下の部分を
volumes:
- vol_redmine:/usr/src/redmine/files
volumes:
- /opt/docker/redmine/files:/usr/src/redmine/files
- /opt/docker/redmine/plugins:/usr/src/redmine/plugins
- /opt/docker/redmine/themes:/usr/src/redmine/public/themes
- /opt/docker/gitrepos:/opt/gitrepos
のように書き換えます。(/opt/docker…の部分は自分の環境に合わせてください。事前にフォルダは作っておく必要があります)
書き換えたら、再度、docker-compose upします。
これでMac側の/opt/docker…の部分が、docker内のVMから見えるようになります。
ローカルにbareリポジトリを作る
Xcodeでgitのリポジトリを作ると、プロジェクトのディレクトリ内に.gitというnon-bareのリポジトリが作成されます。
bare/non-bareについては、以下の記事を参考にさせていただきました。
個人開発なので、本当はローカルコミットだけあれば充分なのですが、redmineで見るには
- bareなリポジトリである必要がある
- dockerから見えるところにある必要がある
という2点の理由からbareなリポジトリをもう1つ作ることにします。
今回はXcodeで作ったgitのリポジトリから、bareなリポジトリを以下のようにして作りました。
プロジェクトを始める前なら、もう少し違う手順もできます。
% mkdir /opt/docker/gitrepos/プロジェクト名
% cd /opt/docker/gitrepos/プロジェクト名
% git clone --bare /Users/…Xcodeで作ったプロジェクトフォルダのパス/.git
これで/opt/docker/gitrepos/プロジェクト名/プロジェクト名.gitができると思います。
ただこれだけだと、/opt/docker/gitrepos→Xcodeの.gitの関係があるものの逆がないので、Xcodeの.gitから/opt/docker/gitreposにプッシュできません。
なので、いったんXcodeのプロジェクトフォルダをリネームしてどっかによけておいて、
% cd //Users/…Xcodeで作ったプロジェクトフォルダの1個上のフォルダのパス
% git clone /opt/docker/gitrepos/プロジェクト名/プロジェクト名.git
で、bareなリポジトリから再度、non-bareのリポジトリ(作業フォルダ)を作成します。
これで、Xcodeからコミット→プッシュで記録されるbareリポジトリができました。
docker内のredmineから、ローカルのbareリポジトリを参照する
docker内のredmineのプロジェクト→リポジトリ→新しいリポジトリ、でバージョン管理システム=Gitにして、リポジトリのパスに「docker側から見えるbareリポジトリのパス」を書きます。
最初、Mac側のパスを書いてはまりました。
そのパスがdocker側からのパスと違う場合、redmineで「リポジトリに、エントリ/リビジョンが存在しません」というメッセージが表示され、ログには「not a git repository」と出力されます。
アクセス権が足りない場合もこのメッセージが出るようです。アクセス権かと思っていろいろ操作しましたが、なんのことはない、パスが間違っていました。
Xcodeからコミットしたらbareリポジトリにそれが入るようにする
個人開発なので、ローカルコミットだけで充分という話はしましたが、なのでプッシュしないとredmineに反映されないとすると面倒くさいのです。
Xcode側プロジェクトフォルダ内の.git/hooksにpost-commitというファイルを作り、chmod 755 post-commit
をしておいて、中身を以下のようにします。
#! /bin/sh
exec git push > /dev/null 2> /dev/null
exit 0
これによりXcodeでコミットすると、bareなgitリポジトリに自動でプッシュされて、redmineからも見えるようになります。
2> /dev/null
の部分がないとXcodeのコミット時にエラーメッセージが出ます。
-
これについては、今回使ったhooksを使えば別にたいしたことなさそう、という気になりました。 ↩︎
redmineサーバのタイムゾーンがUTCになっていたので日付がずれました。
docker-compose.ymlのvolumesの部分を書き換えます。
volumes:
- /opt/docker/redmine/files:/usr/src/redmine/files
- /opt/docker/redmine/plugins:/usr/src/redmine/plugins
- /opt/docker/redmine/themes:/usr/src/redmine/public/themes
- /opt/docker/gitrepos:/opt/gitrepos
- /opt/docker/localtime:/etc/localtime
最後の行のように、/etc/localtimeを置き換えてやるとよさそうです。
% cd /opt/docker
% ln -s /etc/localtime
でホストの/etc/localtimeにリンクしておきます。
書き換えたらdocker-compose upを再度行います。
dbのほうも同様にします。
作業時間の記録もしたいと思い立ちました。
何時何分に開始して、何時何分に終了して、なので何時間、みたいな記録の仕方ができないかと思って、開始日時・終了日時用のカスタムフィールドを追加しました。
で、システム全体の「設定」→「時間管理」→「作業時間に0時間を入力を許可」をONにしました。
作業開始時点で開始日時・終了日時のところに開始日時を打っておいて作業時間=0で記録しておいて、終了時点でこのレコードに終了日時も入れて、作業時間を(手で計算して)更新する、というような想定です。
(最終的にはこの作業時間も開始・終了日時から自動計算されるようにしたい)
ところが作業時間=0だと、作業時間のタブが表示されず、既存のレコードを更新できないという問題が発生しました。
app/helpers/issues_helper.rbのissue_history_tabsというメソッドが、合計時間が0より大きい場合にタブを表示、という判定を入れているせいだとは分かったのですが、これを上書きする方法が分からない。
とりあえず、自前のプラグイン(のひな形)をbundle exec rails generate redmine_plugin zero_time_tab
というような感じで生成し、オリジナルのapp/helpers/issues_helper.rbをコピーしてzero_time_tab/app/helpersに入れたうえで、issue_history_tabsメソッドを上書きして「合計時間が0より大きい場合」の条件を外しました。
これをdockerから見えるpluginsフォルダに入れてredmineを再起動したら表示されるようになりました。
本当は必要なメソッドだけ上書きできると良いと思うのですがrails知識がなく、よく分かりませんでした。
issues_helper.rbのissue_history_tabs以外のメソッドを抜くとエラーになるし(たぶんモジュール全体を上書きしているせいだと思いますが)、といって自前のModuleとするとカスタムissue_history_tabsが呼ばれないし…というようなところです。
最終的にはこの作業時間も開始・終了日時から自動計算されるようにしたい
require "time"
module CalcTimeFromStartEnd
class Hooks < Redmine::Hook::Listener
def controller_timelog_edit_before_save(context)
params = context[:params]
time_entry = context[:time_entry]
start_text_field = time_entry.custom_field_values.detect {|c| c.custom_field.name == "開始日時"}
end_text_field = time_entry.custom_field_values.detect {|c| c.custom_field.name == "終了日時"}
return if start_text_field.nil? || end_text_field.nil?
return if start_text_field.value == "" || end_text_field.value == ""
start_time = Time.parse(start_text_field.value)
end_time = Time.parse(end_text_field.value)
seconds = end_time - start_time
time_entry.hours = seconds / 3600
end
end
end
こんな感じのフックを作って、保存時に自動計算されるようになりました。
(自分で使うだけなのでエラー処理とかもないのですが)
あと、作業時間記録画面の「時間」に初期値0が入るようにしないと…。
誰かの役に立つかも知れないので、GitHubに作業時間自動計算のプラグインを置いておきました。
ところが作業時間=0だと、作業時間のタブが表示されず、既存のレコードを更新できないという問題が発生しました。
これもプラグイン化しました。
誰かの役に立つかも知れないので置いておきました。
あと、作業時間記録画面の「時間」に初期値0が入るようにしないと…。
同じくこれもプラグイン化しました。
「開始日時」カスタムフィールドがあれば、そこに現在時刻が初期設定されるようにもしました。
こんなに分ける必要あるのかという気もしますが…。
3つ使うと、なかなか良い感じです。
macOS Sonoma 14.0にあげたら、docker内のVMの時刻が日本時間でなくなってしまいました。
そのうえ、docker-desktopを最新版(v4.24.0)にしたら、VMが起動失敗してしまうようになりました。
エラーメッセージは以下のようなものが出ています。
macOS側の/private/var/db/timezone/tz/に、2023c.1.0というディレクトリを作ろうとして失敗しているのだろうと思います。なぜ作ろうとしてるのかは分かりませんが。
手元にmacOS 13の環境はもうなくて、macOS 11の環境しかないのですが、こちらには/private/var/db/timezone/tz/2023c.1.0というディレクトリがあります。dockerは入ってないのでmacOS自体が用意しているものだと思います。
これがmacOS 14になってなくなった、ということなんでしょうか。
ちなみにsudo mkdir /private/var/db/timezone/tz/2023c.1.0もoperation not permittedで失敗しました。
いったん、docker-compose.ymlの以下のlocaltimeの部分をコメントアウトしました。
- /opt/docker/localtime:/etc/localtime
↓
# - /opt/docker/localtime:/etc/localtime
コメントアウト後にdocker-compose up -dを実行すると、起動するようになりました。
ただしタイムゾーンはずれたままなので、どうするか再調査・再検討です。
追記
ひとまず、docker-compose.ymlのenvironmentにTZを追加すると、VM(OS?)によってタイムゾーンが変えられる、ということだったので試したところ、今回のVMはうまく変わりました。
environment:
TZ: Asia/Tokyo
上記のTZ行を各VMのenvironment節に追加し、修正後にdocker-compose up -dを実行します。