diff options
author | Emmanuele Bassi <ebassi@gnome.org> | 2020-04-20 16:40:50 +0100 |
---|---|---|
committer | Emmanuele Bassi <ebassi@gnome.org> | 2020-04-22 14:52:02 +0100 |
commit | 822dd6c670eba4b6d41982222460446f14d277bd (patch) | |
tree | a73de3aeb9a2b6c9bc94c5e0a540c0815405cd1e | |
parent | 4801e4657bfa6633a14e79e3f54c6ab460d4e01b (diff) | |
download | atk-822dd6c670eba4b6d41982222460446f14d277bd.tar.gz |
Add CI pipeline for ATK
Use the GitLab CI support to build ATK, so we can at least be confident
that we're not breaking stuff with every commit.
-rw-r--r-- | .gitlab-ci.yml | 65 | ||||
-rw-r--r-- | .gitlab-ci/README.md | 38 | ||||
-rw-r--r-- | .gitlab-ci/debian-stable.Dockerfile | 44 | ||||
-rw-r--r-- | .gitlab-ci/fedora-latest.Dockerfile | 38 | ||||
-rwxr-xr-x | .gitlab-ci/meson-junit-report.py | 111 | ||||
-rwxr-xr-x | .gitlab-ci/run-docker.sh | 135 | ||||
-rwxr-xr-x | .gitlab-ci/run-tests-docker.sh | 26 | ||||
-rwxr-xr-x | .gitlab-ci/setup-build-docker.sh | 8 |
8 files changed, 465 insertions, 0 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..b67fafa --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,65 @@ +stages: + - build + - docs + - deploy + +.cache-paths: &cache-paths + paths: + - _ccache/ + +# Common variables +variables: + COMMON_MESON_FLAGS: "--fatal-meson-warnings --werror" + MESON_TEST_TIMEOUT_MULTIPLIER: 2 + +.default-build: + script: + - .gitlab-ci/setup-build-docker.sh + - ninja -C _build + - .gitlab-ci/run-tests-docker.sh _build + artifacts: + when: always + reports: + junit: + - "${CI_PROJECT_DIR}/_build/report.xml" + name: "atk-${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}" + paths: + - "_build/meson-logs" + - "_build/report.xml" + +fedora-latest: + extends: .default-build + stage: build + image: registry.gitlab.gnome.org/gnome/atk/fedora-latest:v1 + variables: + EXTRA_MESON_FLAGS: "-Dbuildtype=debug -Ddefault_library=both" + +debian-stable: + extends: .default-build + stage: build + image: registry.gitlab.gnome.org/gnome/atk/debian-stable:v1 + variables: + EXTRA_MESON_FLAGS: "-Dbuildtype=debug -Ddefault_library=both" + +reference: + image: registry.gitlab.gnome.org/gnome/atk/fedora-latest:v1 + stage: docs + variables: + EXTRA_MESON_FLAGS: "--buildtype=release -Ddocs=true" + script: + - .gitlab-ci/setup-build-docker.sh + - ninja -C _build atk-doc + - mv _build/docs/html _reference + artifacts: + paths: + - _reference + +pages: + stage: deploy + script: + - mv _reference/ public/ + artifacts: + paths: + - public + only: + - master diff --git a/.gitlab-ci/README.md b/.gitlab-ci/README.md new file mode 100644 index 0000000..b090cfb --- /dev/null +++ b/.gitlab-ci/README.md @@ -0,0 +1,38 @@ +## ATK CI infrastructure + +ATK uses different CI images depending on platform and jobs. + +The CI images are Docker containers, generated either using `docker` or +`podman`, and pushed to the GitLab [container registry][registry]. + +Each Docker image has a tag composed of two parts: + + - `${image}`: the base image for a given platform, like "fedora-latest" + or "debian-stable" + - `${number}`: an incremental version number, or `latest` + +See the [container registry][registry] for the available images for each +branch, as well as their available versions. + +### Checklist for Updating a CI image + + - [ ] Update the `${image}.Dockerfile` file with the dependencies + - [ ] Run `./run-docker.sh build --base ${image} --base-version ${number}` + - [ ] Run `./run-docker.sh push --base ${image} --base-version ${number}` + once the Docker image is built; you may need to log in by using + `docker login` or `podman login` + - [ ] Update the `image` keys in the `.gitlab-ci.yml` file with the new + image tag + - [ ] Open a merge request with your changes and let it run + +### Checklist for Adding a new CI image + + - [ ] Write a new `${image}.Dockerfile` with the instructions to set up + a build environment + - [ ] Add the `pip3 install meson` incantation + - [ ] Run `./run-docker.sh build --base ${image} --base-version ${number}` + - [ ] Run `./run-docker.sh push --base ${image} --base-version ${number}` + - [ ] Add the new job to `.gitlab-ci.yml` referencing the image + - [ ] Open a merge request with your changes and let it run + +[registry]: https://gitlab.gnome.org/GNOME/atk/container_registry diff --git a/.gitlab-ci/debian-stable.Dockerfile b/.gitlab-ci/debian-stable.Dockerfile new file mode 100644 index 0000000..75e2105 --- /dev/null +++ b/.gitlab-ci/debian-stable.Dockerfile @@ -0,0 +1,44 @@ +FROM debian:buster + +RUN apt-get update -qq && apt-get install --no-install-recommends -qq -y \ + ccache \ + dconf-gsettings-backend \ + g++ \ + gcc \ + gettext \ + git \ + gobject-introspection \ + itstool \ + libc6-dev \ + libgirepository1.0-dev \ + libglib2.0-dev \ + libwayland-dev \ + libx11-dev \ + libxml2-dev \ + libxrandr-dev \ + locales \ + ninja-build \ + pkg-config \ + python3 \ + python3-pip \ + python3-setuptools \ + python3-wheel \ + shared-mime-info \ + wayland-protocols \ + xauth \ + xvfb \ + && rm -rf /usr/share/doc/* /usr/share/man/* + +# Locale for our build +RUN locale-gen C.UTF-8 && /usr/sbin/update-locale LANG=C.UTF-8 + +ARG HOST_USER_ID=5555 +ENV HOST_USER_ID ${HOST_USER_ID} +RUN useradd -u $HOST_USER_ID -ms /bin/bash user + +USER user +WORKDIR /home/user + +RUN pip3 install --user meson==0.49.2 + +ENV LANG=C.UTF-8 LANGUAGE=C.UTF-8 LC_ALL=C.UTF-8 diff --git a/.gitlab-ci/fedora-latest.Dockerfile b/.gitlab-ci/fedora-latest.Dockerfile new file mode 100644 index 0000000..b257596 --- /dev/null +++ b/.gitlab-ci/fedora-latest.Dockerfile @@ -0,0 +1,38 @@ +FROM fedora:31 + +RUN dnf -y install \ + at-spi2-atk-devel \ + ccache \ + dbus-x11 \ + gcc \ + gcc-c++ \ + gettext \ + gettext-devel \ + git \ + glib2-devel \ + gobject-introspection-devel \ + gtk-doc \ + iso-codes \ + itstool \ + make \ + meson \ + ninja-build \ + python3 \ + python3-pip \ + python3-wheel \ + redhat-rpm-config \ + wayland-devel \ + wayland-protocols-devel \ + xorg-x11-server-Xvfb \ + && dnf clean all + +ARG HOST_USER_ID=5555 +ENV HOST_USER_ID ${HOST_USER_ID} +RUN useradd -u $HOST_USER_ID -ms /bin/bash user + +USER user +WORKDIR /home/user + +RUN pip3 install --user meson==0.49.2 + +ENV LANG C.utf8 diff --git a/.gitlab-ci/meson-junit-report.py b/.gitlab-ci/meson-junit-report.py new file mode 100755 index 0000000..623f275 --- /dev/null +++ b/.gitlab-ci/meson-junit-report.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 + +# Turns a Meson testlog.json file into a JUnit XML report +# +# Copyright 2019 GNOME Foundation +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Original author: Emmanuele Bassi + +import argparse +import datetime +import json +import os +import sys +import xml.etree.ElementTree as ET + +aparser = argparse.ArgumentParser(description='Turns a Meson test log into a JUnit report') +aparser.add_argument('--project-name', metavar='NAME', + help='The project name', + default='unknown') +aparser.add_argument('--job-id', metavar='ID', + help='The job ID for the report', + default='Unknown') +aparser.add_argument('--branch', metavar='NAME', + help='Branch of the project being tested', + default='master') +aparser.add_argument('--output', metavar='FILE', + help='The output file, stdout by default', + type=argparse.FileType('w', encoding='UTF-8'), + default=sys.stdout) +aparser.add_argument('infile', metavar='FILE', + help='The input testlog.json, stdin by default', + type=argparse.FileType('r', encoding='UTF-8'), + default=sys.stdin) + +args = aparser.parse_args() + +outfile = args.output + +testsuites = ET.Element('testsuites') +testsuites.set('id', '{}/{}'.format(args.job_id, args.branch)) +testsuites.set('package', args.project_name) +testsuites.set('timestamp', datetime.datetime.utcnow().isoformat(timespec='minutes')) + +suites = {} +for line in args.infile: + data = json.loads(line) + unit_name = data['name'] + suite_name = args.project_name + + duration = data['duration'] + return_code = data['returncode'] + result = data['result'] + log = data['stdout'] + + unit = { + 'suite': args.project_name, + 'name': unit_name, + 'duration': duration, + 'returncode': return_code, + 'result': result, + 'stdout': log, + } + + units = suites.setdefault(suite_name, []) + units.append(unit) + +for name, units in suites.items(): + print('Processing suite {} (units: {})'.format(name, len(units))) + + def if_failed(unit): + if unit['result'] in ['ERROR', 'FAIL', 'UNEXPECTEDPASS', 'TIMEOUT']: + return True + return False + + def if_succeded(unit): + if unit['result'] in ['OK', 'EXPECTEDFAIL', 'SKIP']: + return True + return False + + successes = list(filter(if_succeded, units)) + failures = list(filter(if_failed, units)) + print(' - {}: {} pass, {} fail'.format(name, len(successes), len(failures))) + + testsuite = ET.SubElement(testsuites, 'testsuite') + testsuite.set('name', '{}/{}'.format(args.project_name, name)) + testsuite.set('tests', str(len(units))) + testsuite.set('errors', str(len(failures))) + testsuite.set('failures', str(len(failures))) + + for unit in successes: + testcase = ET.SubElement(testsuite, 'testcase') + testcase.set('classname', '{}/{}'.format(args.project_name, unit['suite'])) + testcase.set('name', unit['name']) + testcase.set('time', str(unit['duration'])) + + for unit in failures: + testcase = ET.SubElement(testsuite, 'testcase') + testcase.set('classname', '{}/{}'.format(args.project_name, unit['suite'])) + testcase.set('name', unit['name']) + testcase.set('time', str(unit['duration'])) + + failure = ET.SubElement(testcase, 'failure') + failure.set('classname', '{}/{}'.format(args.project_name, unit['suite'])) + failure.set('name', unit['name']) + failure.set('type', 'error') + failure.text = unit['stdout'] + +output = ET.tostring(testsuites, encoding='unicode') +outfile.write(output) diff --git a/.gitlab-ci/run-docker.sh b/.gitlab-ci/run-docker.sh new file mode 100755 index 0000000..618ddd9 --- /dev/null +++ b/.gitlab-ci/run-docker.sh @@ -0,0 +1,135 @@ +#!/bin/bash + +read_arg() { + # $1 = arg name + # $2 = arg value + # $3 = arg parameter + local rematch='^[^=]*=(.*)$' + if [[ $2 =~ $rematch ]]; then + read "$1" <<< "${BASH_REMATCH[1]}" + else + read "$1" <<< "$3" + # There is no way to shift our callers args, so + # return 1 to indicate they should do it instead. + return 1 + fi +} + +set -e + +build=0 +run=0 +push=0 +list=0 +print_help=0 +no_login=0 + +while (($# > 0)); do + case "${1%%=*}" in + build) build=1;; + run) run=1;; + push) push=1;; + list) list=1;; + help) print_help=1;; + --base|-b) read_arg base "$@" || shift;; + --base-version) read_arg base_version "$@" || shift;; + --no-login) no_login=1;; + *) echo -e "\e[1;31mERROR\e[0m: Unknown option '$1'"; exit 1;; + esac + shift +done + +if [ $print_help == 1 ]; then + echo "$0 - Build and run Docker images" + echo "" + echo "Usage: $0 <command> [options] [basename]" + echo "" + echo "Available commands" + echo "" + echo " build --base=<BASENAME> - Build Docker image <BASENAME>.Dockerfile" + echo " run --base=<BASENAME> - Run Docker image <BASENAME>" + echo " push --base=<BASENAME> - Push Docker image <BASENAME> to the registry" + echo " list - List available images" + echo " help - This help message" + echo "" + exit 0 +fi + +cd "$(dirname "$0")" + +if [ $list == 1 ]; then + echo "Available Docker images:" + for f in *.Dockerfile; do + filename=$( basename -- "$f" ) + basename="${filename%.*}" + + echo -e " \e[1;39m$basename\e[0m" + done + exit 0 +fi + +# All commands after this require --base to be set +if [ -z $base ]; then + echo "Usage: $0 <command>" + exit 1 +fi + +if [ ! -f "$base.Dockerfile" ]; then + echo -e "\e[1;31mERROR\e[0m: Dockerfile for '$base' not found" + exit 1 +fi + +if [ -z $base_version ]; then + base_version="latest" +else + base_version="v$base_version" +fi + +if [ ! -x "$(command -v docker)" ] || [ docker --help |& grep -q podman ]; then + # Docker is actually implemented by podman, and its OCI output + # is incompatible with some of the dockerd instances on GitLab + # CI runners. + echo "Using: Podman" + format="--format docker" + CMD="podman" +else + echo "Using: Docker" + format="" + CMD="sudo socker" +fi + +REGISTRY="registry.gitlab.gnome.org" +TAG="${REGISTRY}/gnome/atk/${base}:${base_version}" + +if [ $build == 1 ]; then + echo -e "\e[1;32mBUILDING\e[0m: ${base} as ${TAG}" + ${CMD} build \ + ${format} \ + --build-arg HOST_USER_ID="$UID" \ + --tag "${TAG}" \ + --file "${base}.Dockerfile" . + exit $? +fi + +if [ $push == 1 ]; then + echo -e "\e[1;32mPUSHING\e[0m: ${base} as ${TAG}" + + if [ $no_login == 0 ]; then + ${CMD} login ${REGISTRY} + fi + + ${CMD} push ${TAG} + exit $? +fi + +if [ $run == 1 ]; then + echo -e "\e[1;32mRUNNING\e[0m: ${base} as ${TAG}" + ${CMD} run \ + --rm \ + --volume "$(pwd)/..:/home/user/app" \ + --workdir "/home/user/app" \ + --tty \ + --interactive "${TAG}" \ + bash + exit $? +fi diff --git a/.gitlab-ci/run-tests-docker.sh b/.gitlab-ci/run-tests-docker.sh new file mode 100755 index 0000000..b35dd6e --- /dev/null +++ b/.gitlab-ci/run-tests-docker.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +set +x +set +e + +srcdir=$( pwd ) +builddir=$1 + +export PATH="${HOME}/.local/bin:${PATH}" + +meson test -C ${builddir} \ + --print-errorlogs + +# Store the exit code for the CI run, but always +# generate the reports +exit_code=$? + +cd ${builddir} + +$srcdir/.gitlab-ci/meson-junit-report.py \ + --project-name=atk \ + --job-id="${CI_JOB_NAME}" \ + --output=report.xml \ + meson-logs/testlog.json + +exit $exit_code diff --git a/.gitlab-ci/setup-build-docker.sh b/.gitlab-ci/setup-build-docker.sh new file mode 100755 index 0000000..96fd6d2 --- /dev/null +++ b/.gitlab-ci/setup-build-docker.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set +e +set +x + +export PATH="${HOME}/.local/bin:${PATH}" + +meson ${EXTRA_MESON_FLAGS:-} _build . |