Skip to content

Instantly share code, notes, and snippets.

@smoser
Last active July 17, 2020 15:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save smoser/d3dbf2b8a073675e0700472e12513e0a to your computer and use it in GitHub Desktop.
Save smoser/d3dbf2b8a073675e0700472e12513e0a to your computer and use it in GitHub Desktop.
build-upstream-pkg build a package from upstream source

build-upstream-pkg

Usage: build-upstream-pkg [ options ] <<ARGUMENTS>>

   build-upstream-pkg <upstream-commitish> <packaging-commitish>

   options:
      --version  VERSION    make the upstream version VERSION
                            default is to use git-describe
      --output   DIR        put created files in DIR
    -v | --verbose          increase verbosity

   If you have the following remotes:
     upstream with 'master' branch
     pkg with 'ubuntu/devel'
   then you'd do:

   $ build-upstream-pkg upstream/master ubuntu/devel

This is especially useful with git-ubuntu as a 'pkg' remote. See below for an example of that.

Example:

  • Get ubuntu source

    $ git ubuntu clone util-linux
    $ cd util-linux
    
  • Add upstream remote

     $ git remote add upstream https://github.com/karelzak/util-linux.git
     $ git fetch upstream
    
  • build a package of upstream/master with pkg/ubuntu/devel

     $ build-upstream-pkg -v upstream/master pkg/ubuntu/devel
     $ build-upstream-pkg -vv --output=../pkg-build upstream/master pkg/ubuntu/devel
    

TODO

  • Should add '--no-patches' or something to remove debian/patches.
