Skip to content

Commit

Permalink
feat(build-debian): Use docker to manually run build and prepare step…
Browse files Browse the repository at this point in the history
…s and add tests (#28)

As underlined in
ubuntu/authd#130 (comment) using
`jtdor/build-deb-action` for building debian packages has some problems
that are yet not clear, and it also does not seem to properly support
building non-native packages.

As per this, given that the code needed for handling the build inside a
simpler docker container is quite small, I've moved the build to manual
handling which allows us some more control over the steps at the cost of
being a bit more verbose.

As bonus:
- We now only build as user (not the sources, since there's no much
point)
- All the network traffic is totally blocked for the builder user (not
just HTTPS)
- ~~`DEB_BUILD_OPTIONS` can be customized for special builds (e.g.
`nocheck`)~~
 - Version uses actual distro version

I've added some commits from #22, but not included the lintian build yet
(that I've ready
[here](3v1n0@1b72205))
not to make this PR harder to check, but also because I think those
could instead be part of another action to allow more parallelization
when used.

By doing this change, not only both test cases pass, but also authd is
correctly built: ubuntu/authd#130 (e.g.
https://github.com/ubuntu/authd/actions/runs/8044158864)

UDENG-2439
  • Loading branch information
3v1n0 authored Nov 13, 2024
2 parents 5b6eda1 + 542f160 commit 6e15fc5
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 33 deletions.
89 changes: 89 additions & 0 deletions .github/workflows/test-build-deb.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: Test build debian package

on:
push:
branches:
- main
paths:
- gh-actions/common/build-debian/**
- .github/workflows/test-build-deb*
pull_request:

env:
DEBIAN_FRONTEND: noninteractive
DEBCONF_NONINTERACTIVE_SEEN: true

jobs:
build_native_deb:
name: Test build native debian package
runs-on: ubuntu-latest
outputs:
pkg-name: ${{ env.PKG_NAME }}
pkg-version: ${{ env.PKG_VERSION }}

steps:
- name: Install dependencies
run: |
sudo apt update
sudo apt install ubuntu-dev-tools
- name: Get and prepare package source
run: |
set -eu
echo "::group::Get source"
pull-lp-source --download-only hello
dpkg-source -x hello*.dsc hello-src
rm -rf hello_*
mv -v hello-src/* .
echo "::endgroup::"
echo "::group::Mark package as a native package"
echo "3.0 (native)" > debian/source/format
dch -v$(dpkg-parsechangelog -S Version | cut -f1 -d-).1 \
"Mark as native package"
echo "::endgroup::"
- name: Checkout code
uses: actions/checkout@v4
with:
path: .source

- name: Build package
uses: ./.source/gh-actions/common/build-debian
with:
docker-image: ubuntu:devel

build_source_deb:
name: Test build quilt debian package
runs-on: ubuntu-latest
outputs:
pkg-name: ${{ env.PKG_NAME }}
pkg-version: ${{ env.PKG_VERSION }}
source-pkg: ${{ steps.build-debian-source-package-upload-step.outputs.artifact-url }}
binaries: ${{ steps.build-debian-binary-packages-upload-step.outputs.artifact-url }}

steps:
- name: Install dependencies
run: |
sudo apt update
sudo apt install ubuntu-dev-tools
- name: Get package source
run: |
set -eu
pull-lp-source --download-only hello
dpkg-source -x hello*.dsc hello-src
- name: Checkout code
uses: actions/checkout@v4
with:
path: .source

- name: Build package
uses: ./.source/gh-actions/common/build-debian
with:
source-dir: ./hello-src
docker-image: ubuntu:devel
extra-source-build-deps: ''
204 changes: 171 additions & 33 deletions gh-actions/common/build-debian/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ inputs:
token:
required: false
description: If provided, used for git authentication in the source build
extra-source-build-deps:
description: A list of extra build dependencies required during source build.
required: false
# FIXME: this should default to '', but we don't want to break job depending on us for now
default: 'ca-certificates git'


# The process:
# 1. We build the source package in a docker container with ca-certificates installed and thus,
# a useful internet connection.
# 1. We build the source package in a docker container. If ca-certificates are
# installed via extra-source-build-deps we can have a useful internet connection.
# 2. We the extract the source package.
# 3. We build the .deb from the source package, in a container without ca-certificates (unless it
# is added as a build dependency), hence without a useful internet connection.
# 3. We build the .deb from the source package, in a container without internet
# kind of internet connection.
#
# To help with debugging, here are the processes and the directories they takes place in:
#
Expand All @@ -44,71 +49,204 @@ runs:
- name: Set up source package build
shell: bash
run: |
echo "::group::Create local version with commit and docker container"
set -eu
echo "::group::Install devscripts"
DEBIAN_FRONTEND=noninteractive sudo apt update
DEBIAN_FRONTEND=noninteractive sudo apt install -y devscripts
echo "::endgroup::"
echo "::group::Append commit SHA to local version"
cd '${{ inputs.source-dir }}'
sanitized_docker=$( echo "${{ inputs.docker-image }}" | sed 's/://' )
debchange --local "+${sanitized_docker}+${{ github.sha }}" "Github build. Job id: ${{ github.run_id }}. Attempt: ${{ github.run_number }}."
# Short commit to avoid "package-has-long-file-name"
echo VERSION_REF=$(date +'%y%m%d').${{ github.run_number }}.$(echo ${{ github.sha }} | cut -c1-8) >> $GITHUB_ENV
echo DEBFULLNAME="GitHub actions runner" >> $GITHUB_ENV
echo DEBEMAIL="[email protected]" >> $GITHUB_ENV
if git status --porcelain &>/dev/null; then
echo DEBFULLNAME="$(git log -1 --format='%an' HEAD) - GH Action" >> $GITHUB_ENV
echo DEBEMAIL="$(git log -1 --format='%ae' HEAD)" >> $GITHUB_ENV
fi
echo "::endgroup::"
- name: Prepare source package
uses: kohlerdominik/[email protected]
with:
image: ${{ inputs.docker-image }}
environment: |
DEBIAN_FRONTEND=noninteractive
DEBFULLNAME=${{ env.DEBFULLNAME }}
DEBEMAIL=${{ env.DEBEMAIL }}
volumes: ${{ github.workspace }}:${{ github.workspace }}
workdir: ${{ github.workspace }}/${{ inputs.source-dir }}
shell: bash
run: |
echo "::group::Update builder instance and install dependencies"
set -eu
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/90aptyes
apt update
apt install devscripts lsb-release
echo "::endgroup::"
echo "::group::Update debian package changelog"
dch --local "+git${{ env.VERSION_REF }}~$(lsb_release -r -s)." \
"Github build. Run id: ${{ github.run_id }}. Run number: ${{ github.run_number }}." \
--distribution "$(lsb_release -c -s)"
dpkg-parsechangelog
echo "::endgroup::"
- name: Parse package source info
shell: bash
run: |
echo "::group::Parsing name and version"
set -eu
cd '${{ inputs.source-dir }}'
echo PKG_NAME="$( dpkg-parsechangelog --show-field source )" >> $GITHUB_ENV
echo PKG_VERSION="$( dpkg-parsechangelog --show-field version )" >> $GITHUB_ENV
cd -
echo "::endgroup::"
echo "::group::Prepare source build"
echo SOURCE_OUTPUT_DIR="$( mktemp --directory --tmpdir=. )" >> $GITHUB_ENV
echo SOURCE_OUTPUT_DIR="$( mktemp --directory --tmpdir="${PWD}" )" >> $GITHUB_ENV
echo "::endgroup::"
- name: Build source package
uses: jtdor/build-deb-action@v1
uses: kohlerdominik/docker-run-action@v2.0.0
with:
source-dir: ${{ inputs.source-dir }}
artifacts-dir: ${{ env.SOURCE_OUTPUT_DIR }}
docker-image: ${{ inputs.docker-image }}
buildpackage-opts: --build=source
extra-build-deps: ca-certificates git
before-build-hook: |
image: ${{ inputs.docker-image }}
environment: |
DEBIAN_FRONTEND=noninteractive
volumes: ${{ github.workspace }}:${{ github.workspace }}
workdir: ${{ github.workspace }}/${{ inputs.source-dir }}
shell: bash
run: |
echo "::group::Update builder instance"
set -eu
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/90aptyes
apt update
apt dist-upgrade
echo "::endgroup::"
echo "::group::Install build dependencies"
apt build-dep .
if [ -n "${{ inputs.extra-source-build-deps }}" ]; then
# Install extra packages for build-deps, to allow downloading vendored sources
deps=(${{ inputs.extra-source-build-deps }})
apt install ${deps[@]}
fi
echo "::endgroup::"
if command -v git &> /dev/null; then
git config --system --add safe.directory "${{ github.workspace }}"
fi
GITHUB_TOKEN="${{ inputs.token }}"
if [ -n "${GITHUB_TOKEN}" ]; then
git config --system url."https://api:${GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"
fi
echo "::group::Build debian source package"
dpkg-buildpackage -D -S --sanitize-env
echo "::endgroup::"
mv -v ../"${{ env.PKG_NAME }}_"* "${{ env.SOURCE_OUTPUT_DIR }}"
- name: Uploading source packages
uses: actions/upload-artifact@v4
id: build-debian-source-package-upload-step
with:
name: ${{ env.PKG_NAME }}_${{ env.PKG_VERSION }}-debian-source
path: ${{ env.SOURCE_OUTPUT_DIR }}/
if-no-files-found: error

- name: Set up package build
shell: bash
run: |
echo "::group::Create build input directory"
set -eu
echo "::group::Create build input directory"
# Appending /source because 'dpkg-source --extract' needs the output directory to be non-existent
BUILD_INPUT_DIR="$( mktemp --directory --tmpdir='.' )/source"
BUILD_INPUT_BASEDIR="$( mktemp --directory --tmpdir="${PWD}" )"
echo BUILD_INPUT_BASEDIR="${BUILD_INPUT_BASEDIR}" >> $GITHUB_ENV
BUILD_INPUT_DIR="${BUILD_INPUT_BASEDIR}/source"
echo BUILD_INPUT_DIR="${BUILD_INPUT_DIR}" >> $GITHUB_ENV
echo "::endgroup::"
echo "::group::Create build output directory"
echo BUILD_OUTPUT_DIR="$( mktemp --directory --tmpdir='.' )" >> $GITHUB_ENV
echo BUILD_OUTPUT_DIR="$( mktemp --directory --tmpdir="${PWD}" )" >> $GITHUB_ENV
echo "::endgroup::"
echo "::group::Extract source package"
BUILD_INPUT_DIR=$(realpath "${BUILD_INPUT_DIR}")
cd ${{ env.SOURCE_OUTPUT_DIR }}
dpkg-source --extract *.dsc "${BUILD_INPUT_DIR}"
cd -
dpkg-source --extract ${{ env.PKG_NAME }}_${{ env.PKG_VERSION }}.dsc "${BUILD_INPUT_DIR}"
echo "::endgroup::"
- name: Build package
uses: jtdor/build-deb-action@v1
- name: Build packages
uses: kohlerdominik/[email protected]
with:
artifacts-dir: ${{ env.BUILD_OUTPUT_DIR }}
source-dir: ${{ env.BUILD_INPUT_DIR }}
docker-image: ${{ inputs.docker-image }}
image: ${{ inputs.docker-image }}
options: --cap-add=NET_ADMIN
environment: |
DEBIAN_FRONTEND=noninteractive
workdir: ${{ env.BUILD_INPUT_DIR }}
volumes: |
${{ env.BUILD_INPUT_BASEDIR }}:${{ env.BUILD_INPUT_BASEDIR }}
${{ env.BUILD_OUTPUT_DIR }}:${{ env.BUILD_OUTPUT_DIR }}
shell: bash
run: |
echo "::group::Update builder instance"
set -eu
echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/90aptyes
apt update
apt dist-upgrade
echo "::endgroup::"
echo "::group::Create build user"
apt install adduser
apt-mark auto adduser
adduser --disabled-password --gecos "" builder
chown builder:builder .. -R
echo "::endgroup::"
echo "::group::Fully disable internet access for user"
apt install iptables
apt-mark auto iptables
iptables -A OUTPUT -m owner --uid-owner $(id -u builder) -d 127.0.0.1 -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner $(id -u builder) -j DROP
echo "::endgroup::"
echo "::group::Cleanup unneeded packages"
apt autoremove
echo "::endgroup::"
echo "::group::Install fakeroot"
apt install fakeroot
echo "::endgroup::"
echo "::group::Install build dependencies"
apt build-dep .
echo "::endgroup::"
echo "::group::Build debian packages"
runuser -u builder -- dpkg-buildpackage -D -b --sanitize-env
echo "::endgroup::"
mv -v ../*"_${{ env.PKG_VERSION }}_"*.deb "${{ env.BUILD_OUTPUT_DIR }}"
echo "::group::Show binaries information"
for i in "${{ env.BUILD_OUTPUT_DIR }}"/*.deb; do
echo "$(basename "$i")"
dpkg --info "$i"
dpkg --contents "$i"
done
echo "::endgroup::"
- name: Upload artifacts
id: build-debian-binary-packages-upload-step
uses: actions/upload-artifact@v4
with:
name: ${{ env.PKG_NAME }}_${{ env.PKG_VERSION }}
name: ${{ env.PKG_NAME }}_${{ env.PKG_VERSION }}-debian-packages
path: ${{ env.BUILD_OUTPUT_DIR }}/
if-no-files-found: error

0 comments on commit 6e15fc5

Please sign in to comment.