Gitリポジトリが公開されているソフトウェアのDebianパッケージをGitで管理する方法

## 概要

本稿は、GitHub などの Git リポジトリで公開されているソフトウェアの Debian パッケージを、git-buildpackage パッケージによって提供される gbp コマンドを使って、Git 上で管理する方法のメモです。

前提として、git-buildpackage パッケージの gbp コマンドを使います。また、リモートリポジトリが2つ登場します。

 * 上流リポジトリ: パッケージ対象のソフトウェアが公開されているリポジトリ
 * 管理リポジトリ: 主として `debian/` 配下のパッケージ用ファイルを管理する目的のリポジトリ

まだ誤りや勘違いが残っていそうな気がするので、コメントよろしくお願いします。

## 最初のバージョンのパッケージを作成する時

最初に、空の作業用リポジトリを用意します。

```shell-session
mkdir pkg-sentencepiece
cd pkg-sentencepiece
git init
git commit --allow-empty -m "first commmit"
```

次に、上流リポジトリをダウンロードします。

```shell-session
git remote add upstream https://github.com/google/sentencepiece.git
git fetch upstream
```

以下のコマンドで、上流リポジトリに相当する `upstream` ブランチを作成しておきます。

```shell-session
git checkout -b upstream upstream/master
```

`upstream` ブランチは、上流のソースコードを追跡するためのブランチなので、`debian/` 配下のパッケージ用ファイルやパッケージ用改変を混入させないように気をつけてください。

パッケージ作成の対象とする `upstream` ブランチにタグを付与しておきます。タグ名の内、`upstream/` は gbp コマンドが使っている固定文字列です。`0.0.0+20170516` の部分は、パッケージメンテナが自由に選べるバージョン文字列です。

```shell-session
git tag upstream/0.0.0+20170516
```

`master` ブランチに戻って、上流のソースコードをマージします。

```shell-session
git checkout master
git merge --allow-unrelated-histories upstream
```

`debian/` 配下の必要なファイルを作成したり、パッケージ用改変を行います。パッケージが正しく作れるようになるまで、各種ファイルを修正しながら以下のコマンドを繰り返し実行します。

```shell-session
gbp buildpackage
```

なお、Debian10 (Buster) 以後の gbp は、パッケージビルド時に cowbuider を自動的に呼び出すようになったので、初回のみ cowbuilder の設定が必要です。設定せずにパッケージをビルドすると、以下のようなエラーになります。

```shell-session
$ gbp buildpackage
（略）
Base directory /var/cache/pbuilder/base-stable-amd64.cow does not exist
```

このエラーメッセージは cowbuilder 用の chroot 環境が存在しないということを意味していますので、以下のコマンドを実行して、クリーンな chroot 環境を作成します。

```shell-session
sudo env DIST=stable ARCH=amd64 git-pbuilder create
```

なお、作成した chroot 環境を更新するには、以下のコマンドを実行します。

```shell-session
sudo env DIST=stable ARCH=amd64 git-pbuilder update
```

筆者の場合は、stable 用のパッケージを作ることがほとんどなので、以下のコマンドでパッケージをビルドしています。

```shell-session
gbp buildpackage -us -uc --git-dist=stable --git-arch=amd64
```

正しく作れるようになったら、以下のコマンドでリリース用パッケージを作成します。このコマンドは、`master` ブランチ上で、リリース用パッケージに対応するタグも付与します。

```shell-session
gbp buildpackage --git-tag
```

パッケージ用ファイルとパッケージ用に改変したソースコードなど一式を、管理リポジトリに格納しておきます。

```shell-session
git remote add origin ssh://git@bitbucket.org/tsuchm/pkg-sentencepiece.git
git push -u origin master
git push -u origin --tags
```

## 上流の更新に追従する時

`upstream` ブランチに移動して、上流リポジトリの更新を取得します。

```shell-session
git checkout upstream
git pull
```

パッケージ対象の上流ソースコードにタグを付与しておきます。

```shell-session
git tag upstream/0.0.0+20170829
```

