株式会社クリアコード > ククログ

ククログ


Debian GNU/Linux上でRPMパッケージ・Yumリポジトリを作って公開する方法: milter managerを例にして

注: FedoraやCentOSのRPMパッケージャーが書いた文章ではありません。FedoraやCentOSのRPMパッケージメンテナになりたい方はFedoraやCentOSが公式に配布している文書の方をお勧めします。例えば、How to create an RPM package - FedoraProjectという文書があります。

Debianパッケージの作り方と公開方法: groongaを例にしてのRPM版のような内容です。

ここでは、迷惑メール対策ソフトウェアmilter managerを例にしてDebian GNU/Linux上でCentOS 5.4向けのRPMパッケージを作成し、それを提供するYumリポジトリを作成・公開する方法を紹介します。RPMパッケージの作成では、1つのspecファイルから複数のパッケージを作成します。この方法は、ライブラリを提供するソフトウェアの場合に多く用いられます。

また、Yumリポジトリを登録するRPMも作成します。そのため、ユーザには以下のようにYumリポジトリを登録してもらうことになります。

Yumリポジトリを登録:

% sudo rpm -Uvh http://milter-manager.sourceforge.net/centos/5/milter-manager-repository-1.0.0-0.noarch.rpm

インストール:

% sudo yum install -y milter-manager

ここで紹介する方法を自動化するスクリプト群はmilter managerのリポジトリで公開されています。ここで紹介する方法でRPMパッケージ・Yumリポジトリを作成・公開する場合は参考にしてください。

それでは、まずはパッケージの作り方です。

パッケージの作り方

RPMパッケージを作るためには.specを作ります。ここでは、複数のパッケージを生成する.specを作成するので、まず、どれをどのパッケージにいれるかを検討します。

構成

