diff --git a/jenkins/add-commit-comment.sh b/jenkins/add-commit-comment.sh index e791cdcac526..b8d52dc911b8 100755 --- a/jenkins/add-commit-comment.sh +++ b/jenkins/add-commit-comment.sh @@ -87,6 +87,8 @@ if test -n "$VERBOSE"; then sed 's/^/ /' "$JSONFILE"; fi +cat $JSONFILE + if ! curl -f -v -H "Authorization: token $TOKEN" -H "User-Agent: command line tool" -d "@$JSONFILE" "https://api.github.com/repos/xamarin/xamarin-macios/commits/$HASH/comments" > "$LOGFILE" 2>&1; then echo "Failed to add commit message." echo "curl output:" diff --git a/jenkins/add-commit-status.sh b/jenkins/add-commit-status.sh index a5291b7198cf..02955ab10b3a 100755 --- a/jenkins/add-commit-status.sh +++ b/jenkins/add-commit-status.sh @@ -128,6 +128,8 @@ trap cleanup EXIT printf "}\n" ) > "$JSONFILE" +cat $JSONFILE + if test -n "$VERBOSE"; then echo "JSON file:" sed 's/^/ /' "$JSONFILE"; diff --git a/jenkins/vsts-device-tests-set-status.sh b/jenkins/vsts-device-tests-set-status.sh index afb69830fe1f..9ed60bf6d7b5 100755 --- a/jenkins/vsts-device-tests-set-status.sh +++ b/jenkins/vsts-device-tests-set-status.sh @@ -89,7 +89,7 @@ if test -z "$START"; then printf "%s%s on [Azure DevOps](%s)($DEVICE_TYPE)%s %s\\n\\n" "$RESULT_EMOJII" "$DESCRIPTION" "$VSTS_BUILD_URL" "$HTML_REPORT" "$RESULT_EMOJII" >> "$MESSAGE_FILE" - FILE=$PWD/tests/TestSummary.md + FILE=$PWD/TestSummary.md if ! test -f "$FILE"; then printf "🔥 Tests failed catastrophically on $DEVICE_TYPE (no summary found)\\n" >> "$MESSAGE_FILE" else diff --git a/tests/Makefile b/tests/Makefile index e13dc2b7218b..d875998baadf 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -382,7 +382,7 @@ runner: xharness/xharness.exe # installed on the system. vsts-device-tests: xharness/xharness.exe $(MAKE) -C $(TOP)/builds .stamp-mono-ios-sdk-destdir download -j - $(Q) ulimit -n 4096 && $(SYSTEM_MONO) --debug $(CURDIR)/$< $(XHARNESS_VERBOSITY) --jenkins --autoconf --rootdir $(CURDIR) --sdkroot $(XCODE_DEVELOPER_ROOT) --use-system:true --label=skip-all-tests,run-device-tests,run-bcl-tests --markdown-summary=$(CURDIR)/TestSummary.md $(TESTS_EXTRA_ARGUMENTS) $(TESTS_PERIODIC_COMMAND) + $(Q) ulimit -n 4096 && $(SYSTEM_MONO) --debug $(CURDIR)/$< $(XHARNESS_VERBOSITY) --jenkins --autoconf --rootdir $(CURDIR) --sdkroot $(XCODE_DEVELOPER_ROOT) --use-system:true --label=skip-all-tests,run-device-tests --markdown-summary=$(CURDIR)/TestSummary.md $(TESTS_EXTRA_ARGUMENTS) $(TESTS_PERIODIC_COMMAND) ifdef ENABLE_XAMARIN wrench-launch-external wrench-report-external: diff --git a/tests/xharness/AppRunner.cs b/tests/xharness/AppRunner.cs index ec597986486a..61dcb2ed19e9 100644 --- a/tests/xharness/AppRunner.cs +++ b/tests/xharness/AppRunner.cs @@ -373,6 +373,7 @@ public bool EnsureCleanSimulatorState { var logFiles = new List (); // add our logs AND the logs of the previous task, which is the build task logFiles.AddRange (Directory.GetFiles (Logs.Directory)); + logFiles.Add (Harness.LogFile); if (BuildTask != null) // when using the run command, we do not have a build task, ergo, there are no logs to add. logFiles.AddRange (Directory.GetFiles (BuildTask.LogDirectory)); // add the attachments and write in the new filename diff --git a/tests/xharness/TestProject.cs b/tests/xharness/TestProject.cs index 2cf069173b17..69e03c9f9a67 100644 --- a/tests/xharness/TestProject.cs +++ b/tests/xharness/TestProject.cs @@ -98,7 +98,6 @@ public bool IsBclTest { public bool IsBclxUnit => IsBclTest && (Name.Contains ("xUnit") || IsMscorlib); - public bool IsMscorlib => Name.Contains ("mscorlib"); diff --git a/tools/devops/beta-device-tests.yml b/tools/devops/beta-device-tests.yml new file mode 100644 index 000000000000..c1e84b3c7928 --- /dev/null +++ b/tools/devops/beta-device-tests.yml @@ -0,0 +1,19 @@ +# xamarin-macios device pipeline, this file sets the different parameters required by stages template which will create set of stages depending +# on the configuration to run different device tests. +# +# lame! cannot have the resources in the template.. :/ +resources: + repositories: + - repository: maccore + type: github + name: xamarin/maccore + ref: refs/heads/device-tests-yaml # should this be on a branch and not on master??? + endpoint: xamarin + +# create parallel stages, this means that one single trigger to the pipeline will execute tvos and ios device tests +stages: +- template: templates/beta-stages.yml # all the smart stuff happens in the template, just needed to set the params + parameters: + pool: 'VSEng-Xamarin-Mac-Devices' + timeout: 1000 + maxParallel: 5 diff --git a/tools/devops/device-tests-common.yml b/tools/devops/device-tests-common.yml deleted file mode 100644 index f266ff9f692a..000000000000 --- a/tools/devops/device-tests-common.yml +++ /dev/null @@ -1,208 +0,0 @@ -# Xamarin -# -# The following template contains all the different steps that can be used to create a device tests pipeline in -# VSTS. The template accepts the following parameters: -# -# statusContext: Contains the context that will be used to set a status in a commit. The status can be used to indentify -# the source of the status, for example, iOS device tests, tvOS device tests, CI, etc.. -# testsLabels: Contains the labels that are going to be passed to the test runner to device which of the tests to be ran, that means -# that the caller can configure which tests to call, for example, just the ios 64b device tests. -# demands: The capabilities that the bots need. - -parameters: - -- name: name - type: string - default: 'macOS' # default - -- name: displayName - type: string - default: 'Run Device Tests' # default - -- name: pool - type: string - default: 'VSEng-Xamarin-Mac-Devices' # default - -- name: statusContext - type: string - default: 'iOS-DDFun' # default context, since we started dealing with iOS devices. - -- name: testsLabels - type: string - default: '--label=run-ios-64-tests' # default context, since we started dealing with iOS devices. - -- name: demands - type: string - default: 'ios' # default context, since we started dealing with iOS devices. - -jobs: -- job: ${{ parameters.name }} - displayName: ${{ parameters.displayName }} - timeoutInMinutes: 1000 - - pool: - name: ${{ parameters.pool }} - demands: ${{ parameters.demands }} - - steps: - - - checkout: self - - checkout: maccore - persistCredentials: true - - - bash: ./xamarin-macios/tools/devops/system-info.sh - displayName: System info - - - bash: cd xamarin-macios && ./configure --enable-xamarin - displayName: 'Enable Xamarin' - - - 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 - if [ $? -eq 0 ]; then - echo "##vso[task.setvariable variable=ProvisioningProfiles]success" - else - echo "##vso[task.setvariable variable=ProvisioningProfiles]failure" - fi - displayName: 'Add provisioning profiles' - env: - LOGIN_KEYCHAIN_PASSWORD: $(OSX_KEYCHAIN_PASS) - - - bash: | - set -x - set -e - - CONFIGURE_ARGS="--provision-xcode --provision-xamarin-studio --provision-mono --ignore-osx --ignore-autotools --ignore-cmake" - if grep ignore-7z xamarin-macios/system-dependencies.sh 2>&1 > /dev/null; then - CONFIGURE_ARGS="$CONFIGURE_ARGS --provision-7z" - fi - if grep ignore-python3 xamarin-macios/system-dependencies.sh 2>&1 > /dev/null; then - CONFIGURE_ARGS="$CONFIGURE_ARGS --ignore-python3" - fi - ./xamarin-macios/system-dependencies.sh $CONFIGURE_ARGS - displayName: 'Provision dependencies' - timeoutInMinutes: 240 - - - bash: ./xamarin-macios/tools/devops/install-xi-pkg.sh - displayName: 'Install XI PKG' - timeoutInMinutes: 15 - - - bash: | - set -x - set +e # Don't care about errors here - - pkill -9 mlaunch - pkill -9 -f mono.*xharness.exe - pkill -9 -f ssh.*rsync.*xamarin-storage - - exit 0 - displayName: 'Process cleanup' - - - bash: | - security set-key-partition-list -S apple-tool:,apple: -s -k $(OSX_KEYCHAIN_PASS) login.keychain - displayName: 'Remove security UI-prompt (http://stackoverflow.com/a/40039594/183422)' - condition: succeededOrFailed() - - - bash: | - set -e - set -x - echo 1234567890 > ~/.mlaunch-verbosity - displayName: 'Make mlaunch verbose' - - - bash: | - set -e - set -x - sudo launchctl stop com.apple.usbmuxd - displayName: 'Fix device discovery (reset launchctl)' - - ### - ### Tell GitHub we're starting working on this commit - ### - - - bash: | - ./xamarin-macios/jenkins/vsts-device-tests-set-status.sh --start "--token=$(GitHub.Token)" "--device=$CONTEXT" - env: - CONTEXT: ${{ parameters.statusContext }} - displayName: Set pending GitHub status - continueOnError: true - condition: succeededOrFailed() - - ### - ### Run the device tests - ### - - - bash: ./xamarin-macios/tools/devops/run-tests.sh - env: - TESTS_EXTRA_ARGUMENTS: ${{ parameters.testsLabels }} - displayName: 'Run tests' - timeoutInMinutes: 600 - - ### - ### Report final results to GitHub - ### - - - bash: ./xamarin-macios/jenkins/vsts-device-tests-set-status.sh "--token=$(GitHub.Token)" "--device=$CONTEXT" - env: - CONTEXT: ${{ parameters.statusContext }} - displayName: Report results to GitHub as comment / status - continueOnError: true - condition: succeededOrFailed() - - - bash: ./xamarin-macios/tools/devops/add-summaries.sh - displayName: 'Add summaries' - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'success')) # if profiles did not succeded we do not need to do a thing since we have no summaries - - - task: ArchiveFiles@1 - displayName: 'Archive HtmlReport' - inputs: - rootFolder: 'xamarin-macios/jenkins-results' - includeRootFolder: false - archiveFile: '$(Build.ArtifactStagingDirectory)/HtmlReport-$(Build.BuildId).zip' - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'success')) # if profiles did not succeded we do not need to do a thing since we have no summaries - - ### - ### Upload the xml results to vsts. We have two types, nunit and xunit. We want both - ### - - - task: PublishTestResults@2 - displayName: 'Publish NUnit Device Test Results' - inputs: - testResultsFormat: NUnit - testResultsFiles: '**/vsts-*.xml' - failTaskOnFailedTests: true - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'success')) # with no profiles we have no test results - - ### - ### Push the HTML report to Azure DevOps (shows up in Summary tab as Build Artifact) - ### - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: HtmlReport' - inputs: - pathtoPublish: '$(Build.ArtifactStagingDirectory)/HtmlReport-$(Build.BuildId).zip' - artifactName: HtmlReport - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'success')) # with no profiles we have no test results - - ### - ### Cleanup after us, not having that can lead to VSMac install issues - ### - - - bash: | - sudo rm -rf /Applications/Visual\ Studio* - rm -rf ~/Library/Caches/VisualStudio - rm -rf ~/Library/Logs/VisualStudio - rm -rf ~/Library/VisualStudio - rm -rf ~/Library/Preferences/Xamarin/ - rm -rf ~/Library/Caches/com.xamarin.provisionator - displayName: 'Cleanup' - continueOnError: true - condition: always() # no matter what, includes cancellation diff --git a/tools/devops/device-tests.yml b/tools/devops/device-tests.yml index 93c601caeba1..38bfdc77cb1e 100644 --- a/tools/devops/device-tests.yml +++ b/tools/devops/device-tests.yml @@ -1,5 +1,6 @@ -# iOS device pipeline, this file sets the different parameters required by the device-tests-common-yml, which contains all the steps and the interesting logic. - +# xamarin-macios device pipeline, this file sets the different parameters required by stages template which will create set of stages depending +# on the configuration to run different device tests. +# # lame! cannot have the resources in the template.. :/ resources: repositories: @@ -9,12 +10,10 @@ resources: ref: refs/heads/device-tests-yaml # should this be on a branch and not on master??? endpoint: xamarin -jobs: -- template: device-tests-common.yml # all the smart stuff happens in the template, just needed to set the params +# create parallel stages, this means that one single trigger to the pipeline will execute tvos and ios device tests +stages: +- template: templates/stable-stages.yml # all the smart stuff happens in the template, just needed to set the params parameters: - name: 'macOS' - displayName: 'Run iOS Device Tests' pool: 'VSEng-Xamarin-Mac-Devices' - demands: 'ios' - statusContext: 'iOS-DDFun' - testsLabels: '--label=run-ios-64-tests' + timeout: 1000 + maxParallel: 5 diff --git a/tools/devops/run-tests.sh b/tools/devops/run-tests.sh index dc222122c58c..2e1ea1ecd5ac 100755 --- a/tools/devops/run-tests.sh +++ b/tools/devops/run-tests.sh @@ -1,6 +1,8 @@ #!/bin/bash -ex - +ls -l +pwd +ls -l xamarin-macios/ if [[ "$SYSTEM_DEBUG" == "true" ]]; then DEBUG="-d" else @@ -9,3 +11,6 @@ fi make $DEBUG -C xamarin-macios/builds download -j make $DEBUG -C xamarin-macios/builds .stamp-mono-ios-sdk-destdir -j make $DEBUG -C xamarin-macios/tests vsts-device-tests +#echo "exit 100" > bar.sh +#chmod a+x bar.sh +#./bar.sh diff --git a/tools/devops/templates/beta-stages.yml b/tools/devops/templates/beta-stages.yml new file mode 100644 index 000000000000..e1955e38dfbc --- /dev/null +++ b/tools/devops/templates/beta-stages.yml @@ -0,0 +1,76 @@ +# Template that contains the different stages to be executed in a pipeline +# +# The tamplate contains the following parameters +# +# include32b: Run tests on iOS 32b devices. +# useBetaDevices: Use beta devices instead of stable ones. +# pool: The pool/lab to use +# timeout: Timeout used in the different jobs. +# maxParallel: Max number of parallel jobs to do in a stage. +# +parameters: + +# set demands as possible parameters because we keep changing them from lab to lab :/ +- name: iOSBetaDemand + type: string + default: 'xismoke-beta' # old QA capability + +- name: tvOSBetaDemand + type: string + default: 'xitvos-beta' # old QA capability + +- name: pool + type: string + default: 'VSEng-Xamarin-Mac-Devices' # default ddfun pool + +- name: timeout + type: number + default: 1000 + +- name: maxParallel + type: number + default: 5 + +stages: +- stage: iOSDeviceTests + displayName: 'iOS Device Tests' + dependsOn: [] # this removes the implicit dependency on previous stage and causes this to run in parallel + jobs: + - template: job-matrix.yml # all the smart stuff happens in the template, just needed to set the params + parameters: + demands: ${{ parameters.iOSBetaDemand }} + deviceLabel: 'run-ios-64-tests' + variation: "iOS" + pool: ${{ parameters.pool }} + timeout: ${{ parameters.timeout }} + maxParallel: ${{ parameters.maxParallel}} + +- stage: tvOSDeviceTests + displayName: 'tvOS Device Tests' + dependsOn: [] # this removes the implicit dependency on previous stage and causes this to run in parallel + jobs: + - template: job-matrix.yml # all the smart stuff happens in the template, just needed to set the params + parameters: + demands: ${{ parameters.tvOSBetaDemand }} + deviceLabel: 'run-tvos-tests' + variation: "tvOS" + pool: ${{ parameters.pool }} + timeout: ${{ parameters.timeout }} + maxParallel: ${{ parameters.maxParallel }} + +- stage: monitoring + displayName: 'Device test monitoring' + dependsOn: + - iOSDeviceTests + - tvOSDeviceTests + + condition: always() # always notify of what happened. + + pool: + name: ${{ parameters.pool }} + demands: 'ios' # we should not have demands here, but the problem is that DDFun does not set all machines to be able to checout private repos :/ + + jobs: + - job: + steps: + - template: publish-results.yml # template contains the steps to get all test reports and upload them diff --git a/tools/devops/templates/device-tests.yml b/tools/devops/templates/device-tests.yml new file mode 100644 index 000000000000..0d40dc30a872 --- /dev/null +++ b/tools/devops/templates/device-tests.yml @@ -0,0 +1,245 @@ +# Xamarin +# +# The following template contains all the different steps that can be used to create a device tests pipeline in +# VSTS. The template accepts the following parameters: +# +# demands: The capabilities that the bots need. +# +# Because we are using a matrix, the following env vars should be set in it: +# +# CONTEXT: The context that is used to set the status of the commit in github. +# TESTS_EXTRA_ARGUMENTS: the extra arguments passed to the runner to decide which tests need to be ran. +# +# Condition: succeededOrFailed() - true even if a previous dependency has failed, unless the run was canceled. + +steps: + +- checkout: self +- checkout: maccore + persistCredentials: true + +- bash: ./xamarin-macios/tools/devops/system-info.sh + displayName: System info + +- bash: cd xamarin-macios && ./configure --enable-xamarin + displayName: 'Enable Xamarin' + +- bash: | + set -x + set -e + + rm -f ~/Library/Caches/com.xamarin.provisionator/Provisions/*p12 + rm -f ~/Library/Caches/com.xamarin.provisionator/Provisions/*mobileprovision + + EC=0 + ./maccore/tools/install-qa-provisioning-profiles.sh -v || EC=$? + if [ $EC -eq 0 ]; then + echo "##vso[task.setvariable variable=ProvisioningProfiles]success" + else + echo "##vso[task.setvariable variable=ProvisioningProfiles]failure" + fi + exit $EC + displayName: 'Add provisioning profiles' + env: + LOGIN_KEYCHAIN_PASSWORD: $(OSX_KEYCHAIN_PASS) + +- bash: | + set -x + set -e + + CONFIGURE_ARGS="--provision-xcode --provision-xamarin-studio --provision-mono --ignore-osx --ignore-autotools --ignore-cmake" + if grep ignore-7z xamarin-macios/system-dependencies.sh 2>&1 > /dev/null; then + CONFIGURE_ARGS="$CONFIGURE_ARGS --provision-7z" + fi + if grep ignore-python3 xamarin-macios/system-dependencies.sh 2>&1 > /dev/null; then + CONFIGURE_ARGS="$CONFIGURE_ARGS --ignore-python3" + fi + + EC=0 + ./xamarin-macios/system-dependencies.sh $CONFIGURE_ARGS || EC=$? + if [ $EC -eq 0 ]; then + echo "##vso[task.setvariable variable=ProvisioningProfiles]success" + else + echo "##vso[task.setvariable variable=ProvisioningProfiles]failure" + fi + exit $EC + displayName: 'Provision dependencies' + timeoutInMinutes: 240 + +- bash: ./xamarin-macios/tools/devops/install-xi-pkg.sh + displayName: 'Install XI PKG' + timeoutInMinutes: 15 + +- bash: | + set -x + set +e # Don't care about errors here + + pkill -9 mlaunch + pkill -9 -f mono.*xharness.exe + pkill -9 -f ssh.*rsync.*xamarin-storage + + exit 0 + displayName: 'Process cleanup' + +- bash: | + security set-key-partition-list -S apple-tool:,apple: -s -k $(OSX_KEYCHAIN_PASS) login.keychain + displayName: 'Remove security UI-prompt (http://stackoverflow.com/a/40039594/183422)' + condition: succeededOrFailed() + +- bash: | + set -e + set -x + echo 1234567890 > ~/.mlaunch-verbosity + displayName: 'Make mlaunch verbose' + +- bash: | + set -e + set -x + sudo launchctl stop com.apple.usbmuxd + displayName: 'Fix device discovery (reset launchctl)' + +### +### Always set TestState to failure. If tests are skipped we do not want to have TestState == null. +### +- bash: | + set -x + set -e + + echo "##vso[task.setvariable variable=TestState]failure" + displayName: 'Set TestState to failure before trying to run tests' + condition: succeededOrFailed() + +### +### Run the device tests +### +- bash: | + set -x + set -e + + EC=0 + ./xamarin-macios/tools/devops/run-tests.sh || EC=$? + + if [ $EC -eq 0 ]; then + echo "##vso[task.setvariable variable=TestState]success" + else + echo "##vso[task.setvariable variable=TestState]failure" + fi + exit $EC + displayName: 'Run tests' + timeoutInMinutes: 600 + + ### + ### If test summary does not exist, create test summary files so that we can notify + ### that no results were published at the per-test AND final results steps + ### +- bash: | + set -e + set -x + FILE="xamarin-macios/tests/TestSummary.md" + echo $FILE + if ! test -f $FILE; then + echo ":fire: Tests failed catastrophically (no summary found)" > $FILE + echo "##vso[task.setvariable variable=TestState]failure" + fi + displayName: "Check for test summary, if does not exist create test summary" + condition: and(succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'success')) + +### +### If provisioning failed, create test summary files to indicate failure +### +- bash: | + set -e + set -x + FILE="xamarin-macios/tests/TestSummary.md" + echo $FILE + echo ":fire: Provisioning step failed" > $FILE + displayName: 'Provisioning failed: create test summary' + condition: and (succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'failure')) + +### +### Publish test summary +### We should publish summary for the following cases: +### 1) run-tests created summary files +### 2) run-tests did not create summary files and we created them +### 3) provisioning failed +### +- task: PublishPipelineArtifact@1 + displayName: 'Publish Artifact: TestSummary' + inputs: + targetPath: 'xamarin-macios/tests/TestSummary.md' + artifactName: TestSummary-$(CONTEXT) + continueOnError: true + condition: succeededOrFailed() + +### +### If there's a test failure || test summary not generated || provisioning failed: +### - update GH commit status +### - add commit comment +### +### Note: pwsh for PowerShell Core (Win + Mac), powershell is for Win only PowerShell +### +- pwsh: ./xamarin-macios/tools/devops/templates/publish_failure.ps1 + env: + BUILD_REVISION: $(BUILD_REVISION) + GH_STATE: $(GH_STATE) + GITHUB_TOKEN: $(GitHub.Token) + CONTEXT: $(CONTEXT) + TESTS: $(TESTS) + TESTSARGS: $(TESTS_EXTRA_ARGUMENTS) + displayName: 'Powershell: Add GH status & commit message' + condition: and(succeededOrFailed(), or (eq(variables['TestState'], 'failure'), eq(variables['ProvisioningProfiles'], 'failure'))) + +- bash: ./xamarin-macios/tools/devops/add-summaries.sh + displayName: 'Add summaries' + continueOnError: true + condition: and(succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'success')) + +- task: ArchiveFiles@1 + displayName: 'Archive HtmlReport' + inputs: + rootFolder: 'xamarin-macios/jenkins-results' + includeRootFolder: false + archiveFile: '$(Build.ArtifactStagingDirectory)/HtmlReport-$(Build.BuildId).zip' + continueOnError: true + condition: and(succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'success')) + +### +### Upload the xml results to vsts. We have two types, nunit and xunit. We want both +### + +- task: PublishTestResults@2 + displayName: 'Publish NUnit Device Test Results' + inputs: + testResultsFormat: NUnit + testResultsFiles: '**/vsts-*.xml' + failTaskOnFailedTests: true + testRunTitle: 'Test-Results-$(CONTEXT)-$(TESTS)' + continueOnError: true + condition: and(succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'success')) + +### +### Push the HTML report to Azure DevOps (shows up in Summary tab as Build Artifact) +### +- task: PublishPipelineArtifact@1 + displayName: 'Publish Artifact: HtmlReport' + inputs: + targetPath: '$(Build.ArtifactStagingDirectory)/HtmlReport-$(Build.BuildId).zip' + artifactName: HtmlReport-$(CONTEXT) + continueOnError: true + condition: and(succeededOrFailed(), eq(variables['ProvisioningProfiles'], 'success')) + + +### +### Cleanup after us, not having that can lead to VSMac install issues +### + +- bash: | + sudo rm -rf /Applications/Visual\ Studio* + rm -rf ~/Library/Caches/VisualStudio + rm -rf ~/Library/Logs/VisualStudio + rm -rf ~/Library/VisualStudio + rm -rf ~/Library/Preferences/Xamarin/ + rm -rf ~/Library/Caches/com.xamarin.provisionator + displayName: 'Cleanup' + continueOnError: true + condition: always() # no matter what, includes cancellation diff --git a/tools/devops/templates/job-matrix.yml b/tools/devops/templates/job-matrix.yml new file mode 100644 index 000000000000..1f95bc156152 --- /dev/null +++ b/tools/devops/templates/job-matrix.yml @@ -0,0 +1,78 @@ +# Common template that contains a matrix definition of the different jobs to be performed for device testing +# in xamarin-macios. The template defines a number of jobs that contains the project tests devided in groups, +# those are executed by different jobs in parallel (depending on agent availability). +# +# The template takes the following parameters: +# +# variation: Contains the string that will be used to identify the variation of the jobs executed. This is used to update the display names of the different jobs. +# pool: The agent pool to be used. +# demands: Contains the demands that the agents should have. This is used to state if the pipeline requires and iOS device, tvOS etc.. +# deviceLabel: Provide the device label used by xharness to decide what device type should be used in the tests execution. +# timeout: The timeout of the jobs in minutes. +# maxParallel: The amount of parallel jobs to be used + + +parameters: +- name: variation + type: string + default: iOS + +- name: pool + type: string + default: 'VSEng-Xamarin-Mac-Devices' # default ddfun pool + +- name: demands + type: string + default: 'ios' # we default to ios devices + +- name: deviceLabel + type: string + default: 'run-ios-64-tests' # default to iOS 64b devices + +- name: timeout + type: number + default: 1000 + +- name: maxParallel + type: number + default: 5 + +jobs: + +### +### Run all the xamarin tests, not bcl or mscorlib +### + +- job: DDFun + displayName: 'Run ${{ parameters.variation }} Device Tests' + timeoutInMinutes: ${{ parameters.timeout }} + pool: + name: ${{ parameters.pool }} + demands: ${{ parameters.demands }} + + strategy: + maxParallel: ${{ parameters.maxParallel }} + matrix: + Xamarin: + CONTEXT: '${{ parameters.variation }}-xamarin-DDFun' + TESTS_EXTRA_ARGUMENTS: '--label=${{ parameters.deviceLabel }},run-non-monotouch-tests' # run all tests but the bcl ones + TESTS: 'Non Monotouch' + Monotouch: + CONTEXT: '${{ parameters.variation }}-monotouch-DDFun' + TESTS_EXTRA_ARGUMENTS: '--label=${{ parameters.deviceLabel }},run-monotouch-tests' # run all tests but the bcl ones + TESTS: 'Monotouch' + xUnitBCL: + CONTEXT: '${{ parameters.variation }}-xUnit-BCL-DDFun' + TESTS_EXTRA_ARGUMENTS: '--label=${{ parameters.deviceLabel }},run-new-bcl-tests,skip-mscorlib-tests' # run only the new bcl tests + TESTS: 'xUnitBCL' + nUnitBCL: + CONTEXT: '${{ parameters.variation }}-NUnit-BCL-DDFun' + TESTS_EXTRA_ARGUMENTS: '--label=${{ parameters.deviceLabel }},run-old-bcl-tests' # run only the new bcl tests + TESTS: 'NUnitBCL' + Mscorlib: + CONTEXT: '${{ parameters.variation }}-mscorlib-DDFun' + TESTS_EXTRA_ARGUMENTS: '--label=${{ parameters.deviceLabel }},run-mscorlib-tests' # run only the new bcl tests + TESTS: 'mscorlib' + + steps: + - template: device-tests.yml # all the smart stuff happens in the template, just needed to set the params diff --git a/tools/devops/templates/publish-results.yml b/tools/devops/templates/publish-results.yml new file mode 100644 index 000000000000..da2821e2160b --- /dev/null +++ b/tools/devops/templates/publish-results.yml @@ -0,0 +1,44 @@ +# Xamarin +# +# The following templates contains all the different steps that can be used to consume the tests results from a +# matrix job and will unzip the pipeline artifacts and upload the results to VSTS +# +# The template takes the following env vars +# +# CONTEXT: The context that is used to set the status of the commit. + +steps: +### +### Download all test summaries +### + +- task: DownloadPipelineArtifact@2 + displayName: Download test summaries + inputs: + patterns: '**/TestSummary*' + allowFailedBuilds: true + path: $(Pipeline.Workspace)/Summaries + +### +### Publish commit message and status +### + +# pwsh for PowerShell Core +# powershell is for Win PowerShell +- pwsh: ./tools/devops/templates/publish_results.ps1 + displayName: Publish commit message + status + condition: succeededOrFailed() + env: + BUILD_REVISION: $(BUILD_REVISION) + GH_STATE: $(GH_STATE) + GITHUB_TOKEN: $(GitHub.Token) + CONTEXT: $(CONTEXT) + +### +### Report final results to GitHub +### + +- bash: ./jenkins/vsts-device-tests-set-status.sh "--token=$(GitHub.Token)" --device="VSTS-DDFun" + displayName: Report results to GitHub as comment / status + continueOnError: true + condition: succeededOrFailed() diff --git a/tools/devops/templates/publish_failure.ps1 b/tools/devops/templates/publish_failure.ps1 new file mode 100644 index 000000000000..ee5f0a3502dc --- /dev/null +++ b/tools/devops/templates/publish_failure.ps1 @@ -0,0 +1,117 @@ +# When this runs, a test failure has occurred +# --state=: The status state. + +Get-Location +Set-Location Env: +Get-ChildItem + +### +### Construct status +### + +$target_url = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + "$Env:SYSTEM_TEAMPROJECT/_build/index?buildId=$Env:BUILD_BUILDID&view=ms.vss-test-web.test-result-details" + +# state: we only report failures +$json_payload = @" +{ + "state" : "failure", + "target_url" : "$target_url", + "description" : "$Env:AGENT_JOBSTATUS", + "context" : "VSTS: $Env:SYSTEM_JOBNAME $Env:SYSTEM_STAGEDISPLAYNAME" +} +"@ + +$url = "https://api.github.com/repos/xamarin/xamarin-macios/statuses/$Env:BUILD_REVISION" + +Write-Host $json_payload +Write-Host $url + +$params = @{ + Uri = $url + Headers = @{'Authorization' = ("token {0}" -f $Env:GITHUB_TOKEN)} + Method = 'POST' + Body = $json_payload + ContentType = 'application/json' +} + +Write-Host $params + +try { + $response = Invoke-RestMethod @params + $response | ConvertTo-Json | Write-Host +} catch { + Write-Host "Error posting GH status: $_" +} + +### +### Construct commit message with test summary +### + +$RESULT_EMOJI = ":fire: " # we only run when there's a failure + +If ($Env:BUILD_DEFINITIONNAME -like '*DDFun*') +{ + ## do stuff +} + +$HEADER = "### :bangbang: :construction: TESTING Experimental DDFun pipeline" + +# SYSTEM_JOBNAME: Xamarin | Monotouch | xUnitBCL | NUnitBCL | mscorlib +# CONTEXT: tvOS | iOS | iOS32 +$DESCRIPTION="Device test **$Env:SYSTEM_JOBNAME** $Env:AGENT_JOBSTATUS on **$Env:CONTEXT**" + +# BUILD_DEFINITIONNAME: Pipeline name, e.g. "iOS Device Tests [DDFun]" +$json_text = "$RESULT_EMOJI $DESCRIPTION on [Azure DevOps]($target_url) ($Env:BUILD_DEFINITIONNAME) $RESULT_EMOJI" + +# Grab test results from default working directory +# Cannot access the published pipeline artifact because it is not always available when this script runs :( +$file = "$Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin-macios/tests" + +Write-Host "xamarin-macios tests children" +Get-ChildItem $file | Write-Host + +$file = "$file/TestSummary.md" + +Write-Host $file +Get-Content $file | Write-Host + +# stringbuilder for extra flavor +$msg = [System.Text.StringBuilder]::new() +$msg.AppendLine($HEADER) +$msg.AppendLine() +$msg.AppendLine($json_text) +$msg.AppendLine() + +# read each line of the summary file, append it with correct \n at the end +foreach ($line in Get-Content -Path $file) +{ + $msg.AppendLine($line) +} + +$message_url = "https://api.github.com/repos/xamarin/xamarin-macios/commits/$Env:BUILD_REVISION/comments" + +$payload = @{ + body = $msg.ToString() +} + +# convert payload to json +$json_payload = $payload | ConvertTo-json + +Write-Host $json_payload + +$params = @{ + Uri = $message_url + Headers = @{'Authorization' = ("token {0}" -f $Env:GITHUB_TOKEN)} + Method = 'POST' + Body = $json_payload + ContentType = 'application/json' +} + +Write-Host $params + +try { + $response = Invoke-RestMethod @params + $response | ConvertTo-Json | Write-Host +} catch { + Write-Host "Error posting GH commit message: $_" +} diff --git a/tools/devops/templates/publish_results.ps1 b/tools/devops/templates/publish_results.ps1 new file mode 100644 index 000000000000..890c2daba716 --- /dev/null +++ b/tools/devops/templates/publish_results.ps1 @@ -0,0 +1,158 @@ +# TODO: set up so that we only log debugging +# debugging code: print out all environment variables, including those passed in via yml +Get-Location +Set-Location Env: +Get-ChildItem + +### +### Construct commit status with combined test results +### + +### +### Get combined status: +### Success only if every status is success, otherwise failure +### If something goes wrong when we Invoke-RestMethod, status is set to error +### + +# url to query for combined status +$combined_status_url = "https://api.github.com/repos/xamarin/xamarin-macios/commits/$Env:BUILD_REVISION/status" + +$params = @{ + Uri = $combined_status_url + Headers = @{'Authorization' = ("token {0}" -f $Env:GITHUB_TOKEN)} + Method = 'GET' + ContentType = 'application/json' +} + +$state = "error" +try { + $response = Invoke-RestMethod @params + $state = $response.state +} catch { + Write-Host "Error querying for GH status: $_" +} + +### +### Post commit status to GitHub +### + +$target_url = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + "$Env:SYSTEM_TEAMPROJECT/_build/index?buildId=$Env:BUILD_BUILDID&view=ms.vss-test-web.test-result-details" + +$json_payload = @" +{ + "state" : "$state", + "target_url" : "$target_url", + "description" : "$state", + "context" : "VSTS: All device tests" +} +"@ + +$url = "https://api.github.com/repos/xamarin/xamarin-macios/statuses/$Env:BUILD_REVISION" + +Write-Host $json_payload +Write-Host $url + +$params = @{ + Uri = $url + Headers = @{'Authorization' = ("token {0}" -f $Env:GITHUB_TOKEN)} + Method = 'POST' + Body = $json_payload + ContentType = 'application/json' +} + +Write-Host $params + +try { + $response = Invoke-RestMethod @params + $response | Write-Host +} catch { + Write-Host "Error posting GH status: $_" +} + +### +### Construct commit message w/ aggregate test summary +### + +If ($Env:BUILD_DEFINITIONNAME -like '*DDFun*') +{ + # do stuff +} + +$dir = "$Env:PIPELINE_WORKSPACE/Summaries/TestSummary-*/TestSummary.md" + +Write-Host $dir +Get-ChildItem $dir | Write-Host + +# Test summary filepath follows the format: +# /Users/xamarinqa/azdo/_work/15/Summaries/TestSummary-DEVICE-TESTNAME/TestSummary.md +# /Users/xamarinqa/azdo/_work/15/Summaries/TestSummary-tvOS-monotouch-DDFun/TestSummary.md +# /Users/xamarinqa/azdo/_work/15/Summaries/TestSummary-tvOS-xamarin-DDFun/TestSummary.md +# Get all test summary files +$files = Get-ChildItem -Path $dir + +# BUILD_DEFINITIONNAME: Pipeline name, e.g. "iOS Device Tests [DDFun]" +$header = "### :boom: :construction: TESTING Experimental DDFun pipeline: Device test aggregate results: on [Azure DevOps]($target_url)" + +# stringbuilder for extra flavor +$msg = [System.Text.StringBuilder]::new() +$msg.AppendLine($header) +$msg.AppendLine() + +$prefix="$Env:PIPELINE_WORKSPACE/Summaries/TestSummary-" +$suffix="/TestSummary.md" + +foreach ($file in $files) +{ + # grab the test name + device string from filepath + $info = $file.FullName.Substring($prefix.Length, $file.FullName.Length - $suffix.Length - $prefix.Length) + + # switch to keep track of when we are on the first line of a summary + $first_line = 1 + + # read each line of the summary file, append it with correct \n at the end + foreach ($line in Get-Content -Path $file) + { + # if reading the first line, append test + device info to the header + # $msg will look like: "Test results: tvOS-monotouch-DDFun" + if ($first_line) + { + $msg.Append($line + ": ") + $msg.AppendLine($info) + $first_line = 0 + } + else { + $msg.AppendLine($line) + } + } + + # new line to separate file contents + $msg.AppendLine() +} + +$message_url = "https://api.github.com/repos/xamarin/xamarin-macios/commits/$Env:BUILD_REVISION/comments" + +$payload = @{ + body = $msg.ToString() +} + +# convert payload to json +$json_payload = $payload | ConvertTo-json + +Write-Host $json_payload + +$params = @{ + Uri = $message_url + Headers = @{'Authorization' = ("token {0}" -f $Env:GITHUB_TOKEN)} + Method = 'POST' + Body = $json_payload + ContentType = 'application/json' +} + +Write-Host $params + +try { + $response = Invoke-RestMethod @params + $response | ConvertTo-Json | Write-Host +} catch { + Write-Host "Error posting GH commit message: $_" +} diff --git a/tools/devops/templates/stable-stages.yml b/tools/devops/templates/stable-stages.yml new file mode 100644 index 000000000000..675eb557a8bd --- /dev/null +++ b/tools/devops/templates/stable-stages.yml @@ -0,0 +1,96 @@ +# Template that contains the different stages to be executed in a pipeline +# +# The tamplate contains the following parameters +# +# include32b: Run tests on iOS 32b devices. +# useBetaDevices: Use beta devices instead of stable ones. +# pool: The pool/lab to use +# timeout: Timeout used in the different jobs. +# maxParallel: Max number of parallel jobs to do in a stage. +# + +parameters: + +# set demands as possible parameters because we keep changing them from lab to lab :/ +- name: iOSDemand + type: string + default: 'ios' + +- name: iOS32Demand + type: string + default: 'xismoke-32' + +- name: tvOSDemand + type: string + default: 'tvos' + +- name: pool + type: string + default: 'VSEng-Xamarin-Mac-Devices' # default ddfun pool + +- name: timeout + type: number + default: 1000 + +- name: maxParallel + type: number + default: 5 + +stages: +- stage: iOSDeviceTests + displayName: 'iOS Device Tests' + dependsOn: [] # this removes the implicit dependency on previous stage and causes this to run in parallel + jobs: + - template: job-matrix.yml # all the smart stuff happens in the template, just needed to set the params + parameters: + demands: ${{ parameters.iOSDemand }} + deviceLabel: 'run-ios-64-tests' + variation: "iOS" + pool: ${{ parameters.pool }} + timeout: ${{ parameters.timeout }} + maxParallel: ${{ parameters.maxParallel}} + +- stage: tvOSDeviceTests + displayName: 'tvOS Device Tests' + dependsOn: [] # this removes the implicit dependency on previous stage and causes this to run in parallel + jobs: + - template: job-matrix.yml # all the smart stuff happens in the template, just needed to set the params + parameters: + demands: ${{ parameters.tvOSDemand }} + deviceLabel: 'run-tvos-tests' + variation: "tvOS" + pool: ${{ parameters.pool }} + timeout: ${{ parameters.timeout }} + maxParallel: ${{ parameters.maxParallel }} + +# Wait for the lab to have devices, templates do not let me do what I want with expressions :/ +#- stage: iOS32DeviceTests +# displayName: 'iOS32b Device Tests' +# dependsOn: [] # this removes the implicit dependency on previous stage and causes this to run in parallel +# jobs: +# - template: job-matrix.yml # all the smart stuff happens in the template, just needed to set the params +# parameters: +# demands: ${{ parameters.iOS32Demand }} # old QA lab capability, might change +# deviceLabel: 'run-ios-32-tests' +# variation: "iOS32b" +# pool: ${{ parameters.pool }} +# timeout: ${{ parameters.timeout }} +# maxParallel: ${{ parameters.maxParallel }} + +- stage: monitoring + displayName: 'Device test monitoring' + dependsOn: +# - iOSDeviceTests + - tvOSDeviceTests +# - iOS32DeviceTests + + condition: always() # always notify of what happened. + + pool: + name: ${{ parameters.pool }} + demands: 'ios' # we should not have demands here, but the problem is that DDFun does not set all machines to be able to checout private repos :/ + + jobs: + - job: + steps: + - template: publish-results.yml # template contains the steps to get all test reports and upload them