[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 <mjbond-msft@outlook.com> Co-authored-by: cadsit <connor.adsit@gmail.com> Co-authored-by: Rolf Bjarne Kvinge <rolf@xamarin.com>
This commit is contained in:
Родитель
734c120bb0
Коммит
1e78a9de6a
|
@ -13,7 +13,7 @@ _build
|
||||||
*.pdb
|
*.pdb
|
||||||
bin
|
bin
|
||||||
obj
|
obj
|
||||||
packages
|
./packages
|
||||||
~.pmcs*
|
~.pmcs*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
jenkins-results
|
jenkins-results
|
||||||
|
@ -33,6 +33,7 @@ tests/bcl-test/SystemXunit.csproj
|
||||||
global*.json
|
global*.json
|
||||||
.idea
|
.idea
|
||||||
device-tests-provisioning.csx
|
device-tests-provisioning.csx
|
||||||
|
build-provisioning.csx
|
||||||
mono_crash.*.json
|
mono_crash.*.json
|
||||||
*.binlog
|
*.binlog
|
||||||
.vscode
|
.vscode
|
||||||
|
|
|
@ -454,9 +454,12 @@ timestamps {
|
||||||
currentStage = "${STAGE_NAME}"
|
currentStage = "${STAGE_NAME}"
|
||||||
echo ("Building on ${env.NODE_NAME}")
|
echo ("Building on ${env.NODE_NAME}")
|
||||||
sh ("env | sort") // Print out environment for debug purposes
|
sh ("env | sort") // Print out environment for debug purposes
|
||||||
|
|
||||||
|
branchName = env.BRANCH_NAME
|
||||||
|
echo ("Branch name: ${branchName}")
|
||||||
|
|
||||||
scmVars = checkout scm
|
scmVars = checkout scm
|
||||||
isPr = (env.CHANGE_ID && !env.CHANGE_ID.empty ? true : false)
|
isPr = (env.CHANGE_ID && !env.CHANGE_ID.empty ? true : false)
|
||||||
branchName = env.BRANCH_NAME
|
|
||||||
if (isPr) {
|
if (isPr) {
|
||||||
gitHash = sh (script: "git log -1 --pretty=%H refs/remotes/origin/${env.BRANCH_NAME}", returnStdout: true).trim ()
|
gitHash = sh (script: "git log -1 --pretty=%H refs/remotes/origin/${env.BRANCH_NAME}", returnStdout: true).trim ()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,14 +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/
|
|
||||||
|
|
||||||
DOTNET_PKG_DIR=$(make -C jenkins 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/
|
|
|
@ -1,7 +0,0 @@
|
||||||
#!/bin/bash -ex
|
|
||||||
|
|
||||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
|
||||||
#WORKSPACE=$(pwd)
|
|
||||||
|
|
||||||
rm -Rf ../package
|
|
||||||
make package
|
|
|
@ -7,4 +7,22 @@ device-tests-provisioning.csx: device-tests-provisioning.csx.in Makefile $(TOP)/
|
||||||
-e 's#@XI_PACKAGE@#$(XI_PACKAGE)#g' \
|
-e 's#@XI_PACKAGE@#$(XI_PACKAGE)#g' \
|
||||||
-e 's#@MONO_PACKAGE@#$(MIN_MONO_URL)#g' \
|
-e 's#@MONO_PACKAGE@#$(MIN_MONO_URL)#g' \
|
||||||
-e 's#@VS_PACKAGE@#$(MIN_VISUAL_STUDIO_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' \
|
||||||
|
$< > $@;
|
||||||
|
|
||||||
|
all check:
|
||||||
|
shellcheck *.sh
|
||||||
|
|
||||||
|
print-abspath-variable:
|
||||||
|
@echo $(VARIABLE)=$(abspath $($(VARIABLE)))
|
||||||
|
|
||||||
|
provisioning: build-provisioning.csx device-tests-provisioning.csx
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,193 @@
|
||||||
|
# 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-*
|
||||||
|
|
||||||
|
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'
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
||||||
|
TOP=../../../..
|
||||||
|
include $(TOP)/Make.config
|
||||||
|
|
||||||
|
run-tests:
|
||||||
|
$(Q_GEN) pwsh -Command "Install-Module -AcceptLicense -Force -AllowClobber Pester;Invoke-Pester"
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
|
@ -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/
|
|
@ -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 }}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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()
|
|
@ -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
|
|
@ -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 }}
|
|
@ -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()
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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'
|
|
@ -0,0 +1,16 @@
|
||||||
|
#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@");
|
|
@ -13,5 +13,6 @@ Xcode ("@XCODE_XIP_NAME@").XcodeSelect (allowUntrusted: true);
|
||||||
Item ("@MONO_PACKAGE@");
|
Item ("@MONO_PACKAGE@");
|
||||||
Item ("@VS_PACKAGE@");
|
Item ("@VS_PACKAGE@");
|
||||||
Item ("@XI_PACKAGE@");
|
Item ("@XI_PACKAGE@");
|
||||||
|
DotNetCoreSdk ("@DOTNET_VERSION@");
|
||||||
|
|
||||||
BrewPackages ("p7zip");
|
BrewPackages ("p7zip");
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
BrewPackages (
|
||||||
|
"cmake",
|
||||||
|
"autoconf",
|
||||||
|
"automake",
|
||||||
|
"libtool",
|
||||||
|
"p7zip",
|
||||||
|
"python",
|
||||||
|
"libmagic",
|
||||||
|
"msitools",
|
||||||
|
"wget"
|
||||||
|
);
|
|
@ -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<string> 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;
|
||||||
|
}
|
|
@ -1,63 +1,10 @@
|
||||||
using System.Collections.Generic;
|
#load "provision-shared.csx"
|
||||||
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;
|
|
||||||
|
|
||||||
// Provision Xcode
|
// Provision Xcode
|
||||||
//
|
//
|
||||||
// Overrides:
|
// Overrides:
|
||||||
// * The current commit can be overridden by setting the PROVISION_FROM_COMMIT variable.
|
// * 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<string> 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 ()
|
void ListXcodes ()
|
||||||
{
|
{
|
||||||
Console.WriteLine ($"Xcodes:");
|
Console.WriteLine ($"Xcodes:");
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
python-magic==0.4.15
|
Загрузка…
Ссылка в новой задаче