Jenkins 构建 deb 包
Jenkins 构建 deb 包。
系统环境:Debian 12。
概览
Jenkins 构建 deb 大致流程,只涉及需要配置的服务或者程序:
Jenkins 构建 deb 概览
APT 缓存
APT 包管理
Jenkins
参考 搭建 Jenkins。
Jenkis 节点
-
如果需要虚拟机,参考 Linux 安装 QEMU-KVM;
-
参考 添加 Jenkins 节点。
配置节点
-
安装基础工具:
sudo apt install curl expect gnupg
-
expect
处理命令交互脚本。
-
-
配置 sbuild 环境,参考:sbuild 构建 deb 包
-
配置 dput,参考:dput 章节
Jenkins 相关脚本
tarball
生成 sbuild 需要的相关文件。
.jenkins/tarball.sh
#!/bin/bash -e
usage() {
printf "Usage: %s -d distribution path\n" "$0"
printf " distribution: bookworm | jammy\n"
exit 1
}
while getopts d: opt
do
case $opt in
d) distribution=$OPTARG;;
?) usage;;
esac
done
shift $((OPTIND - 1))
git_repository=$1
case "$distribution" in
bookworm);;
jammy);;
*) usage;;
esac
if [ "$git_repository" == "" ]; then
git_repository='.'
fi
changelog="$git_repository/debian/changelog"
source=$(dpkg-parsechangelog --file="$changelog" --show-field Source)
# The version number of a package. The format is: [epoch:]upstream_version[-debian_revision]
version=$(dpkg-parsechangelog --file="$changelog" --show-field Version)
if [[ $version == *+* ]]; then
echo "invalid version: $version"
usage
fi
orig_version=$(echo "$version" | sed -E 's/^([0-9]+:)?([^:-]+)(-.*)?$/\2/')
orig_name="${source}_${orig_version}"
git archive --format=tar.gz --prefix="${orig_name}/" --output="${orig_name}.orig.tar.gz" --remote="$git_repository/" HEAD
commit_id=$(git -C source rev-parse HEAD)
echo 'yes' | dch --changelog "$changelog" -v "$version+$distribution" --distribution "$distribution" "Release $commit_id."
dpkg-source -b "$git_repository"
# vim: set tabstop=4 shiftwidth=4 expandtab
-
生成
.orig.tar.gz
包; -
添加
changelog
,主要目的是为相同发行版不同版本设置不同 deb 包名; -
生成
.debian.tar.xz
和.dsc
。
sbuild
构建 debina packages。
.jenkins/sbuild.sh
#!/bin/bash -e
usage() {
printf "Usage: %s -d distribution -a architecture\n" "$0"
printf " distribution: bookworm | jammy\n"
printf " architecture: amd64 | arm64\n"
exit 1
}
while getopts a:d: opt
do
case $opt in
a) architecture=$OPTARG;;
d) distribution=$OPTARG;;
?) usage;;
esac
done
case "$architecture" in
amd64) opt_arch_all='--arch-all';;
arm64) opt_arch_all='--no-arch-all';;
*) usage;;
esac
case "$distribution" in
bookworm);;
jammy);;
*) usage;;
esac
shift $((OPTIND - 1))
sbuild \
"$opt_arch_all" \
--arch="$architecture" \
--dist="$distribution" \
"$@"
# vim: set tabstop=4 shiftwidth=4 expandtab
-
为避免包重复,
amd64
环境使用--arch-all
构建跨平台包,而arm64
平台只构建当前平台相关的包。
添加额外的 apt 源需要修改该文件,如,添加 nodejs 源:
sbuild \
--dist=bookworm \
--extra-repository="deb http://deb.nodesource.com/node_20.x nodistro main" \
--extra-repository-key="./nodesource.asc"
-
extra-repository-key
需要将 GPG 公钥下载到本地后通过文件路径添加。
# amd64
sbuild \
--dist=jammy \
--extra-repository="deb http://mirrors.cloud.tencent.com/ubuntu/ jammy universe" \
--extra-repository="deb http://mirrors.cloud.tencent.com/ubuntu/ jammy-updates universe"
# arm64
sbuild \
--dist=jammy \
--extra-repository="deb http://mirrors.cloud.tencent.com/ubuntu-ports/ jammy universe" \
--extra-repository="deb http://mirrors.cloud.tencent.com/ubuntu-ports/ jammy-updates universe"
dput
上传 deb 相关数据。
.jenkins/dput.exp
#!/usr/bin/expect -f
# dput.exp [host] <package(s).changes>
set password $env(DPUT_CREDS_PSW)
set host [lindex $argv 0]
set changes [lrange $argv 1 end]
set timeout 300
foreach change $changes {
spawn dput -f $host $change
expect {
"Password for *:" {
send "$password\r"
}
timeout {
puts "error: timeout"
exit 1
}
}
expect eof
}
# vim: set tabstop=4 shiftwidth=4 expandtab
-
通过环境变量传入密码。
reprepro
发送 HTTP 请求,添加包到 apt 源。
.jenkins/reprepro.sh
#!/bin/bash
usage() {
printf "Usage: %s -d distribution\n" "$0"
printf " distribution: bookworm | jammy\n"
exit 1
}
if [ "$DPUT_CREDS_USR" = '' ] || [ "$DPUT_CREDS_PSW" = '' ] || [ "$PACKAGES_URL" = '' ]; then
printf "env: DPUT_CREDS_USR DPUT_CREDS_PSW PACKAGES_URL empty.\n"
exit 1
fi
while getopts d: opt
do
case $opt in
d) distribution=$OPTARG;;
?) usage;;
esac
done
case "$distribution" in
bookworm)
os=debian;;
jammy)
os=ubuntu;;
*)
usage;;
esac
curl --fail-with-body \
--location \
--request POST \
--silent \
--user "$DPUT_CREDS_USR:$DPUT_CREDS_PSW" \
"$PACKAGES_URL/incoming/$os/$distribution/cgi-bin/reprepro.cgi"
-
通过环境变量传入用户名、密码、和 packages 域名。
Jenkins
jenkins 构建流程:
.jenkins/Jenkinsfile
pipeline {
agent none
environment {
PACKAGES_URL = 'http://packages.notfound.cn'
DPUT_CREDS = credentials('dput-jenkins')
}
stages {
stage('dist arch') {
parallel {
stage('bookworm amd64') {
agent {
label 'sbuild-bookworm-amd64'
}
steps {
cleanWs()
dir('source') {
git branch: 'sbuild', credentialsId: 'gitee-deploy-key', url: 'git@git.notfound.cn:gitee-pkgs/hello.git'
}
sh './source/.jenkins/tarball.sh -d bookworm source'
sh './source/.jenkins/sbuild.sh -d bookworm -a amd64 *.dsc'
sh './source/.jenkins/dput.exp bookworm *.changes'
sh './source/.jenkins/reprepro.sh -d bookworm'
}
} // bookworm amd64
stage('bookworm arm64') {
agent {
label 'sbuild-bookworm-arm64'
}
steps {
cleanWs()
dir('source') {
git branch: 'sbuild', credentialsId: 'gitee-deploy-key', url: 'git@git.notfound.cn:gitee-pkgs/hello.git'
}
sh './source/.jenkins/tarball.sh -d bookworm source'
sh './source/.jenkins/sbuild.sh -d bookworm -a arm64 *.dsc'
sh './source/.jenkins/dput.exp bookworm *.changes'
sh './source/.jenkins/reprepro.sh -d bookworm'
}
} // bookworm arm64
}
}
}
}