├── .github └── workflows │ ├── build.yml │ └── vs_release_check.yml ├── Dockerfile ├── README.md ├── bin ├── msbuild └── vs_cmd └── build └── install_sdks.sh /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Container 2 | on: 3 | # push: 4 | # tags: 5 | # - '*' 6 | workflow_dispatch: 7 | inputs: 8 | vs_version: 9 | type: string 10 | required: true 11 | default: '17' 12 | jobs: 13 | install_buildtools: 14 | runs-on: windows-latest 15 | steps: 16 | - name: Install buildtools 17 | run: | 18 | # download buildtools installer 19 | Invoke-WebRequest -Uri https://aka.ms/vs/17/release/vs_BuildTools.exe -OutFile vs_BuildTools.exe 20 | 21 | # set VS_VERSION env variable 22 | $env:VS_VERSION = ${{ github.event.inputs.vs_version }} 23 | 24 | # download release metadata 25 | Invoke-WebRequest -Uri https://aka.ms/vs/$env:VS_VERSION/release/channel -OutFile channel.json 26 | 27 | # run buildtools installer 28 | cmd /S /C "start /w vs_BuildTools.exe --channelURI https://aka.ms/vs/%VS_VERSION%/release/channel --nocache --noUpdateInstaller --installPath %cd%\vs_buildtools --addProductLang en-US --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended --wait --quiet" 29 | 30 | # store vs version number 31 | $(Get-Content channel.json | ConvertFrom-Json).info.productDisplayVersion | Out-File vs_buildtools/version.txt -Encoding ascii -NoNewline 32 | 33 | - name: Compress buildtools 34 | uses: DuckSoft/create-7z-action@v1.0 35 | with: 36 | pathSource: vs_buildtools 37 | pathTarget: vs_buildtools.7z 38 | 39 | - name: Publish buildtools artifact 40 | uses: actions/upload-artifact@v3.0.0 41 | with: 42 | name: BuildTools 43 | path: vs_buildtools.7z 44 | 45 | build_container: 46 | needs: install_buildtools 47 | runs-on: ubuntu-20.04 48 | steps: 49 | - name: Clone repository 50 | uses: actions/checkout@v2 51 | 52 | - name: Download buildtools artifact 53 | uses: actions/download-artifact@v3.0.0 54 | with: 55 | name: BuildTools 56 | path: vs_buildtools.7z 57 | 58 | - name: Extract buildtools 59 | uses: DuckSoft/extract-7z-action@v1.0 60 | with: 61 | pathSource: vs_buildtools.7z 62 | pathTarget: ./ 63 | 64 | - name: Login to GitHub Container Registry 65 | uses: docker/login-action@v1 66 | with: 67 | registry: ghcr.io 68 | username: ${{ github.repository_owner }} 69 | password: ${{ secrets.GITHUB_TOKEN }} 70 | 71 | - name: Build and push Docker image 72 | run: | 73 | VS_VERSION=${{ github.event.inputs.vs_version }} 74 | VS_FULL_VERSION=$(cat vs_buildtools/version.txt) 75 | docker build -t ghcr.io/rektinator/msbuild-docker:${{ github.event.inputs.vs_version }}-latest -t ghcr.io/rektinator/msbuild-docker:$VS_FULL_VERSION . 76 | docker push ghcr.io/rektinator/msbuild-docker:${{ github.event.inputs.vs_version }}-latest 77 | docker push ghcr.io/rektinator/msbuild-docker:$VS_FULL_VERSION 78 | if [ $VS_VERSION -eq 17 ] 79 | then 80 | docker tag ghcr.io/rektinator/msbuild-docker:${{ github.event.inputs.vs_version }}-latest ghcr.io/rektinator/msbuild-docker:latest 81 | docker push ghcr.io/rektinator/msbuild-docker:latest 82 | fi 83 | -------------------------------------------------------------------------------- /.github/workflows/vs_release_check.yml: -------------------------------------------------------------------------------- 1 | name: Check for VS releases 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | check_vs_versions: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Check for new VS versions 13 | run: | 14 | #!/usr/bin/env bash 15 | 16 | # don't exit script on error 17 | set +e 18 | 19 | function get-latest-vs-release { 20 | curl -s --location "https://aka.ms/vs/$1/release/channel" | jq -r -e '.info.productDisplayVersion' 21 | } 22 | function check-vs-release { 23 | LATEST_RELEASE=$(get-latest-vs-release $1) 24 | docker manifest inspect ghcr.io/rektinator/msbuild-docker:$LATEST_RELEASE > /dev/null 25 | if [ $? -ne 0 ]; 26 | then 27 | # new release detected, trigger build action 28 | echo "Detected new VS version $LATEST_RELEASE, triggering build!" 29 | curl -s -X POST -H "Accept: application/vnd.github.v3+json" -u rektinator:${{ secrets.WORKFLOW_TOKEN }} https://api.github.com/repos/rektinator/msbuild-docker/actions/workflows/build.yml/dispatches -d "{\"ref\":\"main\", \"inputs\": {\"vs_version\": \"$1\"}}" > /dev/null 30 | fi 31 | } 32 | 33 | check-vs-release 16 34 | check-vs-release 17 35 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # build arguments 2 | ARG UBUNTU_VERSION="20.04" 3 | ARG USER_ID 4 | ARG GROUP_ID 5 | ARG SOLUTION_DIR 6 | 7 | FROM ubuntu:${UBUNTU_VERSION} 8 | 9 | # environment variables 10 | ENV DEBIAN_FRONTEND="noninteractive" 11 | ENV USER_ID=${USER_ID:-1000} 12 | ENV GROUP_ID=${GROUP_ID:-1000} 13 | ENV SOLUTION_DIR=${SOLUTION_DIR:-/src} 14 | 15 | # copy scripts 16 | COPY bin /usr/bin 17 | 18 | # install available updates, add the wine repository and install the required packages 19 | RUN apt-get update && \ 20 | apt-get full-upgrade --yes && \ 21 | apt-get install --yes wget software-properties-common xvfb && \ 22 | dpkg --add-architecture i386 && \ 23 | wget -qO- https://dl.winehq.org/wine-builds/winehq.key | apt-key add - && \ 24 | apt-add-repository "deb http://dl.winehq.org/wine-builds/ubuntu/ $(lsb_release -cs) main" && \ 25 | apt-get update && \ 26 | apt-get install --install-recommends --yes winehq-stable winbind cabextract && \ 27 | wget -q https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks -O /usr/bin/winetricks && \ 28 | chmod +x /usr/bin/winetricks && \ 29 | apt-get clean && \ 30 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 31 | 32 | # create a new user called runner (running things as root is not necessary at this point anymore), create the src folder and give the user full permission to that folder 33 | RUN groupadd --gid ${GROUP_ID} runner && \ 34 | useradd --create-home --uid ${USER_ID} --gid ${GROUP_ID} runner && \ 35 | mkdir /src && \ 36 | chown -R ${USER_ID}:${GROUP_ID} /src && \ 37 | mkdir /opt/msbuild && \ 38 | chown -R ${USER_ID}:${GROUP_ID} /opt/msbuild 39 | USER runner 40 | 41 | # install Windows SDK 42 | COPY --chown=${USER_ID}:${GROUP_ID} build/install_sdks.sh /tmp/ 43 | RUN xvfb-run /tmp/install_sdks.sh && \ 44 | rm -r ${HOME}/.cache/* /tmp/* 45 | 46 | # copy buildtools into container 47 | COPY --chown=${USER_ID}:${GROUP_ID} vs_buildtools /opt/msbuild/vs_buildtools 48 | 49 | # fix winsdk script 50 | # this if-statement condition ALWAYS fails under wine, seems to be a wine bug? 51 | RUN sed -i 's/\"!result:~0,3!\"==\"10.\"/\"1\"==\"1\"/g' /opt/msbuild/vs_buildtools/Common7/Tools/vsdevcmd/core/winsdk.bat 52 | 53 | # set working directory to /src (or Z:\src in wine terms) 54 | WORKDIR /src 55 | VOLUME ["/src"] 56 | 57 | # set vs_cmd as entrypoint 58 | ENTRYPOINT ["vs_cmd"] 59 | 60 | # pass "cmd" as argument to vs_cmd by default, this will open a command prompt 61 | CMD ["cmd"] 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # msbuild-docker 2 | A docker container to run msbuild on linux! 3 | 4 | ## Usage 5 | ### Opening a vs dev shell 6 | ``docker run -v "$(pwd):/src" -i ghcr.io/rektinator/msbuild-docker:latest`` will open a vs dev shell to ``Z:\src\``. ``Z:\src\`` is the current working directory. 7 | 8 | ### Compiling a solution file directly 9 | ``docker run -v "$(pwd):/src" ghcr.io/rektinator/msbuild-docker:latest msbuild my-solution.sln -t:Build`` will build the ``my-solution.sln`` file in the current working directory. 10 | -------------------------------------------------------------------------------- /bin/msbuild: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # abort the script if there is an error 4 | set -eo pipefail 5 | 6 | vs_cmd "msbuild $*" 7 | -------------------------------------------------------------------------------- /bin/vs_cmd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # abort the script if there is an error 4 | set -eo pipefail 5 | 6 | # create solution directory (if it does not exist) 7 | if [ ! -d "${SOLUTION_DIR}" ]; then 8 | mkdir -p "${SOLUTION_DIR}" 9 | fi 10 | 11 | # spawn vs dev shell & execute commands 12 | pushd "${SOLUTION_DIR}" > /dev/null 13 | WINEDEBUG=-all wine cmd /c "/opt/msbuild/vs_buildtools/Common7/Tools/VsDevCmd.bat && cd /d %SOLUTION_DIR% && $*" 14 | popd > /dev/null 15 | -------------------------------------------------------------------------------- /build/install_sdks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # create wine prefix 4 | export WINEDLLOVERRIDES="mscoree=" 5 | export WINEDEBUG="-all" 6 | wineboot --init 7 | 8 | # install dotnet 4.8 9 | winetricks --force --unattended dotnet48 win10 10 | 11 | # kill wineserver 12 | wineserver --kill 13 | 14 | # download the windows SDK 15 | wget -q https://download.microsoft.com/download/d/8/f/d8ff148b-450c-40b3-aeed-2a3944e66bbd/windowssdk/winsdksetup.exe -O ${HOME}/.cache/winsdksetup.exe 16 | 17 | # install the windows SDK 18 | wine64 ${HOME}/.cache/winsdksetup.exe /norestart /q /installpath "Z:\\opt\\msbuild\\winsdk" 19 | 20 | # kill wineserver 21 | wineserver --kill 22 | --------------------------------------------------------------------------------