從頭打包Debian套件流程

前置作業

了解套件狀態

可以到WNPP(Work-Needing and Prospective Packages)的列表查看套件的狀態是什麼。

也可以使用wnpp-check這個套件來查看

$ wnpp-check [套件名]

認領套件

認領套件的方法是把套件狀態改為ITP(Intent to Package),可以使用指令的方式reportbug wnpp,也可以使用發信的方式,下面會介紹寄信的方式。

認領套件有兩種情況:

套件並不存在WNPP上面

  • 信件主旨格式:ITP: [套件名] - 簡述

    此處有誤,"-"應該要有兩個,如下

    ITP: [套件名] -- 簡述
    

    Kaiden Yu2019, 10, 22 02:00 AM

  • 收件者:submit@bugs.debian.org
  • 內容格式:要符合規範,下面給個範例
# 這是註解:owner填上自己代表要打包
Package: wnpp
Severity: wishlist
Owner: Chen-Ying Kuo <evshary@gmail.com>

* Package name    : tzdiff
  Version         : 0.9
  Upstream Author : minmin <belgianbeer@aj.admwt.jp>
* URL             : https://github.com/belgianbeer/tzdiff
* License         : BSD-2-Clause
  Programming Lang: shell script
  Description     : Show Timezone differences with local time in CLI (shell script).

要認領既有套件

如果要認領既有套件其實很簡單,首先先找到你要認領套件的WNPP頁面,確定bug number。

  • 信件主旨:沒有限制,可以一樣是ITP: [套件名] - 簡述

    此處有誤,"-"應該要有兩個,如下

    ITP: [套件名] -- 簡述
    

    Kaiden Yu2019, 10, 22 02:00 AM

  • 收件者:[bug number]@bugs.debian.org
  • 內容格式:主要是要使用Control指令改變title和owner,可參考下面範例
# 這是註解:-1的地方代表延續bug number
# 這是註解:retitle最主要是改變狀態,變成ITP
# 這是註解:owner填上自己代表要打包
Control: retitle -1 ITP: checksec -- Bash script to test executable properties
Control: owner -1  Chen-Ying Kuo <evshary@gmail.com>

# 這是註解:可以表明自己要打包這個套件
I want to package this.

安裝打包的必要套件

$ sudo apt-get install build-essential fakeroot devscripts git git-buildpackage debmake lintian

然而通常如果最後是要打包到SID版本(unstable Debian),可能結果會有些版本上的問題,所以建議可以安裝SID版本的套件。

  1. 修改sources.list
$ vim /etc/apt/sources.list
  1. 加上如下這行
$ deb http://ftp.de.debian.org/debian sid main
  1. 執行如下指令來安裝SID版本
$ sudo apt-get update
# 確認可用版本
$ sudo apt-cache policy debhelper
$ sudo apt-cache policy debmake
$ sudo apt-cache policy lintian
# 更新版本
$ sudo apt-get -t sid install debhelper debmake lintian

開始打包

創造package repository

Debian的套件都是放在一個叫做Salsa的GitLab上面,如果沒有帳號要申請一個,有的話就登入。

進入後就創造repo,名稱跟套件名一樣即可。如果DD已經有創造了,那你可以fork出來。

產生package repository

  1. 先clone剛剛在Salsa創立的repo
$ git clone [剛剛創立的repo]
  1. 產生debian/master的branch
$ cd [剛剛clone的repo]
$ git checkout -b debian/master
  1. 先在上層目錄(與套件目錄平行)抓下upstream的tar檔
    • 通常我們選擇upstream最新的release版本,名稱應該會是[版本].tar.gz
  2. 利用tar檔產生初始branch,然後gbp會問你package的名稱
