52

I've been working on a C# application and wanted to try the GitLab CI out. All I can see is Ruby and can't find any information on how to build a C# application using it.

When I run the test settings, I make the commit, but I don't have my build.

Enter image description here

How should I make a simple build? Which command could I use for that? I don't mind if I get a failed build (but a build).

Pang
  • 9,564
  • 146
  • 81
  • 122
Andres Rojano Ruiz
  • 1,009
  • 1
  • 11
  • 17

6 Answers6

51

I just wanted to share my .gitlab-ci.yml complete with unit testing. You will have to adjust your nuget and possibly other paths. This is for a single project in a solution of the same name.

variables:
  PROJECT_NAME: "ProjectNameGoesHere"
before_script:
  - echo "starting build for %PROJECT_NAME%"
  - echo "Restoring NuGet Packages..."
  - d:\tools\nuget restore "%PROJECT_NAME%.sln"
stages:
  - build
  - test
build:
  stage: build
  script:
  - echo "Release build..."
  - '"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe" /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "%PROJECT_NAME%.sln"'
  artifacts:
    untracked: true
test:
  stage: test
  script:
  - echo "starting tests"
  - cd %PROJECT_NAME%Tests/bin/Release
  - '"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\MSTest.exe" /testcontainer:%PROJECT_NAME%Tests.dll'
  dependencies:
  - build
