From 6b17e69d1bdbb8d5781b3311ffa4919c579322ec Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Wed, 6 Jan 2021 18:18:58 -0500 Subject: [PATCH 01/33] [Xcode12.3][VSTS] Move away from Jenkins to VSTS pipelines for xamarin-macios. (#10302) (#10346) * [VSTS] Move away from Jenkins to VSTS pipelines for xamarin-macios. (#10302) Initial implementation of the projects CI in yaml to be uses with VSTS. The port is not complete as there are some small issues to address, the following are the known issues: * https://github.com/xamarin/maccore/issues/2349 * https://github.com/xamarin/maccore/issues/2350 * https://github.com/xamarin/xamarin-macios/issues/10299 * https://github.com/xamarin/xamarin-macios/issues/10298 * https://github.com/xamarin/xamarin-macios/issues/10300 * https://github.com/xamarin/maccore/issues/2351 Nevertheless the CI already compiles the project, creates the pkgs and nugets and publishes them so that we can create insertions. Co-authored-by: Mike Bond Co-authored-by: cadsit Co-authored-by: Rolf Bjarne Kvinge * Provision netcore 5. * [CI][VSTS] Add trigger for xcode branches. fixes: https://github.com/xamarin/maccore/issues/2355 Co-authored-by: Mike Bond Co-authored-by: cadsit Co-authored-by: Rolf Bjarne Kvinge --- .gitignore | 3 +- jenkins/Jenkinsfile | 5 +- jenkins/build-nugets.sh | 9 - jenkins/build-package.sh | 7 - tools/devops/Makefile | 19 + tools/devops/automation/VSMacVersion.ps1 | 3 + tools/devops/automation/build-pipeline.yml | 194 ++++++ .../automation/scripts/GitHub.Tests.ps1 | 568 ++++++++++++++++++ tools/devops/automation/scripts/GitHub.psm1 | 538 +++++++++++++++++ .../automation/scripts/MLaunch.Tests.ps1 | 49 ++ tools/devops/automation/scripts/MLaunch.psm1 | 41 ++ tools/devops/automation/scripts/Makefile | 5 + .../automation/scripts/System.Tests.ps1 | 65 ++ tools/devops/automation/scripts/System.psm1 | 231 +++++++ .../devops/automation/scripts/VSTS.Tests.ps1 | 181 ++++++ tools/devops/automation/scripts/VSTS.psm1 | 181 ++++++ .../automation/scripts/bash/build-nugets.sh | 20 + .../automation/scripts/bash/clean-bot.sh | 114 ++++ .../automation/scripts/bash}/productsign.sh | 0 .../templates/agent-pool-selector.yml | 65 ++ .../templates/common/download-artifacts.yml | 42 ++ .../templates/common/publish-html.yml | 58 ++ .../templates/common/upload-vsdrops.yml | 27 + .../templates/common/upload-vsts-tests.yml | 25 + .../automation/templates/devices/build.yml | 269 +++++++++ .../automation/templates/devices/stage.yml | 128 ++++ .../templates/governance-checks.yml | 67 +++ .../devops/automation/templates/mac-tests.yml | 51 ++ .../automation/templates/packages/build.yml | 440 ++++++++++++++ .../templates/packages/configure.yml | 83 +++ .../templates/packages/download-artifacts.yml | 38 ++ .../templates/packages/publish-html.yml | 79 +++ .../automation/templates/packages/stage.yml | 149 +++++ .../templates/packages/upload-azure.yml | 183 ++++++ tools/devops/build-provisioning.csx.in | 17 + tools/devops/device-tests-provisioning.csx.in | 1 + tools/devops/provision-brew-packages.csx | 11 + tools/devops/provision-shared.csx | 88 +++ tools/devops/provision-xcode.csx | 55 +- tools/devops/python-dependencies.txt | 1 + 40 files changed, 4038 insertions(+), 72 deletions(-) delete mode 100755 jenkins/build-nugets.sh delete mode 100755 jenkins/build-package.sh create mode 100644 tools/devops/automation/VSMacVersion.ps1 create mode 100644 tools/devops/automation/build-pipeline.yml create mode 100644 tools/devops/automation/scripts/GitHub.Tests.ps1 create mode 100644 tools/devops/automation/scripts/GitHub.psm1 create mode 100644 tools/devops/automation/scripts/MLaunch.Tests.ps1 create mode 100644 tools/devops/automation/scripts/MLaunch.psm1 create mode 100644 tools/devops/automation/scripts/Makefile create mode 100644 tools/devops/automation/scripts/System.Tests.ps1 create mode 100644 tools/devops/automation/scripts/System.psm1 create mode 100644 tools/devops/automation/scripts/VSTS.Tests.ps1 create mode 100644 tools/devops/automation/scripts/VSTS.psm1 create mode 100755 tools/devops/automation/scripts/bash/build-nugets.sh create mode 100755 tools/devops/automation/scripts/bash/clean-bot.sh rename {jenkins => tools/devops/automation/scripts/bash}/productsign.sh (100%) create mode 100644 tools/devops/automation/templates/agent-pool-selector.yml create mode 100644 tools/devops/automation/templates/common/download-artifacts.yml create mode 100644 tools/devops/automation/templates/common/publish-html.yml create mode 100644 tools/devops/automation/templates/common/upload-vsdrops.yml create mode 100644 tools/devops/automation/templates/common/upload-vsts-tests.yml create mode 100644 tools/devops/automation/templates/devices/build.yml create mode 100644 tools/devops/automation/templates/devices/stage.yml create mode 100644 tools/devops/automation/templates/governance-checks.yml create mode 100644 tools/devops/automation/templates/mac-tests.yml create mode 100644 tools/devops/automation/templates/packages/build.yml create mode 100644 tools/devops/automation/templates/packages/configure.yml create mode 100644 tools/devops/automation/templates/packages/download-artifacts.yml create mode 100644 tools/devops/automation/templates/packages/publish-html.yml create mode 100644 tools/devops/automation/templates/packages/stage.yml create mode 100644 tools/devops/automation/templates/packages/upload-azure.yml create mode 100644 tools/devops/build-provisioning.csx.in create mode 100644 tools/devops/provision-brew-packages.csx create mode 100644 tools/devops/provision-shared.csx create mode 100644 tools/devops/python-dependencies.txt diff --git a/.gitignore b/.gitignore index dcbcb19e9e..db2745763a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ _build *.pdb bin obj -packages +./packages ~.pmcs* .DS_Store jenkins-results @@ -33,6 +33,7 @@ tests/bcl-test/SystemXunit.csproj global.json .idea device-tests-provisioning.csx +build-provisioning.csx mono_crash.*.json *.binlog .vscode diff --git a/jenkins/Jenkinsfile b/jenkins/Jenkinsfile index 8a0d7633ee..842cd3c9a8 100644 --- a/jenkins/Jenkinsfile +++ b/jenkins/Jenkinsfile @@ -454,9 +454,12 @@ timestamps { currentStage = "${STAGE_NAME}" echo ("Building on ${env.NODE_NAME}") sh ("env | sort") // Print out environment for debug purposes + + branchName = env.BRANCH_NAME + echo ("Branch name: ${branchName}") + scmVars = checkout scm isPr = (env.CHANGE_ID && !env.CHANGE_ID.empty ? true : false) - branchName = env.BRANCH_NAME if (isPr) { gitHash = sh (script: "git log -1 --pretty=%H refs/remotes/origin/${env.BRANCH_NAME}", returnStdout: true).trim () } else { diff --git a/jenkins/build-nugets.sh b/jenkins/build-nugets.sh deleted file mode 100755 index 351d989dbb..0000000000 --- a/jenkins/build-nugets.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -ex - -cd "$(dirname "${BASH_SOURCE[0]}")/.." - -DOTNET_NUPKG_DIR=$(make -C jenkins print-abspath-variable VARIABLE=DOTNET_NUPKG_DIR | grep "^DOTNET_NUPKG_DIR=" | sed -e 's/^DOTNET_NUPKG_DIR=//') - -mkdir -p ../package/ -rm -f ../package/*.nupkg -cp -c "$DOTNET_NUPKG_DIR"/*.nupkg ../package/ diff --git a/jenkins/build-package.sh b/jenkins/build-package.sh deleted file mode 100755 index 150b8686a7..0000000000 --- a/jenkins/build-package.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -ex - -cd "$(dirname "${BASH_SOURCE[0]}")/.." -#WORKSPACE=$(pwd) - -rm -Rf ../package -make package diff --git a/tools/devops/Makefile b/tools/devops/Makefile index 3401e86c41..f62a50527e 100644 --- a/tools/devops/Makefile +++ b/tools/devops/Makefile @@ -7,4 +7,23 @@ device-tests-provisioning.csx: device-tests-provisioning.csx.in Makefile $(TOP)/ -e 's#@XI_PACKAGE@#$(XI_PACKAGE)#g' \ -e 's#@MONO_PACKAGE@#$(MIN_MONO_URL)#g' \ -e 's#@VS_PACKAGE@#$(MIN_VISUAL_STUDIO_URL)#g' \ + -e 's#@DOTNET_VERSION@#$(DOTNET_VERSION)#g' \ $< > $@; + +build-provisioning.csx: build-provisioning.csx.in Makefile $(TOP)/Make.config + $(Q_GEN) sed \ + -e 's#@XCODE_XIP_NAME@#$(notdir $(XCODE_URL))#g' \ + -e 's#@MONO_PACKAGE@#$(MIN_MONO_URL)#g' \ + -e 's#@VS_PACKAGE@#$(MIN_VISUAL_STUDIO_URL)#g' \ + -e 's#@MIN_SHARPIE_URL@#$(MIN_SHARPIE_URL)#g' \ + -e 's#@DOTNET_VERSION@#$(DOTNET_VERSION)#g' \ + -e 's#@DOTNET5_VERSION@#$(DOTNET5_VERSION)#g' \ + $< > $@; + +all check: + shellcheck *.sh + +print-abspath-variable: + @echo $(VARIABLE)=$(abspath $($(VARIABLE))) + +provisioning: build-provisioning.csx device-tests-provisioning.csx diff --git a/tools/devops/automation/VSMacVersion.ps1 b/tools/devops/automation/VSMacVersion.ps1 new file mode 100644 index 0000000000..9848b419b9 --- /dev/null +++ b/tools/devops/automation/VSMacVersion.ps1 @@ -0,0 +1,3 @@ +$vsInstallPath = "/Applications/Visual Studio.app" +$version = /usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "${vsInstallPath}/Contents/Info.plist" 2> $null +Write-Host $version diff --git a/tools/devops/automation/build-pipeline.yml b/tools/devops/automation/build-pipeline.yml new file mode 100644 index 0000000000..9e9b87650a --- /dev/null +++ b/tools/devops/automation/build-pipeline.yml @@ -0,0 +1,194 @@ +# YAML pipeline build definition +# https://devdiv.visualstudio.com/DevDiv/_apps/hub/ms.vss-ciworkflow.build-ci-hub?_a=edit-build-definition&id=13760&view=Tab_Tasks +# +# YAML build pipeline based on the Jenkins multi-stage (main branch) build workflow +# https://jenkins.internalx.com/view/Xamarin.MaciOS/job/macios/job/main/ +# https://jenkins.internalx.com/view/Xamarin.MaciOS/job/macios/configure +# +parameters: +- name: runTests + type: boolean + default: true + +- name: runDeviceTests + type: boolean + default: true + +resources: + repositories: + - repository: self + checkoutOptions: + submodules: true + + - repository: templates + type: github + name: xamarin/yaml-templates + ref: refs/heads/main + endpoint: xamarin + + - repository: maccore + type: github + name: xamarin/maccore + ref: refs/heads/main + endpoint: xamarin + + - repository: release-scripts + type: github + name: xamarin/release-scripts + ref: refs/heads/sign-and-notarized + endpoint: xamarin + +variables: +- group: xamops-azdev-secrets +- group: Xamarin-Secrets +- group: Xamarin Signing +- group: Xamarin Release +- group: Xamarin Notarization +- group: XamarinCompatLab # provisionator-uri setting +- name: GitHub.Token # Override the GitHub.Token setting defined in the Xamarin Release group + value: $(github--pat--vs-mobiletools-engineering-service2) # Use a token dedicated to critical production workflows and help avoid GitHub throttling +- name: AzDoBuildAccess.Token + value: $(pat--xamarinc--build-access) +- name: system.debug + value: true +- name: SigningKeychain + value: "builder.keychain" +- name: OSX_KEYCHAIN_PASS # UNDONE: Override the OSX_KEYCHAIN_PASS to use same password as used by the iOS mac pool machines + value: $(pass--lab--mac--builder--keychain) +- name: VSDropsPrefix + value: 'https://vsdrop.corp.microsoft.com/file/v1/xamarin-macios/device-tests' +- name: USE_TCP_TUNNEL # Needed to ensure that devices uses the usb cable to communicate with the devices to run the tests. + value: true + +trigger: + branches: + include: + - '*' + +pr: + autoCancel: true + branches: + include: + - main + - d16-* + - xcode* + +stages: + +- stage: governance_checks + displayName: 'Governance Checks' + dependsOn: [] + jobs: + - job: governance + displayName: 'Governance Checks' + pool: + vmImage: windows-latest + steps: + - template: templates/governance-checks.yml + +- stage: build_packages + displayName: 'Build' + dependsOn: [] + jobs: + - template: templates/packages/stage.yml + parameters: + vsdropsPrefix: ${{ variables.vsdropsPrefix }} + runTests: ${{ parameters.runTests }} + runDeviceTests: ${{ parameters.runDeviceTests }} + + # ideally we would use a matrix here, like: + # - job: device_tests + # displayName: 'Device tests' + # timeoutInMinutes: 1000 + # + # strategy: + # matrix: + # iOS32: # TODO: This bots should be moved to the ddfun pool + # deviceDemands: 'xismoke-32' + # testsLabels: '--label=run-ios-32-tests,run-non-monotouch-tests,run-monotouch-tests,run-mscorlib-tests' + # poolName: 'VSEng-Xamarin-QA' + # iOS64: + # deviceDemands: 'ios' + # testsLabels: '--label=run-ios-64-tests,run-non-monotouch-tests,run-monotouch-tests,run-mscorlib-tests' + # poolName: 'VSEng-Xamarin-Mac-Devices' + # tvOS: + # deviceDemands: 'tvos' + # testsLabels: '--label=run-tvos-tests,run-non-monotouch-tests,run-monotouch-tests,run-mscorlib-tests' + # poolName: 'VSEng-Xamarin-Mac-Devices' + # + # pool: + # name: $(poolName) + # demands: $(deviceDemands) + # workspace: + # clean: all + # + # steps: + # - template: templates/device-tests.yml + # + # Unfortunally, variable expansion will not happen on the right time, and will result in an agent error, to fix that + # we use a template for the test and we set each of the jobs. Not ideal, but is only a 3 jobs matrix + +- template: templates/devices/stage.yml + parameters: + devicePrefix: 'iOS32b' + execute: 'runDevice32b' + stageName: 'iOS32b Device Tests' + iOSDevicePool: 'VSEng-Xamarin-QA' + useXamarinStorage: False + testsLabels: '--label=run-ios-32-tests,run-non-monotouch-tests,run-monotouch-tests,run-mscorlib-tests' + statusContext: 'VSTS: device tests iOS32b' + iOSDeviceDemand: 'xismoke-32' + vsdropsPrefix: ${{ variables.vsdropsPrefix }} + keyringPass: $(xma-password) + +- template: templates/devices/stage.yml + parameters: + devicePrefix: 'iOS64' + execute: 'runDevice64b' + stageName: 'iOS64 Device Tests' + iOSDevicePool: 'VSEng-Xamarin-Mac-Devices' + useXamarinStorage: False + testsLabels: '--label=run-ios-64-tests,run-non-monotouch-tests,run-monotouch-tests,run-mscorlib-tests' + statusContext: 'VSTS: device tests iOS' + iOSDeviceDemand: 'ios' + vsdropsPrefix: ${{ variables.vsdropsPrefix }} + keyringPass: $(xma-password) + +- template: templates/devices/stage.yml + parameters: + devicePrefix: 'tvOS' + execute: 'runDeviceTv' + stageName: 'tvOS Device Tests' + iOSDevicePool: 'VSEng-Xamarin-Mac-Devices' + useXamarinStorage: False + testsLabels: '--label=run-tvos-tests,run-non-monotouch-tests,run-monotouch-tests,run-mscorlib-tests' + statusContext: 'VSTS: device tests tvOS' + iOSDeviceDemand: 'tvos' + vsdropsPrefix: ${{ variables.vsdropsPrefix }} + keyringPass: $(xma-password) + +- template: templates/mac-tests.yml + parameters: + stageName: 'Mac Mojave Tests' + macPool: 'Hosted Mac Internal Mojave' + +- template: templates/mac-tests.yml + parameters: + stageName: 'Mac High Sierra Tests' + macPool: 'Hosted Mac Internal' + +# TODO: Not the real step +- stage: sample_testing + displayName: 'Sample testing' + dependsOn: + - build_packages + condition: and(succeeded(), contains (stageDependencies.build_packages.build.outputs['configuration.RunSampleTests'], 'True')) + jobs: + - job: sample_testing + pool: + vmImage: ubuntu-latest + steps: + # TODO: do parse labels + - bash: | + echo "Samples!" + displayName: 'Sample testing' diff --git a/tools/devops/automation/scripts/GitHub.Tests.ps1 b/tools/devops/automation/scripts/GitHub.Tests.ps1 new file mode 100644 index 0000000000..ee8cb4abc2 --- /dev/null +++ b/tools/devops/automation/scripts/GitHub.Tests.ps1 @@ -0,0 +1,568 @@ +<# +Github interaction unit tests. +#> + +Import-Module ./GitHub -Force + +Describe 'Set-GitHubStatus' { + Context 'with all env variables present' { + + It 'calls the rest method succesfully' { + # set the required enviroments in the context + $envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "SYSTEM_JOBNAME" = "SYSTEM_JOBNAME"; + "SYSTEM_STAGEDISPLAYNAME" = "SYSTEM_STAGEDISPLAYNAME" + "BUILD_REVISION" = "BUILD_REVISION"; + "GITHUB_TOKEN" = "GITHUB_TOKEN" + } + + $envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Set-Item -Path "Env:$key" -Value $_.Value + } + + Mock Invoke-RestMethod { + return @{"status"=200;} + } + $status = "error" + $context = "My context" + $description = "Testing Status API" + + Set-GitHubStatus -Status $status -Description $description -Context $context + + # assert the call and compare the expected parameters to the received ones + Assert-MockCalled -CommandName Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { + # validate each of the params and the payload + if ($Uri -ne "https://api.github.com/repos/xamarin/xamarin-macios/statuses/BUILD_REVISION") { + return $False + } + if ($Headers.Authorization -ne ("token {0}" -f $envVariables["GITHUB_TOKEN"])) { + return $False + } + if ($Method -ne "POST") { + return $False + } + if ($ContentType -ne "application/json") { + return $False + } + # compare the payload + $bodyObj = ConvertFrom-Json $Body + if ($bodyObj.state -ne $status) { + return $False + } + + if ($bodyObj.context -ne $context) { + return $False + } + + if ($bodyObj.description -ne $description) { + return $False + } + + return $True + } + } + + It 'calls the rest method with an error' { + # set the required enviroments in the context + $envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "SYSTEM_JOBNAME" = "SYSTEM_JOBNAME"; + "SYSTEM_STAGEDISPLAYNAME" = "SYSTEM_STAGEDISPLAYNAME" + "BUILD_REVISION" = "BUILD_REVISION"; + "GITHUB_TOKEN" = "GITHUB_TOKEN" + } + + $envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Set-Item -Path "Env:$key" -Value $_.Value + } + Mock Invoke-RestMethod { + throw [System.Exception]::("Test") + } + #set env vars + { Set-GitHubStatus -Status $status -Description $description -Context $context } | Should -Throw + } + } + Context 'without an env var' { + It 'failed calling the rest method' { + Mock Invoke-RestMethod { + return @{"status"=200;} + } + + # clear the env vars + $envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "SYSTEM_JOBNAME" = "SYSTEM_JOBNAME"; + "SYSTEM_STAGEDISPLAYNAME" = "SYSTEM_STAGEDISPLAYNAME" + "BUILD_REVISION" = "BUILD_REVISION"; + "GITHUB_TOKEN" = "GITHUB_TOKEN" + } + $envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Remove-Item -Path "Env:$key" + } + + $status = "error" + $context = "My context" + $description = "Testing Status API" + + { Set-GitHubStatus -Status $status -Description $description -Context $context } | Should -Throw + Assert-MockCalled -CommandName Invoke-RestMethod -Times 0 -Scope It + } + } +} + +Describe 'New-GitHubComment' { + Context 'with all env variables present' { + + BeforeAll { + $Script:envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "SYSTEM_JOBNAME" = "SYSTEM_JOBNAME"; + "SYSTEM_STAGEDISPLAYNAME" = "SYSTEM_STAGEDISPLAYNAME" + "BUILD_REVISION" = "BUILD_REVISION"; + "GITHUB_TOKEN" = "GITHUB_TOKEN"; + "BUILD_DEFINITIONNAME" = "BUILD_DEFINITIONNAME" + } + + $Script:envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Set-Item -Path "Env:$key" -Value $_.Value + } + } + + It 'calls the method succesfully' { + Mock Invoke-RestMethod { + return @{"status"=200;} + } + $header = "The header" + $description = "Testing Comments API" + $message = "This is a test" + $emoji = ":tada:" + + New-GitHubComment -Header $header -Description $description -Message $message -Emoji $emoji + + # assert the call and compare the expected parameters to the received ones + Assert-MockCalled -CommandName Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { + # validate each of the params and the payload + if ($Uri -ne "https://api.github.com/repos/xamarin/xamarin-macios/commits/BUILD_REVISION/comments") { + return $False + } + if ($Headers.Authorization -ne ("token {0}" -f $envVariables["GITHUB_TOKEN"])) { + return $False + } + if ($Method -ne "POST") { + return $False + } + if ($ContentType -ne "application/json") { + return $False + } + # compare the payload + $bodyObj = ConvertFrom-Json $Body + $body = $bodyObj.body + if ($bodyObj.body -eq $null) { + return $False + } + + return $True + } + + } + + It 'calls the method with an error and throws' { + Mock Invoke-RestMethod { + throw [System.Exception]::("Test") + } + $header = "The header" + $description = "Testing Comments API" + $message = "This is a test" + $emoji = ":tada:" + + { New-GitHubComment -Header $header -Description $description -Message $message -Emoji $emoji } | Should -Throw + } + + } + Context 'without an env variable' { + BeforeAll { + # clear the env vars + $envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "SYSTEM_JOBNAME" = "SYSTEM_JOBNAME"; + "SYSTEM_STAGEDISPLAYNAME" = "SYSTEM_STAGEDISPLAYNAME" + "BUILD_REVISION" = "BUILD_REVISION"; + "GITHUB_TOKEN" = "GITHUB_TOKEN"; + "BUILD_DEFINITIONNAME" = "BUILD_DEFINITIONNAME" + } + $envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Remove-Item -Path "Env:$key" + } + } + It 'throws and error' { + + Mock Invoke-RestMethod { + return @{"status"=200;} + } + + $header = "The header" + $description = "Testing Comments API" + $message = "This is a test" + $emoji = ":tada:" + + { New-GitHubComment -Header $header -Description $description -Message $message -Emoji $emoji } | Should -Throw + Assert-MockCalled -CommandName Invoke-RestMethod -Times 0 -Scope It + } + } +} + +Describe New-GitHubCommentFromFile { + Context 'file present' { + + BeforeAll { + $Script:tempPath = [System.IO.Path]::GetTempFileName() + $Script:message = "Test message in a bottle" + Set-Content -Path $Script:tempPath -Value $message + } + + AfterAll { + Remove-Item -Path $Script:tempPath + } + + It 'calls the inner method' { + Mock New-GitHubComment + + $header = "My test" + $description = "Le description" + $emoji = ":tada:" + + New-GitHubCommentFromFile -Header $header -Description $description -Path $Script:tempPath -Emoji $emoji + + #just assert that the method was called with the expected values + Assert-MockCalled -CommandName New-GitHubComment -Times 1 -Scope It -ParameterFilter { + if ($Header -ne $header) { + return $False + } + if ($Description -ne $description) { + return $False + } + if ($Emoji -ne $emoji) { + return $False + } + if ($Message -like $Script:message) { + return $False + } + return $True + } + } + } + Context 'file missing' { + It 'throws and error' { + + $header = "My test" + $description = "Le description" + $emoji = ":tada:" + + { New-GitHubCommentFromFile -Header $header -Description $description -Path "missing/path" -Emoji $emoji } | Should -Throw + } + } +} + +Describe 'New-GitHubSummaryComment' { + Context 'all present variables' { + + BeforeAll { + # clear the env vars + $Script:envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "SYSTEM_JOBNAME" = "SYSTEM_JOBNAME"; + "SYSTEM_STAGEDISPLAYNAME" = "SYSTEM_STAGEDISPLAYNAME" + "BUILD_REVISION" = "BUILD_REVISION"; + "GITHUB_TOKEN" = "GITHUB_TOKEN"; + "BUILD_DEFINITIONNAME" = "BUILD_DEFINITIONNAME"; + "SYSTEM_DEFAULTWORKINGDIRECTORY" = "SYSTEM_DEFAULTWORKINGDIRECTORY" + } + $Script:envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Set-Item -Path "Env:$key" -Value $_.Value + } + $Script:context = "Testing" + + $Script:tempPath = [System.IO.Path]::GetTempFileName() + $Script:message = "Test message in a bottle" + Set-Content -Path $Script:tempPath -Value $message + } + + AfterAll { + Remove-Item -Path $Script:tempPath + } + + It 'calls rest methods on a completed and succesful test run' { + Mock Set-GitHubStatus + Mock New-GitHubCommentFromFile + Mock Test-Path { return $true } + + # set job as a success + Set-Item -Path "Env:TESTS_JOBSTATUS" -Value "Succeeded" + + New-GitHubSummaryComment -Context $Script:context -TestSummaryPath $Script:tempPath + + # assert rest calls + Assert-MockCalled -CommandName Set-GitHubStatus -Times 1 -Scope It -ParameterFilter { + if ($Status -ne "success") { + return $False + } + + if ($Context -ne $Script:context) { + return $False + } + + if ($Description -ne "Device tests passed on $Script:context.") { + return $False + } + + return $True + } + + Assert-MockCalled -CommandName New-GitHubCommentFromFile -Times 1 -Scope It -ParameterFilter { + if (-not ($Header -like "Device tests passed on $Script:context*")) { + return $False + } + + if (-not ($Description -like "Device tests passed on $Script:context*")) { + return $False + } + + if ($Path -ne $Script:tempPath) { + return $False + } + + return $True + } + } + + It 'calls rest methods on a completed failed test run' { + Mock Set-GitHubStatus + Mock New-GitHubCommentFromFile + Mock Test-Path { return $true } + + Set-Item -Path "Env:TESTS_JOBSTATUS" -Value "Failed" + + New-GitHubSummaryComment -Context $Script:context -TestSummaryPath $Script:tempPath + + Assert-MockCalled -CommandName Set-GitHubStatus -Times 1 -Scope It -ParameterFilter { + if ($Status -ne "failure") { + return $False + } + + if ($Context -ne $Script:context) { + return $False + } + + if ($Description -ne "Device tests failed on $Script:context.") { + return $False + } + + return $True + } + + Assert-MockCalled -CommandName New-GitHubCommentFromFile -Times 1 -Scope It -ParameterFilter { + if (-not ($Header -like "Device tests failed on $Script:context*")) { + return $False + } + + if (-not ($Description -like "Device tests failed on $Script:context*")) { + return $False + } + + if ($Path -ne $Script:tempPath) { + return $False + } + + return $True + } + } + + It 'calls rest methods on a failed test run (TestSummay.md missing)' { + Mock Set-GitHubStatus + Mock New-GitHubComment + Mock Test-Path { return $false} + + Set-Item -Path "Env:TESTS_JOBSTATUS" -Value "Failed" + + New-GitHubSummaryComment -Context $Script:context -TestSummaryPath $Script:tempPath + + Assert-MockCalled -CommandName Set-GitHubStatus -Times 1 -Scope It -ParameterFilter { + if ($Status -ne "failure") { + return $False + } + + if ($Context -ne $Script:context) { + return $False + } + + if (-not ($Description -like "Tests failed catastrophically on $Script:context (no summary found).")) { + return $False + } + + return $True + } + + Assert-MockCalled -CommandName New-GitHubComment -Times 1 -Scope It -ParameterFilter { + if ($Header -ne "Tests failed catastrophically on $Script:context (no summary found).") { + return $False + } + + if (-not ($Description -like "Result file $Script:tempPath not found.*")) { + return $False + } + + return $True + } + } + + } + + Context 'missing variables' { + + BeforeAll { + $Script:envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "SYSTEM_JOBNAME" = "SYSTEM_JOBNAME"; + "SYSTEM_STAGEDISPLAYNAME" = "SYSTEM_STAGEDISPLAYNAME" + "BUILD_REVISION" = "BUILD_REVISION"; + "GITHUB_TOKEN" = "GITHUB_TOKEN"; + "BUILD_DEFINITIONNAME" = "BUILD_DEFINITIONNAME" + } + + $envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Remove-Item -Path "Env:$key" + } + + $Script:context = "Testing" + + $Script:tempPath = [System.IO.Path]::GetTempFileName() + $Script:message = "Test message in a bottle" + Set-Content -Path $Script:tempPath -Value $message + } + + AfterAll { + Remove-Item -Path $Script:tempPath + } + + It 'throws and exception' { + { New-GitHubSummaryComment -Context $Script:context -TestSummaryPath $Script:tempPath } | Should -Throw + } + } +} + +Describe 'Test-JobSuccess' { + Context 'succesfull' { + Test-JobSuccess -Status "Succeeded" | Should -Be $True + } + + Context 'known failures' { + Test-JobSuccess -Status "Canceled" | Should -Be $False + Test-JobSuccess -Status "Failed" | Should -Be $False + Test-JobSuccess -Status "SucceededWithIssues" | Should -Be $False + } + + Context 'unknonw value' { + Test-JobSuccess -Status "Random value" | Should -Be $False + } +} + +Describe 'Get-GitHubPRInfo' { + Context 'with all env variables present' { + + BeforeAll { + $Script:envVariables = @{ + "GITHUB_TOKEN" = "GITHUB_TOKEN"; + } + + $Script:envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Set-Item -Path "Env:$key" -Value $_.Value + } + } + + It 'calls the method succesfully' { + Mock Invoke-RestMethod { + return @{"status"=200;} + } + $changeId = "ChangeId" + + Get-GitHubPRInfo -ChangeId $changeId + + # assert the call and compare the expected parameters to the received ones + Assert-MockCalled -CommandName Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { + # validate each of the params and the payload + if ($Uri -ne "https://api.github.com/repos/xamarin/xamarin-macios/pulls/$changeId") { + return $False + } + if ($Headers.Authorization -ne ("token {0}" -f $envVariables["GITHUB_TOKEN"])) { + return $False + } + if ($Method -ne "POST") { + return $False + } + if ($ContentType -ne "application/json") { + return $False + } + + return $True + } + + } + + It 'calls the method with an error and throws' { + Mock Invoke-RestMethod { + throw [System.Exception]::("Test") + } + + $changeId = "ChangeId" + + { Get-GitHubPRInfo -ChangeId $changeId } | Should -Throw + } + + } + Context 'without an env variable' { + BeforeAll { + # clear the env vars + $envVariables = @{ + "GITHUB_TOKEN" = "GITHUB_TOKEN"; + } + $envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Remove-Item -Path "Env:$key" + } + } + It 'throws and error' { + + Mock Invoke-RestMethod { + return @{"status"=200;} + } + + $changeId = "ChangeId" + + { Get-GitHubPRInfo -ChangeId $changeId } | Should -Throw + Assert-MockCalled -CommandName Invoke-RestMethod -Times 0 -Scope It + } + } +} diff --git a/tools/devops/automation/scripts/GitHub.psm1 b/tools/devops/automation/scripts/GitHub.psm1 new file mode 100644 index 0000000000..3c6101ff05 --- /dev/null +++ b/tools/devops/automation/scripts/GitHub.psm1 @@ -0,0 +1,538 @@ +<# + .SYNOPSIS + Returns the target url to be used when setting the status. The target url allows users to get back to the CI event that updated the status. +#> +function Get-TargetUrl { + $targetUrl = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + "$Env:SYSTEM_TEAMPROJECT/_build/index?buildId=$Env:BUILD_BUILDID&view=ms.vss-test-web.test-result-details" + return $targetUrl +} + +<# + .SYNOPSIS + Returns the url to the Html Report index page stored in xamarin-storage. +#> +function Get-XamarinStorageIndexUrl { + param ( + [Parameter(Mandatory)] + [String] + $Path + ) + + return "http://xamarin-storage/$Path/jenkins-results/tests/index.html" +} + +<# + .SYNOPSIS + Sets a new status in github for the current build. + .DESCRIPTION + + .PARAMETER Status + The status value to be set in GitHub. The available values are: + + * error + * failure + * pending + * success + + If the wrong value is passed a validation error with be thrown. + + .PARAMETER Description + The description that will be added with the status update. This allows us to add a human readable string + to understand why the status was updated. + + .PARAMETER Context + The context to be used. A status can contain several contexts. The context must be passed to associate + the status with a specific event. + + .EXAMPLE + Set-GitHubStatus -Status "error" -Description "Not enough free space in the host." -Context "VSTS iOS device tests." + + .NOTES + This cmdlet depends on the following environment variables. If one or more of the variables is missing an + InvalidOperationException will be thrown: + + * SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: The uri of the vsts collection. Needed to be able to calculate the target url. + * SYSTEM_TEAMPROJECT: The team project executing the build. Needed to be able to calculate the target url. + * BUILD_BUILDID: The current build id. Needed to be able to calculate the target url. + * BUILD_REVISION: The revision of the current build. Needed to know the commit whose status to change. + * GITHUB_TOKEN: OAuth or PAT token to interact with the GitHub API. +#> +function Set-GitHubStatus { + param + ( + [Parameter(Mandatory)] + [String] + [ValidateScript({ + $("error", "failure", "pending", "success").Contains($_) #validate that the status is in the range of valid values + })] + $Status, + + [Parameter(Mandatory)] + [String] + $Description, + + [Parameter(Mandatory)] + [String] + $Context, + + [String] + $TargetUrl + ) + + # assert that all the env vars that are needed are present, else we do have an error + $envVars = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI; + "SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT; + "BUILD_BUILDID" = $Env:BUILD_BUILDID; + "BUILD_REVISION" = $Env:BUILD_REVISION; + "GITHUB_TOKEN" = $Env:GITHUB_TOKEN; + } + + foreach ($key in $envVars.Keys) { + if (-not($envVars[$key])) { + Write-Debug "Enviroment varible missing $key" + throw [System.InvalidOperationException]::new("Environment variable missing: $key") + } + } + + # use the GitHub API to set the status for the given commit + $detailsUrl = "" + if ($TargetUrl) { + $detailsUrl = $TargetUrl + } else { + $detailsUrl = Get-TargetUrl + } + $payload= @{ + state = $Status + target_url = $detailsUrl + description = $Description + context = $Context + } + $url = "https://api.github.com/repos/xamarin/xamarin-macios/statuses/$Env:BUILD_REVISION" + + $headers = @{ + Authorization = ("token {0}" -f $Env:GITHUB_TOKEN) + } + + return Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-json) -ContentType 'application/json' +} + +<# + .SYNOPSIS + Add a new comment for the commit on GitHub. + + .PARAMETER Header + The header to be used in the comment. + + .PARAMETER Description + A show description to be added in the comment, this will show as a short version of the comment on GitHub. + + .PARAMETER Message + A longer string that contains the full comment message. Will be shown when the comment is expanded. + + .PARAMETER Emoji + Optional string representing and emoji to be used in the comments header. + + .EXAMPLE + New-GitHubComment -Header "Tests failed catastrophically" -Emoji ":fire:" -Description "Not enough free space in the host." + + .NOTES + This cmdlet depends on the following environment variables. If one or more of the variables is missing an + InvalidOperationException will be thrown: + + * SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: The uri of the vsts collection. Needed to be able to calculate the target url. + * SYSTEM_TEAMPROJECT: The team project executing the build. Needed to be able to calculate the target url. + * BUILD_BUILDID: The current build id. Needed to be able to calculate the target url. + * BUILD_REVISION: The revision of the current build. Needed to know the commit whose status to change. + * GITHUB_TOKEN: OAuth or PAT token to interact with the GitHub API. +#> +function New-GitHubComment { + param + ( + [Parameter(Mandatory)] + [String] + $Header, + + [Parameter(Mandatory)] + [String] + $Description, + + [String] + $Message, + + [String] + $Emoji #optionally use an emoji + ) + + # assert that all the env vars that are needed are present, else we do have an error + $envVars = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI; + "SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT; + "BUILD_DEFINITIONNAME" = $Env:BUILD_DEFINITIONNAME; + "BUILD_REVISION" = $Env:BUILD_REVISION; + "GITHUB_TOKEN" = $Env:GITHUB_TOKEN; + } + + foreach ($key in $envVars.Keys) { + if (-not($envVars[$key])) { + Write-Debug "Enviroment varible missing $key" + throw [System.InvalidOperationException]::new("Environment variable missing: $key") + } + } + + $targetUrl = Get-TargetUrl + # build the message, which will be sent to github, users can use markdown + $msg = [System.Text.StringBuilder]::new() + $msg.AppendLine("### $Emoji $Header $Emoji") + $msg.AppendLine() + $msg.AppendLine($Description) + if ($Message) { # only if message is not null or empty + $msg.AppendLine() + $msg.AppendLine($Message) + } + $msg.AppendLine() + $msg.AppendLine("[Pipeline]($targetUrl) on Agent $Env:TESTS_BOT") # Env:TESTS_BOT is added by the pipeline as a variable coming from the execute tests job + + $url = "https://api.github.com/repos/xamarin/xamarin-macios/commits/$Env:BUILD_REVISION/comments" + $payload = @{ + body = $msg.ToString() + } + + $headers = @{ + Authorization = ("token {0}" -f $Env:GITHUB_TOKEN) + } + + $request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-Json) -ContentType 'application/json' + Write-Host $request + return $request +} + +<# + .SYNOPSIS + Add a new comment that contains the result summaries of the test run. + + .PARAMETER Header + The header to be used in the comment. + + .PARAMETER Description + A show description to be added in the comment, this will show as a short version of the comment on GitHub. + + .PARAMETER Message + A longer string that contains the full comment message. Will be shown when the comment is expanded. + + .PARAMETER Emoji + Optional string representing and emoji to be used in the comments header. + + .EXAMPLE + New-GitHubComment -Header "Tests failed catastrophically" -Emoji ":fire:" -Description "Not enough free space in the host." + + .NOTES + This cmdlet depends on the following environment variables. If one or more of the variables is missing an + InvalidOperationException will be thrown: + + * SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: The uri of the vsts collection. Needed to be able to calculate the target url. + * SYSTEM_TEAMPROJECT: The team project executing the build. Needed to be able to calculate the target url. + * BUILD_BUILDID: The current build id. Needed to be able to calculate the target url. + * BUILD_REVISION: The revision of the current build. Needed to know the commit whose status to change. + * GITHUB_TOKEN: OAuth or PAT token to interact with the GitHub API. +#> +function New-GitHubCommentFromFile { + param ( + + [Parameter(Mandatory)] + [String] + $Header, + + [Parameter(Mandatory)] + [String] + $Description, + + [Parameter(Mandatory)] + [String] + [ValidateScript({ + Test-Path -Path $_ -PathType Leaf + })] + $Path, + + [String] + $Emoji #optionally use an emoji + ) + + # read the file, create a message and use the New-GithubComment function + $msg = [System.Text.StringBuilder]::new() + foreach ($line in Get-Content -Path $Path) + { + $msg.AppendLine($line) + } + return New-GithubComment -Header $Header -Description $Description -Message $msg.ToString() -Emoji $Emoji +} + +<# + .SYNOPSIS + Test if the current job is successful or not. +#> +function Test-JobSuccess { + + param ( + [Parameter(Mandatory)] + [String] + $Status + ) + + # return if the status is one of the failure ones + return $Status -eq "Succeeded" +} + +<# + .SYNOPSIS + Add a new comment that contains the summaries to the Html Report as well as set the status accordingly. + + .PARAMETER Context + The context to be used to link the status and the device test run in the GitHub status API. + + .PARAMETER TestSummaryPath + The path to the generated test summary. + + .EXAMPLE + New-GitHubSummaryComment -Context "$Env:CONTEXT" -TestSummaryPath "$Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin/xamarin-macios/tests/TestSummary.md" + .NOTES + This cmdlet depends on the following environment variables. If one or more of the variables is missing an + InvalidOperationException will be thrown: + + * SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: The uri of the vsts collection. Needed to be able to calculate the target url. + * SYSTEM_TEAMPROJECT: The team project executing the build. Needed to be able to calculate the target url. + * BUILD_BUILDID: The current build id. Needed to be able to calculate the target url. + * BUILD_REVISION: The revision of the current build. Needed to know the commit whose status to change. + * GITHUB_TOKEN: OAuth or PAT token to interact with the GitHub API. + +#> +function New-GitHubSummaryComment { + param ( + [Parameter(Mandatory)] + [String] + $Context, + + [Parameter(Mandatory)] + [String] + $TestSummaryPath + ) + + $envVars = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI; + "SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT; + "BUILD_DEFINITIONNAME" = $Env:BUILD_DEFINITIONNAME; + "BUILD_REVISION" = $Env:BUILD_REVISION; + "GITHUB_TOKEN" = $Env:GITHUB_TOKEN; + } + + foreach ($key in $envVars.Keys) { + if (-not($envVars[$key])) { + Write-Debug "Environment variable missing: $key" + throw [System.InvalidOperationException]::new("Environment variable missing: $key") + } + } + + $vstsTargetUrl = Get-TargetUrl + # build the links to provide extra info to the monitoring person, we need to make sure of a few things + # 1. We do have the xamarin-storage path + # 2. We did reach the xamarin-storage, stored in the env var XAMARIN_STORAGE_REACHED + $headerSb = [System.Text.StringBuilder]::new() + $headerSb.AppendLine(); # new line to start the list + $headerSb.AppendLine("* [Azure DevOps]($vstsTargetUrl)") + if ($Env:VSDROPS_INDEX) { + # we did generate an index with the files in vsdrops + $headerSb.AppendLine("* [Html Report (VSDrops)]($Env:VSDROPS_INDEX)") + } + $headerLinks = $headerSb.ToString() + $request = $null + + if (-not (Test-Path $TestSummaryPath -PathType Leaf)) { + Write-Host "No test summary found" + Set-GitHubStatus -Status "failure" -Description "Tests failed catastrophically on $Context (no summary found)." -Context "$Context" + $request = New-GitHubComment -Header "Tests failed catastrophically on $Context (no summary found)." -Emoji ":fire:" -Description "Result file $TestSummaryPath not found. $headerLinks" + } else { + if (Test-JobSuccess -Status $Env:TESTS_JOBSTATUS) { + Set-GitHubStatus -Status "success" -Description "Device tests passed on $Context." -Context "$Context" + $request = New-GitHubCommentFromFile -Header "Device tests passed on $Context." -Description "Device tests passed on $Context. $headerLinks" -Emoji ":white_check_mark:" -Path $TestSummaryPath + } else { + Set-GitHubStatus -Status "failure" -Description "Device tests failed on $Context." -Context "$Context" + $request = New-GitHubCommentFromFile -Header "Device tests failed on $Context" -Description "Device tests failed on $Context. $headerLinks" -Emoji ":x:" -Path $TestSummaryPath + } + } + return $request +} + +<# + .SYNOPSIS + Get the information of a PR in GitHub. + + .PARAMETER ChangeId + The Id whose labels we want to retrieve. +#> +function Get-GitHubPRInfo { + param ( + [Parameter(Mandatory)] + [String] + $ChangeId + ) + + $envVars = @{ + "GITHUB_TOKEN" = $Env:GITHUB_TOKEN; + } + + foreach ($key in $envVars.Keys) { + if (-not($envVars[$key])) { + Write-Debug "Environment variable missing: $key" + throw [System.InvalidOperationException]::new("Environment variable missing: $key") + } + } + + $url = "https://api.github.com/repos/xamarin/xamarin-macios/pulls/$ChangeId" + + $headers = @{ + Authorization = ("token {0}" -f $Env:GITHUB_TOKEN) + } + + $request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -ContentType 'application/json' + Write-Host $request + return $request +} + +<# + .SYNOPSIS + Class used to represent a single file to be added to a gist. +#> +class GistFile +{ + [ValidateNotNullOrEmpty ()] + [string] + $Name + [ValidateNotNullOrEmpty ()] + [string] + $Path + [ValidateNotNullOrEmpty ()] + [string] + $Type + + GistFile ($Name, $Path, $Type) { + # validate that the path does exist + if (Test-Path -Path $Path -PathType Leaf) { + $this.Path = $Path + } else { + throw [System.InvalidOperationException]::new("Path could not be found: $Path") + } + $this.Name = $Name + $this.Type = $Type + } + + [hashtable] ConvertToHashTable () { + # ugly workaround to get decent new lines + $file= [System.Text.StringBuilder]::new() + foreach ($line in Get-Content -Path $this.Path) + { + $file.AppendLine($line) + } + + return @{ + content = $file.ToString() + filename = $this.Name; + language = $this.Type; + } + } +} + +<# + .SYNOPSIS + Creates a new gist that will contain the given collection of files and returns the urlobject defintion, this + is usefull when the 'using' statement generates problems. +#> +function New-GistObjectDefinition { + param ( + + [ValidateNotNullOrEmpty ()] + [string] + $Name, + + [ValidateNotNullOrEmpty ()] + [string] + $Path, + + [ValidateNotNullOrEmpty ()] + [string] + $Type + ) + return [GistFile]::new($Name, $Path, $Type) +} + +<# + .SYNOPSIS + Creates a new gist that will contain the given collection of files and returns the url +#> +function New-GistWithFiles { + param ( + + [ValidateNotNullOrEmpty ()] + [string] + $Description, + + [Parameter(Mandatory)] + [GistFile[]] + $Files, + + [switch] + $IsPublic=$false # default to false, better save than sorry + ) + + $envVars = @{ + "GITHUB_TOKEN" = $Env:GITHUB_TOKEN; + } + + foreach ($key in $envVars.Keys) { + if (-not($envVars[$key])) { + Write-Debug "Environment variable missing: $key" + throw [System.InvalidOperationException]::new("Environment variable missing: $key") + } + } + + # create the hashtable that will contain all the information of all types + $payload = @{ + description = $Description; + files = @{}; # each file is the name of the file + the hashtable of the data to be used + } + + # switchs are converted to {\"IsPresent\"=>true} in json :/ and the ternary operator might not be in all machines + if ($IsPublic) { + $payload["public"] = $true + } else { + $payload["public"] = $false + } + + foreach ($g in $Files) { + # add the file using its name + the hashtable that is used by GitHub + $payload["files"].Add($g.Name, $g.ConvertToHashTable()) + } + + $url = "https://api.github.com/gists" + $payloadJson = $payload | ConvertTo-Json + Write-Host "Url is $url" + Write-Host "Payload is $payloadJson" + + $headers = @{ + Accept = "application/vnd.github.v3+json"; + Authorization = ("token {0}" -f $Env:GITHUB_TOKEN); + } + + $request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body $payloadJson -ContentType 'application/json' + Write-Host $request + return $request.html_url +} + +# module exports, any other functions are private and should not be used outside the module. +Export-ModuleMember -Function Set-GitHubStatus +Export-ModuleMember -Function New-GitHubComment +Export-ModuleMember -Function New-GitHubCommentFromFile +Export-ModuleMember -Function New-GitHubSummaryComment +Export-ModuleMember -Function Test-JobSuccess +Export-ModuleMember -Function Get-GitHubPRInfo +Export-ModuleMember -Function New-GistWithFiles +Export-ModuleMember -Function New-GistObjectDefinition diff --git a/tools/devops/automation/scripts/MLaunch.Tests.ps1 b/tools/devops/automation/scripts/MLaunch.Tests.ps1 new file mode 100644 index 0000000000..5d33639385 --- /dev/null +++ b/tools/devops/automation/scripts/MLaunch.Tests.ps1 @@ -0,0 +1,49 @@ +<# + MLaunch related unit tests. +#> + +Import-Module ./MLaunch -Force + +Describe 'Set-MLaunchVerbosity' { + Context 'default' { + It 'set the given verbosity' { + Mock Set-Content + Mock Test-Path { + return $False + } + + $expectedValue = "#" * 10 + Set-MLaunchVerbosity -Verbosity 10 + + Assert-MockCalled -CommandName Set-Content -Times 1 -Scope It -ParameterFilter { $Path -eq "~/.mlaunch-verbosity" -and $Value -eq $expectedValue} + + } + + It 'warns when overwritting' { + Mock Set-Content + Mock Write-Debug + Mock Test-Path { + return $True + } + + $expectedValue = "#" * 10 + Set-MLaunchVerbosity -Verbosity 10 + + Assert-MockCalled -CommandName Set-Content -Times 1 -Scope It -ParameterFilter { $Path -eq "~/.mlaunch-verbosity" -and $Value -eq $expectedValue} + Assert-MockCalled -CommandName Write-Debug -Times 1 + } + } +} + +Describe 'Optimize-DeviceDiscovery' { + Context 'default' { + It 'stops usbmuxd' { + Mock Start-Process + + Optimize-DeviceDiscovery + + + Assert-MockCalled -CommandName Start-Process -ParameterFilter { $FilePath -eq "launchctl" -and $ArgumentList -eq "stop com.apple.usbmuxd"} -Times 1 -Exactly + } + } +} diff --git a/tools/devops/automation/scripts/MLaunch.psm1 b/tools/devops/automation/scripts/MLaunch.psm1 new file mode 100644 index 0000000000..9e78541edc --- /dev/null +++ b/tools/devops/automation/scripts/MLaunch.psm1 @@ -0,0 +1,41 @@ +<# + .SYNOPSIS + Set the mlaunch verbosity to the given value. + .DESCRIPTION + Set the mlaunch verbosity to the given value. This + function overwrites any already present mlaunch + configuration files. +#> +function Set-MLaunchVerbosity { + param + ( + [Parameter(Mandatory)] + [int] + $Verbosity + ) + + $mlaunchConfigPath = "~/.mlaunch-verbosity" + if (Test-Path $mlaunchConfigPath -PathType Leaf) { + Write-Debug "$mlaunchConfigPath found. Content will be overwritten." + } + + # do not confuse Set-Content with Add-Content, set will override the entire file + $fileData = "#" * $Verbosity + Set-Content -Path $mlaunchConfigPath -Value $fileData +} + +<# + .SYNOPSIS + Ensures that device will be correctly found. + .DESCRIPTION + This function re-starts the daemon that will be used + to find devices. Re-starting it will make sure that new + devices are correctly found. +#> +function Optimize-DeviceDiscovery { + Start-Process "launchctl" -ArgumentList "stop com.apple.usbmuxd" -NoNewWindow -PassThru -Wait +} + +# module exports +Export-ModuleMember -Function Set-MLaunchVerbosity +Export-ModuleMember -Function Optimize-DeviceDiscovery diff --git a/tools/devops/automation/scripts/Makefile b/tools/devops/automation/scripts/Makefile new file mode 100644 index 0000000000..1e805d8ce7 --- /dev/null +++ b/tools/devops/automation/scripts/Makefile @@ -0,0 +1,5 @@ +TOP=../../../.. +include $(TOP)/Make.config + +run-tests: + $(Q_GEN) pwsh -Command "Install-Module -AcceptLicense -Force -AllowClobber Pester;Invoke-Pester" diff --git a/tools/devops/automation/scripts/System.Tests.ps1 b/tools/devops/automation/scripts/System.Tests.ps1 new file mode 100644 index 0000000000..756ed76cf3 --- /dev/null +++ b/tools/devops/automation/scripts/System.Tests.ps1 @@ -0,0 +1,65 @@ +<# +System scripts unit tests. +#> + +Import-Module ./System -Force + +Describe 'Clear-AfterTests' { + Context 'default' { + It 'removes the expected files' { + + Mock Remove-Item + # mock test path to always return true, that is all dirs are present + Mock Test-Path { + return $True + } + + $directories = @( + "/Applications/Visual\ Studio*", + "~/Library/Caches/VisualStudio", + "~/Library/Logs/VisualStudio", + "~/Library/VisualStudio", + "~/Library/Preferences/Xamarin", + "~/Library/Caches/com.xamarin.provisionator" + ) + + Clear-AfterTests + + Assert-MockCalled -CommandName Remove-Item -Times $directories.Count -Scope It + } + } +} +Describe 'Test-HDFreeSpace' { + Context 'checks space' { + It 'returns TRUE with enough space' { + Mock Get-PSDrive { + [PSCustomObject]@{ Free = 539715158016 } + } + + Test-HDFreeSpace -Size 50 | Should -Be $True + } + + It 'returns FALSE with not enough space' { + Mock Get-PSDrive { + [PSCustomObject]@{ Free = 900 } + } + + Test-HDFreeSpace -Size 50 | Should -Be $False + } + } +} + +Describe 'Clear-XamarinProcesses' { + Context 'default' { + It 'kills all processes' { + Mock Start-Process + + # ensure that all the processes are correctly killed via pkill + Clear-XamarinProcesses + + Assert-MockCalled -CommandName Start-Process -ParameterFilter { $FilePath -eq "pkill" -and $ArgumentList -eq "-9 mlaunch"} -Times 1 -Exactly + Assert-MockCalled -CommandName Start-Process -ParameterFilter { $FilePath -eq "pkill" -and $ArgumentList -eq "-9 -f mono.*xharness.exe"} -Times 1 -Exactly + Assert-MockCalled -CommandName Start-Process -ParameterFilter { $FilePath -eq "pkill" -and $ArgumentList -eq "-9 -f ssh.*rsync.*xamarin-storage"} -Times 1 -Exactly + } + } +} diff --git a/tools/devops/automation/scripts/System.psm1 b/tools/devops/automation/scripts/System.psm1 new file mode 100644 index 0000000000..2966bc605d --- /dev/null +++ b/tools/devops/automation/scripts/System.psm1 @@ -0,0 +1,231 @@ +<# + .SYNOPSIS + Returns a hash table with the all the installed versions of a framework and the current version selected. +#> +function Get-FrameworkVersions { + [CmdletBinding()] + [OutputType('Hashtable')] + param + ( + [Parameter(Mandatory)] + [String] + [ValidateScript({ + Test-Path -Path $_ -PathType Container # framework path should be a directory and exist + })] + $Path + ) + + $versionsPath = [System.IO.Path]::Combine($Path, "Versions") + Write-Debug "Searching for version in $versionsPath" + Write-Host "Searching for version in $versionsPath" + + if ( -not (Test-Path $versionsPath -PathType Container)) { + Write-Debug "Path '$versionsPath' was not found." + return @{} + } + + $versionsInformation = [Ordered] @{ + Versions = Get-ChildItem $versionsPath -Exclude "Current" -Name # exclude current for this line + } + + # get the current link and the path it points to + $currentPath = [System.IO.Path]::Combine($versionsPath, "Current") + $currentVersion = Get-Item -Path $currentPath + $versionsInformation["Current"] = $currentVersion.Target + return $versionsInformation +} + +<# + .SYNOPSIS + Returns the version of Xcode selected via xcode-select +#> +function Get-SelectedXcode { + + [CmdletBinding()] + [OutputType('String')] + param() + + # powershell does not have a nice way to execute a process and read the stdout, we use .net + $pinfo = New-Object System.Diagnostics.ProcessStartInfo + $pinfo.FileName = "xcode-select" + $pinfo.RedirectStandardOutput = $true + $pinfo.UseShellExecute = $false + $pinfo.Arguments = "-p" + + $p = New-Object System.Diagnostics.Process + $p.StartInfo = $pinfo + $p.Start() | Out-Null + $path = $p.StandardOutput.ReadToEnd().Trim().Replace("/Contents/Developer", "") + $p.WaitForExit() + return $path +} + +<# + .SYNOPSIS + Returns the current mono version. +#> +function Get-MonoVersion { + [CmdletBinding()] + [OutputType('String')] + param() + + # powershell does not have a nice way to execute a procss and read the stdout, we use .net + $pinfo = New-Object System.Diagnostics.ProcessStartInfo + $pinfo.FileName = "mono" + $pinfo.RedirectStandardOutput = $true + $pinfo.UseShellExecute = $false + $pinfo.Arguments = "--version" + + $p = New-Object System.Diagnostics.Process + $p.StartInfo = $pinfo + $p.Start() | Out-Null + $rv = $p.StandardOutput.ReadToEnd().Trim() + $p.WaitForExit() + return $rv +} + +<# + .SYNOPSIS + Removes all the installed simulators in the system. +#> +function Remove-InstalledSimulators { + param() + # use the .Net libs to execute the process + $pinfo = New-Object System.Diagnostics.ProcessStartInfo + $pinfo.FileName = "/Applications/Xcode.app/Contents/Developer/usr/bin/simctl" + $pinfo.RedirectStandardOutput = $true + $pinfo.UseShellExecute = $false + $pinfo.Arguments = "delete all" + + $p = New-Object System.Diagnostics.Process + $p.StartInfo = $pinfo + $p.Start() | Out-Null + $p.WaitForExit() +} + +<# + .SYNOPSIS + Returns the details of the system that is currently executing the + pipeline. + .DESCRIPTION + This function returns the following details of the system that is + being used to execute the pipeline. Those details include: + + * Runtime info + * OS information + * Xamarin.iOS installed versions + * Xamarin.Mac installed versions + * Xcode installed applications + * Xcode current selected version + * Mono version + * Uptime + * Free HD space + * Used HD space +#> +function Get-SystemInfo { + + [CmdletBinding()] + [OutputType('Hashtable')] + [CmdletBinding()] + param () + if ($IsMacOS) { + $drive = Get-PSDrive "/" + } else { + $drive = Get-PSDrive "C" + } + # created and ordered dictionary with the data + $systemInfo = [Ordered]@{ + OSDescription = [System.Runtime.InteropServices.RuntimeInformation]::OSDescription; + OSArchitecture = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture; + Runtime = [System.Runtime.InteropServices.RuntimeInformation]::FrameworkDescription; + Uptime = Get-Uptime + FreeStorage = "$($drive.Free / 1GB) GB"; + UsedStorage = "$($drive.Used / 1GB) GB"; + } + + if ($IsMacOS) { + $xamariniOSVersions = Get-FrameworkVersions -Path "/Library/Frameworks/Xamarin.iOS.framework" + $xamarinMacVersions = Get-FrameworkVersions -Path "/Library/Frameworks/Xamarin.Mac.framework" + + $systemInfo["XamariniOSVersions"] = $xamariniOSVersions.Versions + $systemInfo["XamariniOSCurrentVersion"] = $xamariniOSVersions.Current + $systemInfo["XamarinMacVersions"] = $xamarinMacVersions.Versions + $systemInfo["XamarinMacCurrentVersion"] = $xamarinMacVersions.Current + $systemInfo["XcodeVersions"] = Get-ChildItem "/Applications" -Include "Xcode*" -Name + $systemInfo["XcodeSelected"] = Get-SelectedXcode + $systemInfo["MonoVersion"] = Get-MonoVersion + } + + return $systemInfo +} + +<# + .SYNOPSIS + Remove known processes from other runs. + .DESCRIPTION + Remove all known processes to xamarin that might have been left + behind after other runs. +#> +function Clear-XamarinProcesses { + # could be cleaner or smarter, but is not large atm + Start-Process -FilePath "pkill" -ArgumentList "-9 mlaunch" -NoNewWindow -PassThru -Wait + Write-Debug "mlaunch terminated" + Start-Process -FilePath "pkill" -ArgumentList "-9 -f mono.*xharness.exe" -NoNewWindow -PassThru -Wait + Write-Debug "xharness terminated" + Start-Process -FilePath "pkill" -ArgumentList "-9 -f ssh.*rsync.*xamarin-storage" -NoNewWindow -PassThru -Wait + Write-Debug "rsync terminater" +} + +<# + .SYNOPSIS + Clear all possible leftovers after the tests. +#> +function Clear-AfterTests { + Get-PSDrive "/" | Format-Table -Wrap + + # common dirs to delete + $directories = @( + "/Applications/Visual\ Studio*", + "~/Library/Caches/VisualStudio", + "~/Library/Logs/VisualStudio", + "~/Library/VisualStudio", + "~/Library/Preferences/Xamarin", + "~/Library/Caches/com.xamarin.provisionator" + ) + + foreach ($dir in $directories) { + Write-Debug "Removing $dir" + try { + if (Test-Path -Path $dir) { + Remove-Item –Path $dir -Recurse -ErrorAction SilentlyContinue -Force + } else { + Write-Debug "Path not found '$dir'" + } + } catch { + Write-Error "Could not remove dir $dir - $_" + } + } + Get-PSDrive "/" | Format-Table -Wrap +} + +<# + .SYNOPSIS + Checks if there is enough space in the HD +#> +function Test-HDFreeSpace { + param + ( + [Parameter(Mandatory)] + [int] + $Size + ) + $drive = Get-PSDrive "/" + return $drive.Free / 1GB -gt $Size +} + +# module exports, any other functions are private and should not be used outside the module +Export-ModuleMember -Function Get-SystemInfo +Export-ModuleMember -Function Clear-XamarinProcesses +Export-ModuleMember -Function Test-HDFreeSpace +Export-ModuleMember -Function Clear-AfterTests +Export-ModuleMember -Function Remove-InstalledSimulators diff --git a/tools/devops/automation/scripts/VSTS.Tests.ps1 b/tools/devops/automation/scripts/VSTS.Tests.ps1 new file mode 100644 index 0000000000..367a002f92 --- /dev/null +++ b/tools/devops/automation/scripts/VSTS.Tests.ps1 @@ -0,0 +1,181 @@ +<# +VSTS interaction unit tests. +#> + +Import-Module ./VSTS -Force + +Describe 'Stop-Pipeline' { + Context 'with all the env vars present' { + + BeforeAll { + $Script:envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "ACCESSTOKEN" = "ACCESSTOKEN" + } + + $envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Set-Item -Path "Env:$key" -Value $_.Value + } + } + + It 'performs the rest call' { + Mock Invoke-RestMethod { + return @{"status"=200;} + } + + Stop-Pipeline + + $expectedUri = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURISYSTEM_TEAMPROJECT/_apis/build/builds/BUILD_BUILDID?api-version=5.1" + Assert-MockCalled -CommandName Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { + # validate the paremters + if ($Uri -ne $expectedUri) { + return $False + } + + if ($Headers.Authorization -ne ("Bearer {0}" -f $envVariables["ACCESSTOKEN"])) { + return $False + } + + if ($Method -ne "PATCH") { + return $False + } + + if ($ContentType -ne "application/json") { + return $False + } + + # compare the payload + $bodyObj = ConvertFrom-Json $Body + if ($bodyObj.status -ne "Cancelling") { + return $False + } + return $True + } + } + + It 'performs the rest method with an error' { + Mock Invoke-RestMethod { + throw [System.Exception]::("Test") + } + #set env vars + { Stop-Pipeline } | Should -Throw + } + } + + Context 'without an env var' { + BeforeAll { + $Script:envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "ACCESSTOKEN" = "ACCESSTOKEN" + } + + $Script:envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Set-Item -Path "Env:$key" -Value $_.Value + Remove-Item -Path "Env:$key" + } + } + + It 'fails calling the rest method' { + Mock Invoke-RestMethod { + return @{"status"=200;} + } + + { Stop-Pipeline } | Should -Throw + Assert-MockCalled -CommandName Invoke-RestMethod -Times 0 -Scope It + } + } +} + +Describe 'Set-PipelineResult' { + Context 'with all the env vars present' { + + BeforeAll { + $Script:envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "ACCESSTOKEN" = "ACCESSTOKEN" + } + + $envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Set-Item -Path "Env:$key" -Value $_.Value + } + } + + It 'performs the rest call' { + Mock Invoke-RestMethod { + return @{"status"=200;} + } + + Set-PipelineResult "succeeded" + + $expectedUri = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURISYSTEM_TEAMPROJECT/_apis/build/builds/BUILD_BUILDID?api-version=5.1" + Assert-MockCalled -CommandName Invoke-RestMethod -Times 1 -Scope It -ParameterFilter { + # validate the paremters + if ($Uri -ne $expectedUri) { + return $False + } + + if ($Headers.Authorization -ne ("Bearer {0}" -f $envVariables["ACCESSTOKEN"])) { + return $False + } + + if ($Method -ne "PATCH") { + return $False + } + + if ($ContentType -ne "application/json") { + return $False + } + + # compare the payload + $bodyObj = ConvertFrom-Json $Body + if ($bodyObj.result -ne "succeeded") { + return $False + } + return $True + } + } + + It 'performs the rest method with an error' { + Mock Invoke-RestMethod { + throw [System.Exception]::("Test") + } + #set env vars + { Set-PipelineResult "failed" } | Should -Throw + } + } + + Context 'without an env var' { + BeforeAll { + $Script:envVariables = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"; + "SYSTEM_TEAMPROJECT" = "SYSTEM_TEAMPROJECT"; + "BUILD_BUILDID" = "BUILD_BUILDID"; + "ACCESSTOKEN" = "ACCESSTOKEN" + } + + $Script:envVariables.GetEnumerator() | ForEach-Object { + $key = $_.Key + Set-Item -Path "Env:$key" -Value $_.Value + Remove-Item -Path "Env:$key" + } + } + + It 'fails calling the rest method' { + Mock Invoke-RestMethod { + return @{"status"=200;} + } + + { Set-PipelineResult "failed" } | Should -Throw + Assert-MockCalled -CommandName Invoke-RestMethod -Times 0 -Scope It + } + } +} diff --git a/tools/devops/automation/scripts/VSTS.psm1 b/tools/devops/automation/scripts/VSTS.psm1 new file mode 100644 index 0000000000..9983e422a2 --- /dev/null +++ b/tools/devops/automation/scripts/VSTS.psm1 @@ -0,0 +1,181 @@ +<# + .SYNOPSIS + Returns the uri to be used for the VSTS rest API. +#> +function Get-BuildUrl { + $targetUrl = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + "$Env:SYSTEM_TEAMPROJECT/_apis/build/builds/" + $Env:BUILD_BUILDID + "?api-version=5.1" + return $targetUrl +} + +<# + .SYNOPSIS + Returns the uri to be used for the VSTS rest API for tags. +#> +function Get-TagsRestAPIUrl { + param + ( + [Parameter(Mandatory)] + [String] + $Tag + ) + + $targetUrl = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + "$Env:SYSTEM_TEAMPROJECT/_apis/build/builds/" + $Env:BUILD_BUILDID + "/tags/" + $Tag + "?api-version=6.0" + return $targetUrl +} + +<# + .SYNOPSIS + Returns the auth heater to use with the REST API of VSTS. +#> +function Get-AuthHeader([string] $AccessToken) +{ + # User name can be anything. It is the personal access token (PAT) token that matters. + $user = "AnyUser" + $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $AccessToken))) + $headers = @{Authorization = "Basic {0}" -f $base64AuthInfo} + + return $headers +} + +<# + .SYNOPSIS + Cancels the pipeline and no other steps of job will be executed. + + .EXAMPLE + Stop-Pipeline + + .NOTES + The cmdlet depends on the following environment variables. If they are not present + an InvalidOperationException will be thrown. + + * SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: Contains the full uri of the VSTS for the team. + * SYSTEM_TEAMPROJECT: Contains the name of the team in VSTS. + * BUILD_BUILDID: The id of the build to cancel. + * ACCESSTOKEN: The PAT used to be able to perform the rest call to the VSTS API. +#> +function Stop-Pipeline { + # assert that all the env vars that are needed are present, else we do have an error + $envVars = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI; + "SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT; + "BUILD_BUILDID" = $Env:BUILD_BUILDID; + "ACCESSTOKEN" = $Env:ACCESSTOKEN + } + + foreach ($key in $envVars.Keys) { + if (-not($envVars[$key])) { + Write-Debug "Environment variable missing: $key" + throw [System.InvalidOperationException]::new("Environment variable missing: $key") + } + } + + $url = Get-BuildUrl + + $headers = Get-AuthHeader -AccessToken $Env:ACCESSTOKEN + + $payload = @{ + status = "Cancelling" + } + + return Invoke-RestMethod -Uri $url -Headers $headers -Method "PATCH" -Body ($payload | ConvertTo-json) -ContentType 'application/json' +} + +<# + .SYNOPSIS + Allows to set the final status of the pipeline. + + .EXAMPLE + Set-PipelineResult "failed" + + .NOTES + The cmdlet depends on the following environment variables. If they are not present + an InvalidOperationException will be thrown. + + * SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: Contains the full uri of the VSTS for the team. + * SYSTEM_TEAMPROJECT: Contains the name of the team in VSTS. + * BUILD_BUILDID: The id of the build to cancel. + * ACCESSTOKEN: The PAT used to be able to perform the rest call to the VSTS API. + + The valid values of status are: + * "canceled" The build was canceled before starting. + * "failed" The build completed unsuccessfully. + * "none" No result + * "partiallySucceeded" The build completed compilation successfully but had other errors. + * "succeeded" The build completed successfully. +#> +function Set-PipelineResult { + param + ( + [Parameter(Mandatory)] + [String] + [ValidateScript({ + $("canceled", "failed", "none", "partiallySucceeded", "succeeded").Contains($_) # validate that the status is in the range of valid values + })] + $Status + ) + + # assert that all the env vars that are needed are present, else we do have an error + $envVars = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI; + "SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT; + "BUILD_BUILDID" = $Env:BUILD_BUILDID; + "ACCESSTOKEN" = $Env:ACCESSTOKEN + } + + foreach ($key in $envVars.Keys) { + if (-not($envVars[$key])) { + Write-Debug "Environment variable missing: $key" + throw [System.InvalidOperationException]::new("Environment variable missing: $key") + } + } + + $url = Get-BuildUrl + + $headers = Get-AuthHeader -AccessToken $Env:ACCESSTOKEN + + $payload = @{ + result = $Status + } + + return Invoke-RestMethod -Uri $url -Headers $headers -Method "PATCH" -Body ($payload | ConvertTo-json) -ContentType 'application/json' +} + +function Set-BuildTags { + param + ( + [String[]] + $Tags + ) + + $envVars = @{ + "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI; + "SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT; + "BUILD_BUILDID" = $Env:BUILD_BUILDID; + "ACCESSTOKEN" = $Env:ACCESSTOKEN + } + + foreach ($key in $envVars.Keys) { + if (-not($envVars[$key])) { + Write-Debug "Environment variable missing: $key" + throw [System.InvalidOperationException]::new("Environment variable missing: $key") + } + } + + # there is an api to just do one request, but it is not clear what should the body be, and we are trying and failing, ergo, use + # the API that sets one tag at at time. + # This is why people should write documentation, now I'm being annoying with the tags + + $headers = Get-AuthHeader -AccessToken $Env:ACCESSTOKEN + + foreach ($t in $Tags) { + $url = Get-TagsRestAPIUrl -Tag $t + Write-Host "Uri is $url" + + Invoke-RestMethod -Uri $url -Headers $headers -Method "PUT" -ContentType 'application/json' + } +} + +# export public functions, other functions are private and should not be used ouside the module. +Export-ModuleMember -Function Stop-Pipeline +Export-ModuleMember -Function Set-PipelineResult +Export-ModuleMember -Function Set-BuildTags diff --git a/tools/devops/automation/scripts/bash/build-nugets.sh b/tools/devops/automation/scripts/bash/build-nugets.sh new file mode 100755 index 0000000000..7b9db98802 --- /dev/null +++ b/tools/devops/automation/scripts/bash/build-nugets.sh @@ -0,0 +1,20 @@ +#!/bin/bash -ex + +# env var should have been defined by the CI +if test -z "$XAM_TOP"; then + echo "Variable XAM_TOP is missing." + exit 1 +fi + +cd $XAM_TOP + +DOTNET_NUPKG_DIR=$(make -C tools/devops print-abspath-variable VARIABLE=DOTNET_NUPKG_DIR | grep "^DOTNET_NUPKG_DIR=" | sed -e 's/^DOTNET_NUPKG_DIR=//') + +mkdir -p ../package/ +rm -f ../package/*.nupkg +cp -c "$DOTNET_NUPKG_DIR"/*.nupkg ../package/ + +DOTNET_PKG_DIR=$(make -C tools/devops print-abspath-variable VARIABLE=DOTNET_PKG_DIR | grep "^DOTNET_PKG_DIR=" | sed -e 's/^DOTNET_PKG_DIR=//') +make -C dotnet package -j +cp -c "$DOTNET_PKG_DIR"/*.pkg ../package/ +cp -c "$DOTNET_PKG_DIR"/*.msi ../package/ diff --git a/tools/devops/automation/scripts/bash/clean-bot.sh b/tools/devops/automation/scripts/bash/clean-bot.sh new file mode 100755 index 0000000000..3885cf0d51 --- /dev/null +++ b/tools/devops/automation/scripts/bash/clean-bot.sh @@ -0,0 +1,114 @@ +#!/bin/bash -ex + +# Do some simple validation +if [[ "$BUILD_REVISION" != "jenkins" ]] ; then + echo "This script should only be run on Jenkins bots." + exit 1 +fi + +# Print disk status before cleaning +df -h + +# We don't care about errors in this section, we just want to clean as much as possible +set +e + +# Delete all the simulator devices. These can take up a lot of space over time (I've seen 100+GB on the bots) +/Applications/Xcode.app/Contents/Developer/usr/bin/simctl delete all + +# Delete old Xcodes. +ls -lad /Applications/Xcode*.app + +oldXcodes=( + "/Applications/Xcode44.app" + "/Applications/Xcode5.app" + "/Applications/Xcode502.app" + "/Applications/Xcode511.app" + "/Applications/Xcode6.0.1.app" + "/Applications/Xcode6.app" + "/Applications/Xcode601.app" + "/Applications/Xcode61.app" + "/Applications/Xcode611.app" + "/Applications/Xcode62.app" + "/Applications/Xcode63.app" + "/Applications/Xcode64.app" + "/Applications/Xcode7.app" + "/Applications/Xcode701.app" + "/Applications/Xcode71.app" + "/Applications/Xcode711.app" + "/Applications/Xcode72.app" + "/Applications/Xcode731.app" + "/Applications/Xcode8-GM.app" + "/Applications/Xcode8.app" + "/Applications/Xcode81-GM.app" + "/Applications/Xcode81.app" + "/Applications/Xcode82.app" + "/Applications/Xcode821.app" + "/Applications/Xcode83.app" + "/Applications/Xcode833.app" + "/Applications/Xcode9-GM.app" + "/Applications/Xcode9.app" + "/Applications/Xcode91.app" + "/Applications/Xcode92.app" + "/Applications/Xcode93.app" + "/Applications/Xcode94.app" + "/Applications/Xcode941.app" + "/Applications/Xcode10.app" + "/Applications/Xcode101-beta2.app" + "/Applications/Xcode101-beta3.app" + "/Applications/Xcode101.app" + "/Applications/Xcode102-beta1.app" + "/Applications/Xcode102.app" + "/Applications/Xcode1021.app" + "/Applications/Xcode103.app" + "/Applications/Xcode10GM.app" + "/Applications/Xcode11-beta3.app" + "/Applications/Xcode11-GM.app" + "/Applications/Xcode11.app" + "/Applications/Xcode111.app" + "/Applications/Xcode112.app" + "/Applications/Xcode1121.app" + "/Applications/Xcode113.app" + "/Applications/Xcode1131.app" + "/Applications/Xcode114-beta1.app" + "/Applications/Xcode114-beta2.app" + "/Applications/Xcode114-beta3.app" + "/Applications/Xcode114.app" + "/Applications/Xcode1141.app" + "/Applications/Xcode115-beta1.app" + "/Applications/Xcode115-beta2.app" + "/Applications/Xcode115-GM.app" + "/Applications/Xcode_8.0.app" + "/Applications/Xcode_8.1.app" + "/Applications/Xcode_8.2.1.app" + "/Applications/Xcode_8.3.3.app" + "/Applications/Xcode_9.0.app" + "/Applications/Xcode_9.1.0.app" + "/Applications/Xcode_9.2.0.app" + "/Applications/Xcode_9.2.app" + "/Applications/Xcode_9.4.1.app" +# Xcode 10.2.1 is currently used by Binding Tools for Swift # /Applications/Xcode_10.2.1.app + "/Applications/Xcode_11.3.0.app" + "/Applications/Xcode_11.5.0.app" + "/Applications/Xcode_11.6.0-beta1.app" + "/Applications/Xcode_12.0.0-beta1.app" + "/Applications/Xcode_12.0.0-beta2.app" + "/Applications/Xcode_12.0.0-beta3.app" + "/Applications/Xcode_12.0.0-beta4.app" + "/Applications/Xcode_12.0.0-beta5.app" + "/Applications/Xcode_12.0.0-beta6.app" + "/Applications/Xcode_12.1.0-GM.app" + "/Applications/Xcode_12.0.0-GMb.app" + "/Applications/Xcode_12.2.0-beta1.app" + "/Applications/Xcode_12.2.0-beta2.app" + "/Applications/Xcode_12.2.0-beta3.app" + "/Applications/Xcode_12.2.0-beta.3.app" + "/Applications/Xcode_12.2.0-rc.app" +) + +for oldXcode in "${oldXcodes[@]}"; do + sudo rm -Rf "$oldXcode" +done + + +# Print disk status after cleaning +df -h diff --git a/jenkins/productsign.sh b/tools/devops/automation/scripts/bash/productsign.sh similarity index 100% rename from jenkins/productsign.sh rename to tools/devops/automation/scripts/bash/productsign.sh diff --git a/tools/devops/automation/templates/agent-pool-selector.yml b/tools/devops/automation/templates/agent-pool-selector.yml new file mode 100644 index 0000000000..1c6d7e5ab2 --- /dev/null +++ b/tools/devops/automation/templates/agent-pool-selector.yml @@ -0,0 +1,65 @@ +# +# Selects appropriate agent pool based on trigger type (PR or CI) +# +parameters: + agentPoolPR: 'VSEng-Xamarin-RedmondMacCatalinaBuildPool-iOS-Untrusted' + agentPoolPRUrl: 'https://devdiv.visualstudio.com/DevDiv/_settings/agentqueues?queueId=2734&view=agents' + agentPoolCI: 'VSEng-Xamarin-RedmondMacCatalinaBuildPool-iOS-Trusted' + agentPoolCIUrl: 'https://devdiv.visualstudio.com/DevDiv/_settings/agentqueues?queueId=2748&view=agents' + condition: succeeded() + +steps: + - powershell: | + $buildReason = "$(Build.Reason)" + $buildSourceBranchName = "$(Build.SourceBranchName)" + $agentPoolPR = "${{ parameters.agentPoolPR }}" + $agentPoolPRUrl = "${{ parameters.agentPoolPRUrl }}" + $agentPoolCI = "${{ parameters.agentPoolCI }}" + $agentPoolCIUrl = "${{ parameters.agentPoolCIUrl }}" + + Write-Host "buildReason: ${buildReason}" + Write-Host "buildSourceBranchName: ${buildSourceBranchName}" + Write-Host "agentPoolPR: ${agentPoolPR}" + Write-Host "agentPoolPRUrl: ${agentPoolPRUrl}" + Write-Host "agentPoolCI: ${agentPoolCI}" + Write-Host "agentPoolCIUrl: ${agentPoolCIUrl}" + + $agentPool = $agentPoolPR # Default to Catalina PR pool + $agentPoolUrl = $agentPoolPRUrl + Write-Host "Default agent pool: ${agentPool}" + + [bool] $isTopicBranch = $False + [bool] $isPullRequest = $False + + if (-not ($buildSourceBranchName -eq 'main' -or $buildSourceBranchName -eq 'master' -or $buildSourceBranchName.StartsWith('d16-'))) { + $isTopicBranch = $True + } + + if ($buildReason -eq 'PullRequest') { + $prTargetBranchName = "$(System.PullRequest.TargetBranch)" # This system variable is only defined (and in turn the value macro replaced) when $buildReason is 'PullRequest'. Consequently, it cannot be defined as part of an input parameter + Write-Host "prTargetBranchName: System.PullRequest.TargetBranch: ${prTargetBranchName}" + $isPullRequest = $True + $targetBranch = $prTargetBranchName + } else { + $targetBranch = $buildSourceBranchName + } + + Write-Host "Settings:" + Write-Host " targetBranch: ${targetBranch}" + Write-Host " isTopicBranch: ${isTopicBranch}" + Write-Host " isPullRequest: ${isPullRequest}" + + if ($isTopicBranch -or $isPullRequest) { + $agentPool = $agentPoolPR # Untrusted on-prem iOS pool used for all PRs (including those from forks) and feature/topic branch commits not targeting main or d16-x branches + $agentPoolUrl = $agentPoolPRUrl + } else { + $agentPool = $agentPoolCI # Trusted on-prem iOS pool used for CIs targeting main and d16-x release branches + $agentPoolUrl = $agentPoolCIUrl + } + + Write-Host "AgentPoolComputed: ${agentPool}" + Write-Host "Selected agent pool: ${agentPoolUrl}" + Write-Host "##vso[task.setvariable variable=AgentPoolComputed;isOutput=true]$agentPool" + name: setAgentPool + displayName: 'AgentPoolSelector: Select agent pool' + condition: ${{ parameters.condition }} diff --git a/tools/devops/automation/templates/common/download-artifacts.yml b/tools/devops/automation/templates/common/download-artifacts.yml new file mode 100644 index 0000000000..0f8905cd1c --- /dev/null +++ b/tools/devops/automation/templates/common/download-artifacts.yml @@ -0,0 +1,42 @@ +# common steps to download the artifacts from the test results. +parameters: + +- name: devicePrefix + type: string + default: 'ios' # default context, since we started dealing with iOS devices. + +steps: + +- checkout: self + persistCredentials: true + +# Download the Html Report that was added by the tests job. +- task: DownloadPipelineArtifact@2 + displayName: Download html report + inputs: + patterns: 'HtmlReport-${{ parameters.devicePrefix }}/HtmlReport.zip' + allowFailedBuilds: true + path: $(System.DefaultWorkingDirectory)/Reports + +# Unzip report. +- task: ExtractFiles@1 + displayName: 'Extract HmlReport' + inputs: + archiveFilePatterns: '$(System.DefaultWorkingDirectory)/Reports/HtmlReport-${{ parameters.devicePrefix }}/HtmlReport.zip' + destinationFolder: '$(System.DefaultWorkingDirectory)/HtmlReport-${{ parameters.devicePrefix }}' + +# Download the test report to write the comment. +- task: DownloadPipelineArtifact@2 + displayName: Download Test Summary + inputs: + patterns: '**/TestSummary-${{ parameters.devicePrefix }}/TestSummary.md' + allowFailedBuilds: true + path: $(System.DefaultWorkingDirectory)\Reports + +- powershell: | + Get-ChildItem -Recurse $Env:SYSTEM_DEFAULTWORKINGDIRECTORY + + Write-Host "##vso[task.setvariable variable=TEST_SUMMARY_PATH]$Env:SYSTEM_DEFAULTWORKINGDIRECTORY\Reports\TestSummary-${{ parameters.devicePrefix }}\TestSummary.md" + Write-Host "##vso[task.setvariable variable=HTML_REPORT_PATH]$Env:SYSTEM_DEFAULTWORKINGDIRECTORY\HtmlReport-${{ parameters.devicePrefix }}" + displayName: Pusblish artifact paths + name: artifacts # not to be confused with the displayName, this is used to later use the name of the step to access the output variables from an other job diff --git a/tools/devops/automation/templates/common/publish-html.yml b/tools/devops/automation/templates/common/publish-html.yml new file mode 100644 index 0000000000..f71a167b17 --- /dev/null +++ b/tools/devops/automation/templates/common/publish-html.yml @@ -0,0 +1,58 @@ +# Job that will download the other artifact from the tests job and will publish them in the +# vsdrops + +########################################################### +# WARNING WARNING WARNING WARNING WARNING WARNING WARNING # +########################################################### + +# This job is executed on WINDOWS! make sure you DO NOT USE +# bash or linux file paths on scripts. Another important +# details is that System.DefaultWorkingDirectory +# on mac os x points on the top dir while on windows +# is the checked out dir + +parameters: + +- name: statusContext + type: string + default: 'iOS Device Tests' # default context, since we started dealing with iOS devices. + +- name: vsdropsPrefix + type: string + +- name: devicePrefix + type: string + default: 'ios' # default context, since we started dealing with iOS devices. + +steps: + +- checkout: self + persistCredentials: true + +- template: download-artifacts.yml + parameters: + devicePrefix: ${{ parameters.devicePrefix }} + +# Use the cmdlet to post a new summary comment. The cmdlet checks if we have the TestSummary.md file or not. It will also add the appropriate links to the comment. +# this step uses variables that have been set by the tests job dependency via output variables, those variables contain if the xamarin-storage could be used and its path +- powershell: | + $env:VSDROPS_INDEX="$Env:VSDROPSPREFIX/$Env:BUILD_BUILDNUMBER/$Env:BUILD_BUILDID/$Env:DEVICE_PREFIX/;/tests/vsdrops_index.html" + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\xamarin-macios\tools\devops\automation\scripts\GitHub.psm1 + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\xamarin-macios\tools\devops\automation\scripts\VSTS.psm1 + $response = New-GitHubSummaryComment -Context "$Env:CONTEXT" -TestSummaryPath "$Env:TESTS_SUMMARY" + Write-Host $response + if($Env:TESTS_JOBSTATUS -ne "Succeeded") + { + Set-PipelineResult -Status partiallySucceeded + } + env: + BUILD_REVISION: $(Build.SourceVersion) + CONTEXT: ${{ parameters.statusContext }} + DEVICE_PREFIX: ${{ parameters.devicePrefix }} + GITHUB_TOKEN: $(GitHub.Token) + TESTS_JOBSTATUS: $(TESTS_JOBSTATUS) # set by the runTests step + TESTS_SUMMARY: $(TEST_SUMMARY_PATH) + ACCESSTOKEN: $(System.AccessToken) + displayName: 'Add summaries' + condition: always() + timeoutInMinutes: 1 diff --git a/tools/devops/automation/templates/common/upload-vsdrops.yml b/tools/devops/automation/templates/common/upload-vsdrops.yml new file mode 100644 index 0000000000..223f9ce02e --- /dev/null +++ b/tools/devops/automation/templates/common/upload-vsdrops.yml @@ -0,0 +1,27 @@ + +# job that downloads the html report from the artifacts and uploads them into vsdrops. +parameters: + +- name: devicePrefix + type: string + default: 'ios' # default context, since we started dealing with iOS devices. + +steps: + +- checkout: self + persistCredentials: true + +- template: download-artifacts.yml + parameters: + devicePrefix: ${{ parameters.devicePrefix }} + +# Upload full report to vsdrops using the the build numer and id as uuids. +- task: ms-vscs-artifact.build-tasks.artifactDropTask-1.artifactDropTask@0 + displayName: 'Publish to Artifact Services Drop' + inputs: + dropServiceURI: 'https://devdiv.artifacts.visualstudio.com/DefaultCollection' + dropMetadataContainerName: 'DropMetadata-${{ parameters.devicePrefix }}' + buildNumber: 'xamarin-macios/device-tests/$(Build.BuildNumber)/$(Build.BuildId)/${{ parameters.devicePrefix }}' + sourcePath: $(HTML_REPORT_PATH) + detailedLog: true + usePat: true diff --git a/tools/devops/automation/templates/common/upload-vsts-tests.yml b/tools/devops/automation/templates/common/upload-vsts-tests.yml new file mode 100644 index 0000000000..5f4e662d67 --- /dev/null +++ b/tools/devops/automation/templates/common/upload-vsts-tests.yml @@ -0,0 +1,25 @@ +# imports the xml to the vsts test results for the job +parameters: + +- name: devicePrefix + type: string + default: 'ios' # default context, since we started dealing with iOS devices. + +steps: + +- checkout: self + persistCredentials: true + +- template: download-artifacts.yml + parameters: + devicePrefix: ${{ parameters.devicePrefix }} + +# Upload test results to vsts. +- task: PublishTestResults@2 + displayName: 'Publish NUnit Device Test Results' + inputs: + testResultsFormat: NUnit + testResultsFiles: '**/vsts-*.xml' + failTaskOnFailedTests: true + continueOnError: true + condition: succeededOrFailed() diff --git a/tools/devops/automation/templates/devices/build.yml b/tools/devops/automation/templates/devices/build.yml new file mode 100644 index 0000000000..085522a6d7 --- /dev/null +++ b/tools/devops/automation/templates/devices/build.yml @@ -0,0 +1,269 @@ +# Xamarin +# +# Template that contains the different steps required to run device +# tests. The template takes a number of parameters so that it can +# be configured for the different type of devices. +# +parameters: + +- name: statusContext + type: string + default: 'iOS Device Tests' # default context, since we started dealing with iOS devices. + +- name: testsLabels + type: string + default: '--label=run-ios-64-tests,run-non-monotouch-tests,run-monotouch-tests,run-mscorlib-tests' # default context, since we started dealing with iOS devices. + +- name: disableProvisionatorCache + type: boolean + default: false + +- name: clearProvisionatorCache + type: boolean + default: false + +- name: useXamarinStorage + type: boolean + default: false # xamarin-storage will disappear, so by default do not use it + +- name: vsdropsPrefix + type: string + +# can depend on the pool, which is annoying, but we should keep it in mind +- name: keyringPass + type: string + +- name: devicePrefix + type: string + default: 'ios' # default context, since we started dealing with iOS devices. + +steps: + +- checkout: self +- checkout: maccore + persistCredentials: true # hugely important, else there are some scripts that check a single file from maccore that will fail + +- bash: $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/automation/scripts/bash/clean-bot.sh + displayName: 'Clean bot' + env: + BUILD_REVISION: 'jenkins' + continueOnError: true + +- bash: | + security set-key-partition-list -S apple-tool:,apple: -s -k $KEYCHAIN_PASS login.keychain + env: + KEYCHAIN_PASS: ${{ parameters.keyringPass }} + displayName: 'Remove security UI-prompt (http://stackoverflow.com/a/40039594/183422)' + condition: succeededOrFailed() # we do not care about the previous process cleanup + continueOnError: true + +- bash: cd $(System.DefaultWorkingDirectory)/xamarin-macios/ && git clean -xdf + displayName: 'Clean workspace' + +# Run the pipeline script tests to ensure that we will have not have an unexpected behaviour. +- bash: make -C $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/automation/scripts run-tests + displayName: 'Run pipeline script tests' + +- pwsh : | + gci env: | format-table -autosize -wrap + displayName: 'Dump Environment' + +# Use a cmdlet to check if the space available in the devices root system is larger than 50 gb. If there is not +# enough space available it: +# 1. Set the status of the build to error. It is not a failure since no tests have been ran. +# 2. Set a comment stating the same as what was sent to the status. +# 3. Cancel the pipeline and do not execute any of the following steps. +- pwsh: | + cd $Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin-macios/tools/devops/automation/scripts/ + Import-Module ./System.psm1 + Import-Module ./VSTS.psm1 + Import-Module ./GitHub.psm1 + if ( -not (Test-HDFreeSpace -Size 50)) { + Set-GitHubStatus -Status "error" -Description "Not enough free space in the host." -Context "$Env:CONTEXT" + New-GitHubComment -Header "Tests failed catastrophically on $Env:CONTEXT" -Emoji ":fire:" -Description "Not enough free space in the host." + Stop-Pipeline + } else { + Set-GitHubStatus -Status "pending" -Description "Device tests on VSTS have been started." -Context "$Env:CONTEXT" + } + env: + BUILD_REVISION: $(Build.SourceVersion) + CONTEXT: ${{ parameters.statusContext }} + GITHUB_TOKEN: $(GitHub.Token) + ACCESSTOKEN: $(System.AccessToken) + displayName: 'Check HD Free Space' + timeoutInMinutes: 5 + condition: succeededOrFailed() # we do not care about the previous step + +# if we got to this point, it means that we do have at least 50 Gb to run the test, should +# be more than enough, else the above script would have stopped the pipeline +- bash: | + set -x + set -e + cd xamarin-macios + ./configure --enable-xamarin + displayName: 'Enable Xamarin' + timeoutInMinutes: 1 + +# Add the required provisioning profiles to be able to execute the tests. +- bash: | + set -x + set -e + rm -f ~/Library/Caches/com.xamarin.provisionator/Provisions/*p12 + rm -f ~/Library/Caches/com.xamarin.provisionator/Provisions/*mobileprovision + ./maccore/tools/install-qa-provisioning-profiles.sh -v + displayName: 'Add provisioning profiles' + timeoutInMinutes: 30 + env: + LOGIN_KEYCHAIN_PASSWORD: ${{ parameters.keyringPass }} + +# download the artifacts.json, which will use to find the URI of the built pkg to later be installed by provisionator +- task: DownloadPipelineArtifact@2 + displayName: Download artifacts.json + inputs: + patterns: '**/*.json' + allowFailedBuilds: true + path: $(Build.SourcesDirectory)/artifacts + +- pwsh: | + Dir $(Build.SourcesDirectory)/artifacts + $json = Get-Content '$(Build.SourcesDirectory)/artifacts/pkg-info/artifacts.json' | Out-String | ConvertFrom-Json + foreach ($i in $json) { + if ($i.tag -like "xamarin-ios*" -and -not ($i.url -like "*notarized*")) { + $url = $i.url + Write-Host "##vso[task.setvariable variable=XI_PACKAGE;]$url" + break + } + } + displayName: 'Set iOS pkgs url' + timeoutInMinutes: 5 + +- bash: | + echo "Pkg uri is $XI_PACKAGE" + make -C $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/ device-tests-provisioning.csx + displayName: 'Generate Provisionator csx file' + +# Executed ONLY if we want to clear the provisionator cache. +- bash: rm -rf "$TOOLS_DIR/provisionator" + env: + TOOLS_DIR: $(Agent.ToolsDirectory) + displayName: 'Nuke Provisionator Tool Cache' + condition: ${{ parameters.clearProvisionatorCache }} + +# Use the provisionator to install the test dependencies. Those have been generated in the 'Generate Provisionator csx file' step. +- task: xamops.azdevex.provisionator-task.provisionator@1 + displayName: 'Provision dependencies' + inputs: + provisioning_script: $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/device-tests-provisioning.csx + provisioning_extra_args: '-vvvv' + timeoutInMinutes: 250 + +# remove any old processes that might have been left behind. +- pwsh : | + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin-macios/tools/devops/automation/scripts/System.psm1 + Clear-XamarinProcesses + displayName: 'Process cleanup' + +# Increase mlaunch verbosity. Will step on the old setting present. +- pwsh : | + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin-macios/tools/devops/automation/scripts/MLaunch.psm1 + Set-MLaunchVerbosity -Verbosity 10 + displayName: 'Make mlaunch verbose' + condition: succeededOrFailed() # we do not care about the previous step + +# Re-start the daemon used to find the devices in the bot. +- pwsh : | + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin-macios/tools/devops/automation/scripts/MLaunch.psm1 + Optimize-DeviceDiscovery + displayName: 'Fix device discovery (reset launchctl)' + condition: succeededOrFailed() # making mlaunch verbose should be a non blocker + +# Update the status to pending, that way the monitoring person knows that we started running the tests. Up to this +# point we were just setting up the agent. +- pwsh: | + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin-macios/tools/devops/automation/scripts/GitHub.psm1 + Set-GitHubStatus -Status "pending" -Context "$Env:CONTEXT" -Description "Running device tests on $Env:CONTEXT" + env: + BUILD_REVISION: $(Build.SourceVersion) + CONTEXT: ${{ parameters.statusContext }} + GITHUB_TOKEN: $(GitHub.Token) + displayName: Set pending GitHub status + continueOnError: true + condition: succeededOrFailed() # re-starting the daemon should not be an issue + timeoutInMinutes: 5 + +# Run tests. If we are using xamarin-storage add a periodic command to be executed by xharness, else, since we are using vsdrops do nothing. +- bash: | + set -x + set -e + + cd $WORKING_DIR/xamarin-macios + + echo "Running tests on $AGENT_NAME" + echo "##vso[task.setvariable variable=TESTS_BOT;isOutput=true]$AGENT_NAME" + + make -C builds download -j || true + make -C builds downloads -j || true + make -C builds .stamp-mono-ios-sdk-destdir -j || true + EC=0 + MONO_ENV_OPTIONS=--trace=E:all make -C tests vsts-device-tests || EC=$? + if [ $EC -eq 0 ]; then + echo '##vso[task.setvariable variable=TESTS_JOBSTATUS;isOutput=true]Succeeded' + else + echo '##vso[task.setvariable variable=TESTS_JOBSTATUS;isOutput=true]Failed' + fi + env: + WORKING_DIR: $(System.DefaultWorkingDirectory) + TESTS_EXTRA_ARGUMENTS: ${{ parameters.testsLabels }} + USE_XAMARIN_STORAGE: ${{ parameters.useXamarinStorage }} + VSDROPS_URI: '${{ parameters.vsdropsPrefix }}/$(Build.BuildNumber)/$(Build.BuildId);/tests/' # uri used to create the vsdrops index using full uri + USE_TCP_TUNNEL: 'true' + displayName: 'Run tests' + name: runTests # not to be confused with the displayName, this is used to later use the name of the step to access the output variables from an other job + timeoutInMinutes: 600 + +# Upload TestSummary as an artifact. +- task: PublishPipelineArtifact@1 + displayName: 'Publish Artifact: TestSummary' + inputs: + targetPath: 'xamarin-macios/tests/TestSummary.md' + artifactName: TestSummary-${{ parameters.devicePrefix }} + continueOnError: true + condition: succeededOrFailed() + +- pwsh: | + $summaryName = "TestSummary-$Env:PREFIX.md" + $summaryPath = "$Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin-macios/tests/TestSummary.md" + Write-Host "##vso[task.addattachment type=Distributedtask.Core.Summary;name=$summaryName;]$summaryPath" + displayName: Set TestSummary + env: + PREFIX: ${{ parameters.devicePrefix }} + +# Archive files for the Html Report so that the report can be easily uploaded as artifacts of the build. +- task: ArchiveFiles@1 + displayName: 'Archive HtmlReport' + inputs: + rootFolder: 'xamarin-macios/jenkins-results' + includeRootFolder: false + archiveFile: '$(Build.ArtifactStagingDirectory)/HtmlReport.zip' + continueOnError: true + condition: succeededOrFailed() + +# Create HtmlReport artifact. This serves two purposes: +# 1. It is the way we are going to share the HtmlReport with the publish_html job that is executed on a Windows machine. +# 2. Users can download this if they want. +- task: PublishPipelineArtifact@1 + displayName: 'Publish Artifact: HtmlReport' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)/HtmlReport.zip' + artifactName: HtmlReport-${{ parameters.devicePrefix }} + continueOnError: true + condition: succeededOrFailed() + +# Be nice and clean behind you +- pwsh: | + cd $Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin-macios/tools/devops/automation/scripts/ + Import-Module ./System.psm1 + Clear-AfterTests + displayName: 'Cleanup' + continueOnError: true + condition: always() # no matter what, includes cancellation diff --git a/tools/devops/automation/templates/devices/stage.yml b/tools/devops/automation/templates/devices/stage.yml new file mode 100644 index 0000000000..04708c1f1f --- /dev/null +++ b/tools/devops/automation/templates/devices/stage.yml @@ -0,0 +1,128 @@ +# Main template that contains all the jobs that are required to run the device tests. +# +# The stage contains two different jobs +# +# tests: Runs the tests on a pool that contains devices that are capable to run them. +# publish_html: Because vsdrop is not supported on macOS we have an extra job that +# will run on a pool with Windows devices that will publish the results on VSDrop to +# be browsable. + +parameters: + +# string that is used to identify the status to be used to expose the result on GitHub +- name: statusContext + type: string + default: 'iOS Device Tests' # default context, since we started dealing with iOS devices. + +# string that contains the extra labels to pass to xharness to select the tests to execute. +- name: testsLabels + type: string + default: '--label=run-ios-64-tests,run-non-monotouch-tests,run-monotouch-tests,run-mscorlib-tests' # default context, since we started dealing with iOS devices. + +# name of the pool that contains the iOS devices +- name: iOSDevicePool + type: string + default: 'VSEng-Xamarin-QA' + +# demand that has to be matched by a bot to be able to run the tests. +- name: iOSDeviceDemand + type: string + default: 'xismoke' + +- name: useXamarinStorage + type: boolean + default: false + +- name: vsdropsPrefix + type: string + +- name: stageName + type: string + +- name: keyringPass + type: string + +- name: execute + type: string + +- name: devicePrefix + type: string + default: 'ios' # default context, since we started dealing with iOS devices. + +stages: +- stage: + displayName: ${{ parameters.stageName }} + dependsOn: + - build_packages + # we need to have the pkgs built and the device sets to be ran, that is decided via the labels or type of build during the build_packages stage + condition: and(succeeded(), eq(dependencies.build_packages.outputs['build.configuration.RunDeviceTests'], 'True')) + jobs: + - job: tests + displayName: 'Run ${{ parameters.devicePrefix }} Device Tests' + timeoutInMinutes: 1000 + pool: + name: ${{ parameters.iOSDevicePool }} + demands: ${{ parameters.iOSDeviceDemand }} + workspace: + clean: all + steps: + - template: build.yml + parameters: + testsLabels: ${{ parameters.testsLabels }} + statusContext: ${{ parameters.statusContext }} + useXamarinStorage: ${{ parameters.useXamarinStorage }} + vsdropsPrefix: ${{ parameters.vsdropsPrefix }} + keyringPass: ${{ parameters.keyringPass }} + devicePrefix: ${{ parameters.devicePrefix }} + + - job: upload_vsdrops + displayName: 'Upload report to vsdrops' + timeoutInMinutes: 1000 + dependsOn: tests # can start as soon as the tests are done + condition: succeededOrFailed() + pool: + vmImage: 'windows-latest' + workspace: + clean: all + steps: + - template: ../common/upload-vsdrops.yml + parameters: + devicePrefix: ${{ parameters.devicePrefix }} + + - job: upload_vsts_tests + displayName: 'Upload xml to vsts' + timeoutInMinutes: 1000 + dependsOn: tests # can start as soon as the tests are done + condition: succeededOrFailed() + pool: + vmImage: 'windows-latest' + workspace: + clean: all + steps: + - template: ../common/upload-vsts-tests.yml + parameters: + devicePrefix: ${{ parameters.devicePrefix }} + + - job: publish_html + displayName: 'Publish Html report in VSDrops' + timeoutInMinutes: 1000 + dependsOn: # has to wait for the tests to be done AND the data to be uploaded + - tests + - upload_vsdrops + - upload_vsts_tests + condition: succeededOrFailed() + variables: + # Define the variable FOO from the previous job + # Note the use of single quotes! + TESTS_BOT: $[ dependencies.tests.outputs['runTests.TESTS_BOT'] ] + TESTS_JOBSTATUS: $[ dependencies.tests.outputs['runTests.TESTS_JOBSTATUS'] ] + pool: + vmImage: 'windows-latest' + workspace: + clean: all + steps: + - template: ../common/publish-html.yml + parameters: + statusContext: ${{ parameters.statusContext }} + vsdropsPrefix: ${{ parameters.vsdropsPrefix }} + devicePrefix: ${{ parameters.devicePrefix }} diff --git a/tools/devops/automation/templates/governance-checks.yml b/tools/devops/automation/templates/governance-checks.yml new file mode 100644 index 0000000000..b432b5d52c --- /dev/null +++ b/tools/devops/automation/templates/governance-checks.yml @@ -0,0 +1,67 @@ +steps: + +- checkout: self +- checkout: maccore + persistCredentials: true # hugely important, else there are some scripts that check a single file from maccore that will fail + +- task: CredScan@3 + displayName: "Run CredScan" + inputs: + suppressionsFile: '$(System.DefaultWorkingDirectory)/maccore/tools/devops/CredScanSuppressions.json' + outputFormat: 'sarif' + verboseOutput: true + +- powershell: | + Write-Host 'Source dir $(Build.SourcesDirectory)' + Write-Host 'Working dir $System.DefaultWorkingDirectory)' + + Dir $(Build.SourcesDirectory) + Dir $(System.DefaultWorkingDirectory) + displayName: Dump enviroment + +- powershell: | + gci env: | format-table -autosize -wrap + displayName: 'Dump Environment' + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + +- task: PoliCheck@1 + inputs: + inputType: 'Basic' + targetType: 'F' + targetArgument: '$(Build.SourcesDirectory)' + result: 'PoliCheck.xml' + optionsUEPATH: '$(System.DefaultWorkingDirectory)/maccore/tools/devops/PoliCheckExclusions.xml' + +- task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@1 + displayName: 'Post Analysis' + inputs: + CredScan: true + PoliCheck: true + +- task: WhiteSource Bolt@20 + displayName: "WhiteSource Bolt analysis" + inputs: + cwd: $(System.DefaultWorkingDirectory) + +- powershell: echo "##vso[task.setvariable variable=CHECKS_FAILED]True" + condition: failed() # we failed running the tests, therefore stop the pipeline + +- powershell: | + Import-Module "$(System.DefaultWorkingDirectory)\xamarin-macios\tools\devops\automation\scripts\GitHub.psm1" + Import-Module "$(System.DefaultWorkingDirectory)\xamarin-macios\tools\devops\automation\scripts\VSTS.psm1" + $context = "Governance" + + Write-Host "Checks failed: '$Env:CHECKS_FAILED'" + if ($Env:CHECKS_FAILED -eq "True") { + Set-GitHubStatus -Status "error" -Description "Governance checks failed" -Context "$context" + } else { + Set-GitHubStatus -Status "success" -Description "Governance checks passed" -Context "$context" + } + env: + BUILD_REVISION: $(Build.SourceVersion) + GITHUB_TOKEN: $(GitHub.Token) + ACCESSTOKEN: $(System.AccessToken) + displayName: "Set Github status" + condition: succeededOrFailed() diff --git a/tools/devops/automation/templates/mac-tests.yml b/tools/devops/automation/templates/mac-tests.yml new file mode 100644 index 0000000000..39f6f0fa18 --- /dev/null +++ b/tools/devops/automation/templates/mac-tests.yml @@ -0,0 +1,51 @@ +parameters: +- name: macPool + type: string + +- name: stageName + type: string + +stages: +- stage: + displayName: ${{ parameters.stageName }} + dependsOn: + - build_packages + # we need to have the pkgs built and the device sets to be ran, that is decided via the labels or type of build during the build_packages stage + condition: and(succeeded(), eq (stageDependencies.build_packages.build.outputs['configuration.RunMacTests'], 'True')) + + jobs: + - job: run_tests + displayName: 'Mac OS X tests' + timeoutInMinutes: 1000 + workspace: + clean: all + + pool: + name: ${{ parameters.macPool }} + demands: + - Agent.OS -equals Darwin + + steps: + - checkout: self # https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#checkout + clean: true # Executes: git clean -ffdx && git reset --hard HEAD + submodules: recursive + + - bash: echo "Hello Tests" + displayName: 'So many job' + + - job: upload_vsdrops + displayName: 'Upload results to vsdrops' + dependsOn: run_tests + timeoutInMinutes: 1000 + workspace: + clean: all + + pool: + name: VSEng-Xamarin-Win-XMA + demands: + - Agent.OS -equals Windows_NT + + steps: + - checkout: self # https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#checkout + clean: true # Executes: git clean -ffdx && git reset --hard HEAD + submodules: recursive diff --git a/tools/devops/automation/templates/packages/build.yml b/tools/devops/automation/templates/packages/build.yml new file mode 100644 index 0000000000..21a50e02b4 --- /dev/null +++ b/tools/devops/automation/templates/packages/build.yml @@ -0,0 +1,440 @@ +parameters: +- name: runTests + type: boolean + default: true + +- name: runDeviceTests + type: boolean + default: true + +- name: vsdropsPrefix + type: string + +steps: +- checkout: self # https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#checkout + clean: true # Executes: git clean -ffdx && git reset --hard HEAD + submodules: recursive + +- checkout: maccore + clean: true + persistCredentials: true # hugely important, else there are some scripts that check a single file from maccore that will fail + +- checkout: templates + clean: true + +- checkout: release-scripts + clean: true + +- powershell: | + gci env: | format-table -autosize -wrap + displayName: 'Dump Environment' + +- bash: $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/automation/scripts/bash/clean-bot.sh + displayName: 'Clean bot' + env: + BUILD_REVISION: 'jenkins' + continueOnError: true + +- bash: | + security set-key-partition-list -S apple-tool:,apple: -s -k $OSX_KEYCHAIN_PASS login.keychain + env: + OSX_KEYCHAIN_PASS: $(OSX_KEYCHAIN_PASS) + displayName: 'Remove security UI-prompt (http://stackoverflow.com/a/40039594/183422)' + condition: succeededOrFailed() # we do not care about the previous process cleanup + +- template: install-certificates.yml@templates + parameters: + DeveloperIdApplication: $(developer-id-application) + DeveloperIdInstaller: $(developer-id-installer) + IphoneDeveloper: $(iphone-developer) + MacDeveloper: $(mac-developer) + HostedMacKeychainPassword: $(OSX_KEYCHAIN_PASS) + +- task: xamops.azdevex.provisionator-task.provisionator@2 + displayName: 'Provision Brew components' + inputs: + provisioning_script: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/provision-brew-packages.csx + provisioning_extra_args: '-vvvv' + timeoutInMinutes: 30 + enabled: false + +- bash: | + make -C $(Build.SourcesDirectory)/xamarin-macios/tools/devops build-provisioning.csx + displayName: 'Generate provisionator files.' + +- task: xamops.azdevex.provisionator-task.provisionator@1 + displayName: 'Provision Products & Frameworks' + inputs: + provisioning_script: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/build-provisioning.csx + provisioning_extra_args: '-vvvv' + timeoutInMinutes: 250 + +- bash: | + set -x + sudo rm -Rf /Developer/MonoTouch + sudo rm -Rf /Library/Frameworks/Xamarin.iOS.framework + sudo rm -Rf /Library/Frameworks/Xamarin.Mac.framework + displayName: 'Delete library folders' + timeoutInMinutes: 5 + +- bash: + set -x + set -e + rm -Rvf $(Build.SourcesDirectory)/package + time make -C $(Build.SourcesDirectory)/xamarin-macios/ git-clean-all + displayName: 'Clear results directory' + timeoutInMinutes: 5 + +# Use the env variables that were set by the label parsing in the configure step +# print some useful logging to allow to know what is going on AND allow make some +# choices, there are labels that contradict each other (skip-package vs build-packages) +# we use warnings for those case we are not sure about. +- pwsh: | + # we have a number of scripts that require to be executed from the top of the src, rather + # than keeping track of the location of the script, we create two env vars that can be used to + # get to the top + $xamTop = "$(Build.SourcesDirectory)/xamarin-macios/" + Write-Host "##vso[task.setvariable variable=XAM_TOP]$xamTop" + + $maccoreTop = "$(Build.SourcesDirectory)/maccore/" + Write-Host "##vso[task.setvariable variable=MACCORE_TOP]$maccoreTop" + + $buildReason = "$(Build.Reason)" + $buildSourceBranchName = "$(Build.SourceBranchName)" + + # decide if we are dealing with a PR or a re-triggered PR or a build from + # a branch in origin + + if ($buildReason -eq "PullRequest" -or (($buildReason -eq "Manual") -and ($buildSourceBranchName -eq "merge")) ) { + Write-Host '##vso[task.setvariable variable=IsPR;isOutput=true]False' + + if ($Env:BuildPackage -eq "True") { + Write-Host '##vso[task.setvariable variable=BuildPkgs;isOutput=true]True' + } else { + Write-Host '##vso[task.setvariable variable=BuildPkgs;isOutput=true]False' + } + + # interesting case, we have build-pkg and skip-pkg... if that is the case, we build it, but we set a warning + if ($Env:BuildPackage -eq "True" -and $Env:SkipPackages -eq "True") { + Write-Host "##vso[task.logissue type=warning]'build-package' and 'skip-packages' are both present. Building packages in case of a doubt." + Write-Host "##vso[task.setvariable variable=BuildPkgs;isOutput=true]True" + } + + # if we want to have device tests, we do need the pkgs so that we can fwd them to the device tests + if ($Env:TriggerDeviceTests -eq "True") { + Write-Host "##vso[task.setvariable variable=BuildPkgs;isOutput=true]True" + Write-Host "##vso[task.setvariable variable=RunDeviceTests;isOutput=true]True" + } + + if ($Env:SkipNugets -eq "True") { + Write-Host "##vso[task.setvariable variable=BuildNugets;isOutput=true]False" + } else { + Write-Host "##vso[task.setvariable variable=BuildNugets;isOutput=true]True" + } + + if ($Env:SkipSigning -eq "True") { + Write-Host "##vso[task.setvariable variable=SignPkgs;isOutput=true]False" + } else { + Write-Host "##vso[task.setvariable variable=SignPkgs;isOutput=true]True" + } + + if ($Env:SkipExternalTests -eq "True") { + Write-Host "##vso[task.setvariable variable=RunExternalTests;isOutput=true]False" + } else { + Write-Host "##vso[task.setvariable variable=RunExternalTests;isOutput=true]True" + } + + if ($Env:SkipPackagedXamarinMacTests -eq "True") { + Write-Host "##vso[task.setvariable variable=RunMacTests;isOutput=true]False" + } else { + Write-Host "##vso[task.setvariable variable=RunMacTests;isOutput=true]True" + } + + if ($Env:SkipPublicJenkins -eq "True") { + Write-Host "##vso[task.setvariable variable=SkipPublicJenkins;isOutput=true]True" + } else { + Write-Host "##vso[task.setvariable variable=SkipPublicJenkins;isOutput=true]False" + } + + Write-Host "##vso[task.setvariable variable=RunSampleTests;isOutput=true]$Env:RunSampleTests" + Write-Host "##vso[task.setvariable variable=RunInternalTests;isOutput=true]$Env:RunInternalTests" + + } else { + # set the defaults, all the things! o/ + Write-Host "##vso[task.setvariable variable=IsPR;isOutput=true]False" + + # build pkg, nugets and sign them + Write-Host "##vso[task.setvariable variable=BuildPkgs;isOutput=true]True" + Write-Host "##vso[task.setvariable variable=BuildNugets;isOutput=true]True" + Write-Host "##vso[task.setvariable variable=SignPkgs;isOutput=true]True" + + # tests, run all of them, internal, external, mac but not sample tests + Write-Host "##vso[task.setvariable variable=RunInternalTests;isOutput=true]True" + Write-Host "##vso[task.setvariable variable=RunExternalTests;isOutput=true]True" + Write-Host "##vso[task.setvariable variable=RunMacTests;isOutput=true]True" + Write-Host "##vso[task.setvariable variable=RunSampleTests;isOutput=true]False" + Write-Host "##vso[task.setvariable variable=SkipPublicJenkins;isOutput=true]False" + + # if a developer decided to trigger one without device tests, allow it + if ($Env:RUN_DEVICE_TESTS -eq "true") { + Write-Host "##vso[task.setvariable variable=RunDeviceTests;isOutput=true]True" + } else { + Write-Host "##vso[task.setvariable variable=RunDeviceTests;isOutput=true]False" + } + } + + name: configuration + displayName: "Parse PR labels" + timeoutInMinutes: 5 + env: + RUN_DEVICE_TESTS: '${{ parameters.runDeviceTests }}' + +- bash: | + set -x + set -e + + if [[ "$IsPR" == "True" ]]; then + echo "Xamarin private packages NOT configured. Building a PR." + CONFIGURE_FLAGS="" + else + echo "Xamarin private packages configured." + CONFIGURE_FLAGS="--enable-xamarin" + fi + + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-dotnet --enable-install-source" + + cd $(Build.SourcesDirectory)/xamarin-macios/ + ./configure $CONFIGURE_FLAGS + echo $(cat $(Build.SourcesDirectory)/xamarin-macios/configure.inc) + env: + IsPR: $(configuration.IsPR) + displayName: "Configure build" + timeoutInMinutes: 5 + +# Actual build of the project +- bash: | + set -x + set -e + time make -C $(Build.SourcesDirectory)/xamarin-macios/ reset + time make -C $(Build.SourcesDirectory)/xamarin-macios/ all -j8 + time make -C $(Build.SourcesDirectory)/xamarin-macios/ install -j8 + displayName: 'Build' + timeoutInMinutes: 180 + +# build not signed .pkgs for the SDK +- bash: | + set -x + set -e + rm -Rf $(Build.SourcesDirectory)/package/*.pkg + rm -Rf $(Build.SourcesDirectory)/package/notarized/*.pkg + time make -C $(Build.SourcesDirectory)/xamarin-macios/ package + + # output vars for other steps to use and not need to recomputed the paths + IOS_PKG=$(find $(Build.SourcesDirectory)/package -type f -name "xamarin.ios-*" | xargs basename) + if [ -z "$IOS_PKG" ]; then + echo "Xamarin.iOS package not found." + else + IOS_PKG="$(Build.SourcesDirectory)/package/$IOS_PKG" + echo "##vso[task.setvariable variable=IOS_PKG;]$IOS_PKG" + echo "Xamarin.iOS package found at $IOS_PKG" + fi + + MAC_PKG=$(find $(Build.SourcesDirectory)/package -type f -name "xamarin.mac-*" | xargs basename) + if [ -z "$MAC_PKG" ]; then + echo "Xamarin.Mac package not found." + else + MAC_PKG="$(Build.SourcesDirectory)/package/$MAC_PKG" + echo "##vso[task.setvariable variable=MAC_PKG;]$MAC_PKG" + echo "Xamarin.Mac package found at $MAC_PKG" + fi + name: packages + displayName: 'Build Packages' + condition: and(succeeded(), contains(variables['configuration.BuildPkgs'], 'True')) + timeoutInMinutes: 180 + +# build nugets +- bash: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/automation/scripts/bash/build-nugets.sh + displayName: 'Build Nugets' + condition: and(succeeded(), contains(variables['configuration.BuildNugets'], 'True')) + continueOnError: true # should not stop the build since is not official just yet. + timeoutInMinutes: 180 + +- bash: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/automation/scripts/bash/productsign.sh + env: + PRODUCTSIGN_KEYCHAIN_PASSWORD: $(xma-password) + displayName: 'Signing PR Build' + condition: and(succeeded(), contains(variables['configuration.SignPkgs'], 'True'), contains(variables['configuration.IsPr'], 'True')) + + # Ensure virtualenv is on the PATH +- template: set-path/v1.yml@templates + parameters: + prependToPath: '/Users/builder/Library/Python/2.7/bin' + +- bash: | + VIRTUAL_ENV_PATH=$(Build.SourcesDirectory)/venv + pip install virtualenv + virtualenv "$VIRTUAL_ENV_PATH" --system-site-packages + source "$VIRTUAL_ENV_PATH/bin/activate" + pip install python-magic + + security unlock-keychain -p $PRODUCTSIGN_KEYCHAIN_PASSWORD builder.keychain + PACKAGES="$IOS_PKG $MAC_PKG" + echo "Packages found at $PACKAGES" + + echo "$PACKAGES" | xargs python $(Build.SourcesDirectory)/release-scripts/sign_and_notarize.py -a "$APP_ID" -i "$INSTALL_ID" -u "$APPLE_ACCOUNT" -p "$APPLE_PASS" -t "$TEAM_ID" -d $(Build.SourcesDirectory)/package/notarized -e "$MAC_ENTITLEMENTS" -k "$KEYCHAIN" + + deactivate + rm -Rf "$VIRTUAL_ENV_PATH" + env: + PRODUCTSIGN_KEYCHAIN_PASSWORD: $(OSX_KEYCHAIN_PASS) + MAC_ENTITLEMENTS: $(Build.SourcesDirectory)/xamarin-macios/mac-entitlements.plist + APP_ID: $(XamarinAppId) + INSTALL_ID: $(XamarinAppId) + APPLE_ACCOUNT: $(XamarinUserId) + APPLE_PASS: $(XamarinPassword) + TEAM_ID: $(TeamID) + KEYCHAIN: $(SigningKeychain) + name: notarize + displayName: 'Signing Release Build' + condition: and(succeeded(), contains(variables['configuration.SignPkgs'], 'True'), contains(variables['configuration.IsPr'], 'False')) + timeoutInMinutes: 90 + +- template: generate-workspace-info.yml@templates + parameters: + GitHubToken: $(GitHub.Token) + ArtifactDirectory: $(Build.SourcesDirectory)/package-internal + +- template: uninstall-certificates/v1.yml@templates + parameters: + HostedMacKeychainPassword: $(OSX_KEYCHAIN_PASS) + +# upload each of the pkgs into the pipeline artifacts +- task: PublishPipelineArtifact@1 + displayName: 'Publish Build Artifacts' + inputs: + targetPath: $(Build.SourcesDirectory)/package + artifactName: package + continueOnError: true + +- task: PublishPipelineArtifact@1 + displayName: 'Publish Build Internal Artifacts' + inputs: + targetPath: $(Build.SourcesDirectory)/package-internal + artifactName: package-internal + continueOnError: true + +- bash: | + set -x + set -e + + make -C $(Build.SourcesDirectory)/xamarin-macios/tests package-tests + displayName: 'Package Xamarin.mac tests' + condition: and(succeeded(), contains(variables['configuration.RunMacTests'], 'True')) + continueOnError: true # not a terrible blocking issue + +- task: PublishPipelineArtifact@1 + displayName: 'Publish Xamarin.Mac tests' + inputs: + targetPath: $(Build.SourcesDirectory)/xamarin-macios/tests/*.7z + artifactName: package-internal + condition: and(succeeded(), contains(variables['configuration.RunMacTests'], 'True')) + continueOnError: true + +- bash: | + make -j8 -C $(Build.SourcesDirectory)/xamarin-macios/tools/apidiff jenkins-api-diff + + # remove some files that do not need to be uploaded + cd $(Build.SourcesDirectory)/xamarin-macios/tools/apidiff/ + rm -Rf *.exe *.pdb *.stamp *.zip *.sh ./references ./temp + displayName: 'API diff (from stable)' + condition: and(succeeded(), contains(variables['configuration.SkipPublicJenkins'], 'False')) + continueOnError: true + env: + BUILD_REVISION: 'jenkins' + +- task: ArchiveFiles@1 + displayName: 'Archive API diff (from stable)' + inputs: + rootFolder: $(Build.SourcesDirectory)/xamarin-macios/tools/apidiff + includeRootFolder: false + archiveFile: '$(Build.ArtifactStagingDirectory)/apidiff-stable.zip' + condition: and(succeeded(), contains(variables['configuration.SkipPublicJenkins'], 'False')) + continueOnError: true + +- task: PublishPipelineArtifact@1 + displayName: 'Publish API diff (from stable)' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)/apidiff-stable.zip' + artifactName: apidiff-stable + condition: and(succeeded(), contains(variables['configuration.SkipPublicJenkins'], 'False')) + continueOnError: true + +- bash: | + set -x + set -e + echo "Running tests on $AGENT_NAME" + echo "##vso[task.setvariable variable=TESTS_BOT;isOutput=true]$AGENT_NAME" + + echo "##vso[task.setvariable variable=TESTS_RAN;isOutput=true]True" + rm -rf ~/.config/.mono/keypairs/ + + RC=0 + make -C $(Build.SourcesDirectory)/xamarin-macios/tests "$TARGET" || RC=$? + + if [ $RC -eq 0 ]; then + echo "##vso[task.setvariable variable=TESTS_JOBSTATUS;isOutput=true]Succeeded" + else + echo "##vso[task.setvariable variable=TESTS_JOBSTATUS;isOutput=true]Failed" + fi + + if test -f "$(Build.SourcesDirectory)/xamarin-macios//jenkins/failure-stamp"; then + echo "Something went wrong:" + cat "$(Build.SourcesDirectory)/xamarin-macios//jenkins/pr-comments.md" + exit 1 + fi + displayName: 'Run tests' + name: runTests # not to be confused with the displayName, this is used to later use the name of the step to access the output variables from an other job + timeoutInMinutes: 600 + condition: succeededOrFailed() # we do not care about the previous process cleanup + enabled: ${{ parameters.runTests }} + env: + BUILD_REVISION: jenkins + TARGET: 'wrench-jenkins' + VSDROPS_URI: '${{ parameters.vsdropsPrefix }}/$(Build.BuildNumber)/$(Build.BuildId);/tests/' # uri used to create the vsdrops index using full uri + +# Upload TestSummary as an artifact. +- task: PublishPipelineArtifact@1 + displayName: 'Publish Artifact: TestSummary' + inputs: + targetPath: 'xamarin-macios/tests/TestSummary.md' + artifactName: TestSummary-sim + continueOnError: true + condition: and(succeededOrFailed(), contains(variables['runTests.TESTS_RAN'], 'True')) # if tests did not run, there is nothing to do + +- pwsh: | + $summaryName = "TestSummary.md" + $summaryPath = "$Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin-macios/tests/TestSummary.md" + Write-Host "##vso[task.addattachment type=Distributedtask.Core.Summary;name=$summaryName;]$summaryPath" + displayName: Set TestSummary + condition: and(succeededOrFailed(), contains(variables['runTests.TESTS_RAN'], 'True')) # if tests did not run, there is nothing to do + +# Archive files for the Html Report so that the report can be easily uploaded as artifacts of the build. +- task: ArchiveFiles@1 + displayName: 'Archive HtmlReport' + inputs: + rootFolder: 'xamarin-macios/jenkins-results' + includeRootFolder: false + archiveFile: '$(Build.ArtifactStagingDirectory)/HtmlReport.zip' + continueOnError: true + condition: and(succeededOrFailed(), contains(variables['runTests.TESTS_RAN'], 'True')) # if tests did not run, there is nothing to do + +# Create HtmlReport artifact. This serves two purposes: +# 1. It is the way we are going to share the HtmlReport with the publish_html job that is executed on a Windows machine. +# 2. Users can download this if they want. +- task: PublishPipelineArtifact@1 + displayName: 'Publish Artifact: HtmlReport' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)/HtmlReport.zip' + artifactName: HtmlReport-sim + continueOnError: true + condition: and(succeededOrFailed(), contains(variables['runTests.TESTS_RAN'], 'True')) # if tests did not run, there is nothing to do diff --git a/tools/devops/automation/templates/packages/configure.yml b/tools/devops/automation/templates/packages/configure.yml new file mode 100644 index 0000000000..94d7814a0f --- /dev/null +++ b/tools/devops/automation/templates/packages/configure.yml @@ -0,0 +1,83 @@ +# This job will parse all the labels present in a PR, will set +# the tags for the build AND will set a number of configuration +# variables to be used by the rest of the projects +steps: +- checkout: self # https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#checkout + clean: true # Executes: git clean -ffdx && git reset --hard HEAD + submodules: false + +- pwsh: | + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY/tools/devops/automation/scripts/GitHub.psm1 + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY/tools/devops/automation/scripts/VSTS.psm1 + + $buildReason = "$(Build.Reason)" + $buildSourceBranchName = "$(Build.SourceBranchName)" + $buildSourceBranch = "$(Build.SourceBranch)" + + Write-Host "buildReason: ${buildReason}" + Write-Host "buildSourceBranchName: ${buildSourceBranchName}" + Write-Host "buildSourceBranch: $buildSourceBranch" + + # the following list will be used to track the tags and set them in VSTS to make the monitoring person life easier + [System.Collections.Generic.List[string]]$tags = @() + + if ($buildReason -eq "PullRequest" -or (($buildReason -eq "Manual") -and ($buildSourceBranchName -eq "merge")) ) { + Write-Host "Configuring build from PR." + # This is an interesting step, we do know we are dealing with a PR, but we need the PR id to + # be able to get the labels, the buildSourceBranch follows the pattern: refs/pull/{ChangeId}/merge + # we could use a regexp but then we would have two problems instead of one + $changeId = $buildSourceBranch.Replace("refs/pull/", "").Replace("/merge", "") + $prInfo = Get-GitHubPRInfo -ChangeId $changeId + Write-Host $prInfo + + # make peoples life better, loop over the labels and add them as tags in the vsts build + foreach ($labelInfo in $prInfo.labels) { + $labelName = $labelInfo.name + Write-Host "Found label $labelName" + $tags.Add($labelName) + $tagsCount = $tags.Count + Write-Host "Tags count $tagsCount" + } + # special tag, we want to know if we are using a pr + $tags.Add("prBuild") + + # special tag, lets add the target branch, will be useful to the users + $ref = $prInfo.base.ref + $tags.Add("$ref") + + # set output variables based on the git labels + $labelsOfInterest = @( + "build-package", + "run-internal-tests", + "skip-packages", + "skip-nugets", + "skip-signing", + "skip-external-tests", + "trigger-device-tests", + "run-sample-tests", + "skip-packaged-xamarin-mac-tests", + "skip-public-jenkins", + "skip-api-comparison" + ) + + foreach ($l in $labelsOfInterest) { + $labelPresent = 1 -eq ($prInfo.labels | Where-Object { $_.name -eq "$l"}).Count + Write-Host "##vso[task.setvariable variable=$l;isOutput=true]$labelPresent" + } + + Write-Host "##vso[task.setvariable variable=prBuild;isOutput=true]True" + } else { + # set the name of the branch under build + $tags.Add("$buildSourceBranchName") + Write-Host "##vso[task.setvariable variable=prBuild;isOutput=true]False" + } + # set the tags using the cmdlet + $tagCount = $tags.Count + Write-Host "Found '$tagsCount' tags" + Set-BuildTags -Tags $tags.ToArray() + env: + BUILD_REVISION: $(Build.SourceVersion) + GITHUB_TOKEN: $(GitHub.Token) + ACCESSTOKEN: $(AzDoBuildAccess.Token) + name: labels + displayName: 'Configure build' diff --git a/tools/devops/automation/templates/packages/download-artifacts.yml b/tools/devops/automation/templates/packages/download-artifacts.yml new file mode 100644 index 0000000000..597c9c44a8 --- /dev/null +++ b/tools/devops/automation/templates/packages/download-artifacts.yml @@ -0,0 +1,38 @@ +steps: + +- checkout: self + persistCredentials: true + +# download the common artifacts + the api diff +- template: ../common/download-artifacts.yml + parameters: + devicePrefix: sim + +# Download the Html Report that was added by the tests job. +- task: DownloadPipelineArtifact@2 + displayName: 'Download API diff (from stable)' + inputs: + patterns: 'apidiff-stable/apidiff-stable.zip' + allowFailedBuilds: true + path: $(System.DefaultWorkingDirectory)/Reports + +- task: ExtractFiles@1 + displayName: 'Extract API diff (from stable)' + inputs: + archiveFilePatterns: '$(System.DefaultWorkingDirectory)/Reports/apidiff-stable/apidiff-stable.zip' + destinationFolder: '$(System.DefaultWorkingDirectory)/apidiff-stable' + +- powershell: | + Get-ChildItem -Recurse $Env:SYSTEM_DEFAULTWORKINGDIRECTORY + + Write-Host "##vso[task.setvariable variable=STABLE_APIDDIFF_PATH]$Env:SYSTEM_DEFAULTWORKINGDIRECTORY\apidiff-stable" + displayName: Pusblish apidiff paths + name: apidiff # not to be confused with the displayName, this is used to later use the name of the step to access the output variables from an other job + +# download the artifacts.json, which will use to find the URI of the built pkg to later be given to the user +- task: DownloadPipelineArtifact@2 + displayName: Download artifacts.json + inputs: + patterns: '**/*.json' + allowFailedBuilds: true + path: $(Build.SourcesDirectory)/artifacts diff --git a/tools/devops/automation/templates/packages/publish-html.yml b/tools/devops/automation/templates/packages/publish-html.yml new file mode 100644 index 0000000000..24e86d51cb --- /dev/null +++ b/tools/devops/automation/templates/packages/publish-html.yml @@ -0,0 +1,79 @@ + +# Job that will download the other artifact from the tests job and will publish them in the +# vsdrops + +########################################################### +# WARNING WARNING WARNING WARNING WARNING WARNING WARNING # +########################################################### + +# This job is executed on WINDOWS! make sure you DO NOT USE +# bash or linux file paths on scripts. Another important +# details is that System.DefaultWorkingDirectory +# on mac os x points on the top dir while on windows +# is the checked out dir + +parameters: + +- name: vsdropsPrefix + type: string + +steps: + +- checkout: self + persistCredentials: true + +- template: download-artifacts.yml + +- pwsh: | + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\xamarin-macios\tools\devops\automation\scripts\GitHub.psm1 + + $apiDiffRoot = "$Env:STABLE_APIDDIFF_PATH" + $filePatterns = @{ + "iOS" = "ios-*.md"; + "macOS"="macos-*.md"; + "tvOS"="tvos-*.md"; + "watchOS"="watchos-*.md" + } + + [System.Collections.Generic.List[string]]$gistsObj = @() + [System.Collections.Generic.List[string]]$gists = @{} + + foreach ($key in $filePatterns.Keys) { + $filter = $filePatterns[$key] + $fileName = Get-ChildItem $apiDiffRoot -Filter $filter -Name + if ($fileName) { + $obj = New-GistObjectDefinition -Name $fileName -Path "$apiDiffRoot\$fileName" -Type "markdown" + $gistsObj.Add($obj) + # create a gist just for this file + $url = New-GistWithFiles -Description "$key API diff from stable" -Files @($obj) + Write-Host "New gist created at $url" + $gists.Add($key, $url) + } + } + + # create a gist with all diffs + $url = New-GistWithFiles -Description "API diff from stable (all platforms)" -Files $gistsObj + $gists.Add("all", $url) + + # set env variables to be used in later jobs + foreach ($key in $gists.Keys) { + $envVarName = "$($key.ToUpper())_STABLE_URL" # results in ALL_STABLE_URL/IOS_STABLE_URL etc + $url = $gists[$key] + Write-Host "##vso[task.setvariable variable=$envVarName]$url" + } + displayName: 'Create API from stable diff gists' + timeoutInMinutes: 1 + env: + GITHUB_TOKEN: $(GitHub.Token) + +- pwsh: | + Write-Host "Writing comment!" + displayName: 'Generating GitHub Comment' + condition: always() + timeoutInMinutes: 1 + +- pwsh: | + Get-ChildItem -Recurse $Env:SYSTEM_DEFAULTWORKINGDIRECTORY + displayName: 'Add summaries' + condition: always() + timeoutInMinutes: 1 diff --git a/tools/devops/automation/templates/packages/stage.yml b/tools/devops/automation/templates/packages/stage.yml new file mode 100644 index 0000000000..a00e8d146b --- /dev/null +++ b/tools/devops/automation/templates/packages/stage.yml @@ -0,0 +1,149 @@ +# template that contains all the different steps to create a pkgs, publish the results and provide feedback to the +# developers in github. +parameters: +- name: runTests + type: boolean + default: true + +- name: vsdropsPrefix + type: string + +- name: runDeviceTests + type: boolean + default: true + +jobs: +- job: configure + displayName: 'Configure build' + pool: + vmImage: ubuntu-latest + + steps: + - template: configure.yml + +- job: AgentPoolSelector # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml + pool: # Consider using an agentless (server) job here, but would need to host selection logic as an Azure function: https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#server + vmImage: ubuntu-latest + steps: + - checkout: none # https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#checkout + + # Selects appropriate agent pool based on trigger type (PR or CI) + - template: ../agent-pool-selector.yml + +- job: build + dependsOn: + - AgentPoolSelector + - configure + displayName: 'Build packages' + timeoutInMinutes: 1000 + variables: + AgentPoolComputed: $[ dependencies.AgentPoolSelector.outputs['setAgentPool.AgentPoolComputed'] ] + # add all the variables that have been parsed by the configuration step. Could we have a less verbose way?? + # + # build-package + # run-internal-tests + # skip-packages + # skip-nugets + # skip-signing + # skip-external-tests + # trigger-device-tests + # run-sample-tests + # skip-packaged-xamarin-mac-tests + BuildPackage: $[ dependencies.configure.outputs['labels.build-package'] ] + RunInternalTests: $[ dependencies.configure.outputs['labels.run-internal-tests'] ] + SkipPackages: $[ dependencies.configure.outputs['labels.skip-packages'] ] + SkipNugets: $[ dependencies.configure.outputs['labels.skip-nugets'] ] + SkipSigning: $[ dependencies.configure.outputs['labels.skip-signing'] ] + SkipExternalTests: $[ dependencies.configure.outputs['labels.skip-external-tests'] ] + TriggerDeviceTests: $[ dependencies.configure.outputs['labels.trigger-device-tests'] ] + RunSampleTests: $[ dependencies.configure.outputs['labels.run-sample-tests'] ] + SkipPackagedXamarinMacTests: $[ dependencies.configure.outputs['labels.skip-packaged-xamarin-mac-tests'] ] + SkipPublicJenkins: $[ dependencies.configure.outputs['labels.skip-public-jenkins'] ] + SkipApiComparison: $[ dependencies.configure.outputs['labels.skip-api-comparison'] ] + # set the branch variable name, this is required by jenkins and we have a lot of scripts that depend on it + BRANCH_NAME: $(Build.SourceBranchName) + pool: + name: $(AgentPoolComputed) + demands: + - Agent.OS -equals Darwin + - Agent.OSVersion -equals 10.15 + workspace: + clean: all + + steps: + - template: build.yml + parameters: + runTests: ${{ parameters.runTests }} + runDeviceTests: ${{ parameters.runDeviceTests }} + vsdropsPrefix: ${{ parameters.vsdropsPrefix }} + +- job: upload_azure_blob + displayName: 'Upload packages to Azure' + timeoutInMinutes: 1000 + dependsOn: build # can start as soon as the packages are available + condition: and(succeeded(), contains (dependencies.build.outputs['configuration.BuildPkgs'], 'True')) # only run when we do have pkgs + + variables: + Parameters.outputStorageUri: '' + + pool: + vmImage: 'windows-latest' + workspace: + clean: all + steps: + - template: upload-azure.yml + +- job: upload_vsdrops + displayName: 'Upload test results to VSDrops' + timeoutInMinutes: 1000 + dependsOn: build # can start as soon as the tests are done + condition: and(succeededOrFailed() , contains (dependencies.build.outputs['runTests.TESTS_RAN'], 'True')) # only run when we did run the tests + + pool: + vmImage: 'windows-latest' + workspace: + clean: all + steps: + - template: ../common/upload-vsdrops.yml + parameters: + devicePrefix: sim + +- job: upload_vsts_tests + displayName: 'Upload xml to vsts' + timeoutInMinutes: 1000 + dependsOn: build # can start as soon as the tests are done + condition: and(succeededOrFailed() , contains (dependencies.build.outputs['runTests.TESTS_RAN'], 'True')) # only run when we did run the tests + + pool: + vmImage: 'windows-latest' + workspace: + clean: all + steps: + - template: ../common/upload-vsts-tests.yml + parameters: + devicePrefix: sim + +- job: publish_html + displayName: 'Publish Html report in VSDrops' + timeoutInMinutes: 1000 + dependsOn: # has to wait for the tests to be done AND the data to be uploaded + - build + - upload_azure_blob + - upload_vsdrops + - upload_vsts_tests + condition: succeededOrFailed() + variables: + # Define the variable FOO from the previous job + # Note the use of single quotes! + TESTS_BOT: $[ dependencies.build.outputs['runTests.TESTS_BOT'] ] + TESTS_JOBSTATUS: $[ dependencies.build.outputs['runTests.TESTS_JOBSTATUS'] ] + pool: + vmImage: 'windows-latest' + workspace: + clean: all + steps: + - template: ../common/publish-html.yml + parameters: + statusContext: "Build" + vsdropsPrefix: ${{ parameters.vsdropsPrefix }} + devicePrefix: sim diff --git a/tools/devops/automation/templates/packages/upload-azure.yml b/tools/devops/automation/templates/packages/upload-azure.yml new file mode 100644 index 0000000000..bf3534e5e6 --- /dev/null +++ b/tools/devops/automation/templates/packages/upload-azure.yml @@ -0,0 +1,183 @@ +steps: + +- checkout: self + persistCredentials: true + +# Download the Html Report that was added by the tests job. +- task: DownloadPipelineArtifact@2 + displayName: Download packages + inputs: + patterns: '**' + allowFailedBuilds: true + path: $(Build.SourcesDirectory)/artifacts + +- powershell : | + $packagePrefix = "https://bosstoragemirror.blob.core.windows.net/wrench/$Env:VIRTUAL_PATH/package" + $files = Get-ChildItem -Path "$(Build.SourcesDirectory)\artifacts\package" -File -Force -Name + $manifestFile = "$(Build.SourcesDirectory)\artifacts\package\manifest" + foreach ($f in $files) { + Add-Content -Path "$manifestFile" -Value "$packagePrefix/$f" + } + Add-Content -Path "$manifestFile" -Value "$packagePrefix/$artifacts.json" + Add-Content -Path "$manifestFile" -Value "$packagePrefix/manifest" + env: + VIRTUAL_PATH: $(Build.SourceBranchName)/$(Build.SourceVersion)/$(Build.BuildId) + displayName: "Build manifest" + +# Important needed for the next step +- template: generate-workspace-info.yml@templates + parameters: + GitHubToken: $(GitHub.Token) + ArtifactDirectory: $(Build.SourcesDirectory)/package-internal + +- task: AzureFileCopy@3 + displayName: 'Publish package to Azure' + name: upload + inputs: + SourcePath: $(Build.SourcesDirectory)/artifacts/package + azureSubscription: 'Azure Releng (7b4817ae-218f-464a-bab1-a9df2d99e1e5)' + Destination: AzureBlob + storage: bosstoragemirror + ContainerName: wrench + BlobPrefix: $(Build.SourceBranchName)/$(Build.SourceVersion)/$(Build.BuildId)/package # ideally, we would use a variable for this + outputStorageUri: Parameters.outputStorageUri + outputStorageContainerSasToken: PackageSasToken + +- task: AzureFileCopy@3 + displayName: 'Publish manifest to Azure' + inputs: + SourcePath: $(Build.SourcesDirectory)/artifacts/package/manifest + azureSubscription: 'Azure Releng (7b4817ae-218f-464a-bab1-a9df2d99e1e5)' + Destination: AzureBlob + storage: bosstoragemirror + ContainerName: wrench + BlobPrefix: jenkins/$(Build.SourceBranchName)/$(Build.SourceVersion) + outputStorageUri: Parameters.outputStorageUri + outputStorageContainerSasToken: PackageSasToken + +- task: AzureFileCopy@3 + displayName: 'Publish manifest to Azure as latest' + inputs: + SourcePath: $(Build.SourcesDirectory)/artifacts/package/manifest + azureSubscription: 'Azure Releng (7b4817ae-218f-464a-bab1-a9df2d99e1e5)' + Destination: AzureBlob + storage: bosstoragemirror + ContainerName: wrench + BlobPrefix: jenkins/$(Build.SourceBranchName)/latest + outputStorageUri: Parameters.outputStorageUri + outputStorageContainerSasToken: PackageSasToken + +- task: AzureFileCopy@3 + displayName: 'Publish manifest to Azure per commit' + inputs: + SourcePath: $(Build.SourcesDirectory)/artifacts/package/manifest + azureSubscription: 'Azure Releng (7b4817ae-218f-464a-bab1-a9df2d99e1e5)' + Destination: AzureBlob + storage: bosstoragemirror + ContainerName: wrench + BlobPrefix: jenkins/$(Build.SourceVersion) + outputStorageUri: Parameters.outputStorageUri + outputStorageContainerSasToken: PackageSasToken + +- powershell: | + $execPath="$Env:BUILD_SOURCESDIRECTORY\Xamarin.Build.Tasks\tools\BuildTasks\build-tasks.exe" + + if (-not (Test-Path $execPath -PathType Leaf)) { + Write-Host "Build task not found at $execPath!" + } + + $maciosPath="$Env:BUILD_SOURCESDIRECTORY" + + Write-Host "Exect path is $execPath" + Write-Host "Macios path is $maciosPath" + Write-Host "$Env:VIRTUAL_PATH" + Write-Host "Artifacts url wrench/$Env:VIRTUAL_PATH/package" + + Invoke-Expression "$execPath artifacts -s `"$maciosPath`" -a bosstoragemirror -c $Env:STORAGE_PASS -u `"wrench/$Env:VIRTUAL_PATH/package`" -d `"$(Build.SourcesDirectory)\artifacts\package`" -o `"$(Build.SourcesDirectory)\artifacts\package`"" + env: + VIRTUAL_PATH: $(Build.SourceBranchName)/$(Build.SourceVersion)/$(Build.BuildId) + GITHUB_AUTH_TOKEN: $(GitHub.Token) + STORAGE_PASS: $(auth-xamarin-bosstoragemirror-account-key) + displayName: 'Generate artifacts.json' + +# upload the artifacts.json to the build pipeline artifacts so that it can be consumed by other jobs to +# get the required urls +- task: PublishPipelineArtifact@1 + displayName: 'Publish Build Artifacts' + inputs: + targetPath: $(Build.SourcesDirectory)/artifacts/package/artifacts.json + artifactName: pkg-info + continueOnError: true + +- powershell: | + + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\tools\devops\automation\scripts\GitHub.psm1 + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\tools\devops\automation\scripts\VSTS.psm1 + + Dir "$(Build.SourcesDirectory)\artifacts\package" + + # $Env:STORAGE_URI/ ends with a /, annoying + $pkgsVirtualUrl = "$Env:STORAGE_URI" +"$(Build.SourceBranchName)/$(Build.SourceVersion)/$(Build.BuildId)/package" + Write-Host "Urls is $pkgsVirtualUrl" + + $pkgsPath = "$(Build.SourcesDirectory)\artifacts\package" + + $iOSPkg = Get-ChildItem -Path $pkgsPath -File -Force -Name xamarin.ios-*.pkg + Write-Host "iOS PKG is $iOSPkg" + + $macPkg = Get-ChildItem -Path $pkgsPath -File -Force -Name xamarin.mac-*.pkg + Write-Host "mac PKG is $iOSPkg" + + if (Test-Path "$pkgsPath\$iOSPkg" -PathType Leaf) { + Set-GitHubStatus -Status "success" -Description $iOSPkg -TargetUrl "$pkgsVirtualUrl/$iOSPkg" -Context "PKG-Xamarin.iOS" + } else { + Set-GitHubStatus -Status "error" -Description "xamarin.ios pkg not found" -Context "PKG-Xamarin.iOS" + } + + if (Test-Path "$pkgsPath\notarized\xamarin.ios-*.pkg" -PathType Leaf) { + Set-GitHubStatus -Status "success" -Description "$iOSPkg (Notarized)" -TargetUrl "$pkgsVirtualUrl/notarized/$iOSPkg" -Context "PKG-Xamarin.iOS-notarized" + } else { + Set-GitHubStatus -Status "error" -Description "Notarized xamarin.ios pkg not found" -Context "PKG-Xamarin.iOS-notarized" + } + + if (Test-Path "$pkgsPath\xamarin.mac-*.pkg" -PathType Leaf) { + Set-GitHubStatus -Status "success" -Description "$pkgsPath" -TargetUrl "$pkgsVirtualUrl/$macPkg" -Context "PKG-Xamarin.Mac" + } else { + Set-GitHubStatus -Status "error" -Description "xamarin.mac pkg not found." -Context "PKG-Xamarin.Mac" + } + + if (Test-Path "$pkgsPath\notarized\xamarin.mac-*.pkg" -PathType Leaf) { + Set-GitHubStatus -Status "success" -Description "$pkgsPath (Notarized)" -TargetUrl "$pkgsVirtualUrl/notarized/$macPkg" -Context "PKG-Xamarin.Mac-notarized" + } else { + Set-GitHubStatus -Status "error" -Description "Notarized xamarin.mac pkg not found." -Context "PKG-Xamarin.Mac-notarized" + } + + if (Test-Path "$pkgsPath\bundle.zip" -PathType Leaf) { + Set-GitHubStatus -Status "success" -Description "bundle.zip" -TargetUrl "$pkgsVirtualUrl/bundle.zip" -Context "bundle.zip" + } else { + Set-GitHubStatus -Status "error" -Description "bundle.zip not found." -Context "bundle.zip" + } + + if (Test-Path "$pkgsPath\msbuild.zip" -PathType Leaf) { + Set-GitHubStatus -Status "success" -Description "msbuild.zip" -TargetUrl "$pkgsVirtualUrl/msbuild.zip" -Context "msbuild.zip" + } else { + Set-GitHubStatus -Status "error" -Description "msbuild.zip not found." -Context "msbuild.zip" + } + + $nugets = $files = Get-ChildItem -Path $pkgsPath -Filter *.nupkg -File -Name + + foreach ($n in $nugets) { + Set-GitHubStatus -Status "success" -Description "$n" -TargetUrl "$pkgsVirtualUrl/$n" -Context "$n" + } + + $msi = $files = Get-ChildItem -Path $pkgsPath -Filter *.msi -File -Name + + foreach ($n in $msi) { + Set-GitHubStatus -Status "success" -Description "$n" -TargetUrl "$pkgsVirtualUrl/$n" -Context "$n" + } + env: + BUILD_REVISION: $(Build.SourceVersion) + GITHUB_TOKEN: $(GitHub.Token) + ACCESSTOKEN: $(System.AccessToken) + STORAGE_URI: $(Parameters.outputStorageUri) + displayName: 'Set GithubStatus' diff --git a/tools/devops/build-provisioning.csx.in b/tools/devops/build-provisioning.csx.in new file mode 100644 index 0000000000..00aba51a2a --- /dev/null +++ b/tools/devops/build-provisioning.csx.in @@ -0,0 +1,17 @@ +#r "_provisionator/provisionator.dll" + +using System.IO; +using System.Reflection; +using System.Linq; + +using static Xamarin.Provisioning.ProvisioningScript; + +// Provision Xcode using the xip name declared in Make.config +Xcode ("@XCODE_XIP_NAME@").XcodeSelect (allowUntrusted: true); + +// provisionator knows how to deal with this items +Item ("@MONO_PACKAGE@"); +Item ("@MIN_SHARPIE_URL@"); +Item ("@VS_PACKAGE@"); +DotNetCoreSdk ("@DOTNET_VERSION@"); +DotNetCoreSdk ("@DOTNET5_VERSION@"); diff --git a/tools/devops/device-tests-provisioning.csx.in b/tools/devops/device-tests-provisioning.csx.in index 02d8df5cf5..1f0df1b18b 100644 --- a/tools/devops/device-tests-provisioning.csx.in +++ b/tools/devops/device-tests-provisioning.csx.in @@ -14,6 +14,7 @@ Item (xcodeItem).XcodeSelect (allowUntrusted: true); Item ("@MONO_PACKAGE@"); Item ("@VS_PACKAGE@"); Item ("@XI_PACKAGE@"); +DotNetCoreSdk ("@DOTNET_VERSION@"); BrewPackage ("p7zip"); diff --git a/tools/devops/provision-brew-packages.csx b/tools/devops/provision-brew-packages.csx new file mode 100644 index 0000000000..728b20602a --- /dev/null +++ b/tools/devops/provision-brew-packages.csx @@ -0,0 +1,11 @@ +BrewPackages ( + "cmake", + "autoconf", + "automake", + "libtool", + "p7zip", + "python", + "libmagic", + "msitools", + "wget" + ); diff --git a/tools/devops/provision-shared.csx b/tools/devops/provision-shared.csx new file mode 100644 index 0000000000..8846c8be77 --- /dev/null +++ b/tools/devops/provision-shared.csx @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +using Newtonsoft.Json.Linq; + +using Xamarin.Provisioning; +using Xamarin.Provisioning.Model; + +var commit = Environment.GetEnvironmentVariable ("BUILD_SOURCEVERSION"); +var provision_from_commit = Environment.GetEnvironmentVariable ("PROVISION_FROM_COMMIT") ?? commit; + +// Looks for a variable either in the environment, or in current repo's Make.config. +// Returns null if the variable couldn't be found. +IEnumerable make_config = null; +string FindConfigurationVariable (string variable, string hash = "HEAD") +{ + var value = Environment.GetEnvironmentVariable (variable); + if (!string.IsNullOrEmpty (value)) + return value; + + if (make_config == null) { + try { + make_config = Exec ("git", "show", $"{hash}:Make.config"); + } catch { + Console.WriteLine ("Could not find a Make.config"); + return null; + } + } + foreach (var line in make_config) { + if (line.StartsWith (variable + "=", StringComparison.Ordinal)) + return line.Substring (variable.Length + 1); + } + + return null; +} + +string FindVariable (string variable) +{ + var value = FindConfigurationVariable (variable, provision_from_commit); + if (!string.IsNullOrEmpty (value)) + return value; + + throw new Exception ($"Could not find {variable} in environment nor in the commit's ({commit}) manifest."); +} + +void ExecVerbose (string filename, params string[] args) +{ + Console.WriteLine ($"{filename} {string.Join (" ", args)}"); + Exec (filename, args); +} + +bool IsAtLeastVersion(string actualVer, string minVer) +{ + if (actualVer.Equals(minVer, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + var actualVerChars = actualVer.ToCharArray(); + var minVerChars = minVer.ToCharArray(); + + var length = Math.Min (minVerChars.Length, actualVerChars.Length); + + var i = 0; + while (i < length) + { + if (actualVerChars[i] > minVerChars[i]) + { + return true; + } + else if (minVerChars[i] > actualVerChars[i]) + { + return false; + } + i++; + } + + if (actualVerChars.Length == minVerChars.Length) + { + return true; + } + + return actualVerChars.Length > minVerChars.Length; +} diff --git a/tools/devops/provision-xcode.csx b/tools/devops/provision-xcode.csx index 589e16857e..3a9c2e2586 100644 --- a/tools/devops/provision-xcode.csx +++ b/tools/devops/provision-xcode.csx @@ -1,63 +1,10 @@ -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -using Newtonsoft.Json.Linq; - -using Xamarin.Provisioning; -using Xamarin.Provisioning.Model; +#load "provision-shared.csx" // Provision Xcode // // Overrides: // * The current commit can be overridden by setting the PROVISION_FROM_COMMIT variable. -var commit = Environment.GetEnvironmentVariable ("BUILD_SOURCEVERSION"); -var provision_from_commit = Environment.GetEnvironmentVariable ("PROVISION_FROM_COMMIT") ?? commit; - -// Looks for a variable either in the environment, or in current repo's Make.config. -// Returns null if the variable couldn't be found. -IEnumerable make_config = null; -string FindConfigurationVariable (string variable, string hash = "HEAD") -{ - var value = Environment.GetEnvironmentVariable (variable); - if (!string.IsNullOrEmpty (value)) - return value; - - if (make_config == null) { - try { - make_config = Exec ("git", "show", $"{hash}:Make.config"); - } catch { - Console.WriteLine ("Could not find a Make.config"); - return null; - } - } - foreach (var line in make_config) { - if (line.StartsWith (variable + "=", StringComparison.Ordinal)) - return line.Substring (variable.Length + 1); - } - - return null; -} - -string FindVariable (string variable) -{ - var value = FindConfigurationVariable (variable, provision_from_commit); - if (!string.IsNullOrEmpty (value)) - return value; - - throw new Exception ($"Could not find {variable} in environment nor in the commit's ({commit}) manifest."); -} - -void ExecVerbose (string filename, params string[] args) -{ - Console.WriteLine ($"{filename} {string.Join (" ", args)}"); - Exec (filename, args); -} - void ListXcodes () { Console.WriteLine ($"Xcodes:"); diff --git a/tools/devops/python-dependencies.txt b/tools/devops/python-dependencies.txt new file mode 100644 index 0000000000..6863aff602 --- /dev/null +++ b/tools/devops/python-dependencies.txt @@ -0,0 +1 @@ +python-magic==0.4.15 From c381c2043c8a4bf5a950292e3a273c6a68271640 Mon Sep 17 00:00:00 2001 From: vs-mobiletools-engineering-service2 <67918504+vs-mobiletools-engineering-service2@users.noreply.github.com> Date: Thu, 7 Jan 2021 12:00:45 -0500 Subject: [PATCH 02/33] [CI][VSTS] Add the correct mac pkg file name. (#10355) The wrong variable was used and therefore the dir path was used instead of the mac pkg name. fixes: https://github.com/xamarin/maccore/issues/2353 Co-authored-by: Manuel de la Pena --- tools/devops/automation/templates/packages/upload-azure.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/devops/automation/templates/packages/upload-azure.yml b/tools/devops/automation/templates/packages/upload-azure.yml index bf3534e5e6..e1396fb9d0 100644 --- a/tools/devops/automation/templates/packages/upload-azure.yml +++ b/tools/devops/automation/templates/packages/upload-azure.yml @@ -126,7 +126,7 @@ steps: Write-Host "iOS PKG is $iOSPkg" $macPkg = Get-ChildItem -Path $pkgsPath -File -Force -Name xamarin.mac-*.pkg - Write-Host "mac PKG is $iOSPkg" + Write-Host "mac PKG is $macPkg" if (Test-Path "$pkgsPath\$iOSPkg" -PathType Leaf) { Set-GitHubStatus -Status "success" -Description $iOSPkg -TargetUrl "$pkgsVirtualUrl/$iOSPkg" -Context "PKG-Xamarin.iOS" @@ -141,13 +141,13 @@ steps: } if (Test-Path "$pkgsPath\xamarin.mac-*.pkg" -PathType Leaf) { - Set-GitHubStatus -Status "success" -Description "$pkgsPath" -TargetUrl "$pkgsVirtualUrl/$macPkg" -Context "PKG-Xamarin.Mac" + Set-GitHubStatus -Status "success" -Description "$macPkg" -TargetUrl "$pkgsVirtualUrl/$macPkg" -Context "PKG-Xamarin.Mac" } else { Set-GitHubStatus -Status "error" -Description "xamarin.mac pkg not found." -Context "PKG-Xamarin.Mac" } if (Test-Path "$pkgsPath\notarized\xamarin.mac-*.pkg" -PathType Leaf) { - Set-GitHubStatus -Status "success" -Description "$pkgsPath (Notarized)" -TargetUrl "$pkgsVirtualUrl/notarized/$macPkg" -Context "PKG-Xamarin.Mac-notarized" + Set-GitHubStatus -Status "success" -Description "$macPkg (Notarized)" -TargetUrl "$pkgsVirtualUrl/notarized/$macPkg" -Context "PKG-Xamarin.Mac-notarized" } else { Set-GitHubStatus -Status "error" -Description "Notarized xamarin.mac pkg not found." -Context "PKG-Xamarin.Mac-notarized" } From 72c04096b3284a9f5c478727cc26ee9e6b1ede95 Mon Sep 17 00:00:00 2001 From: vs-mobiletools-engineering-service2 <67918504+vs-mobiletools-engineering-service2@users.noreply.github.com> Date: Thu, 7 Jan 2021 12:01:11 -0500 Subject: [PATCH 03/33] [CI][VSTS] Ensure that xharness creates an index with the correct uris. (#10354) The prefix of the location of the logs depends on the platform where it was executed (else we step on them) but that was not added in the env var use by xharness. fixes: https://github.com/xamarin/maccore/issues/2349 Co-authored-by: Manuel de la Pena --- tools/devops/automation/templates/devices/build.yml | 2 +- tools/devops/automation/templates/packages/build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/devops/automation/templates/devices/build.yml b/tools/devops/automation/templates/devices/build.yml index 085522a6d7..025936f048 100644 --- a/tools/devops/automation/templates/devices/build.yml +++ b/tools/devops/automation/templates/devices/build.yml @@ -215,7 +215,7 @@ steps: WORKING_DIR: $(System.DefaultWorkingDirectory) TESTS_EXTRA_ARGUMENTS: ${{ parameters.testsLabels }} USE_XAMARIN_STORAGE: ${{ parameters.useXamarinStorage }} - VSDROPS_URI: '${{ parameters.vsdropsPrefix }}/$(Build.BuildNumber)/$(Build.BuildId);/tests/' # uri used to create the vsdrops index using full uri + VSDROPS_URI: '${{ parameters.vsdropsPrefix }}/$(Build.BuildNumber)/$(Build.BuildId)/${{ parameters.devicePrefix }};/tests/' # uri used to create the vsdrops index using full uri USE_TCP_TUNNEL: 'true' displayName: 'Run tests' name: runTests # not to be confused with the displayName, this is used to later use the name of the step to access the output variables from an other job diff --git a/tools/devops/automation/templates/packages/build.yml b/tools/devops/automation/templates/packages/build.yml index 21a50e02b4..c7ec4569f9 100644 --- a/tools/devops/automation/templates/packages/build.yml +++ b/tools/devops/automation/templates/packages/build.yml @@ -400,7 +400,7 @@ steps: env: BUILD_REVISION: jenkins TARGET: 'wrench-jenkins' - VSDROPS_URI: '${{ parameters.vsdropsPrefix }}/$(Build.BuildNumber)/$(Build.BuildId);/tests/' # uri used to create the vsdrops index using full uri + VSDROPS_URI: '${{ parameters.vsdropsPrefix }}/$(Build.BuildNumber)/$(Build.BuildId)/sim;/tests/' # uri used to create the vsdrops index using full uri # Upload TestSummary as an artifact. - task: PublishPipelineArtifact@1 From 49789d5d4cc0b4bf0a1db7a359419053a8ea5745 Mon Sep 17 00:00:00 2001 From: vs-mobiletools-engineering-service2 <67918504+vs-mobiletools-engineering-service2@users.noreply.github.com> Date: Fri, 8 Jan 2021 12:34:14 -0500 Subject: [PATCH 04/33] [Actions] Ensure the new bot is considered. (#10361) @monojenkins is no more. Co-authored-by: Manuel de la Pena --- .github/workflows/label-checker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label-checker.yml b/.github/workflows/label-checker.yml index 24aba17507..b862376130 100644 --- a/.github/workflows/label-checker.yml +++ b/.github/workflows/label-checker.yml @@ -17,7 +17,7 @@ jobs: - run: exit 0 name: 'Monojenkins PR' # always happy if monojenkins - if: github.actor == 'monojenkins' + if: github.actor == 'monojenkins' || github.actor == 'vs-mobiletools-engineering-service2' - run: exit 1 name: 'User PR with no labels' From fd8f7d3de3ba0969103740bf6723972e35c58be4 Mon Sep 17 00:00:00 2001 From: vs-mobiletools-engineering-service2 <67918504+vs-mobiletools-engineering-service2@users.noreply.github.com> Date: Fri, 8 Jan 2021 15:37:50 -0500 Subject: [PATCH 05/33] [xcode12.3] [CI][VSTS] Add comments to PR not only to commits. (#10358) The GitHup url to be used to create comments in PRs is diff to the one for comments. Use the build reason AND the changeID to identify if we are building due to a PR and use the correct url. fixes: https://github.com/xamarin/maccore/issues/2356 Co-authored-by: Manuel de la Pena Co-authored-by: Alex Soto --- tools/devops/automation/scripts/GitHub.psm1 | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/devops/automation/scripts/GitHub.psm1 b/tools/devops/automation/scripts/GitHub.psm1 index 3c6101ff05..b4714c150f 100644 --- a/tools/devops/automation/scripts/GitHub.psm1 +++ b/tools/devops/automation/scripts/GitHub.psm1 @@ -170,6 +170,8 @@ function New-GitHubComment { "SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT; "BUILD_DEFINITIONNAME" = $Env:BUILD_DEFINITIONNAME; "BUILD_REVISION" = $Env:BUILD_REVISION; + "BUILD_REASON" = $Env:BUILD_REASON; + "BUILD_SOURCEBRANCH" = $Env:BUILD_SOURCEBRANCH; "GITHUB_TOKEN" = $Env:GITHUB_TOKEN; } @@ -193,7 +195,16 @@ function New-GitHubComment { $msg.AppendLine() $msg.AppendLine("[Pipeline]($targetUrl) on Agent $Env:TESTS_BOT") # Env:TESTS_BOT is added by the pipeline as a variable coming from the execute tests job - $url = "https://api.github.com/repos/xamarin/xamarin-macios/commits/$Env:BUILD_REVISION/comments" + # if the build was due to PR, we want to write the comment in the PR rather than in the comment + if ($Env:BUILD_REASON -eq "PullRequest") { + # calcualte the change ID which is the PR number + $buildSourceBranch = $Env:BUILD_SOURCEBRANCH + $changeId = $buildSourceBranch.Replace("refs/pull/", "").Replace("/merge", "") + $url = "https://api.github.com/repos/xamarin/xamarin-macios/issues/$changeId/comments" + } else { + $url = "https://api.github.com/repos/xamarin/xamarin-macios/commits/$Env:BUILD_REVISION/comments" + } + $payload = @{ body = $msg.ToString() } From 094fa14c56099df314f954cefaa31484d1fe5ada Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Fri, 8 Jan 2021 15:50:55 -0500 Subject: [PATCH 06/33] [xcode12.3] Update Xcode to stable 12.3 (#10364) --- Make.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Make.config b/Make.config index 9214229153..b5f1948ba6 100644 --- a/Make.config +++ b/Make.config @@ -112,8 +112,8 @@ WATCHOS_NUGET_VERSION_FULL=$(WATCHOS_NUGET_VERSION_NO_METADATA)+$(NUGET_BUILD_ME # Xcode version should have both a major and a minor version (even if the minor version is 0) XCODE_VERSION=12.3 -XCODE_URL=http://xamarin-storage/bot-provisioning/xcodes/Xcode_12.3_Release_Candidate.xip -XCODE_DEVELOPER_ROOT=/Applications/Xcode_12.3.0-rc.app/Contents/Developer +XCODE_URL=http://xamarin-storage/bot-provisioning/xcodes/Xcode_12.3.xip +XCODE_DEVELOPER_ROOT=/Applications/Xcode_12.3.0.app/Contents/Developer XCODE_PRODUCT_BUILD_VERSION:=$(shell /usr/libexec/PlistBuddy -c 'Print :ProductBuildVersion' $(XCODE_DEVELOPER_ROOT)/../version.plist) # Mono version embedded in XI/XM (NEEDED_MONO_VERSION/BRANCH) are specified in mk/mono.mk From 9c45eb5ac78f18ced72a58f98430f8f6f08439dd Mon Sep 17 00:00:00 2001 From: vs-mobiletools-engineering-service2 <67918504+vs-mobiletools-engineering-service2@users.noreply.github.com> Date: Mon, 11 Jan 2021 11:11:00 -0800 Subject: [PATCH 07/33] [Actions] Add the user to all branches. Remove monojenkins. (#10370) Co-authored-by: Manuel de la Pena --- .github/workflows/label-checker.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/label-checker.yml b/.github/workflows/label-checker.yml index b862376130..b65a848864 100644 --- a/.github/workflows/label-checker.yml +++ b/.github/workflows/label-checker.yml @@ -17,14 +17,14 @@ jobs: - run: exit 0 name: 'Monojenkins PR' # always happy if monojenkins - if: github.actor == 'monojenkins' || github.actor == 'vs-mobiletools-engineering-service2' + if: github.actor == 'vs-mobiletools-engineering-service2' - run: exit 1 name: 'User PR with no labels' # failure if not monojenkins and no labels - if: github.actor != 'monojenkins' && join(github.event.pull_request.labels, ',') == '' + if: github.actor != 'vs-mobiletools-engineering-service2' && join(github.event.pull_request.labels, ',') == '' - run: exit 0 name: 'User PR with labels' # success if not monojenkins but labels - if: github.actor != 'monojenkins' && join(github.event.pull_request.labels, ',') != '' + if: github.actor != 'vs-mobiletools-engineering-service2' && join(github.event.pull_request.labels, ',') != '' From 79b58d9c78b81ef5e3ed4238a709f40c51144a5f Mon Sep 17 00:00:00 2001 From: vs-mobiletools-engineering-service2 <67918504+vs-mobiletools-engineering-service2@users.noreply.github.com> Date: Wed, 13 Jan 2021 09:35:43 -0500 Subject: [PATCH 08/33] [CI][VSTS] Rename path to fix API diff (#10390) Since 'packages' is a common dir name that is ignored. Revert the change in the .gitignore and rename the template path since it just means a one liner change in the entry yaml file. fixes: https://github.com/xamarin/maccore/issues/2359 Co-authored-by: Manuel de la Pena --- .gitignore | 2 +- tools/devops/automation/build-pipeline.yml | 2 +- tools/devops/automation/templates/{packages => build}/build.yml | 0 .../automation/templates/{packages => build}/configure.yml | 0 .../templates/{packages => build}/download-artifacts.yml | 0 .../automation/templates/{packages => build}/publish-html.yml | 0 tools/devops/automation/templates/{packages => build}/stage.yml | 0 .../automation/templates/{packages => build}/upload-azure.yml | 0 8 files changed, 2 insertions(+), 2 deletions(-) rename tools/devops/automation/templates/{packages => build}/build.yml (100%) rename tools/devops/automation/templates/{packages => build}/configure.yml (100%) rename tools/devops/automation/templates/{packages => build}/download-artifacts.yml (100%) rename tools/devops/automation/templates/{packages => build}/publish-html.yml (100%) rename tools/devops/automation/templates/{packages => build}/stage.yml (100%) rename tools/devops/automation/templates/{packages => build}/upload-azure.yml (100%) diff --git a/.gitignore b/.gitignore index db2745763a..fb41fd24c8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ _build *.pdb bin obj -./packages +packages ~.pmcs* .DS_Store jenkins-results diff --git a/tools/devops/automation/build-pipeline.yml b/tools/devops/automation/build-pipeline.yml index 9e9b87650a..ccc315fa16 100644 --- a/tools/devops/automation/build-pipeline.yml +++ b/tools/devops/automation/build-pipeline.yml @@ -90,7 +90,7 @@ stages: displayName: 'Build' dependsOn: [] jobs: - - template: templates/packages/stage.yml + - template: templates/build/stage.yml parameters: vsdropsPrefix: ${{ variables.vsdropsPrefix }} runTests: ${{ parameters.runTests }} diff --git a/tools/devops/automation/templates/packages/build.yml b/tools/devops/automation/templates/build/build.yml similarity index 100% rename from tools/devops/automation/templates/packages/build.yml rename to tools/devops/automation/templates/build/build.yml diff --git a/tools/devops/automation/templates/packages/configure.yml b/tools/devops/automation/templates/build/configure.yml similarity index 100% rename from tools/devops/automation/templates/packages/configure.yml rename to tools/devops/automation/templates/build/configure.yml diff --git a/tools/devops/automation/templates/packages/download-artifacts.yml b/tools/devops/automation/templates/build/download-artifacts.yml similarity index 100% rename from tools/devops/automation/templates/packages/download-artifacts.yml rename to tools/devops/automation/templates/build/download-artifacts.yml diff --git a/tools/devops/automation/templates/packages/publish-html.yml b/tools/devops/automation/templates/build/publish-html.yml similarity index 100% rename from tools/devops/automation/templates/packages/publish-html.yml rename to tools/devops/automation/templates/build/publish-html.yml diff --git a/tools/devops/automation/templates/packages/stage.yml b/tools/devops/automation/templates/build/stage.yml similarity index 100% rename from tools/devops/automation/templates/packages/stage.yml rename to tools/devops/automation/templates/build/stage.yml diff --git a/tools/devops/automation/templates/packages/upload-azure.yml b/tools/devops/automation/templates/build/upload-azure.yml similarity index 100% rename from tools/devops/automation/templates/packages/upload-azure.yml rename to tools/devops/automation/templates/build/upload-azure.yml From 3c487fdb4d1116ff6eb315cc31fd1ca5f762ff96 Mon Sep 17 00:00:00 2001 From: vs-mobiletools-engineering-service2 <67918504+vs-mobiletools-engineering-service2@users.noreply.github.com> Date: Wed, 13 Jan 2021 09:36:24 -0500 Subject: [PATCH 09/33] [xcode12.3] [CI][VSTS] Add a better title for the comments. (#10391) Remove the misleading title 'Device tests..' for 'Tests..'. The message already contains the context of the test execution, it can be: * Build * VSTS: device tests iOS32b * VSTS: device tests tvOS * VSTS: device tests iOS Removing the 'Device' word is enough to do not confuse users. fixes: https://github.com/xamarin/maccore/issues/2358 Co-authored-by: Manuel de la Pena --- tools/devops/automation/scripts/GitHub.psm1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/devops/automation/scripts/GitHub.psm1 b/tools/devops/automation/scripts/GitHub.psm1 index b4714c150f..52ba70678e 100644 --- a/tools/devops/automation/scripts/GitHub.psm1 +++ b/tools/devops/automation/scripts/GitHub.psm1 @@ -363,11 +363,11 @@ function New-GitHubSummaryComment { $request = New-GitHubComment -Header "Tests failed catastrophically on $Context (no summary found)." -Emoji ":fire:" -Description "Result file $TestSummaryPath not found. $headerLinks" } else { if (Test-JobSuccess -Status $Env:TESTS_JOBSTATUS) { - Set-GitHubStatus -Status "success" -Description "Device tests passed on $Context." -Context "$Context" - $request = New-GitHubCommentFromFile -Header "Device tests passed on $Context." -Description "Device tests passed on $Context. $headerLinks" -Emoji ":white_check_mark:" -Path $TestSummaryPath + Set-GitHubStatus -Status "success" -Description "Tests passed on $Context." -Context "$Context" + $request = New-GitHubCommentFromFile -Header "Tests passed on $Context." -Description "Tests passed on $Context. $headerLinks" -Emoji ":white_check_mark:" -Path $TestSummaryPath } else { - Set-GitHubStatus -Status "failure" -Description "Device tests failed on $Context." -Context "$Context" - $request = New-GitHubCommentFromFile -Header "Device tests failed on $Context" -Description "Device tests failed on $Context. $headerLinks" -Emoji ":x:" -Path $TestSummaryPath + Set-GitHubStatus -Status "failure" -Description "Tests failed on $Context." -Context "$Context" + $request = New-GitHubCommentFromFile -Header "Tests failed on $Context" -Description "Tests failed on $Context. $headerLinks" -Emoji ":x:" -Path $TestSummaryPath } } return $request From 71eafbd4fbecc8570ab85a9e4b8d27ea2fbe589d Mon Sep 17 00:00:00 2001 From: vs-mobiletools-engineering-service2 <67918504+vs-mobiletools-engineering-service2@users.noreply.github.com> Date: Wed, 13 Jan 2021 16:14:27 -0500 Subject: [PATCH 10/33] [xcode12.3] [CI][VSTS] Add links to the created pkgs. (#10394) Add a list with all the pkgs created to make users life better. fixes: https://github.com/xamarin/xamarin-macios/issues/10298 Co-authored-by: Manuel de la Pena --- tools/devops/automation/scripts/GitHub.psm1 | 41 +++++++++++++++--- .../templates/build/download-artifacts.yml | 15 ++++++- .../templates/build/publish-html.yml | 42 +++++++++++++++---- .../automation/templates/build/stage.yml | 4 +- 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/tools/devops/automation/scripts/GitHub.psm1 b/tools/devops/automation/scripts/GitHub.psm1 index 52ba70678e..18909a70a5 100644 --- a/tools/devops/automation/scripts/GitHub.psm1 +++ b/tools/devops/automation/scripts/GitHub.psm1 @@ -325,7 +325,10 @@ function New-GitHubSummaryComment { [Parameter(Mandatory)] [String] - $TestSummaryPath + $TestSummaryPath, + + [string] + $Artifacts ) $envVars = @{ @@ -347,14 +350,40 @@ function New-GitHubSummaryComment { # build the links to provide extra info to the monitoring person, we need to make sure of a few things # 1. We do have the xamarin-storage path # 2. We did reach the xamarin-storage, stored in the env var XAMARIN_STORAGE_REACHED - $headerSb = [System.Text.StringBuilder]::new() - $headerSb.AppendLine(); # new line to start the list - $headerSb.AppendLine("* [Azure DevOps]($vstsTargetUrl)") + $sb = [System.Text.StringBuilder]::new() + $sb.AppendLine(); # new line to start the list + $sb.AppendLine("* [Azure DevOps]($vstsTargetUrl)") if ($Env:VSDROPS_INDEX) { # we did generate an index with the files in vsdrops - $headerSb.AppendLine("* [Html Report (VSDrops)]($Env:VSDROPS_INDEX)") + $sb.AppendLine("* [Html Report (VSDrops)]($Env:VSDROPS_INDEX)") } - $headerLinks = $headerSb.ToString() + if ($Artifacts) { + if (-not (Test-Path $Artifacts -PathType Leaf)) { + $sb.AppendLine("Path $Artifacts was not found!") + } else { + # read the json file, convert it to an object and add a line for each artifact + $json = Get-Content $Artifacts | ConvertFrom-Json + if ($json.Count -gt 0) { + $sb.AppendLine("
View packages") + foreach ($a in $json) { + $url = $a.url + if ($url.EndsWith(".pkg") -or $url.EndsWith(".nupkg")) { + try { + $fileName = $a.url.Substring($a.url.LastIndexOf("/" + 1)) + $sb.AppendLine("* [$fileName]($($a.url))") + } catch { + Write-Host "Could not get file name for url $url" + } + } + } + $sb.AppendLine("
") + } else { + $sb.AppendLine("No packages found.") + } + } + } + + $headerLinks = $sb.ToString() $request = $null if (-not (Test-Path $TestSummaryPath -PathType Leaf)) { diff --git a/tools/devops/automation/templates/build/download-artifacts.yml b/tools/devops/automation/templates/build/download-artifacts.yml index 597c9c44a8..da570a5d49 100644 --- a/tools/devops/automation/templates/build/download-artifacts.yml +++ b/tools/devops/automation/templates/build/download-artifacts.yml @@ -23,8 +23,6 @@ steps: destinationFolder: '$(System.DefaultWorkingDirectory)/apidiff-stable' - powershell: | - Get-ChildItem -Recurse $Env:SYSTEM_DEFAULTWORKINGDIRECTORY - Write-Host "##vso[task.setvariable variable=STABLE_APIDDIFF_PATH]$Env:SYSTEM_DEFAULTWORKINGDIRECTORY\apidiff-stable" displayName: Pusblish apidiff paths name: apidiff # not to be confused with the displayName, this is used to later use the name of the step to access the output variables from an other job @@ -36,3 +34,16 @@ steps: patterns: '**/*.json' allowFailedBuilds: true path: $(Build.SourcesDirectory)/artifacts + +- task: DownloadPipelineArtifact@2 + displayName: Download artifacts.json + inputs: + patterns: '**/*.json' + allowFailedBuilds: true + path: $(Build.DefaultWorkingDirectory)/artifacts + +- pwsh: | + Get-ChildItem -Recurse $Env:SYSTEM_DEFAULTWORKINGDIRECTORY + Write-Host "##vso[task.setvariable variable=ARTIFACTS_JSON_PATH]$Env:SYSTEM_DEFAULTWORKINGDIRECTORY\artifacts\pkg-info\artifacts.json" + displayName: 'Set artifacts.json path' + timeoutInMinutes: 5 diff --git a/tools/devops/automation/templates/build/publish-html.yml b/tools/devops/automation/templates/build/publish-html.yml index 24e86d51cb..c178b0c86d 100644 --- a/tools/devops/automation/templates/build/publish-html.yml +++ b/tools/devops/automation/templates/build/publish-html.yml @@ -14,9 +14,17 @@ parameters: +- name: statusContext + type: string + default: 'iOS Device Tests' # default context, since we started dealing with iOS devices. + - name: vsdropsPrefix type: string +- name: devicePrefix + type: string + default: 'ios' # default context, since we started dealing with iOS devices. + steps: - checkout: self @@ -62,18 +70,36 @@ steps: Write-Host "##vso[task.setvariable variable=$envVarName]$url" } displayName: 'Create API from stable diff gists' + enabled: false timeoutInMinutes: 1 env: GITHUB_TOKEN: $(GitHub.Token) -- pwsh: | - Write-Host "Writing comment!" - displayName: 'Generating GitHub Comment' - condition: always() - timeoutInMinutes: 1 - -- pwsh: | - Get-ChildItem -Recurse $Env:SYSTEM_DEFAULTWORKINGDIRECTORY +- powershell: | + $env:VSDROPS_INDEX="$Env:VSDROPSPREFIX/$Env:BUILD_BUILDNUMBER/$Env:BUILD_BUILDID/$Env:DEVICE_PREFIX/;/tests/vsdrops_index.html" + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\xamarin-macios\tools\devops\automation\scripts\GitHub.psm1 + Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\xamarin-macios\tools\devops\automation\scripts\VSTS.psm1 + if ($Env:BUILD_PACKAGE -eq "True") { + Write-Host "Json path is $Env:ARTIFACTS_JSON_PATH" + $response = New-GitHubSummaryComment -Context "$Env:CONTEXT" -TestSummaryPath "$Env:TESTS_SUMMARY" -Artifacts "$Env:ARTIFACTS_JSON_PATH" + Write-Host $response + } else { + Write-Host "Json path is $Env:ARTIFACTS_JSON_PATH" + $response = New-GitHubSummaryComment -Context "$Env:CONTEXT" -TestSummaryPath "$Env:TESTS_SUMMARY" + Write-Host $response + } + if($Env:TESTS_JOBSTATUS -ne "Succeeded") + { + Set-PipelineResult -Status partiallySucceeded + } + env: + BUILD_REVISION: $(Build.SourceVersion) + CONTEXT: ${{ parameters.statusContext }} + DEVICE_PREFIX: ${{ parameters.devicePrefix }} + GITHUB_TOKEN: $(GitHub.Token) + TESTS_JOBSTATUS: $(TESTS_JOBSTATUS) # set by the runTests step + TESTS_SUMMARY: $(TEST_SUMMARY_PATH) + ACCESSTOKEN: $(System.AccessToken) displayName: 'Add summaries' condition: always() timeoutInMinutes: 1 diff --git a/tools/devops/automation/templates/build/stage.yml b/tools/devops/automation/templates/build/stage.yml index a00e8d146b..e8065f9b73 100644 --- a/tools/devops/automation/templates/build/stage.yml +++ b/tools/devops/automation/templates/build/stage.yml @@ -127,6 +127,7 @@ jobs: displayName: 'Publish Html report in VSDrops' timeoutInMinutes: 1000 dependsOn: # has to wait for the tests to be done AND the data to be uploaded + - configure - build - upload_azure_blob - upload_vsdrops @@ -135,6 +136,7 @@ jobs: variables: # Define the variable FOO from the previous job # Note the use of single quotes! + BUILD_PACKAGE: $[ dependencies.configure.outputs['labels.build-package'] ] TESTS_BOT: $[ dependencies.build.outputs['runTests.TESTS_BOT'] ] TESTS_JOBSTATUS: $[ dependencies.build.outputs['runTests.TESTS_JOBSTATUS'] ] pool: @@ -142,7 +144,7 @@ jobs: workspace: clean: all steps: - - template: ../common/publish-html.yml + - template: publish-html.yml parameters: statusContext: "Build" vsdropsPrefix: ${{ parameters.vsdropsPrefix }} From 78f7eaaedb22fbad3db9dc0a054400897a0b3a9a Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Thu, 14 Jan 2021 09:36:36 -0800 Subject: [PATCH 11/33] [xcode12.3] [CI][VSTS] Clean .xip files. (#10416) At some point either due to a manual error or a bug in provisionator a *.xip file was left behind. Those files make provisioantor fail, for example: https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=4361631&view=logs&j=f0137cdf-e292-5541-69aa-72303a08a0ba&t=59bea1c0-f907-5818-000e-f6fef9d3ffa2&l=230 We will remove those files to make sure provisionator works and we get device tests back. Co-authored-by: Manuel de la Pena Co-authored-by: Rolf Bjarne Kvinge --- tools/devops/automation/scripts/bash/clean-bot.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/devops/automation/scripts/bash/clean-bot.sh b/tools/devops/automation/scripts/bash/clean-bot.sh index 3885cf0d51..bcd7dbb643 100755 --- a/tools/devops/automation/scripts/bash/clean-bot.sh +++ b/tools/devops/automation/scripts/bash/clean-bot.sh @@ -105,6 +105,10 @@ oldXcodes=( "/Applications/Xcode_12.2.0-rc.app" ) +# remove wrongly added .xip files under /Applications, confuses provisionator and +# are not needed and wrong +sudo rm -Rf /Applications/Xcode*.xip + for oldXcode in "${oldXcodes[@]}"; do sudo rm -Rf "$oldXcode" done From 22c813ad3ee651d1613279bb8c670448f84ac4de Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Fri, 15 Jan 2021 07:15:24 -0800 Subject: [PATCH 12/33] [CI][VSTS] If comments are too large, provide a gist. (#10434) Github has a limited size for messages in comments. If we did reach that limit, we create a gist to show all the results. Co-authored-by: Manuel de la Pena --- tools/devops/automation/scripts/GitHub.psm1 | 85 ++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/tools/devops/automation/scripts/GitHub.psm1 b/tools/devops/automation/scripts/GitHub.psm1 index 18909a70a5..fd4ee6bf18 100644 --- a/tools/devops/automation/scripts/GitHub.psm1 +++ b/tools/devops/automation/scripts/GitHub.psm1 @@ -205,8 +205,22 @@ function New-GitHubComment { $url = "https://api.github.com/repos/xamarin/xamarin-macios/commits/$Env:BUILD_REVISION/comments" } + # github has a max size for the comments to be added in a PR, it can be the case that because we failed so much, that we + # cannot add the full message, in that case, we add part of it, then a link to a gist with the content. + $maxLength = 32768 + $body = $msg.ToString() + if ($body.Length -ge $maxLength) { + # create a gist with the contents, next, add substring of the message - the length of the info about the gist so that users + # can click, set that as the body + $gist = New-GistWithContent -Description "Build results" -FileName "TestResult.md" -GistContent $body -FileType "md" + $linkMessage = "The message from CI is too large for the GitHub comments. You can find the full results [here]($gist)." + $messageLength = $maxLength - ($linkMessage.Length + 2) # +2 is to add a nice space + $body = $body.Substring(0, $messageLength); + $body = $body + "\n\n" + $linkMessage + } + $payload = @{ - body = $msg.ToString() + body = $body } $headers = @{ @@ -481,6 +495,74 @@ class GistFile } } +function New-GistWithContent { + param ( + + [ValidateNotNullOrEmpty ()] + [string] + $Description, + + [Parameter(Mandatory)] + [string] + $FileName, + + [Parameter(Mandatory)] + [string] + $GistContent, + + [Parameter(Mandatory)] + [string] + $FileType, + + [switch] + $IsPublic=$false # default to false, better save than sorry + ) + + $envVars = @{ + "GITHUB_TOKEN" = $Env:GITHUB_TOKEN; + } + + foreach ($key in $envVars.Keys) { + if (-not($envVars[$key])) { + Write-Debug "Environment variable missing: $key" + throw [System.InvalidOperationException]::new("Environment variable missing: $key") + } + } + + # create the hashtable that will contain all the information of all types + $payload = @{ + description = $Description; + files = @{ + "$FileName" = @{ + content = $GistContent; + filename = $FileName + language = $FileType; + }; + }; # each file is the name of the file + the hashtable of the data to be used + } + + # switchs are converted to {\"IsPresent\"=>true} in json :/ and the ternary operator might not be in all machines + if ($IsPublic) { + $payload["public"] = $true + } else { + $payload["public"] = $false + } + + $url = "https://api.github.com/gists" + $payloadJson = $payload | ConvertTo-Json + Write-Host "Url is $url" + Write-Host "Payload is $payloadJson" + + $headers = @{ + Accept = "application/vnd.github.v3+json"; + Authorization = ("token {0}" -f $Env:GITHUB_TOKEN); + } + + $request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body $payloadJson -ContentType 'application/json' + Write-Host $request + return $request.html_url +} + <# .SYNOPSIS Creates a new gist that will contain the given collection of files and returns the urlobject defintion, this @@ -576,3 +658,4 @@ Export-ModuleMember -Function Test-JobSuccess Export-ModuleMember -Function Get-GitHubPRInfo Export-ModuleMember -Function New-GistWithFiles Export-ModuleMember -Function New-GistObjectDefinition +Export-ModuleMember -Function New-GistWithContent From d1511f4d8af040e3a8f71e6eb647cf9056fad80f Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Fri, 15 Jan 2021 07:17:15 -0800 Subject: [PATCH 13/33] [xcode12.3] [CI][VSTS] When webservers are rude, retry (#10430) Perseverance is failing 19 times and succeeding the 20th. fix: https://github.com/xamarin/maccore/issues/2361 Co-authored-by: Manuel de la Pena --- tools/devops/automation/scripts/GitHub.psm1 | 47 +++++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/tools/devops/automation/scripts/GitHub.psm1 b/tools/devops/automation/scripts/GitHub.psm1 index fd4ee6bf18..2210224a95 100644 --- a/tools/devops/automation/scripts/GitHub.psm1 +++ b/tools/devops/automation/scripts/GitHub.psm1 @@ -1,3 +1,42 @@ +<# + .SYNOPSIS + Simple retry block to workaround certain issues with the webservices that cannot handle the load. + + .PARAMETER Request + The request to be performed and retried if failed. + + .PARAMETER Retries + The number of times the we will retry to perform the request. +#> +function Invoke-Request { + param ( + [scriptblock] + $Request, + + [int] + $Retries=5 + ) + $count = 0 + do { + try { + # that & is important, tells pwsh to execute the script block, else you simple returns the block itself + return & $Request + } catch { + if ($count -gt $Retries) { + # notify and throw + Write-Host "Could not perform request after $Retries attempts." + throw $_.Exception + } else { + $count = $count + 1 + $seconds = 5 * $count + Write-Host "Error performing request trying in $seconds seconds" + Start-Sleep -Seconds $seconds + } + } + + } while ($true) +} + <# .SYNOPSIS Returns the target url to be used when setting the status. The target url allows users to get back to the CI event that updated the status. @@ -114,7 +153,7 @@ function Set-GitHubStatus { Authorization = ("token {0}" -f $Env:GITHUB_TOKEN) } - return Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-json) -ContentType 'application/json' + return Invoke-Request -Request { Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-json) -ContentType 'application/json' } } <# @@ -227,7 +266,7 @@ function New-GitHubComment { Authorization = ("token {0}" -f $Env:GITHUB_TOKEN) } - $request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-Json) -ContentType 'application/json' + $request = Invoke-Request -Request { Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-Json) -ContentType 'application/json' } Write-Host $request return $request } @@ -447,7 +486,7 @@ function Get-GitHubPRInfo { Authorization = ("token {0}" -f $Env:GITHUB_TOKEN) } - $request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -ContentType 'application/json' + $request = Invoke-Request -Request { Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -ContentType 'application/json' } Write-Host $request return $request } @@ -644,7 +683,7 @@ function New-GistWithFiles { Authorization = ("token {0}" -f $Env:GITHUB_TOKEN); } - $request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body $payloadJson -ContentType 'application/json' + $request = Invoke-Request -Request { Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body $payloadJson -ContentType 'application/json' } Write-Host $request return $request.html_url } From fae2103e772a848b0c154f42ba43386a534659f9 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Wed, 20 Jan 2021 04:44:45 -0800 Subject: [PATCH 14/33] [xcode12.3] [CI][VSTS] Add required profiles for mac. (#10396) Co-authored-by: Manuel de la Pena --- tools/devops/automation/build-pipeline.yml | 1 + .../devops/automation/templates/build/build.yml | 16 ++++++++++++++++ .../devops/automation/templates/build/stage.yml | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/tools/devops/automation/build-pipeline.yml b/tools/devops/automation/build-pipeline.yml index ccc315fa16..15da42e550 100644 --- a/tools/devops/automation/build-pipeline.yml +++ b/tools/devops/automation/build-pipeline.yml @@ -95,6 +95,7 @@ stages: vsdropsPrefix: ${{ variables.vsdropsPrefix }} runTests: ${{ parameters.runTests }} runDeviceTests: ${{ parameters.runDeviceTests }} + keyringPass: $(xma-password) # ideally we would use a matrix here, like: # - job: device_tests diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index c7ec4569f9..41a8b191dc 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -10,6 +10,9 @@ parameters: - name: vsdropsPrefix type: string +- name: keyringPass + type: string + steps: - checkout: self # https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#checkout clean: true # Executes: git clean -ffdx && git reset --hard HEAD @@ -323,6 +326,19 @@ steps: artifactName: package-internal continueOnError: true +# funny enough we need these profiles to build the mac tests +- bash: | + set -x + set -e + + cd "$SOURCES_DIR/maccore/tools/" + ./install-qa-provisioning-profiles.sh -v + displayName: 'Add tests provisioning profiles' + timeoutInMinutes: 30 + env: + LOGIN_KEYCHAIN_PASSWORD: ${{ parameters.keyringPass }} + SOURCES_DIR: $(Build.SourcesDirectory) + - bash: | set -x set -e diff --git a/tools/devops/automation/templates/build/stage.yml b/tools/devops/automation/templates/build/stage.yml index e8065f9b73..6766934b81 100644 --- a/tools/devops/automation/templates/build/stage.yml +++ b/tools/devops/automation/templates/build/stage.yml @@ -12,6 +12,9 @@ parameters: type: boolean default: true +- name: keyringPass + type: string + jobs: - job: configure displayName: 'Configure build' @@ -76,6 +79,7 @@ jobs: runTests: ${{ parameters.runTests }} runDeviceTests: ${{ parameters.runDeviceTests }} vsdropsPrefix: ${{ parameters.vsdropsPrefix }} + keyringPass: ${{ parameters.keyringPass }} - job: upload_azure_blob displayName: 'Upload packages to Azure' From 0e6773dba38fe20bb050aad8b4d34ec282c801f9 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Wed, 20 Jan 2021 04:48:19 -0800 Subject: [PATCH 15/33] [CI][VSTS] Remove warning from mac tests upload. (#10465) The template does not expand wildcards. The template does not know how to reuse paths. Co-authored-by: Manuel de la Pena --- tools/devops/automation/templates/build/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index 41a8b191dc..4f5ce827dd 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -351,8 +351,8 @@ steps: - task: PublishPipelineArtifact@1 displayName: 'Publish Xamarin.Mac tests' inputs: - targetPath: $(Build.SourcesDirectory)/xamarin-macios/tests/*.7z - artifactName: package-internal + targetPath: $(Build.SourcesDirectory)/xamarin-macios/tests/mac-test-package.7z + artifactName: mac-test-package condition: and(succeeded(), contains(variables['configuration.RunMacTests'], 'True')) continueOnError: true From ae5479927bbf419312cdee48e59b7b28b1924bbb Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Thu, 21 Jan 2021 06:42:41 -0800 Subject: [PATCH 16/33] [Xharness] Fix the xharness links to work with vsdrops. (#10477) We need to add the prefix to the xharness logs or we will not be able to acceess them via a click. Co-authored-by: Manuel de la Pena --- tests/xharness/Jenkins/Reports/HtmlReportWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/xharness/Jenkins/Reports/HtmlReportWriter.cs b/tests/xharness/Jenkins/Reports/HtmlReportWriter.cs index 8a2ccda537..0b59f20585 100644 --- a/tests/xharness/Jenkins/Reports/HtmlReportWriter.cs +++ b/tests/xharness/Jenkins/Reports/HtmlReportWriter.cs @@ -116,7 +116,7 @@ namespace Xharness.Jenkins.Reports { writer.WriteLine ($""); foreach (var log in jenkins.Logs) - writer.WriteLine ("{1}
", log.FullPath.Substring (jenkins.LogDirectory.Length + 1), log.Description); + writer.WriteLine ("{1}
", GetLinkFullPath (log.FullPath.Substring (jenkins.LogDirectory.Length + 1)), log.Description); writer.WriteLine ("
"); var headerColor = "black"; From af1ab9cad6d75ca517766a3c4cda9514dbd1b9a5 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Thu, 21 Jan 2021 06:52:31 -0800 Subject: [PATCH 17/33] [xcode12.3] [CI][VSTS] Move to ESRP for notarization. (#10485) This change perse does not do other thing than removing the use of virtuanenv and remove the installation of python-magic. We have taken this approach because the images that will be used to provision the CI bots will have python-magic installed by default. We want to do this because: 1. CI should not be modifying the machine, we do not install any pkg. 2. python-magic should be present, if not, we will get an import error and we will know how to fix it (re-image). Co-authored-by: Manuel de la Pena --- tools/devops/automation/build-pipeline.yml | 2 ++ .../automation/templates/build/build.yml | 28 +++++++++++-------- .../automation/templates/build/stage.yml | 1 + tools/devops/provision-brew-packages.csx | 5 ++-- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/tools/devops/automation/build-pipeline.yml b/tools/devops/automation/build-pipeline.yml index 15da42e550..9c7448e3ab 100644 --- a/tools/devops/automation/build-pipeline.yml +++ b/tools/devops/automation/build-pipeline.yml @@ -59,6 +59,8 @@ variables: value: 'https://vsdrop.corp.microsoft.com/file/v1/xamarin-macios/device-tests' - name: USE_TCP_TUNNEL # Needed to ensure that devices uses the usb cable to communicate with the devices to run the tests. value: true +- name: TeamName + value: 'xamarin-macios' trigger: branches: diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index 4f5ce827dd..f25a0875ae 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -59,7 +59,7 @@ steps: provisioning_script: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/provision-brew-packages.csx provisioning_extra_args: '-vvvv' timeoutInMinutes: 30 - enabled: false + enabled: true - bash: | make -C $(Build.SourcesDirectory)/xamarin-macios/tools/devops build-provisioning.csx @@ -274,20 +274,12 @@ steps: prependToPath: '/Users/builder/Library/Python/2.7/bin' - bash: | - VIRTUAL_ENV_PATH=$(Build.SourcesDirectory)/venv - pip install virtualenv - virtualenv "$VIRTUAL_ENV_PATH" --system-site-packages - source "$VIRTUAL_ENV_PATH/bin/activate" - pip install python-magic - security unlock-keychain -p $PRODUCTSIGN_KEYCHAIN_PASSWORD builder.keychain PACKAGES="$IOS_PKG $MAC_PKG" echo "Packages found at $PACKAGES" - echo "$PACKAGES" | xargs python $(Build.SourcesDirectory)/release-scripts/sign_and_notarize.py -a "$APP_ID" -i "$INSTALL_ID" -u "$APPLE_ACCOUNT" -p "$APPLE_PASS" -t "$TEAM_ID" -d $(Build.SourcesDirectory)/package/notarized -e "$MAC_ENTITLEMENTS" -k "$KEYCHAIN" - - deactivate - rm -Rf "$VIRTUAL_ENV_PATH" + echo "$PACKAGES" | xargs python $(Build.SourcesDirectory)/release-scripts/sign_and_notarize.py --no_notarization -a "$APP_ID" -i "$INSTALL_ID" -u "$APPLE_ACCOUNT" -p "$APPLE_PASS" -t "$TEAM_ID" -d $(Build.SourcesDirectory)/package/notarized -e "$MAC_ENTITLEMENTS" -k "$KEYCHAIN" + ls -R $(Build.SourcesDirectory)/package env: PRODUCTSIGN_KEYCHAIN_PASSWORD: $(OSX_KEYCHAIN_PASS) MAC_ENTITLEMENTS: $(Build.SourcesDirectory)/xamarin-macios/mac-entitlements.plist @@ -302,6 +294,20 @@ steps: condition: and(succeeded(), contains(variables['configuration.SignPkgs'], 'True'), contains(variables['configuration.IsPr'], 'False')) timeoutInMinutes: 90 +- task: ms-vseng.MicroBuildTasks.30666190-6959-11e5-9f96-f56098202fef.MicroBuildSigningPlugin@3 + displayName: 'Install Signing Plugin' + inputs: + signType: 'Real' # test is not present for mac.. + azureSubscription: 'MicroBuild Signing Task (DevDiv)' + version: 1.1.352-g82fc43b9d7 + feedSource: 'https://pkgs.dev.azure.com/devdiv/_packaging/VS_TempPkgs/nuget/v3/index.json' + condition: and(succeeded(), contains(variables['configuration.SignPkgs'], 'True'), contains(variables['configuration.IsPr'], 'False')) # if we are a PR, the installation of the plugin will return a 403 and everything will collapse + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +- pwsh: $(Build.SourcesDirectory)/release-scripts/notarize.ps1 -FolderForApps $(Build.SourcesDirectory)/package/notarized + displayName: 'ESRP notarizing packages' + - template: generate-workspace-info.yml@templates parameters: GitHubToken: $(GitHub.Token) diff --git a/tools/devops/automation/templates/build/stage.yml b/tools/devops/automation/templates/build/stage.yml index 6766934b81..1788c5c95a 100644 --- a/tools/devops/automation/templates/build/stage.yml +++ b/tools/devops/automation/templates/build/stage.yml @@ -70,6 +70,7 @@ jobs: demands: - Agent.OS -equals Darwin - Agent.OSVersion -equals 10.15 + - macios_image -equals v1 workspace: clean: all diff --git a/tools/devops/provision-brew-packages.csx b/tools/devops/provision-brew-packages.csx index 728b20602a..a1ec79c04d 100644 --- a/tools/devops/provision-brew-packages.csx +++ b/tools/devops/provision-brew-packages.csx @@ -4,8 +4,7 @@ BrewPackages ( "automake", "libtool", "p7zip", - "python", - "libmagic", "msitools", - "wget" + "wget", + "azure-cli" ); From 63410a385ec45b15a87688f5b1662c09bbdd7e52 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Thu, 21 Jan 2021 12:15:36 -0800 Subject: [PATCH 18/33] [CI][VSTS] Remove ALL .xip (#10490) This is clearly something that has a human behind it and not a script. We have seen Xcode*.xip which is the download name BUT also xcode12.xip which looks like a point&click intervention (https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=4387395&view=logs&j=f0137cdf-e292-5541-69aa-72303a08a0ba&t=59bea1c0-f907-5818-000e-f6fef9d3ffa2&l=234) Co-authored-by: Manuel de la Pena --- tools/devops/automation/scripts/bash/clean-bot.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/devops/automation/scripts/bash/clean-bot.sh b/tools/devops/automation/scripts/bash/clean-bot.sh index bcd7dbb643..705398bc0f 100755 --- a/tools/devops/automation/scripts/bash/clean-bot.sh +++ b/tools/devops/automation/scripts/bash/clean-bot.sh @@ -107,7 +107,7 @@ oldXcodes=( # remove wrongly added .xip files under /Applications, confuses provisionator and # are not needed and wrong -sudo rm -Rf /Applications/Xcode*.xip +sudo rm -Rf /Applications/*.xip for oldXcode in "${oldXcodes[@]}"; do sudo rm -Rf "$oldXcode" From 581847192694699b7ac078c90479a497ad6597c3 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Thu, 21 Jan 2021 12:17:44 -0800 Subject: [PATCH 19/33] [xcode12.3] [VSTS][CI] Add links with the generated pkgs. (#10488) Co-authored-by: Manuel de la Pena --- tools/devops/automation/scripts/GitHub.psm1 | 18 ++++++++++++++---- .../templates/build/publish-html.yml | 6 +++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/tools/devops/automation/scripts/GitHub.psm1 b/tools/devops/automation/scripts/GitHub.psm1 index 2210224a95..a73443d970 100644 --- a/tools/devops/automation/scripts/GitHub.psm1 +++ b/tools/devops/automation/scripts/GitHub.psm1 @@ -381,7 +381,7 @@ function New-GitHubSummaryComment { $TestSummaryPath, [string] - $Artifacts + $Artifacts="" ) $envVars = @{ @@ -410,7 +410,8 @@ function New-GitHubSummaryComment { # we did generate an index with the files in vsdrops $sb.AppendLine("* [Html Report (VSDrops)]($Env:VSDROPS_INDEX)") } - if ($Artifacts) { + if (-not [string]::IsNullOrEmpty($Artifacts)) { + Write-Host "Parsing artifacts" if (-not (Test-Path $Artifacts -PathType Leaf)) { $sb.AppendLine("Path $Artifacts was not found!") } else { @@ -418,12 +419,19 @@ function New-GitHubSummaryComment { $json = Get-Content $Artifacts | ConvertFrom-Json if ($json.Count -gt 0) { $sb.AppendLine("
View packages") + $sb.AppendLine("") # no new line results in a bad rendering in the links foreach ($a in $json) { $url = $a.url if ($url.EndsWith(".pkg") -or $url.EndsWith(".nupkg")) { try { - $fileName = $a.url.Substring($a.url.LastIndexOf("/" + 1)) - $sb.AppendLine("* [$fileName]($($a.url))") + $fileName = $a.url.Substring($a.url.LastIndexOf("/") + 1) + Write-Host "Adding link for $fileName" + if ($a.url.Contains("notarized")) { + $link = "* [$fileName (notarized)]($($a.url))" + } else { + $link = "* [$fileName]($($a.url))" + } + $sb.AppendLine($link) } catch { Write-Host "Could not get file name for url $url" } @@ -434,6 +442,8 @@ function New-GitHubSummaryComment { $sb.AppendLine("No packages found.") } } + } else { + Write-Host "Artifacts were not provided." } $headerLinks = $sb.ToString() diff --git a/tools/devops/automation/templates/build/publish-html.yml b/tools/devops/automation/templates/build/publish-html.yml index c178b0c86d..809d852155 100644 --- a/tools/devops/automation/templates/build/publish-html.yml +++ b/tools/devops/automation/templates/build/publish-html.yml @@ -79,7 +79,11 @@ steps: $env:VSDROPS_INDEX="$Env:VSDROPSPREFIX/$Env:BUILD_BUILDNUMBER/$Env:BUILD_BUILDID/$Env:DEVICE_PREFIX/;/tests/vsdrops_index.html" Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\xamarin-macios\tools\devops\automation\scripts\GitHub.psm1 Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\xamarin-macios\tools\devops\automation\scripts\VSTS.psm1 - if ($Env:BUILD_PACKAGE -eq "True") { + + $buildReason = "$(Build.Reason)" + $buildSourceBranchName = "$(Build.SourceBranchName)" + + if ($buildReason -ne "PullRequest" -or $Env:BUILD_PACKAGE -eq "True") { Write-Host "Json path is $Env:ARTIFACTS_JSON_PATH" $response = New-GitHubSummaryComment -Context "$Env:CONTEXT" -TestSummaryPath "$Env:TESTS_SUMMARY" -Artifacts "$Env:ARTIFACTS_JSON_PATH" Write-Host $response From 6c5f5cd59f80818f4a671cf05652a16ac5fff467 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Fri, 22 Jan 2021 07:11:28 -0800 Subject: [PATCH 20/33] [CI][VSTS] Continue on profiles failure. (#10498) We want to continue and generate the pkgs, the failure of this step only results in failing tests. fixes: https://github.com/xamarin/maccore/issues/2376 Co-authored-by: Manuel de la Pena --- tools/devops/automation/templates/build/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index f25a0875ae..f5fb0618da 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -341,6 +341,7 @@ steps: ./install-qa-provisioning-profiles.sh -v displayName: 'Add tests provisioning profiles' timeoutInMinutes: 30 + continueOnError: true # should not stop the build will result in test failures but we do want the pkg env: LOGIN_KEYCHAIN_PASSWORD: ${{ parameters.keyringPass }} SOURCES_DIR: $(Build.SourcesDirectory) From 909e1fa1be9d4db8a95a2e244d805e43b6325d06 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Fri, 22 Jan 2021 07:13:49 -0800 Subject: [PATCH 21/33] [CI][VSTS] Ensure that the required simulators are present in the machine. (#10497) Co-authored-by: Manuel de la Pena --- tools/devops/automation/templates/build/build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index f5fb0618da..65fb15b110 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -72,6 +72,12 @@ steps: provisioning_extra_args: '-vvvv' timeoutInMinutes: 250 +- bash: | + set -x + set -e + $(Build.SourcesDirectory)/xamarin-macios/system-dependencies.sh --provision-simulators + displayName: 'Provision simulators' + - bash: | set -x sudo rm -Rf /Developer/MonoTouch From 053a3ee26b8f449ca1e215e2abf694e3a1988273 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Fri, 22 Jan 2021 13:08:51 -0800 Subject: [PATCH 22/33] [xcode12.3] [CI][VSTS] Add a poor mans debugger. (#10503) Co-authored-by: Manuel de la Pena --- .../automation/templates/build/build.yml | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index 65fb15b110..bb4f74b1df 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -102,11 +102,13 @@ steps: # we have a number of scripts that require to be executed from the top of the src, rather # than keeping track of the location of the script, we create two env vars that can be used to # get to the top + $configVars = @{} # var name/value pair, later print twice, to process and debug + $xamTop = "$(Build.SourcesDirectory)/xamarin-macios/" - Write-Host "##vso[task.setvariable variable=XAM_TOP]$xamTop" + $configVars.Add("XAM_TOP", $xamTop) $maccoreTop = "$(Build.SourcesDirectory)/maccore/" - Write-Host "##vso[task.setvariable variable=MACCORE_TOP]$maccoreTop" + $configVars.Add("MACCORE_TOP", $maccoreTop) $buildReason = "$(Build.Reason)" $buildSourceBranchName = "$(Build.SourceBranchName)" @@ -115,82 +117,87 @@ steps: # a branch in origin if ($buildReason -eq "PullRequest" -or (($buildReason -eq "Manual") -and ($buildSourceBranchName -eq "merge")) ) { - Write-Host '##vso[task.setvariable variable=IsPR;isOutput=true]False' + $configVars.Add("IsPR", "True") if ($Env:BuildPackage -eq "True") { - Write-Host '##vso[task.setvariable variable=BuildPkgs;isOutput=true]True' + $configVars.Add("BuildPkgs", "True") } else { - Write-Host '##vso[task.setvariable variable=BuildPkgs;isOutput=true]False' + $configVars.Add("BuildPkgs", "False") } # interesting case, we have build-pkg and skip-pkg... if that is the case, we build it, but we set a warning if ($Env:BuildPackage -eq "True" -and $Env:SkipPackages -eq "True") { Write-Host "##vso[task.logissue type=warning]'build-package' and 'skip-packages' are both present. Building packages in case of a doubt." - Write-Host "##vso[task.setvariable variable=BuildPkgs;isOutput=true]True" + $configVars.Add("BuildPkgs", "True") } # if we want to have device tests, we do need the pkgs so that we can fwd them to the device tests if ($Env:TriggerDeviceTests -eq "True") { - Write-Host "##vso[task.setvariable variable=BuildPkgs;isOutput=true]True" - Write-Host "##vso[task.setvariable variable=RunDeviceTests;isOutput=true]True" + $configVars.Add("BuildPkgs", "True") + $configVars.Add("RunDeviceTests", "True") } if ($Env:SkipNugets -eq "True") { - Write-Host "##vso[task.setvariable variable=BuildNugets;isOutput=true]False" + $configVars.Add("BuildNugets", "False") } else { - Write-Host "##vso[task.setvariable variable=BuildNugets;isOutput=true]True" + $configVars.Add("BuildNugets", "True") } if ($Env:SkipSigning -eq "True") { - Write-Host "##vso[task.setvariable variable=SignPkgs;isOutput=true]False" + $configVars.Add("SignPkgs", "False") } else { - Write-Host "##vso[task.setvariable variable=SignPkgs;isOutput=true]True" + $configVars.Add("SignPkgs", "True") } if ($Env:SkipExternalTests -eq "True") { - Write-Host "##vso[task.setvariable variable=RunExternalTests;isOutput=true]False" + $configVars.Add("RunExternalTests", "False") } else { - Write-Host "##vso[task.setvariable variable=RunExternalTests;isOutput=true]True" + $configVars.Add("RunExternalTests", "True") } if ($Env:SkipPackagedXamarinMacTests -eq "True") { - Write-Host "##vso[task.setvariable variable=RunMacTests;isOutput=true]False" + $configVars.Add("RunMacTests", "False") } else { - Write-Host "##vso[task.setvariable variable=RunMacTests;isOutput=true]True" + $configVars.Add("RunMacTests", "True") } if ($Env:SkipPublicJenkins -eq "True") { - Write-Host "##vso[task.setvariable variable=SkipPublicJenkins;isOutput=true]True" + $configVars.Add("SkipPublicJenkins", "True") } else { - Write-Host "##vso[task.setvariable variable=SkipPublicJenkins;isOutput=true]False" + $configVars.Add("SkipPublicJenkins", "False") } - Write-Host "##vso[task.setvariable variable=RunSampleTests;isOutput=true]$Env:RunSampleTests" - Write-Host "##vso[task.setvariable variable=RunInternalTests;isOutput=true]$Env:RunInternalTests" + $configVars.Add("RunSampleTests", $Env:RunSampleTests) + $configVars.Add("RunInternalTests", $Env:RunInternalTests) } else { # set the defaults, all the things! o/ - Write-Host "##vso[task.setvariable variable=IsPR;isOutput=true]False" + $configVars.Add("IsPR", "False") # build pkg, nugets and sign them - Write-Host "##vso[task.setvariable variable=BuildPkgs;isOutput=true]True" - Write-Host "##vso[task.setvariable variable=BuildNugets;isOutput=true]True" - Write-Host "##vso[task.setvariable variable=SignPkgs;isOutput=true]True" + $configVars.Add("BuildPkgs", "True") + $configVars.Add("BuildNugets", "True") + $configVars.Add("SignPkgs", "True") # tests, run all of them, internal, external, mac but not sample tests - Write-Host "##vso[task.setvariable variable=RunInternalTests;isOutput=true]True" - Write-Host "##vso[task.setvariable variable=RunExternalTests;isOutput=true]True" - Write-Host "##vso[task.setvariable variable=RunMacTests;isOutput=true]True" - Write-Host "##vso[task.setvariable variable=RunSampleTests;isOutput=true]False" - Write-Host "##vso[task.setvariable variable=SkipPublicJenkins;isOutput=true]False" + $configVars.Add("RunInternalTests", "True") + $configVars.Add("RunExternalTests", "True") + $configVars.Add("RunMacTests", "True") + $configVars.Add("RunSampleTests", "False") + $configVars.Add("SkipPublicJenkins", "False") # if a developer decided to trigger one without device tests, allow it if ($Env:RUN_DEVICE_TESTS -eq "true") { - Write-Host "##vso[task.setvariable variable=RunDeviceTests;isOutput=true]True" + $configVars.Add("RunDeviceTests", "True") } else { - Write-Host "##vso[task.setvariable variable=RunDeviceTests;isOutput=true]False" + $configVars.Add("RunDeviceTests", "False") } } + # write debugging and process of the vars + foreach($key in $configVars.Keys) { + Write-Host "$key='$($configVars[$key])'" + Write-Host "##vso[task.setvariable variable=$key;isOutput=true]$($configVars[$key])" + } name: configuration displayName: "Parse PR labels" @@ -272,7 +279,7 @@ steps: env: PRODUCTSIGN_KEYCHAIN_PASSWORD: $(xma-password) displayName: 'Signing PR Build' - condition: and(succeeded(), contains(variables['configuration.SignPkgs'], 'True'), contains(variables['configuration.IsPr'], 'True')) + condition: and(succeeded(), contains(variables['configuration.BuildPkgs'], 'True'), contains(variables['configuration.SignPkgs'], 'True'), contains(variables['configuration.IsPr'], 'True')) # Ensure virtualenv is on the PATH - template: set-path/v1.yml@templates From 3f81a5681af41755ad602005a20db9437b1d5d50 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Fri, 22 Jan 2021 15:01:51 -0800 Subject: [PATCH 23/33] [CI][VSTS] Fix needed in ESRP landed in the prod feed so we can use it now. (#10506) Co-authored-by: Manuel de la Pena --- tools/devops/automation/templates/build/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index bb4f74b1df..1a91b814f3 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -312,8 +312,6 @@ steps: inputs: signType: 'Real' # test is not present for mac.. azureSubscription: 'MicroBuild Signing Task (DevDiv)' - version: 1.1.352-g82fc43b9d7 - feedSource: 'https://pkgs.dev.azure.com/devdiv/_packaging/VS_TempPkgs/nuget/v3/index.json' condition: and(succeeded(), contains(variables['configuration.SignPkgs'], 'True'), contains(variables['configuration.IsPr'], 'False')) # if we are a PR, the installation of the plugin will return a 403 and everything will collapse env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) From b5855c821eba70cb8cf39f5e21c9b7acc5587a44 Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Mon, 25 Jan 2021 10:12:13 -0500 Subject: [PATCH 24/33] [xcode12.4] Initial bump to Xcode 12.4 RC (#10509) --- Make.config | 6 +++--- Make.versions | 6 +++--- Versions-ios.plist.in | 1 + tests/xtro-sharpie/iOS-ExposureNotification.ignore | 7 +++++++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Make.config b/Make.config index b5f1948ba6..722833e684 100644 --- a/Make.config +++ b/Make.config @@ -111,9 +111,9 @@ WATCHOS_NUGET_VERSION_NO_METADATA=$(WATCHOS_NUGET_VERSION)-$(NUGET_PRERELEASE_ID WATCHOS_NUGET_VERSION_FULL=$(WATCHOS_NUGET_VERSION_NO_METADATA)+$(NUGET_BUILD_METADATA) # Xcode version should have both a major and a minor version (even if the minor version is 0) -XCODE_VERSION=12.3 -XCODE_URL=http://xamarin-storage/bot-provisioning/xcodes/Xcode_12.3.xip -XCODE_DEVELOPER_ROOT=/Applications/Xcode_12.3.0.app/Contents/Developer +XCODE_VERSION=12.4 +XCODE_URL=http://xamarin-storage/bot-provisioning/xcodes/Xcode_12.4_Release_Candidate.xip +XCODE_DEVELOPER_ROOT=/Applications/Xcode_12.4.0-rc.app/Contents/Developer XCODE_PRODUCT_BUILD_VERSION:=$(shell /usr/libexec/PlistBuddy -c 'Print :ProductBuildVersion' $(XCODE_DEVELOPER_ROOT)/../version.plist) # Mono version embedded in XI/XM (NEEDED_MONO_VERSION/BRANCH) are specified in mk/mono.mk diff --git a/Make.versions b/Make.versions index d7ba734091..6c2f085353 100644 --- a/Make.versions +++ b/Make.versions @@ -48,8 +48,8 @@ # line changed in git). # -IOS_PACKAGE_VERSION=14.8.0.$(IOS_COMMIT_DISTANCE) -MAC_PACKAGE_VERSION=7.2.0.$(MAC_COMMIT_DISTANCE) +IOS_PACKAGE_VERSION=14.10.0.$(IOS_COMMIT_DISTANCE) +MAC_PACKAGE_VERSION=7.4.0.$(MAC_COMMIT_DISTANCE) # # ** NuGet package version numbers ** @@ -66,7 +66,7 @@ MAC_PACKAGE_VERSION=7.2.0.$(MAC_COMMIT_DISTANCE) # WARNING: Do **not** use versions higher than the available Xcode SDK or else we will have issues with mtouch (See https://github.com/xamarin/xamarin-macios/issues/7705) # When bumping the major macOS version in MACOS_NUGET_VERSION also update the macOS version where we execute on bots in jenkins/Jenkinsfile (in the 'node' element) -IOS_NUGET_VERSION=14.3.100 +IOS_NUGET_VERSION=14.4.100 TVOS_NUGET_VERSION=14.3.100 WATCHOS_NUGET_VERSION=7.2.100 MACOS_NUGET_VERSION=11.1.100 diff --git a/Versions-ios.plist.in b/Versions-ios.plist.in index 109511c516..eab817108a 100644 --- a/Versions-ios.plist.in +++ b/Versions-ios.plist.in @@ -44,6 +44,7 @@ 14.1 14.2 14.3 + 14.4 tvOS diff --git a/tests/xtro-sharpie/iOS-ExposureNotification.ignore b/tests/xtro-sharpie/iOS-ExposureNotification.ignore index 326df09872..446e1678c4 100644 --- a/tests/xtro-sharpie/iOS-ExposureNotification.ignore +++ b/tests/xtro-sharpie/iOS-ExposureNotification.ignore @@ -121,3 +121,10 @@ !missing-selector! ENManager::setInvalidationHandler: not bound !missing-selector! +ENManager::authorizationStatus not bound !missing-selector! ENManager::exposureNotificationEnabled not bound +!missing-enum! ENActivityFlags not bound +!missing-selector! ENManager::activityHandler not bound +!missing-selector! ENManager::diagnosisKeysAvailableHandler not bound +!missing-selector! ENManager::preAuthorizeDiagnosisKeysWithCompletionHandler: not bound +!missing-selector! ENManager::requestPreAuthorizedDiagnosisKeysWithCompletionHandler: not bound +!missing-selector! ENManager::setActivityHandler: not bound +!missing-selector! ENManager::setDiagnosisKeysAvailableHandler: not bound From 293f10feb9245f49ff72a94e772d58d2f1d525e8 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Tue, 26 Jan 2021 16:35:28 -0800 Subject: [PATCH 25/33] [CI][VSTS] Set to no output vars back to be in the env. (#10525) If we set the variables as output variables they are not accessible by the bash scripts resulting in a warning. Co-authored-by: Manuel de la Pena --- tools/devops/automation/templates/build/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index 1a91b814f3..798b513591 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -105,10 +105,10 @@ steps: $configVars = @{} # var name/value pair, later print twice, to process and debug $xamTop = "$(Build.SourcesDirectory)/xamarin-macios/" - $configVars.Add("XAM_TOP", $xamTop) + Write-Host "##vso[task.setvariable variable=XAM_TOP]$xamTop" $maccoreTop = "$(Build.SourcesDirectory)/maccore/" - $configVars.Add("MACCORE_TOP", $maccoreTop) + Write-Host "##vso[task.setvariable variable=MACCORE_TOP]$maccoreTop" $buildReason = "$(Build.Reason)" $buildSourceBranchName = "$(Build.SourceBranchName)" From 693067f1997b31ddcd5a1a1c15d32acf0849ddd8 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Tue, 26 Jan 2021 16:35:46 -0800 Subject: [PATCH 26/33] [CI][VSTS] Do not use ESRP when no pkgs is present or in a PR. (#10529) Co-authored-by: Manuel de la Pena --- tools/devops/automation/templates/build/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index 798b513591..6b84f9b69c 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -318,6 +318,7 @@ steps: - pwsh: $(Build.SourcesDirectory)/release-scripts/notarize.ps1 -FolderForApps $(Build.SourcesDirectory)/package/notarized displayName: 'ESRP notarizing packages' + condition: and(succeeded(), contains(variables['configuration.SignPkgs'], 'True'), contains(variables['configuration.IsPr'], 'False')) # if we are a PR, do not use ESRP since is not supported - template: generate-workspace-info.yml@templates parameters: From 71c33d373137fa625bdef88e139a4bf0732b6568 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Wed, 27 Jan 2021 21:00:10 -0800 Subject: [PATCH 27/33] [xcode12.4] [CI][VSTS] Use the creds store in git for https. (#10538) --- tools/devops/automation/templates/build/build.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index 6b84f9b69c..f7551655eb 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -28,6 +28,12 @@ steps: - checkout: release-scripts clean: true +- pwsh: | + rm -Rf "$HOME\.git-credentials" + git config --global credential.helper store + Set-Content -Path "$HOME\.git-credentials" -Value "https://$(GitHub.Token):x-oauth-basic@github.com`n" -NoNewline + displayName: 'Add git creds store' + - powershell: | gci env: | format-table -autosize -wrap displayName: 'Dump Environment' @@ -473,3 +479,8 @@ steps: artifactName: HtmlReport-sim continueOnError: true condition: and(succeededOrFailed(), contains(variables['runTests.TESTS_RAN'], 'True')) # if tests did not run, there is nothing to do + +- pwsh: | + rm -Rf "$HOME\.git-credentials" + displayName: 'Remove git creds store' + condition: always() From 5a05865f660ccfa5c56b4be7fedb9090f875c01c Mon Sep 17 00:00:00 2001 From: Alex Soto Date: Thu, 28 Jan 2021 02:02:41 -0500 Subject: [PATCH 28/33] [xcode12.4] Bump to use Xcode 12.4 Stable (#10544) --- Make.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Make.config b/Make.config index 722833e684..ff03e757be 100644 --- a/Make.config +++ b/Make.config @@ -112,8 +112,8 @@ WATCHOS_NUGET_VERSION_FULL=$(WATCHOS_NUGET_VERSION_NO_METADATA)+$(NUGET_BUILD_ME # Xcode version should have both a major and a minor version (even if the minor version is 0) XCODE_VERSION=12.4 -XCODE_URL=http://xamarin-storage/bot-provisioning/xcodes/Xcode_12.4_Release_Candidate.xip -XCODE_DEVELOPER_ROOT=/Applications/Xcode_12.4.0-rc.app/Contents/Developer +XCODE_URL=http://xamarin-storage/bot-provisioning/xcodes/Xcode_12.4.xip +XCODE_DEVELOPER_ROOT=/Applications/Xcode_12.4.0.app/Contents/Developer XCODE_PRODUCT_BUILD_VERSION:=$(shell /usr/libexec/PlistBuddy -c 'Print :ProductBuildVersion' $(XCODE_DEVELOPER_ROOT)/../version.plist) # Mono version embedded in XI/XM (NEEDED_MONO_VERSION/BRANCH) are specified in mk/mono.mk From c0b698eefcb2fc3c2dc6015cd08032b5948cc18f Mon Sep 17 00:00:00 2001 From: Manuel de la Pena Date: Mon, 1 Feb 2021 18:27:46 -0500 Subject: [PATCH 29/33] [xcode12.4][VSTS][CI] Fix issues with the internal pool and VSTS git usage. (#10560) The internal pool, for some reason, do not like the cred store. We need this to fix the interaction with the maccore repo. 1. VSTS uses https 2. We need to set the pat and try no to modify all bash scripts. We do so by changing the remote url of maccores git repo so that it includes the pat. --- .../devops/automation/templates/build/build.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index f7551655eb..85efc660c8 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -29,10 +29,17 @@ steps: clean: true - pwsh: | - rm -Rf "$HOME\.git-credentials" + # should we need sudo, no, but someone did something wrong in the images.. + sudo rm -Rf "$HOME/.git-credentials" git config --global credential.helper store - Set-Content -Path "$HOME\.git-credentials" -Value "https://$(GitHub.Token):x-oauth-basic@github.com`n" -NoNewline - displayName: 'Add git creds store' + Set-Content -Path "$HOME/.git-credentials" -Value "https://$(GitHub.Token):x-oauth-basic@github.com`n" -NoNewline + + # maccore is special, we use fetch there in some bash scripts, but VSTS uses https.. and some pools don't like the above.. :/ + cd $(System.DefaultWorkingDirectory)/maccore + git remote remove origin + git remote add origin https://$(GitHub.Token)@github.com/xamarin/maccore.git + git remote # don't add -v else we see the pat + displayName: 'Clean git mess from VSTS' - powershell: | gci env: | format-table -autosize -wrap @@ -481,6 +488,7 @@ steps: condition: and(succeededOrFailed(), contains(variables['runTests.TESTS_RAN'], 'True')) # if tests did not run, there is nothing to do - pwsh: | - rm -Rf "$HOME\.git-credentials" + # should we need sudo, no, but someone did something wrong in the images.. + sudo rm -Rf "$HOME/.git-credentials" displayName: 'Remove git creds store' condition: always() From b0f4e350837369d76111eba2d98aecd91e8f5371 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Thu, 4 Feb 2021 07:15:01 -0800 Subject: [PATCH 30/33] [xcode12.4] [CI][VSTS] Do not step over success statuses in rebuilds. (#10567) Co-authored-by: Manuel de la Pena --- tools/devops/automation/scripts/GitHub.psm1 | 31 +++++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/tools/devops/automation/scripts/GitHub.psm1 b/tools/devops/automation/scripts/GitHub.psm1 index a73443d970..4c9d790fb0 100644 --- a/tools/devops/automation/scripts/GitHub.psm1 +++ b/tools/devops/automation/scripts/GitHub.psm1 @@ -124,6 +124,7 @@ function Set-GitHubStatus { "SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT; "BUILD_BUILDID" = $Env:BUILD_BUILDID; "BUILD_REVISION" = $Env:BUILD_REVISION; + "BUILD_SOURCEBRANCHNAME" = $Env:BUILD_SOURCEBRANCHNAME; "GITHUB_TOKEN" = $Env:GITHUB_TOKEN; } @@ -134,6 +135,29 @@ function Set-GitHubStatus { } } + $url = "https://api.github.com/repos/xamarin/xamarin-macios/statuses/$Env:BUILD_REVISION" + + $headers = @{ + Authorization = ("token {0}" -f $Env:GITHUB_TOKEN) + } + + $requestContext = $Context + # before we set a status, we are going to check if it is present and a success, if it is a success, do not + # re-set it. The reason for this is that statuses are linked to the hash of a commit. A commit hash can be in two + # different branches. If a hash had a success we will not set the status and we will create a warning and a new context + # Why only on success, well we do want to support rebuilds, in any non-success case we want to step over + $presentStatuses = Invoke-Request -Request { Invoke-RestMethod -Uri $url -Headers $headers -Method "GET" -ContentType 'application/json' } + + # try to find the status with the same context and make a decision, this is not a dict but an array :/ + foreach ($s in $presentStatuses) { + # we found a status from a previous build that was a success, we do not want to step on it + if (($s.context -eq $Context) -and ($s.state -eq "success")) { + Write-Host "WARNING: Not setting status for $Context because it was already set as a success, using '$Context $Env:BUILD_SOURCEBRANCHNAME' instead." + # modify the context to include the branch name in the status + $requestContext = "$Context $Env:BUILD_SOURCEBRANCHNAME" + } + } + # use the GitHub API to set the status for the given commit $detailsUrl = "" if ($TargetUrl) { @@ -145,12 +169,7 @@ function Set-GitHubStatus { state = $Status target_url = $detailsUrl description = $Description - context = $Context - } - $url = "https://api.github.com/repos/xamarin/xamarin-macios/statuses/$Env:BUILD_REVISION" - - $headers = @{ - Authorization = ("token {0}" -f $Env:GITHUB_TOKEN) + context = $requestContext } return Invoke-Request -Request { Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-json) -ContentType 'application/json' } From 2ad820e4e41c1a8b45bd0c767bf95f2e8c391ee0 Mon Sep 17 00:00:00 2001 From: VS MobileTools Engineering Service 2 Date: Wed, 10 Feb 2021 12:30:37 -0800 Subject: [PATCH 31/33] [dotnet-linker] Delete unnecessary NuGet.config. (#10608) Co-authored-by: Rolf Bjarne Kvinge --- tools/dotnet-linker/NuGet.config | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 tools/dotnet-linker/NuGet.config diff --git a/tools/dotnet-linker/NuGet.config b/tools/dotnet-linker/NuGet.config deleted file mode 100644 index daf86b35ce..0000000000 --- a/tools/dotnet-linker/NuGet.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - From fbb7c5760980b57fc7131c0996a78b8cf8b746c8 Mon Sep 17 00:00:00 2001 From: Sebastien Pouliot Date: Thu, 11 Feb 2021 09:35:04 -0500 Subject: [PATCH 32/33] [xcode12.4] Update NuGet feed (#10600) bump maccore for dependencies updates New commits in xamarin/maccore: * xamarin/maccore@722e62d680 Remove dependency on XmlDocSync repo/tools (#2393) * xamarin/maccore@f3d1b21947 Update NuGet.config to enforce a single feed (#2374) Diff: https://github.com/xamarin/maccore/compare/77a7f937d8d54b3c7f8eb13602831205fd2a28db..722e62d68045cb7257043c5969ad31dba4038a28 * Disable dotnet (and related tests from release branch) * [xharness] Forcefully disable .NET tests. * Exclude more .NET stuff. * Ignore dotnet (if not enabled) for tests Co-authored-by: Sebastien Pouliot Co-authored-by: Rolf Bjarne Kvinge --- Makefile | 6 +++++- NuGet.config | 8 +++++++- jenkins/build.sh | 4 ++-- mk/xamarin.mk | 2 +- tests/Makefile | 6 +++++- tests/xharness/Jenkins/TestSelector.cs | 4 ++-- tools/devops/automation/templates/build/build.yml | 2 +- 7 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index db311ea7eb..762be7b888 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,12 @@ TOP=. -SUBDIRS=builds runtime fsharp src msbuild tools dotnet +SUBDIRS=builds runtime fsharp src msbuild tools include $(TOP)/Make.config include $(TOP)/mk/versions.mk +ifdef ENABLE_DOTNET +SUBDIRS += dotnet +endif + # # Common # diff --git a/NuGet.config b/NuGet.config index aa239d6eb3..063910a55e 100644 --- a/NuGet.config +++ b/NuGet.config @@ -4,8 +4,14 @@ - + + + + + + + diff --git a/jenkins/build.sh b/jenkins/build.sh index 9b974c0dc5..55cfb01a13 100755 --- a/jenkins/build.sh +++ b/jenkins/build.sh @@ -120,8 +120,8 @@ if test -z "$ENABLE_DEVICE_BUILD"; then CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-ios-device" fi -# Enable dotnet bits on the bots -CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-dotnet --enable-install-source" +# Disable dotnet bits on the bots (only useful in 'main' right now) +CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-dotnet --enable-install-source" echo "Configuring the build with: $CONFIGURE_FLAGS" # shellcheck disable=SC2086 diff --git a/mk/xamarin.mk b/mk/xamarin.mk index b70cf795fc..90fe9fb98e 100644 --- a/mk/xamarin.mk +++ b/mk/xamarin.mk @@ -7,7 +7,7 @@ MONO_BRANCH := $(shell cd $(MONO_PATH) 2> /dev/null && git symbolic-ref --sho endif ifdef ENABLE_XAMARIN -NEEDED_MACCORE_VERSION := 77a7f937d8d54b3c7f8eb13602831205fd2a28db +NEEDED_MACCORE_VERSION := 722e62d68045cb7257043c5969ad31dba4038a28 NEEDED_MACCORE_BRANCH := d16-8 MACCORE_DIRECTORY := maccore diff --git a/tests/Makefile b/tests/Makefile index f05438abf7..daa7b3c667 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,5 +1,9 @@ TOP = .. -SUBDIRS=test-libraries dotnet +SUBDIRS=test-libraries + +ifdef ENABLE_DOTNET +SUBDIRS += dotnet +endif # disabled for now: mac-test diff --git a/tests/xharness/Jenkins/TestSelector.cs b/tests/xharness/Jenkins/TestSelector.cs index 5bfeec3549..4de2901e47 100644 --- a/tests/xharness/Jenkins/TestSelector.cs +++ b/tests/xharness/Jenkins/TestSelector.cs @@ -175,7 +175,7 @@ namespace Xharness.Jenkins { SetEnabled (files, macBindingProject, "mac-binding-project", ref jenkins.IncludeMacBindingProject); SetEnabled (files, xtroPrefixes, "xtro", ref jenkins.IncludeXtro); SetEnabled (files, cecilPrefixes, "cecil", ref jenkins.IncludeCecil); - SetEnabled (files, dotnetFilenames, "dotnet", ref jenkins.IncludeDotNet); + // SetEnabled (files, dotnetFilenames, "dotnet", ref jenkins.IncludeDotNet); } void SelectTestsByLabel (int pullRequest) @@ -234,7 +234,7 @@ namespace Xharness.Jenkins { SetEnabled (labels, "xtro", ref jenkins.IncludeXtro); SetEnabled (labels, "cecil", ref jenkins.IncludeCecil); SetEnabled (labels, "old-simulator", ref jenkins.IncludeOldSimulatorTests); - SetEnabled (labels, "dotnet", ref jenkins.IncludeDotNet); + // SetEnabled (labels, "dotnet", ref jenkins.IncludeDotNet); SetEnabled (labels, "all", ref jenkins.IncludeAll); // enabled by default diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index 85efc660c8..6bc4df0278 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -230,7 +230,7 @@ steps: CONFIGURE_FLAGS="--enable-xamarin" fi - CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-dotnet --enable-install-source" + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --disable-dotnet --enable-install-source" cd $(Build.SourcesDirectory)/xamarin-macios/ ./configure $CONFIGURE_FLAGS From 296bdcdbb7fa04a69da25257981019613931fce8 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 12 Feb 2021 11:13:43 +0100 Subject: [PATCH 33/33] Move ENABLE_DOTNET logic until after we've set the variable. --- tests/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index e32d53c7b9..e96a58edd9 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,15 +1,15 @@ TOP = .. SUBDIRS=test-libraries -ifdef ENABLE_DOTNET -SUBDIRS += dotnet -endif - # disabled for now: mac-test include $(TOP)/Make.config include $(TOP)/mk/rules.mk +ifdef ENABLE_DOTNET +SUBDIRS += dotnet +endif + MTOUCH=$(IOS_DESTDIR)/$(MONOTOUCH_PREFIX)/bin/mtouch UNIT_SERVER_DIR=$(TOUCH_UNIT_PATH)/Touch.Server UNIT_SERVER=$(UNIT_SERVER_DIR)/bin/Debug/Touch.Server.exe