$ gbp import-orig --pristine-tar ../[版本].tar.gz --debian-branch=debian/master -u [版本]
  1. 完成後就是初步的套件了
    • repo裡面會有三個branch(debian/master, pristine-tar, upstream)
    • 外層資料夾會有[package_name]_[version].orig.tar.gz的檔案
    • 這三個branch分別代表的意義如下
      1. upstream: 原作者的source
      2. pristine-tar: 利用原作者的source產生出來的tar檔
      3. debian/master: 除了upstream的source外,還包括debian的資料夾,這也是我們主要修改上傳資訊的地方

產生Debian套件所需內容

接著我們會執行debmake,產生debian的資料夾,裡面的相關檔案就是打包套件所需的資訊

# 把剛剛創的資料夾加上版號
$ mv [repo] [repo]_[版號]
$ cd [repo]_[版號]
$ debmake

此處有誤,應將 "_" 改為 "-" ,如下

mv [repo] [repo]-[版號]
cd [repo]-[版號]

Kaiden Yu2019, 06, 22 11:00 PM

修改debian相關檔案

修改的規則可參考Debian Policy Manual,下面記錄的內容僅供參考,因為policy會變,還是要以manual為主。

以下是需要修改的檔案。

debian/copyright

copyright格式要小心,必須按照原作者的copyright,通常demake會幫忙判斷版權,不過如果有錯誤還是得自己修正。

如果有原作者自己定義的License的話,可以參考zsh的改法

幾個可參考的連結

debian/control

  1. Section選擇應有分類,選misc,參考官網分類
  2. 其餘email名字也要修改,並且放上官網和敘述
  3. 如果有套件相依性的話,需要在Depends上面加上去,然後跑debuild和pubuilder的時候應該就會自己裝了。
    • 不過有些套件不用寫上去,除了沒用到的以外還有essential,可用這個指令dpkg-query -Wf '${Package;-40}${Essential}\n' | grep yes確定哪些是essential,可參考Finding all “essential” packages with apt
    • 如果不清楚相依套件或版本的話,可能就需要詢問上游了。不過如果沒有特別需求,版本一般可以不用填。
    • 有些shell裡面會寫有目前使用的指令,如果要反查是什麼套件的話可用如下指令(參考自Command to find the source package of a binary?)
dpkg -S `which COMMANDHERE`
  1. 記得要補上VCS

debian/changelog

這邊會用到dch,可使用dch -i來修改changelog檔案,基本上一次上傳只進一版(第一次的上傳所有修改都只會是x.x.x-1)

有幾個地方要修改一下

  • unknown要改成unstable
  • bug number要改為自己的ITP number
  • 修改自己的名字、email

debian/rules

如果原始程式碼的Makefile並不是我們要的,例如說會install檔案到/usr/local/bin(我們沒有權限可以做這件事),那應該就必須要修改這個檔案。

記住要把debmake產生的註解拿掉。

debian/patch

  • 若有更動原始碼的需求,使用gbp打patch,記得加上no-patch-num,來去除patch number
$ gbp clone --pristine-tar [Salsa的git位置] delay_package_gbm

$ gbp pq import --no-patch-num

# 此時會切換到patch branch,開始做必要的更動並commit

$ gbp pq export --no-patch-num

debian/[pkg_name].manpages

  • 若無manpage,請在github上開issue請原作者加入,或參考別人的package加入後回饋給上游
  • [pkg_name].manpages內新增manpage的位置,例如[package].1
  • 如果想要看manpage長什麼樣子,可用nroff -man [package].1

debian/tests

實際上怎麼修改可以參考4. autopkgtest: Automatic testing for packagesTutorial: Functional Testing of Debian Packages

  1. 首先先在debian底下新增tests資料夾
mkdir tests
  1. tests底下增加control這個檔案,內容如下
    • Tests右邊的build代表我們要測試的script file
    • Depends右邊的@代表相依檔案只有自己,如果有其他就加在這行
    • 可參考別人的範例
Tests: build
Depends: @
  1. 接著新增build這個檔案
#!/bin/sh

set -e
echo "run start"
# 要做的測試
echo "run: OK"