Jeff
  • 4,622
  • 2
  • 27
  • 40
  • 4
    For those reading this comment, you have FIRST to configure your machine to be the runner. Follow the guide of Prasanth Louis below, and especially the link https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/install/windows.md (because there were updates). Only then you can use this .gitlab-ci.yml. – corentinaltepe Dec 02 '16 at 06:31
  • 1
    @corentin Thank you for including this. Yes and you will need visual studio installed on your build machine as I believe MSTest is part of it. – Jeff Dec 06 '16 at 19:07
  • 4
    Please consider submitting your example to the [GitLab CI Yml project](https://gitlab.com/gitlab-org/gitlab-ci-yml/issues/12). – bbodenmiller Jan 27 '17 at 19:21
  • my project build successfully but removes all untracked files, including needed bin/ folders for testing.. something strange with artifacts: untracked: true? – fiorebat Sep 03 '18 at 15:43
  • 1
    @fiorebat If they are not tracked by git they should be added as per https://docs.gitlab.com/ee/ci/yaml/#artifacts-untracked If you are checking in dll files to git then you will have to tweak this or ask yourself why you are not pulling them from nugget. – Jeff Sep 04 '18 at 15:34
  • Sorry, I found, problem for me was that obj and bin directories was too big, at end of build there was a little notice about that, resolving marking only bin folder as artifacts – fiorebat Sep 06 '18 at 11:04
22

In order to build a C# application you should have a Windows runner (with shell executor) configured for a project in GitLab CI.

Your .gitlab-ci.yml file should look something like that:

stages:
  - build

job:
  stage: build
  script:
  - echo "Restoring NuGet Packages..."
  - '"c:\nuget\nuget.exe" restore "MySolution.sln"'
  - ''
  - echo "Release build..."
  - C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe /consoleloggerparameters:ErrorsOnly /maxcpucount /nologo /property:Configuration=Release /verbosity:quiet "MySolution.sln"
  tags:
  except:
  - tags

On a Windows machine you need the following tools:

  • Runner installed
  • Git, added to PATH
  • Latest nuget.exe at C:\nuget (or somewhere else. Just make sure you got the path right in the .gitlab-ci.yml file)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Grisha
  • 426
  • 5
  • 9
  • I'm sorry, what do you mean with 'git, added to PATH'? – E. van der Spoel Dec 01 '15 at 13:30
  • 2
    Got it, it's for Windows to know where git is located so the git can be executed as shell. – E. van der Spoel Dec 01 '15 at 14:04
  • 2
    Your path to msbuild doesn't work if there is a space in it. – Jeremy Dec 08 '15 at 19:47
  • @grisha On gitlab.com there's not a shared windows runner available. The shared runner on gitlab.com has Docker, but Docker doesn't support (yet) windows containers.* Do you know of plans of Gitlab supporting a windows shared runner? *) Yes, Docker does support windows containers in a weird way, but only if Docker client runs on a windows machine, which to a certain extent defies the purpose. – gijswijs May 29 '16 at 18:52
  • @gijswijs There is installation instructions for windows available [here](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/install/windows.md). There are links to [x86](https://gitlab-ci-multi-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-ci-multi-runner-windows-386.exe) and [amd64](https://gitlab-ci-multi-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-ci-multi-runner-windows-amd64.exe) runners. Hope it helps. – Grisha Jun 14 '16 at 06:43
  • @Grisha I'm afraid it doesn't. Those instructions are for the self-hosted version of Gitlab, aka Gitlab CE. I was trying to do this on the SaaS version on Gitlab.com – gijswijs Jun 17 '16 at 15:14
  • Please consider submitting your example to the [GitLab CI Yml project](https://gitlab.com/gitlab-org/gitlab-ci-yml/issues/12). – bbodenmiller Jan 27 '17 at 19:21
11

The other answers are good. But I'd like to explain how to install a runner in addition. I use my own local system (Windows), so I chose to run shell. But you could use a Docker image if you'd like.

cd C:\Multi-Runner
gitlab-ci-multi-runner register

Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
https://gitlab.com
Please enter the gitlab-ci token for this runner
xxx
Please enter the gitlab-ci description for this runner
my-runner
INFO[0034] fcf5c619 Registering runner... succeeded
Please enter the executor: shell, docker, docker-ssh, ssh?
shell
INFO[0037] Runner registered successfully. Feel free to start it, but if it's
running already the config should be automatically reloaded!

Source: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/install/windows.md

Afterwards, you can use a YAML file a like this:

stages:
    - build
job:
    stage: build
    script: '"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe" "something.sln"'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Prasanth Louis
  • 4,658
  • 2
  • 34
  • 47
  • Please consider submitting your example to the [GitLab CI Yml project](https://gitlab.com/gitlab-org/gitlab-ci-yml/issues/12). – bbodenmiller Jan 27 '17 at 19:22
10

Installing the build runner on a Windows machine helps a lot, and @prasanth-louis has a great example on how to do that.

As for the .gitlab-ci.yml file, you can simplify it even more by using Cake Build:

stages:
    - build
build:
    stage: build
    script:
        - .\build.ps1 -Target Build
    tags:
        - windows

And your build.cake file can look like this (based of the example repository):

#tool nuget:?package=NUnit.ConsoleRunner&version=3.4.0

var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");

var solution = "./example-project.sln";
var buildDir = Directory("./example-project/bin");

Task("Default")
    .IsDependentOn("Unit-Tests")
    .Does(() =>
{
    Information("Running Default task!");
});

Task("Clean")
    .Does(() =>
{
    CleanDirectory(buildDir);
});

Task("PackageRestore")
    .IsDependentOn("Clean")
    .Does(() =>
{
    Information("Restoring NuGet packages for {0}", solution);
    NuGetRestore(solution);
});

Task("Build")
    .IsDependentOn("PackageRestore")
    .Does(() =>
{
    Information("Restoring NuGet packages for {0}", solution);
    MSBuild(solution, settings => settings.SetConfiguration(configuration));
});

Task("Unit-Tests")
    .IsDependentOn("Build")
    .Does(() =>
{
    NUnit3("./example-project.Tests/**/bin/" + configuration + "/*.Tests.dll");
});

Task("Publish")
    .Does(() =>
{

});

RunTarget(target);
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
SolThoth
  • 369
  • 3
  • 7
  • Cake is definitely the way to go especially if you are transitioning from TeamCity, Jenkins, etc. server that handled a lot of this stuff for you. – David Archer Jan 19 '18 at 00:13
5

Here my working .gitlab-ci.yml file for c# application with NUnit as unit test framework and mono as basic image.

Not very fancy but working:

image: mono:latest

stages:
    - build
    - test

variables:
    solution: "Project.sln"
    test: "Project.Test"

before_script:
    - nuget restore

build:
    stage: build
    script:
        - msbuild /p:Configuration=Release $solution

test:
    stage: test
    script:
        - msbuild /p:Configuration=Release $solution
        - mono ./packages/NUnit.ConsoleRunner.3.10.0/tools/nunit3-console.exe ./$test/bin/Release/$test.dll
user1747547
  • 81
  • 1
  • 5
1

The other answers, while informative, miss a few crucial bits of info:

  • If you are targetting .net core 3.1, .net 5, .net 6 or any newer version then you can build and run your app on linux
  • If you are targetting .net framework (4.8 or below) then you have to build and run your code on a windows machine.

Now Gitlab runs build jobs using runners (aka build agents), that in turn use executors to specifiy the environment in which the build process happens. Runners can run on the Gitlab infrastructure (Saas runners), or can be installed on premise. There are several types of executors: docker, shell, custom, ...

Most of the gitlab build scripts that can be found around the internet assume a saas runner with a docker executor running on linux, which is based on a specific docker image. This won't work if you want to build your app on windows. For this, either install your own executor, as several other answers instruct to do, or use a windows saas runner which, although in beta, works fine even for production (we have been doing this for months without trouble). While installing your own runner can be useful, e.g. for debugging purposes, I find this defeats the whole purpose of building your software on a hosted cicd platform (github actions, gitlab ci, ...). For debugging, I prefer to base my build script on shell commands which can be run on your local dev box, which minimizes trial and error and makes installing your own runner superfluous.

To get started with a windows runner, see this exemple script. One or two gotchas that I went through:

  • You cannot choose the VM image of the build machine, it is managed by Gitlab and its content is not documented. This leaves you vulnerable to breaking changes if they decide to change their image.
  • Strangely, at the time of writing, the .net framework 4.8 developer pack is not installed on the windows executor image, but luckily chocolatey is, so if you target net48 you must install it in your before_script section.
  • The provisionning of the build machine is somewhat slower than a linux maching running docker, our builds average 15mn which we find acceptable.

That being said, here is a trimmed example based on our build scripts:

variables:
  MSBUILD_EXE: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\bin\msbuild.exe'
  UPDATE_VERSION: '.\path\to\Update-Version.ps1'
  APP_FOLDER: '.\path\to\Artifacts\_PublishedWebsites\MyApp\'
  
.windows_build_base:
  tags:
    - shared-windows
    - windows
    - windows-1809
  before_script:
    - Set-Variable -Name "time" -Value (date -Format "%H:%m")
    - echo ${time}
    - echo "started by ${GITLAB_USER_NAME}"
    - echo ".NET versions already installed"
    - Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" 
      | Where {$_.DisplayName -like '*.NET*' -and $_.DisplayVersion -like '4.*'} 
      | Select-Object DisplayName, DisplayVersion
    - echo "Installing .NET 4.8 Developer Pack"
    - choco install netfx-4.8-devpack -y

stages:
  - build
  - publish

build:
  only:
   - prod
   - test
  extends:
    - .windows_build_base
  stage: build
  script:
    - nuget restore
    - '& "${UPDATE_VERSION}" -srcDir . | Set-Variable -Name VERSION' # capture output of UPDATE_PRODUCTINFO script in variable VERSION
    - echo "VERSION=${VERSION}"
    - Add-Content -Path app_version.env -Value "VERSION=${VERSION}" # store value of VERSION in dotenv file to be passed to dependent stages, as per this workaround https://gitlab.com/gitlab-org/gitlab/-/issues/212629#note_430278657
    - Get-Content app_version.env
    - '& "${MSBUILD_EXE}" /t:solution_path\to\MyApp /p:Configuration=Release /clp:Summary /verbosity:Minimal /p:OutDir=.\Artifacts'
  artifacts:
    expire_in: 1 day
    paths:
      - '${APP_FOLDER}'
    reports:
      dotenv: app_version.env

publish:
   only:
   - prod
   - test
   image: 
     name: octopusdeploy/octo
     entrypoint: [""]
   stage: publish
   dependencies:
    - build
   script:
     - echo "VERSION=$VERSION"
     - octo pack --id="MyApp" --format="zip" --version="$VERSION" --basePath="path/to/Artifacts/_PublishedWebsites/MyApp" --outFolder="/output/"
     - octo push --overwrite-mode="OverwriteExisting" --server="https://mycompany.octopus.app" --space "MyOctoSpace" --package="/output/MyApp.$VERSION.zip"

This script:

  1. Installs .net 4.8 dev pack
  2. Restores nuget packages
  3. Manually updates the software version and passes it between the build and publish stages
  4. Builds the app
  5. Packs and pushes it to octopus deploy
Dharman
  • 30,962
  • 25
  • 85
  • 135
Nicolas
  • 139
  • 1
  • 7
  • This is the only answer that explains how to build on windows without the need to install a runner on your own machine or on-prem hardware. – Nicolas Jan 19 '23 at 13:40