GitLab CI Examples

This page collects some example snippets of GitLab CI configurations used to build, package, and publish with Nitrile.

Linux builds

The following is a very simple example to build a package. When the pipeline is run for a tag, it publishes the package to the registry.

Because builds by default run on an x64 Linux system, this will create a package for this platform.

image: cleanlang/nitrile # it's a good idea to specify the version here

stages: [build, publish]

build-linux-x64:
  stage: build
  before_script:
    - nitrile update
    - nitrile fetch
  script:
    - nitrile build
    - nitrile package
  artifacts:
    paths:
      - *.tar.gz

publish:
  stage: publish
  rules:
    - if: $CI_COMMIT_TAG
  script:
    - nitrile publish --targets=linux-x64

x86

To build for x86, specify --arch=x86:

build-linux-x86:
  stage: build
  before_script:
    - nitrile update
    - nitrile --arch=x86 fetch
  script:
    - nitrile --arch=x86 build
    - nitrile --arch=x86 package
  artifacts:
    paths:
      - *.tar.gz

Windows builds

The following is a minimal example to build a package on Windows:

build-windows-x64:
  tags: [saas-windows-medium-amd64]
  before_script:
        - $Env:NitrileVersion = '0.4.0' # Or some other version
    - Set-ExecutionPolicy ByPass -Scope Process -Force
    - (New-Object System.Net.WebClient).DownloadString('https://clean-lang.org/install.ps1') | powershell -Command -
    - $Env:PATH += ";${Env:APPDATA}\Nitrile\bin"
    - nitrile update
    - nitrile fetch
  script:
    - nitrile build
    - nitrile package
  artifacts:
    paths:
      - *.tar.gz

Compiling C code

If you need to compile C code using the Microsoft C Compiler, you need to get some environment variable settings from a script called vcvarsall.bat. These settings need to be imported into the PowerShell instance, which can be done by adding the following at the top of your before_script:

build-windows-x64:
  before_script:
    # We need to get the environment set by a batch script; see https://stackoverflow.com/a/41399983:
    - >-
      cmd /c '"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 & set'
      | select-string '^([^=]*)=(.*)$'
      | foreach-object { [Environment]::SetEnvironmentVariable($_.Matches[0].Groups[1].Value, $_.Matches[0].Groups[2].Value) }

This adds programs like cl to your PATH.

Catching errors

Warning

By default, native commands (.exe files) do not participate in the PowerShell error stream. This means that non-zero exit codes of native commands cannot be caught as errors by Nitrile, so that nitrile build (and other commands) may succeed even if there were errors.

To make the behaviour more intuitive, make sure that you are using PowerShell 7.3 or higher with the experimental feature PSNativeCommandErrorActionPreference enabled. This makes sure that non-zero exit codes end up in the error stream so that Nitrile can pick them up:

build-windows-x64:
  before_script:
    - pwsh -Command 'Enable-ExperimentalFeature PSNativeCommandErrorActionPreference'

This will become the default behaviour once PSNativeCommandErrorActionPreference becomes a stable feature; see issue #24 for tracking.

Generic builds

If you are packaging a library, it may be that the package is the same for every platform. In this case you can specify any as the platform and/or architecture, using the global options --arch and --platform.

Warning

To use this feature you should be absolutely sure that your library works for all the targets included by any. For example, if you are distributing some object or library files as part of your library, it is not fully generic.

If your library is fully generic, you could use something like the following. Note that we specify --arch=any --platform=any when packaging and any-any as the target for nitrile publish:

image: cleanlang/nitrile # it's a good idea to specify the version here

stages: [build]

build:
  stage: build
  script:
    # probably no update, fetch, and build needed if you have a generic package
    - nitrile --arch=any --platform=any package
    - '[ "$CI_COMMIT_TAG" = "" ] || nitrile publish --targets=any-any'
  artifacts:
    paths:
      - *.tar.gz

If you have a separate Linux and Windows version, but are not interested in different architectures and bitwidths, you might use a configuration like the one below. Note that we still specify --arch=any and use linux-any and windows-any as the targets for nitrile publish.

Info

If you publish overlapping packages to the registry, the behaviour is undefined. For instance, if you publish a linux-any package and a linux-x64 package, a Linux x64 user may get either when they have your package as a dependency. As a package author you are responsible for making sure that there is no overlap in the set of packages that you publish to the registry.

stages: [build, publish]

build-linux:
  stage: build
  script:
    # optionally update, fetch, build...
    - nitrile --arch=any package
  artifacts:
    paths:
      - *.tar.gz

build-windows-x64:
  tags: [saas-windows-medium-amd64]
  before_script:
    - Set-ExecutionPolicy ByPass -Scope Process -Force
    - (New-Object System.Net.WebClient).DownloadString('https://clean-lang.org/install.ps1') | powershell -Command -
    - $Env:PATH += ";${Env:APPDATA}\Nitrile\bin"
  script:
    - nitrile --arch=any package
  artifacts:
    paths:
      - *.tar.gz

publish:
  stage: publish
  rules:
    - if: $CI_COMMIT_TAG
  script:
    - nitrile publish --targets=linux-any,windows-any

Avoiding duplicate builds on branches, merge requests, and tags

You probably want to run only one pipeline for a commit in a merge request. By default, two are created: one for the branch and one for the merge request. The same thing happens with tags.

The following snippet at the top-level of .gitlab-ci.yml ensures that you get:

  • A detached merge request pipeline for commits in a merge request.
  • A tag pipeline for tags.
  • A branch pipeline for all other commits.
workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
      when: never
    - if: '$CI_COMMIT_BRANCH && $CI_COMMIT_TAG'
      when: never
    - if: '$CI_COMMIT_TAG'
    - if: '$CI_COMMIT_BRANCH'

No separate publish job when there is only one build

When you have only one build job, you could publish from that job instead of using a separate publish stage. Just be sure that you only publish tags.

For Linux, add the following at the end of the script:

    - '[ "$CI_COMMIT_TAG" = "" ] || nitrile publish'

For Windows you can use:

    - If (Test-Path variable:CI_COMMIT_TAG) { nitrile publish }

Parallelizing tests

GitLab CI allows you to parallelize jobs with the parallel keyword. You can use this to parallelize tests over multiple jobs:

test:
  parallel: 5
  script:
    - nitrile test --test-runner-options=parallel:$CI_NODE_INDEX:$CI_NODE_TOTAL

To parallelize property tests, it can be useful to define a separate job to build the property tests. This avoids building the same tests in each instance of the parallelized job. You can then pass the executables to a second job which actually runs them:

build-tests:
  script:
    - nitrile test --property-tester-options=compile-only
  artifacts:
    paths:
      - nitrile-tests

run-tests:
  needs: [build-tests]
  parallel: 5
  script:
    - nitrile test --property-tester-options=no-regenerate --test-runner-options=parallel:$CI_NODE_INDEX:$CI_NODE_TOTAL