TiagoMSSantos/MobileRT

View on GitHub
.github/workflows/docker.yml

Summary

Maintainability
Test Coverage
name: Docker

on:
  workflow_dispatch:
  push:
    paths-ignore:
    - '*'
    - '**/**'
    - '!.github/workflows/docker.yml'
    - '!.github/macports.yml'
    - '!app/third_party/conan/Native/**'
    - '!deploy/Dockerfile*'
    - '!scripts/install_dependencies.sh'
    - '!scripts/compile_native.sh'
    - '!scripts/helper_functions.sh'
    - '!scripts/profile.sh'
    - '!scripts/docker.sh'
    - '!scripts/test/docker/dockerfile.sh'
    - '!**/CMakeLists*'
    - '!**/AndroidTest/resources/**'
    - '!**/*.c*'
    - '!**/*.h'
    - '!**/*.hpp'
    - '!**/*.ui*'

defaults:
  run:
    shell: sh
    working-directory: .

concurrency:
  group: ${{ github.workflow }} ${{ github.ref }}
  cancel-in-progress: true

# Default environment variables.
env:
  GITHUB_STEP_TIMEOUT_SMALL: 4
  GITHUB_STEP_TIMEOUT_MEDIUM: 10
  GITHUB_STEP_TIMEOUT_LONG: 20

