Vahagn
Vahagn

Reputation: 426

GitHub Continuous Integration CMake/C++

I am trying to set up a CI for my cmake/c++ project hosted on a private repository on GitHub.

The project depends on lots of third party libraries that should be git-cloned and built. The latter takes a while, hence, I created a docker image with all dependencies installed and hosted it on the docker hub. (Ideally, I would like the docker image to be private also, but if it is not possible, I can make it public.)

I want to achieve the following:

On pull requests to the master branch, the application is automatically built on the docker container(because all dependencies are there), all unit tests (gtest) are run and, if everything is alright, the branch is merged to master.

Ideally, I would like to see the logs and statistics generated by gcovr/lcov.

OS: Ubuntu 18.04

I wonder if this is even achievable as I have been searching for 2 days with no luck and a billion of possible readings.

Upvotes: 1

Views: 3272

Answers (1)

Mizux
Mizux

Reputation: 9309

My 2 cents (more a comment) on controlled build using docker.
For automatic merge, I don't know since I would be against it since code review can't be replaced by CI only IMHO...

Take a look at https://github.com/Mizux/cmake-cpp

Introduction

I use a Makefile for orchestration (docker command can be way too long ;)) and docker for isolated build on various distro.

pro:

  • Be able to test locally (Just need a GNU/Linux distro with Docker & Make)
  • Can migrate easily to various CI runners provider (Travis-CI, GitHub Workflow, gitlab-runner, bitbucket?)
  • Contributors can test locally before sending a PR

cons:

  • Less coupled to github -> more complex to maintain.
  • more difficult to have a cache between workflow

note: Dockerfile are stored in the repository in ci/docker i.e. I rebuild the images in the first steps but you should be able to replace this step by a simple docker load if your image is located on docker hub (not tested)

Setup

Dockerfile

I split my Dockerfile in several stages (mostly for debug).

note: you can replace ubuntu:rolling with your own image...

ci/docker/ubuntu/Dockerfile:

# Create a virtual environment with all tools installed
# ref: https://hub.docker.com/_/ubuntu
FROM ubuntu:rolling AS env
# Install system build dependencies
# note: here we use the CMake package provided by Ubuntu
# see: https://repology.org/project/cmake/versions
ENV PATH=/usr/local/bin:$PATH
RUN apt-get update -q && \
apt-get install -yq git build-essential cmake && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

CMD [ "/bin/sh" ]

# Add the library src to our build env
FROM env AS devel
# Create lib directory
WORKDIR /home/lib
# Bundle lib source
COPY . .

# Build in an other stage
FROM devel AS build
# CMake configure
RUN cmake -H. -Bbuild
# CMake build
RUN cmake --build build --target all
# CMake install
RUN cmake --build build --target install

# Create an install image to check cmake install config
FROM env AS install
# Copy lib from build to install
COPY --from=build /usr/local /usr/local/
# Copy  sample
WORKDIR /home/sample
COPY ci/sample .

Runner jobs

Github action runners have docker installed.

note: you can have one badge per yml file. e.g. You could should use one job per distro for example to have one jobs per distro or one file for Release and one file for Debug...

.github/workflows/docker.yml:

name: C++ CI

on: [push, pull_request]

jobs:
  build-docker:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Build env image
      run: docker build --target=env --tag project:env -f ci/docker/ubuntu/Dockerfile . 
    - name: Build devel image
      run: docker build --target=devel --tag project:devel -f ci/docker/ubuntu/Dockerfile .
    - name: Build build image
      run: docker build --target=build --tag project:build -f ci/docker/ubuntu/Dockerfile .

For testing you can add an other stage or run it using the project:build image:

docker run --rm --init -t --name test project:build cmake --build build --target test

Annexes

Faster build

You can add a .dockerignore file to remove unneeded files (e.g. LICENCES, doc, local build dir if testing locally...) to reduce docker context and the COPY . .

.dockerignore:

# Project Files unneeded by docker
ci/cache
ci/docker
ci/Makefile
.git
.gitignore
.github
.dockerignore
.travis.yml
.appveyor.yml
.clang-format
AUTHORS
CONTRIBUTING.md
CONTRIBUTHORS
INSTALL
LICENSE
README.md
doc

# Native CMake build
build/

# Editor directories and files
*.user
*.swp

Custom CMake version install

You can use the following instead of apt install -y cmake

Can take time since you rebuild CMake...

# Install CMake 3.16.4
RUN wget "https://cmake.org/files/v3.16/cmake-3.16.4.tar.gz" \
&& tar xzf cmake-3.16.4.tar.gz \
&& rm cmake-3.16.4.tar.gz \
&& cd cmake-3.16.4 \
&& ./bootstrap --prefix=/usr/local/ \
&& make \
&& make install \
&& cd .. \
&& rm -rf cmake-3.16.4

so you can use the prebuild version instead using:

# Install CMake 3.16.4
RUN wget "https://cmake.org/files/v3.16/cmake-3.16.4-Linux-x86_64.sh" \
&& chmod a+x cmake-3.16.4-Linux-x86_64.sh \
&& ./cmake-3.16.4-Linux-x86_64.sh --prefix=/usr/local/ --skip-license \
&& rm cmake-3.16.4-Linux-x86_64.sh

Upvotes: 2

Related Questions