redmine ソースパケジ掘削 (2)

メンテナスクリプト掘削情報を以下に。

debian/prerm

*rm なナニから確認していきます。とりあえず以下はおやくそく。

#!/bin/sh
# prerm script for redmine
#
# see: dh_installdeb(1)

set -e
#set -x

. /usr/share/debconf/confmodule

で、prerm では引数が remove の場合のみ処理が記述されています。

case "$1" in
    remove)

# snip

    ;;
    
    purge|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
    ;;
esac

#DEBHELPER#

exit 0

では remove な処理はどうなっているか、というと殆どが dbconfig-common な処理になっています。まず redmine/oldinstances なテンプレを取得して以下。

        db_get redmine/old-instances || true
        gOldInstances="${RET}"
        for lInstance in $gOldInstances; do
            db_get redmine/instances/$lInstance/dbconfig-install || true
            if [ "$RET" = "true" ]; then
                ( . /usr/share/dbconfig-common/dpkg/prerm ; dbc_go redmine/instances/$lInstance $@ )
            fi
        done

基本的に値は default 一発らしいのですが、そうでないケイスってどうなってるのか、というかどういったケイスでそうなるのか、とか確認したい。とは言え条件式的に言えば dbconfig-common が導入されてりゃ値は true なのだろうなと。
そうであれば dbconfig-common な prerm を source して dbc_go コマンド実行してるのか。おそらくこれは定型なナニのはず。
次は以下。

        db_get redmine/current-instances || true
        gInstances="${RET}"
        for lInstance in $gInstances; do
            db_get redmine/instances/$lInstance/dbconfig-install || true
            if [ "$RET" = "true" ]; then
                ( . /usr/share/dbconfig-common/dpkg/prerm ; dbc_go redmine/instances/$lInstance $@ )
            fi
        done

同じことを redmine/current-instances についても実行しております。で、最後に以下。

        # removes symbolic links installed by
        vendordir="/usr/share/redmine/vendor/"
        rm -f ${vendordir}rails

こちらは postinst で symlink 張ってる以下なナニの後始末ですね。

        if [ ! -L vendor/rails ]; then
            ln -s "$fRailsDir" vendor/rails
        else

これで prerm はおしまい。

debian/postrm

次は postrm ですが、こちらも短いです。まずはお約束。ってか postrm は confmodule の存在チェックしてますね。何らかの根拠があるのかどうか。削除する場合、スデに debconf が削除済み、ってことがあるの? debconf って Pre-Depends なパケジなのだけどどうなんでしょ。

#!/bin/sh
# postrm script for redmine
#
# see: dh_installdeb(1)

set -e
#set -x

if [ -f /usr/share/debconf/confmodule ]; then
    . /usr/share/debconf/confmodule
fi

あと、postrm は remove および purge な引数にのみ対応な様子。

case "$1" in
	upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
	;;
	
	remove)

# (snip)
	;;

	purge)

# (snip)

	;;
esac

#DEBHELPER#

exit 0

順に見ていきます。とりあえず上からってことで remove から。

remove

ここでも存在チェックしてますね。削除の順は保証の範疇外なのだろうか。あ、dbconfig-common は Pre-Depends ではなくて Depends になってますね。

        if [ -f /usr/share/dbconfig-common/dpkg/postrm ]; then
            
            db_get redmine/old-instances || true
            gOldInstances="${RET}"
            for lInstance in $gOldInstances; do
                db_get redmine/instances/$lInstance/dbconfig-install || true
                if [ "$RET" = "true" ]; then
                    ( . /usr/share/dbconfig-common/dpkg/postrm ; dbc_go redmine/instances/$lInstance $@ )
                fi
            done

ここも定型なのでしょうね。current-instances についても同じことをしてますが引用は略します。remove な処理はこれだけです。
次は purge なんですが

purge

ヤッツケ感満点な書き方になってます。

        db_get redmine/old-instances || true
        gOldInstances="${RET}"
        db_get redmine/current-instances || true
        gInstances="${RET}"