#!/bin/bash
# https://gist.github.com/smoser/d3dbf2b8a073675e0700472e12513e0a
VERBOSITY=0
TEMP_D=""
error() { echo "$@" 1>&2; }
fail() { local r=$?; [ $r -eq 0 ] && r=1; failrc "$r" "$@"; }
failrc() { local r=$1; shift; [ $# -eq 0 ] || error "$@"; exit $r; }
Usage() {
cat <<EOF
Usage: ${0##*/} [ options ] <<ARGUMENTS>>
build-upstream-pkg <upstream-commitish> <packaging-commitish>
options:
--version VERSION make the upstream version VERSION
default is to use git-describe
--output DIR put created files in DIR
-v | --verbose increase verbosity
If you have the following remotes:
upstream with 'master' branch
pkg with 'ubuntu/devel'
then you'd do:
$ build-upstream-pkg upstream/master ubuntu/devel
EOF
}
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; return 1; }
cleanup(){
[ ! -d "$TEMP_D" ] || rm -Rf "$TEMP_D";
git worktree prune
}
debug() {
local level=${1}; shift;
[ "${level}" -gt "${VERBOSITY}" ] && return
error "${@}"
}
get_version() {
# get version if not provided
local ref=$1 match="" out="" version=""
for match in "" "[0-9]*" "v[0-9]*"; do
out=$(git describe --abbrev=8 ${match:+"--match=$match"} $ref 2>&1) &&
[ -n "$out" ] && break
out=""
done
if [ -z "$out" ]; then
# try with --tags
for match in "" "[0-9]*" "v[0-9]*"; do
out=$(git describe --abbrev=8 --tags ${match:+"--match=$match"} $ref 2>&1) &&
[ -n "$out" ] && break
done
fi
case "$out" in
"") fail "failed to git-describe anything";;
[a-z][a-z]*-[0-9.]*) version=${out#*-};; # lxc-4.0.0-123-gxxx
[a-z]*[0-9.]*)
version=$out
while [ "${version}" != "${version#[a-z]}" ]; do
version=${version#[a-z]}
done
;;
[0-9]*-[0-9]*-g[a-f0-9]*) version=$out;;
esac
debug 1 "version now $version from $out"
case "$version" in
[0-9]*-[0-9]*-g[a-f0-9]*) :;;
*) fail "git describe got us '$out'" \
"not sure how to turn that into X.Y.Z-<number>-<gHASH>"
esac
_RET=$version
}
main() {
local short_opts="ho:vV:"
local long_opts="help,output:,version:,verbose"
local getopt_out=""
getopt_out=$(getopt --name "${0##*/}" \
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
eval set -- "${getopt_out}" ||
{ bad_Usage; return; }
local cur="" next="" out_d="../out" version=""
while [ $# -ne 0 ]; do
cur="$1"; next="$2";
case "$cur" in
-h|--help) Usage ; exit 0;;
-o|--output) out_d=$next; shift;;
-V|--version) version=$next; shift;;
-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
--) shift; break;;
esac
shift;
done
[ $# -ge 2 ] || { bad_Usage "got $# args, expected 2"; return; }
local up_cish="$1" pkg_cish="$2"
shift 2
local build_cmd=""
if [ $# -eq 0 ]; then
build_cmd=( debuild -d -S -nc )
else
build_cmd=( "$@" )
fi
# check both commitish
local up_hash="" pkg_hash=""
up_hash=$(git rev-parse "$up_cish") ||
fail "failed to get hash for $up_cish"
pkg_hash=$(git rev-parse "$pkg_cish") ||
fail "failed to get hash for $pkg_cish"
local user="" email=""
user=$(git config user.name) &&
email=$(git config user.email) ||
fail "failed to get user or email"
local start_d="$PWD" out_d_in="$out_d"
[ -d "$out_d" ] || mkdir -p "$out_d" ||
fail "failed to create $out_d"
out_d=$(cd "$out_d" && pwd) || {
fail "failed to get full path to $out_d_in"
}
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") ||
fail "failed to make tempdir"
trap cleanup EXIT
local wtd="${TEMP_D}/wtd"
git worktree add --force "$wtd" "$pkg_hash" ||
fail "failed worktree add $wtd $ref"
cd "$wtd"
local pkg_ver="0upstream1" pkg_name="" old_full_ver="" old_pkg_ver=""
# get the pkg name from pkg commit
git checkout "$pkg_hash" ||
fail "failed to checkout $pkg_hash"
pkg_name=$(dpkg-parsechangelog --show-field Source) ||
fail "failed to read Source from changelog"
old_full_ver=$(dpkg-parsechangelog --show-field Version) ||
fail "failed reading package version"
local epoch=""
case "$old_full_ver" in
[0-9]:*) epoch=${old_full_ver%%:*};;
esac
if [ -f debian/source/format ] &&
out=$(grep "(native)" debian/source/format); then
fail "debian/source/format says this is native package"
fi
git checkout "$up_hash" ||
fail "failed to checkout $up_hash"
if [ -z "$version" ]; then
get_version && version="$_RET" ||
fail "failed to get version from $up_hash"
fi
debug 1 "pkg_name is ${pkg_name} upstream_version=$version"
local orig_tarball="${TEMP_D}/${pkg_name}_${version}.orig.tar.gz"
# make an upstream tarball that does not contain debian/
tar -czf "${orig_tarball}" \
"--transform=s,^[.]/,${pkg_name}-${version}/," \
"--exclude=.git" "--exclude=debian" . ||
fail "failed to create upstream tarball ${pkg_name}_${version}"
# checkout pkg commitish
git checkout "$pkg_hash" ||
fail "failed to checkout $pkg_hash"
# git rm anything not debian/
local f="" rms=""
rms=( )
for f in * .*; do
case "$f" in
.git|.|..|debian) continue;;
esac
rms[${#rms[@]}]="$f"
done
local cur_hash="$pkg_hash"
if [ "${#rms[@]}" -eq 0 ]; then
debug 1 "pkg tree was debian-only"
else
git rm --quiet -r "${rms[@]}" || fail "failed to git remove files"
git commit --quiet -a -m "remove non-debian files from pkg branch" ||
fail "failed to commit cleanup"
cur_hash=$(git rev-parse HEAD)
fi
# add debian/changelog entry
local suite="UNRELEASED"
debug 1 "${pkg_name} (${epoch:+${epoch}:}$version-${pkg_ver}) $suite; urgency=medium"
cat >debian/changelog <<EOF
${pkg_name} (${epoch:+${epoch}:}$version-${pkg_ver}) $suite; urgency=medium
* new upstream version $version.
-- $user <$email> $(date -R)
EOF
[ $? -eq 0 ] || fail "failed to write debian/changelog"
git show "$cur_hash:debian/changelog" >> debian/changelog ||
fail "failed to append to debian changelog"
# commit
git commit -m "update changelog" debian/changelog ||
fail "failed to commit changelog update"
cur_hash=$(git rev-parse HEAD)
# I think its a bug in dpkt-source, but it complains about
# deleted files, so make it not have deleted files.
tar --strip-components=1 -xzf "$orig_tarball" ||
fail "failed to extrat tarball"
# invoke debuild
debug 0 "building $cur_hash with: ${build_cmd[*]}"
"${build_cmd[@]}" ||
fail "FAILED: ${build_cmd[*]}"
debug 0 "built $pkg_name at $version. source tree in $cur_hash"
debug 0 "wrote:"
for f in "${TEMP_D}/"*; do
[ -f "$f" ] || continue
cp "$f" "$out_d/" && echo " ${out_d_in}/${f##*/}" ||
fail "failed to copy $f to $out_d"
done
cd "$start_d"
return 0
}
main "$@"
# vi: ts=4 expandtab
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment