diff --git a/.github/workflows/test-build-deb.yaml b/.github/workflows/test-build-deb.yaml new file mode 100644 index 00000000..f58b6aa6 --- /dev/null +++ b/.github/workflows/test-build-deb.yaml @@ -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: '' diff --git a/gh-actions/common/build-debian/action.yml b/gh-actions/common/build-debian/action.yml index a912bfeb..a389eb12 100644 --- a/gh-actions/common/build-debian/action.yml +++ b/gh-actions/common/build-debian/action.yml @@ -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: # @@ -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="noreply@github.com" >> $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/docker-run-action@v2.0.0 + 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/docker-run-action@v2.0.0 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