old-instances と current-instances をざくっと取得。これまで別で書いてたのに何があったのだろう、と心配になってしまいます。で、存在チェックしつつ定型の処理。

        if [ -f /usr/share/dbconfig-common/dpkg/postrm ]; then
            for lInstance in $gOldInstances; do
                db_get redmine/instances/$lInstance/dbconfig-install || true
                if [ "$RET" = "true" ]; then
                    ( . /usr/share/dbconfig-common/dpkg/postrm ; dbc_go redmine/instances/$lInstance $@ )
                fi
            done

同じことを current-instances についても実行してますが引用略。ここまでは remove と同様です。ここからが purge 特有になります。基本的には /etc とか /var 配下のファイルの削除となっている様子。
まず /etc 配下のナニを old-instances および current-instances について ucf で purge してますね。

        if which ucf >/dev/null 2>&1; then
            for lInstance in $gOldInstances; do
                ucf --purge /etc/redmine/$lInstance/database.yml
                ucf --purge /etc/redmine/$lInstance/email.yml
                ucf --purge /etc/redmine/$lInstance/session.yml
            done
            for lInstance in $gInstances; do
                ucf --purge /etc/redmine/$lInstance/database.yml
                ucf --purge /etc/redmine/$lInstance/email.yml
                ucf --purge /etc/redmine/$lInstance/session.yml
            done
        fi