debian/watch

  1. 修改watch的檔案,可參考官網介紹這邊的範例
version=4
opts=\
repacksuffix=+ds,\
repack,compression=xz,\
dversionmangle=s/\+(debian|dfsg|ds|deb)(\.?\d+)?$//,\
filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%<project>-$1.tar.gz% \
 https://github.com/jarun/Buku/releases \
 (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate
  1. 可用uscan測試
# 測試方式
uscan --no-download --verbose
# 如果要debug
uscan --no-download --verbose --debug

debian/upstream

  1. 在debian資料夾下新增upstream資料夾
mkdir upstream
  1. 在upstream資料夾中新增metadata
cd upstream
vi metadata
  1. metadata中增加相關資訊,可參考別人的範例
Bug-Database: https://github.com/skymizer/SkyPat/issues
Bug-Submit: https://github.com/skymizer/SkyPat/issues/new
Name: skypat
Repository: https://github.com/skymizer/SkyPat.git
Repository-Browse: https://github.com/skymizer/SkyPat

debian/install

  1. 在debian資料夾下新增install這個檔案
vi install
  1. 增加要搬移的位置,通常為usr/bin
[執行檔] usr/bin

debian/[package_name].docs

要記得創立debian/[package_name].docs,主要是要把README.md包入binary package內

最後修改完相關debian檔案後,git log要整理,第一次上套件的log只留一筆,內容可以寫"Initialize packaging"。

執行debuild

改完後就可以開始用debuild了,不過我們不是DD,所以可以跳過sign key的動作。

修改~/.devscripts/etc/devscripts.conf為如下

DEBUILD_LINTIAN=yes

# -E include experimental tags
DEBUILD_LINTIAN_OPTS="-i -EvIL +pedantic --show-overrides"

#-uc -us代表不要sign key
DEBUILD_DPKG_BUILDPACKAGE_OPTS="-uc -us --changes-option=-sa"

# DEBSIGN_KEYID可填可不填
DEBSIGN_KEYID="FIXME - YOUR KEYID"

DEB_BUILD_OPTIONS="parallel=10"

執行完成後會在自己資料夾的上一層產生許多檔案,包括[套件名稱].dsc,這就是所謂的Debian source control files,之後如果要跑pubuilder也會需要用到。

$ debuild

清除debuild產生的檔案

因為debuild會產生許多檔案,這些都是不用上git的。如果想要清除的話,可以用如下指令

debuild -- clean

解決Lintian的問題

跑完後可能會出現Lintian相關的錯誤,相關錯誤的解法可參考Solutions to some lintian warnings

驗證修改

為了確認打包結果是否可以在一個乾淨的環境上執行,以及是否能通用在32, 64bit的系統上,所以要經過下面的測試。

使用pbuilder建立sid環境

使用pubuilder最主要的原因是debuild是在我們當前環境,可能不夠乾淨,pbuilder可以模擬一個乾淨的環境供我們測試是否有相依性或其他問題。

  1. 安裝pbuilder後,首先設定/etc/pbuilderrc
# this is your configuration file for pbuilder.
# the file in /usr/share/pbuilder/pbuilderrc is the default template.
# /etc/pbuilderrc is the one meant for overwriting defaults in
# the default template
#
# read pbuilderrc.5 document for notes on specific options.
MIRRORSITE=http://deb.debian.org/debian
# the hook dir may already be set/populated!
HOOKDIR="/var/cache/pbuilder/hooks/"
if [ -n "$DEPS" ] ; then
        export DEPSBASE=/var/cache/pbuilder/deps
        BINDMOUNTS=$DEPSBASE
fi
  1. pbuilder執行時會執行hook資料夾下設定的hook,通常我們會加入一個Lintian hook來做檢查。在/var/cache/pbuilder/hooks/B90lintian中輸入下面內容,相關example可在/usr/share/doc/pbuilder/examples找到。
    P.S. 每個不同功能的hook都有制式的名字,不能隨意輸入
#!/bin/dash
set -e

install_packages() {
        apt-get -y --allow-downgrades install "$@"
}

install_packages lintian

echo "+++ lintian output +++"
su -c "lintian -i -EvIL +pedantic --show-overrides /tmp/buildd/*.changes" - pbuilder
# use this version if you don't want lintian to fail the build
#su -c "lintian -i -I --show-overrides /tmp/buildd/*.changes; :" - pbuilder
echo "+++ end of lintian output +++"
  1. 記得把/var/cache/pbuilder/hooks/B90lintian執行權限打開
chmod a+x /var/cache/pbuilder/hooks/B90lintian
  1. 由於我們要在SID的環境執行(也就是不穩定的Debian版本),可參考這裡的設定,先創立~/.pbuilderrc(通常應該是/root/.pbuilderrc),然後貼上如下內容
# Codenames for Debian suites according to their alias. Update these when
# needed.
UNSTABLE_CODENAME="sid"
TESTING_CODENAME="buster"
STABLE_CODENAME="stretch"
STABLE_BACKPORTS_SUITE="$STABLE_CODENAME-backports"

# List of Debian suites.
DEBIAN_SUITES=($UNSTABLE_CODENAME $TESTING_CODENAME $STABLE_CODENAME $STABLE_BACKPORTS_SUITE
    "experimental" "unstable" "testing" "stable")

# List of Ubuntu suites. Update these when needed.
UBUNTU_SUITES=("xenial" "wily" "vivid" "utopic" "trusty")

# Mirrors to use. Update these to your preferred mirror.
DEBIAN_MIRROR="deb.debian.org"
UBUNTU_MIRROR="mirrors.kernel.org"

# Optionally use the changelog of a package to determine the suite to use if
# none set.
if [ -z "${DIST}" ] && [ -r "debian/changelog" ]; then
    DIST=$(dpkg-parsechangelog --show-field=Distribution)
fi

# Optionally set a default distribution if none is used. Note that you can set
# your own default (i.e. ${DIST:="unstable"}).
: ${DIST:="$(lsb_release --short --codename)"}

# Optionally change Debian codenames in $DIST to their aliases.
case "$DIST" in
    $UNSTABLE_CODENAME)
        DIST="unstable"
        ;;
    $TESTING_CODENAME)
        DIST="testing"
        ;;
    $STABLE_CODENAME)
        DIST="stable"
        ;;