milter managerはmilterプロトコルを実装したライブラリ、それを利用したmilter管理アプリケーション、ユーティリティツールで構成されています。この場合、以下のように複数のパッケージに分解することが多いようです。

  • libXXX: ライブラリを使用しているソフトウェアを実行するために必要なファイルを提供するパッケージ。/usr/lib/libXXX.soなどを提供する。
  • libXXX-devel: ライブラリを使用しているソフトウェアをビルドするために必要なファイルを提供するパッケージ。/usr/include/XXX/*.hなどを提供することが多い。libXXXに依存する。
  • XXX: (ライブラリではなく)コマンドを提供するパッケージ。libXXXに依存する。

milter managerの場合は以下のパッケージを作成することとします。

  • libmilter-toolkit: /usr/lib/libmilter-client.soなどを提供する。
  • libmilter-toolkit-devel: /usr/include/milter-manager/milter/client.hなどを提供。
  • libmilter-compatible: libmilterとABI互換の/usr/lib/libmilter.soを提供する。
  • libmilter-compatible-devel: libmilterとAPI互換の/usr/include/milter-manager/libmilter/*.hを提供する。
  • milter-manager: milter-managerコマンドを提供する。
  • milter-manager-munin-plugin: milter managerの統計情報を収集するmunin-nodeのプラグインを提供する。

雛形

複数のパッケージを作成する.specは以下のような内容になります。簡単ですね、とは言えないくらいの長さです*1

Summary: 簡単な説明
Name: (メイン)パッケージ名(「milter-manager」など)
Version: バージョン番号(「1.5.0」など)
Release: リリースバージョン(最初は「0{?dist}」にしておけばよい)
License: ライセンス(「GPLv3+」など)
URL: ソフトウェアのサイトのURL
     (「http://milter-manager.sourceforge.net/」など)
Group: (メイン)パッケージが属するグループ
       (「System Environment/Daemons」など。
         「/usr/share/doc/rpm/GROUPS」に一覧がある。)
Source: ソースのアーカイブのURL
        (「http://downloads.sourceforge.net/milter-manager/milter-manager-1.5.0.tar.gz」など)
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
           (常に↑でOK)
BuildRequires: ビルドに必要なパッケージ
BuildRequires: 必要な分だけ書く
Requires: 動作に必要なパッケージ
Requires: 必要な分だけ書く

%description
詳細なパッケージの説明。複数行になってもOK。

% package -n サブパッケージ名
          (「-n」をつけないと「『パッケージ名』-『サブパッケージ名』」
            というパッケージ名になるので、「libXXX」というパッケージ名を
            つけるときは「-n」をつけないといけない)
Summary: サブパッケージの簡単な説明
Group: サブパッケージの属するグループ

% description -n サブパッケージ名
              (「-n」の意味は「%package」と同じ)
詳細なサブパッケージの説明。複数行になってもOK。

%prep
%setup -q
%build
%configure オプション
          (「--with-default-effective-user=milter-manager」など)
make %{?_smp_mflags}

% install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}

%clean
rm -rf %{buildroot}

%files
%defattr(-, root, root, -)
(メイン)パッケージに含めるファイルを指定。

%files -n サブパッケージ名
%defattr(-, root, root, -)
サブパッケージに含めるファイルを指定。

%changelog
* 日付 名前 <メールアドレス>
- (バージョン番号-リリース番号)
- new upstream release

1つの.specで1つのパッケージを作る場合は%package -n サブパッケージ名%description -n サブパッケージ名%files -n サブパッケージ名がなくなるだけで、基本的な記述は変わりません。

milter managerの場合は以下のような.specになっています。%prepostなどが増えていますが、これらは、それぞれインストール前・インストール後に実行するシェルスクリプトを指定しているだけです。

そこそこ長いので、詳細に興味がない場合は読み飛ばしてください。

Summary: A milter to use milters effectively
Name: milter-manager
Version: 1.5.0
Release: 11%{?dist}
License: GPLv3+, LGPL3+, AGPL3+, GFDL, Public Domain
URL: http://milter-manager.sourceforge.net/
Group: System Environment/Daemons
Source: http://downloads.sourceforge.net/milter-manager/milter-manager-1.5.0.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
BuildRequires: intltool
BuildRequires: gettext
BuildRequires: gcc
BuildRequires: make
BuildRequires: glib2-devel
BuildRequires: ruby
BuildRequires: ruby-devel
Requires: glib2
Requires: ruby
Requires: libmilter-toolkit = %{version}-%{release}
Requires(pre): /usr/bin/getent, /usr/sbin/useradd
Requires(pre): /usr/bin/id, /usr/sbin/groupadd
Requires(post): /sbin/chkconfig
Requires(preun): /sbin/service, /sbin/chkconfig
Requires(postun): /sbin/service, /sbin/chkconfig, /usr/sbin/userdel

%description
milter manager administrates milters instead of MTA to reduce milter
administration cost and combine milters flexibly.

%package -n libmilter-toolkit
Summary: A milter protocol library
Group: System Environment/Libraries

%description -n libmilter-toolkit
Both of client-side and server-side milter protocol are implemented.
This package contains the library files required for running services
built using libmilter-toolkit.

%package -n libmilter-toolkit-devel
Summary: Development files for libmilter-toolkit
Group: Development/Libraries
Requires: libmilter-toolkit = %{version}-%{release}

%description -n libmilter-toolkit-devel
This package contains the headers, and other support files
required for developing applications against libmilter-toolkit.

%package -n libmilter-compatible
Summary: libmilter API and ABI compatible milter library
Group: System Environment/Libraries
Requires: libmilter-toolkit = %{version}-%{release}

%description -n libmilter-compatible
A libmilter API and ABI compatible library based on libmilter-toolkit.
This package contains the library files required for running services
built using Sendmail libmilter or libmilter-compatible.

%package -n libmilter-compatible-devel
Summary: Development files for libmilter-compatible
Group: Development/Libraries
Requires: libmilter-compatible = %{version}-%{release}
Requires: libmilter-toolkit-devel = %{version}-%{release}

%description -n libmilter-compatible-devel
This package contains the headers, and other support files
required for developing applications against libmilter-compatible.

%package -n milter-manager-munin-plugin
Summary: Munin plugin for milter manager
Group: System Environment/Libraries
Requires: milter-manager = %{version}-%{release}
Requires: munin-node

%description -n milter-manager-munin-plugin
This package contains the munin plugin for munin-node.

%prep
%setup -q

%build
%configure								\
    --with-default-effective-user=milter-manager			\
    --with-default-effective-group=milter-manager			\
    --with-default-socket-group=smmsp					\
    --with-default-pid-file=/var/run/milter-manager/milter-manager.pid	\
    --with-default-connection-spec=unix:/var/run/milter-manager/milter-manager.sock
make %{?_smp_mflags}

%install
rm -rf %{buildroot}
make install DESTDIR=%{buildroot}

mkdir -p %{buildroot}%{_initrddir}
install -m 755 data/init.d/redhat/milter-manager %{buildroot}%{_initrddir}/milter-manager

mkdir -p %{buildroot}%{_sysconfdir}/sysconfig
install -m 644 data/init.d/redhat/sysconfig/milter-manager %{buildroot}%{_sysconfdir}/sysconfig/milter-manager

mkdir -p %{buildroot}%{_sysconfdir}/cron.d
install -m 600 data/cron.d/redhat/milter-manager-log %{buildroot}%{_sysconfdir}/cron.d/milter-manager-log

mkdir -p %{buildroot}%{_localstatedir}/run/milter-manager/

mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d/
cat <<EOC > %{buildroot}%{_sysconfdir}/httpd/conf.d/milter-manager-log.conf
Alias /milter-manager-log/ /var/lib/milter-manager/public_html/log/
EOC

mv %{buildroot}%{_datadir}/milter-manager/munin/ %{buildroot}%{_datadir}/
mkdir -p %{buildroot}%{_sysconfdir}/munin/plugin-conf.d/
cat <<EOC > %{buildroot}%{_sysconfdir}/munin/plugin-conf.d/milter-manager
[milter_manager_*]
  user milter-manager
  env.logdir /var/lib/milter-manager/public_html/log
EOC

%clean
rm -rf %{buildroot}

%pre
if ! /usr/bin/getent group milter-manager &>/dev/null; then
    /usr/sbin/groupadd -r milter-manager || \
        %logmsg "Unexpected error adding group \"milter-manager\". Aborting installation."
fi
if ! /usr/bin/id milter-manager &>/dev/null; then
    /usr/sbin/useradd -r -s /sbin/nologin -c 'milter manager' \
        -d %{_localstatedir}/lib/milter-manager --create-home \
        -g milter-manager milter-manager || \
        %logmsg "Unexpected error adding user \"milter-manager\". Aborting installation."
fi

%post
/sbin/chkconfig --add milter-manager
/bin/mkdir -p /var/run/milter-manager
/bin/chown -R milter-manager:milter-manager /var/run/milter-manager

%post -n milter-manager-munin-plugin
/usr/sbin/munin-node-configure --shell | \
    grep -e '\(milter_manager_\|\(postfix\|sendmail\)_processes\)' | \
    sh
[ -f /var/lock/subsys/munin-node ] && \
    /sbin/service munin-node restart > /dev/null 2>&1
:

%preun
if [ $1 -eq 0 ] ; then
    /sbin/service milter-manager stop > /dev/null 2>&1
    /sbin/chkconfig --del milter-manager
fi

%postun
if [ $1 -ge 1 ] ; then
    /sbin/service milter-manager condrestart > /dev/null 2>&1
fi

if [ $1 -eq 0 ]; then
    /usr/sbin/userdel -r milter-manager || \
        %logmsg "User \"milter-manager\" could not be deleted."
fi

%postun -n milter-manager-munin-plugin
if [ $1 -eq 0 ]; then
    rm %{_sysconfdir}/munin/plugins/milter_manager_* > /dev/null 2>&1
    rm %{_sysconfdir}/munin/plugins/postfix_processes > /dev/null 2>&1
    rm %{_sysconfdir}/munin/plugins/sendmail_processes > /dev/null 2>&1
    [ -f /var/lock/subsys/munin-node ] && \
        /sbin/service munin-node restart > /dev/null 2>&1
    :
fi

%files
%defattr(-, root, root, -)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%doc %{_datadir}/gtk-doc/html/milter-manager/
%{_bindir}/milter-manager-log-analyzer
%{_sbindir}/milter-manager
%{_includedir}/milter-manager/milter/manager.h
%{_includedir}/milter-manager/milter/manager/
%{_libdir}/libmilter-manager.*
%{_libdir}/milter-manager/binding/
%{_libdir}/milter-manager/module/
%{_libdir}/pkgconfig/milter-manager.pc
%{_mandir}/man1/milter-manager.*
%{_mandir}/man1/milter-manager-log-analyzer.*
%{_mandir}/ja/man1/milter-manager.*
%{_mandir}/ja/man1/milter-manager-log-analyzer.*
%{_initrddir}/milter-manager
%{_datadir}/milter-manager/admin/
%{_sysconfdir}/milter-manager/cron.d/
%{_sysconfdir}/milter-manager/init.d/
%{_sysconfdir}/milter-manager/rc.d/
%{_sysconfdir}/cron.d/
%config %{_sysconfdir}/sysconfig/milter-manager
%config %{_sysconfdir}/milter-manager/milter-manager.conf
%config %{_sysconfdir}/milter-manager/defaults/
%config %{_sysconfdir}/milter-manager/applicable-conditions/
%config %{_sysconfdir}/httpd/conf.d/milter-manager-log.conf

%defattr(-, milter-manager, milter-manager, 0755)
%dir %{_localstatedir}/run/milter-manager/

%files -n libmilter-toolkit
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%{_bindir}/milter-test-client
%{_bindir}/milter-test-server
%{_bindir}/milter-performance-check
%{_libdir}/libmilter-core.so.*
%{_libdir}/libmilter-client.so.*
%{_libdir}/libmilter-server.so.*
%{_mandir}/man1/milter-test-client.*
%{_mandir}/man1/milter-test-server.*
%{_mandir}/man1/milter-performance-check.*
%{_mandir}/ja/man1/milter-test-client.*
%{_mandir}/ja/man1/milter-test-server.*
%{_mandir}/ja/man1/milter-performance-check.*

%files -n libmilter-toolkit-devel
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%doc %{_datadir}/gtk-doc/html/milter-manager/
%{_includedir}/milter-manager/milter/core.h
%{_includedir}/milter-manager/milter/core/
%{_includedir}/milter-manager/milter/client.h
%{_includedir}/milter-manager/milter/client/
%{_includedir}/milter-manager/milter/server.h
%{_includedir}/milter-manager/milter/server/
%{_libdir}/libmilter-core.so
%{_libdir}/libmilter-core.la
%{_libdir}/libmilter-client.so
%{_libdir}/libmilter-client.la
%{_libdir}/libmilter-server.so
%{_libdir}/libmilter-server.la
%{_libdir}/pkgconfig/milter-core.pc
%{_libdir}/pkgconfig/milter-client.pc
%{_libdir}/pkgconfig/milter-server.pc

%files -n libmilter-compatible
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%{_libdir}/milter-manager/libmilter.so.*

%files -n libmilter-compatible-devel
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%doc %{_datadir}/gtk-doc/html/milter-manager/
%{_includedir}/milter-manager/libmilter/
%{_libdir}/milter-manager/libmilter.so
%{_libdir}/milter-manager/libmilter.la
%{_libdir}/pkgconfig/libmilter.pc

%files -n milter-manager-munin-plugin
%defattr(-,root,root)
%doc ChangeLog ChangeLog.toolkit README README.ja NEWS NEWS.ja TODO
%doc %{_datadir}/milter-manager/license/
%{_datadir}/munin/
%config %{_sysconfdir}/munin/plugin-conf.d/

%changelog
* Thu Feb 17 2010 Kouhei Sutou <kou@clear-code.com>
- (1.5.0-11)
- new upstream release

* Thu Oct 29 2009 Kouhei Sutou <kou@clear-code.com>
- (1.4.1-0)
- new upstream release

* Thu Oct 13 2009 Kouhei Sutou <kou@clear-code.com>
- (1.4.0-0)
- new upstream release

* Wed Sep 16 2009 Kouhei Sutou <kou@clear-code.com>
- (1.3.1-0)
- new upstream release

* Wed Aug 12 2009 Kouhei Sutou <kou@clear-code.com>
- (1.3.0-0)
- new upstream release

* Fri Jul 17 2009 Kouhei Sutou <kou@clear-code.com>
- (1.2.0-0)
- new upstream release

* Fri Jul 03 2009 Kouhei Sutou <kou@clear-code.com>
- (1.1.1-0)
- new upstream release

* Tue Jun 02 2009 Kouhei Sutou <kou@clear-code.com>
- (1.1.0-0)
- initial 1.1.x development seriese release

* Thu Apr 16 2009 Kouhei Sutou <kou@clear-code.com>
- (1.0.0-1)
- initial stable release

ビルド: rinse

Debian GNU/Linux上でCentOS用のRPMパッケージをビルドすることは茨の道です。そこで、CentOS用のchroot環境を作って、そこでビルドすることにします。こうすることで、CentOSの実機がなくてもビルドできる上に、きれいな環境でビルドすることもできます。

Debian GNU/LinuxやUbuntuのchroot環境を作るにはdebootstrapが便利です。CentOSやFedoraのchroot環境を作るにはrinseが便利です。

/var/lib/chroot/centos-i386/以下にCentOS 5.4 i386用のchroot環境を構築するには以下のようにします。

% sudo aptitude install -y rinse
% sudo mkdir -p /var/lib/chroot/centos-i386/etc/rpm/
% sudo sh -c "echo i386-centos-linux > /var/lib/chroot/centos-i386/etc/rpm/platform"
% sudo rinse --arch i386 --distribution centos-5 --directory /var/lib/chroot/centos-i386/

/etc/fstabに以下を追記:

/dev          /var/lib/chroot/centos-i386/dev     none   bind     0 0
devpts-chroot /var/lib/chroot/centos-i386/dev/pts devpts defaults 0 0
proc-chroot   /var/lib/chroot/centos-i386/proc    proc   defaults 0 0

本質的な部分はsudo rinse ...だけです。sudo rinse ...の前にあるRPMパッケージで利用するプラットフォームを指定している箇所はrinseのバグを回避するためです*2。これがないとamd64のDebian GNU/Linux環境でi386のCentOS環境を作成することができません。

/etc/fstabへの追記は、再起動する度にmountしなおすのが面倒だからです。

この作業はbuild-in-chroot.shの中で自動化されています。

ビルド: chroot

CentOSのchroot環境ができたら、その環境にビルド専用アカウントを作成し、そのユーザでビルドします。ここで紹介する作業はbuild-rpm.shの中で自動化されています。ここでは、その中の一部を説明します。

まず、ビルド用ユーザが存在しない場合はユーザを作成します。

1
2
3
4
USER_NAME=milter-manager-build
if ! id $USER_NAME >/dev/null 2>&1; then
  useradd -m $USER_NAME
fi

次に、そのビルド用ユーザが実行するビルドスクリプトを作成します。.tar.gzや.specなどのビルドに必要なファイルはchroot環境の/tmp/以下(/var/lib/chroot/centos-i386/tmp/以下)に事前にコピーしておきます。それぞれの処理内容はコメントとして説明しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
BUILD_SCRIPT=/tmp/build-milter-manager.sh
VERSION=`cat /tmp/milter-manager-version`

cat <<EOF > $BUILD_SCRIPT
#!/bin/sh

# RPMパッケージのビルドは~/rpm/以下で行う。
if [ ! -f ~/.rpmmacros ]; then
    cat <<EOM > ~/.rpmmacros
%_topdir \$HOME/rpm
EOM
fi

# RPMパッケージ作成に必要なディレクトリを作成。
# rpmdevtoolsパッケージに含まれているrpmdev-setuptreeコマンド
# でも同様のことができるよう。
mkdir -p rpm/SOURCES
mkdir -p rpm/SPECS
mkdir -p rpm/BUILD
mkdir -p rpm/RPMS
mkdir -p rpm/SRPMS

# ソースと.specを配置。
cp /tmp/milter-manager-$VERSION.tar.gz rpm/SOURCES/
cp /tmp/milter-manager.spec rpm/SPECS/

# RPMパッケージ作成。
rpmbuild -ba rpm/SPECS/milter-manager.spec
EOF

このビルドスクリプトをビルド用ユーザで実行します。

1
2
chmod +x $BUILD_SCRIPT
su - $USER_NAME $BUILD_SCRIPT

ビルドが成功するとビルド用ユーザの~/rpm/RPMS/i386/以下にRPMパッケージができます*3

RPMパッケージができたら、それらに署名をします。

署名

RPMでは、パッケージに署名することによりパッケージ作成者のなりすましを防止することができます。署名の検証を無効にすることもできるので、署名なしのRPMパッケージでもYumリポジトリで公開・インストールすることはできますが、よほどの理由がない場合は署名をするべきでしょう。

パッケージへの署名はパッケージ作成時にも作成後にも行うことができます。パッケージ作成後に行う場合はrpmコマンドの--resignオプションを使います。署名する鍵は_gpg_nameで指定します。

% rpm -D "_gpg_name Kouhei Sutou <kou@clear-code.com>" --resign XXX.rpm

これをそれぞれの.rpmに対して行います。

RPMパッケージに署名したら、署名済みRPM使ってYumリポジトリを作ります。

Yumリポジトリの作成

milter managerは安定版と開発版の2つのリリースラインがあります。そのため、以下のようなディレクトリ構成でYumリポジトリも2つ作ります。下の図のdevelopmentとstableがそれぞれYumリポジトリになります。各YumリポジトリはSRPMS, i386, x86_64ディレクトリを持ち、その下にビルドしたRPMパッケージを配置します。

.
+--- centos/
     +--- 5/
          +--- development
          |    +--- SRPMS/
          |    |    +--- milter-manager-1.5.0-0.src.rpm
          |    |    +--- ...
          |    +--- i386/
          |    |    +--- CentOS/
          |    |         +--- milter-manager-1.5.0-0.i386.rpm
          |    |         +--- ...
          |    +--- x86_64/
          |         +--- CentOS/
          |              +--- milter-manager-1.5.0-0.x86_64.rpm
          |              +--- ...
          +--- stable
               +--- SRPMS/
               |    +--- milter-manager-1.4.1-0.src.rpm
               |    +--- ...
               +--- i386/
               |    +--- CentOS/
               |         +--- milter-manager-1.4.1-0.i386.rpm
               |         +--- ...
               +--- x86_64/
                    +--- CentOS/
                         +--- milter-manager-1.4.1-0.x86_64.rpm
                         +--- ...

このように.rpmを配置したらSRPMS, i386, x86_64のそれぞれのディレクトリに対してcreaterepoコマンドを実行します。この作業はupdate-repository.shで自動化されています。

1
2
3
for dir in centos/5/*/*; do
    createrepo $dir