`master` ブランチに戻り、上流ソースコードの更新をマージします。

```shell-session
git checkout master
git merge upstream
```

必要な改変を行って、パッケージを作成します。

## 作業用ローカルリポジトリを作り直す時

最初に、管理リポジトリを複製します。

```shell-session
git clone ssh://git@bitbucket.org/tsuchm/pkg-sentencepiece.git
```

次に、上流リポジトリを追加します。

```shell-session
cd pkg-sentencepiece
git remote add upstream https://github.com/google/sentencepiece.git
git fetch upsteream
```

上流リポジトリの最新に相当する `upstream` ブランチを作成しておきます。

```shell-session
git checkout -b upstream upstream/master
```

## 上流ソースコードが tarball で公開されている時

本稿の主題とは異なりますが、上流ソースコードが tarball で公開されている場合の git-buildpackage の使い方も、ついでに紹介しておきます。まず、tarball を `upstream` ブランチに import します。

```shell-session
tar xzf crfsuite-0.12.tar.gz
cd crfsuite-0.12
git init
git commit --allow-empty -m "first commit"
git branch upstream
git checkout upstream
git add *
git commit "Import crfsuite-0.12"
git tag "upstream/0.12"
```

後は `master` ブランチに戻って通常のパッケージ作成手順となります。

```shell-session
git checkout master
git merge upstream
dh_make
gbp buildpackage
```

## パッケージが構築できることを自動的に確認する

最近の Git リポジトリサービスでは、継続的インテグレーション（CI）を行うために、git push 後に自動的にコマンドを実行する仕組みがあることが多くなっています。この仕組みを使うと、パッケージがビルドできるかどうか？をチェックすることができます。例として、[BitBucket Pipelines](https://ja.atlassian.com/software/bitbucket/features/pipelines) を使って、パッケージがビルドできるかどうかをチェックする手順を以下に示します。

まず、以下のようにパッケージのビルド手順を記述した `bitbucket-pipelines.yml` というファイルを用意します。ここでは、Docker Hub から unstable な debian コンテナをダウンロードし、ビルドに必要なパッケージをインストールしてから、gbp を実行しています。

```yml:bitbucket-pipelines.yml
image: debian:unstable

pipelines:
  default:
    - step:
        script:
          - apt update
          - env DEBIAN_FRONTEND=noninteractive apt upgrade -y
          - env DEBIAN_FRONTEND=noninteractive apt install -y build-essential git-buildpackage
          - env DEBIAN_FRONTEND=noninteractive apt install -y `perl -ne 's/#.*$//;if(s/^Build-Depends:\s*//){$p=$_;}elsif($p){if(/^\s/){$p.=$_;}else{$p=~s/\(.*?\)//g;$p=~s/[,\s]+/ /g;print $p;undef $p}}' debian/control`
          - gbp buildpackage -us -uc
```

下から2行目の perl を使っているワンライナーは、`debian/control` ファイルから Build-Depends で指定されているパッケージリストを取り出してインストールしています。なお、単純に全てのパッケージをインストールしようとしますので、アーキテクチャ別の条件分岐や複数のパッケージのどちらかが入っていれば良いなどの論理式を使っている場合は、適切に記述を変更する必要があります。

DEBIAN_FRONTEND 環境変数の設定を行わないと、apt 実行時にエラーになります。また、gbp コマンドに `-us` オプションと `-uc` オプションを渡さないと、署名に失敗してエラーになります。

単に `bitbucket-pipelines.yml` というファイルを置いておくと、`debian/` ディレクトリ以外に変更が生じていることになるため、dpkg-source コマンド実行時にエラーになります。そのため、以下のような内容の `debian/source/options` ファイルを作成しておき、`bitbucket-pipelines.yml` の変更を無視するようにしておきます。

```conf:debian/source/options
extend-diff-ignore = "^bitbucket-pipelines\.yml$"
```

適切に設定すると、gbp コマンドまで正常に実行される（つまり、パッケージが正常にビルドできる）と、Pipelines の実行結果に Successful と表示されるようになります。