む、てことはここらへんは postinst あたりで ucf 使って作成してるのかな。ちょろっと見てみたところでは、database.yml と session.yml は云々されてるけど、email.yml は ucf で云々、ってのは無いみたい。
で、以降でファイルを削除して db_purge で締め。

        # package-generated or runtime files are removed
        rm -f /usr/share/redmine/db/schema.db
        # removing /var/run files is not needed : it is mounted tmpfs in wheezy - anyway it does not hurt
        rm -rf /var/run/redmine
        rm -rf /var/log/redmine
        rm -rf /usr/share/redmine/tmp
        rm -rf /var/lib/redmine/*/sessions
        rm -rf /var/cache/redmine
        rm -rf /etc/redmine
        # user files are not
        rmdir /var/lib/redmine/*/files || true
        rmdir /var/lib/redmine/* || true
        db_purge

上記、基本的に postinst で mkdir しているものですかね。なんか /var/run なあたりに微妙なコメントがありますがスルー。
これらを踏まえて postinst を確認していきます。

debian/postinst

順に確認していきます。まずお約束で以下。

#!/bin/sh
# postinst script for redmine
#
# see: dh_installdeb(1)

set -e
#set -x

RAKE_VERBOSE=false

. /usr/share/debconf/confmodule

で、コメントにもあるように prerm を remove で呼び出して postrm を purge で呼び出している模様。まず prerm から

# remove and purge old instances each time postinst is called
db_get redmine/old-instances || true
gOldInstances="${RET}"
for lInstance in $gOldInstances; do
    db_get redmine/instances/$lInstance/dbconfig-install || true
    if [ "$RET" = "true" ]; then
        ( . /usr/share/dbconfig-common/dpkg/prerm ; dbc_go redmine/instances/$lInstance remove )
    fi
done

で、purge なナニは以下。このパケジの postrm が呼びだされるのか、とナチュラル勘違いをしそうになりました。そうではないはず。
で、/etc 配下を ucf で purge したりディレクトリ削除したりもしてます。

for lInstance in $gOldInstances; do
    db_get redmine/instances/$lInstance/dbconfig-install || true
    if [ "$RET" = "true" ]; then
        ( . /usr/share/dbconfig-common/dpkg/postrm ; dbc_go redmine/instances/$lInstance purge )
    fi
    if which ucf >/dev/null 2>&1; then
        ucf --purge /etc/redmine/$lInstance/database.yml
        ucf --purge /etc/redmine/$lInstance/email.yml
        ucf --purge /etc/redmine/$lInstance/session.yml
    fi
    rm -rf /etc/redmine/$lInstance
done
db_set redmine/old-instances ""

で、この時点で redmine/old-instances なテンプレを初期化してますね。その後、もう少し初期処理は続きます。変数設定とか。

fRailsEnv=production
fRailsLog=/var/log/redmine
fRailsVar=/var/lib/redmine
fRailsCache=/var/cache/redmine
fRailsDir=/usr/lib/ruby/vendor_ruby/rails
if [ ! -e "$fRailsDir" ]; then
    # keep rails package compatibility
    fRailsDir="/usr/share/rails-ruby1.8"
fi

あとは configure ないし reconfigure の場合、な記述になってます。

case "$1" in
    configure|reconfigure)

とりあえずアタマから順に見ていきます。

        # passenger runs as the owner of that file, thanks Micah Anderson.
        chown www-data:root /usr/share/redmine/config/environment.rb
        chown -f www-data:www-data $fRailsLog
        chown -f www-data:www-data $fRailsVar
        chown -f www-data:www-data $fRailsCache
        savedir="`pwd`"
        cd /usr/share/redmine

これらのディレクトリについてはパケジに同梱されております。postinst はパケジが展開された後に実行されるはずなのでディレクトリの実態はスデに存在する、と。で、それに対して apache な権限を付けてます。ちなみにいっちゃん上のソレはコメントにあるように passenger に配慮したものらしい。逆に言うと redmineapache+passenger 限定なのかどうなのか。
で、pwd 保存しといて /usr/share/redmine に cd してます。以降は若干 Rails 固有の下準備になるのかどうなのか。

        if [ -L vendor/railties ]; then
            # rails 2.2 to 2.3 migration
            rm -f vendor/actionmailer
            rm -f vendor/actionpack
            rm -f vendor/activemodel
            rm -f vendor/activerecord
            rm -f vendor/activeresource
            rm -f vendor/activesupport
            rm -f vendor/railties
            rm -f vendor/rails
        fi

vendor/railties な symlink があったら云々。あまり Rails 詳しくないのでスルーします。次は vendor/rails な symlink があるとか無いとかなナニ。

        if [ ! -L vendor/rails ]; then
            ln -s "$fRailsDir" vendor/rails
        else
            # rails 2.3 to ruby-rails-2.3 migration
            fMigrateLink=$(readlink vendor/rails)
            if [ "$fMigrateLink" != "$fRailsDir" ]; then
                rm -f vendor/rails
                ln -s "$fRailsDir" vendor/rails
            fi
        fi

無ければ作って、あったらリンク実体の確認して云々。で、ファイルが無かった作成したりとか

        if [ ! -e app/views/members ]; then
            mkdir app/views/members
        fi
        if [ ! -e app/sweepers ]; then
            mkdir app/sweepers
        fi
        if [ ! -e lib/plugins ]; then
            mkdir lib/plugins
        fi

あるいは /usr 配下は writable ではないから云々とか

        # this directory should never be used: /usr not writable policy
        if [ -e tmp ]; then
            rm -rf tmp
        fi

で、カレントディレクトリを元に戻して終了。

        cd $savedir
    ;;

    abort-upgrade|abort-remove|abort-deconfigure)
    ;;

    *)
        echo "postinst called with unknown argument \`$1'" >&2
        exit 1
    ;;
esac

で、ここからが核心になります。remine/current-instances 取得して云々

db_get redmine/current-instances || true
gInstances="${RET}"
for lInstance in $gInstances; do

まず下準備。

    fRailsEtc=/etc/redmine/$lInstance
    fRailsLog=/var/log/redmine/$lInstance
    fRailsVar=/var/lib/redmine/$lInstance
    fRailsCache=/var/cache/redmine/$lInstance
    # dbconfig needs this folder to ouput database.yml
    mkdir -p $fRailsEtc
    chown -f www-data:www-data $fRailsEtc
    withdb=0
    lInstall=0

current-instances が複数だった場合に切り分けが可能な形になってます。/etc/redmine まではパケジに含まれてますがその先は mkdir で作成してますね。ucf じゃなくて良いのだろうか。このあたりに切り分けも理解が微妙。
次は dbconfig-common のお約束なナニ。

    db_get redmine/instances/$lInstance/dbconfig-install || true
    if [ "$RET" = "true" ]; then
        lInstall=1
        fCode=0
        db_get redmine/instances/$lInstance/dbconfig-reinstall || fCode=$?
        if [ $fCode -eq 0 ]; then
            if [ "$RET" = "false" ]; then
                db_fget redmine/instances/$lInstance/dbconfig-reinstall seen || true
                if [ "$RET" = "true" ]; then
                    lInstall=0
                fi
            fi
        fi
    fi

あら、ここで lInstall な変数をもごもごしてますね。dbconfig-install とか dbconfig-reinstall が云々というあたり、若干微妙です。dbconfig-common 関連のドキュメントが少なすぎる。
で、直後で lInstall の値によって処理が分岐してます。

    if [ $lInstall -eq 1 ]; then

とりあえず lInstall が 0 のケイスから (短いので) ということで以下。

    elif [ -e "$fRailsEtc/database.yml" ]; then
        # don't reinstall, but upgrade
        withdb=1
        lInstall=0
    fi

ええと lInstall が 0 の場合ってのは

  • dbconfig-install が false
  • dbconfig-install が true で dbconfig-reinstall が false で dbconfig-reinstall の seen フラグが true

下のケイスってコメントにある_upgrade じゃなくて upgrade_というソレに合致するのかどうか。あ、上の条件に /etc/redmine/default/database.yml がある場合、って事か。
で、次は lInstall が単純に 1 の場合を確認します。ここ、基本的には dbconfig-common なナニをもごもごしてる感じですねぇ。
とりあえず先頭あたりが以下。

        fYml=$fRailsEtc/database.yml.new
        lTemplate="database.yml.template"
        # if dbtype=sqlite3, use dbbasepath, else dbname
        fCode=0
        db_get redmine/instances/$lInstance/database-type || fCode=$?
        lDbType="$RET"
        if [ $fCode -eq 0 -a "$lDbType" = "sqlite3" ]; then
            lTemplate="database-sqlite.yml.template"
            lDbType="sqlite"
        fi

補足するとソースパケジの debian 配下に conf というディレクトリがありまして、その中には

  • database-sqlite.yml.template
  • database.yml.template

というファイルが投入されております。lDbType の微妙な書き換えも気にはなりますが、スルーします。次はコメントにある通り、redmine-$lDbType なパケジが installed なのかどうかな分岐。

        # check if redmine-$lDbType is installed, if not, don't configure and explain what to do
        hasPack=$(dpkg-query -W -f='${Status}\n' "redmine-$lDbType" |cut -d" " -f3)
        if [ "$hasPack" != "installed" ]; then
            # not installed, this cannot be detected in debian/config
            # since redmine-* packages are not yet installed
            db_subst redmine/missing-redmine-package dbtype "$lDbType"
            db_subst redmine/missing-redmine-package instance "$lInstance"
            db_fset redmine/missing-redmine-package seen "false"
            db_input high redmine/missing-redmine-package || true
            db_go || true
            db_stop || true
            echo "Skipping $lInstance because of missing dependency."
            continue
        fi

dpkg-query ってコマンドがあるんスね。てか、このケイスって非常にレアなエラーケイスになるのでしょうか。redmine-* なパケジは Depends に入っているはずなので。
で、次か。次は dbconfig-common な変数 (?) 設定してます。

        dbc_generate_include=template:$fYml
        dbc_generate_include_args="-o template_infile=/usr/share/redmine/templates/${lTemplate}"
        dbc_generate_include_owner="root:www-data"
        dbc_generate_include_perms="640"
        dbc_dbfile_owner="root:www-data"
        # this is for sqlite3 to be r/w for www-data
        dbc_dbfile_perms="0660"
        dbc_dbuser=redmine
        # make sure mysql or pgsql database charset is UTF8
        dbc_mysql_createdb_encoding="UTF8"
        dbc_pgsql_createdb_encoding="UTF8"
        # use same dbname if one has been registered in debconf before
        # this is also needed for migration from version <= 0.9.0~svn2819

データベースパケジの属性な設定に見えます。このあたりの変数なナニとかもあまりドキュメントが無いんですがソース嫁ってことなのかなぁ。
次に出てくるのは DB 実体へのパスなどの情報設定ですね。

        fCode=0
        db_get redmine/instances/$lInstance/db/dbname || fCode=$?
        if [ $fCode -eq 0 -a -n "$RET" ]; then
            dbc_dbname="$RET"
        else
            dbc_dbname=redmine_$lInstance
        fi
        fCode=0
        db_get redmine/instances/$lInstance/db/basepath || fCode=$?
        if [ $fCode -eq 0 -a -n "$RET" ]; then
            dbc_basepath="$RET"
        fi

dbname なテンプレ設定されてればそれがファイル名になる。そうでなければ redmine_sqlite みたいな形になる。あるいは basepath なテンプレ設定されてれば云々。確認できてるところでは dbname は未設定で basepath は /var/lib/dbconfig-common なんちゃら、が設定されてたはず。あ、dbname は設定されてますね。

 redmine/instances/default/db/basepath: /var/lib/dbconfig-common/sqlite3/redmine/instances/default
 redmine/instances/default/db/dbname: redmine_default

dbname は設定されてたのか後から、なのかは不明です。次は以下なのですが、何を意図しているのか不明。

        ucf --purge $fYml
        ( . /usr/share/dbconfig-common/dpkg/postinst ; dbc_go redmine/instances/$lInstance $@ )
        ucf --purge $fYml

dbconfig-common なナニは定型なんだと思いますが、ucf --purge で囲んでいるのが意味不明。で、次で database.yml が作成完了するのかな。

        if [ -e $fYml ]; then
            hasdb=$(grep -m 1 -o -E 'adapter: (mysql|pgsql|sqlite3|postgresql)' $fYml) || true
            if [ -n "$hasdb" ]; then
                hasdb=$(echo -n ${hasdb#*:})
                withdb=1
                case "$hasdb" in
                    mysql|postgresql|pgsql)
                        sed -i -r -e 's/pgsql/postgresql/g' $fYml
                    ;;
                    sqlite3)
                        sed -i -r -e 's/^[^#]+((host|port|username|password): [^:]*)$/# \1/g' $fYml
                    ;;
                esac
            fi
            ucf --debconf-ok $fYml /etc/redmine/$lInstance/database.yml
            rm -f $fYml
        fi

置き換えたりコメントアウトしてたりします。最後に ucf で database.yml を投入してますね。これは postrm で ucf --purge されていたはず。
次からが核心部分のクライマックスとなります。
とりあえず処理の対象は configure および reconfigure な様子。

	case "$1" in
		configure|reconfigure)

# (snip)

		;;
	
		abort-upgrade|abort-remove|abort-deconfigure)
		;;
	
		*)
			echo "postinst called with unknown argument \`$1'" >&2
			exit 1
		;;
	esac

では中身を見ていきます。まずはディレクトリを掘って権限の調整。

            # create directories
            mkdir -p $fRailsLog
            mkdir -p $fRailsVar
            mkdir -p $fRailsVar/files
            mkdir -p $fRailsCache
            mkdir -p $fRailsCache/plugin_assets
            chmod 750 $fRailsLog
            chmod 750 $fRailsVar
            chmod 755 $fRailsCache
            chmod 755 $fRailsCache/plugin_assets
            chown -f www-data:www-data $fRailsLog
            chown -f www-data:www-data $fRailsVar
            chown -f www-data:www-data $fRailsVar/files
            chown -f www-data:www-data $fRailsCache
            chown -f www-data:www-data $fRailsCache/plugin_assets

で、カレントディレクトリを保存しておいて /usr/share/redmine に移動。

            savedir="`pwd`"
            cd /usr/share/redmine

db/schema.db をコピーしています。なんとなく ucf 使うか使わないかなナニが微妙だなぁ。ここでは単純に cp で良いらしい。

            # copy schema.db to its instance directory
            if [ -f db/schema.db ]; then
                cp db/schema.db $fRailsCache/schema.db
            fi

次は $fRailsEtc/session.yml が無かった場合に云々、な模様。

            # add secret key, set permissions, manage file with ucf
            if [ ! -f "${fRailsEtc}/session.yml" ]; then

そうか session.yml をここで生成してるんですね。で、ucf で云々か。

                rake -s generate_session_store YML_SESSION_FILENAME="session.yml.new" RAILS_ENV=$fRailsEnv X_DEBIAN_SITEID="${lInstance}" || true
                chown -f root:www-data ${fRailsEtc}/session.yml.new
                chmod 640 ${fRailsEtc}/session.yml.new
                ucf --debconf-ok ${fRailsEtc}/session.yml.new ${fRailsEtc}/session.yml
                rm ${fRailsEtc}/session.yml.new
                echo "A new secret session key has been generated in ${fRailsEtc}/session.yml"

う、これって rake でいきなり /etc/redmine 配下 (ちょっと違うけど) に session.yml.new が生成されるの? 直前で /usr/share/redmine に cd してるのでカレントディレクトリはそこなはず。
で、探してみたら、ありました。config/environment.rb な模様。

ENV['X_DEBIAN_SITEID'] ||= 'default'
ENV['RAILS_ETC'] ||= "/etc/redmine/#{ENV['X_DEBIAN_SITEID']}"

environment.rb もでびあん方式にするために相当手が入ってるんだろうな。以降は session.yml があった場合の処理になるのか。

            else
                fHasOldSessionName=$(fgrep -c session_key "${fRailsEtc}/session.yml" || true)
                if [ $fHasOldSessionName -gt 0 ]; then
                    # in-place, because ucf might be configured to keep the old version without asking
                    sed -i -r -e 's/session_key/key/g' ${fRailsEtc}/session.yml        
                fi
            fi

session_key という文言を置きかえているのかな。次はファイルがあったら云々。意味不明なのでスルー気味で。

            if [ -f config/initializers/session_store.rb ]; then
                mv config/initializers/session_store.rb config/initializers/session_store.rb.dpkg-old
                echo "The old secret session key can be found in
/usr/share/redmine/config/initializers/session_store.rb.dpkg-old"
            fi

核心部分の最後は withdb が 1 ならば、という条件付き。新規導入の場合と upgrade の場合、ということで良いかな。

            if [ $withdb -eq 1 ]; then

まず default-language なテンプレの値を DEFAULT_LANGUAGE という変数に格納してます。

                db_get redmine/instances/${lInstance}/default-language && DEFAULT_LANGUAGE="$RET"

で、rake db:migrate してますね。

                # handle rake install
                echo "Populating database for redmine instance \"${lInstance}\".
This may take a while."
                fCode=0
                rake -s db:migrate RAILS_ENV=$fRailsEnv X_DEBIAN_SITEID="${lInstance}" VERBOSE=$RAKE_VERBOSE || fCode=$?

で、戻りを確認。

                if [ $fCode -eq 0 ]; then
                    echo "Done."

新規導入だったら (?)

                    if [ $lInstall -eq 1 ]; then

rake redmine:load_default_data して production.log を云々。

                        rake -s redmine:load_default_data RAILS_ENV=$fRailsEnv X_DEBIAN_SITEID="${lInstance}" REDMINE_LANG=$DEFAULT_LANGUAGE || true
                        # because rake task is executed as root here, and this file is used later by web server, make sure owner is www-data
                        touch ${fRailsLog}/production.log
                        chown -f www-data:www-data ${fRailsLog}/production.log
                    fi

ネストが深いですね。次に db:migrate_plugins して終わり。

                    # handle plugins migration
                    rake -s db:migrate_plugins RAILS_ENV=$fRailsEnv X_DEBIAN_SITEID="${lInstance}" VERBOSE=$RAKE_VERBOSE || true

以下は rake db:migrate が失敗した場合のナニです。

                else
                    echo "Error when running rake db:migrate, check database configuration."
                    exit -1
                fi

以下は withdb が 1 以外の場合のナニ。

            else
                echo "Redmine instance \"${lInstance}\" database must be configured manually."
            fi
            cd $savedir || true

最後にカレントディレクトリを元に戻しておしまい。こうして見るに他の Rails な Web アプリでも redmine 方式を踏襲してメンテナスクリプトを作るのはありだと思ってます。
ただ、今回は sqlite3 限定にする方向なので沢山手が入るけどある意味簡単になるはず。