done

createrepoコマンドを実行するとそれぞれのディレクトリの下にrepodataというディレクトリが作成され、パッケージの情報が格納されます。

これでYumリポジトリは完成です。HTTPでアクセスできるところにアップロードしてください。

http://milter-manager.sourceforge.net/centos/以下でアクセスできるところにアップロードしたとすると、Yumのリポジトリ指定は以下のようになります。

/etc/yum.repos.d/milter-manager.repo:

[milter-manager]
name=milter manager for CentOS-$releasever
baseurl=http://milter-manager.sourceforge.net/centos/$releasever/stable/$basearch/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-milter-manager

gpgkeyはRPMに署名した時に使ったキーの公開鍵が入ったファイルを指定します。公開鍵は以下のコマンドで出力できます。

% gpg --export --armor "Kouhei Sutou <kou@clear-code.com>"

これを/etc/pki/rpm-gpg/RPM-GPG-KEY-milter-managerに置くことになります。

つまり、Yumリポジトリを登録するためには以下の作業が必要になります。

  1. /etc/yum.repos.d/milter-manager.repoの作成
  2. 公開鍵の配置

2段階にわかれているので面倒ですね。Yumリポジトリ登録の手間を軽減するために、「Yumリポジトリを登録するRPMパッケージ」を作りましょう。