jobs:
  Build:
    if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'

    strategy:
      fail-fast: false
      matrix:
        include:
        - host_os: ubuntu-latest
          BASE_IMAGE: ubuntu:24.04
        - host_os: ubuntu-latest
          BASE_IMAGE: tgagor/centos:stream9
        - host_os: ubuntu-latest
          BASE_IMAGE: archlinux/archlinux:base-devel
        - host_os: ubuntu-latest
          BASE_IMAGE: alpine:3.21.2
        - host_os: ubuntu-latest
          BASE_IMAGE: gentoo/stage3:latest
        - host_os: windows-latest
          BASE_IMAGE: mcr.microsoft.com/windows/servercore:ltsc2022

    name: ${{ matrix.BASE_IMAGE }} (${{ matrix.host_os }})
    runs-on: ${{ matrix.host_os }}
    # Necessary timeout of +/- 300 min to be able to build MobileRT with gentoo.
    # That's why we try to pull a previously available version and update it with a new layer instead.
    timeout-minutes: 300

    steps:
    - name: Checkout
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_MEDIUM) }}
      if: success()
      uses: actions/checkout@v4

    - name: Enable KVM group permissions
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_SMALL) }}
      if: success() && startsWith(matrix.host_os, 'ubuntu')
      run: |
        echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules;
        sudo udevadm control --reload-rules;
        sudo udevadm trigger --name-match=kvm;

    - name: Add branch name and version to environment variables
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_SMALL) }}
      if: success()
      shell: bash
      env:
        BASE_IMAGE: ${{ matrix.BASE_IMAGE }}
      run: |
        export BRANCH=${GITHUB_REF#refs/heads/};
        echo "BRANCH: ${BRANCH}";
        echo "BRANCH=${BRANCH}" >> "${GITHUB_ENV}";
        export VERSION="${BASE_IMAGE//\//-}";
        export VERSION="${VERSION//:/-}";
        echo "VERSION=${VERSION}" >> "${GITHUB_ENV}";
        echo 'BUILD_IMAGE=yes' >> "${GITHUB_ENV}";

    - name: Install MacPorts (MacOS 11)
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_MEDIUM) }}
      if: success() && startsWith(matrix.host_os, 'macos-11')
      uses: melusina-org/setup-macports@v1
      with:
        # Check available parameters in: https://github.com/melusina-org/setup-macports/blob/main/action.yaml
        parameters: '.github/macports.yml'

    - name: Install docker MacOS
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_LONG) }}
      if: success() && startsWith(matrix.host_os, 'macos')
      env:
        BASE_IMAGE: ${{ matrix.BASE_IMAGE }}
      run: |
        # shellcheck disable=SC1091
        . scripts/docker.sh && installDockerCommandForMacOS;

    - name: Windows copy necessary tools to workspace
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_SMALL) }}
      if: success() && env.BUILD_IMAGE == 'yes' && startsWith(matrix.host_os, 'windows')
      shell: powershell
      run: |
        $ErrorActionPreference = 'Stop'
        Write-Host "Copying shell binaries to workspace."
        # where.exe sh
        New-Item -Type dir .\\tools\\binariesShell
        Copy-Item -Path "C:\\Program Files\\Git\\usr\\bin\\*" -Destination ".\\tools\\binariesShell" -Recurse

        Write-Host "Copying resource compiler binaries to workspace."
        # Get-ChildItem -Path "C:\\Program Files (x86)\\Windows Kits\\10\\bin" -Include rc.exe -File -Recurse -ErrorAction SilentlyContinue | ForEach-Object{$_.FullName}
        New-Item -Type dir .\\tools\\binariesResourceCompiler
        $windowsKitsVersion = Get-ChildItem -Path "C:\\Program Files (x86)\\Windows Kits\\10\\bin" | Where {$_.name -match "^[0-9\.]+$"} | Sort-Object -Descending | Select name | Select-Object -ExpandProperty Name | Select -first 1
        Copy-Item -Path "C:\\Program Files (x86)\\Windows Kits\\10\\bin\\$windowsKitsVersion\\x64\\*" -Destination ".\\tools\\binariesResourceCompiler" -Recurse

    - name: Set up Docker Buildx
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_SMALL) }}
      if: success() && !startsWith(matrix.host_os, 'windows')
      uses: docker/setup-buildx-action@v3
      with:
        # Check available parameters in: https://github.com/docker/setup-buildx-action/blob/master/action.yml
        version: latest
        platforms: linux/amd64
        cache-binary: true
        cleanup: false

    - name: Login to DockerHub Registry
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_SMALL) }}
      if: success()
      uses: docker/login-action@v3
      with:
        # Check available parameters in: https://github.com/docker/login-action/blob/master/action.yml
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_PASSWORD }}
        ecr: false
        logout: true

    - name: Delete unnecessary Docker images
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_MEDIUM) }}
      if: success()
      run: |
        baseImageRegex=$(echo "${{ matrix.BASE_IMAGE }}" | sed 's/:/.*/');
        mobilertImageRegex=$(echo "mobile_rt.*${{ env.VERSION }}");
        echo "baseImageRegex: ${baseImageRegex}";
        echo "mobilertImageRegex: ${mobilertImageRegex}";
        docker images -a;
        docker images -a | grep GB | grep -v "${baseImageRegex}" | grep -v "${mobilertImageRegex}" | grep -v 'REPOSITORY' | head -1 | xargs docker rmi -f || true;
        docker system prune --volumes --force;
        docker images -a;

    - name: Install Docker BuildKit & Buildx & build image Windows
      timeout-minutes: 60
      if: success() && env.BUILD_IMAGE == 'yes' && startsWith(matrix.host_os, 'windows') && false
      shell: powershell
      working-directory: .
      run: |
        $ErrorActionPreference = 'Stop'
        $url = "https://api.github.com/repos/containerd/containerd/releases/latest"
        $version = (Invoke-RestMethod -Uri $url -UseBasicParsing).tag_name
        $version = $version -replace "v",""
        $arch = "amd64"    # arm64 also available
        Write-Host "Downloading & extracting containerd $version - $arch"
        curl.exe -LO https://github.com/containerd/containerd/releases/download/v$version/containerd-$version-windows-$arch.tar.gz
        tar xvf .\containerd-$version-windows-$arch.tar.gz
        Write-Host "Copy containerd Windows binaries."
        Copy-Item -Path .\bin -Destination $Env:ProgramFiles\containerd -Recurse -Force

        Write-Host "Add the binaries (containerd, ctr) in $env:Path."
        $Path = [Environment]::GetEnvironmentVariable("PATH", "Machine") + [IO.Path]::PathSeparator + "$Env:ProgramFiles\containerd"
        [Environment]::SetEnvironmentVariable( "Path", $Path, "Machine")
        $Env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")

        Write-Host "Configuring containerd"
        containerd config default | Out-File $Env:ProgramFiles\containerd\config.toml -Encoding ascii
        Get-Content $Env:ProgramFiles\containerd\config.toml

        Write-Host "Register and start containerd service"
        containerd --register-service
        Start-Service containerd

        Write-Host "Enable Containers and Microsoft-Hyper-V features."
        Enable-WindowsOptionalFeature -Online -FeatureName $("Containers","Microsoft-Hyper-V","Microsoft-Windows-Subsystem-Linux","VirtualMachinePlatform") -All

        $url = "https://api.github.com/repos/moby/buildkit/releases/latest"
        $version = (Invoke-RestMethod -Uri $url -UseBasicParsing).tag_name
        $version = $version -replace "v",""
        $arch = "amd64" # arm64 binary available too
        Write-Host "Downloading buildkit $version - $arch"
        curl.exe -LO https://github.com/moby/buildkit/releases/download/v$version/buildkit-v$version.windows-$arch.tar.gz
        Write-Host "Extracting buildkit."
        tar xvf .\buildkit-v$version.windows-$arch.tar.gz

        Write-Host "Listing buildkit files."
        Copy-Item -Path ".\bin" -Destination "$Env:ProgramFiles\buildkit" -Recurse -Force
        Write-Host "Adding `buildkitd` and `buildctl` binaries in the $Env:PATH"
        $Path = [Environment]::GetEnvironmentVariable("PATH", "Machine") + `
          [IO.Path]::PathSeparator + "$Env:ProgramFiles\buildkit"
        Write-Host "Storing the Path variable"
        [Environment]::SetEnvironmentVariable( "Path", $Path, "Machine")
        echo "Path=$Path" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
        Write-Host "Checking Path variable"
        $Env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + `
          [System.Environment]::GetEnvironmentVariable("Path","User")

        $url = "https://api.github.com/repos/docker/buildx/releases/latest"
        $version = (Invoke-RestMethod -Uri $url -UseBasicParsing).tag_name
        $version = $version -replace "v",""
        $arch = "amd64"
        Write-Host "Downloading buildx $version - $arch"
        curl.exe -LO https://github.com/docker/buildx/releases/download/v$version/buildx-v$version.windows-$arch.exe
        Write-Host "Copying buildx to C:\ProgramData\Docker\cli-plugins"
        Copy-Item -Path "buildx-v$version.windows-$arch.exe" -Destination "C:\ProgramData\Docker\cli-plugins\docker-buildx.exe" -Force
        $DOCKER_CLI_EXPERIMENTAL = "enabled"
        $Env:DOCKER_CLI_EXPERIMENTAL = "enabled"
        $DOCKER_BUILDKIT = "1"
        $Env:DOCKER_BUILDKIT = "1"

        $networkName = 'nat'
        # Get-HnsNetwork is available once you have enabled the 'Hyper-V Host Compute Service' feature
        # which must have been done at the Quick setup above
        # Enable-WindowsOptionalFeature -Online -FeatureName containers -All
        # Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V-All -All
        # the default one named `nat` should be available
        $natInfo = Get-HnsNetwork -ErrorAction Ignore | Where-Object { $_.Name -eq $networkName }
        if ($null -eq $natInfo) {
          throw "NAT network not found, check if you enabled containers, Hyper-V features and restarted the machine"
        }
        $gateway = $natInfo.Subnets[0].GatewayAddress
        $subnet = $natInfo.Subnets[0].AddressPrefix

        $cniConfPath = "$env:ProgramFiles\containerd\cni\conf\0-containerd-nat.conf"
        $cniBinDir = "$env:ProgramFiles\containerd\cni\bin"
        $cniVersion = "0.3.0"

        # get the CNI plugins (binaries)
        mkdir $cniBinDir -Force
        curl.exe -LO https://github.com/microsoft/windows-container-networking/releases/download/v$cniVersion/windows-container-networking-cni-amd64-v$cniVersion.zip
        tar xvf windows-container-networking-cni-amd64-v$cniVersion.zip -C $cniBinDir

        $natConfig = @"
        {
          "cniVersion": "$cniVersion",
          "name": "$networkName",
          "type": "nat",
          "master": "Ethernet",
          "ipam": {
            "subnet": "$subnet",
            "routes": [
              {
                "gateway": "$gateway"
              }
            ]
          },
          "capabilities": {
            "portMappings": true,
            "dns": true
          }
        }
        "@
        Set-Content -Path $cniConfPath -Value $natConfig

        Write-Host "Starting buildkitd."
        Start-Process -NoNewWindow buildkitd -ArgumentList "--containerd-cni-config-path=`"C:\Program Files\containerd\cni\conf\0-containerd-nat.conf`"","--containerd-cni-binary-dir=`"C:\Program Files\containerd\cni\bin`""
        docker buildx
        docker buildx install
        docker buildx version
        docker buildx create --name buildkit-exp --use --driver=remote npipe:////./pipe/buildkitd    
        docker buildx use --default --global buildkit-exp
        docker buildx inspect
        docker buildx ls
        buildctl build --help

        Write-Host "Building image"
        Get-Location
        Get-ChildItem -Force

        bash -c ". scripts/docker.sh && buildDockerImage ${{ matrix.BASE_IMAGE }} ${{ env.BRANCH }} ${{ env.VERSION }};"
        if (-not $?) {throw "Failed to build image."}
        docker images

    - name: Pull base Docker image
      timeout-minutes: 60
      if: success()
      run: |
        if echo "${{ matrix.BASE_IMAGE }}" | grep -q -e 'gentoo'; then
          echo "Pulling MobileRT docker image ${{ env.VERSION }}";
          . scripts/docker.sh && pullDockerImage ptpuscas/mobile_rt:${{ env.VERSION }} &
          pid_docker_pull="$!";
          export BUILD_IMAGE='no';
          wait ${pid_docker_pull};
          echo "Squashing MobileRT docker image: ${{ env.VERSION }}";
          . scripts/docker.sh && squashMobileRTDockerImage ${{ env.VERSION }} &
          pid_docker="$!";
        else
          echo "Pulling docker base image ${{ matrix.BASE_IMAGE }}";
          . scripts/docker.sh && pullDockerImage ${{ matrix.BASE_IMAGE }} &
          pid_docker="$!";
          export BUILD_IMAGE='yes';
        fi
        echo 'Waiting for docker pull.';
        wait ${pid_docker};
        dockerStatus=$?;
        echo "BUILD_IMAGE=${BUILD_IMAGE}" >> "${GITHUB_ENV}";
        docker images -a;
        if [ "${dockerStatus}" != '0' ]; then
          exit 1;
        fi

    - name: Build Docker image
      timeout-minutes: 300
      if: success() && env.BUILD_IMAGE == 'yes'
      run: |
        # shellcheck disable=SC1091
        echo "BASE_IMAGE: ${{ matrix.BASE_IMAGE }}";
        echo "BRANCH: ${{ env.BRANCH }}";
        echo "VERSION: ${{ env.VERSION }}";
        . scripts/docker.sh && buildDockerImage ${{ matrix.BASE_IMAGE }} ${{ env.BRANCH }} ${{ env.VERSION }};

    - name: Build MobileRT
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_LONG) }}
      if: success() && env.BUILD_IMAGE != 'yes'
      run: |
        # shellcheck disable=SC1091
        echo "VERSION: ${{ env.VERSION }}";
        . scripts/docker.sh && compileMobileRTInDockerContainer ${{ env.VERSION }};

    - name: Commit MobileRT Docker image layer
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_SMALL) }}
      if: success() && env.BUILD_IMAGE != 'yes'
      run: |
        # shellcheck disable=SC1091
        echo "VERSION: ${{ env.VERSION }}";
        . scripts/docker.sh && commitMobileRTDockerImage ${{ env.VERSION }};

    - name: Run unit tests
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_SMALL) }}
      if: success()
      run: |
        # shellcheck disable=SC1091
        echo "VERSION: ${{ env.VERSION }}";
        . scripts/docker.sh && executeUnitTestsInDockerContainer ${{ env.VERSION }} --gtest_filter=-*Engine*;

    - name: Run system tests Ray Tracing engine (for code coverage)
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_SMALL) }}
      if: success()
      run: |
        # shellcheck disable=SC1091
        echo "VERSION: ${{ env.VERSION }}";
        . scripts/docker.sh && executeUnitTestsInDockerContainer ${{ env.VERSION }} --gtest_filter=*Engine*;

    - name: Run Dockerfile unit tests
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_SMALL) }}
      if: success()
      working-directory: .
      run: |
        if echo "${{ matrix.BASE_IMAGE }}" | grep -q 'alpine'; then
          sh scripts/test/docker/dockerfile.sh ${{ env.VERSION }} 143; # Expect SIGTERM (15). 128 + 15 = 143
        else
          sh scripts/test/docker/dockerfile.sh ${{ env.VERSION }};
        fi

    - name: Push Docker image
      timeout-minutes: ${{ fromJSON(env.GITHUB_STEP_TIMEOUT_LONG) }}
      if: success() && env.BRANCH == 'master'
      run: |
        # shellcheck disable=SC1091
        echo "VERSION: ${{ env.VERSION }}";
        . scripts/docker.sh && pushMobileRTDockerImage ${{ env.VERSION }};