esac

# Optionally set the architecture to the host architecture if none set. Note
# that you can set your own default (i.e. ${ARCH:="i386"}).
: ${ARCH:="$(dpkg --print-architecture)"}

NAME="$DIST"
if [ -n "${ARCH}" ]; then
    NAME="$NAME-$ARCH"
    DEBOOTSTRAPOPTS=("--arch" "$ARCH" "${DEBOOTSTRAPOPTS[@]}")
fi
BASETGZ="/var/cache/pbuilder/$NAME-base.tgz"
DISTRIBUTION="$DIST"
BUILDRESULT="/var/cache/pbuilder/$NAME/result/"
APTCACHE="/var/cache/pbuilder/$NAME/aptcache/"
BUILDPLACE="/var/cache/pbuilder/build/"
HOOKDIR="/var/cache/pbuilder/hooks/"

if $(echo ${DEBIAN_SUITES[@]} | grep -q $DIST); then
    # Debian configuration
    MIRRORSITE="http://$DEBIAN_MIRROR/debian/"
    COMPONENTS="main contrib non-free"
    if $(echo "$STABLE_CODENAME stable" | grep -q $DIST); then
        OTHERMIRROR="$OTHERMIRROR | deb $MIRRORSITE $STABLE_BACKPORTS_SUITE $COMPONENTS"
    fi
elif $(echo ${UBUNTU_SUITES[@]} | grep -q $DIST); then
    # Ubuntu configuration
    MIRRORSITE="http://$UBUNTU_MIRROR/ubuntu/"
    COMPONENTS="main restricted universe multiverse"