Yumリポジトリ登録RPMの作成

ここで作成する「Yumリポジトリを登録するRPMパッケージ」はbuild-repository-rpm.shで自動化されています。

Yumリポジトリ登録に必要なものは「リポジトリを指定するファイル」と「RPMパッケージを署名している公開鍵」の2つです。それらをアーカイブした.tar.gzがソースのパッケージを作成します。

% tar cvzf milter-manager-repository.tar.gz milter-manager.repo RPM-GPG-KEY-milter-manager

.specはこのようになります。

Summary: milter manager RPM repository configuration
Name: milter-manager-repository
Version: 1.0.0
Release: 0
License: GPLv3+
URL: http://milter-manager.sourceforge.net/
Source: milter-manager-repository.tar.gz
Group: System Environment/Base
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-%(%{__id_u} -n)
BuildArchitectures: noarch

%description
milter manager RPM repository configuration.

%prep
%setup -c

%build

%install
%{__rm} -rf %{buildroot}

%{__install} -Dp -m0644 RPM-GPG-KEY-milter-manager %{buildroot}%{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-milter-manager

%{__install} -Dp -m0644 milter-manager.repo %{buildroot}%{_sysconfdir}/yum.repos.d/milter-manager.repo

%clean
%{__rm} -rf %{buildroot}

%post
rpm -q gpg-pubkey-1c837f31-4a2b9c3f &>/dev/null || \
    rpm --import %{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-milter-manager

%files
%defattr(-, root, root, 0755)
%doc *
%pubkey RPM-GPG-KEY-milter-manager
%dir %{_sysconfdir}/yum.repos.d/
%config(noreplace) %{_sysconfdir}/yum.repos.d/milter-manager.repo
%dir %{_sysconfdir}/pki/rpm-gpg/
%{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-milter-manager

%changelog
* Sat Feb 06 2010 Kouhei Sutou <kou@clear-code.com>
- (1.0.0-0)
- Initial package.

大事な部分はBuildArchitectures%postの部分です。

このYumリポジトリ登録RPMはプラットフォームに関係なく使えるのでnoarchを指定しています。

%postではRPMにも公開鍵を登録しています。

%post
rpm -q gpg-pubkey-1c837f31-4a2b9c3f &>/dev/null || \
    rpm --import %{_sysconfdir}/pki/rpm-gpg/RPM-GPG-KEY-milter-manager

このYumリポジトリ登録RPMはDebian GNU/Linux上でも作成できます。作成方法はCentOS上での方法と同じです。

% echo "%_topdir $HOME/rpm" > ~/.rpmmacros
% mkdir -p ~/rpm/{SOURCES,SPECS,BUILD,RPMS,SRPMS}
% cp milter-manager-repository.tar.gz ~/rpm/SOURCES/
% cp milter-manager-repository.spec ~/rpm/SPECS/
% rpmbuild -ba ~/rpm/SPECS/milter-manager-repository.spec

これで、~/rpm/RPMS/noarch/以下に.rpmができ、~/rpm/SRPMS/以下に.src.rpmができます。

ここで作成したRPMが冒頭で紹介したRPMです。このRPMを使うことで以下のようにパッケージをインストールできるのでしたね。

% sudo rpm -Uvh http://milter-manager.sourceforge.net/centos/5/milter-manager-repository-1.0.0-0.noarch.rpm
% sudo yum install -y milter-manager

Yumリポジトリの公開

ここまできたら、後はアクセスできる場所にアップロードするだけです。SourceForge.netではrsyncでアップロードできます。今回はhttp://milter-manager.sourceforge.net/centos/以下で公開したいので以下のようなコマンドになります。

% rsync -avz --exclude .gitignore centos/ \
    ktou,milter-manager@web.sourceforge.net:/home/groups/m/mi/milter-manager/htdocs/centos

rsyncではパスの最後の「/」の有無に注意してください。

まとめ

以上がRPMパッケージの作成と作成したRPMパッケージをYumリポジトリで公開する手順です。これら一連の手順を自動化したものはmilter managerのリポジトリで公開しています。読んでみてわかる通り、手順が多く、バージョンが上がる毎に手動で作業するのは大変です。簡単にバージョンアップに対応できるよう、自動化しておきましょう。

*1 Fedoraにはrpmdevtoolsパッケージに含まれているrpmdev-newspecで雛形を作れるようです。

*2 このバグを修正するパッチは作者に送りましたが、まだ取り込まれていません。

*3 src.rpmは~/rpm/SRPMS/以下にできます。

2010-03-03

test-unit 2.0.7とCutter 1.1.1をリリース

先日、Ruby用のxUnit系テスティングフレームワークtest-unit 2.0.7とC・C++用のxUnit系テスティングフレームワークCutter 1.1.1がリリースされました。

どちらも、テストの書きやすさ(テストをキレイに書けるとテストを保守しやすい)だけではなく、テストが失敗した時に「できるだけ素早く問題の原因にたどり着ける」ことも重視しています。

Rails/Rack界隈ではCucumberWebratcapybaraなどを使って、「"ログイン"ボタンをクリックする」とか「click_link("ログイン")」などと、直感的にテストを書けるようになっています。では、テストが失敗したときの結果はどのように表示されるでしょうか。HTML全体やテキスト全体が表示されて、「"ログイン"というボタン(リンク)はなかったよ」と言われたらどうでしょう。あなたのコードはどこが悪かったのでしょう。

そういうときに、失敗結果を見て、すぐに「あぁ、ここが悪いかも!」と作業を進めていけるようなテスティングフレームワークにしたいものです。開発はデバッグの連続なのですから、よりスムーズにデバッグ作業を進める手助けとなるツールを使って開発したいですよね。

test-unitやCutterはWebアプリケーション用に特別なサポート機能は提供していないので上記のようなことをうまい具合に解決できるわけではないのですが、ライブラリのテストでは上記のようなことをうまい具合に解決する機能を提供しています。

Cutterのインストール方法がより簡単に

一応、リリースで変わったことを少し書いておきます。

機能面でもよくなっているのですが、インストールまわりだけにしておきます。

Cutterプロジェクトでは、これまでDebian, Ubuntu用のapt-lineとMacPortsのPortfileを提供していましたが、今回のリリースから、FedoraのYumリポジトリも提供するようにしました。以下でインストールできます。yumの管理下に入るのでアップデートも簡単ですね。

% sudo rpm -Uvh http://cutter.sourceforge.net/fedora/cutter-repository-1.0.0-0.noarch.rpm
% sudo yum install cutter

test-unitの方は変わらずgemをサポートしているので、こちらもインストール・アップデートが簡単ですね。

書きやすさだけではなくデバッグしやすさも重視したテスティングフレームワークに興味のある方は使ってみてはいかがでしょうか。

タグ: テスト | Ruby | Cutter
2010-03-11

るびま0029号

るびま0029号がリリースされていますね。おめでとうございます。

せっかくなので少し紹介します。

ActiveLdapを使ってみよう

今回のるびまにはRubyでLDAPを操作するための便利ライブラリActiveLdapの記事の後編ActiveLdap を使ってみよう(後編)が入っています。

後編では今まではあまり文書化されていなかったフィルタのこと関連性のことにも触れています。全てのエントリを扱うクラスは、知る人ぞ知るのノウハウではないでしょうか。(ActiveLdap付属のサンプルアプリケーションでは使われていますが文書化はされていなかったはず。)LDAPサーバの設定やデータを確認する場合にはLDAP のスキーマ情報を ActiveLdap から参照するあたりの情報が役に立ちます。ldapsearchなどではなく、irbでLDAPサーバの情報を確認できるので、環境構築時やデバッグ時にとても便利です。(irbの補完機能を有効にするとより便利です。)

記事を書いている高瀬さんActiveLdapのチュートリアルの翻訳もしている頼もしい方です。ActiveLdapに興味はあるけどまだよく知らないという方は、まず、 ActiveLdap を使ってみよう(前編)を読んでからチュートリアルを読んで、最後に後編を読むのがよいのではないでしょうか。

0029-RubyNewsにもActiveLdap 1.2.1のリリースが載っていますね。

とちぎRuby会議02

発表者として参加したとちぎRuby会議02のレポート記事RegionalRubyKaigi レポート (10) とちぎ Ruby 会議 02もあります。レポートにもありますが、とちぎRuby会議02のお題が他のRegionalRubyKaigiと違ったものだったので、独特な内容になっていましたね。

Rubyベストプラクティス

0029 号 巻頭言で紹介されているRuby 1.9向けに書かれたRubyベストプラクティスという本がそろそろ発売するようです。テストまわりの章だけレビューに参加しました。

少しくせがある印象なので、Ruby初心者の方にはつらいかもしれません。自分で判断できる程度にRubyを知っている方ならいろいろ考えながら読むとおもしろいかもしれません。

まとめ

るびまがリリースされていたので紹介しました。

ところで、みなさんは編集後記は読んでいるのでしょうか。たまにおもしろかったりするので、読んでいない方は読んでみてはいかがでしょうか。短いのでさっと目を通せます。

タグ: Ruby
2010-03-16

Ruby 1.9.xでRange#include?を高速に動かす方法

Ruby 1.9.xではRange#include?の実装が変わり、Ruby 1.8.xよりも圧倒的に遅くなるケースがあります。これは、Ruby 1.9.xへ移行したときの有名なハマりポイントの1つでしょう。

例えば、こんなケースです。

1
2
3
4
5
6
7
8
9
10
require 'time'
require 'benchmark'

Benchmark.bm(10) do |bm|
  march = Time.parse("2010/03/01")...Time.parse("2010/04/01")
  march_15 = Time.parse("2010/03/15")
  bm.report("include?") do
    march.include?(march_15)
  end
end

2010年3月15日が2010年3月に入っているかを調べています。

これをRuby 1.8.7で動かすとこうなります。

% ruby -v /tmp/range-include.rb
ruby 1.8.7 (2010-01-10 patchlevel 249) [x86_64-linux]
                user     system      total        real
include?    0.000000   0.000000   0.000000 (  0.000011)

一瞬ですね。

Ruby 1.9.1で動かすとこうなります。

% ruby1.9.1 -v /tmp/range-include.rb
ruby 1.9.1p378 (2010-01-10 revision 26273) [x86_64-linux]
                user     system      total        real
include?    1.360000   0.030000   1.390000 (  1.766556)

1万倍以上も遅くなります。

原因

どうしてこうなるのかというと、1.8.xのRange#include?と1.9.xのRange#include?では引数の値の見つけ方が異なるからです。

1.8.xでは引数が範囲の最初の値より大きいかつ最後の値より小さい、ことだけを確認していました。1.9.xでは範囲の最初から最後まで順に繰り返し、その中に引数と==な値が含まれるかどうかを確認します。コードで表すとこんな感じです。

1
2
3
4
5
6
7
8
9
10
11
12
13
def range_include_18(range, value)
  range.begin < value and value < range.end
end

def range_include_19(range, value)
  range_value = range.begin
  loop do
    return true if range_value == value
    range_value = range_value.succ
    break if range_value >= range.end
  end
  false
end

Time#succは1秒先の時間を返します。そのため、一回のRange#include?で最大2678400回ループをまわすことになります(範囲に入っていないとき、つまり、falseを返すとき)。

1
(Time.parse("2010/03/01")...Time.parse("2010/04/01")).to_a.size # => 2678400

真ん中あたりのTime.parse("2010/03/15")でも1209601番目なので、1.8.xのRange#include?と比べてだいぶ遅くなるというわけです。

1
(Time.parse("2010/03/01")..Time.parse("2010/03/15")).to_a.size # => 1209601

解決法

1.9.xには1.8.xのRange#include?と同じように比較するメソッドRange#cover?が追加されているのでそれを使うとよいでしょう。

1
2
3
4
5
6
7
8
9
10
require 'time'
require 'benchmark'

Benchmark.bm(10) do |bm|
  march = Time.parse("2010/03/01")...Time.parse("2010/04/01")
  march_15 = Time.parse("2010/04/01")
  bm.report("cover?") do
    march.cover?(march_15)
  end
end

実行するとたしかに速いです。

% ruby1.9.1 -v /tmp/range-cover.rb
ruby 1.9.1p378 (2010-01-10 revision 26273) [x86_64-linux]
                user     system      total        real
cover?      0.000000   0.000000   0.000000 (  0.000010)

ただし、1.8.xにはRange#cover?はないので、Range#cover?を使うと1.9.x専用のスクリプトになってしまいます。

高速なRange#include?

実は、1.9.xのRange#include?も、数字の範囲または文字の範囲、の場合は高速に動作します。この場合だけ特別扱いされていて1.8.xと同様の比較方法を用いているからです。つまり、どうにかして数字の範囲か文字の範囲に落としこめば1.8.xでも1.9.xでも高速に動作するようなスクリプトを書けるということです。

Timeの場合はTime#to_iで数値にしてしまうのがよいでしょう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require 'time'
require 'benchmark'

Benchmark.bm(10) do |bm|
  march = Time.parse("2010/03/01")...Time.parse("2010/04/01")
  march_15 = Time.parse("2010/03/15")
  bm.report("Time") do
    march.include?(march_15)
  end

  march_integer = (Time.parse("2010/03/01").to_i)...(Time.parse("2010/04/01").to_i)
  march_15_integer = Time.parse("2010/03/15").to_i
  bm.report("Integer") do
    march_integer.include?(march_15_integer)
  end
end

結果は一目瞭然です。

% ruby1.9.1 -v /tmp/range-include-integer.rb
ruby 1.9.1p378 (2010-01-10 revision 26273) [x86_64-linux]
                user     system      total        real
Time        1.230000   0.030000   1.260000 (  1.504888)
Integer     0.000000   0.000000   0.000000 (  0.000009)

まとめ

徐々にRuby 1.9.xを使う人が増えているかもしれない、ということで、Ruby 1.9.xを使った場合にハマりそうなポイントとその解決法を紹介しました。1.9.xでは変わったことがたくさんあります。1.9.xのことをもっと知って上手に付き合ってみてはいかがでしょうか。

タグ: Ruby
2010-03-25

«前月 最新記事 翌月»
タグ:
年・日ごとに見る
2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|