4.2 KiB · text History d82bd83
#!/usr/bin/env bash
#
# Assemble a .deb from an already-built OTP release.
#
#   packaging/deb/build-deb.sh <release-dir> [output-dir]
#
# `release-dir` is the directory `mix release` produced — the one
# holding bin/, lib/, releases/ and erts-*. In CI that comes out of the
# Dockerfile's `release-export` stage; locally, `MIX_ENV=prod mix
# release` puts it in _build/prod/rel/git_gud.
#
# Everything that varies per build is an environment variable:
#
#   SUITE        Debian suite the package is published for — appended to
#                the version as `+<suite>`, since one artifact published
#                to two suites needs two filenames to sit in the pool
#   VERSION      package version   (default: mix version + ~ci<date>.g<sha>)
#   ARCH         Debian arch       (default: dpkg --print-architecture)
#   COMMIT       provenance        (default: git rev-parse HEAD)
#   MAINTAINER   control field     (default: the git committer identity)
#   HOMEPAGE     control field
#
# This script builds the same package CI does, which is the point: a
# packaging bug should be reproducible without pushing a commit.
set -euo pipefail

release_dir=${1:?usage: build-deb.sh <release-dir> [output-dir]}
out_dir=${2:-dist}
here=$(cd -- "$(dirname -- "$0")" && pwd)
repo_root=$(cd -- "$here/../.." && pwd)

if [ ! -x "$release_dir/bin/git_gud" ]; then
  echo "not an OTP release: $release_dir (no bin/git_gud)" >&2
  exit 1
fi

mix_version=$(sed -n 's/^ *version: "\([^"]*\)".*/\1/p' "$repo_root/mix.exs" | head -1)
commit=${COMMIT:-$(git -C "$repo_root" rev-parse HEAD 2>/dev/null || echo unknown)}
short=${commit:0:12}

# Debian version ordering: `~` sorts BEFORE the plain version, which is
# what a pre-release of 0.1.0 wants — 0.1.0~ci… upgrades cleanly to a
# released 0.1.0 later. The date keeps successive CI builds ordered.
#
# It's the COMMIT's date, not the clock: building the same commit twice
# then produces the same version, and one release packaged for several
# suites shares a base version instead of drifting by however many
# seconds the loop took.
commit_date=$(git -C "$repo_root" show -s --format=%cd --date=format:%Y%m%d%H%M%S "$commit" 2>/dev/null \
  || date -u +%Y%m%d%H%M%S)

version=${VERSION:-${mix_version}~ci${commit_date}.g${short}}

# One build, several suites: `gitgud_0.1.0~ci….g<sha>+trixie_amd64.deb`
# and `…+forky_amd64.deb` are two filenames for the same bytes, which is
# what lets both sit in the pool and each `Packages` index name its own.
if [ -n "${SUITE:-}" ]; then
  version="${version}+${SUITE}"
fi
arch=${ARCH:-$(dpkg --print-architecture)}
maintainer=${MAINTAINER:-$(git -C "$repo_root" config user.name >/dev/null 2>&1 \
  && echo "$(git -C "$repo_root" config user.name) <$(git -C "$repo_root" config user.email)>" \
  || echo "GitGud <noreply@gitgud.local>")}
homepage=${HOMEPAGE:-https://github.com/gmorell/gitgud}

staging=$(mktemp -d)
trap 'rm -rf "$staging"' EXIT

install -d "$staging/opt/gitgud" "$staging/etc/gitgud" \
           "$staging/lib/systemd/system" "$staging/DEBIAN" \
           "$staging/usr/share/doc/gitgud"

cp -a "$release_dir/." "$staging/opt/gitgud/"
install -m 0644 "$here/gitgud.service" "$staging/lib/systemd/system/gitgud.service"
install -m 0640 "$here/gitgud.env" "$staging/etc/gitgud/gitgud.env"

for script in postinst prerm postrm; do
  install -m 0755 "$here/$script" "$staging/DEBIAN/$script"
done

# dpkg asks before overwriting a conffile the operator has edited.
# Without this an upgrade would silently overwrite the database
# password.
echo "/etc/gitgud/gitgud.env" > "$staging/DEBIAN/conffiles"

installed_size=$(du -ks "$staging" | cut -f1)

sed -e "s|@VERSION@|$version|" \
    -e "s|@ARCH@|$arch|" \
    -e "s|@COMMIT@|$commit|" \
    -e "s|@MAINTAINER@|$maintainer|" \
    -e "s|@HOMEPAGE@|$homepage|" \
    -e "s|@INSTALLED_SIZE@|$installed_size|" \
    "$here/control.in" > "$staging/DEBIAN/control"

if [ -f "$repo_root/LICENSE.md" ]; then
  install -m 0644 "$repo_root/LICENSE.md" "$staging/usr/share/doc/gitgud/copyright"
fi

mkdir -p "$out_dir"
package="$out_dir/gitgud_${version}_${arch}.deb"

# --root-owner-group: the staging tree is owned by whoever ran the
# build, and a .deb whose files are owned by uid 1000 is a bug.
dpkg-deb --build --root-owner-group "$staging" "$package" >/dev/null

echo "$package"