else
    echo "Unknown distribution: $DIST"
    exit 1
fi
  1. 建立pbuilder環境
$ sudo OS=debian DIST=sid ARCH=i386 pbuilder create
$ sudo OS=debian DIST=sid ARCH=amd64 pbuilder create
  1. 執行pbuilder
# 注意若changelog版本號有變,xxx.dsc的檔案名也要更動
$ sudo OS=debian DIST=sid ARCH=i386 pbuilder build [剛剛產生的的dsc檔].dsc
$ sudo OS=debian DIST=sid ARCH=amd64 pbuilder build [剛剛產生的的dsc檔].dsc

# 若有錯誤,修正後先跑debuild然後再重複上面pbuilder build的步驟
$ debuild
  1. 若有更動/root/.pbuilderrc,記得更新pbuilder環境
$ sudo OS=debian DIST=sid ARCH=i386 pbuilder update --override-config --configfile ~/.pbuilderrc
$ sudo OS=debian DIST=sid ARCH=amd64 pbuilder update --override-config --configfile ~/.pbuilderrc

一切完成後

  1. commit並推上自己在Salsa的repository
  2. 請認識的Debian Developer來clone repository,或是發MR到已經存在的repository,再請他們幫忙打包上傳

注意事項

  • 在製作deb前確認原始碼與環境(可重製環境)乾淨
  • 記得跑lintian檢查製作的.dsc檔案是否正確
  • 當修正並非特別為Debian或Linux時,記得將patch送回上游進行反饋

更新套件

要更新套件的話,我們需要更新三個branch並且發MR出去,三個branch分別為debian/master, pristine-tar和upstream。

不過要特別注意自己fork的repository和目前在Salsa上的repository有沒有同步,如果沒有要先做好同步才行。

從原作者的repository更新

如果我們有設定 watch 的話,可以直接透過 gbp 指令來更新,可參考 New upstream release

  • gbp pq 相關操作可參考這邊
  • gbp import-orig 相關操作可參考這邊
  • 在執行前確認自己 local 端都已經有相對應的 branch,包括 debian/master, pristine-tar, upstream
# 從 upstream 更新程式碼到 pristine-tar 和 debian branch,並且指定 local 的 debian branch 為 debian/master
$ gbp import-orig --pristine-tar --uscan --debian-branch=debian/master

# 如果跟 upstream 有衝突的話,可以用 --merge-mode=replace 強制使用 upstream commit
$ gbp import-orig --pristine-tar --uscan --debian-branch=debian/master --merge-mode=replace

完成後就可以切回 debian/master branch 並做後續的修改了。

更新changelog

一樣可以用gbp更新changelog(記得要把UNRELEASED改為unstable)

gbp dch --debian-branch=debian/master

要特別注意的是,changelog上面要紀錄你對debian file的所有修改(不是upstream的部份)。

如果不是upstream更新,而是debian相關的進版

  • 如果上層目錄沒有tar檔的話,先用pristine-tar來取得tar檔
# 目前有哪些tar檔
pristine-tar list
# 選擇最新的tar檔
pristine-tar checkout <tar>
# 把tar檔移到上層
mv <tar> ..
  • 更新changelog
dch -i

確認debian/control和debian/compat

更新套件的時候要注意control裡面的資訊有沒有過時,若沒有更新,在跑pbuilder應該就會跳出警告了。

  • Build-Depends:這個跟debhelper版本有關,記得更新完也要更新compat
  • Standards-Version:這個跟debmake有關

如果發現自己local端的debhelper和debmake過時,那可以用前面安裝SID的方式更新。

發Merge Request

當套件都更新完後,一樣要用debuild和pbuilder再跑一次,確認沒問題,然後就可以把debian/master、pristine-tar和upstream都push上去Salsa。

記得三個branch都要發MR給DD。

參考資源

Select a repo