From 9c1c09b22ab97cc4a641ab895f90e6c51d8a21eb Mon Sep 17 00:00:00 2001 From: Sylvain Fortin Date: Mon, 19 Jun 2023 18:45:27 -0400 Subject: [PATCH] GitLab dynamic pipeline for samples Similar to GitHub Actions workflow, samples jobs are now dynamically generated when pipeline run. When the pipeline run, 'Generate Samples Pipeline' job creates a child pipeline definition directly from SamplesDef.json. Adding a new sample to this file will automatically add it to GitLab pipeline. --- .gitlab/.gitlab-ci.yml | 274 +++----------------------------- .gitlab/Get-SamplesPipeline.ps1 | 118 ++++++++++++++ docs/Samples.md | 40 +++++ 3 files changed, 178 insertions(+), 254 deletions(-) create mode 100644 .gitlab/Get-SamplesPipeline.ps1 create mode 100644 docs/Samples.md diff --git a/.gitlab/.gitlab-ci.yml b/.gitlab/.gitlab-ci.yml index c89da40a3..0f0a8bc30 100644 --- a/.gitlab/.gitlab-ci.yml +++ b/.gitlab/.gitlab-ci.yml @@ -37,6 +37,18 @@ compilation:linux: tags: [square-linux-k8s-compil] image: mcr.microsoft.com/dotnet/sdk:6.0 +generate_samples_pipeline: + stage: build + script: | + pwsh -Command { + Install-Module -Name powershell-yaml -RequiredVersion 0.4.7 -Force + .\.gitlab\Get-SamplesPipeline.ps1 | Tee-Object -FilePath .gitlab/samples.yml + } + artifacts: + paths: + - .gitlab/samples.yml + expire_in: 1 day + ############# # Unit tests ############# @@ -92,263 +104,17 @@ functional_test: expire_in: 1 day ############# -# Linux Sample tests +# Samples ############# -.linux_sample_test: +samples: stage: test - tags: - - square_linux_dind - services: - - docker:20.10.10-dind variables: - DOCKER_HOST: tcp://localhost:2376 - DOCKER_TLS_VERIFY: 1 - DOCKER_CERT_PATH: /certs/client - parallel: - matrix: - - configuration: [debug, release] - framework: [net6.0] - needs: - - "compilation:linux: [release]" - -HelloLinux: - extends: .linux_sample_test - script: - # Install Powershell on Alpine (https://learn.microsoft.com/en-us/powershell/scripting/install/install-alpine?view=powershell-7.2) - - apk add --no-cache - ca-certificates - less - ncurses-terminfo-base - krb5-libs - libgcc - libintl - libssl1.1 - libstdc++ - tzdata - userspace-rcu - zlib - icu-libs - curl - - apk -X https://dl-cdn.alpinelinux.org/alpine/edge/main add --no-cache lttng-ust - - curl -L https://github.com/PowerShell/PowerShell/releases/download/v7.2.11/powershell-7.2.11-linux-alpine-x64.tar.gz -o /tmp/powershell.tar.gz - - mkdir -p /opt/microsoft/powershell/7 - - tar zxf /tmp/powershell.tar.gz -C /opt/microsoft/powershell/7 - - chmod +x /opt/microsoft/powershell/7/pwsh - - ln -s /opt/microsoft/powershell/7/pwsh /usr/bin/pwsh - # Run HelloLinux sample - - pwsh ./RunSample.ps1 -sampleName HelloLinux -configuration $configuration - -############# -# MacOs Sample tests -############# -.macos_sample_test: - stage: test - tags: - - square_mac - parallel: - matrix: - - configuration: [debug, release] - os: [macos-latest] - framework: [net6.0] - needs: - - "compilation:mac: [release]" - artifacts: - when: on_failure - untracked: true - expire_in: 1 day - -HelloXCode: - extends: .macos_sample_test - script: - - pwsh ./RunSample.ps1 -sampleName HelloXCode -configuration $configuration -framework $framework - -XCodeProjects: - extends: .macos_sample_test - script: - - pwsh ./RunSample.ps1 -sampleName XCodeProjects -configuration $configuration -framework $framework - -############# -# Windows Sample tests -############# -.windows_sample_test: - stage: test - parallel: - matrix: - - configuration: [debug, release] - os: [windows-2019, windows-2022] - framework: [net6.0] - rules: - - if: $os == "windows-2019" - variables: - VS_VERSION_SUFFIX: "Vs2019" - - if: $os == "windows-2022" - variables: - VS_VERSION_SUFFIX: "Vs2022" - needs: - - "compilation:windows: [release]" - artifacts: - when: on_failure - untracked: true - expire_in: 1 day - -CompileCommandDatabase: - extends: .windows_sample_test - rules: - - if: $configuration == "debug" - when: on_success - - when: never - script: - - pwsh RunSample.ps1 -sampleName CompileCommandDatabase -configuration $configuration -framework $framework -os $os - -ConfigureOrder: - extends: .windows_sample_test - rules: - - if: $configuration == "release" - when: on_success - - when: never - script: - - pwsh RunSample.ps1 -sampleName ConfigureOrder -configuration $configuration -framework $framework -os $os - -CPPCLI: - extends: .windows_sample_test - rules: - - if: $os == "windows-2019" - when: on_success - - when: never - script: - - pwsh RunSample.ps1 -sampleName CPPCLI -configuration $configuration -framework $framework -os $os - -CSharpHelloWorld_old_frameworks: - extends: .windows_sample_test - rules: - - if: $os == "windows-2019" - when: on_success - - when: never - script: - - pwsh RunSample.ps1 -sampleName CSharpHelloWorld_old_frameworks -configuration $configuration -framework $framework -os $os - -CSharpHelloWorld: - extends: .windows_sample_test - rules: - - if: $os == "windows-2022" - when: on_success - - when: never - variables: - testFolder: samples/CSharpHelloWorld - script: - - pwsh RunSample.ps1 -sampleName "CSharpHelloWorld" -configuration $configuration -framework $framework -os $os - -CSharpImports: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName CSharpImports -configuration $configuration -framework $framework -os $os - -CSharpVsix: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName CSharpVsix -configuration $configuration -framework $framework -os $os - # that one can't be run unfortunately - -CustomBuildStep: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName CustomBuildStep -configuration $configuration -framework $framework -os $os - -CSharpWCF: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName CSharpWCF -configuration $configuration -framework $framework -os $os - # that one can't be run unfortunately - -FastBuildSimpleExecutable: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName FastBuildSimpleExecutable -configuration $configuration -framework $framework -os $os -vsVersionSuffix ${env:VS_VERSION_SUFFIX} - -# Temp disable after Github VM update which updated the NDK and broke it -# HelloAndroid: -# extends: .windows_sample_test -# variables: -# JAVA_HOME: ${Env:JAVA_HOME_11_X64} -# script: -# - pwsh RunSample.ps1 -sampleName HelloAndroid -configuration $configuration -framework $framework -os $os - -HelloClangCl: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName HelloClangCl -configuration $configuration -framework $framework -os $os -vsVersionSuffix ${env:VS_VERSION_SUFFIX} - -HelloEvents: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName HelloEvents -configuration $configuration -framework $framework -os $os -vsVersionSuffix ${env:VS_VERSION_SUFFIX} - -HelloWorld: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName HelloWorld -configuration $configuration -framework $framework -os $os - -NetCore-DotNetCoreFrameworkHelloWorld: - extends: .windows_sample_test - rules: - - if: $os == "windows-2022" - when: on_success - script: - - pwsh RunSample.ps1 -sampleName "NetCore-DotNetCoreFrameworkHelloWorld" -configuration $configuration -framework $framework -os $os - -NetCore-DotNetFrameworkHelloWorld: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName "NetCore-DotNetFrameworkHelloWorld" -configuration $configuration -framework $framework -os $os - -NetCore-DotNetFrameworkHelloWorld_OldFrameworks: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName "NetCore-DotNetFrameworkHelloWorld_OldFrameworks" -configuration $configuration -framework $framework -os $os - -NetCore-DotNetMultiFrameworksHelloWorld: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName "NetCore-DotNetMultiFrameworksHelloWorld" -configuration $configuration -framework $framework -os $os - -NetCore-DotNetOSMultiFrameworksHelloWorld: - extends: .windows_sample_test - rules: - - if: $os == "windows-2022" - when: on_success - - when: never - variables: - testFolder: samples\NetCore\DotNetOSMultiFrameworksHelloWorld - script: - - pwsh RunSample.ps1 -sampleName "NetCore-DotNetOSMultiFrameworksHelloWorld" -configuration $configuration -framework $framework -os $os - -PackageReferences: - extends: .windows_sample_test - script: - - pwsh RunSample.ps1 -sampleName "PackageReferences" -configuration $configuration -framework $framework -os $os - -QTFileCustomBuild: - extends: .windows_sample_test - before_script: - - choco install python3 --version 3.10.6 --side-by-side -y --no-progress - script: - - pwsh RunSample.ps1 -sampleName "QTFileCustomBuild" -configuration $configuration -framework $framework -os $os - -SimpleExeLibDependency: - extends: .windows_sample_test - rules: - - if: $configuration == "debug" - when: on_success - - when: never - script: - - pwsh RunSample.ps1 -sampleName "SimpleExeLibDependency" -configuration $configuration -framework $framework -os $os - -vcpkg: - extends: .windows_sample_test - variables: - testFolder: samples/vcpkg - script: - - pwsh RunSample.ps1 -sampleName vcpkg -configuration $configuration -framework $framework -os $os -vsVersionSuffix ${env:VS_VERSION_SUFFIX} + PARENT_PIPELINE_ID: $CI_PIPELINE_ID + trigger: + include: + - artifact: .gitlab/samples.yml + job: generate_samples_pipeline + strategy: depend ############# # Deploy stage diff --git a/.gitlab/Get-SamplesPipeline.ps1 b/.gitlab/Get-SamplesPipeline.ps1 new file mode 100644 index 000000000..404fae2b4 --- /dev/null +++ b/.gitlab/Get-SamplesPipeline.ps1 @@ -0,0 +1,118 @@ +<# +.SYNOPSIS +Gets the child pipeline definition used to execute samples jobs on GitLab pipeline. + +.DESCRIPTION +Transform the content of SamplesDef.json into a GitLab pipeline Yaml representation that can be used to execute samples job. + +.OUTPUTS +System.String + Get-SamplesPipeline returns the definition of the samples pipeline formatted as Yaml. +#> + +# Load samples definitions. +$samplesDef = Get-Content -Raw -Path 'SamplesDef.json' | ConvertFrom-Json + +# Transform into a hash table with an entry for each samples. +$samplesPipeline = @{} +foreach ($sample in $samplesDef.Samples) +{ + if ($sample.CIs.Contains('gitlab')) + { + foreach ($os in $sample.OSs) + { + foreach ($framework in $sample.Frameworks) + { + foreach ($configuration in $sample.Configurations) + { + # Array of commands to execute for a sample. + $script = @() + + # Compose properties specific to runner os. + switch -Wildcard ( $os ) + { + 'linux' + { + $osProperties = @{ + tags = @( 'square_linux_dind' ) + services = @( 'docker:20.10.10-dind' ) + variables = @{ + DOCKER_HOST = 'tcp://localhost:2376' + DOCKER_TLS_VERIFY = 1 + DOCKER_CERT_PATH = '/certs/client' + } + } + $osCompilationName = 'linux' + + # Install Powershell on Alpine (https://learn.microsoft.com/en-us/powershell/scripting/install/install-alpine?view=powershell-7.2) + # Required to run RunSample.ps1. + $script += @( + 'apk add --no-cache ca-certificates less ncurses-terminfo-base krb5-libs libgcc libintl libssl1.1 libstdc++ tzdata userspace-rcu zlib icu-libs curl' + 'apk -X https://dl-cdn.alpinelinux.org/alpine/edge/main add --no-cache lttng-ust' + 'curl -L https://github.com/PowerShell/PowerShell/releases/download/v7.2.11/powershell-7.2.11-linux-alpine-x64.tar.gz -o /tmp/powershell.tar.gz' + 'mkdir -p /opt/microsoft/powershell/7' + 'tar zxf /tmp/powershell.tar.gz -C /opt/microsoft/powershell/7' + 'chmod +x /opt/microsoft/powershell/7/pwsh' + 'ln -s /opt/microsoft/powershell/7/pwsh /usr/bin/pwsh' + ) + } + 'macos' + { + $osProperties = @{ + tags = @( 'square_mac' ) + } + $osCompilationName = 'mac' + } + 'windows*' + { + $osProperties = @{ + tags = @( 'square_windows' ) + } + $osCompilationName = 'windows' + $vsVersionSuffix = switch ($os) + { + 'windows-2019' { 'Vs2019' } + 'windows-2022' { 'Vs2022' } + } + } + } + + + # Compose properties specific to a sample. + # These should be exceptions since idealy these commands should be in SamplesDef.json. + switch ($sample.Name) + { + 'QTFileCustomBuild' + { + $script += 'choco install python3 --version 3.10.6 --side-by-side -y --no-progress' + } + } + + + $script += "pwsh ./RunSample.ps1 -sampleName ""$($sample.Name)"" -configuration $configuration -framework $framework -os $os -vsVersionSuffix $vsVersionSuffix" + + # Merge sample properties into a single hash table. + $sampleJob = $osProperties + @{ + artifacts = [PSCustomObject]@{ + when = 'on_failure' + untracked = $true + expire_in = '1 day' + } + needs = [PSCustomObject]@{ + pipeline = '$PARENT_PIPELINE_ID' + job = "compilation:${osCompilationName}: [release]" + } + script = $script + } + + # Add sample to pipeline hash table. + $samplesPipeline.Add("$($sample.Name): [$os, $framework, $configuration]", $sampleJob) + } + } + } + } +} + +# Output samples pipeline as Yaml. +# Enable dynamically specifying samples jobs as a child pipeline. +$samplesPipeline | ConvertTo-Yaml -Options DisableAliases diff --git a/docs/Samples.md b/docs/Samples.md new file mode 100644 index 000000000..ebc80e4fc --- /dev/null +++ b/docs/Samples.md @@ -0,0 +1,40 @@ +# Samples + +Sharpmake repository contains many samples that showcase various features. If you also consider the various CI systems used by Sharpmake, it can be tedious to add or modify a sample in these systems. To simplify this maintenance, sample jobs declarations are data driven from the file `SamplesDef.json` and are dynamically injected into CI pipelines. This file is also used by `RunSample.ps1` script to execute samples. + +## Samples definition format + +Here an example for the sample HelloWorld in `SamplesDef.json`: + +```json +{ + "Name": "HelloWorld", + "CIs": [ "github", "gitlab" ], + "OSs": [ "windows-2019", "windows-2022" ], + "Frameworks": [ "net6.0" ], + "Configurations": [ "debug", "release" ], + "TestFolder": "samples/HelloWorld", + "Commands": + [ + "./RunSharpmake.ps1 -workingDirectory {testFolder} -sharpmakeFile \"HelloWorld.sharpmake.cs\" -framework {framework}", + "./Compile.ps1 -slnOrPrjFile \"helloworld_vs2019_win32.sln\" -configuration {configuration} -platform \"Win32\" -WorkingDirectory \"{testFolder}/projects\" -VsVersion {os} -compiler MsBuild", + "&'./{testFolder}/projects/output/win32/{configuration}/helloWorld.exe'", + "./Compile.ps1 -slnOrPrjFile \"helloworld_vs2019_win64.sln\" -configuration {configuration} -platform \"x64\" -WorkingDirectory \"{testFolder}/projects\" -VsVersion {os} -compiler MsBuild", + "&'./{testFolder}/projects/output/win64/{configuration}/helloWorld.exe'" + ] +} +``` + +Here the description for each properties: + +- *Name*: Name of the sample. +- *CIs*: CI systems where the sample can be executed. Valid values: "github" and "gitlab". An empty array here will completely disable the sample on CI systems. +- *OSs*: Operating systems where can be executed. Valid values: "linux", "macos", "windows-2019" and "windows-2022". +- *Frameworks*: .NET frameworks used by Sharpmake executable. Currently only "net6.0" is supported. +- *Configuration*: Configurations that the sample support. Valid values: "debug" and "release". +- *TestFolder*: Base directory of the sample files. +- *Commands*: List of commands to execute for the sample. Note that these commands are executed with a Powershell Invoke-Expression cmdlet. So the command can be any valid Powershell expression. This also mean that they share the same context. Setting a variable in one command makes it available to subsequent commands. + +## Adding a sample + +If you need to add a new sample. Adding a new entry in `SamplesDef.json` should be the only thing you need to do. Once committed, CI systems should dynamically add a job for the new sample.