6

Background

I have a CI pipeline for a C++ library I've been developing. So far, I can distribute this lib to Linux and Windows systems. Since I use GitLab to build, test and package my lib, I'd like to have my Windows builds running faster and I have no clue on how to do that.

Currently, I use the following script for my Windows builds:

.windows_template:
tags:
    - windows
before_script:
    - choco install cmake.install -y --installargs '"ADD_CMAKE_TO_PATH=System"'
    - choco install python --pre -y
    - choco install git -y
    - $env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.."; Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"; refreshenv
    - python -m pip install --upgrade pip
    - pip install conan monotonic

The problem

Any build with the script above can take up to 10 minutes; worse: I have two stages, each one taking the same amount of time. This means that my whole CI pipeline will take 20 minutes to finish because of slowness in Windows builds.

Ideal solution

EVERYTHING in my before_script can be cached or stored as an image. I only need some hints on how to do it properly.

Additional information

I use the following tools for my builds:

  • CMake: to support my building process;
  • Python3: to test and build packages;
  • Conan (requires Python3): to support the creation of packages with several features, as well as distribute them;
  • Git: to download Googletest in CMake configuration step This is already provided in the cookbooks - I might just remove this extra installation step in my before_script;
  • Googletest (requires Python3): testing library;
  • Visual Studio DEV Tools: to compile the library This is already in the cookbooks.
Rodrigo Novaes
  • 168
  • 1
  • 6
  • Or, you can use a Docker image with all pre-installed and be able to reproduce the same build environment anywhere. Conan Docker Tools offers a [Docker recipe](https://github.com/conan-io/conan-docker-tools/tree/master/msvc_15), but there is no Docker image available due Visual Studio license restriction for distribution. You could fork that project and update what you need. – uilianries Feb 15 '21 at 19:06
  • @uilianries I thought it wasn't possible to run Windows-based images on GitLab CI. I supposed the only option was to use Packer, as stated in the [documentation](https://docs.gitlab.com/ee/development/windows.html) (and honestly, I didn't try further on this option). Their documentation for [Docker images](https://docs.gitlab.com/ee/ci/docker/using_docker_images.html) do not mention anything about Windows containers, as well. If that's possible, I can consider your comment as the answer. – Rodrigo Novaes Feb 15 '21 at 19:29

1 Answers1

2

Installing packages like this (whether it's OS packages though apt-get install... or pip, or anything else) is generally against best practices for CI/CD jobs because every job that runs will have to do the same thing, costing a lot of time as you run more pipelines, as you've seen already.

A few alternatives are to search for an existing image that has everything you need (possible but not likely with more dependencies), split up your job into pieces that might be solved by an image with just one or two dependencies, or create a custom docker image to use in your jobs. I answered a similar question with an example a few weeks ago here: "Unable to locate package git" when running GitLab CI/CD pipeline

But here's an example Dockerfile with Windows:

# Dockerfile
FROM mcr.microsoft.com/windows
RUN ./install_chocolatey.sh
RUN choco install cmake.install -y --installargs '"ADD_CMAKE_TO_PATH=System"'
RUN choco install python --pre -y
RUN choco install git -y
...

The FROM line says that our new image extends the mcr.microsoft.com/windows base image. You can extend any image you have access to, even if it already extends another image (in fact, that's how most images work: they start with something small, like a base OS installation, then add things needed for that package. PHP for example starts on an Ubuntu image, then installs the necessary PHP packages).

The first RUN line is just an example. I'm not a Windows user and don't have experience installing Chocolatey, but you'd do here whatever you'd normally do to install it locally. The rest are for installing whatever else you need.

Then run

docker build /path/to/dockerfile-dir -t mygroup/mytag:version

The path you supply needs to be the directory that contains the Dockerfile, not the Dockerfile itself. The -t flag sets the image's tag after it's built (though you can do that with a separate command, docker tag too).

Next, you'll have to log into whichever registry you're using (Docker Hub (https://docs.docker.com/docker-hub/repos/), Gitlab Container Registry (https://docs.gitlab.com/ee/user/packages/container_registry/), a private registry your employer may support, or any other option.

docker login my.docker.hub.com

Now you can push the image to the registry:

docker push my.docker.hub.com/mygroup/mytag:version

You'll have to review the information in the docs about telling your Gitlab runner or pipelines how to authenticate with the registry (unless it's Public on Docker Hub or you use the Gitlab Container Registry) https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#define-an-image-from-a-private-container-registry

Once all that's done, you can use your new image in your CI jobs, and everything we put into the image will be ready to use:

.windows_template:
image: my.docker.hub.com/mygroup/mytag:version
tags:
    - windows
...
Adam Marshall
  • 6,369
  • 1
  • 29
  • 45
  • Hi @Adam! From [GitLab docs](https://docs.gitlab.com/13.8/ee/user/gitlab_com/index.html) I can see that Windows shared runners do not have `image` or `services` enabled. That's the main reason I wanted to go with some cache strategy rather than creating my own image, although this is the best solution (that's what I did for my Linux builds, by the way). Do you know some other solutions, please? – Rodrigo Novaes Feb 16 '21 at 15:47
  • According to [the docs](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-executors) there is a `docker-windows` executor that you could use to containerize your pipeline jobs, but if you can't use it for one reason or another, are you using the `shell` executor? If so, you could install whatever you need directly on the host running the gitlab-runner. – Adam Marshall Feb 16 '21 at 16:23
  • I use the GitLab CI shared runners, which means I don't have a dedicated runner for me. I have everything setup already: my [image](https://hub.docker.com/layers/cefetmg/samp-dev-tools/win32-msvc2019-cxx-1.0.0/images/sha256-e4d4bc8052dc93cf958ec5f20a94df4fd9e1d8d414a90784bfe11a1f5beb317d?context=repo) and my jobs. But it seems GitLab CI can't pull a Windows image and use it in an executor... At least not in a way I know how to do. Why Windows development is so hard? I was really hoping I could get this done by today, but that seems to be very unlikely :(. – Rodrigo Novaes Feb 16 '21 at 23:23
  • Hi @Adam. I just found this post with a workaround: https://stackoverflow.com/questions/65199082/how-to-use-a-custom-windows-docker-container-on-gitlab-ci-shared-runner I thought doing the same, but honestly I'd rather having things a little more sophisticated, if possible. Any ideas? – Rodrigo Novaes Feb 17 '21 at 15:30
  • You can use Docker images with the Windows shared runners, but you have to pull them yourself. However, it's so incredibly slow that it's unusable for most purposes. See https://gitlab.com/gitlab-org/gitlab-runner/-/issues/26606. – magjac Oct 29 '21 at 20:43
  • 1
    @magjac I also tried using that, but, as you've mentioned, didn't get any value from it. On a different matter, recently I've noticed that using `choco install` became somewhat faster across different builds. I didn't get the chance to validate this, but maybe they created some level of caching? – Rodrigo Novaes Nov 01 '21 at 10:29
  • 1
    I'm guessing this solution is only possible on your own windows-runners, not gitlab's shared runners. It would be nice to be able to do this since visualstudio2019community with all the addons is 18 GiB in size, and therefore nothing one can download each time. Another issue is that Gitlab's caching system (last I checked) only worked on stuff relative to your project directory. This also affects Nix builds, since they don't cache stuff in the `/nix/store`. – CMCDragonkai May 13 '22 at 04:32