Compare commits

...

20 Commits

Author SHA1 Message Date
Julio Barba
857770cd3d TEST 2019-12-13 14:47:09 -05:00
Tingluo Huang
38b03139f1 c 2019-12-12 15:04:38 -05:00
Tingluo Huang
c1154c0ec9 c 2019-12-12 14:43:50 -05:00
Tingluo Huang
d7f2f3085c c 2019-12-12 14:42:15 -05:00
Tingluo Huang
1404a73762 rm aad 2019-12-12 14:42:02 -05:00
Tingluo Huang
3ea3b5ff59 c 2019-12-12 14:05:25 -05:00
Tingluo Huang
b37aa3254f c 2019-12-12 14:05:25 -05:00
Tingluo Huang
9b5eab9c81 c 2019-12-12 14:05:25 -05:00
Tingluo Huang
132d06dc9b localrun 2019-12-12 14:05:25 -05:00
Tingluo Huang
f2db563c89 delete un-used code. 2019-12-12 14:05:25 -05:00
Julio Barba
f78d35dc4e Trim Build2 SDK (#219)
* Trim Build2 SDK REST API methods
* Remove unused files
2019-12-12 13:53:12 -05:00
David Kale
181dac1c07 Update node external to 12.13.1 (#215)
* Update node external to 12.13.1

* Typo

* Typo2
2019-12-12 09:38:48 -05:00
eric sciple
ab87b39f53 better repo matching for issue file path (#208) 2019-12-11 14:21:26 -05:00
Julio Barba
a3c6a8c201 Introduce name config argument (#217) 2019-12-11 13:26:06 -05:00
Julio Barba
275ab753a1 Runner cleanup - continuation (#209)
* Agent/AgentCredential -> Runner/RunnerCredential
* Test trait rename: Agent -> Runner
* Enable remaining RunnerL0 tests
* Remove job message PII variable masking code
* Remove unused Agent.ToolsDirectory variable
* Misc test Agent -> Runner renaming
* Some more misc cleaning
2019-12-09 17:54:41 -05:00
Tingluo Huang
3ed80b7c10 Fix L0 tests, add/update runner release yaml. (#214) 2019-12-09 16:11:00 -05:00
Tingluo Huang
d81a7656a4 Add Proxy Support for self-hosted runner. (#206) 2019-12-09 15:15:54 -05:00
Julio Barba
56e18f3606 Update .NET install SH script (again) (#210) 2019-12-04 17:57:16 -05:00
Julio Barba
cd2cec8282 Another runner code cleanup round (#197)
* Remove remaining non-SDK references of capabilities/demands
* Remove unused Runner.Common constants
* Remove more variables
* Clean up RU link, and named-pipe support
* Remove NotificationSocketAddress
* Re-add legacy OnPremises JobDispatcher code (commented out)
* More misc cleanup
2019-12-04 10:18:37 -05:00
Julio Barba
f8829feb63 Update dotnet-install scripts (#207) 2019-12-03 17:13:03 -05:00
858 changed files with 2738 additions and 123300 deletions

184
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,184 @@
name: Runner CD
on:
push:
paths:
- src/runnerversion_block # Change this to src/runnerversion when we are ready.
jobs:
build:
strategy:
matrix:
runtime: [ linux-x64, linux-arm64, linux-arm, win-x64, osx-x64 ]
include:
- runtime: linux-x64
os: ubuntu-latest
devScript: ./dev.sh
- runtime: linux-arm64
os: ubuntu-latest
devScript: ./dev.sh
- runtime: linux-arm
os: ubuntu-latest
devScript: ./dev.sh
- runtime: osx-x64
os: macOS-latest
devScript: ./dev.sh
- runtime: win-x64
os: windows-latest
devScript: ./dev
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
# Build runner layout
- name: Build & Layout Release
run: |
${{ matrix.devScript }} layout Release ${{ matrix.runtime }}
working-directory: src
# Run tests
- name: L0
run: |
${{ matrix.devScript }} test
working-directory: src
if: matrix.runtime != 'linux-arm64' && matrix.runtime != 'linux-arm'
# Create runner package tar.gz/zip
- name: Package Release
if: github.event_name != 'pull_request'
run: |
${{ matrix.devScript }} package Release
working-directory: src
# Upload runner package tar.gz/zip as artifact.
# Since each package name is unique, so we don't need to put ${{matrix}} info into artifact name
- name: Publish Artifact
if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v1
with:
name: runner-packages
path: _package
release:
needs: build
runs-on: linux-latest
steps:
# Download runner package tar.gz/zip produced by 'build' job
- name: Download Artifact
uses: actions/download-artifact@v1
with:
name: runner-packages
# Create ReleaseNote file
- name: Create ReleaseNote
id: releaseNote
uses: actions/github-script@0.3.0
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const fs = require('fs');
// Get runner version from ./src/runnerVersion file
const versionContent = await github.repos.getContents({
owner: '${{ github.event.repository.owner.name }}',
repo: '${{ github.event.repository.name }}',
path: 'src/runnerversion'
ref: ${{ github.sha }}
})
const runnerVersion = Buffer.from(versionContent.data.content, 'base64').toString()
console.log("Runner Version ' + runnerVersion)
core.setOutput('version', runnerVersion);
// Query GitHub release ensure version is bumped
const latestRelease = await github.repos.getLatestRelease({
owner: '${{ github.event.repository.owner.name }}',
repo: '${{ github.event.repository.name }}'
})
console.log(latestRelease.name)
const latestReleaseVersion = latestRelease.name.substring(1)
const vLatest = latestReleaseVersion.split('.')
const vNew = runnerVersion.split('.')
let versionBumped = true
for (let i = 0; i < 3; ++i) {
var v1 = parseInt(vLatest[i], 10);
var v2 = parseInt(vNew[i], 10);
if (v2 > v1) {
console.log(runnerVersion + " > " + latestReleaseVersion + "(Latest)")
break
}
if (v1 > v2) {
versionBumped = false
core.setFailed(runnerVersion + " < " + latestReleaseVersion + "(Latest)")
break
}
}
// Generate release note
if (versionBumped) {
const releaseNoteContent = await github.repos.getContents({
owner: '${{ github.event.repository.owner.name }}',
repo: '${{ github.event.repository.name }}',
path: 'releaseNote.md'
ref: ${{ github.sha }}
})
const releaseNote = Buffer.from(releaseNoteContent.data.content, 'base64').toString().replace("<RUNNER_VERSION>", runnerVersion)
console.log(releaseNote)
core.setOutput('note', releaseNote);
}
# Create GitHub release
- uses: actions/create-release@v1
id: createRelease
name: Create ${{ steps.releaseNote.outputs.version }} Runner Release
with:
tag_name: "v${{ steps.releaseNote.outputs.version }}"
release_name: "v${{ steps.releaseNote.outputs.version }}"
body: ${{ steps.releaseNote.outputs.note }}
prerelease: true
# Upload release assets
- name: Upload Release Asset (win-x64)
uses: actions/upload-release-asset@v1.0.1
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ./actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}.zip
asset_name: actions-runner-win-x64-${{ steps.releaseNote.outputs.version }}.zip
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-x64)
uses: actions/upload-release-asset@v1.0.1
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ./actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}.zip
asset_name: actions-runner-linux-x64-${{ steps.releaseNote.outputs.version }}.zip
asset_content_type: application/octet-stream
- name: Upload Release Asset (mac-x64)
uses: actions/upload-release-asset@v1.0.1
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ./actions-runner-mac-x64-${{ steps.releaseNote.outputs.version }}.zip
asset_name: actions-runner-mac-x64-${{ steps.releaseNote.outputs.version }}.zip
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm)
uses: actions/upload-release-asset@v1.0.1
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ./actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}.zip
asset_name: actions-runner-linux-arm-${{ steps.releaseNote.outputs.version }}.zip
asset_content_type: application/octet-stream
- name: Upload Release Asset (linux-arm64)
uses: actions/upload-release-asset@v1.0.1
with:
upload_url: ${{ steps.createRelease.outputs.upload_url }}
asset_path: ./actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.zip
asset_name: actions-runner-linux-arm64-${{ steps.releaseNote.outputs.version }}.zip
asset_content_type: application/octet-stream

View File

@@ -221,15 +221,17 @@ stages:
$releaseCreated = Invoke-RestMethod @releaseParams $releaseCreated = Invoke-RestMethod @releaseParams
Write-Host $releaseCreated Write-Host $releaseCreated
$releaseId = $releaseCreated.id $releaseId = $releaseCreated.id
$assets = [System.IO.File]::ReadAllText("$(Build.SourcesDirectory)\assets.json").Replace("<RUNNER_VERSION>","$(ReleaseAgentVersion)") Get-ChildItem -LiteralPath "$(System.ArtifactsDirectory)/runners" | ForEach-Object {
$assetsParams = @{ Write-Host "Uploading $_ as GitHub release assets"
Uri = "https://uploads.github.com/repos/actions/runner/releases/$releaseId/assets?name=assets.json" $assetsParams = @{
Method = 'POST'; Uri = "https://uploads.github.com/repos/actions/runner/releases/$releaseId/assets?name=$($_.Name)"
Headers = @{ Method = 'POST';
Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("github:$(GithubToken)")); Headers = @{
Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("github:$(GithubToken)"));
}
ContentType = 'application/octet-stream';
Body = [System.IO.File]::ReadAllBytes($_.FullName)
} }
ContentType = 'application/octet-stream'; Invoke-RestMethod @assetsParams
Body = [system.Text.Encoding]::UTF8.GetBytes($assets)
} }
Invoke-RestMethod @assetsParams
displayName: Create agent release on Github displayName: Create agent release on Github

View File

@@ -167,7 +167,7 @@ function Get-CLIArchitecture-From-Architecture([string]$Architecture) {
{ $_ -eq "x86" } { return "x86" } { $_ -eq "x86" } { return "x86" }
{ $_ -eq "arm" } { return "arm" } { $_ -eq "arm" } { return "arm" }
{ $_ -eq "arm64" } { return "arm64" } { $_ -eq "arm64" } { return "arm64" }
default { throw "Architecture not supported. If you think this is a bug, report it at https://github.com/dotnet/cli/issues" } default { throw "Architecture not supported. If you think this is a bug, report it at https://github.com/dotnet/sdk/issues" }
} }
} }
@@ -309,14 +309,12 @@ function Parse-Jsonfile-For-Version([string]$JSonFile) {
If (-Not (Test-Path $JSonFile)) { If (-Not (Test-Path $JSonFile)) {
throw "Unable to find '$JSonFile'" throw "Unable to find '$JSonFile'"
exit 0
} }
try { try {
$JSonContent = Get-Content($JSonFile) -Raw | ConvertFrom-Json | Select-Object -expand "sdk" -ErrorAction SilentlyContinue $JSonContent = Get-Content($JSonFile) -Raw | ConvertFrom-Json | Select-Object -expand "sdk" -ErrorAction SilentlyContinue
} }
catch { catch {
throw "Json file unreadable: '$JSonFile'" throw "Json file unreadable: '$JSonFile'"
exit 0
} }
if ($JSonContent) { if ($JSonContent) {
try { try {
@@ -330,16 +328,13 @@ function Parse-Jsonfile-For-Version([string]$JSonFile) {
} }
catch { catch {
throw "Unable to parse the SDK node in '$JSonFile'" throw "Unable to parse the SDK node in '$JSonFile'"
exit 0
} }
} }
else { else {
throw "Unable to find the SDK node in '$JSonFile'" throw "Unable to find the SDK node in '$JSonFile'"
exit 0
} }
If ($Version -eq $null) { If ($Version -eq $null) {
throw "Unable to find the SDK:version node in '$JSonFile'" throw "Unable to find the SDK:version node in '$JSonFile'"
exit 0
} }
return $Version return $Version
} }
@@ -430,7 +425,7 @@ function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePath
Say-Invocation $MyInvocation Say-Invocation $MyInvocation
$DotnetPackagePath = Join-Path -Path $InstallRoot -ChildPath $RelativePathToPackage | Join-Path -ChildPath $SpecificVersion $DotnetPackagePath = Join-Path -Path $InstallRoot -ChildPath $RelativePathToPackage | Join-Path -ChildPath $SpecificVersion
Say-Verbose "Is-Dotnet-Package-Installed: Path to a package: $DotnetPackagePath" Say-Verbose "Is-Dotnet-Package-Installed: DotnetPackagePath=$DotnetPackagePath"
return Test-Path $DotnetPackagePath -PathType Container return Test-Path $DotnetPackagePath -PathType Container
} }
@@ -663,8 +658,22 @@ if ($DownloadFailed) {
Say "Extracting zip from $DownloadLink" Say "Extracting zip from $DownloadLink"
Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot
# Check if the SDK version is now installed; if not, fail the installation. # Check if the SDK version is installed; if not, fail the installation.
$isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion $isAssetInstalled = $false
# if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed.
if ($SpecificVersion -Match "rtm" -or $SpecificVersion -Match "servicing") {
$ReleaseVersion = $SpecificVersion.Split("-")[0]
Say-Verbose "Checking installation: version = $ReleaseVersion"
$isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $ReleaseVersion
}
# Check if the SDK version is installed.
if (!$isAssetInstalled) {
Say-Verbose "Checking installation: version = $SpecificVersion"
$isAssetInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $SpecificVersion
}
if (!$isAssetInstalled) { if (!$isAssetInstalled) {
throw "`"$assetName`" with version = $SpecificVersion failed to install with an unknown error." throw "`"$assetName`" with version = $SpecificVersion failed to install with an unknown error."
} }

View File

@@ -144,7 +144,7 @@ get_linux_platform_name() {
else else
if [ -e /etc/os-release ]; then if [ -e /etc/os-release ]; then
. /etc/os-release . /etc/os-release
echo "$ID.$VERSION_ID" echo "$ID${VERSION_ID:+.${VERSION_ID}}"
return 0 return 0
elif [ -e /etc/redhat-release ]; then elif [ -e /etc/redhat-release ]; then
local redhatRelease=$(</etc/redhat-release) local redhatRelease=$(</etc/redhat-release)
@@ -159,6 +159,10 @@ get_linux_platform_name() {
return 1 return 1
} }
is_musl_based_distro() {
(ldd --version 2>&1 || true) | grep -q musl
}
get_current_os_name() { get_current_os_name() {
eval $invocation eval $invocation
@@ -173,10 +177,10 @@ get_current_os_name() {
local linux_platform_name local linux_platform_name
linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; } linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; }
if [[ $linux_platform_name == "rhel.6" ]]; then if [ "$linux_platform_name" = "rhel.6" ]; then
echo $linux_platform_name echo $linux_platform_name
return 0 return 0
elif [[ $linux_platform_name == alpine* ]]; then elif is_musl_based_distro; then
echo "linux-musl" echo "linux-musl"
return 0 return 0
else else
@@ -202,7 +206,7 @@ get_legacy_os_name() {
else else
if [ -e /etc/os-release ]; then if [ -e /etc/os-release ]; then
. /etc/os-release . /etc/os-release
os=$(get_legacy_os_name_from_platform "$ID.$VERSION_ID" || echo "") os=$(get_legacy_os_name_from_platform "$ID${VERSION_ID:+.${VERSION_ID}}" || echo "")
if [ -n "$os" ]; then if [ -n "$os" ]; then
echo "$os" echo "$os"
return 0 return 0
@@ -245,20 +249,29 @@ check_pre_reqs() {
fi fi
if [ "$(uname)" = "Linux" ]; then if [ "$(uname)" = "Linux" ]; then
if [ ! -x "$(command -v ldconfig)" ]; then if is_musl_based_distro; then
echo "ldconfig is not in PATH, trying /sbin/ldconfig." if ! command -v scanelf > /dev/null; then
LDCONFIG_COMMAND="/sbin/ldconfig" say_warning "scanelf not found, please install pax-utils package."
return 0
fi
LDCONFIG_COMMAND="scanelf --ldpath -BF '%f'"
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libintl)" ] && say_warning "Unable to locate libintl. Probable prerequisite missing; install libintl (or gettext)."
else else
LDCONFIG_COMMAND="ldconfig" if [ ! -x "$(command -v ldconfig)" ]; then
say_verbose "ldconfig is not in PATH, trying /sbin/ldconfig."
LDCONFIG_COMMAND="/sbin/ldconfig"
else
LDCONFIG_COMMAND="ldconfig"
fi
local librarypath=${LD_LIBRARY_PATH:-}
LDCONFIG_COMMAND="$LDCONFIG_COMMAND -NXv ${librarypath//:/ }"
fi fi
local librarypath=${LD_LIBRARY_PATH:-} [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep zlib)" ] && say_warning "Unable to locate zlib. Probable prerequisite missing; install zlib."
LDCONFIG_COMMAND="$LDCONFIG_COMMAND -NXv ${librarypath//:/ }" [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep ssl)" ] && say_warning "Unable to locate libssl. Probable prerequisite missing; install libssl."
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libunwind)" ] && say_warning "Unable to locate libunwind. Probable prerequisite missing; install libunwind."
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libssl)" ] && say_warning "Unable to locate libssl. Probable prerequisite missing; install libssl."
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libicu)" ] && say_warning "Unable to locate libicu. Probable prerequisite missing; install libicu." [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libicu)" ] && say_warning "Unable to locate libicu. Probable prerequisite missing; install libicu."
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep -F libcurl.so)" ] && say_warning "Unable to locate libcurl. Probable prerequisite missing; install libcurl." [ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep lttng)" ] && say_warning "Unable to locate liblttng. Probable prerequisite missing; install libcurl."
[ -z "$($LDCONFIG_COMMAND 2>/dev/null | grep libcurl)" ] && say_warning "Unable to locate libcurl. Probable prerequisite missing; install libcurl."
fi fi
return 0 return 0
@@ -360,7 +373,7 @@ get_normalized_architecture_from_architecture() {
;; ;;
esac esac
say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/cli/issues" say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/sdk/issues"
return 1 return 1
} }
@@ -471,6 +484,7 @@ parse_jsonfile_for_version() {
return 1 return 1
fi fi
unset IFS;
echo "$version_info" echo "$version_info"
return 0 return 0
} }
@@ -631,7 +645,7 @@ copy_files_or_dirs_from_list() {
local osname="$(get_current_os_name)" local osname="$(get_current_os_name)"
local override_switch=$( local override_switch=$(
if [ "$override" = false ]; then if [ "$override" = false ]; then
if [[ "$osname" == "linux-musl" ]]; then if [ "$osname" = "linux-musl" ]; then
printf -- "-u"; printf -- "-u";
else else
printf -- "-n"; printf -- "-n";
@@ -840,13 +854,27 @@ install_dotnet() {
say "Extracting zip from $download_link" say "Extracting zip from $download_link"
extract_dotnet_package "$zip_path" "$install_root" extract_dotnet_package "$zip_path" "$install_root"
# Check if the SDK version is now installed; if not, fail the installation. # Check if the SDK version is installed; if not, fail the installation.
if ! is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then # if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed.
say_err "\`$asset_name\` with version = $specific_version failed to install with an unknown error." if [[ $specific_version == *"rtm"* || $specific_version == *"servicing"* ]]; then
return 1 IFS='-'
read -ra verArr <<< "$specific_version"
release_version="${verArr[0]}"
unset IFS;
say_verbose "Checking installation: version = $release_version"
if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$release_version"; then
return 0
fi
fi fi
return 0 # Check if the standard SDK version is installed.
say_verbose "Checking installation: version = $specific_version"
if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then
return 0
fi
say_err "\`$asset_name\` with version = $specific_version failed to install with an unknown error."
return 1
} }
args=("$@") args=("$@")

View File

@@ -3,7 +3,7 @@ PACKAGERUNTIME=$1
PRECACHE=$2 PRECACHE=$2
NODE_URL=https://nodejs.org/dist NODE_URL=https://nodejs.org/dist
NODE12_VERSION="12.4.0" NODE12_VERSION="12.13.1"
get_abs_path() { get_abs_path() {
# exploits the fact that pwd will print abs path when no args # exploits the fact that pwd will print abs path when no args
@@ -139,20 +139,13 @@ fi
# Download the external tools for Linux PACKAGERUNTIMEs. # Download the external tools for Linux PACKAGERUNTIMEs.
if [[ "$PACKAGERUNTIME" == "linux-x64" ]]; then if [[ "$PACKAGERUNTIME" == "linux-x64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-x64.tar.gz" node12 fix_nested_dir acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-x64.tar.gz" node12 fix_nested_dir
# TODO: Repath this blob to use a consistent version format (_ vs .) acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE12_VERSION}/alpine/x64/node-${NODE12_VERSION}-alpine-x64.tar.gz" node12_alpine
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/12_4_0/alpine/node-v${NODE12_VERSION}-alpine.tar.gz" node12_alpine
# acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/12.13.0/alpine/x64/node-v${NODE12_VERSION}-alpine-x64.tar.gz" node12_alpine
fi fi
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-arm64.tar.gz" node12 fix_nested_dir acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-arm64.tar.gz" node12 fix_nested_dir
# TODO: alpine node runtime for arm64(8)
# acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/12.13.0/alpine/arm64/node-v${NODE12_VERSION}-alpine-arm64.tar.gz" node12_alpine
fi fi
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-armv7l.tar.gz" node12 fix_nested_dir acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-armv7l.tar.gz" node12 fix_nested_dir
# TODO: alpine node runtime for arm32(7)
# Need to set up custom gcc toolchain to cross compile on x64 ubuntu for armv7 (per https://github.com/nodejs/node/blob/master/BUILDING.md)
# acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/12.13.0/alpine/arm/node-v${NODE12_VERSION}-alpine-arm.tar.gz" node12_alpine
fi fi

View File

@@ -9,9 +9,6 @@ varCheckList=(
'GRADLE_HOME' 'GRADLE_HOME'
'NVM_BIN' 'NVM_BIN'
'NVM_PATH' 'NVM_PATH'
'VSTS_HTTP_PROXY'
'VSTS_HTTP_PROXY_USERNAME'
'VSTS_HTTP_PROXY_PASSWORD'
'LD_LIBRARY_PATH' 'LD_LIBRARY_PATH'
'PERL5LIB' 'PERL5LIB'
) )

View File

@@ -15,24 +15,12 @@ namespace GitHub.Runner.Common
[DataContract] [DataContract]
public sealed class RunnerSettings public sealed class RunnerSettings
{ {
[DataMember(EmitDefaultValue = false)]
public bool AcceptTeeEula { get; set; }
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public int AgentId { get; set; } public int AgentId { get; set; }
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public string AgentName { get; set; } public string AgentName { get; set; }
[DataMember(EmitDefaultValue = false)]
public string NotificationPipeName { get; set; }
[DataMember(EmitDefaultValue = false)]
public string NotificationSocketAddress { get; set; }
[DataMember(EmitDefaultValue = false)]
public bool SkipCapabilitiesScan { get; set; }
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public bool SkipSessionRecover { get; set; } public bool SkipSessionRecover { get; set; }
@@ -151,7 +139,6 @@ namespace GitHub.Runner.Common
public bool HasCredentials() public bool HasCredentials()
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
Trace.Info("HasCredentials()"); Trace.Info("HasCredentials()");
bool credsStored = (new FileInfo(_credFilePath)).Exists; bool credsStored = (new FileInfo(_credFilePath)).Exists;
Trace.Info("stored {0}", credsStored); Trace.Info("stored {0}", credsStored);
@@ -161,14 +148,13 @@ namespace GitHub.Runner.Common
public bool IsConfigured() public bool IsConfigured()
{ {
Trace.Info("IsConfigured()"); Trace.Info("IsConfigured()");
bool configured = HostContext.RunMode == RunMode.Local || (new FileInfo(_configFilePath)).Exists; bool configured = new FileInfo(_configFilePath).Exists;
Trace.Info("IsConfigured: {0}", configured); Trace.Info("IsConfigured: {0}", configured);
return configured; return configured;
} }
public bool IsServiceConfigured() public bool IsServiceConfigured()
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
Trace.Info("IsServiceConfigured()"); Trace.Info("IsServiceConfigured()");
bool serviceConfigured = (new FileInfo(_serviceConfigFilePath)).Exists; bool serviceConfigured = (new FileInfo(_serviceConfigFilePath)).Exists;
Trace.Info($"IsServiceConfigured: {serviceConfigured}"); Trace.Info($"IsServiceConfigured: {serviceConfigured}");
@@ -177,7 +163,6 @@ namespace GitHub.Runner.Common
public CredentialData GetCredentials() public CredentialData GetCredentials()
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
if (_creds == null) if (_creds == null)
{ {
_creds = IOUtil.LoadObject<CredentialData>(_credFilePath); _creds = IOUtil.LoadObject<CredentialData>(_credFilePath);
@@ -207,7 +192,6 @@ namespace GitHub.Runner.Common
public void SaveCredential(CredentialData credential) public void SaveCredential(CredentialData credential)
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
Trace.Info("Saving {0} credential @ {1}", credential.Scheme, _credFilePath); Trace.Info("Saving {0} credential @ {1}", credential.Scheme, _credFilePath);
if (File.Exists(_credFilePath)) if (File.Exists(_credFilePath))
{ {
@@ -223,7 +207,6 @@ namespace GitHub.Runner.Common
public void SaveSettings(RunnerSettings settings) public void SaveSettings(RunnerSettings settings)
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
Trace.Info("Saving runner settings."); Trace.Info("Saving runner settings.");
if (File.Exists(_configFilePath)) if (File.Exists(_configFilePath))
{ {
@@ -239,13 +222,11 @@ namespace GitHub.Runner.Common
public void DeleteCredential() public void DeleteCredential()
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
IOUtil.Delete(_credFilePath, default(CancellationToken)); IOUtil.Delete(_credFilePath, default(CancellationToken));
} }
public void DeleteSettings() public void DeleteSettings()
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
IOUtil.Delete(_configFilePath, default(CancellationToken)); IOUtil.Delete(_configFilePath, default(CancellationToken));
} }

View File

@@ -2,12 +2,6 @@
namespace GitHub.Runner.Common namespace GitHub.Runner.Common
{ {
public enum RunMode
{
Normal, // Keep "Normal" first (default value).
Local,
}
public enum WellKnownDirectory public enum WellKnownDirectory
{ {
Bin, Bin,
@@ -29,9 +23,6 @@ namespace GitHub.Runner.Common
Service, Service,
CredentialStore, CredentialStore,
Certificates, Certificates,
Proxy,
ProxyCredentials,
ProxyBypass,
Options, Options,
} }
@@ -93,43 +84,28 @@ namespace GitHub.Runner.Common
//validArgs array as well present in the CommandSettings.cs //validArgs array as well present in the CommandSettings.cs
public static class Args public static class Args
{ {
public static readonly string Agent = "agent";
public static readonly string Auth = "auth"; public static readonly string Auth = "auth";
public static readonly string CollectionName = "collectionname";
public static readonly string DeploymentGroupName = "deploymentgroupname";
public static readonly string DeploymentPoolName = "deploymentpoolname";
public static readonly string DeploymentGroupTags = "deploymentgrouptags";
public static readonly string MachineGroupName = "machinegroupname";
public static readonly string MachineGroupTags = "machinegrouptags";
public static readonly string Matrix = "matrix";
public static readonly string MonitorSocketAddress = "monitorsocketaddress"; public static readonly string MonitorSocketAddress = "monitorsocketaddress";
public static readonly string NotificationPipeName = "notificationpipename"; public static readonly string Name = "name";
public static readonly string NotificationSocketAddress = "notificationsocketaddress";
public static readonly string Pool = "pool"; public static readonly string Pool = "pool";
public static readonly string ProjectName = "projectname";
public static readonly string ProxyUrl = "proxyurl";
public static readonly string ProxyUserName = "proxyusername";
public static readonly string SslCACert = "sslcacert"; public static readonly string SslCACert = "sslcacert";
public static readonly string SslClientCert = "sslclientcert"; public static readonly string SslClientCert = "sslclientcert";
public static readonly string SslClientCertKey = "sslclientcertkey"; public static readonly string SslClientCertKey = "sslclientcertkey";
public static readonly string SslClientCertArchive = "sslclientcertarchive"; public static readonly string SslClientCertArchive = "sslclientcertarchive";
public static readonly string SslClientCertPassword = "sslclientcertpassword";
public static readonly string StartupType = "startuptype"; public static readonly string StartupType = "startuptype";
public static readonly string Url = "url"; public static readonly string Url = "url";
public static readonly string UserName = "username"; public static readonly string UserName = "username";
public static readonly string WindowsLogonAccount = "windowslogonaccount"; public static readonly string WindowsLogonAccount = "windowslogonaccount";
public static readonly string Work = "work"; public static readonly string Work = "work";
public static readonly string Yml = "yml";
// Secret args. Must be added to the "Secrets" getter as well. // Secret args. Must be added to the "Secrets" getter as well.
public static readonly string Password = "password"; public static readonly string Password = "password";
public static readonly string ProxyPassword = "proxypassword"; public static readonly string SslClientCertPassword = "sslclientcertpassword";
public static readonly string Token = "token"; public static readonly string Token = "token";
public static readonly string WindowsLogonPassword = "windowslogonpassword"; public static readonly string WindowsLogonPassword = "windowslogonpassword";
public static string[] Secrets => new[] public static string[] Secrets => new[]
{ {
Password, Password,
ProxyPassword,
SslClientCertPassword, SslClientCertPassword,
Token, Token,
WindowsLogonPassword, WindowsLogonPassword,
@@ -139,7 +115,6 @@ namespace GitHub.Runner.Common
public static class Commands public static class Commands
{ {
public static readonly string Configure = "configure"; public static readonly string Configure = "configure";
public static readonly string LocalRun = "localRun";
public static readonly string Remove = "remove"; public static readonly string Remove = "remove";
public static readonly string Run = "run"; public static readonly string Run = "run";
public static readonly string Warmup = "warmup"; public static readonly string Warmup = "warmup";
@@ -149,26 +124,16 @@ namespace GitHub.Runner.Common
//validFlags array as well present in the CommandSettings.cs //validFlags array as well present in the CommandSettings.cs
public static class Flags public static class Flags
{ {
public static readonly string AcceptTeeEula = "acceptteeeula";
public static readonly string AddDeploymentGroupTags = "adddeploymentgrouptags";
public static readonly string AddMachineGroupTags = "addmachinegrouptags";
public static readonly string Commit = "commit"; public static readonly string Commit = "commit";
public static readonly string DeploymentGroup = "deploymentgroup";
public static readonly string DeploymentPool = "deploymentpool";
public static readonly string OverwriteAutoLogon = "overwriteautologon";
public static readonly string GitUseSChannel = "gituseschannel"; public static readonly string GitUseSChannel = "gituseschannel";
public static readonly string Help = "help"; public static readonly string Help = "help";
public static readonly string MachineGroup = "machinegroup";
public static readonly string Replace = "replace"; public static readonly string Replace = "replace";
public static readonly string NoRestart = "norestart";
public static readonly string LaunchBrowser = "launchbrowser"; public static readonly string LaunchBrowser = "launchbrowser";
public static readonly string Once = "once"; public static readonly string Once = "once";
public static readonly string RunAsAutoLogon = "runasautologon";
public static readonly string RunAsService = "runasservice"; public static readonly string RunAsService = "runasservice";
public static readonly string SslSkipCertValidation = "sslskipcertvalidation"; public static readonly string SslSkipCertValidation = "sslskipcertvalidation";
public static readonly string Unattended = "unattended"; public static readonly string Unattended = "unattended";
public static readonly string Version = "version"; public static readonly string Version = "version";
public static readonly string WhatIf = "whatif";
} }
} }
@@ -193,23 +158,16 @@ namespace GitHub.Runner.Common
public static class Configuration public static class Configuration
{ {
public static readonly string AAD = "AAD";
public static readonly string OAuthAccessToken = "OAuthAccessToken"; public static readonly string OAuthAccessToken = "OAuthAccessToken";
public static readonly string PAT = "PAT";
public static readonly string OAuth = "OAuth"; public static readonly string OAuth = "OAuth";
} }
public static class Expressions public static class Expressions
{ {
public static readonly string Always = "always"; public static readonly string Always = "always";
public static readonly string Canceled = "canceled";
public static readonly string Cancelled = "cancelled"; public static readonly string Cancelled = "cancelled";
public static readonly string Failed = "failed";
public static readonly string Failure = "failure"; public static readonly string Failure = "failure";
public static readonly string Success = "success"; public static readonly string Success = "success";
public static readonly string Succeeded = "succeeded";
public static readonly string SucceededOrFailed = "succeededOrFailed";
public static readonly string Variables = "variables";
} }
public static class Path public static class Path
@@ -221,9 +179,7 @@ namespace GitHub.Runner.Common
public static readonly string ExternalsDirectory = "externals"; public static readonly string ExternalsDirectory = "externals";
public static readonly string RunnerDiagnosticLogPrefix = "Runner_"; public static readonly string RunnerDiagnosticLogPrefix = "Runner_";
public static readonly string TempDirectory = "_temp"; public static readonly string TempDirectory = "_temp";
public static readonly string TeeDirectory = "tee";
public static readonly string ToolDirectory = "_tool"; public static readonly string ToolDirectory = "_tool";
public static readonly string TaskJsonFile = "task.json";
public static readonly string UpdateDirectory = "_update"; public static readonly string UpdateDirectory = "_update";
public static readonly string WorkDirectory = "_work"; public static readonly string WorkDirectory = "_work";
public static readonly string WorkerDiagnosticLogPrefix = "Worker_"; public static readonly string WorkerDiagnosticLogPrefix = "Worker_";
@@ -244,99 +200,14 @@ namespace GitHub.Runner.Common
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG"; public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
} }
public static class Agent
{
//
// Keep alphabetical
//
public static readonly string AcceptTeeEula = "agent.acceptteeeula";
public static readonly string AllowAllEndpoints = "agent.allowAllEndpoints"; // remove after sprint 120 or so.
public static readonly string AllowAllSecureFiles = "agent.allowAllSecureFiles"; // remove after sprint 121 or so.
public static readonly string BuildDirectory = "agent.builddirectory";
public static readonly string ContainerId = "agent.containerid";
public static readonly string ContainerNetwork = "agent.containernetwork";
public static readonly string HomeDirectory = "agent.homedirectory";
public static readonly string Id = "agent.id";
public static readonly string GitUseSChannel = "agent.gituseschannel";
public static readonly string JobName = "agent.jobname";
public static readonly string MachineName = "agent.machinename";
public static readonly string Name = "agent.name";
public static readonly string OS = "agent.os";
public static readonly string OSArchitecture = "agent.osarchitecture";
public static readonly string OSVersion = "agent.osversion";
public static readonly string ProxyUrl = "agent.proxyurl";
public static readonly string ProxyUsername = "agent.proxyusername";
public static readonly string ProxyPassword = "agent.proxypassword";
public static readonly string ProxyBypassList = "agent.proxybypasslist";
public static readonly string RetainDefaultEncoding = "agent.retainDefaultEncoding";
public static readonly string RootDirectory = "agent.RootDirectory";
public static readonly string RunMode = "agent.runmode";
public static readonly string ServerOMDirectory = "agent.ServerOMDirectory";
public static readonly string ServicePortPrefix = "agent.services";
public static readonly string SslCAInfo = "agent.cainfo";
public static readonly string SslClientCert = "agent.clientcert";
public static readonly string SslClientCertKey = "agent.clientcertkey";
public static readonly string SslClientCertArchive = "agent.clientcertarchive";
public static readonly string SslClientCertPassword = "agent.clientcertpassword";
public static readonly string SslSkipCertValidation = "agent.skipcertvalidation";
public static readonly string TempDirectory = "agent.TempDirectory";
public static readonly string ToolsDirectory = "agent.ToolsDirectory";
public static readonly string Version = "agent.version";
public static readonly string WorkFolder = "agent.workfolder";
public static readonly string WorkingDirectory = "agent.WorkingDirectory";
}
public static class Build
{
//
// Keep alphabetical
//
public static readonly string ArtifactStagingDirectory = "build.artifactstagingdirectory";
public static readonly string BinariesDirectory = "build.binariesdirectory";
public static readonly string Number = "build.buildNumber";
public static readonly string Clean = "build.clean";
public static readonly string DefinitionName = "build.definitionname";
public static readonly string GatedRunCI = "build.gated.runci";
public static readonly string GatedShelvesetName = "build.gated.shelvesetname";
public static readonly string RepoClean = "build.repository.clean";
public static readonly string RepoGitSubmoduleCheckout = "build.repository.git.submodulecheckout";
public static readonly string RepoId = "build.repository.id";
public static readonly string RepoLocalPath = "build.repository.localpath";
public static readonly string RepoName = "build.Repository.name";
public static readonly string RepoProvider = "build.repository.provider";
public static readonly string RepoTfvcWorkspace = "build.repository.tfvc.workspace";
public static readonly string RepoUri = "build.repository.uri";
public static readonly string SourceBranch = "build.sourcebranch";
public static readonly string SourceTfvcShelveset = "build.sourcetfvcshelveset";
public static readonly string SourceVersion = "build.sourceversion";
public static readonly string SourcesDirectory = "build.sourcesdirectory";
public static readonly string StagingDirectory = "build.stagingdirectory";
public static readonly string SyncSources = "build.syncSources";
}
public static class System public static class System
{ {
// //
// Keep alphabetical // Keep alphabetical
// //
public static readonly string AccessToken = "system.accessToken"; public static readonly string AccessToken = "system.accessToken";
public static readonly string ArtifactsDirectory = "system.artifactsdirectory";
public static readonly string CollectionId = "system.collectionid";
public static readonly string Culture = "system.culture"; public static readonly string Culture = "system.culture";
public static readonly string DefaultWorkingDirectory = "system.defaultworkingdirectory";
public static readonly string DefinitionId = "system.definitionid";
public static readonly string EnableAccessToken = "system.enableAccessToken";
public static readonly string HostType = "system.hosttype";
public static readonly string PhaseDisplayName = "system.phaseDisplayName"; public static readonly string PhaseDisplayName = "system.phaseDisplayName";
public static readonly string PreferGitFromPath = "system.prefergitfrompath";
public static readonly string PullRequestTargetBranchName = "system.pullrequest.targetbranch";
public static readonly string SelfManageGitCreds = "system.selfmanagegitcreds";
public static readonly string ServerType = "system.servertype";
public static readonly string TFServerUrl = "system.TeamFoundationServerUri"; // back compat variable, do not document
public static readonly string TeamProject = "system.teamproject";
public static readonly string TeamProjectId = "system.teamProjectId";
public static readonly string WorkFolder = "system.workfolder";
} }
} }
} }

View File

@@ -20,12 +20,12 @@ namespace GitHub.Runner.Common
{ {
public interface IHostContext : IDisposable public interface IHostContext : IDisposable
{ {
RunMode RunMode { get; set; }
StartupType StartupType { get; set; } StartupType StartupType { get; set; }
CancellationToken RunnerShutdownToken { get; } CancellationToken RunnerShutdownToken { get; }
ShutdownReason RunnerShutdownReason { get; } ShutdownReason RunnerShutdownReason { get; }
ISecretMasker SecretMasker { get; } ISecretMasker SecretMasker { get; }
ProductInfoHeaderValue UserAgent { get; } ProductInfoHeaderValue UserAgent { get; }
RunnerWebProxy WebProxy { get; }
string GetDirectory(WellKnownDirectory directory); string GetDirectory(WellKnownDirectory directory);
string GetConfigFile(WellKnownConfigFile configFile); string GetConfigFile(WellKnownConfigFile configFile);
Tracing GetTrace(string name); Tracing GetTrace(string name);
@@ -57,7 +57,6 @@ namespace GitHub.Runner.Common
private readonly ProductInfoHeaderValue _userAgent = new ProductInfoHeaderValue($"GitHubActionsRunner-{BuildConstants.RunnerPackage.PackageName}", BuildConstants.RunnerPackage.Version); private readonly ProductInfoHeaderValue _userAgent = new ProductInfoHeaderValue($"GitHubActionsRunner-{BuildConstants.RunnerPackage.PackageName}", BuildConstants.RunnerPackage.Version);
private CancellationTokenSource _runnerShutdownTokenSource = new CancellationTokenSource(); private CancellationTokenSource _runnerShutdownTokenSource = new CancellationTokenSource();
private object _perfLock = new object(); private object _perfLock = new object();
private RunMode _runMode = RunMode.Normal;
private Tracing _trace; private Tracing _trace;
private Tracing _vssTrace; private Tracing _vssTrace;
private Tracing _httpTrace; private Tracing _httpTrace;
@@ -67,12 +66,14 @@ namespace GitHub.Runner.Common
private IDisposable _diagListenerSubscription; private IDisposable _diagListenerSubscription;
private StartupType _startupType; private StartupType _startupType;
private string _perfFile; private string _perfFile;
private RunnerWebProxy _webProxy = new RunnerWebProxy();
public event EventHandler Unloading; public event EventHandler Unloading;
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token; public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
public ShutdownReason RunnerShutdownReason { get; private set; } public ShutdownReason RunnerShutdownReason { get; private set; }
public ISecretMasker SecretMasker => _secretMasker; public ISecretMasker SecretMasker => _secretMasker;
public ProductInfoHeaderValue UserAgent => _userAgent; public ProductInfoHeaderValue UserAgent => _userAgent;
public RunnerWebProxy WebProxy => _webProxy;
public HostContext(string hostType, string logFile = null) public HostContext(string hostType, string logFile = null)
{ {
// Validate args. // Validate args.
@@ -147,19 +148,47 @@ namespace GitHub.Runner.Common
_trace.Error(ex); _trace.Error(ex);
} }
} }
}
public RunMode RunMode // Check and trace proxy info
{ if (!string.IsNullOrEmpty(WebProxy.HttpProxyAddress))
get
{ {
return _runMode; if (string.IsNullOrEmpty(WebProxy.HttpProxyUsername) && string.IsNullOrEmpty(WebProxy.HttpProxyPassword))
{
_trace.Info($"Configuring anonymous proxy {WebProxy.HttpProxyAddress} for all HTTP requests.");
}
else
{
// Register proxy password as secret
if (!string.IsNullOrEmpty(WebProxy.HttpProxyPassword))
{
this.SecretMasker.AddValue(WebProxy.HttpProxyPassword);
}
_trace.Info($"Configuring authenticated proxy {WebProxy.HttpProxyAddress} for all HTTP requests.");
}
} }
set if (!string.IsNullOrEmpty(WebProxy.HttpsProxyAddress))
{ {
_trace.Info($"Set run mode: {value}"); if (string.IsNullOrEmpty(WebProxy.HttpsProxyUsername) && string.IsNullOrEmpty(WebProxy.HttpsProxyPassword))
_runMode = value; {
_trace.Info($"Configuring anonymous proxy {WebProxy.HttpsProxyAddress} for all HTTPS requests.");
}
else
{
// Register proxy password as secret
if (!string.IsNullOrEmpty(WebProxy.HttpsProxyPassword))
{
this.SecretMasker.AddValue(WebProxy.HttpsProxyPassword);
}
_trace.Info($"Configuring authenticated proxy {WebProxy.HttpsProxyAddress} for all HTTPS requests.");
}
}
if (string.IsNullOrEmpty(WebProxy.HttpProxyAddress) && string.IsNullOrEmpty(WebProxy.HttpsProxyAddress))
{
_trace.Info($"No proxy settings were found based on environmental variables (http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY)");
} }
} }
@@ -201,8 +230,8 @@ namespace GitHub.Runner.Common
break; break;
case WellKnownDirectory.Tools: case WellKnownDirectory.Tools:
// TODO: Coallesce to just check RUNNER_TOOL_CACHE when images stabilize path = Environment.GetEnvironmentVariable("RUNNER_TOOL_CACHE");
path = Environment.GetEnvironmentVariable("RUNNER_TOOL_CACHE") ?? Environment.GetEnvironmentVariable("RUNNER_TOOLSDIRECTORY") ?? Environment.GetEnvironmentVariable("AGENT_TOOLSDIRECTORY") ?? Environment.GetEnvironmentVariable(Constants.Variables.Agent.ToolsDirectory);
if (string.IsNullOrEmpty(path)) if (string.IsNullOrEmpty(path))
{ {
path = Path.Combine( path = Path.Combine(
@@ -282,24 +311,6 @@ namespace GitHub.Runner.Common
".certificates"); ".certificates");
break; break;
case WellKnownConfigFile.Proxy:
path = Path.Combine(
GetDirectory(WellKnownDirectory.Root),
".proxy");
break;
case WellKnownConfigFile.ProxyCredentials:
path = Path.Combine(
GetDirectory(WellKnownDirectory.Root),
".proxycredentials");
break;
case WellKnownConfigFile.ProxyBypass:
path = Path.Combine(
GetDirectory(WellKnownDirectory.Root),
".proxybypass");
break;
case WellKnownConfigFile.Options: case WellKnownConfigFile.Options:
path = Path.Combine( path = Path.Combine(
GetDirectory(WellKnownDirectory.Root), GetDirectory(WellKnownDirectory.Root),
@@ -580,8 +591,7 @@ namespace GitHub.Runner.Common
public static HttpClientHandler CreateHttpClientHandler(this IHostContext context) public static HttpClientHandler CreateHttpClientHandler(this IHostContext context)
{ {
HttpClientHandler clientHandler = new HttpClientHandler(); HttpClientHandler clientHandler = new HttpClientHandler();
var runnerWebProxy = context.GetService<IRunnerWebProxy>(); clientHandler.Proxy = context.WebProxy;
clientHandler.Proxy = runnerWebProxy.WebProxy;
return clientHandler; return clientHandler;
} }
} }

View File

@@ -12,53 +12,21 @@ namespace GitHub.Runner.Common
[ServiceLocator(Default = typeof(JobNotification))] [ServiceLocator(Default = typeof(JobNotification))]
public interface IJobNotification : IRunnerService, IDisposable public interface IJobNotification : IRunnerService, IDisposable
{ {
Task JobStarted(Guid jobId, string accessToken, Uri serverUrl); void JobStarted(Guid jobId, string accessToken, Uri serverUrl);
Task JobCompleted(Guid jobId); Task JobCompleted(Guid jobId);
void StartClient(string pipeName, string monitorSocketAddress, CancellationToken cancellationToken); void StartClient(string monitorSocketAddress);
void StartClient(string socketAddress, string monitorSocketAddress);
} }
public sealed class JobNotification : RunnerService, IJobNotification public sealed class JobNotification : RunnerService, IJobNotification
{ {
private NamedPipeClientStream _outClient;
private StreamWriter _writeStream;
private Socket _socket;
private Socket _monitorSocket; private Socket _monitorSocket;
private bool _configured = false;
private bool _useSockets = false;
private bool _isMonitorConfigured = false; private bool _isMonitorConfigured = false;
public async Task JobStarted(Guid jobId, string accessToken, Uri serverUrl) public void JobStarted(Guid jobId, string accessToken, Uri serverUrl)
{ {
Trace.Info("Entering JobStarted Notification"); Trace.Info("Entering JobStarted Notification");
StartMonitor(jobId, accessToken, serverUrl); StartMonitor(jobId, accessToken, serverUrl);
if (_configured)
{
String message = $"Starting job: {jobId.ToString()}";
if (_useSockets)
{
try
{
Trace.Info("Writing JobStarted to socket");
_socket.Send(Encoding.UTF8.GetBytes(message));
Trace.Info("Finished JobStarted writing to socket");
}
catch (SocketException e)
{
Trace.Error($"Failed sending message \"{message}\" on socket!");
Trace.Error(e);
}
}
else
{
Trace.Info("Writing JobStarted to pipe");
await _writeStream.WriteLineAsync(message);
await _writeStream.FlushAsync();
Trace.Info("Finished JobStarted writing to pipe");
}
}
} }
public async Task JobCompleted(Guid jobId) public async Task JobCompleted(Guid jobId)
@@ -66,95 +34,10 @@ namespace GitHub.Runner.Common
Trace.Info("Entering JobCompleted Notification"); Trace.Info("Entering JobCompleted Notification");
await EndMonitor(); await EndMonitor();
if (_configured)
{
String message = $"Finished job: {jobId.ToString()}";
if (_useSockets)
{
try
{
Trace.Info("Writing JobCompleted to socket");
_socket.Send(Encoding.UTF8.GetBytes(message));
Trace.Info("Finished JobCompleted writing to socket");
}
catch (SocketException e)
{
Trace.Error($"Failed sending message \"{message}\" on socket!");
Trace.Error(e);
}
}
else
{
Trace.Info("Writing JobCompleted to pipe");
await _writeStream.WriteLineAsync(message);
await _writeStream.FlushAsync();
Trace.Info("Finished JobCompleted writing to pipe");
}
}
} }
public async void StartClient(string pipeName, string monitorSocketAddress, CancellationToken cancellationToken) public void StartClient(string monitorSocketAddress)
{ {
if (pipeName != null && !_configured)
{
Trace.Info("Connecting to named pipe {0}", pipeName);
_outClient = new NamedPipeClientStream(".", pipeName, PipeDirection.Out, PipeOptions.Asynchronous);
await _outClient.ConnectAsync(cancellationToken);
_writeStream = new StreamWriter(_outClient, Encoding.UTF8);
_configured = true;
Trace.Info("Connection successful to named pipe {0}", pipeName);
}
ConnectMonitor(monitorSocketAddress);
}
public void StartClient(string socketAddress, string monitorSocketAddress)
{
if (!_configured)
{
try
{
string[] splitAddress = socketAddress.Split(':');
if (splitAddress.Length != 2)
{
Trace.Error("Invalid socket address {0}. Job Notification will be disabled.", socketAddress);
return;
}
IPAddress address;
try
{
address = IPAddress.Parse(splitAddress[0]);
}
catch (FormatException e)
{
Trace.Error("Invalid socket ip address {0}. Job Notification will be disabled",splitAddress[0]);
Trace.Error(e);
return;
}
int port = -1;
Int32.TryParse(splitAddress[1], out port);
if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
{
Trace.Error("Invalid tcp socket port {0}. Job Notification will be disabled.", splitAddress[1]);
return;
}
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
_socket.Connect(address, port);
Trace.Info("Connection successful to socket {0}", socketAddress);
_useSockets = true;
_configured = true;
}
catch (SocketException e)
{
Trace.Error("Connection to socket {0} failed!", socketAddress);
Trace.Error(e);
}
}
ConnectMonitor(monitorSocketAddress); ConnectMonitor(monitorSocketAddress);
} }
@@ -275,15 +158,6 @@ namespace GitHub.Runner.Common
{ {
if (disposing) if (disposing)
{ {
_outClient?.Dispose();
if (_socket != null)
{
_socket.Send(Encoding.UTF8.GetBytes("<EOF>"));
_socket.Shutdown(SocketShutdown.Both);
_socket = null;
}
if (_monitorSocket != null) if (_monitorSocket != null)
{ {
_monitorSocket.Send(Encoding.UTF8.GetBytes("<EOF>")); _monitorSocket.Send(Encoding.UTF8.GetBytes("<EOF>"));

View File

@@ -32,11 +32,6 @@ namespace GitHub.Runner.Common
public async Task ConnectAsync(VssConnection jobConnection) public async Task ConnectAsync(VssConnection jobConnection)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return;
}
_connection = jobConnection; _connection = jobConnection;
int attemptCount = 5; int attemptCount = 5;
while (!_connection.HasAuthenticated && attemptCount-- > 0) while (!_connection.HasAuthenticated && attemptCount-- > 0)
@@ -73,88 +68,48 @@ namespace GitHub.Runner.Common
public Task<TaskLog> AppendLogContentAsync(Guid scopeIdentifier, string hubName, Guid planId, int logId, Stream uploadStream, CancellationToken cancellationToken) public Task<TaskLog> AppendLogContentAsync(Guid scopeIdentifier, string hubName, Guid planId, int logId, Stream uploadStream, CancellationToken cancellationToken)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.FromResult<TaskLog>(null);
}
CheckConnection(); CheckConnection();
return _taskClient.AppendLogContentAsync(scopeIdentifier, hubName, planId, logId, uploadStream, cancellationToken: cancellationToken); return _taskClient.AppendLogContentAsync(scopeIdentifier, hubName, planId, logId, uploadStream, cancellationToken: cancellationToken);
} }
public Task AppendTimelineRecordFeedAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, Guid stepId, IList<string> lines, CancellationToken cancellationToken) public Task AppendTimelineRecordFeedAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, Guid stepId, IList<string> lines, CancellationToken cancellationToken)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.CompletedTask;
}
CheckConnection(); CheckConnection();
return _taskClient.AppendTimelineRecordFeedAsync(scopeIdentifier, hubName, planId, timelineId, timelineRecordId, stepId, lines, cancellationToken: cancellationToken); return _taskClient.AppendTimelineRecordFeedAsync(scopeIdentifier, hubName, planId, timelineId, timelineRecordId, stepId, lines, cancellationToken: cancellationToken);
} }
public Task<TaskAttachment> CreateAttachmentAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, string type, string name, Stream uploadStream, CancellationToken cancellationToken) public Task<TaskAttachment> CreateAttachmentAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, Guid timelineRecordId, string type, string name, Stream uploadStream, CancellationToken cancellationToken)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.FromResult<TaskAttachment>(null);
}
CheckConnection(); CheckConnection();
return _taskClient.CreateAttachmentAsync(scopeIdentifier, hubName, planId, timelineId, timelineRecordId, type, name, uploadStream, cancellationToken: cancellationToken); return _taskClient.CreateAttachmentAsync(scopeIdentifier, hubName, planId, timelineId, timelineRecordId, type, name, uploadStream, cancellationToken: cancellationToken);
} }
public Task<TaskLog> CreateLogAsync(Guid scopeIdentifier, string hubName, Guid planId, TaskLog log, CancellationToken cancellationToken) public Task<TaskLog> CreateLogAsync(Guid scopeIdentifier, string hubName, Guid planId, TaskLog log, CancellationToken cancellationToken)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.FromResult<TaskLog>(null);
}
CheckConnection(); CheckConnection();
return _taskClient.CreateLogAsync(scopeIdentifier, hubName, planId, log, cancellationToken: cancellationToken); return _taskClient.CreateLogAsync(scopeIdentifier, hubName, planId, log, cancellationToken: cancellationToken);
} }
public Task<Timeline> CreateTimelineAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, CancellationToken cancellationToken) public Task<Timeline> CreateTimelineAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, CancellationToken cancellationToken)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.FromResult<Timeline>(null);
}
CheckConnection(); CheckConnection();
return _taskClient.CreateTimelineAsync(scopeIdentifier, hubName, planId, new Timeline(timelineId), cancellationToken: cancellationToken); return _taskClient.CreateTimelineAsync(scopeIdentifier, hubName, planId, new Timeline(timelineId), cancellationToken: cancellationToken);
} }
public Task<List<TimelineRecord>> UpdateTimelineRecordsAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, IEnumerable<TimelineRecord> records, CancellationToken cancellationToken) public Task<List<TimelineRecord>> UpdateTimelineRecordsAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, IEnumerable<TimelineRecord> records, CancellationToken cancellationToken)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.FromResult<List<TimelineRecord>>(null);
}
CheckConnection(); CheckConnection();
return _taskClient.UpdateTimelineRecordsAsync(scopeIdentifier, hubName, planId, timelineId, records, cancellationToken: cancellationToken); return _taskClient.UpdateTimelineRecordsAsync(scopeIdentifier, hubName, planId, timelineId, records, cancellationToken: cancellationToken);
} }
public Task RaisePlanEventAsync<T>(Guid scopeIdentifier, string hubName, Guid planId, T eventData, CancellationToken cancellationToken) where T : JobEvent public Task RaisePlanEventAsync<T>(Guid scopeIdentifier, string hubName, Guid planId, T eventData, CancellationToken cancellationToken) where T : JobEvent
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.CompletedTask;
}
CheckConnection(); CheckConnection();
return _taskClient.RaisePlanEventAsync(scopeIdentifier, hubName, planId, eventData, cancellationToken: cancellationToken); return _taskClient.RaisePlanEventAsync(scopeIdentifier, hubName, planId, eventData, cancellationToken: cancellationToken);
} }
public Task<Timeline> GetTimelineAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, CancellationToken cancellationToken) public Task<Timeline> GetTimelineAsync(Guid scopeIdentifier, string hubName, Guid planId, Guid timelineId, CancellationToken cancellationToken)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.FromResult<Timeline>(null);
}
CheckConnection(); CheckConnection();
return _taskClient.GetTimelineAsync(scopeIdentifier, hubName, planId, timelineId, includeRecords: true, cancellationToken: cancellationToken); return _taskClient.GetTimelineAsync(scopeIdentifier, hubName, planId, timelineId, includeRecords: true, cancellationToken: cancellationToken);
} }

View File

@@ -63,7 +63,6 @@ namespace GitHub.Runner.Common
private Task[] _allDequeueTasks; private Task[] _allDequeueTasks;
private readonly TaskCompletionSource<int> _jobCompletionSource = new TaskCompletionSource<int>(); private readonly TaskCompletionSource<int> _jobCompletionSource = new TaskCompletionSource<int>();
private bool _queueInProcess = false; private bool _queueInProcess = false;
private ITerminal _term;
public event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling; public event EventHandler<ThrottlingEventArgs> JobServerQueueThrottling;
@@ -85,11 +84,6 @@ namespace GitHub.Runner.Common
public void Start(Pipelines.AgentJobRequestMessage jobRequest) public void Start(Pipelines.AgentJobRequestMessage jobRequest)
{ {
Trace.Entering(); Trace.Entering();
if (HostContext.RunMode == RunMode.Local)
{
_term = HostContext.GetService<ITerminal>();
return;
}
if (_queueInProcess) if (_queueInProcess)
{ {
@@ -129,11 +123,6 @@ namespace GitHub.Runner.Common
// TimelineUpdate queue error will become critical when timeline records contain output variabls. // TimelineUpdate queue error will become critical when timeline records contain output variabls.
public async Task ShutdownAsync() public async Task ShutdownAsync()
{ {
if (HostContext.RunMode == RunMode.Local)
{
return;
}
if (!_queueInProcess) if (!_queueInProcess)
{ {
Trace.Info("No-op, all queue process tasks have been stopped."); Trace.Info("No-op, all queue process tasks have been stopped.");
@@ -169,32 +158,11 @@ namespace GitHub.Runner.Common
public void QueueWebConsoleLine(Guid stepRecordId, string line) public void QueueWebConsoleLine(Guid stepRecordId, string line)
{ {
Trace.Verbose("Enqueue web console line queue: {0}", line); Trace.Verbose("Enqueue web console line queue: {0}", line);
if (HostContext.RunMode == RunMode.Local)
{
if ((line ?? string.Empty).StartsWith("##[section]"))
{
Console.WriteLine("******************************************************************************");
Console.WriteLine(line.Substring("##[section]".Length));
Console.WriteLine("******************************************************************************");
}
else
{
Console.WriteLine(line);
}
return;
}
_webConsoleLineQueue.Enqueue(new ConsoleLineInfo(stepRecordId, line)); _webConsoleLineQueue.Enqueue(new ConsoleLineInfo(stepRecordId, line));
} }
public void QueueFileUpload(Guid timelineId, Guid timelineRecordId, string type, string name, string path, bool deleteSource) public void QueueFileUpload(Guid timelineId, Guid timelineRecordId, string type, string name, string path, bool deleteSource)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return;
}
ArgUtil.NotEmpty(timelineId, nameof(timelineId)); ArgUtil.NotEmpty(timelineId, nameof(timelineId));
ArgUtil.NotEmpty(timelineRecordId, nameof(timelineRecordId)); ArgUtil.NotEmpty(timelineRecordId, nameof(timelineRecordId));
@@ -215,11 +183,6 @@ namespace GitHub.Runner.Common
public void QueueTimelineRecordUpdate(Guid timelineId, TimelineRecord timelineRecord) public void QueueTimelineRecordUpdate(Guid timelineId, TimelineRecord timelineRecord)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return;
}
ArgUtil.NotEmpty(timelineId, nameof(timelineId)); ArgUtil.NotEmpty(timelineId, nameof(timelineId));
ArgUtil.NotNull(timelineRecord, nameof(timelineRecord)); ArgUtil.NotNull(timelineRecord, nameof(timelineRecord));
ArgUtil.NotEmpty(timelineRecord.Id, nameof(timelineRecord.Id)); ArgUtil.NotEmpty(timelineRecord.Id, nameof(timelineRecord.Id));

View File

@@ -66,11 +66,6 @@ namespace GitHub.Runner.Common
public async Task ConnectAsync(Uri serverUrl, VssCredentials credentials) public async Task ConnectAsync(Uri serverUrl, VssCredentials credentials)
{ {
if (HostContext.RunMode == RunMode.Local)
{
return;
}
var createGenericConnection = EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(100)); var createGenericConnection = EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(100));
var createMessageConnection = EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(60)); var createMessageConnection = EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(60));
var createRequestConnection = EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(60)); var createRequestConnection = EstablishVssConnection(serverUrl, credentials, TimeSpan.FromSeconds(60));
@@ -303,29 +298,18 @@ namespace GitHub.Runner.Common
public Task<TaskAgentJobRequest> RenewAgentRequestAsync(int poolId, long requestId, Guid lockToken, CancellationToken cancellationToken = default(CancellationToken)) public Task<TaskAgentJobRequest> RenewAgentRequestAsync(int poolId, long requestId, Guid lockToken, CancellationToken cancellationToken = default(CancellationToken))
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.FromResult(JsonUtility.FromString<TaskAgentJobRequest>("{ lockedUntil: \"" + DateTime.Now.Add(TimeSpan.FromMinutes(5)).ToString("u") + "\" }"));
}
CheckConnection(RunnerConnectionType.JobRequest); CheckConnection(RunnerConnectionType.JobRequest);
return _requestTaskAgentClient.RenewAgentRequestAsync(poolId, requestId, lockToken, cancellationToken: cancellationToken); return _requestTaskAgentClient.RenewAgentRequestAsync(poolId, requestId, lockToken, cancellationToken: cancellationToken);
} }
public Task<TaskAgentJobRequest> FinishAgentRequestAsync(int poolId, long requestId, Guid lockToken, DateTime finishTime, TaskResult result, CancellationToken cancellationToken = default(CancellationToken)) public Task<TaskAgentJobRequest> FinishAgentRequestAsync(int poolId, long requestId, Guid lockToken, DateTime finishTime, TaskResult result, CancellationToken cancellationToken = default(CancellationToken))
{ {
if (HostContext.RunMode == RunMode.Local)
{
return Task.FromResult<TaskAgentJobRequest>(null);
}
CheckConnection(RunnerConnectionType.JobRequest); CheckConnection(RunnerConnectionType.JobRequest);
return _requestTaskAgentClient.FinishAgentRequestAsync(poolId, requestId, lockToken, finishTime, result, cancellationToken: cancellationToken); return _requestTaskAgentClient.FinishAgentRequestAsync(poolId, requestId, lockToken, finishTime, result, cancellationToken: cancellationToken);
} }
public Task<TaskAgentJobRequest> GetAgentRequestAsync(int poolId, long requestId, CancellationToken cancellationToken = default(CancellationToken)) public Task<TaskAgentJobRequest> GetAgentRequestAsync(int poolId, long requestId, CancellationToken cancellationToken = default(CancellationToken))
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
CheckConnection(RunnerConnectionType.JobRequest); CheckConnection(RunnerConnectionType.JobRequest);
return _requestTaskAgentClient.GetAgentRequestAsync(poolId, requestId, cancellationToken: cancellationToken); return _requestTaskAgentClient.GetAgentRequestAsync(poolId, requestId, cancellationToken: cancellationToken);
} }
@@ -335,7 +319,6 @@ namespace GitHub.Runner.Common
//----------------------------------------------------------------- //-----------------------------------------------------------------
public Task<List<PackageMetadata>> GetPackagesAsync(string packageType, string platform, int top, CancellationToken cancellationToken) public Task<List<PackageMetadata>> GetPackagesAsync(string packageType, string platform, int top, CancellationToken cancellationToken)
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
CheckConnection(RunnerConnectionType.Generic); CheckConnection(RunnerConnectionType.Generic);
return _genericTaskAgentClient.GetPackagesAsync(packageType, platform, top, cancellationToken: cancellationToken); return _genericTaskAgentClient.GetPackagesAsync(packageType, platform, top, cancellationToken: cancellationToken);
} }

View File

@@ -1,196 +0,0 @@
using GitHub.Runner.Common.Util;
using System;
using System.Linq;
using System.Net;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using GitHub.Runner.Sdk;
namespace GitHub.Runner.Common
{
[ServiceLocator(Default = typeof(RunnerWebProxy))]
public interface IRunnerWebProxy : IRunnerService
{
string ProxyAddress { get; }
string ProxyUsername { get; }
string ProxyPassword { get; }
List<string> ProxyBypassList { get; }
IWebProxy WebProxy { get; }
}
public class RunnerWebProxy : RunnerService, IRunnerWebProxy
{
private readonly List<Regex> _regExBypassList = new List<Regex>();
private readonly List<string> _bypassList = new List<string>();
private RunnerWebProxyCore _runnerWebProxy = new RunnerWebProxyCore();
public string ProxyAddress { get; private set; }
public string ProxyUsername { get; private set; }
public string ProxyPassword { get; private set; }
public List<string> ProxyBypassList => _bypassList;
public IWebProxy WebProxy => _runnerWebProxy;
public override void Initialize(IHostContext context)
{
base.Initialize(context);
LoadProxySetting();
}
// This should only be called from config
public void SetupProxy(string proxyAddress, string proxyUsername, string proxyPassword)
{
ArgUtil.NotNullOrEmpty(proxyAddress, nameof(proxyAddress));
Trace.Info($"Update proxy setting from '{ProxyAddress ?? string.Empty}' to'{proxyAddress}'");
ProxyAddress = proxyAddress;
ProxyUsername = proxyUsername;
ProxyPassword = proxyPassword;
if (string.IsNullOrEmpty(ProxyUsername) || string.IsNullOrEmpty(ProxyPassword))
{
Trace.Info($"Config proxy use DefaultNetworkCredentials.");
}
else
{
Trace.Info($"Config authentication proxy as: {ProxyUsername}.");
}
_runnerWebProxy.Update(ProxyAddress, ProxyUsername, ProxyPassword, ProxyBypassList);
}
// This should only be called from config
public void SaveProxySetting()
{
if (!string.IsNullOrEmpty(ProxyAddress))
{
string proxyConfigFile = HostContext.GetConfigFile(WellKnownConfigFile.Proxy);
IOUtil.DeleteFile(proxyConfigFile);
Trace.Info($"Store proxy configuration to '{proxyConfigFile}' for proxy '{ProxyAddress}'");
File.WriteAllText(proxyConfigFile, ProxyAddress);
File.SetAttributes(proxyConfigFile, File.GetAttributes(proxyConfigFile) | FileAttributes.Hidden);
string proxyCredFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyCredentials);
IOUtil.DeleteFile(proxyCredFile);
if (!string.IsNullOrEmpty(ProxyUsername) && !string.IsNullOrEmpty(ProxyPassword))
{
string lookupKey = Guid.NewGuid().ToString("D").ToUpperInvariant();
Trace.Info($"Store proxy credential lookup key '{lookupKey}' to '{proxyCredFile}'");
File.WriteAllText(proxyCredFile, lookupKey);
File.SetAttributes(proxyCredFile, File.GetAttributes(proxyCredFile) | FileAttributes.Hidden);
var credStore = HostContext.GetService<IRunnerCredentialStore>();
credStore.Write($"GITHUB_ACTIONS_RUNNER_PROXY_{lookupKey}", ProxyUsername, ProxyPassword);
}
}
else
{
Trace.Info("No proxy configuration exist.");
}
}
// This should only be called from unconfig
public void DeleteProxySetting()
{
string proxyCredFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyCredentials);
if (File.Exists(proxyCredFile))
{
Trace.Info("Delete proxy credential from credential store.");
string lookupKey = File.ReadAllLines(proxyCredFile).FirstOrDefault();
if (!string.IsNullOrEmpty(lookupKey))
{
var credStore = HostContext.GetService<IRunnerCredentialStore>();
credStore.Delete($"GITHUB_ACTIONS_RUNNER_PROXY_{lookupKey}");
}
Trace.Info($"Delete .proxycredentials file: {proxyCredFile}");
IOUtil.DeleteFile(proxyCredFile);
}
string proxyBypassFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyBypass);
if (File.Exists(proxyBypassFile))
{
Trace.Info($"Delete .proxybypass file: {proxyBypassFile}");
IOUtil.DeleteFile(proxyBypassFile);
}
string proxyConfigFile = HostContext.GetConfigFile(WellKnownConfigFile.Proxy);
Trace.Info($"Delete .proxy file: {proxyConfigFile}");
IOUtil.DeleteFile(proxyConfigFile);
}
private void LoadProxySetting()
{
string proxyConfigFile = HostContext.GetConfigFile(WellKnownConfigFile.Proxy);
if (File.Exists(proxyConfigFile))
{
// we expect the first line of the file is the proxy url
Trace.Verbose($"Try read proxy setting from file: {proxyConfigFile}.");
ProxyAddress = File.ReadLines(proxyConfigFile).FirstOrDefault() ?? string.Empty;
ProxyAddress = ProxyAddress.Trim();
Trace.Verbose($"{ProxyAddress}");
}
if (!string.IsNullOrEmpty(ProxyAddress) && !Uri.IsWellFormedUriString(ProxyAddress, UriKind.Absolute))
{
Trace.Info($"The proxy url is not a well formed absolute uri string: {ProxyAddress}.");
ProxyAddress = string.Empty;
}
if (!string.IsNullOrEmpty(ProxyAddress))
{
Trace.Info($"Config proxy at: {ProxyAddress}.");
string proxyCredFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyCredentials);
if (File.Exists(proxyCredFile))
{
string lookupKey = File.ReadAllLines(proxyCredFile).FirstOrDefault();
if (!string.IsNullOrEmpty(lookupKey))
{
var credStore = HostContext.GetService<IRunnerCredentialStore>();
var proxyCred = credStore.Read($"GITHUB_ACTIONS_RUNNER_PROXY_{lookupKey}");
ProxyUsername = proxyCred.UserName;
ProxyPassword = proxyCred.Password;
}
}
if (!string.IsNullOrEmpty(ProxyPassword))
{
HostContext.SecretMasker.AddValue(ProxyPassword);
}
if (string.IsNullOrEmpty(ProxyUsername) || string.IsNullOrEmpty(ProxyPassword))
{
Trace.Info($"Config proxy use DefaultNetworkCredentials.");
}
else
{
Trace.Info($"Config authentication proxy as: {ProxyUsername}.");
}
string proxyBypassFile = HostContext.GetConfigFile(WellKnownConfigFile.ProxyBypass);
if (File.Exists(proxyBypassFile))
{
Trace.Verbose($"Try read proxy bypass list from file: {proxyBypassFile}.");
foreach (string bypass in File.ReadAllLines(proxyBypassFile))
{
if (string.IsNullOrWhiteSpace(bypass))
{
continue;
}
else
{
Trace.Info($"Bypass proxy for: {bypass}.");
ProxyBypassList.Add(bypass.Trim());
}
}
}
_runnerWebProxy.Update(ProxyAddress, ProxyUsername, ProxyPassword, ProxyBypassList);
}
else
{
Trace.Info($"No proxy setting found.");
}
}
}
}

View File

@@ -50,6 +50,13 @@ namespace GitHub.Runner.Common
public void Error(Exception exception) public void Error(Exception exception)
{ {
Trace(TraceEventType.Error, exception.ToString()); Trace(TraceEventType.Error, exception.ToString());
var innerEx = exception.InnerException;
while (innerEx != null)
{
Trace(TraceEventType.Error, "#####################################################");
Trace(TraceEventType.Error, innerEx.ToString());
innerEx = innerEx.InnerException;
}
} }
// Do not remove the non-format overload. // Do not remove the non-format overload.

View File

@@ -42,15 +42,11 @@ namespace GitHub.Runner.Listener
private readonly string[] validArgs = private readonly string[] validArgs =
{ {
Constants.Runner.CommandLine.Args.Agent,
Constants.Runner.CommandLine.Args.Auth, Constants.Runner.CommandLine.Args.Auth,
Constants.Runner.CommandLine.Args.MonitorSocketAddress, Constants.Runner.CommandLine.Args.MonitorSocketAddress,
Constants.Runner.CommandLine.Args.NotificationPipeName, Constants.Runner.CommandLine.Args.Name,
Constants.Runner.CommandLine.Args.Password, Constants.Runner.CommandLine.Args.Password,
Constants.Runner.CommandLine.Args.Pool, Constants.Runner.CommandLine.Args.Pool,
Constants.Runner.CommandLine.Args.ProxyPassword,
Constants.Runner.CommandLine.Args.ProxyUrl,
Constants.Runner.CommandLine.Args.ProxyUserName,
Constants.Runner.CommandLine.Args.SslCACert, Constants.Runner.CommandLine.Args.SslCACert,
Constants.Runner.CommandLine.Args.SslClientCert, Constants.Runner.CommandLine.Args.SslClientCert,
Constants.Runner.CommandLine.Args.SslClientCertKey, Constants.Runner.CommandLine.Args.SslClientCertKey,
@@ -174,15 +170,6 @@ namespace GitHub.Runner.Listener
// //
// Args. // Args.
// //
public string GetAgentName()
{
return GetArgOrPrompt(
name: Constants.Runner.CommandLine.Args.Agent,
description: "Enter the name of runner:",
defaultValue: Environment.MachineName ?? "myagent",
validator: Validators.NonEmptyValidator);
}
public string GetAuth(string defaultValue) public string GetAuth(string defaultValue)
{ {
return GetArgOrPrompt( return GetArgOrPrompt(
@@ -210,6 +197,15 @@ namespace GitHub.Runner.Listener
validator: Validators.NonEmptyValidator); validator: Validators.NonEmptyValidator);
} }
public string GetRunnerName()
{
return GetArgOrPrompt(
name: Constants.Runner.CommandLine.Args.Name,
description: "Enter the name of runner:",
defaultValue: Environment.MachineName ?? "myrunner",
validator: Validators.NonEmptyValidator);
}
public string GetToken() public string GetToken()
{ {
return GetArgOrPrompt( return GetArgOrPrompt(
@@ -285,37 +281,12 @@ namespace GitHub.Runner.Listener
return GetArg(Constants.Runner.CommandLine.Args.MonitorSocketAddress); return GetArg(Constants.Runner.CommandLine.Args.MonitorSocketAddress);
} }
public string GetNotificationPipeName()
{
return GetArg(Constants.Runner.CommandLine.Args.NotificationPipeName);
}
public string GetNotificationSocketAddress()
{
return GetArg(Constants.Runner.CommandLine.Args.NotificationSocketAddress);
}
// This is used to find out the source from where the Runner.Listener.exe was launched at the time of run // This is used to find out the source from where the Runner.Listener.exe was launched at the time of run
public string GetStartupType() public string GetStartupType()
{ {
return GetArg(Constants.Runner.CommandLine.Args.StartupType); return GetArg(Constants.Runner.CommandLine.Args.StartupType);
} }
public string GetProxyUrl()
{
return GetArg(Constants.Runner.CommandLine.Args.ProxyUrl);
}
public string GetProxyUserName()
{
return GetArg(Constants.Runner.CommandLine.Args.ProxyUserName);
}
public string GetProxyPassword()
{
return GetArg(Constants.Runner.CommandLine.Args.ProxyPassword);
}
public bool GetSkipCertificateValidation() public bool GetSkipCertificateValidation()
{ {
return TestFlag(Constants.Runner.CommandLine.Flags.SslSkipCertValidation); return TestFlag(Constants.Runner.CommandLine.Flags.SslSkipCertValidation);

View File

@@ -79,31 +79,12 @@ namespace GitHub.Runner.Listener.Configuration
_term.WriteLine("| |", ConsoleColor.White); _term.WriteLine("| |", ConsoleColor.White);
_term.WriteLine("--------------------------------------------------------------------------------", ConsoleColor.White); _term.WriteLine("--------------------------------------------------------------------------------", ConsoleColor.White);
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
Trace.Info(nameof(ConfigureAsync)); Trace.Info(nameof(ConfigureAsync));
if (IsConfigured()) if (IsConfigured())
{ {
throw new InvalidOperationException("Cannot configure the runner because it is already configured. To reconfigure the runner, run 'config.cmd remove' or './config.sh remove' first."); throw new InvalidOperationException("Cannot configure the runner because it is already configured. To reconfigure the runner, run 'config.cmd remove' or './config.sh remove' first.");
} }
// Populate proxy setting from commandline args
var runnerProxy = HostContext.GetService<IRunnerWebProxy>();
bool saveProxySetting = false;
string proxyUrl = command.GetProxyUrl();
if (!string.IsNullOrEmpty(proxyUrl))
{
if (!Uri.IsWellFormedUriString(proxyUrl, UriKind.Absolute))
{
throw new ArgumentOutOfRangeException(nameof(proxyUrl));
}
Trace.Info("Reset proxy base on commandline args.");
string proxyUserName = command.GetProxyUserName();
string proxyPassword = command.GetProxyPassword();
(runnerProxy as RunnerWebProxy).SetupProxy(proxyUrl, proxyUserName, proxyPassword);
saveProxySetting = true;
}
// Populate cert setting from commandline args // Populate cert setting from commandline args
var runnerCertManager = HostContext.GetService<IRunnerCertificateManager>(); var runnerCertManager = HostContext.GetService<IRunnerCertificateManager>();
bool saveCertSetting = false; bool saveCertSetting = false;
@@ -232,7 +213,7 @@ namespace GitHub.Runner.Listener.Configuration
TaskAgent agent; TaskAgent agent;
while (true) while (true)
{ {
runnerSettings.AgentName = command.GetAgentName(); runnerSettings.AgentName = command.GetRunnerName();
_term.WriteLine(); _term.WriteLine();
@@ -367,21 +348,10 @@ namespace GitHub.Runner.Listener.Configuration
// We will Combine() what's stored with root. Defaults to string a relative path // We will Combine() what's stored with root. Defaults to string a relative path
runnerSettings.WorkFolder = command.GetWork(); runnerSettings.WorkFolder = command.GetWork();
// notificationPipeName for Hosted agent provisioner.
runnerSettings.NotificationPipeName = command.GetNotificationPipeName();
runnerSettings.MonitorSocketAddress = command.GetMonitorSocketAddress(); runnerSettings.MonitorSocketAddress = command.GetMonitorSocketAddress();
runnerSettings.NotificationSocketAddress = command.GetNotificationSocketAddress();
_store.SaveSettings(runnerSettings); _store.SaveSettings(runnerSettings);
if (saveProxySetting)
{
Trace.Info("Save proxy setting to disk.");
(runnerProxy as RunnerWebProxy).SaveProxySetting();
}
if (saveCertSetting) if (saveCertSetting)
{ {
Trace.Info("Save agent cert setting to disk."); Trace.Info("Save agent cert setting to disk.");
@@ -426,7 +396,6 @@ namespace GitHub.Runner.Listener.Configuration
public async Task UnconfigureAsync(CommandSettings command) public async Task UnconfigureAsync(CommandSettings command)
{ {
ArgUtil.Equal(RunMode.Normal, HostContext.RunMode, nameof(HostContext.RunMode));
string currentAction = string.Empty; string currentAction = string.Empty;
_term.WriteSection("Runner removal"); _term.WriteSection("Runner removal");
@@ -520,8 +489,6 @@ namespace GitHub.Runner.Listener.Configuration
currentAction = "Removing .runner"; currentAction = "Removing .runner";
if (isConfigured) if (isConfigured)
{ {
// delete proxy setting
(HostContext.GetService<IRunnerWebProxy>() as RunnerWebProxy).DeleteProxySetting();
// delete agent cert setting // delete agent cert setting
(HostContext.GetService<IRunnerCertificateManager>() as RunnerCertificateManager).DeleteCertificateSetting(); (HostContext.GetService<IRunnerCertificateManager>() as RunnerCertificateManager).DeleteCertificateSetting();
@@ -551,7 +518,7 @@ namespace GitHub.Runner.Listener.Configuration
Trace.Info(nameof(GetCredentialProvider)); Trace.Info(nameof(GetCredentialProvider));
var credentialManager = HostContext.GetService<ICredentialManager>(); var credentialManager = HostContext.GetService<ICredentialManager>();
string authType = command.GetAuth(defaultValue: Constants.Configuration.AAD); string authType = command.GetAuth(defaultValue: Constants.Configuration.OAuthAccessToken);
// Create the credential. // Create the credential.
Trace.Info("Creating credential for auth: {0}", authType); Trace.Info("Creating credential for auth: {0}", authType);
@@ -574,7 +541,7 @@ namespace GitHub.Runner.Listener.Configuration
PublicKey = new TaskAgentPublicKey(publicKey.Exponent, publicKey.Modulus), PublicKey = new TaskAgentPublicKey(publicKey.Exponent, publicKey.Modulus),
}; };
// update - update instead of delete so we don't lose user capabilities etc... // update - update instead of delete so we don't lose labels etc...
agent.Version = BuildConstants.RunnerPackage.Version; agent.Version = BuildConstants.RunnerPackage.Version;
agent.OSDescription = RuntimeInformation.OSDescription; agent.OSDescription = RuntimeInformation.OSDescription;

View File

@@ -20,8 +20,6 @@ namespace GitHub.Runner.Listener.Configuration
{ {
public static readonly Dictionary<string, Type> CredentialTypes = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase) public static readonly Dictionary<string, Type> CredentialTypes = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
{ {
{ Constants.Configuration.AAD, typeof(AadDeviceCodeAccessToken)},
{ Constants.Configuration.PAT, typeof(PersonalAccessToken)},
{ Constants.Configuration.OAuth, typeof(OAuthCredential)}, { Constants.Configuration.OAuth, typeof(OAuthCredential)},
{ Constants.Configuration.OAuthAccessToken, typeof(OAuthAccessTokenCredential)}, { Constants.Configuration.OAuthAccessToken, typeof(OAuthAccessTokenCredential)},
}; };

View File

@@ -1,13 +1,5 @@
using System; using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using GitHub.Runner.Common.Util;
using GitHub.Services.Client;
using GitHub.Services.Common; using GitHub.Services.Common;
using GitHub.Services.WebApi;
using GitHub.Runner.Common; using GitHub.Runner.Common;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using GitHub.Services.OAuth; using GitHub.Services.OAuth;
@@ -37,125 +29,6 @@ namespace GitHub.Runner.Listener.Configuration
public abstract void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl); public abstract void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl);
} }
public sealed class AadDeviceCodeAccessToken : CredentialProvider
{
private string _azureDevOpsClientId = "97877f11-0fc6-4aee-b1ff-febb0519dd00";
public override Boolean RequireInteractive => true;
public AadDeviceCodeAccessToken() : base(Constants.Configuration.AAD) { }
public override VssCredentials GetVssCredentials(IHostContext context)
{
ArgUtil.NotNull(context, nameof(context));
Tracing trace = context.GetTrace(nameof(AadDeviceCodeAccessToken));
trace.Info(nameof(GetVssCredentials));
ArgUtil.NotNull(CredentialData, nameof(CredentialData));
CredentialData.Data.TryGetValue(Constants.Runner.CommandLine.Args.Url, out string serverUrl);
ArgUtil.NotNullOrEmpty(serverUrl, nameof(serverUrl));
var tenantAuthorityUrl = GetTenantAuthorityUrl(context, serverUrl);
if (tenantAuthorityUrl == null)
{
throw new NotSupportedException($"'{serverUrl}' is not backed by Azure Active Directory.");
}
LoggerCallbackHandler.LogCallback = ((LogLevel level, string message, bool containsPii) =>
{
switch (level)
{
case LogLevel.Information:
trace.Info(message);
break;
case LogLevel.Error:
trace.Error(message);
break;
case LogLevel.Warning:
trace.Warning(message);
break;
default:
trace.Verbose(message);
break;
}
});
LoggerCallbackHandler.UseDefaultLogging = false;
AuthenticationContext ctx = new AuthenticationContext(tenantAuthorityUrl.AbsoluteUri);
var queryParameters = $"redirect_uri={Uri.EscapeDataString(new Uri(serverUrl).GetLeftPart(UriPartial.Authority))}";
DeviceCodeResult codeResult = ctx.AcquireDeviceCodeAsync("https://management.core.windows.net/", _azureDevOpsClientId, queryParameters).GetAwaiter().GetResult();
var term = context.GetService<ITerminal>();
term.WriteLine($"Please finish AAD device code flow in browser ({codeResult.VerificationUrl}), user code: {codeResult.UserCode}");
if (string.Equals(CredentialData.Data[Constants.Runner.CommandLine.Flags.LaunchBrowser], bool.TrueString, StringComparison.OrdinalIgnoreCase))
{
try
{
#if OS_WINDOWS
Process.Start(new ProcessStartInfo() { FileName = codeResult.VerificationUrl, UseShellExecute = true });
#elif OS_LINUX
Process.Start(new ProcessStartInfo() { FileName = "xdg-open", Arguments = codeResult.VerificationUrl });
#else
Process.Start(new ProcessStartInfo() { FileName = "open", Arguments = codeResult.VerificationUrl });
#endif
}
catch (Exception ex)
{
// not able to open browser, ex: xdg-open/open is not installed.
trace.Error(ex);
term.WriteLine($"Fail to open browser. {codeResult.Message}");
}
}
AuthenticationResult authResult = ctx.AcquireTokenByDeviceCodeAsync(codeResult).GetAwaiter().GetResult();
ArgUtil.NotNull(authResult, nameof(authResult));
trace.Info($"receive AAD auth result with {authResult.AccessTokenType} token");
var aadCred = new VssAadCredential(new VssAadToken(authResult));
VssCredentials creds = new VssCredentials(null, aadCred, CredentialPromptType.DoNotPrompt);
trace.Info("cred created");
return creds;
}
public override void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl)
{
ArgUtil.NotNull(context, nameof(context));
Tracing trace = context.GetTrace(nameof(AadDeviceCodeAccessToken));
trace.Info(nameof(EnsureCredential));
ArgUtil.NotNull(command, nameof(command));
CredentialData.Data[Constants.Runner.CommandLine.Args.Url] = serverUrl;
CredentialData.Data[Constants.Runner.CommandLine.Flags.LaunchBrowser] = command.GetAutoLaunchBrowser().ToString();
}
private Uri GetTenantAuthorityUrl(IHostContext context, string serverUrl)
{
using (var client = new HttpClient(context.CreateHttpClientHandler()))
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("X-TFS-FedAuthRedirect", "Suppress");
client.DefaultRequestHeaders.UserAgent.Clear();
client.DefaultRequestHeaders.UserAgent.AddRange(VssClientHttpRequestSettings.Default.UserAgent);
var requestMessage = new HttpRequestMessage(HttpMethod.Head, $"{serverUrl.Trim('/')}/_apis/connectiondata");
var response = client.SendAsync(requestMessage).GetAwaiter().GetResult();
// Get the tenant from the Login URL, MSA backed accounts will not return `Bearer` www-authenticate header.
var bearerResult = response.Headers.WwwAuthenticate.Where(p => p.Scheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (bearerResult != null && bearerResult.Parameter.StartsWith("authorization_uri=", StringComparison.OrdinalIgnoreCase))
{
var authorizationUri = bearerResult.Parameter.Substring("authorization_uri=".Length);
if (Uri.TryCreate(authorizationUri, UriKind.Absolute, out Uri aadTenantUrl))
{
return aadTenantUrl;
}
}
return null;
}
}
}
public sealed class OAuthAccessTokenCredential : CredentialProvider public sealed class OAuthAccessTokenCredential : CredentialProvider
{ {
public OAuthAccessTokenCredential() : base(Constants.Configuration.OAuthAccessToken) { } public OAuthAccessTokenCredential() : base(Constants.Configuration.OAuthAccessToken) { }
@@ -190,42 +63,4 @@ namespace GitHub.Runner.Listener.Configuration
CredentialData.Data[Constants.Runner.CommandLine.Args.Token] = command.GetToken(); CredentialData.Data[Constants.Runner.CommandLine.Args.Token] = command.GetToken();
} }
} }
public sealed class PersonalAccessToken : CredentialProvider
{
public PersonalAccessToken() : base(Constants.Configuration.PAT) { }
public override VssCredentials GetVssCredentials(IHostContext context)
{
ArgUtil.NotNull(context, nameof(context));
Tracing trace = context.GetTrace(nameof(PersonalAccessToken));
trace.Info(nameof(GetVssCredentials));
ArgUtil.NotNull(CredentialData, nameof(CredentialData));
string token;
if (!CredentialData.Data.TryGetValue(Constants.Runner.CommandLine.Args.Token, out token))
{
token = null;
}
ArgUtil.NotNullOrEmpty(token, nameof(token));
trace.Info("token retrieved: {0} chars", token.Length);
// PAT uses a basic credential
VssBasicCredential basicCred = new VssBasicCredential("ActionsRunner", token);
VssCredentials creds = new VssCredentials(null, basicCred, CredentialPromptType.DoNotPrompt);
trace.Info("cred created");
return creds;
}
public override void EnsureCredential(IHostContext context, CommandSettings command, string serverUrl)
{
ArgUtil.NotNull(context, nameof(context));
Tracing trace = context.GetTrace(nameof(PersonalAccessToken));
trace.Info(nameof(EnsureCredential));
ArgUtil.NotNull(command, nameof(command));
CredentialData.Data[Constants.Runner.CommandLine.Args.Token] = command.GetToken();
}
}
} }

View File

@@ -22,7 +22,6 @@ namespace GitHub.Runner.Listener
void Run(Pipelines.AgentJobRequestMessage message, bool runOnce = false); void Run(Pipelines.AgentJobRequestMessage message, bool runOnce = false);
bool Cancel(JobCancelMessage message); bool Cancel(JobCancelMessage message);
Task WaitAsync(CancellationToken token); Task WaitAsync(CancellationToken token);
TaskResult GetLocalRunJobResult(AgentJobRequestMessage message);
Task ShutdownAsync(); Task ShutdownAsync();
} }
@@ -165,11 +164,6 @@ namespace GitHub.Runner.Listener
} }
} }
public TaskResult GetLocalRunJobResult(AgentJobRequestMessage message)
{
return _localRunJobResult.Value[message.RequestId];
}
public async Task ShutdownAsync() public async Task ShutdownAsync()
{ {
Trace.Info($"Shutting down JobDispatcher. Make sure all WorkerDispatcher has finished."); Trace.Info($"Shutting down JobDispatcher. Make sure all WorkerDispatcher has finished.");
@@ -373,37 +367,29 @@ namespace GitHub.Runner.Listener
ArgUtil.NotNullOrEmpty(pipeHandleOut, nameof(pipeHandleOut)); ArgUtil.NotNullOrEmpty(pipeHandleOut, nameof(pipeHandleOut));
ArgUtil.NotNullOrEmpty(pipeHandleIn, nameof(pipeHandleIn)); ArgUtil.NotNullOrEmpty(pipeHandleIn, nameof(pipeHandleIn));
if (HostContext.RunMode == RunMode.Normal) // Save STDOUT from worker, worker will use STDOUT report unhandle exception.
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
{ {
// Save STDOUT from worker, worker will use STDOUT report unhandle exception. if (!string.IsNullOrEmpty(stdout.Data))
processInvoker.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
{ {
if (!string.IsNullOrEmpty(stdout.Data)) lock (_outputLock)
{ {
lock (_outputLock) workerOutput.Add(stdout.Data);
{
workerOutput.Add(stdout.Data);
}
} }
}; }
};
// Save STDERR from worker, worker will use STDERR on crash. // Save STDERR from worker, worker will use STDERR on crash.
processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr) processInvoker.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
{
if (!string.IsNullOrEmpty(stderr.Data))
{
lock (_outputLock)
{
workerOutput.Add(stderr.Data);
}
}
};
}
else if (HostContext.RunMode == RunMode.Local)
{ {
processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs e) => Console.WriteLine(e.Data); if (!string.IsNullOrEmpty(stderr.Data))
processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs e) => Console.WriteLine(e.Data); {
} lock (_outputLock)
{
workerOutput.Add(stderr.Data);
}
}
};
// Start the child process. // Start the child process.
HostContext.WritePerfCounter("StartingWorkerProcess"); HostContext.WritePerfCounter("StartingWorkerProcess");
@@ -468,7 +454,7 @@ namespace GitHub.Runner.Listener
// send notification to machine provisioner. // send notification to machine provisioner.
var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase)); var systemConnection = message.Resources.Endpoints.SingleOrDefault(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"]; var accessToken = systemConnection?.Authorization?.Parameters["AccessToken"];
await notification.JobStarted(message.JobId, accessToken, systemConnection.Url); notification.JobStarted(message.JobId, accessToken, systemConnection.Url);
HostContext.WritePerfCounter($"SentJobToWorker_{requestId.ToString()}"); HostContext.WritePerfCounter($"SentJobToWorker_{requestId.ToString()}");
@@ -730,11 +716,6 @@ namespace GitHub.Runner.Listener
private async Task CompleteJobRequestAsync(int poolId, Pipelines.AgentJobRequestMessage message, Guid lockToken, TaskResult result, string detailInfo = null) private async Task CompleteJobRequestAsync(int poolId, Pipelines.AgentJobRequestMessage message, Guid lockToken, TaskResult result, string detailInfo = null)
{ {
Trace.Entering(); Trace.Entering();
if (HostContext.RunMode == RunMode.Local)
{
_localRunJobResult.Value[message.RequestId] = result;
return;
}
if (PlanUtil.GetFeatures(message.Plan).HasFlag(PlanFeatures.JobCompletedPlanEvent)) if (PlanUtil.GetFeatures(message.Plan).HasFlag(PlanFeatures.JobCompletedPlanEvent))
{ {
@@ -787,8 +768,10 @@ namespace GitHub.Runner.Listener
var jobServer = HostContext.GetService<IJobServer>(); var jobServer = HostContext.GetService<IJobServer>();
VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection); VssCredentials jobServerCredential = VssUtil.GetVssCredential(systemConnection);
Uri jobServerUrl = systemConnection.Url; VssConnection jobConnection = VssUtil.CreateConnection(systemConnection.Url, jobServerCredential);
/* Below is the legacy 'OnPremises' code that is currently unused by the runner
ToDo: re-implement code as appropriate once GHES support is added.
// Make sure SystemConnection Url match Config Url base for OnPremises server // Make sure SystemConnection Url match Config Url base for OnPremises server
if (!message.Variables.ContainsKey(Constants.Variables.System.ServerType) || if (!message.Variables.ContainsKey(Constants.Variables.System.ServerType) ||
string.Equals(message.Variables[Constants.Variables.System.ServerType]?.Value, "OnPremises", StringComparison.OrdinalIgnoreCase)) string.Equals(message.Variables[Constants.Variables.System.ServerType]?.Value, "OnPremises", StringComparison.OrdinalIgnoreCase))
@@ -814,11 +797,12 @@ namespace GitHub.Runner.Listener
//cannot parse the Uri - not a fatal error //cannot parse the Uri - not a fatal error
Trace.Error(ex); Trace.Error(ex);
} }
} } */
VssConnection jobConnection = VssUtil.CreateConnection(jobServerUrl, jobServerCredential);
await jobServer.ConnectAsync(jobConnection); await jobServer.ConnectAsync(jobConnection);
var timeline = await jobServer.GetTimelineAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, CancellationToken.None); var timeline = await jobServer.GetTimelineAsync(message.Plan.ScopeIdentifier, message.Plan.PlanType, message.Plan.PlanId, message.Timeline.Id, CancellationToken.None);
ArgUtil.NotNull(timeline, nameof(timeline)); ArgUtil.NotNull(timeline, nameof(timeline));
TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job"); TimelineRecord jobRecord = timeline.Records.FirstOrDefault(x => x.Id == message.JobId && x.RecordType == "Job");
ArgUtil.NotNull(jobRecord, nameof(jobRecord)); ArgUtil.NotNull(jobRecord, nameof(jobRecord));

View File

@@ -24,7 +24,6 @@
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.4.0" /> <PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.4.0" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" /> <PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" />
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.4.0" /> <PackageReference Include="System.ServiceProcess.ServiceController" Version="4.4.0" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="3.19.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@@ -37,9 +37,8 @@ namespace GitHub.Runner.Listener
{ {
try try
{ {
var runnerWebProxy = HostContext.GetService<IRunnerWebProxy>();
var runnerCertManager = HostContext.GetService<IRunnerCertificateManager>(); var runnerCertManager = HostContext.GetService<IRunnerCertificateManager>();
VssUtil.InitializeVssClientSettings(HostContext.UserAgent, runnerWebProxy.WebProxy, runnerCertManager.VssClientCertificateManager); VssUtil.InitializeVssClientSettings(HostContext.UserAgent, HostContext.WebProxy, runnerCertManager.VssClientCertificateManager);
_inConfigStage = true; _inConfigStage = true;
_completedCommand.Reset(); _completedCommand.Reset();
@@ -191,25 +190,6 @@ namespace GitHub.Runner.Listener
} }
} }
#if !OS_WINDOWS
// Fix the work folder setting on Linux
if (settings.WorkFolder.Contains("vsts", StringComparison.OrdinalIgnoreCase))
{
var workFolder = "/runner/work";
var unix = HostContext.GetService<IUnixUtil>();
// create new work folder /runner/work
await unix.ExecAsync(HostContext.GetDirectory(WellKnownDirectory.Root), "sh", $"-c \"sudo mkdir -p {workFolder}\"");
// fix permission
await unix.ExecAsync(HostContext.GetDirectory(WellKnownDirectory.Root), "sh", $"-c \"sudo chown -R $USER {workFolder}\"");
// update settings
settings.WorkFolder = workFolder;
store.SaveSettings(settings);
}
#endif
Trace.Info($"Set runner startup type - {startType}"); Trace.Info($"Set runner startup type - {startType}");
HostContext.StartupType = startType; HostContext.StartupType = startType;
@@ -293,14 +273,8 @@ namespace GitHub.Runner.Listener
try try
{ {
var notification = HostContext.GetService<IJobNotification>(); var notification = HostContext.GetService<IJobNotification>();
if (!String.IsNullOrEmpty(settings.NotificationSocketAddress))
{ notification.StartClient(settings.MonitorSocketAddress);
notification.StartClient(settings.NotificationSocketAddress, settings.MonitorSocketAddress);
}
else
{
notification.StartClient(settings.NotificationPipeName, settings.MonitorSocketAddress, HostContext.RunnerShutdownToken);
}
bool autoUpdateInProgress = false; bool autoUpdateInProgress = false;
Task<bool> selfUpdateTask = null; Task<bool> selfUpdateTask = null;

View File

@@ -79,8 +79,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
{ {
// Validate args. // Validate args.
ArgUtil.NotNull(executionContext, nameof(executionContext)); ArgUtil.NotNull(executionContext, nameof(executionContext));
Uri proxyUrlWithCred = null;
string proxyUrlWithCredString = null;
bool useSelfSignedCACert = false; bool useSelfSignedCACert = false;
bool useClientCert = false; bool useClientCert = false;
string clientCertPrivateKeyAskPassFile = null; string clientCertPrivateKeyAskPassFile = null;
@@ -164,25 +162,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
// 3. git version greater than 2.14.2 if use SChannel for SSL backend (Windows only) // 3. git version greater than 2.14.2 if use SChannel for SSL backend (Windows only)
RequirementCheck(executionContext, gitCommandManager, gitLfsSupport); RequirementCheck(executionContext, gitCommandManager, gitLfsSupport);
// prepare credentail embedded urls
var runnerProxy = executionContext.GetProxyConfiguration();
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
{
proxyUrlWithCred = UrlUtil.GetCredentialEmbeddedUrl(new Uri(runnerProxy.ProxyAddress), runnerProxy.ProxyUsername, runnerProxy.ProxyPassword);
// uri.absoluteuri will not contains port info if the scheme is http/https and the port is 80/443
// however, git.exe always require you provide port info, if nothing passed in, it will use 1080 as default
// as result, we need prefer the uri.originalstring when it's different than uri.absoluteuri.
if (string.Equals(proxyUrlWithCred.AbsoluteUri, proxyUrlWithCred.OriginalString, StringComparison.OrdinalIgnoreCase))
{
proxyUrlWithCredString = proxyUrlWithCred.AbsoluteUri;
}
else
{
proxyUrlWithCredString = proxyUrlWithCred.OriginalString;
}
}
// prepare askpass for client cert private key, if the repository's endpoint url match the runner config url // prepare askpass for client cert private key, if the repository's endpoint url match the runner config url
var systemConnection = executionContext.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase)); var systemConnection = executionContext.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
if (runnerCert != null && Uri.Compare(repositoryUrl, systemConnection.Url, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0) if (runnerCert != null && Uri.Compare(repositoryUrl, systemConnection.Url, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0)
@@ -373,13 +352,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.{repositoryUrl.AbsoluteUri}.extraheader", string.Empty); await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.{repositoryUrl.AbsoluteUri}.extraheader", string.Empty);
} }
// always remove any possible left proxy setting from git config, the proxy setting may contains credential
if (await gitCommandManager.GitConfigExist(executionContext, targetPath, $"http.proxy"))
{
executionContext.Debug("Remove any proxy setting from git config.");
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.proxy", string.Empty);
}
List<string> additionalFetchArgs = new List<string>(); List<string> additionalFetchArgs = new List<string>();
List<string> additionalLfsFetchArgs = new List<string>(); List<string> additionalLfsFetchArgs = new List<string>();
@@ -389,15 +361,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
additionalFetchArgs.Add($"-c http.extraheader=\"AUTHORIZATION: {GenerateBasicAuthHeader(executionContext, accessToken)}\""); additionalFetchArgs.Add($"-c http.extraheader=\"AUTHORIZATION: {GenerateBasicAuthHeader(executionContext, accessToken)}\"");
} }
// Prepare proxy config for fetch.
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
{
executionContext.Debug($"Config proxy server '{runnerProxy.ProxyAddress}' for git fetch.");
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
additionalFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
additionalLfsFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
}
// Prepare ignore ssl cert error config for fetch. // Prepare ignore ssl cert error config for fetch.
if (acceptUntrustedCerts) if (acceptUntrustedCerts)
{ {
@@ -539,14 +502,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
additionalSubmoduleUpdateArgs.Add($"-c http.{authorityUrl}.extraheader=\"AUTHORIZATION: {GenerateBasicAuthHeader(executionContext, accessToken)}\""); additionalSubmoduleUpdateArgs.Add($"-c http.{authorityUrl}.extraheader=\"AUTHORIZATION: {GenerateBasicAuthHeader(executionContext, accessToken)}\"");
} }
// Prepare proxy config for submodule update.
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
{
executionContext.Debug($"Config proxy server '{runnerProxy.ProxyAddress}' for git submodule update.");
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
additionalSubmoduleUpdateArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
}
// Prepare ignore ssl cert error config for fetch. // Prepare ignore ssl cert error config for fetch.
if (acceptUntrustedCerts) if (acceptUntrustedCerts)
{ {
@@ -637,7 +592,7 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
int exitCode_configUnset = await gitCommandManager.GitConfigUnset(executionContext, targetPath, configKey); int exitCode_configUnset = await gitCommandManager.GitConfigUnset(executionContext, targetPath, configKey);
if (exitCode_configUnset != 0) if (exitCode_configUnset != 0)
{ {
// if unable to use git.exe unset http.extraheader, http.proxy or core.askpass, modify git config file on disk. make sure we don't left credential. // if unable to use git.exe unset http.extraheader or core.askpass, modify git config file on disk. make sure we don't left credential.
if (!string.IsNullOrEmpty(configValue)) if (!string.IsNullOrEmpty(configValue))
{ {
executionContext.Warning("An unsuccessful attempt was made using git command line to remove \"http.extraheader\" from the git config. Attempting to modify the git config file directly to remove the credential."); executionContext.Warning("An unsuccessful attempt was made using git command line to remove \"http.extraheader\" from the git config. Attempting to modify the git config file directly to remove the credential.");
@@ -650,9 +605,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_0
string setting = $"extraheader = {configValue}"; string setting = $"extraheader = {configValue}";
gitConfigContent = Regex.Replace(gitConfigContent, setting, string.Empty, RegexOptions.IgnoreCase); gitConfigContent = Regex.Replace(gitConfigContent, setting, string.Empty, RegexOptions.IgnoreCase);
setting = $"proxy = {configValue}";
gitConfigContent = Regex.Replace(gitConfigContent, setting, string.Empty, RegexOptions.IgnoreCase);
setting = $"askpass = {configValue}"; setting = $"askpass = {configValue}";
gitConfigContent = Regex.Replace(gitConfigContent, setting, string.Empty, RegexOptions.IgnoreCase); gitConfigContent = Regex.Replace(gitConfigContent, setting, string.Empty, RegexOptions.IgnoreCase);

View File

@@ -65,8 +65,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
// Validate args. // Validate args.
ArgUtil.NotNull(executionContext, nameof(executionContext)); ArgUtil.NotNull(executionContext, nameof(executionContext));
Dictionary<string, string> configModifications = new Dictionary<string, string>(); Dictionary<string, string> configModifications = new Dictionary<string, string>();
Uri proxyUrlWithCred = null;
string proxyUrlWithCredString = null;
bool useSelfSignedCACert = false; bool useSelfSignedCACert = false;
bool useClientCert = false; bool useClientCert = false;
string clientCertPrivateKeyAskPassFile = null; string clientCertPrivateKeyAskPassFile = null;
@@ -153,18 +151,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
// 3. git version greater than 2.14.2 if use SChannel for SSL backend (Windows only) // 3. git version greater than 2.14.2 if use SChannel for SSL backend (Windows only)
RequirementCheck(executionContext, gitCommandManager, gitLfsSupport); RequirementCheck(executionContext, gitCommandManager, gitLfsSupport);
// prepare credentail embedded urls
var runnerProxy = executionContext.GetProxyConfiguration();
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
{
proxyUrlWithCred = UrlUtil.GetCredentialEmbeddedUrl(new Uri(runnerProxy.ProxyAddress), runnerProxy.ProxyUsername, runnerProxy.ProxyPassword);
// uri.absoluteuri will not contains port info if the scheme is http/https and the port is 80/443
// however, git.exe always require you provide port info, if nothing passed in, it will use 1080 as default
// as result, we need prefer the uri.originalstring over uri.absoluteuri.
proxyUrlWithCredString = proxyUrlWithCred.OriginalString;
}
// prepare askpass for client cert private key, if the repository's endpoint url match the runner config url // prepare askpass for client cert private key, if the repository's endpoint url match the runner config url
var systemConnection = executionContext.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase)); var systemConnection = executionContext.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
if (runnerCert != null && Uri.Compare(repositoryUrl, systemConnection.Url, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0) if (runnerCert != null && Uri.Compare(repositoryUrl, systemConnection.Url, UriComponents.SchemeAndServer, UriFormat.Unescaped, StringComparison.OrdinalIgnoreCase) == 0)
@@ -355,13 +341,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.{repositoryUrl.AbsoluteUri}.extraheader", string.Empty); await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.{repositoryUrl.AbsoluteUri}.extraheader", string.Empty);
} }
// always remove any possible left proxy setting from git config, the proxy setting may contains credential
if (await gitCommandManager.GitConfigExist(executionContext, targetPath, $"http.proxy"))
{
executionContext.Debug("Remove any proxy setting from git config.");
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.proxy", string.Empty);
}
List<string> additionalFetchArgs = new List<string>(); List<string> additionalFetchArgs = new List<string>();
List<string> additionalLfsFetchArgs = new List<string>(); List<string> additionalLfsFetchArgs = new List<string>();
@@ -376,15 +355,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
throw new InvalidOperationException($"Git config failed with exit code: {exitCode_config}"); throw new InvalidOperationException($"Git config failed with exit code: {exitCode_config}");
} }
// Prepare proxy config for fetch.
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
{
executionContext.Debug($"Config proxy server '{runnerProxy.ProxyAddress}' for git fetch.");
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
additionalFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
additionalLfsFetchArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
}
// Prepare ignore ssl cert error config for fetch. // Prepare ignore ssl cert error config for fetch.
if (acceptUntrustedCerts) if (acceptUntrustedCerts)
{ {
@@ -514,14 +484,6 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
List<string> additionalSubmoduleUpdateArgs = new List<string>(); List<string> additionalSubmoduleUpdateArgs = new List<string>();
// Prepare proxy config for submodule update.
if (runnerProxy != null && !string.IsNullOrEmpty(runnerProxy.ProxyAddress) && !runnerProxy.WebProxy.IsBypassed(repositoryUrl))
{
executionContext.Debug($"Config proxy server '{runnerProxy.ProxyAddress}' for git submodule update.");
ArgUtil.NotNullOrEmpty(proxyUrlWithCredString, nameof(proxyUrlWithCredString));
additionalSubmoduleUpdateArgs.Add($"-c http.proxy=\"{proxyUrlWithCredString}\"");
}
// Prepare ignore ssl cert error config for fetch. // Prepare ignore ssl cert error config for fetch.
if (acceptUntrustedCerts) if (acceptUntrustedCerts)
{ {
@@ -592,7 +554,7 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
GitCliManager gitCommandManager = new GitCliManager(); GitCliManager gitCommandManager = new GitCliManager();
await gitCommandManager.LoadGitExecutionInfo(executionContext); await gitCommandManager.LoadGitExecutionInfo(executionContext);
executionContext.Debug("Remove any extraheader and proxy setting from git config."); executionContext.Debug("Remove any extraheader setting from git config.");
var configKeys = JsonUtility.FromString<List<string>>(Environment.GetEnvironmentVariable("STATE_modifiedgitconfig")); var configKeys = JsonUtility.FromString<List<string>>(Environment.GetEnvironmentVariable("STATE_modifiedgitconfig"));
if (configKeys?.Count > 0) if (configKeys?.Count > 0)
{ {
@@ -677,7 +639,7 @@ namespace GitHub.Runner.Plugins.Repository.v1_1
int exitCode_configUnset = await gitCommandManager.GitConfigUnset(executionContext, targetPath, configKey); int exitCode_configUnset = await gitCommandManager.GitConfigUnset(executionContext, targetPath, configKey);
if (exitCode_configUnset != 0) if (exitCode_configUnset != 0)
{ {
// if unable to use git.exe unset http.extraheader, http.proxy or core.askpass, modify git config file on disk. make sure we don't left credential. // if unable to use git.exe unset http.extraheader or core.askpass, modify git config file on disk. make sure we don't left credential.
if (!string.IsNullOrEmpty(configValue)) if (!string.IsNullOrEmpty(configValue))
{ {
executionContext.Warning("An unsuccessful attempt was made using git command line to remove \"http.extraheader\" from the git config. Attempting to modify the git config file directly to remove the credential."); executionContext.Warning("An unsuccessful attempt was made using git command line to remove \"http.extraheader\" from the git config. Attempting to modify the git config file directly to remove the credential.");

View File

@@ -24,6 +24,7 @@ namespace GitHub.Runner.Sdk
{ {
private readonly string DebugEnvironmentalVariable = "ACTIONS_STEP_DEBUG"; private readonly string DebugEnvironmentalVariable = "ACTIONS_STEP_DEBUG";
private VssConnection _connection; private VssConnection _connection;
private RunnerWebProxy _webProxy;
private readonly object _stdoutLock = new object(); private readonly object _stdoutLock = new object();
private readonly ITraceWriter _trace; // for unit tests private readonly ITraceWriter _trace; // for unit tests
@@ -57,6 +58,19 @@ namespace GitHub.Runner.Sdk
} }
} }
[JsonIgnore]
public RunnerWebProxy WebProxy
{
get
{
if (_webProxy == null)
{
_webProxy = new RunnerWebProxy();
}
return _webProxy;
}
}
public VssConnection InitializeVssConnection() public VssConnection InitializeVssConnection()
{ {
var headerValues = new List<ProductInfoHeaderValue>(); var headerValues = new List<ProductInfoHeaderValue>();
@@ -84,15 +98,7 @@ namespace GitHub.Runner.Sdk
} }
} }
var proxySetting = GetProxyConfiguration(); VssHttpMessageHandler.DefaultWebProxy = this.WebProxy;
if (proxySetting != null)
{
if (!string.IsNullOrEmpty(proxySetting.ProxyAddress))
{
VssHttpMessageHandler.DefaultWebProxy = new RunnerWebProxyCore(proxySetting.ProxyAddress, proxySetting.ProxyUsername, proxySetting.ProxyPassword, proxySetting.ProxyBypassList);
}
}
ServiceEndpoint systemConnection = this.Endpoints.FirstOrDefault(e => string.Equals(e.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase)); ServiceEndpoint systemConnection = this.Endpoints.FirstOrDefault(e => string.Equals(e.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
ArgUtil.NotNull(systemConnection, nameof(systemConnection)); ArgUtil.NotNull(systemConnection, nameof(systemConnection));
ArgUtil.NotNull(systemConnection.Url, nameof(systemConnection.Url)); ArgUtil.NotNull(systemConnection.Url, nameof(systemConnection.Url));
@@ -255,29 +261,6 @@ namespace GitHub.Runner.Sdk
} }
} }
public RunnerWebProxySettings GetProxyConfiguration()
{
string proxyUrl = GetRunnerContext("ProxyUrl");
if (!string.IsNullOrEmpty(proxyUrl))
{
string proxyUsername = GetRunnerContext("ProxyUsername");
string proxyPassword = GetRunnerContext("ProxyPassword");
List<string> proxyBypassHosts = StringUtil.ConvertFromJson<List<string>>(GetRunnerContext("ProxyBypassList") ?? "[]");
return new RunnerWebProxySettings()
{
ProxyAddress = proxyUrl,
ProxyUsername = proxyUsername,
ProxyPassword = proxyPassword,
ProxyBypassList = proxyBypassHosts,
WebProxy = new RunnerWebProxyCore(proxyUrl, proxyUsername, proxyPassword, proxyBypassHosts)
};
}
else
{
return null;
}
}
private string Escape(string input) private string Escape(string input)
{ {
foreach (var mapping in _commandEscapeMappings) foreach (var mapping in _commandEscapeMappings)

View File

@@ -0,0 +1,224 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
namespace GitHub.Runner.Sdk
{
public struct ByPassInfo
{
public string Host { get; set; }
public string Port { get; set; }
};
public class RunnerWebProxy : IWebProxy
{
private string _httpProxyAddress;
private string _httpProxyUsername;
private string _httpProxyPassword;
private string _httpsProxyAddress;
private string _httpsProxyUsername;
private string _httpsProxyPassword;
private readonly List<ByPassInfo> _noProxyList = new List<ByPassInfo>();
private readonly HashSet<string> _noProxyUnique = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
private readonly Regex _validIpRegex = new Regex("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$", RegexOptions.Compiled);
public string HttpProxyAddress => _httpProxyAddress;
public string HttpProxyUsername => _httpProxyUsername;
public string HttpProxyPassword => _httpProxyPassword;
public string HttpsProxyAddress => _httpsProxyAddress;
public string HttpsProxyUsername => _httpsProxyUsername;
public string HttpsProxyPassword => _httpsProxyPassword;
public List<ByPassInfo> NoProxyList => _noProxyList;
public ICredentials Credentials { get; set; }
public RunnerWebProxy()
{
Credentials = new CredentialCache();
var httpProxyAddress = Environment.GetEnvironmentVariable("http_proxy");
if (string.IsNullOrEmpty(httpProxyAddress))
{
httpProxyAddress = Environment.GetEnvironmentVariable("HTTP_PROXY");
}
httpProxyAddress = httpProxyAddress?.Trim();
var httpsProxyAddress = Environment.GetEnvironmentVariable("https_proxy");
if (string.IsNullOrEmpty(httpsProxyAddress))
{
httpsProxyAddress = Environment.GetEnvironmentVariable("HTTPS_PROXY");
}
httpsProxyAddress = httpsProxyAddress?.Trim();
var noProxyList = Environment.GetEnvironmentVariable("no_proxy");
if (string.IsNullOrEmpty(noProxyList))
{
noProxyList = Environment.GetEnvironmentVariable("NO_PROXY");
}
if (string.IsNullOrEmpty(httpProxyAddress) && string.IsNullOrEmpty(httpsProxyAddress))
{
return;
}
if (!string.IsNullOrEmpty(httpProxyAddress) && Uri.TryCreate(httpProxyAddress, UriKind.Absolute, out var proxyHttpUri))
{
_httpProxyAddress = proxyHttpUri.AbsoluteUri;
// the proxy url looks like http://[user:pass@]127.0.0.1:8888
var userInfo = Uri.UnescapeDataString(proxyHttpUri.UserInfo).Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
if (userInfo.Length == 2)
{
_httpProxyUsername = userInfo[0];
_httpProxyPassword = userInfo[1];
}
else if (userInfo.Length == 1)
{
_httpProxyUsername = userInfo[0];
}
if (!string.IsNullOrEmpty(_httpProxyUsername) || !string.IsNullOrEmpty(_httpProxyPassword))
{
var credentials = new NetworkCredential(_httpProxyUsername, _httpProxyPassword);
// Replace the entry in the credential cache if it exists
(Credentials as CredentialCache).Remove(proxyHttpUri, "Basic");
(Credentials as CredentialCache).Add(proxyHttpUri, "Basic", credentials);
}
}
if (!string.IsNullOrEmpty(httpsProxyAddress) && Uri.TryCreate(httpsProxyAddress, UriKind.Absolute, out var proxyHttpsUri))
{
_httpsProxyAddress = proxyHttpsUri.AbsoluteUri;
// the proxy url looks like http://[user:pass@]127.0.0.1:8888
var userInfo = Uri.UnescapeDataString(proxyHttpsUri.UserInfo).Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
if (userInfo.Length == 2)
{
_httpsProxyUsername = userInfo[0];
_httpsProxyPassword = userInfo[1];
}
else if (userInfo.Length == 1)
{
_httpsProxyUsername = userInfo[0];
}
if (!string.IsNullOrEmpty(_httpsProxyUsername) || !string.IsNullOrEmpty(_httpsProxyPassword))
{
var credentials = new NetworkCredential(_httpsProxyUsername, _httpsProxyPassword);
// Replace the entry in the credential cache if it exists
(Credentials as CredentialCache).Remove(proxyHttpsUri, "Basic");
(Credentials as CredentialCache).Add(proxyHttpsUri, "Basic", credentials);
}
}
if (!string.IsNullOrEmpty(noProxyList))
{
var noProxyListSplit = noProxyList.Split(',', StringSplitOptions.RemoveEmptyEntries);
foreach (string noProxy in noProxyListSplit)
{
var noProxyTrim = noProxy.Trim();
if (string.IsNullOrEmpty(noProxyTrim))
{
continue;
}
else if (_noProxyUnique.Add(noProxyTrim))
{
var noProxyInfo = new ByPassInfo();
var noProxyHostPort = noProxyTrim.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
if (noProxyHostPort.Length == 1)
{
noProxyInfo.Host = noProxyHostPort[0];
}
else if (noProxyHostPort.Length == 2)
{
noProxyInfo.Host = noProxyHostPort[0];
noProxyInfo.Port = noProxyHostPort[1];
}
// We don't support IP address for no_proxy
if (_validIpRegex.IsMatch(noProxyInfo.Host))
{
continue;
}
_noProxyList.Add(noProxyInfo);
}
}
}
}
public Uri GetProxy(Uri destination)
{
if (IsBypassed(destination))
{
return null;
}
if (destination.Scheme == Uri.UriSchemeHttps)
{
return new Uri(_httpsProxyAddress);
}
else
{
return new Uri(_httpProxyAddress);
}
}
public bool IsBypassed(Uri uri)
{
if (uri.Scheme == Uri.UriSchemeHttps && string.IsNullOrEmpty(_httpsProxyAddress))
{
return true;
}
if (uri.Scheme == Uri.UriSchemeHttp && string.IsNullOrEmpty(_httpProxyAddress))
{
return true;
}
return uri.IsLoopback || IsUriInBypassList(uri);
}
private bool IsUriInBypassList(Uri input)
{
foreach (var noProxy in _noProxyList)
{
var matchHost = false;
var matchPort = false;
if (string.IsNullOrEmpty(noProxy.Port))
{
matchPort = true;
}
else
{
matchPort = string.Equals(noProxy.Port, input.Port.ToString());
}
if (noProxy.Host.StartsWith('.'))
{
matchHost = input.Host.EndsWith(noProxy.Host, StringComparison.OrdinalIgnoreCase);
}
else
{
matchHost = string.Equals(input.Host, noProxy.Host, StringComparison.OrdinalIgnoreCase) || input.Host.EndsWith($".{noProxy.Host}", StringComparison.OrdinalIgnoreCase);
}
if (matchHost && matchPort)
{
return true;
}
}
return false;
}
}
}

View File

@@ -1,104 +0,0 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
namespace GitHub.Runner.Sdk
{
public class RunnerWebProxySettings
{
public string ProxyAddress { get; set; }
public string ProxyUsername { get; set; }
public string ProxyPassword { get; set; }
public List<string> ProxyBypassList { get; set; }
public IWebProxy WebProxy { get; set; }
}
public class RunnerWebProxyCore : IWebProxy
{
private string _proxyAddress;
private readonly List<Regex> _regExBypassList = new List<Regex>();
public ICredentials Credentials { get; set; }
public RunnerWebProxyCore()
{
}
public RunnerWebProxyCore(string proxyAddress, string proxyUsername, string proxyPassword, List<string> proxyBypassList)
{
Update(proxyAddress, proxyUsername, proxyPassword, proxyBypassList);
}
public void Update(string proxyAddress, string proxyUsername, string proxyPassword, List<string> proxyBypassList)
{
_proxyAddress = proxyAddress?.Trim();
if (string.IsNullOrEmpty(proxyUsername) || string.IsNullOrEmpty(proxyPassword))
{
Credentials = CredentialCache.DefaultNetworkCredentials;
}
else
{
Credentials = new NetworkCredential(proxyUsername, proxyPassword);
}
if (proxyBypassList != null)
{
foreach (string bypass in proxyBypassList)
{
if (string.IsNullOrWhiteSpace(bypass))
{
continue;
}
else
{
try
{
Regex bypassRegex = new Regex(bypass.Trim(), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.ECMAScript);
_regExBypassList.Add(bypassRegex);
}
catch (Exception)
{
// eat all exceptions
}
}
}
}
}
public Uri GetProxy(Uri destination)
{
if (IsBypassed(destination))
{
return destination;
}
else
{
return new Uri(_proxyAddress);
}
}
public bool IsBypassed(Uri uri)
{
return string.IsNullOrEmpty(_proxyAddress) || uri.IsLoopback || IsMatchInBypassList(uri);
}
private bool IsMatchInBypassList(Uri input)
{
string matchUriString = input.IsDefaultPort ?
input.Scheme + "://" + input.Host :
input.Scheme + "://" + input.Host + ":" + input.Port.ToString();
foreach (Regex r in _regExBypassList)
{
if (r.IsMatch(matchUriString))
{
return true;
}
}
return false;
}
}
}

View File

@@ -15,7 +15,7 @@ namespace GitHub.Runner.Worker
[ServiceLocator(Default = typeof(DiagnosticLogManager))] [ServiceLocator(Default = typeof(DiagnosticLogManager))]
public interface IDiagnosticLogManager : IRunnerService public interface IDiagnosticLogManager : IRunnerService
{ {
Task UploadDiagnosticLogsAsync(IExecutionContext executionContext, void UploadDiagnosticLogs(IExecutionContext executionContext,
IExecutionContext parentContext, IExecutionContext parentContext,
Pipelines.AgentJobRequestMessage message, Pipelines.AgentJobRequestMessage message,
DateTime jobStartTimeUtc); DateTime jobStartTimeUtc);
@@ -31,12 +31,10 @@ namespace GitHub.Runner.Worker
public sealed class DiagnosticLogManager : RunnerService, IDiagnosticLogManager public sealed class DiagnosticLogManager : RunnerService, IDiagnosticLogManager
{ {
private static string DateTimeFormat = "yyyyMMdd-HHmmss"; private static string DateTimeFormat = "yyyyMMdd-HHmmss";
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously (method has async logic on only certain platforms) public void UploadDiagnosticLogs(IExecutionContext executionContext,
public async Task UploadDiagnosticLogsAsync(IExecutionContext executionContext, IExecutionContext parentContext,
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously Pipelines.AgentJobRequestMessage message,
IExecutionContext parentContext, DateTime jobStartTimeUtc)
Pipelines.AgentJobRequestMessage message,
DateTime jobStartTimeUtc)
{ {
executionContext.Debug("Starting diagnostic file upload."); executionContext.Debug("Starting diagnostic file upload.");

View File

@@ -41,7 +41,6 @@ namespace GitHub.Runner.Worker
TaskResult? CommandResult { get; set; } TaskResult? CommandResult { get; set; }
CancellationToken CancellationToken { get; } CancellationToken CancellationToken { get; }
List<ServiceEndpoint> Endpoints { get; } List<ServiceEndpoint> Endpoints { get; }
List<SecureFile> SecureFiles { get; }
PlanFeatures Features { get; } PlanFeatures Features { get; }
Variables Variables { get; } Variables Variables { get; }
@@ -136,7 +135,6 @@ namespace GitHub.Runner.Worker
public Task ForceCompleted => _forceCompleted.Task; public Task ForceCompleted => _forceCompleted.Task;
public CancellationToken CancellationToken => _cancellationTokenSource.Token; public CancellationToken CancellationToken => _cancellationTokenSource.Token;
public List<ServiceEndpoint> Endpoints { get; private set; } public List<ServiceEndpoint> Endpoints { get; private set; }
public List<SecureFile> SecureFiles { get; private set; }
public Variables Variables { get; private set; } public Variables Variables { get; private set; }
public Dictionary<string, string> IntraActionState { get; private set; } public Dictionary<string, string> IntraActionState { get; private set; }
public HashSet<string> OutputVariables => _outputvariables; public HashSet<string> OutputVariables => _outputvariables;
@@ -257,7 +255,6 @@ namespace GitHub.Runner.Worker
child.Features = Features; child.Features = Features;
child.Variables = Variables; child.Variables = Variables;
child.Endpoints = Endpoints; child.Endpoints = Endpoints;
child.SecureFiles = SecureFiles;
if (intraActionState == null) if (intraActionState == null)
{ {
child.IntraActionState = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); child.IntraActionState = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -549,9 +546,6 @@ namespace GitHub.Runner.Worker
// Endpoints // Endpoints
Endpoints = message.Resources.Endpoints; Endpoints = message.Resources.Endpoints;
// SecureFiles
SecureFiles = message.Resources.SecureFiles;
// Variables // Variables
Variables = new Variables(HostContext, message.Variables); Variables = new Variables(HostContext, message.Variables);
@@ -616,29 +610,6 @@ namespace GitHub.Runner.Worker
// PostJobSteps for job ExecutionContext // PostJobSteps for job ExecutionContext
PostJobSteps = new Stack<IStep>(); PostJobSteps = new Stack<IStep>();
// Proxy variables
// var agentWebProxy = HostContext.GetService<IRunnerWebProxy>();
// if (!string.IsNullOrEmpty(agentWebProxy.ProxyAddress))
// {
// SetRunnerContext("proxyurl", agentWebProxy.ProxyAddress);
// if (!string.IsNullOrEmpty(agentWebProxy.ProxyUsername))
// {
// SetRunnerContext("proxyusername", agentWebProxy.ProxyUsername);
// }
// if (!string.IsNullOrEmpty(agentWebProxy.ProxyPassword))
// {
// HostContext.SecretMasker.AddValue(agentWebProxy.ProxyPassword);
// SetRunnerContext("proxypassword", agentWebProxy.ProxyPassword);
// }
// if (agentWebProxy.ProxyBypassList.Count > 0)
// {
// SetRunnerContext("proxybypasslist", JsonUtility.ToString(agentWebProxy.ProxyBypassList));
// }
// }
// // Certificate variables // // Certificate variables
// var agentCert = HostContext.GetService<IRunnerCertificateManager>(); // var agentCert = HostContext.GetService<IRunnerCertificateManager>();
// if (agentCert.SkipServerCertificateValidation) // if (agentCert.SkipServerCertificateValidation)
@@ -874,33 +845,6 @@ namespace GitHub.Runner.Worker
{ {
this.Warning(string.Format("The job is currently being throttled by the server. You may experience delays in console line output, job status reporting, and action log uploads.")); this.Warning(string.Format("The job is currently being throttled by the server. You may experience delays in console line output, job status reporting, and action log uploads."));
if (!String.IsNullOrEmpty(this.Variables.System_TFCollectionUrl))
{
// Construct a URL to the resource utilization page, to aid the user debug throttling issues
UriBuilder uriBuilder = new UriBuilder(Variables.System_TFCollectionUrl);
NameValueCollection query = HttpUtility.ParseQueryString(uriBuilder.Query);
DateTime endTime = DateTime.UtcNow;
string queryDate = endTime.AddHours(-1).ToString("s") + "," + endTime.ToString("s");
uriBuilder.Path += (Variables.System_TFCollectionUrl.EndsWith("/") ? "" : "/") + "_usersSettings/usage";
query["tab"] = "pipelines";
query["queryDate"] = queryDate;
// Global RU link
uriBuilder.Query = query.ToString();
string global = $"Link to resource utilization page (global 1-hour view): {uriBuilder.ToString()}.";
if (!String.IsNullOrEmpty(this.Variables.Build_DefinitionName))
{
query["keywords"] = this.Variables.Build_Number;
query["definition"] = this.Variables.Build_DefinitionName;
}
// RU link scoped for the build/release
uriBuilder.Query = query.ToString();
this.Warning($"{global}\nLink to resource utilization page (1-hour view by pipeline): {uriBuilder.ToString()}.");
}
_throttlingReported = true; _throttlingReported = true;
} }
} }

View File

@@ -18,44 +18,46 @@ namespace GitHub.Runner.Worker.Handlers
private static readonly Regex _colorCodeRegex = new Regex(@"\x0033\[[0-9;]*m?", RegexOptions.Compiled | RegexOptions.CultureInvariant); private static readonly Regex _colorCodeRegex = new Regex(@"\x0033\[[0-9;]*m?", RegexOptions.Compiled | RegexOptions.CultureInvariant);
private readonly IActionCommandManager _commandManager; private readonly IActionCommandManager _commandManager;
private readonly IExecutionContext _executionContext; private readonly IExecutionContext _executionContext;
private readonly int _failsafe = 50;
private readonly object _matchersLock = new object(); private readonly object _matchersLock = new object();
private readonly TimeSpan _timeout; private readonly TimeSpan _timeout;
private IssueMatcher[] _matchers = Array.Empty<IssueMatcher>(); private IssueMatcher[] _matchers = Array.Empty<IssueMatcher>();
// Mapping that indicates whether a directory belongs to the workflow repository
private readonly Dictionary<string, string> _directoryMap = new Dictionary<string, string>();
public OutputManager(IExecutionContext executionContext, IActionCommandManager commandManager) public OutputManager(IExecutionContext executionContext, IActionCommandManager commandManager)
{ {
//executionContext.Debug("ENTERING OutputManager ctor");
_executionContext = executionContext; _executionContext = executionContext;
_commandManager = commandManager; _commandManager = commandManager;
//_executionContext.Debug("OutputManager ctor - determine timeout from variable"); // Recursion failsafe (test override)
var failsafeString = Environment.GetEnvironmentVariable("RUNNER_TEST_GET_REPOSITORY_PATH_FAILSAFE");
if (!string.IsNullOrEmpty(failsafeString))
{
_failsafe = int.Parse(failsafeString, NumberStyles.None);
}
// Determine the timeout // Determine the timeout
var timeoutStr = _executionContext.Variables.Get(_timeoutKey); var timeoutStr = _executionContext.Variables.Get(_timeoutKey);
if (string.IsNullOrEmpty(timeoutStr) || if (string.IsNullOrEmpty(timeoutStr) ||
!TimeSpan.TryParse(timeoutStr, CultureInfo.InvariantCulture, out _timeout) || !TimeSpan.TryParse(timeoutStr, CultureInfo.InvariantCulture, out _timeout) ||
_timeout <= TimeSpan.Zero) _timeout <= TimeSpan.Zero)
{ {
//_executionContext.Debug("OutputManager ctor - determine timeout from env var");
timeoutStr = Environment.GetEnvironmentVariable(_timeoutKey); timeoutStr = Environment.GetEnvironmentVariable(_timeoutKey);
if (string.IsNullOrEmpty(timeoutStr) || if (string.IsNullOrEmpty(timeoutStr) ||
!TimeSpan.TryParse(timeoutStr, CultureInfo.InvariantCulture, out _timeout) || !TimeSpan.TryParse(timeoutStr, CultureInfo.InvariantCulture, out _timeout) ||
_timeout <= TimeSpan.Zero) _timeout <= TimeSpan.Zero)
{ {
//_executionContext.Debug("OutputManager ctor - set timeout to default");
_timeout = TimeSpan.FromSeconds(1); _timeout = TimeSpan.FromSeconds(1);
} }
} }
//_executionContext.Debug("OutputManager ctor - adding matchers");
// Lock // Lock
lock (_matchersLock) lock (_matchersLock)
{ {
//_executionContext.Debug("OutputManager ctor - adding OnMatcherChanged");
_executionContext.Add(OnMatcherChanged); _executionContext.Add(OnMatcherChanged);
//_executionContext.Debug("OutputManager ctor - getting matchers");
_matchers = _executionContext.GetMatchers().Select(x => new IssueMatcher(x, _timeout)).ToArray(); _matchers = _executionContext.GetMatchers().Select(x => new IssueMatcher(x, _timeout)).ToArray();
} }
//_executionContext.Debug("LEAVING OutputManager ctor");
} }
public void Dispose() public void Dispose()
@@ -71,7 +73,6 @@ namespace GitHub.Runner.Worker.Handlers
public void OnDataReceived(object sender, ProcessDataReceivedEventArgs e) public void OnDataReceived(object sender, ProcessDataReceivedEventArgs e)
{ {
//_executionContext.Debug("ENTERING OutputManager OnDataReceived");
var line = e.Data; var line = e.Data;
// ## commands // ## commands
@@ -82,7 +83,6 @@ namespace GitHub.Runner.Worker.Handlers
// The logging queues and command handlers are thread-safe. // The logging queues and command handlers are thread-safe.
if (_commandManager.TryProcessCommand(_executionContext, line)) if (_commandManager.TryProcessCommand(_executionContext, line))
{ {
//_executionContext.Debug("LEAVING OutputManager OnDataReceived - command processed");
return; return;
} }
} }
@@ -142,7 +142,6 @@ namespace GitHub.Runner.Worker.Handlers
// Log issue // Log issue
_executionContext.AddIssue(issue, stripped); _executionContext.AddIssue(issue, stripped);
//_executionContext.Debug("LEAVING OutputManager OnDataReceived - issue logged");
return; return;
} }
} }
@@ -151,7 +150,6 @@ namespace GitHub.Runner.Worker.Handlers
// Regular output // Regular output
_executionContext.Output(line); _executionContext.Output(line);
//_executionContext.Debug("LEAVING OutputManager OnDataReceived");
} }
private void OnMatcherChanged(object sender, MatcherChangedEventArgs e) private void OnMatcherChanged(object sender, MatcherChangedEventArgs e)
@@ -261,7 +259,7 @@ namespace GitHub.Runner.Worker.Handlers
var file = match.File; var file = match.File;
// Root using fromPath // Root using fromPath
if (!string.IsNullOrWhiteSpace(match.FromPath) && !Path.IsPathRooted(file)) if (!string.IsNullOrWhiteSpace(match.FromPath) && !Path.IsPathFullyQualified(file))
{ {
var fromDirectory = Path.GetDirectoryName(match.FromPath); var fromDirectory = Path.GetDirectoryName(match.FromPath);
if (!string.IsNullOrWhiteSpace(fromDirectory)) if (!string.IsNullOrWhiteSpace(fromDirectory))
@@ -271,7 +269,7 @@ namespace GitHub.Runner.Worker.Handlers
} }
// Root using workspace // Root using workspace
if (!Path.IsPathRooted(file)) if (!Path.IsPathFullyQualified(file))
{ {
var workspace = _executionContext.GetGitHubContext("workspace"); var workspace = _executionContext.GetGitHubContext("workspace");
ArgUtil.NotNullOrEmpty(workspace, "workspace"); ArgUtil.NotNullOrEmpty(workspace, "workspace");
@@ -279,31 +277,27 @@ namespace GitHub.Runner.Worker.Handlers
file = Path.Combine(workspace, file); file = Path.Combine(workspace, file);
} }
// Normalize slashes // Remove relative pathing and normalize slashes
file = file.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); file = Path.GetFullPath(file);
// File exists // Check whether the file exists
if (File.Exists(file)) if (File.Exists(file))
{ {
// Repository path // Check whether the file is under the workflow repository
var repositoryPath = _executionContext.GetGitHubContext("workspace"); var repositoryPath = GetRepositoryPath(file);
ArgUtil.NotNullOrEmpty(repositoryPath, nameof(repositoryPath)); if (!string.IsNullOrEmpty(repositoryPath))
// Normalize slashes
repositoryPath = repositoryPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
if (!file.StartsWith(repositoryPath, IOUtil.FilePathStringComparison))
{ {
// File is not under repo // Get the relative file path
_executionContext.Debug($"Dropping file value '{file}'. Path is not under the repo."); var relativePath = file.Substring(repositoryPath.Length).TrimStart(Path.DirectorySeparatorChar);
// Prefer `/` on all platforms
issue.Data["file"] = relativePath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
} }
else else
{ {
// Prefer `/` on all platforms _executionContext.Debug($"Dropping file value '{file}'. Path is not under the workflow repo.");
issue.Data["file"] = file.Substring(repositoryPath.Length).TrimStart(Path.DirectorySeparatorChar).Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
} }
} }
// File does not exist
else else
{ {
_executionContext.Debug($"Dropping file value '{file}'. Path does not exist"); _executionContext.Debug($"Dropping file value '{file}'. Path does not exist");
@@ -317,5 +311,60 @@ namespace GitHub.Runner.Worker.Handlers
return issue; return issue;
} }
private string GetRepositoryPath(string filePath, int recursion = 0)
{
// Prevent the cache from growing too much
if (_directoryMap.Count > 100)
{
_directoryMap.Clear();
}
// Empty directory means we hit the root of the drive
var directoryPath = Path.GetDirectoryName(filePath);
if (string.IsNullOrEmpty(directoryPath) || recursion > _failsafe)
{
return null;
}
// Check the cache
if (_directoryMap.TryGetValue(directoryPath, out string repositoryPath))
{
return repositoryPath;
}
try
{
// Check if .git/config exists
var gitConfigPath = Path.Combine(directoryPath, ".git", "config");
if (File.Exists(gitConfigPath))
{
// Check if the config contains the workflow repository url
var qualifiedRepository = _executionContext.GetGitHubContext("repository");
var configMatch = $"url = https://github.com/{qualifiedRepository}";
var content = File.ReadAllText(gitConfigPath);
foreach (var line in content.Split("\n").Select(x => x.Trim()))
{
if (String.Equals(line, configMatch, StringComparison.OrdinalIgnoreCase))
{
repositoryPath = directoryPath;
break;
}
}
}
else
{
// Recursive call
repositoryPath = GetRepositoryPath(directoryPath, recursion + 1);
}
}
catch (Exception ex)
{
_executionContext.Debug($"Error when attempting to determine whether the path '{filePath}' is under the workflow repository: {ex.Message}");
}
_directoryMap[directoryPath] = repositoryPath;
return repositoryPath;
}
} }
} }

View File

@@ -19,7 +19,7 @@ namespace GitHub.Runner.Worker
public interface IJobExtension : IRunnerService public interface IJobExtension : IRunnerService
{ {
Task<List<IStep>> InitializeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message); Task<List<IStep>> InitializeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message);
Task FinalizeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, DateTime jobStartTimeUtc); void FinalizeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, DateTime jobStartTimeUtc);
} }
public sealed class JobExtension : RunnerService, IJobExtension public sealed class JobExtension : RunnerService, IJobExtension
@@ -42,7 +42,6 @@ namespace GitHub.Runner.Worker
List<IStep> preJobSteps = new List<IStep>(); List<IStep> preJobSteps = new List<IStep>();
List<IStep> jobSteps = new List<IStep>(); List<IStep> jobSteps = new List<IStep>();
List<IStep> postJobSteps = new List<IStep>();
using (var register = jobContext.CancellationToken.Register(() => { context.CancelToken(); })) using (var register = jobContext.CancellationToken.Register(() => { context.CancelToken(); }))
{ {
try try
@@ -55,10 +54,13 @@ namespace GitHub.Runner.Worker
context.Debug($"Primary repository: {repoFullName}"); context.Debug($"Primary repository: {repoFullName}");
// Print proxy setting information for better diagnostic experience // Print proxy setting information for better diagnostic experience
var runnerWebProxy = HostContext.GetService<IRunnerWebProxy>(); if (!string.IsNullOrEmpty(HostContext.WebProxy.HttpProxyAddress))
if (!string.IsNullOrEmpty(runnerWebProxy.ProxyAddress))
{ {
context.Output($"Runner is running behind proxy server: '{runnerWebProxy.ProxyAddress}'"); context.Output($"Runner is running behind proxy server '{HostContext.WebProxy.HttpProxyAddress}' for all HTTP requests.");
}
if (!string.IsNullOrEmpty(HostContext.WebProxy.HttpsProxyAddress))
{
context.Output($"Runner is running behind proxy server '{HostContext.WebProxy.HttpsProxyAddress}' for all HTTPS requests.");
} }
// Prepare the workflow directory // Prepare the workflow directory
@@ -228,7 +230,7 @@ namespace GitHub.Runner.Worker
} }
} }
public async Task FinalizeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, DateTime jobStartTimeUtc) public void FinalizeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, DateTime jobStartTimeUtc)
{ {
Trace.Entering(); Trace.Entering();
ArgUtil.NotNull(jobContext, nameof(jobContext)); ArgUtil.NotNull(jobContext, nameof(jobContext));
@@ -242,19 +244,6 @@ namespace GitHub.Runner.Worker
context.Start(); context.Start();
context.Debug("Starting: Complete job"); context.Debug("Starting: Complete job");
// Wait for agent log plugin process exits
// var logPlugin = HostContext.GetService<IAgentLogPlugin>();
// try
// {
// await logPlugin.WaitAsync(context);
// }
// catch (Exception ex)
// {
// // Log and ignore the error from log plugin finalization.
// Trace.Error($"Caught exception from log plugin finalization: {ex}");
// context.Output(ex.Message);
// }
if (context.Variables.GetBoolean(Constants.Variables.Actions.RunnerDebug) ?? false) if (context.Variables.GetBoolean(Constants.Variables.Actions.RunnerDebug) ?? false)
{ {
Trace.Info("Support log upload starting."); Trace.Info("Support log upload starting.");
@@ -264,7 +253,7 @@ namespace GitHub.Runner.Worker
try try
{ {
await diagnosticLogManager.UploadDiagnosticLogsAsync(executionContext: context, parentContext: jobContext, message: message, jobStartTimeUtc: jobStartTimeUtc); diagnosticLogManager.UploadDiagnosticLogs(executionContext: context, parentContext: jobContext, message: message, jobStartTimeUtc: jobStartTimeUtc);
Trace.Info("Support log upload complete."); Trace.Info("Support log upload complete.");
context.Output("Completed runner diagnostic log upload"); context.Output("Completed runner diagnostic log upload");

View File

@@ -113,10 +113,6 @@ namespace GitHub.Runner.Worker
Directory.CreateDirectory(toolsDirectory); Directory.CreateDirectory(toolsDirectory);
jobContext.SetRunnerContext("tool_cache", toolsDirectory); jobContext.SetRunnerContext("tool_cache", toolsDirectory);
// remove variable from env
Environment.SetEnvironmentVariable("AGENT_TOOLSDIRECTORY", null);
Environment.SetEnvironmentVariable(Constants.Variables.Agent.ToolsDirectory, null);
// Setup TEMP directories // Setup TEMP directories
_tempDirectoryManager = HostContext.GetService<ITempDirectoryManager>(); _tempDirectoryManager = HostContext.GetService<ITempDirectoryManager>();
_tempDirectoryManager.InitializeTempDirectory(jobContext); _tempDirectoryManager.InitializeTempDirectory(jobContext);
@@ -183,7 +179,7 @@ namespace GitHub.Runner.Worker
finally finally
{ {
Trace.Info("Finalize job."); Trace.Info("Finalize job.");
await jobExtension.FinalizeJob(jobContext, message, jobStartTimeUtc); jobExtension.FinalizeJob(jobContext, message, jobStartTimeUtc);
} }
Trace.Info($"Job result after all job steps finish: {jobContext.Result ?? TaskResult.Succeeded}"); Trace.Info($"Job result after all job steps finish: {jobContext.Result ?? TaskResult.Succeeded}");

View File

@@ -1,12 +1,13 @@
using GitHub.DistributedTask.WebApi; using System;
using GitHub.Runner.Common.Util;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using GitHub.Build.WebApi;
using GitHub.DistributedTask.WebApi;
using GitHub.DistributedTask.Logging; using GitHub.DistributedTask.Logging;
using GitHub.DistributedTask.Pipelines.ContextData; using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.Runner.Common; using GitHub.Runner.Common;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
namespace GitHub.Runner.Worker namespace GitHub.Runner.Worker
@@ -62,10 +63,7 @@ namespace GitHub.Runner.Worker
// DO NOT add file path variable to here. // DO NOT add file path variable to here.
// All file path variables needs to be retrive and set through ExecutionContext, so it can handle container file path translation. // All file path variables needs to be retrive and set through ExecutionContext, so it can handle container file path translation.
public string Build_Number => Get(BuildVariables.BuildNumber);
public string Build_DefinitionName => Get(Constants.Variables.Build.DefinitionName);
public string Build_Number => Get(Constants.Variables.Build.Number);
#if OS_WINDOWS #if OS_WINDOWS
public bool Retain_Default_Encoding => false; public bool Retain_Default_Encoding => false;
@@ -73,44 +71,10 @@ namespace GitHub.Runner.Worker
public bool Retain_Default_Encoding => true; public bool Retain_Default_Encoding => true;
#endif #endif
public string System_CollectionId => Get(Constants.Variables.System.CollectionId);
public bool? Step_Debug => GetBoolean(Constants.Variables.Actions.StepDebug); public bool? Step_Debug => GetBoolean(Constants.Variables.Actions.StepDebug);
public string System_DefinitionId => Get(Constants.Variables.System.DefinitionId);
public string System_PhaseDisplayName => Get(Constants.Variables.System.PhaseDisplayName); public string System_PhaseDisplayName => Get(Constants.Variables.System.PhaseDisplayName);
public string System_TFCollectionUrl => Get(WellKnownDistributedTaskVariables.TFCollectionUrl);
public static readonly HashSet<string> PiiVariables = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"Build.AuthorizeAs",
"Build.QueuedBy",
"Build.RequestedFor",
"Build.RequestedForEmail",
"Build.SourceBranch",
"Build.SourceBranchName",
"Build.SourceTfvcShelveset",
"Build.SourceVersion",
"Build.SourceVersionAuthor",
"Job.AuthorizeAs",
"Release.Deployment.RequestedFor",
"Release.Deployment.RequestedForEmail",
"Release.RequestedFor",
"Release.RequestedForEmail",
};
public static readonly string PiiArtifactVariablePrefix = "Release.Artifacts";
public static readonly List<string> PiiArtifactVariableSuffixes = new List<string>()
{
"SourceBranch",
"SourceBranchName",
"SourceVersion",
"RequestedFor"
};
public string Get(string name) public string Get(string name)
{ {
Variable variable; Variable variable;

View File

@@ -40,9 +40,8 @@ namespace GitHub.Runner.Worker
// Validate args. // Validate args.
ArgUtil.NotNullOrEmpty(pipeIn, nameof(pipeIn)); ArgUtil.NotNullOrEmpty(pipeIn, nameof(pipeIn));
ArgUtil.NotNullOrEmpty(pipeOut, nameof(pipeOut)); ArgUtil.NotNullOrEmpty(pipeOut, nameof(pipeOut));
var runnerWebProxy = HostContext.GetService<IRunnerWebProxy>();
var runnerCertManager = HostContext.GetService<IRunnerCertificateManager>(); var runnerCertManager = HostContext.GetService<IRunnerCertificateManager>();
VssUtil.InitializeVssClientSettings(HostContext.UserAgent, runnerWebProxy.WebProxy, runnerCertManager.VssClientCertificateManager); VssUtil.InitializeVssClientSettings(HostContext.UserAgent, HostContext.WebProxy, runnerCertManager.VssClientCertificateManager);
var jobRunner = HostContext.CreateService<IJobRunner>(); var jobRunner = HostContext.CreateService<IJobRunner>();
using (var channel = HostContext.CreateService<IProcessChannel>()) using (var channel = HostContext.CreateService<IProcessChannel>())
@@ -74,7 +73,7 @@ namespace GitHub.Runner.Worker
SetCulture(jobMessage); SetCulture(jobMessage);
// Start the job. // Start the job.
Trace.Info($"Job message:{Environment.NewLine} {StringUtil.ConvertToJson(WorkerUtilities.ScrubPiiData(jobMessage))}"); Trace.Info($"Job message:{Environment.NewLine} {StringUtil.ConvertToJson(jobMessage)}");
Task<TaskResult> jobRunnerTask = jobRunner.RunAsync(jobMessage, jobRequestCancellationToken.Token); Task<TaskResult> jobRunnerTask = jobRunner.RunAsync(jobMessage, jobRequestCancellationToken.Token);
// Start listening for a cancel message from the channel. // Start listening for a cancel message from the channel.
@@ -179,15 +178,6 @@ namespace GitHub.Runner.Worker
} }
} }
} }
// Add masks for secure file download tickets
foreach (SecureFile file in message.Resources.SecureFiles ?? new List<SecureFile>())
{
if (!string.IsNullOrEmpty(file.Ticket))
{
HostContext.SecretMasker.AddValue(file.Ticket);
}
}
} }
private void SetCulture(Pipelines.AgentJobRequestMessage message) private void SetCulture(Pipelines.AgentJobRequestMessage message)

View File

@@ -1,92 +0,0 @@
using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.WebApi;
using Pipelines = GitHub.DistributedTask.Pipelines;
using System;
using System.Collections.Generic;
using System.Linq;
using GitHub.Runner.Sdk;
namespace GitHub.Runner.Worker
{
public class WorkerUtilities
{
public static Pipelines.AgentJobRequestMessage ScrubPiiData(Pipelines.AgentJobRequestMessage message)
{
ArgUtil.NotNull(message, nameof(message));
var scrubbedVariables = new Dictionary<string, VariableValue>();
// Scrub the known PII variables
foreach (var variable in message.Variables)
{
if (Variables.PiiVariables.Contains(variable.Key) ||
(variable.Key.StartsWith(Variables.PiiArtifactVariablePrefix, StringComparison.OrdinalIgnoreCase)
&& Variables.PiiArtifactVariableSuffixes.Any(varSuffix => variable.Key.EndsWith(varSuffix, StringComparison.OrdinalIgnoreCase))))
{
scrubbedVariables[variable.Key] = "[PII]";
}
else
{
scrubbedVariables[variable.Key] = variable.Value;
}
}
var scrubbedRepositories = new List<Pipelines.RepositoryResource>();
// Scrub the repository resources
foreach (var repository in message.Resources.Repositories)
{
Pipelines.RepositoryResource scrubbedRepository = repository.Clone();
var versionInfo = repository.Properties.Get<Pipelines.VersionInfo>(Pipelines.RepositoryPropertyNames.VersionInfo);
if (versionInfo != null)
{
scrubbedRepository.Properties.Set(
Pipelines.RepositoryPropertyNames.VersionInfo,
new Pipelines.VersionInfo()
{
Author = "[PII]",
Message = versionInfo.Message
});
}
scrubbedRepositories.Add(scrubbedRepository);
}
var scrubbedJobResources = new Pipelines.JobResources();
scrubbedJobResources.Containers.AddRange(message.Resources.Containers);
scrubbedJobResources.Endpoints.AddRange(message.Resources.Endpoints);
scrubbedJobResources.Repositories.AddRange(scrubbedRepositories);
scrubbedJobResources.SecureFiles.AddRange(message.Resources.SecureFiles);
var contextData = new DictionaryContextData();
if (message.ContextData?.Count > 0)
{
foreach (var pair in message.ContextData)
{
contextData[pair.Key] = pair.Value;
}
}
// Reconstitute a new agent job request message from the scrubbed parts
return new Pipelines.AgentJobRequestMessage(
plan: message.Plan,
timeline: message.Timeline,
jobId: message.JobId,
jobDisplayName: message.JobDisplayName,
jobName: message.JobName,
jobContainer: message.JobContainer,
jobServiceContainers: message.JobServiceContainers,
environmentVariables: message.EnvironmentVariables,
variables: scrubbedVariables,
maskHints: message.MaskHints,
jobResources: scrubbedJobResources,
contextData: contextData,
workspaceOptions: message.Workspace,
steps: message.Steps,
scopes: message.Scopes);
}
}
}

View File

@@ -1,264 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Runtime.InteropServices;
using System.Text;
using GitHub.Services.Common;
namespace GitHub.Services.Client
{
internal static class CookieUtility
{
public static readonly String AcsMetadataRetrievalExceptionText = "Unable to retrieve ACS Metadata from '{0}'";
public static readonly String FedAuthCookieName = "FedAuth";
public static readonly String WindowsLiveSignOutUrl = "https://login.live.com/uilogout.srf";
public static readonly Uri WindowsLiveCookieDomain = new Uri("https://login.live.com/");
public static CookieCollection GetFederatedCookies(Uri cookieDomainAndPath)
{
CookieCollection result = null;
Cookie cookie = GetCookieEx(cookieDomainAndPath, FedAuthCookieName).FirstOrDefault();
if (cookie != null)
{
result = new CookieCollection();
result.Add(cookie);
for (Int32 x = 1; x < 50; x++)
{
String cookieName = FedAuthCookieName + x;
cookie = GetCookieEx(cookieDomainAndPath, cookieName).FirstOrDefault();
if (cookie != null)
{
result.Add(cookie);
}
else
{
break;
}
}
}
return result;
}
public static CookieCollection GetFederatedCookies(String[] token)
{
CookieCollection result = null;
if (token != null && token.Length > 0 && token[0] != null)
{
result = new CookieCollection();
result.Add(new Cookie(FedAuthCookieName, token[0]));
for (Int32 x = 1; x < token.Length; x++)
{
String cookieName = FedAuthCookieName + x;
if (token[x] != null)
{
Cookie cookie = new Cookie(cookieName, token[x]);
cookie.HttpOnly = true;
result.Add(cookie);
}
else
{
break;
}
}
}
return result;
}
public static CookieCollection GetFederatedCookies(IHttpResponse webResponse)
{
CookieCollection result = null;
IEnumerable<String> cookies = null;
if (webResponse.Headers.TryGetValues("Set-Cookie", out cookies))
{
foreach (String cookie in cookies)
{
if (cookie != null && cookie.StartsWith(CookieUtility.FedAuthCookieName, StringComparison.OrdinalIgnoreCase))
{
// Only take the security token field of the cookie, and discard the rest
String fedAuthToken = cookie.Split(';').FirstOrDefault();
Int32 index = fedAuthToken.IndexOf('=');
if (index > 0 && index < fedAuthToken.Length - 1)
{
String name = fedAuthToken.Substring(0, index);
String value = fedAuthToken.Substring(index + 1);
result = result ?? new CookieCollection();
result.Add(new Cookie(name, value));
}
}
}
}
return result;
}
public static CookieCollection GetAllCookies(Uri cookieDomainAndPath)
{
CookieCollection result = null;
List<Cookie> cookies = GetCookieEx(cookieDomainAndPath, null);
foreach (Cookie cookie in cookies)
{
if (result == null)
{
result = new CookieCollection();
}
result.Add(cookie);
}
return result;
}
public static void DeleteFederatedCookies(Uri cookieDomainAndPath)
{
CookieCollection cookies = GetFederatedCookies(cookieDomainAndPath);
if (cookies != null)
{
foreach (Cookie cookie in cookies)
{
DeleteCookieEx(cookieDomainAndPath, cookie.Name);
}
}
}
public static void DeleteWindowsLiveCookies()
{
DeleteAllCookies(WindowsLiveCookieDomain);
}
public static void DeleteAllCookies(Uri cookieDomainAndPath)
{
CookieCollection cookies = GetAllCookies(cookieDomainAndPath);
if (cookies != null)
{
foreach (Cookie cookie in cookies)
{
DeleteCookieEx(cookieDomainAndPath, cookie.Name);
}
}
}
public const UInt32 INTERNET_COOKIE_HTTPONLY = 0x00002000;
[DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool InternetGetCookieEx(
String url, String cookieName, StringBuilder cookieData, ref Int32 size, UInt32 flags, IntPtr reserved);
[DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool InternetSetCookieEx(
String url, String cookieName, String cookieData, UInt32 flags, IntPtr reserved);
public static Boolean DeleteCookieEx(Uri cookiePath, String cookieName)
{
UInt32 flags = INTERNET_COOKIE_HTTPONLY;
String path = cookiePath.ToString();
if (!path.EndsWith("/", StringComparison.Ordinal))
{
path = path + "/";
}
DateTime expiration = DateTime.UtcNow.AddYears(-1);
String cookieData = String.Format(CultureInfo.InvariantCulture, "{0}=0;expires={1};path=/;domain={2};httponly", cookieName, expiration.ToString("R"), cookiePath.Host);
return InternetSetCookieEx(path, null, cookieData, flags, IntPtr.Zero);
}
public static Boolean SetCookiesEx(
Uri cookiePath,
CookieCollection cookies)
{
String path = cookiePath.ToString();
if (!path.EndsWith("/", StringComparison.Ordinal))
{
path = path + "/";
}
Boolean successful = true;
foreach (Cookie cookie in cookies)
{
// This means it doesn't expire
if (cookie.Expires.Year == 1)
{
continue;
}
String cookieData = String.Format(CultureInfo.InvariantCulture,
"{0}; path={1}; domain={2}; expires={3}; httponly",
cookie.Value,
cookie.Path,
cookie.Domain,
cookie.Expires.ToString("ddd, dd-MMM-yyyy HH:mm:ss 'GMT'"));
successful &= InternetSetCookieEx(path, cookie.Name, cookieData, INTERNET_COOKIE_HTTPONLY, IntPtr.Zero);
}
return successful;
}
public static List<Cookie> GetCookieEx(Uri cookiePath, String cookieName)
{
UInt32 flags = INTERNET_COOKIE_HTTPONLY;
List<Cookie> cookies = new List<Cookie>();
Int32 size = 256;
StringBuilder cookieData = new StringBuilder(size);
String path = cookiePath.ToString();
if (!path.EndsWith("/", StringComparison.Ordinal))
{
path = path + "/";
}
if (!InternetGetCookieEx(path, cookieName, cookieData, ref size, flags, IntPtr.Zero))
{
if (size < 0)
{
return cookies;
}
cookieData = new StringBuilder(size);
if (!InternetGetCookieEx(path, cookieName, cookieData, ref size, flags, IntPtr.Zero))
{
return cookies;
}
}
if (cookieData.Length > 0)
{
String[] cookieSections = cookieData.ToString().Split(new char[] { ';' });
foreach (String cookieSection in cookieSections)
{
String[] cookieParts = cookieSection.Split(new char[] { '=' }, 2);
if (cookieParts.Length == 2)
{
Cookie cookie = new Cookie();
cookie.Name = cookieParts[0].TrimStart();
cookie.Value = cookieParts[1];
cookie.HttpOnly = true;
cookies.Add(cookie);
}
}
}
return cookies;
}
}
}

View File

@@ -1,95 +0,0 @@
using System;
using System.Net.Http;
using System.Security;
using GitHub.Services.Common;
namespace GitHub.Services.Client
{
/// <summary>
/// Currently it is impossible to get whether prompting is allowed from the credential itself without reproducing the logic
/// used by VssClientCredentials. Since this is a stop gap solution to get Windows integrated authentication to work against
/// AAD via ADFS for now this class will only support that one, non-interactive flow. We need to assess how much we want to
/// invest in this legacy stack rather than recommending people move to the VssConnect API for future authentication needs.
/// </summary>
[Serializable]
public sealed class VssAadCredential : FederatedCredential
{
private string username;
private SecureString password;
public VssAadCredential()
: base(null)
{
}
public VssAadCredential(VssAadToken initialToken)
: base(initialToken)
{
}
public VssAadCredential(string username)
: base(null)
{
this.username = username;
}
public VssAadCredential(string username, string password)
: base(null)
{
this.username = username;
if (password != null)
{
this.password = new SecureString();
foreach (char character in password)
{
this.password.AppendChar(character);
}
}
}
public VssAadCredential(string username, SecureString password)
: base(null)
{
this.username = username;
this.password = password;
}
public override VssCredentialsType CredentialType
{
get
{
return VssCredentialsType.Aad;
}
}
internal string Username
{
get
{
return username;
}
}
internal SecureString Password => password;
public override bool IsAuthenticationChallenge(IHttpResponse webResponse)
{
bool isNonAuthenticationChallenge = false;
return VssFederatedCredential.IsVssFederatedAuthenticationChallenge(webResponse, out isNonAuthenticationChallenge) ?? false;
}
protected override IssuedTokenProvider OnCreateTokenProvider(
Uri serverUrl,
IHttpResponse response)
{
if (response == null && base.InitialToken == null)
{
return null;
}
return new VssAadTokenProvider(this);
}
}
}

View File

@@ -1,89 +0,0 @@
using System;
using System.Diagnostics;
using GitHub.Services.WebApi;
using GitHub.Services.WebApi.Internal;
namespace GitHub.Services.Client
{
internal static class VssAadSettings
{
public const string DefaultAadInstance = "https://login.microsoftonline.com/";
public const string CommonTenant = "common";
// VSTS service principal.
public const string Resource = "499b84ac-1321-427f-aa17-267ca6975798";
// Visual Studio IDE client ID originally provisioned by Azure Tools.
public const string Client = "872cd9fa-d31f-45e0-9eab-6e460a02d1f1";
// AAD Production Application tenant.
private const string ApplicationTenantId = "f8cdef31-a31e-4b4a-93e4-5f571e91255a";
#if !NETSTANDARD
public static Uri NativeClientRedirectUri
{
get
{
Uri nativeClientRedirect = null;
try
{
string nativeRedirect = VssClientEnvironment.GetSharedConnectedUserValue<string>(VssConnectionParameterOverrideKeys.AadNativeClientRedirect);
if (!string.IsNullOrEmpty(nativeRedirect))
{
Uri.TryCreate(nativeRedirect, UriKind.RelativeOrAbsolute, out nativeClientRedirect);
}
}
catch (Exception e)
{
Debug.WriteLine(string.Format("NativeClientRedirectUri: {0}", e));
}
return nativeClientRedirect ?? new Uri("urn:ietf:wg:oauth:2.0:oob");
}
}
public static string ClientId
{
get
{
string nativeRedirect = VssClientEnvironment.GetSharedConnectedUserValue<string>(VssConnectionParameterOverrideKeys.AadNativeClientIdentifier);
return nativeRedirect ?? VssAadSettings.Client;
}
}
#endif
public static string AadInstance
{
get
{
#if !NETSTANDARD
string aadInstance = VssClientEnvironment.GetSharedConnectedUserValue<string>(VssConnectionParameterOverrideKeys.AadInstance);
#else
string aadInstance = null;
#endif
if (string.IsNullOrWhiteSpace(aadInstance))
{
aadInstance = DefaultAadInstance;
}
else if (!aadInstance.EndsWith("/"))
{
aadInstance = aadInstance + "/";
}
return aadInstance;
}
}
#if !NETSTANDARD
/// <summary>
/// Application tenant either from a registry override or a constant
/// </summary>
public static string ApplicationTenant =>
VssClientEnvironment.GetSharedConnectedUserValue<string>(VssConnectionParameterOverrideKeys.AadApplicationTenant)
?? VssAadSettings.ApplicationTenantId;
#endif
}
}

View File

@@ -1,124 +0,0 @@
using System;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using GitHub.Services.Common;
namespace GitHub.Services.Client
{
[Serializable]
public class VssAadToken : IssuedToken
{
private string accessToken;
private string accessTokenType;
private AuthenticationContext authenticationContext;
private UserCredential userCredential;
private VssAadTokenOptions options;
public VssAadToken(AuthenticationResult authentication)
{
// Prevent any attempt to store this token.
this.FromStorage = true;
if (!string.IsNullOrWhiteSpace(authentication.AccessToken))
{
this.Authenticated();
}
this.accessToken = authentication.AccessToken;
this.accessTokenType = authentication.AccessTokenType;
}
public VssAadToken(
string accessTokenType,
string accessToken)
{
// Prevent any attempt to store this token.
this.FromStorage = true;
if (!string.IsNullOrWhiteSpace(accessToken) && !string.IsNullOrWhiteSpace(accessTokenType))
{
this.Authenticated();
}
this.accessToken = accessToken;
this.accessTokenType = accessTokenType;
}
public VssAadToken(
AuthenticationContext authenticationContext,
UserCredential userCredential = null,
VssAadTokenOptions options = VssAadTokenOptions.None)
{
// Prevent any attempt to store this token.
this.FromStorage = true;
this.authenticationContext = authenticationContext;
this.userCredential = userCredential;
this.options = options;
}
protected internal override VssCredentialsType CredentialType
{
get
{
return VssCredentialsType.Aad;
}
}
public AuthenticationResult AcquireToken()
{
if (this.authenticationContext == null)
{
return null;
}
AuthenticationResult authenticationResult = null;
for (int index = 0; index < 3; index++)
{
try
{
if (this.userCredential == null && !options.HasFlag(VssAadTokenOptions.AllowDialog))
{
authenticationResult = authenticationContext.AcquireTokenSilentAsync(VssAadSettings.Resource, VssAadSettings.Client).ConfigureAwait(false).GetAwaiter().GetResult();
}
else
{
authenticationResult = authenticationContext.AcquireTokenAsync(VssAadSettings.Resource, VssAadSettings.Client, this.userCredential).ConfigureAwait(false).GetAwaiter().GetResult();
}
if (authenticationResult != null)
{
break;
}
}
catch (Exception x)
{
System.Diagnostics.Debug.WriteLine("Failed to get ADFS token: " + x.ToString());
}
}
return authenticationResult;
}
internal override void ApplyTo(IHttpRequest request)
{
AuthenticationResult authenticationResult = AcquireToken();
if (authenticationResult != null)
{
request.Headers.SetValue(Common.Internal.HttpHeaders.Authorization, $"{authenticationResult.AccessTokenType} {authenticationResult.AccessToken}");
}
else if (!string.IsNullOrEmpty(this.accessTokenType) && !string.IsNullOrEmpty(this.accessToken))
{
request.Headers.SetValue(Common.Internal.HttpHeaders.Authorization, $"{this.accessTokenType} {this.accessToken}");
}
}
}
[Flags]
public enum VssAadTokenOptions
{
None = 0,
AllowDialog = 1
}
}

View File

@@ -1,77 +0,0 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using GitHub.Services.Common;
namespace GitHub.Services.Client
{
internal sealed class VssAadTokenProvider : IssuedTokenProvider
{
public VssAadTokenProvider(VssAadCredential credential)
: base(credential, null, null)
{
}
public override bool GetTokenIsInteractive
{
get
{
return false;
}
}
private VssAadToken GetVssAadToken()
{
AuthenticationContext authenticationContext = new AuthenticationContext(string.Concat(VssAadSettings.AadInstance, VssAadSettings.CommonTenant));
UserCredential userCredential = null;
VssAadCredential credential = this.Credential as VssAadCredential;
if (credential?.Username != null)
{
#if NETSTANDARD
// UserPasswordCredential does not currently exist for ADAL 3.13.5 for any non-desktop build.
userCredential = new UserCredential(credential.Username);
#else
if (credential.Password != null)
{
userCredential = new UserPasswordCredential(credential.Username, credential.Password);
}
else
{
userCredential = new UserCredential(credential.Username);
}
#endif
}
else
{
userCredential = new UserCredential();
}
return new VssAadToken(authenticationContext, userCredential);
}
/// <summary>
/// Temporary implementation since we don't have a good configuration story here at the moment.
/// </summary>
protected override Task<IssuedToken> OnGetTokenAsync(IssuedToken failedToken, CancellationToken cancellationToken)
{
// If we have already tried to authenticate with an AAD token retrieved from Windows integrated authentication and it is not working, clear out state.
if (failedToken != null && failedToken.CredentialType == VssCredentialsType.Aad && failedToken.IsAuthenticated)
{
this.CurrentToken = null;
return Task.FromResult<IssuedToken>(null);
}
try
{
return Task.FromResult<IssuedToken>(GetVssAadToken());
}
catch
{ }
return Task.FromResult<IssuedToken>(null);
}
}
}

View File

@@ -1,172 +0,0 @@
using System;
using System.Linq;
using System.Net;
using GitHub.Services.Common;
using GitHub.Services.Common.Internal;
namespace GitHub.Services.Client
{
/// <summary>
/// Provides federated authentication with a hosted <c>VssConnection</c> instance using cookies.
/// </summary>
[Serializable]
public sealed class VssFederatedCredential : FederatedCredential
{
/// <summary>
/// Initializes a new <c>VssFederatedCredential</c> instance.
/// </summary>
public VssFederatedCredential()
: this(true)
{
}
/// <summary>
/// Initializes a new <c>VssFederatedCredential</c> instance.
/// </summary>
public VssFederatedCredential(Boolean useCache)
: this(useCache, null)
{
}
/// <summary>
/// Initializes a new <c>VssFederatedCredential</c> instance.
/// </summary>
/// <param name="initialToken">The initial token if available</param>
public VssFederatedCredential(VssFederatedToken initialToken)
: this(false, initialToken)
{
}
public VssFederatedCredential(
Boolean useCache,
VssFederatedToken initialToken)
: base(initialToken)
{
#if !NETSTANDARD
if (useCache)
{
Storage = new VssClientCredentialStorage();
}
#endif
}
/// <summary>
///
/// </summary>
public override VssCredentialsType CredentialType
{
get
{
return VssCredentialsType.Federated;
}
}
public override Boolean IsAuthenticationChallenge(IHttpResponse webResponse)
{
bool isNonAuthenticationChallenge = false;
return IsVssFederatedAuthenticationChallenge(webResponse, out isNonAuthenticationChallenge) ?? isNonAuthenticationChallenge;
}
protected override IssuedTokenProvider OnCreateTokenProvider(
Uri serverUrl,
IHttpResponse response)
{
// The response is only null when attempting to determine the most appropriate token provider to
// use for the connection. The only way we should do anything here is if we have an initial token
// since that means we can present something without making a server call.
if (response == null && base.InitialToken == null)
{
return null;
}
Uri signInUrl = null;
String realm = String.Empty;
String issuer = String.Empty;
if (response != null)
{
var location = response.Headers.GetValues(HttpHeaders.Location).FirstOrDefault();
if (location == null)
{
location = response.Headers.GetValues(HttpHeaders.TfsFedAuthRedirect).FirstOrDefault();
}
if (!String.IsNullOrEmpty(location))
{
signInUrl = new Uri(location);
}
// Inform the server that we support the javascript notify "smart client" pattern for ACS auth
AddParameter(ref signInUrl, "protocol", "javascriptnotify");
// Do not automatically sign in with existing FedAuth cookie
AddParameter(ref signInUrl, "force", "1");
GetRealmAndIssuer(response, out realm, out issuer);
}
return new VssFederatedTokenProvider(this, serverUrl, signInUrl, issuer, realm);
}
internal static void GetRealmAndIssuer(
IHttpResponse response,
out String realm,
out String issuer)
{
realm = response.Headers.GetValues(HttpHeaders.TfsFedAuthRealm).FirstOrDefault();
issuer = response.Headers.GetValues(HttpHeaders.TfsFedAuthIssuer).FirstOrDefault();
if (!String.IsNullOrWhiteSpace(issuer))
{
issuer = new Uri(issuer).GetLeftPart(UriPartial.Authority);
}
}
internal static Boolean? IsVssFederatedAuthenticationChallenge(
IHttpResponse webResponse,
out Boolean isNonAuthenticationChallenge)
{
isNonAuthenticationChallenge = false;
if (webResponse == null)
{
return false;
}
// Check to make sure that the redirect was issued from the Tfs service. We include the TfsServiceError
// header to avoid the possibility that a redirect from a non-tfs service is issued and we incorrectly
// launch the credentials UI.
if (webResponse.StatusCode == HttpStatusCode.Found ||
webResponse.StatusCode == HttpStatusCode.Redirect)
{
return webResponse.Headers.GetValues(HttpHeaders.Location).Any() && webResponse.Headers.GetValues(HttpHeaders.TfsFedAuthRealm).Any();
}
else if (webResponse.StatusCode == HttpStatusCode.Unauthorized)
{
return webResponse.Headers.GetValues(HttpHeaders.WwwAuthenticate).Any(x => x.StartsWith("TFS-Federated", StringComparison.OrdinalIgnoreCase));
}
else if (webResponse.StatusCode == HttpStatusCode.Forbidden)
{
// This is not strictly an "authentication challenge" but it is a state the user can do something about so they can get access to the resource
// they are attempting to access. Specifically, the user will hit this when they need to update or create a profile required by business policy.
isNonAuthenticationChallenge = webResponse.Headers.GetValues(HttpHeaders.TfsFedAuthRedirect).Any();
if (isNonAuthenticationChallenge)
{
return null;
}
}
return false;
}
private static void AddParameter(ref Uri uri, String name, String value)
{
if (uri.Query.IndexOf(String.Concat(name, "="), StringComparison.OrdinalIgnoreCase) < 0)
{
UriBuilder builder = new UriBuilder(uri);
builder.Query = String.Concat(builder.Query.TrimStart('?'), "&", name, "=", value);
uri = builder.Uri;
}
}
}
}

View File

@@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net;
using GitHub.Services.Common;
namespace GitHub.Services.Client
{
/// <summary>
/// Provides a cookie-based authentication token.
/// </summary>
[Serializable]
public sealed class VssFederatedToken : IssuedToken
{
/// <summary>
/// Initializes a new <c>VssFederatedToken</c> instance using the specified cookies.
/// </summary>
/// <param name="cookies"></param>
public VssFederatedToken(CookieCollection cookies)
{
ArgumentUtility.CheckForNull(cookies, "cookies");
m_cookies = cookies;
}
/// <summary>
/// Returns the CookieCollection contained within this token. For internal use only.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public CookieCollection CookieCollection
{
get
{
return m_cookies;
}
}
protected internal override VssCredentialsType CredentialType
{
get
{
return VssCredentialsType.Federated;
}
}
internal override void ApplyTo(IHttpRequest request)
{
// From http://www.ietf.org/rfc/rfc2109.txt:
// Note: For backward compatibility, the separator in the Cookie header
// is semi-colon (;) everywhere.
//
// HttpRequestHeaders uses comma as the default separator, so instead of returning
// a list of cookies, the method returns one semicolon separated string.
IEnumerable<String> values = request.Headers.GetValues(s_cookieHeader);
request.Headers.SetValue(s_cookieHeader, GetHeaderValue(values));
}
private String GetHeaderValue(IEnumerable<String> cookieHeaders)
{
List<String> currentCookies = new List<String>();
if (cookieHeaders != null)
{
foreach (String value in cookieHeaders)
{
currentCookies.AddRange(value.Split(';').Select(x => x.Trim()));
}
}
currentCookies.RemoveAll(x => String.IsNullOrEmpty(x));
foreach (Cookie cookie in m_cookies)
{
// Remove all existing cookies that match the name of the cookie we are going to add.
currentCookies.RemoveAll(x => String.Equals(x.Substring(0, x.IndexOf('=')), cookie.Name, StringComparison.OrdinalIgnoreCase));
currentCookies.Add(String.Concat(cookie.Name, "=", cookie.Value));
}
return String.Join("; ", currentCookies);
}
private CookieCollection m_cookies;
private static readonly String s_cookieHeader = HttpRequestHeader.Cookie.ToString();
}
}

View File

@@ -1,157 +0,0 @@
using System;
using System.Net;
using System.Net.Http;
using GitHub.Services.Common;
using System.Globalization;
namespace GitHub.Services.Client
{
/// <summary>
/// Provides authentication for internet identities using single-sign-on cookies.
/// </summary>
internal sealed class VssFederatedTokenProvider : IssuedTokenProvider, ISupportSignOut
{
public VssFederatedTokenProvider(
VssFederatedCredential credential,
Uri serverUrl,
Uri signInUrl,
String issuer,
String realm)
: base(credential, serverUrl, signInUrl)
{
Issuer = issuer;
Realm = realm;
}
protected override String AuthenticationScheme
{
get
{
return "TFS-Federated";
}
}
protected override String AuthenticationParameter
{
get
{
if (String.IsNullOrEmpty(this.Issuer) && String.IsNullOrEmpty(this.Realm))
{
return String.Empty;
}
else
{
return String.Format(CultureInfo.InvariantCulture, "issuer=\"{0}\", realm=\"{1}\"", this.Issuer, this.Realm);
}
}
}
/// <summary>
/// Gets the federated credential from which this provider was created.
/// </summary>
public new VssFederatedCredential Credential
{
get
{
return (VssFederatedCredential)base.Credential;
}
}
/// <summary>
/// Gets a value indicating whether or not a call to get token will require interactivity.
/// </summary>
public override Boolean GetTokenIsInteractive
{
get
{
return this.CurrentToken == null;
}
}
/// <summary>
/// Gets the issuer for the token provider.
/// </summary>
public String Issuer
{
get;
private set;
}
/// <summary>
/// Gets the realm for the token provider.
/// </summary>
public String Realm
{
get;
private set;
}
protected internal override Boolean IsAuthenticationChallenge(IHttpResponse webResponse)
{
if (!base.IsAuthenticationChallenge(webResponse))
{
return false;
}
// This means we were proactively constructed without any connection information. In this case
// we return false to ensure that a new provider is reconstructed with all appropriate configuration
// to retrieve a new token.
if (this.SignInUrl == null)
{
return false;
}
String realm, issuer;
VssFederatedCredential.GetRealmAndIssuer(webResponse, out realm, out issuer);
return this.Realm.Equals(realm, StringComparison.OrdinalIgnoreCase) &&
this.Issuer.Equals(issuer, StringComparison.OrdinalIgnoreCase);
}
protected override IssuedToken OnValidatingToken(
IssuedToken token,
IHttpResponse webResponse)
{
// If the response has Set-Cookie headers, attempt to retrieve the FedAuth cookie from the response
// and replace the current token with the new FedAuth cookie. Note that the server only reissues the
// FedAuth cookie if it is issued for more than an hour.
CookieCollection fedAuthCookies = CookieUtility.GetFederatedCookies(webResponse);
if (fedAuthCookies != null)
{
// The reissued token should have the same user information as the previous one.
VssFederatedToken federatedToken = new VssFederatedToken(fedAuthCookies)
{
Properties = token.Properties,
UserId = token.UserId,
UserName = token.UserName
};
token = federatedToken;
}
return token;
}
public void SignOut(Uri signOutUrl, Uri replyToUrl, String identityProvider)
{
// The preferred implementation is to follow the signOutUrl with a browser and kill the browser whenever it
// arrives at the replyToUrl (or if it bombs out somewhere along the way).
// This will work for all Web-based identity providers (Live, Google, Yahoo, Facebook) supported by ACS provided that
// the TFS server has registered sign-out urls (in the TF Registry) for each of these.
// This is the long-term approach that should be pursued and probably the approach recommended to other
// clients which don't have direct access to the cookie store (TEE?)
// In the short term we are simply going to delete the TFS cookies and the Windows Live cookies that are exposed to this
// session. This has the drawback of not properly signing out of Live (you'd still be signed in to e.g. Hotmail, Xbox, MSN, etc.)
// but will allow the user to re-enter their live credentials and sign-in again to TFS.
// The other drawback is that the clients will have to be updated again when we pursue the implementation outlined above.
CookieUtility.DeleteFederatedCookies(replyToUrl);
if (!String.IsNullOrEmpty(identityProvider) && identityProvider.Equals("Windows Live ID", StringComparison.OrdinalIgnoreCase))
{
CookieUtility.DeleteWindowsLiveCookies();
}
}
}
}

View File

@@ -1,12 +0,0 @@
using System;
using GitHub.Services.Common;
namespace GitHub.Build.WebApi {
[GenerateAllConstants]
public static class AgentTargetExecutionType {
public const Int32 Normal = 0;
public const Int32 VariableMultipliers = 1;
public const Int32 MultipleAgents = 2;
}
}

View File

@@ -1,76 +1,14 @@
using System; using System;
using System.ComponentModel;
using GitHub.Services.Common; using GitHub.Services.Common;
namespace GitHub.Build.WebApi namespace GitHub.Build.WebApi
{ {
[Obsolete("Use ArtifactResourceTypes instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static class WellKnownArtifactResourceTypes
{
public const String FilePath = ArtifactResourceTypes.FilePath;
public const String SymbolStore = ArtifactResourceTypes.SymbolStore;
public const String VersionControl = ArtifactResourceTypes.VersionControl;
public const String Container = ArtifactResourceTypes.Container;
public const String GitRef = ArtifactResourceTypes.GitRef;
public const String TfvcLabel = ArtifactResourceTypes.TfvcLabel;
public const String SymbolRequest = ArtifactResourceTypes.SymbolRequest;
}
[GenerateAllConstants]
public static class ArtifactResourceTypes public static class ArtifactResourceTypes
{ {
/// <summary>
/// UNC or local folder path
/// E.g. \\vscsstor\CIDrops\CloudU.Gated\140317.115955 or file://vscsstor/CIDrops/CloudU.Gated/140317.115955
/// </summary>
public const String FilePath = "FilePath";
/// <summary>
/// Symbol store UNC path
/// E.g. \\symbolstore
/// </summary>
public const String SymbolStore = "SymbolStore";
/// <summary>
/// TF VC server folder path
/// E.g. $/Dev1/Drops/CloudU.Gated/140317.115955
/// </summary>
public const String VersionControl = "VersionControl";
/// <summary> /// <summary>
/// Build container reference /// Build container reference
/// E.g. #/2121/drop /// E.g. #/2121/drop
/// </summary> /// </summary>
public const String Container = "Container"; public const String Container = "Container";
/// <summary>
/// Git ref
/// E.g. refs/tags/MyCIDefinition.Buildable
/// </summary>
public const String GitRef = "GitRef";
/// <summary>
/// TFVC label
/// </summary>
public const String TfvcLabel = "TfvcLabel";
/// <summary>
/// Symbol store URL
/// E.g. https://mseng.artifacts.visualstudio.com/...
/// </summary>
public const String SymbolRequest = "SymbolRequest";
/// <summary>
/// Dedup Drop (old name fo PipelineArtifact)
/// E.g. drop1
/// </summary>
public const String Drop = "Drop";
/// <summary>
/// Dedup'ed pipeline artifact
/// E.g. artifact1
/// </summary>
public const String PipelineArtifact = "PipelineArtifact";
} }
} }

View File

@@ -1,17 +0,0 @@
using GitHub.Services.Common;
namespace GitHub.Build.WebApi
{
public static class BuildDefinitionExtensions
{
public static T GetProcess<T>(
this BuildDefinition definition) where T : BuildProcess
{
ArgumentUtility.CheckForNull(definition, nameof(definition));
ArgumentUtility.CheckForNull(definition.Process, nameof(definition.Process));
ArgumentUtility.CheckType<T>(definition.Process, nameof(definition.Process), nameof(T));
return definition.Process as T;
}
}
}

View File

@@ -1,44 +0,0 @@
using System;
using System.IO;
using System.Text;
using GitHub.Build.WebApi.Internals;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
namespace GitHub.Build.WebApi
{
public static class BuildDefinitionHelpers
{
public static BuildDefinition Deserialize(
String definitionString)
{
var definition = JsonUtility.FromString<BuildDefinition>(definitionString);
if (definition?.Process == null)
{
var legacyDefinition = JsonConvert.DeserializeObject<BuildDefinition3_2>(definitionString);
definition = legacyDefinition.ToBuildDefinition();
}
return definition;
}
public static BuildDefinitionTemplate GetTemplateFromStream(
Stream stream)
{
String templateString;
using (var reader = new StreamReader(stream, Encoding.UTF8, false, 1024, true))
{
templateString = reader.ReadToEnd();
}
var template = JsonConvert.DeserializeObject<BuildDefinitionTemplate>(templateString);
if (template?.Template?.Process == null)
{
var legacyTemplate = JsonConvert.DeserializeObject<BuildDefinitionTemplate3_2>(templateString);
template = legacyTemplate.ToBuildDefinitionTemplate();
}
return template;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
using System;
namespace GitHub.Build.WebApi
{
public static class BuildIssueKeys
{
public const String CodeCategory = "code";
public const String SourcePath = "sourcePath";
public const String LineNumber = "lineNumber";
public const String Message = "message";
}
[Obsolete("Use BuildIssueKeys instead.")]
public static class WellKnownBuildKeys
{
public const String BuildIssueCodeCategory = BuildIssueKeys.CodeCategory;
public const String BuildIssueFileKey = BuildIssueKeys.SourcePath;
public const String BuildIssueLineNumberKey = BuildIssueKeys.LineNumber;
public const String BuildIssueMessageKey = BuildIssueKeys.Message;
}
}

View File

@@ -1,12 +0,0 @@
using System;
using GitHub.Services.Common;
namespace GitHub.Build.WebApi
{
[GenerateAllConstants]
public static class BuildOrchestrationType
{
public const Int32 Build = 1;
public const Int32 Cleanup = 2;
}
}

View File

@@ -1,40 +0,0 @@
using System;
namespace GitHub.Build.WebApi
{
public static class BuildPermissions
{
public static readonly Int32 ViewBuilds = 1;
public static readonly Int32 EditBuildQuality = 2;
public static readonly Int32 RetainIndefinitely = 4;
public static readonly Int32 DeleteBuilds = 8;
public static readonly Int32 ManageBuildQualities = 16;
public static readonly Int32 DestroyBuilds = 32;
public static readonly Int32 UpdateBuildInformation = 64;
public static readonly Int32 QueueBuilds = 128;
public static readonly Int32 ManageBuildQueue = 256;
public static readonly Int32 StopBuilds = 512;
public static readonly Int32 ViewBuildDefinition = 1024;
public static readonly Int32 EditBuildDefinition = 2048;
public static readonly Int32 DeleteBuildDefinition = 4096;
public static readonly Int32 OverrideBuildCheckInValidation = 8192;
public static readonly Int32 AdministerBuildPermissions = 16384;
public static readonly Int32 AllPermissions =
ViewBuilds |
EditBuildQuality |
RetainIndefinitely |
DeleteBuilds |
ManageBuildQualities |
DestroyBuilds |
UpdateBuildInformation |
QueueBuilds |
ManageBuildQueue |
StopBuilds |
ViewBuildDefinition |
EditBuildDefinition |
DeleteBuildDefinition |
OverrideBuildCheckInValidation |
AdministerBuildPermissions;
}
}

View File

@@ -1,165 +1,10 @@
using System; using System;
using GitHub.Services.Common;
namespace GitHub.Build.WebApi namespace GitHub.Build.WebApi
{ {
[GenerateAllConstants(alternateName: "Build2ResourceIds")]
public static class BuildResourceIds public static class BuildResourceIds
{ {
// keep these sorted
public const String AreaId = "5D6898BB-45EC-463F-95F9-54D49C71752E"; public const String AreaId = "5D6898BB-45EC-463F-95F9-54D49C71752E";
public const String AreaName = "build"; public const String AreaName = "build";
public static readonly Guid Artifacts = new Guid("{1DB06C96-014E-44E1-AC91-90B2D4B3E984}");
public const String ArtifactsResource = "artifacts";
public const String AttachmentLocation = "AF5122D3-3438-485E-A25A-2DBBFDE84EE6";
public static readonly Guid Attachment = new Guid(AttachmentLocation);
public const String AttachmentsLocation = "F2192269-89FA-4F94-BAF6-8FB128C55159";
public static readonly Guid Attachments = new Guid(AttachmentsLocation);
public const String AttachmentsResource = "attachments";
public static readonly Guid BuildBadge = new Guid("21b3b9ce-fad5-4567-9ad0-80679794e003");
public const String BuildBadgeResource = "buildbadge";
public const String BuildChangesLocationId = "54572C7B-BBD3-45D4-80DC-28BE08941620";
public static readonly Guid BuildChangesBetweenBuilds = new Guid("{F10F0EA5-18A1-43EC-A8FB-2042C7BE9B43}");
public static readonly Guid BuildChanges = new Guid(BuildChangesLocationId);
public const String BuildChangesResource = "changes";
public static readonly Guid BuildDefinitionBadge = new Guid("de6a4df8-22cd-44ee-af2d-39f6aa7a4261");
public const String BuildDefinitionBadgeResource = "badge";
public static readonly Guid BuildDeployments = new Guid("{F275BE9A-556A-4EE9-B72F-F9C8370CCAEE}");
public const String BuildDeploymentsResource = "deployments";
public static readonly Guid BuildLogs = new Guid("{35A80DAF-7F30-45FC-86E8-6B813D9C90DF}");
public const String BuildLogsResource = "logs";
public const String BuildPropertiesLocationString = "0A6312E9-0627-49B7-8083-7D74A64849C9";
public static readonly Guid BuildProperties = new Guid(BuildPropertiesLocationString);
public static readonly Guid BuildReport = new Guid("{45BCAA88-67E1-4042-A035-56D3B4A7D44C}");
public const String BuildReportResource = "report";
public static readonly Guid Builds = new Guid("{0CD358E1-9217-4D94-8269-1C1EE6F93DCF}");
public const String BuildsResource = "builds";
public const String BuildTagsLocationIdString = "6E6114B2-8161-44C8-8F6C-C5505782427F";
public static readonly Guid BuildTags = new Guid(BuildTagsLocationIdString);
public const String BuildTagsResource = "tags";
public const String BuildWorkItemsLocationId = "5A21F5D2-5642-47E4-A0BD-1356E6731BEE";
public static readonly Guid BuildWorkItemsBetweenBuilds = new Guid("{52BA8915-5518-42E3-A4BB-B0182D159E2D}");
public static readonly Guid BuildWorkItems = new Guid(BuildWorkItemsLocationId);
public const String BuildWorkItemsResource = "workitems";
public const String ControllersLocationString = "{FCAC1932-2EE1-437F-9B6F-7F696BE858F6}";
public static readonly Guid Controllers = new Guid(ControllersLocationString);
public const String ControllersResource = "Controllers";
public const String DefinitionMetricsLocationString = "D973B939-0CE0-4FEC-91D8-DA3940FA1827";
public static readonly Guid DefinitionMetrics = new Guid(DefinitionMetricsLocationString);
public const String DefinitionMetricsResource = "metrics";
public const String DefinitionPropertiesLocationString = "D9826AD7-2A68-46A9-A6E9-677698777895";
public static readonly Guid DefinitionProperties = new Guid(DefinitionPropertiesLocationString);
public static readonly Guid DefinitionResources = new Guid("ea623316-1967-45eb-89ab-e9e6110cf2d6");
public const String DefinitionResourcesResource = "resources";
public static readonly Guid DefinitionRevisions = new Guid("{7C116775-52E5-453E-8C5D-914D9762D8C4}");
public const String DefinitionRevisionsResource = "revisions";
public static readonly Guid Definitions = new Guid("{DBEAF647-6167-421A-BDA9-C9327B25E2E6}");
public const String DefinitionsResource = "definitions";
public const String DefinitionTagsLocationIdString = "CB894432-134A-4D31-A839-83BECEAACE4B";
public static readonly Guid DefinitionTags = new Guid(DefinitionTagsLocationIdString);
public static readonly Guid Folders = new Guid("{A906531B-D2DA-4F55-BDA7-F3E676CC50D9}");
public const String FoldersResource = "folders";
// information nodes for XAML builds
public static readonly Guid InformationNodes = new Guid("9F094D42-B41C-4920-95AA-597581A79821");
public static readonly Guid InputValuesQuery = new Guid("{2182A7F0-B363-4B2D-B89E-ED0A0B721E95}");
public const String InputValuesQueryResource = "InputValuesQuery";
public static readonly Guid LatestBuildLocationId = new Guid("54481611-01F4-47F3-998F-160DA0F0C229");
public const String LatestBuildResource = "latest";
public static readonly Guid Metrics = new Guid("104AD424-B758-4699-97B7-7E7DA427F9C2");
public const String MetricsResource = "Metrics";
public static readonly Guid Options = new Guid("{591CB5A4-2D46-4F3A-A697-5CD42B6BD332}");
public const String OptionsResource = "options";
public const String ProjectMetricsLocationString = "7433FAE7-A6BC-41DC-A6E2-EEF9005CE41A";
public static readonly Guid ProjectMetrics = new Guid(ProjectMetricsLocationString);
public static readonly Guid ProjectAuthorizedResources = new Guid("398c85bc-81aa-4822-947c-a194a05f0fef");
public const String ProjectAuthorizedResourcesResource = "authorizedresources";
public const String PropertiesResource = "properties";
public static readonly Guid Queues = new Guid("{09F2A4B8-08C9-4991-85C3-D698937568BE}");
public const String QueuesResource = "queues";
public static readonly Guid Settings = new Guid("{AA8C1C9C-EF8B-474A-B8C4-785C7B191D0D}");
public const String SettingsResource = "settings";
public const String SourceProviderBranchesResource = "branches";
public const String SourceProviderBranchesLocationIdString = "E05D4403-9B81-4244-8763-20FDE28D1976";
public static readonly Guid SourceProviderBranchesLocationId = new Guid(SourceProviderBranchesLocationIdString);
public const String SourceProviderFileContentsResource = "fileContents";
public const String SourceProviderFileContentsLocationIdString = "29D12225-B1D9-425F-B668-6C594A981313";
public static readonly Guid SourceProviderFileContentsLocationId = new Guid(SourceProviderFileContentsLocationIdString);
public const String SourceProviderPathContentsResource = "pathContents";
public const String SourceProviderPathContentsLocationIdString = "7944D6FB-DF01-4709-920A-7A189AA34037";
public static readonly Guid SourceProviderPathContentsLocationId = new Guid(SourceProviderPathContentsLocationIdString);
public const String SourceProviderPullRequestsResource = "pullRequests";
public const String SourceProviderPullRequestsLocationIdString = "D8763EC7-9FF0-4FB4-B2B2-9D757906FF14";
public static readonly Guid SourceProviderPullRequestsLocationId = new Guid(SourceProviderPullRequestsLocationIdString);
public const String SourceProviderRepositoriesResource = "repositories";
public const String SourceProviderRepositoriesLocationIdString = "D44D1680-F978-4834-9B93-8C6E132329C9";
public static readonly Guid SourceProviderRepositoriesLocationId = new Guid(SourceProviderRepositoriesLocationIdString);
public const String SourceProviderRestoreWebhooksLocationIdString = "793BCEB8-9736-4030-BD2F-FB3CE6D6B478";
public static readonly Guid SourceProviderRestoreWebhooksLocationId = new Guid(SourceProviderRestoreWebhooksLocationIdString);
public const String SourceProvidersResource = "sourceProviders";
public const String SourceProvidersLocationIdString = "3CE81729-954F-423D-A581-9FEA01D25186";
public static readonly Guid SourceProviders = new Guid(SourceProvidersLocationIdString);
public const String SourceProviderWebhooksResource = "webhooks";
public const String SourceProviderWebhooksLocationIdString = "8F20FF82-9498-4812-9F6E-9C01BDC50E99";
public static readonly Guid SourceProviderWebhooksLocationId = new Guid(SourceProviderWebhooksLocationIdString);
public const String SourcesLocationId = "56EFDCDC-CF90-4028-9D2F-D41000682202";
public static readonly Guid Sources = new Guid(SourcesLocationId);
public const String SourcesResource = "sources";
public const String StatusBadgeLocationIdString = "07ACFDCE-4757-4439-B422-DDD13A2FCC10";
public static readonly Guid StatusBadgeLocationId = new Guid(StatusBadgeLocationIdString);
public const String StatusBadgeResource = "status";
public const String TagsLocationIdString = "D84AC5C6-EDC7-43D5-ADC9-1B34BE5DEA09";
public static readonly Guid Tags = new Guid(TagsLocationIdString);
public static readonly Guid Templates = new Guid("{E884571E-7F92-4D6A-9274-3F5649900835}");
public const String TemplatesResource = "templates";
public static readonly Guid Timelines = new Guid("8baac422-4c6e-4de5-8532-db96d92acffa");
public const String TimelinesResource = "Timeline";
public static readonly Guid Usage = new Guid("3813d06c-9e36-4ea1-aac3-61a485d60e3d");
public const String UsageResource = "ResourceUsage";
} }
} }

View File

@@ -1,29 +0,0 @@
using System;
using GitHub.Services.Common;
namespace GitHub.Build.WebApi
{
[GenerateAllConstants]
public static class BuildTemplateCategories
{
public static readonly String All = "All";
public static readonly String Build = "Build";
public static readonly String Utility = "Utility";
public static readonly String Test = "Test";
public static readonly String Package = "Package";
public static readonly String Deploy = "Deploy";
public static readonly String Tool = "Tool";
public static readonly String Custom = "Custom";
public static readonly String[] AllCategories = new String[] {
All,
Build,
Utility,
Test,
Package,
Deploy,
Tool,
Custom
};
}
}

View File

@@ -1,87 +1,13 @@
using System; using System;
using GitHub.Services.Common;
namespace GitHub.Build.WebApi namespace GitHub.Build.WebApi
{ {
/* IMPORTANT NOTE: if you're adding a new build variable that's designed to hold PII data
(e.g. names, addresses, phone numbers, IP addresses, emails), please add a corresponding reference to `PiiVariables` at
https://github.com/Microsoft/azure-pipelines-agent/blob/master/src/Agent.Worker/Variables.cs
This is so the agent can scrub the variable value from the diagnostics log. */
[GenerateAllConstants]
public static class BuildVariables public static class BuildVariables
{ {
public const String CollectionId = "system.collectionId";
public const String DefinitionId = "system.definitionId";
public const String HostType = "system.hosttype";
public const String IsFork = "system.pullRequest.isFork";
public const String ForkSecretsRemoved= "system.pullRequest.forkSecretsRemoved";
public const String PullRequestId = "system.pullRequest.pullRequestId";
public const String PullRequestNumber = "system.pullRequest.pullRequestNumber";
public const String PullRequestIterationId = "system.pullRequest.pullRequestIteration";
public const String PullRequestSourceBranch = "system.pullRequest.sourceBranch";
public const String PullRequestTargetBranch = "system.pullRequest.targetBranch";
public const String PullRequestSourceRepositoryUri = "system.pullRequest.sourceRepositoryUri";
public const String PullRequestSourceCommitId = "system.pullRequest.sourceCommitId";
public const String PullRequestMergedAt = "system.pullRequest.mergedAt";
public const String System = "system";
public const String TeamProject = "system.teamProject";
public const String TeamProjectId = "system.teamProjectId"; public const String TeamProjectId = "system.teamProjectId";
public const String BuildId = "build.buildId"; public const String BuildId = "build.buildId";
public const String BuildNumber = "build.buildNumber"; public const String BuildNumber = "build.buildNumber";
public const String BuildUri = "build.buildUri";
public const String ContainerId = "build.containerId"; public const String ContainerId = "build.containerId";
public const String DefinitionName = "build.definitionName";
public const String DefinitionVersion = "build.definitionVersion";
public const String JobAuthorizeAs = "Job.AuthorizeAs";
public const String JobAuthorizeAsId = "Job.AuthorizeAsId";
public const String QueuedBy = "build.queuedBy";
public const String QueuedById = "build.queuedById";
public const String Reason = "build.reason";
public const String RepoUri = "build.repository.uri";
public const String RequestedFor = "build.requestedFor";
public const String RequestedForEmail = "build.requestedForEmail";
public const String RequestedForId = "build.requestedForId";
public const String SourceBranch = "build.sourceBranch";
public const String SourceBranchName = "build.sourceBranchName";
public const String SourceTfvcShelveset = "build.sourceTfvcShelveset";
public const String SourceVersion = "build.sourceVersion";
public const String SourceVersionAuthor = "build.sourceVersionAuthor";
public const String SourceVersionMessage = "build.sourceVersionMessage";
public const String SyncSources = "build.syncSources";
}
[Obsolete("Use BuildVariables instead.")]
public static class WellKnownBuildVariables
{
public const String System = BuildVariables.System;
public const String CollectionId = BuildVariables.CollectionId;
public const String TeamProject = BuildVariables.TeamProject;
public const String TeamProjectId = BuildVariables.TeamProjectId;
public const String DefinitionId = BuildVariables.DefinitionId;
public const String HostType = BuildVariables.HostType;
public const String IsFork = BuildVariables.IsFork;
public const String DefinitionName = BuildVariables.DefinitionName;
public const String DefinitionVersion = BuildVariables.DefinitionVersion;
public const String QueuedBy = BuildVariables.QueuedBy;
public const String QueuedById = BuildVariables.QueuedById;
public const String Reason = BuildVariables.Reason;
public const String RequestedFor = BuildVariables.RequestedFor;
public const String RequestedForId = BuildVariables.RequestedForId;
public const String RequestedForEmail = BuildVariables.RequestedForEmail;
public const String SourceBranch = BuildVariables.SourceBranch;
public const String SourceBranchName = BuildVariables.SourceBranchName;
public const String SourceVersion = BuildVariables.SourceVersion;
public const String SourceVersionAuthor = BuildVariables.SourceVersionAuthor;
public const String SourceVersionMessage = BuildVariables.SourceVersionMessage;
public const String SourceTfvcShelveset = BuildVariables.SourceTfvcShelveset;
public const String BuildId = BuildVariables.BuildId;
public const String BuildUri = BuildVariables.BuildUri;
public const String BuildNumber = BuildVariables.BuildNumber;
public const String ContainerId = BuildVariables.ContainerId;
public const String SyncSources = BuildVariables.SyncSources;
public const String JobAuthorizeAs = BuildVariables.JobAuthorizeAs;
public const String JobAuthorizeAsId = BuildVariables.JobAuthorizeAsId;
public const String RepoUri = BuildVariables.RepoUri;
} }
} }

View File

@@ -1,134 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.Common;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a queue for running builds.
/// </summary>
[DataContract]
#pragma warning disable 618
public class AgentPoolQueue : ShallowReference, ISecuredObject
#pragma warning restore 618
{
public AgentPoolQueue()
{
}
internal AgentPoolQueue(
ISecuredObject securedObject)
{
this.m_securedObject = securedObject;
}
/// <summary>
/// The ID of the queue.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public new Int32 Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
/// <summary>
/// The name of the queue.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public new String Name
{
get
{
return base.Name;
}
set
{
base.Name = value;
}
}
/// <summary>
/// The full http link to the resource.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public new String Url
{
get
{
return base.Url;
}
set
{
base.Url = value;
}
}
/// <summary>
/// The pool used by this queue.
/// </summary>
[DataMember]
public TaskAgentPoolReference Pool
{
get;
set;
}
/// <summary>
/// The links to other objects related to this object.
/// </summary>
public ReferenceLinks Links
{
get
{
if (m_links == null)
{
m_links = new ReferenceLinks();
}
return m_links;
}
}
[DataMember(Name = "_links", EmitDefaultValue = false)]
private ReferenceLinks m_links;
#region ISecuredObject implementation
[IgnoreDataMember]
Guid ISecuredObject.NamespaceId
{
get
{
ArgumentUtility.CheckForNull(m_securedObject, nameof(m_securedObject));
return m_securedObject.NamespaceId;
}
}
[IgnoreDataMember]
Int32 ISecuredObject.RequiredPermissions
{
get
{
ArgumentUtility.CheckForNull(m_securedObject, nameof(m_securedObject));
return m_securedObject.RequiredPermissions;
}
}
String ISecuredObject.GetToken()
{
ArgumentUtility.CheckForNull(m_securedObject, nameof(m_securedObject));
return m_securedObject.GetToken();
}
private ISecuredObject m_securedObject;
#endregion
}
}

View File

@@ -1,102 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Describes how a phase should run against an agent queue.
/// </summary>
[DataContract]
public class AgentPoolQueueTarget : PhaseTarget
{
public AgentPoolQueueTarget()
: this(null)
{
}
internal AgentPoolQueueTarget(
ISecuredObject securedObject)
: base(PhaseTargetType.Agent, securedObject)
{
}
/// <summary>
/// The queue.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public AgentPoolQueue Queue
{
get;
set;
}
/// <summary>
/// Agent specification of the target.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public AgentSpecification AgentSpecification
{
get;
set;
}
/// <summary>
/// The list of demands required for the queue.
/// </summary>
public List<Demand> Demands
{
get
{
if (m_demands == null)
{
m_demands = new List<Demand>();
}
return m_demands;
}
set
{
m_demands = new List<Demand>(value);
}
}
/// <summary>
/// The execution options.
/// </summary>
[DataMember]
public AgentTargetExecutionOptions ExecutionOptions
{
get;
set;
}
/// <summary>
/// Enables scripts and other processes launched while executing phase to access the OAuth token
/// </summary>
[DataMember]
public Boolean AllowScriptsAuthAccessOption
{
get;
set;
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
SerializationHelper.Copy(ref m_serializedDemands, ref m_demands, true);
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
SerializationHelper.Copy(ref m_demands, ref m_serializedDemands);
}
[DataMember(Name = "Demands", EmitDefaultValue = false)]
private List<Demand> m_serializedDemands;
private List<Demand> m_demands;
}
}

View File

@@ -1,28 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Specification of the agent defined by the pool provider.
/// </summary>
[DataContract]
public class AgentSpecification: BaseSecuredObject
{
public AgentSpecification()
{
}
public AgentSpecification(ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// Agent specification unique identifier.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Identifier { get; set; }
}
}

View File

@@ -1,51 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Additional options for running phases against an agent queue.
/// </summary>
[DataContract]
[KnownType(typeof(MultipleAgentExecutionOptions))]
[KnownType(typeof(VariableMultipliersAgentExecutionOptions))]
[JsonConverter(typeof(AgentTargetExecutionOptionsJsonConverter))]
public class AgentTargetExecutionOptions : BaseSecuredObject
{
public AgentTargetExecutionOptions()
: this(AgentTargetExecutionType.Normal)
{
}
protected AgentTargetExecutionOptions(Int32 type)
: this(type, null)
{
}
internal AgentTargetExecutionOptions(
ISecuredObject securedObject)
: this(AgentTargetExecutionType.Normal, securedObject)
{
}
internal AgentTargetExecutionOptions(
Int32 type,
ISecuredObject securedObject)
: base(securedObject)
{
this.Type = type;
}
/// <summary>
/// Indicates the type of execution options.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public Int32 Type
{
get;
set;
}
}
}

View File

@@ -1,42 +0,0 @@
using System;
namespace GitHub.Build.WebApi
{
internal sealed class AgentTargetExecutionOptionsJsonConverter : TypePropertyJsonConverter<AgentTargetExecutionOptions>
{
protected override AgentTargetExecutionOptions GetInstance(Type objectType)
{
if (objectType == typeof(AgentTargetExecutionType))
{
return new AgentTargetExecutionOptions();
}
else if (objectType == typeof(VariableMultipliersAgentExecutionOptions))
{
return new VariableMultipliersAgentExecutionOptions();
}
else if (objectType == typeof(MultipleAgentExecutionOptions))
{
return new MultipleAgentExecutionOptions();
}
else
{
return base.GetInstance(objectType);
}
}
protected override AgentTargetExecutionOptions GetInstance(Int32 targetType)
{
switch (targetType)
{
case AgentTargetExecutionType.Normal:
return new AgentTargetExecutionOptions();
case AgentTargetExecutionType.VariableMultipliers:
return new VariableMultipliersAgentExecutionOptions();
case AgentTargetExecutionType.MultipleAgents:
return new MultipleAgentExecutionOptions();
default:
return null;
}
}
}
}

View File

@@ -50,46 +50,5 @@ namespace GitHub.Build.WebApi
get; get;
set; set;
} }
/// <summary>
/// The full http link to the resource.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public String Url
{
get;
set;
}
/// <summary>
/// A link to download the resource.
/// </summary>
/// <remarks>
/// This might include things like query parameters to download as a zip file.
/// </remarks>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public String DownloadUrl
{
get;
set;
}
/// <summary>
/// The links to other objects related to this object.
/// </summary>
public ReferenceLinks Links
{
get
{
if (m_links == null)
{
m_links = new ReferenceLinks();
}
return m_links;
}
}
[DataMember(Name = "_links", EmitDefaultValue = false)]
private ReferenceLinks m_links;
} }
} }

View File

@@ -1,51 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents an attachment to a build.
/// </summary>
[DataContract]
public class Attachment : BaseSecuredObject
{
public Attachment()
{
}
internal Attachment(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The name of the attachment.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Name
{
get;
set;
}
/// <summary>
/// The links to other objects related to this object.
/// </summary>
public ReferenceLinks Links
{
get
{
if (m_links == null)
{
m_links = new ReferenceLinks();
}
return m_links;
}
}
[DataMember(Name = "_links", EmitDefaultValue = false)]
private ReferenceLinks m_links;
}
}

View File

@@ -1,605 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using GitHub.Core.WebApi;
//using GitHub.Core.WebApi;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Data representation of a build.
/// </summary>
[DataContract]
public class Build : ISecuredObject
{
public Build()
{
Reason = BuildReason.Manual;
Priority = QueuePriority.Normal;
}
#region BuildReference members
// these are also present in BuildReference. ideally this class would inherit from that.
// however, moving them to a base class changes the order in which they are serialized to xml
// which breaks compat with subscribers (like RM) who may not be on the same milestone
// TODO: remove these when we figure out how to version service bus events
/// <summary>
/// The ID of the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
[Key]
public Int32 Id
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The build number/name of the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String BuildNumber
{
get;
set;
}
/// <summary>
/// The status of the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildStatus? Status
{
get;
set;
}
/// <summary>
/// The build result.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildResult? Result
{
get;
set;
}
/// <summary>
/// The time that the build was queued.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime? QueueTime
{
get;
set;
}
/// <summary>
/// The time that the build was started.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime? StartTime
{
get;
set;
}
/// <summary>
/// The time that the build was completed.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime? FinishTime
{
get;
set;
}
/// <summary>
/// The links to other objects related to this object.
/// </summary>
public ReferenceLinks Links
{
get
{
if (m_links == null)
{
m_links = new ReferenceLinks();
}
return m_links;
}
}
[DataMember(Name = "_links", EmitDefaultValue = false)]
private ReferenceLinks m_links;
#endregion
/// <summary>
/// The REST URL of the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Url
{
get;
set;
}
/// <summary>
/// The definition associated with the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DefinitionReference Definition
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The build number revision.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32? BuildNumberRevision
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The team project.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public TeamProjectReference Project
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The URI of the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Uri Uri
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The source branch.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String SourceBranch
{
get;
set;
}
/// <summary>
/// The source version.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String SourceVersion
{
get;
set;
}
/// <summary>
/// The queue. This is only set if the definition type is Build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public AgentPoolQueue Queue
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The agent specification for the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public AgentSpecification AgentSpecification
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The current position of the build in the queue.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32? QueuePosition
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The build's priority.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public QueuePriority Priority
{
get;
set;
}
/// <summary>
/// The reason that the build was created.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildReason Reason
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The identity on whose behalf the build was queued.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IdentityRef RequestedFor
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The identity that queued the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IdentityRef RequestedBy
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The date the build was last changed.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime LastChangedDate
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The identity representing the process or person that last changed the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IdentityRef LastChangedBy
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The date the build was deleted.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime? DeletedDate
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The identity of the process or person that deleted the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IdentityRef DeletedBy
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The description of how the build was deleted.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String DeletedReason
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The parameters for the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Parameters
{
get;
set;
}
/// <summary>
/// A list of demands that represents the agent capabilities required by this build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public List<Demand> Demands
{
get;
set;
}
/// <summary>
/// The orchestration plan for the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public TaskOrchestrationPlanReference OrchestrationPlan
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The list of Orchestration plans associated with the build.
/// </summary>
/// <remarks>
/// The build may have plans in addition to the main plan. For example, the cleanup job may have an orchestration plan associated with it.
/// </remarks>
public List<TaskOrchestrationPlanReference> Plans
{
get
{
if (m_plans == null)
{
m_plans = new List<TaskOrchestrationPlanReference>();
}
return m_plans;
}
set
{
m_plans = value;
}
}
/// <summary>
/// Information about the build logs.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildLogReference Logs
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The repository.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildRepository Repository
{
get;
set;
}
/// <summary>
/// Additional options for queueing the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public QueueOptions QueueOptions
{
get;
set;
}
/// <summary>
/// Indicates whether the build has been deleted.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean Deleted
{
get;
set;
}
/// <summary>
/// A collection of properties which may be used to extend the storage fields available
/// for a given build.
/// </summary>
public PropertiesCollection Properties
{
get
{
if (m_properties == null)
{
m_properties = new PropertiesCollection();
}
return m_properties;
}
internal set
{
m_properties = value;
}
}
/// <summary>
/// A collection of tags associated with the build.
/// </summary>
public List<String> Tags
{
get
{
if (m_tags == null)
{
m_tags = new List<String>();
}
return m_tags;
}
internal set
{
m_tags = value;
}
}
/// <summary>
/// The list of validation errors and warnings.
/// </summary>
public List<BuildRequestValidationResult> ValidationResults
{
get
{
if (m_validationResults == null)
{
m_validationResults = new List<BuildRequestValidationResult>();
}
return m_validationResults;
}
}
/// <summary>
/// Indicates whether the build should be skipped by retention policies.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean? KeepForever
{
get;
set;
}
/// <summary>
/// The quality of the xaml build (good, bad, etc.)
/// </summary>
/// <remarks>
/// This is only used for XAML builds.
/// </remarks>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public String Quality
{
get;
set;
}
/// <summary>
/// Indicates whether the build is retained by a release.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean? RetainedByRelease
{
get;
set;
}
/// <summary>
/// The build that triggered this build via a Build completion trigger.
/// </summary>
[DataMember]
public Build TriggeredByBuild { get; set; }
/// <summary>
/// Trigger-specific information about the build.
/// </summary>
public IDictionary<String, String> TriggerInfo
{
get
{
if (m_triggerInfo == null)
{
m_triggerInfo = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
}
return m_triggerInfo;
}
internal set
{
if (value != null)
{
m_triggerInfo = new Dictionary<String, String>(value, StringComparer.OrdinalIgnoreCase);
}
}
}
[DataMember(EmitDefaultValue = false, Name = "Properties")]
private PropertiesCollection m_properties;
[DataMember(EmitDefaultValue = false, Name = "Tags")]
private List<String> m_tags;
[DataMember(EmitDefaultValue = false, Name = "ValidationResults")]
private List<BuildRequestValidationResult> m_validationResults;
/// <summary>
/// Orchestration plans associated with the build (build, cleanup)
/// </summary>
[DataMember(EmitDefaultValue = false, Name = "Plans")]
private List<TaskOrchestrationPlanReference> m_plans;
/// <summary>
/// Sourceprovider-specific information about what triggered the build
/// </summary>
/// <remarks>Added in 3.2-preview.3</remarks>
[DataMember(EmitDefaultValue = false, Name = "TriggerInfo")]
private Dictionary<String, String> m_triggerInfo;
#region ISecuredObject implementation
Guid ISecuredObject.NamespaceId => Security.BuildNamespaceId;
Int32 ISecuredObject.RequiredPermissions => BuildPermissions.ViewBuilds;
String ISecuredObject.GetToken()
{
if (!String.IsNullOrEmpty(m_nestingToken))
{
return m_nestingToken;
}
return ((ISecuredObject)this.Definition)?.GetToken();
}
internal void SetNestingSecurityToken(String tokenValue)
{
// Spike: investigate imposing restrictions on the amount of information being returned
// when a nesting security token is being used.
m_nestingToken = tokenValue;
}
private String m_nestingToken = String.Empty;
#endregion
}
}

View File

@@ -1,36 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a build badge.
/// </summary>
[DataContract]
public class BuildBadge
{
public BuildBadge()
{
}
/// <summary>
/// The ID of the build represented by this badge.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 BuildId
{
get;
set;
}
/// <summary>
/// A link to the SVG resource.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String ImageUrl
{
get;
set;
}
}
}

View File

@@ -1,365 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Common.Contracts;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a build definition.
/// </summary>
[DataContract]
public class BuildDefinition : BuildDefinitionReference
{
public BuildDefinition()
{
this.JobAuthorizationScope = BuildAuthorizationScope.ProjectCollection;
}
/// <summary>
/// The build number format.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String BuildNumberFormat
{
get;
set;
}
/// <summary>
/// A save-time comment for the definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Comment
{
get;
set;
}
/// <summary>
/// The description.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Description
{
get;
set;
}
/// <summary>
/// The drop location for the definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String DropLocation
{
get;
set;
}
/// <summary>
/// The job authorization scope for builds queued against this definition.
/// </summary>
[DataMember]
public BuildAuthorizationScope JobAuthorizationScope
{
get;
set;
}
/// <summary>
/// The job execution timeout (in minutes) for builds queued against this definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 JobTimeoutInMinutes
{
get;
set;
}
/// <summary>
/// The job cancel timeout (in minutes) for builds cancelled by user for this definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 JobCancelTimeoutInMinutes
{
get;
set;
}
/// <summary>
/// Indicates whether badges are enabled for this definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean BadgeEnabled
{
get;
set;
}
/// <summary>
/// The list of steps for this definition.
/// </summary>
[Obsolete]
[EditorBrowsable(EditorBrowsableState.Never)]
public List<BuildDefinitionStep> Steps
{
get;
}
/// <summary>
/// The build process.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildProcess Process
{
get;
set;
}
/// <summary>
/// A list of build options used by this definition.
/// </summary>
public List<BuildOption> Options
{
get
{
if (m_options == null)
{
m_options = new List<BuildOption>();
}
return m_options;
}
internal set
{
m_options = value;
}
}
/// <summary>
/// The repository.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildRepository Repository
{
get;
set;
}
/// <summary>
/// The process parameters for this definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public ProcessParameters ProcessParameters
{
get;
set;
}
/// <summary>
/// The list of triggers for this definition.
/// </summary>
public List<BuildTrigger> Triggers
{
get
{
if (m_triggers == null)
{
m_triggers = new List<BuildTrigger>();
}
return m_triggers;
}
internal set
{
m_triggers = value;
}
}
/// <summary>
/// The variables used by this definition.
/// </summary>
public IDictionary<String, BuildDefinitionVariable> Variables
{
get
{
if (m_variables == null)
{
m_variables = new Dictionary<String, BuildDefinitionVariable>(StringComparer.OrdinalIgnoreCase);
}
return m_variables;
}
internal set
{
m_variables = new Dictionary<String, BuildDefinitionVariable>(value, StringComparer.OrdinalIgnoreCase);
}
}
/// <summary>
/// The variable groups used by this definition.
/// </summary>
public List<VariableGroup> VariableGroups
{
get
{
if (m_variableGroups == null)
{
m_variableGroups = new List<VariableGroup>();
}
return m_variableGroups;
}
internal set
{
m_variableGroups = value;
}
}
/// <summary>
/// The list of demands that represents the capabilities required by all agents for this definition.
/// </summary>
public List<Demand> Demands
{
get
{
if (m_demands == null)
{
m_demands = new List<Demand>();
}
return m_demands;
}
internal set
{
m_demands = value;
}
}
/// <summary>
/// The list of retention policies for this definition.
/// </summary>
public List<RetentionPolicy> RetentionRules
{
get
{
if (m_retentionRules == null)
{
m_retentionRules = new List<RetentionPolicy>();
}
return m_retentionRules;
}
internal set
{
m_retentionRules = value;
}
}
/// <summary>
/// A collection of properties which may be used to extend the storage fields available
/// for a given definition.
/// </summary>
public PropertiesCollection Properties
{
get
{
if (m_properties == null)
{
m_properties = new PropertiesCollection();
}
return m_properties;
}
internal set
{
m_properties = value;
}
}
/// <summary>
/// A collection of tags associated with the build definition.
/// </summary>
public List<String> Tags
{
get
{
if (m_tags == null)
{
m_tags = new List<String>();
}
return m_tags;
}
internal set
{
m_tags = value;
}
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
SerializationHelper.Copy(ref m_serializedOptions, ref m_options, true);
SerializationHelper.Copy(ref m_serializedTriggers, ref m_triggers, true);
SerializationHelper.Copy(ref m_serializedVariables, ref m_variables, StringComparer.OrdinalIgnoreCase, true);
SerializationHelper.Copy(ref m_serializedVariableGroups, ref m_variableGroups, true);
SerializationHelper.Copy(ref m_serializedDemands, ref m_demands, true);
SerializationHelper.Copy(ref m_serializedRetentionRules, ref m_retentionRules, true);
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
SerializationHelper.Copy(ref m_options, ref m_serializedOptions);
SerializationHelper.Copy(ref m_triggers, ref m_serializedTriggers);
SerializationHelper.Copy(ref m_variables, ref m_serializedVariables, StringComparer.OrdinalIgnoreCase);
SerializationHelper.Copy(ref m_variableGroups, ref m_serializedVariableGroups);
SerializationHelper.Copy(ref m_demands, ref m_serializedDemands);
SerializationHelper.Copy(ref m_retentionRules, ref m_serializedRetentionRules);
}
[OnSerialized]
private void OnSerialized(StreamingContext context)
{
m_serializedOptions = null;
m_serializedTriggers = null;
m_serializedVariables = null;
m_serializedVariableGroups = null;
m_serializedRetentionRules = null;
}
[DataMember(Name = "Options", EmitDefaultValue = false)]
private List<BuildOption> m_serializedOptions;
[DataMember(Name = "Triggers", EmitDefaultValue = false)]
private List<BuildTrigger> m_serializedTriggers;
[DataMember(Name = "Variables", EmitDefaultValue = false)]
private IDictionary<String, BuildDefinitionVariable> m_serializedVariables;
[DataMember(Name = "VariableGroups", EmitDefaultValue = false)]
private List<VariableGroup> m_serializedVariableGroups;
[DataMember(Name = "Demands", EmitDefaultValue = false)]
private List<Demand> m_serializedDemands;
[DataMember(Name = "RetentionRules", EmitDefaultValue = false)]
private List<RetentionPolicy> m_serializedRetentionRules;
[DataMember(IsRequired = false, EmitDefaultValue = false, Name = "Properties")]
private PropertiesCollection m_properties;
[DataMember(EmitDefaultValue = false, Name = "Tags")]
private List<String> m_tags;
private List<Demand> m_demands;
private List<BuildOption> m_options;
private List<BuildTrigger> m_triggers;
private List<RetentionPolicy> m_retentionRules;
private List<VariableGroup> m_variableGroups;
private IDictionary<String, BuildDefinitionVariable> m_variables;
}
}

View File

@@ -1,162 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a reference to a build definition.
/// </summary>
[DataContract]
public class BuildDefinitionReference : DefinitionReference
{
public BuildDefinitionReference()
{
Type = DefinitionType.Build;
QueueStatus = DefinitionQueueStatus.Enabled;
}
/// <summary>
/// The quality of the definition document (draft, etc.)
/// </summary>
[DataMember(EmitDefaultValue = false, Name = "Quality")]
public DefinitionQuality? DefinitionQuality
{
get;
set;
}
/// <summary>
/// The author of the definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IdentityRef AuthoredBy
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// A reference to the definition that this definition is a draft of, if this is a draft definition.
/// </summary>
[DataMember(EmitDefaultValue = false, Name = "draftOf")]
public DefinitionReference ParentDefinition
{
get;
set;
}
/// <summary>
/// The list of drafts associated with this definition, if this is not a draft definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public List<DefinitionReference> Drafts
{
get
{
return m_drafts ?? (m_drafts = new List<DefinitionReference>());
}
internal set
{
m_drafts = value;
}
}
/// <summary>
/// The default queue for builds run against this definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public AgentPoolQueue Queue
{
get;
set;
}
/// <summary>
/// The metrics for this definition.
/// </summary>
public List<BuildMetric> Metrics
{
get
{
return m_metrics ?? (m_metrics = new List<BuildMetric>());
}
internal set
{
m_metrics = value;
}
}
/// <summary>
/// The latest build for this definition.
/// </summary>
public Build LatestBuild
{
get
{
return m_latestBuild;
}
internal set
{
m_latestBuild = value;
}
}
/// <summary>
/// The latest completed build for this definition.
/// </summary>
public Build LatestCompletedBuild
{
get
{
return m_latestCompletedBuild;
}
internal set
{
m_latestCompletedBuild = value;
}
}
/// <summary>
/// The links to other objects related to this object.
/// </summary>
public ReferenceLinks Links => m_links ?? (m_links = new ReferenceLinks());
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
SerializationHelper.Copy(ref m_serializedMetrics, ref m_metrics, true);
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
SerializationHelper.Copy(ref m_metrics, ref m_serializedMetrics);
}
[OnSerialized]
private void OnSerialized(StreamingContext context)
{
m_serializedMetrics = null;
}
[DataMember(Name = "Metrics", EmitDefaultValue = false)]
private List<BuildMetric> m_serializedMetrics;
[DataMember(Name = "_links", EmitDefaultValue = false)]
private ReferenceLinks m_links;
private List<BuildMetric> m_metrics;
private List<DefinitionReference> m_drafts;
[DataMember(EmitDefaultValue = false, Name = "LatestBuild")]
private Build m_latestBuild;
[DataMember(EmitDefaultValue = false, Name = "LatestCompletedBuild")]
private Build m_latestCompletedBuild;
}
}

View File

@@ -1,86 +0,0 @@
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a revision of a build definition.
/// </summary>
[DataContract]
public class BuildDefinitionRevision
{
/// <summary>
/// The revision number.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 Revision
{
get;
set;
}
/// <summary>
/// The name of the definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Name
{
get;
set;
}
/// <summary>
/// The identity of the person or process that changed the definition.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false, Order = 30)]
public IdentityRef ChangedBy
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The date and time that the definition was changed.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime ChangedDate
{
get;
set;
}
/// <summary>
/// The change type (add, edit, delete).
/// </summary>
[DataMember(EmitDefaultValue = false)]
public AuditAction ChangeType
{
get;
set;
}
/// <summary>
/// The comment associated with the change.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Comment
{
get;
set;
}
/// <summary>
/// A link to the definition at this revision.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String DefinitionUrl
{
get;
set;
}
}
}

View File

@@ -1,250 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using GitHub.Services.Common;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// A reference to a task definition.
/// </summary>
[DataContract]
public class TaskDefinitionReference : BaseSecuredObject
{
public TaskDefinitionReference()
{
}
public TaskDefinitionReference(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The ID of the task.
/// </summary>
[DataMember(IsRequired = true)]
public Guid Id
{
get;
set;
}
/// <summary>
/// The version of the task.
/// </summary>
[DataMember(IsRequired = true)]
public String VersionSpec
{
get;
set;
}
/// <summary>
/// The type of task (task or task group).
/// </summary>
[DataMember(IsRequired = false)]
public String DefinitionType
{
get;
set;
}
/// <summary>
/// A clone of this reference.
/// </summary>
/// <returns></returns>
public TaskDefinitionReference Clone()
{
return (TaskDefinitionReference)this.MemberwiseClone();
}
}
/// <summary>
/// Represents a step in a build phase.
/// </summary>
[DataContract]
public class BuildDefinitionStep : BaseSecuredObject
{
public BuildDefinitionStep()
{
}
internal BuildDefinitionStep(
ISecuredObject securedObject)
: base(securedObject)
{
}
private BuildDefinitionStep(BuildDefinitionStep toClone)
{
ArgumentUtility.CheckForNull(toClone, nameof(toClone));
this.Enabled = toClone.Enabled;
this.ContinueOnError = toClone.ContinueOnError;
this.AlwaysRun = toClone.AlwaysRun;
this.DisplayName = toClone.DisplayName;
this.TimeoutInMinutes = toClone.TimeoutInMinutes;
this.Condition = toClone.Condition;
this.RefName = toClone.RefName;
// Cloning the reference type variables since memberwiseclone does a shallow copy
if (toClone.TaskDefinition != null)
{
this.TaskDefinition = toClone.TaskDefinition.Clone();
}
if (toClone.m_inputs != null)
{
foreach (var property in toClone.m_inputs)
{
this.Inputs.Add(property.Key, property.Value);
}
}
if (toClone.m_environment != null)
{
foreach (var property in toClone.m_environment)
{
this.Environment.Add(property.Key, property.Value);
}
}
}
/// <summary>
/// The task associated with this step.
/// </summary>
[DataMember(IsRequired = true, Order = 1, Name = "Task")]
public TaskDefinitionReference TaskDefinition
{
get;
set;
}
/// <summary>
/// The inputs used by this step.
/// </summary>
public IDictionary<String, String> Inputs
{
get
{
if (m_inputs == null)
{
m_inputs = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
}
return m_inputs;
}
set
{
m_inputs = new Dictionary<String, String>(value, StringComparer.OrdinalIgnoreCase);
}
}
/// <summary>
/// Indicates whether the step is enabled.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public Boolean Enabled
{
get;
set;
}
/// <summary>
/// Indicates whether the phase should continue even if this step fails.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public Boolean ContinueOnError
{
get;
set;
}
/// <summary>
/// Indicates whether this step should run even if a previous step fails.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public Boolean AlwaysRun
{
get;
set;
}
/// <summary>
/// The display name for this step.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String DisplayName
{
get;
set;
}
/// <summary>
/// The time, in minutes, that this step is allowed to run.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public Int32 TimeoutInMinutes
{
get;
set;
}
/// <summary>
/// A condition that determines whether this step should run.
/// </summary>
[DataMember(EmitDefaultValue = false, IsRequired = false)]
public String Condition
{
get;
set;
}
/// <summary>
/// The reference name for this step.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String RefName
{
get;
set;
}
/// <summary>
/// The run-time environment for this step.
/// </summary>
public IDictionary<String, String> Environment
{
get
{
if (m_environment == null)
{
m_environment = new Dictionary<String, String>(StringComparer.Ordinal);
}
return m_environment;
}
set
{
m_environment = new Dictionary<String, String>(value, StringComparer.Ordinal);
}
}
/// <summary>
/// A clone of this step.
/// </summary>
/// <returns></returns>
public BuildDefinitionStep Clone()
{
return new BuildDefinitionStep(this);
}
[DataMember(Name = "Environment", EmitDefaultValue = false)]
private Dictionary<String, String> m_environment;
[DataMember(Name = "Inputs", EmitDefaultValue = false, Order = 2)]
private Dictionary<String, String> m_inputs;
}
}

View File

@@ -1,122 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a template from which new build definitions can be created.
/// </summary>
[DataContract]
public class BuildDefinitionTemplate
{
public BuildDefinitionTemplate()
{
Category = "Custom";
}
/// <summary>
/// The ID of the template.
/// </summary>
[DataMember(IsRequired = true)]
public String Id
{
get;
set;
}
/// <summary>
/// The name of the template.
/// </summary>
[DataMember(IsRequired = true)]
public String Name
{
get;
set;
}
/// <summary>
/// Indicates whether the template can be deleted.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public Boolean CanDelete
{
get;
set;
}
/// <summary>
/// The template category.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public String Category
{
get;
set;
}
/// <summary>
/// An optional hosted agent queue for the template to use by default.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public String DefaultHostedQueue
{
get;
set;
}
/// <summary>
/// The ID of the task whose icon is used when showing this template in the UI.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Guid IconTaskId
{
get;
set;
}
/// <summary>
/// A description of the template.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Description
{
get;
set;
}
/// <summary>
/// The actual template.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildDefinition Template
{
get;
set;
}
/// <summary>
/// A dictionary of media type strings to icons for this template.
/// </summary>
public IDictionary<String, String> Icons
{
get
{
if (m_icons == null)
{
m_icons = new Dictionary<String, String>(StringComparer.Ordinal);
}
return m_icons;
}
internal set
{
m_icons = new Dictionary<String, String>(value, StringComparer.Ordinal);
}
}
[DataMember(EmitDefaultValue = false, Name = "Icons")]
private Dictionary<String, String> m_icons;
}
}

View File

@@ -1,69 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a variable used by a build definition.
/// </summary>
[DataContract]
public class BuildDefinitionVariable : BaseSecuredObject
{
public BuildDefinitionVariable()
{
}
internal BuildDefinitionVariable(
ISecuredObject securedObject)
: base(securedObject)
{
}
private BuildDefinitionVariable(BuildDefinitionVariable variableToClone)
{
Value = variableToClone.Value;
AllowOverride = variableToClone.AllowOverride;
IsSecret = variableToClone.IsSecret;
}
/// <summary>
/// The value of the variable.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public String Value
{
get;
set;
}
/// <summary>
/// Indicates whether the value can be set at queue time.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean AllowOverride
{
get;
set;
}
/// <summary>
/// Indicates whether the variable's value is a secret.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean IsSecret
{
get;
set;
}
/// <summary>
/// A clone of this BuildDefinitionVariable.
/// </summary>
/// <returns>A new BuildDefinitionVariable</returns>
public BuildDefinitionVariable Clone()
{
return new BuildDefinitionVariable(this);
}
}
}

View File

@@ -1,53 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a build log.
/// </summary>
[DataContract]
public class BuildLog : BuildLogReference
{
public BuildLog()
{
}
public BuildLog(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The number of lines in the log.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int64 LineCount
{
get;
set;
}
/// <summary>
/// The date and time the log was created.
/// </summary>
[DataMember]
public DateTime? CreatedOn
{
get;
set;
}
/// <summary>
/// The date and time the log was last changed.
/// </summary>
[DataMember]
public DateTime? LastChangedOn
{
get;
set;
}
}
}

View File

@@ -1,54 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a reference to a build log.
/// </summary>
[DataContract]
public class BuildLogReference : BaseSecuredObject
{
public BuildLogReference()
{
}
internal BuildLogReference(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The ID of the log.
/// </summary>
// EmitDefaultValue is true to ensure that id = 0 is sent for XAML builds' "ActivityLog.xml"
[DataMember(IsRequired = false, EmitDefaultValue = true)]
public Int32 Id
{
get;
set;
}
/// <summary>
/// The type of the log location.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public String Type
{
get;
set;
}
/// <summary>
/// A full link to the log resource.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public String Url
{
get;
set;
}
}
}

View File

@@ -1,63 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents metadata about builds in the system.
/// </summary>
[DataContract]
public class BuildMetric : BaseSecuredObject
{
public BuildMetric()
{
}
internal BuildMetric(
ISecuredObject securedObject)
:base(securedObject)
{
}
/// <summary>
/// The name of the metric.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Name
{
get;
set;
}
/// <summary>
/// The scope.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Scope
{
get;
set;
}
/// <summary>
/// The value.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public Int32 IntValue
{
get;
set;
}
/// <summary>
/// The date for the scope.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime? Date
{
get;
set;
}
}
}

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents the application of an optional behavior to a build definition.
/// </summary>
[DataContract]
public class BuildOption : BaseSecuredObject
{
public BuildOption()
{
}
internal BuildOption(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// A reference to the build option.
/// </summary>
[DataMember(IsRequired = true, Order = 1, Name = "Definition")]
public virtual BuildOptionDefinitionReference BuildOptionDefinition
{
get;
set;
}
/// <summary>
/// The inputs that configure the behavior.
/// </summary>
public virtual IDictionary<String, String> Inputs
{
get
{
if (m_inputs == null)
{
m_inputs = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
}
return m_inputs;
}
internal set
{
m_inputs = new Dictionary<String, String>(value, StringComparer.OrdinalIgnoreCase);
}
}
/// <summary>
/// Indicates whether the behavior is enabled.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public virtual Boolean Enabled
{
get;
set;
}
[DataMember(Name = "Inputs", EmitDefaultValue = false, Order = 2)]
private Dictionary<String, String> m_inputs;
}
}

View File

@@ -1,100 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents an optional behavior that can be applied to a build definition.
/// </summary>
[DataContract]
public class BuildOptionDefinition : BuildOptionDefinitionReference
{
public BuildOptionDefinition()
{
}
internal BuildOptionDefinition(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// A value that indicates the relative order in which the behavior should be applied.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 Ordinal
{
get;
set;
}
/// <summary>
/// The name of the build option.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Name
{
get;
set;
}
/// <summary>
/// The description.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Description
{
get;
set;
}
/// <summary>
/// The list of inputs defined for the build option.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IList<BuildOptionInputDefinition> Inputs
{
get
{
if (m_inputs == null)
{
m_inputs = new List<BuildOptionInputDefinition>();
}
return m_inputs;
}
set
{
m_inputs = new List<BuildOptionInputDefinition>(value);
}
}
/// <summary>
/// The list of input groups defined for the build option.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IList<BuildOptionGroupDefinition> Groups
{
get
{
if (m_groups == null)
{
m_groups = new List<BuildOptionGroupDefinition>();
}
return m_groups;
}
set
{
m_groups = new List<BuildOptionGroupDefinition>(value);
}
}
private List<BuildOptionInputDefinition> m_inputs;
private List<BuildOptionGroupDefinition> m_groups;
}
}

View File

@@ -1,33 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a reference to a build option definition.
/// </summary>
[DataContract]
public class BuildOptionDefinitionReference : BaseSecuredObject
{
public BuildOptionDefinitionReference()
{
}
internal BuildOptionDefinitionReference(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The ID of the referenced build option.
/// </summary>
[DataMember(IsRequired = true, Order = 1)]
public Guid Id
{
get;
set;
}
}
}

View File

@@ -1,53 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a group of inputs for a build option.
/// </summary>
[DataContract]
public class BuildOptionGroupDefinition : BaseSecuredObject
{
public BuildOptionGroupDefinition()
{
}
internal BuildOptionGroupDefinition(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The internal name of the group.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Name
{
get;
set;
}
/// <summary>
/// The name of the group to display in the UI.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String DisplayName
{
get;
set;
}
/// <summary>
/// Indicates whether the group is initially displayed as expanded in the UI.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean IsExpanded
{
get;
set;
}
}
}

View File

@@ -1,144 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents an input for a build option.
/// </summary>
[DataContract]
public class BuildOptionInputDefinition : BaseSecuredObject
{
public BuildOptionInputDefinition()
: this(null)
{
}
internal BuildOptionInputDefinition(
ISecuredObject securedObject)
: base(securedObject)
{
InputType = BuildOptionInputType.String;
DefaultValue = String.Empty;
Required = false;
}
/// <summary>
/// The name of the input.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Name
{
get;
set;
}
/// <summary>
/// The label for the input.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Label
{
get;
set;
}
/// <summary>
/// The default value.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String DefaultValue
{
get;
set;
}
/// <summary>
/// Indicates whether the input is required to have a value.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean Required
{
get;
set;
}
/// <summary>
/// Indicates the type of the input value.
/// </summary>
[DataMember(Name = "Type")]
public BuildOptionInputType InputType
{
get;
set;
}
/// <summary>
/// The rule that is applied to determine whether the input is visible in the UI.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String VisibleRule
{
// Typical format is "NAMEOFTHEDEPENDENTINPUT = VALUETOBEBOUND"
get;
set;
}
/// <summary>
/// The name of the input group that this input belongs to.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String GroupName
{
get;
set;
}
/// <summary>
/// A dictionary of options for this input.
/// </summary>
public Dictionary<String, String> Options
{
get
{
if (m_Options == null)
{
m_Options = new Dictionary<String, String>();
}
return m_Options;
}
set
{
m_Options = value;
}
}
/// <summary>
/// A dictionary of help documents for this input.
/// </summary>
public Dictionary<String, String> HelpDocuments
{
get
{
if (m_HelpDocuments == null)
{
m_HelpDocuments = new Dictionary<String, String>();
}
return m_HelpDocuments;
}
set
{
m_HelpDocuments = new Dictionary<String, String>(value);
}
}
[DataMember(Name = "Options", EmitDefaultValue = false)]
private Dictionary<String, String> m_Options;
[DataMember(Name = "Help", EmitDefaultValue = false)]
private Dictionary<String, String> m_HelpDocuments;
}
}

View File

@@ -1,45 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a build process.
/// </summary>
[DataContract]
[KnownType(typeof(DesignerProcess))]
[KnownType(typeof(YamlProcess))]
[KnownType(typeof(DockerProcess))]
[KnownType(typeof(JustInTimeProcess))]
[JsonConverter(typeof(BuildProcessJsonConverter))]
public class BuildProcess : BaseSecuredObject
{
protected BuildProcess(
Int32 type)
{
}
protected internal BuildProcess(
Int32 type,
ISecuredObject securedObject)
: base(securedObject)
{
this.Type = type;
}
/// <summary>
/// The type of the process.
/// </summary>
/// <remarks>
/// See <see cref="ProcessType" /> for a list of valid process types.
/// </remarks>
[DataMember(Name = "Type")]
public Int32 Type
{
get;
internal set;
}
}
}

View File

@@ -1,68 +0,0 @@
using System;
using Newtonsoft.Json.Linq;
namespace GitHub.Build.WebApi
{
internal sealed class BuildProcessJsonConverter : TypePropertyJsonConverter<BuildProcess>
{
protected override BuildProcess GetInstance(
Type objectType)
{
if (objectType == typeof(DesignerProcess))
{
return new DesignerProcess();
}
else if (objectType == typeof(YamlProcess))
{
return new YamlProcess();
}
else if (objectType == typeof(DockerProcess))
{
return new DockerProcess();
}
else if (objectType == typeof(JustInTimeProcess))
{
return new JustInTimeProcess();
}
else
{
return base.GetInstance(objectType);
}
}
protected override BuildProcess GetInstance(
Int32 targetType)
{
switch (targetType)
{
case ProcessType.Yaml:
return new YamlProcess();
case ProcessType.Docker:
return new DockerProcess();
case ProcessType.JustInTime:
return new JustInTimeProcess();
case ProcessType.Designer:
default:
return new DesignerProcess();
}
}
protected override Boolean TryInferType(
JObject value,
out Int32 type)
{
// if it has a YamlFilename property, assume it's a YamlProcess
if (value.TryGetValue("yamlFilename", StringComparison.OrdinalIgnoreCase, out JToken yamlFilename))
{
type = ProcessType.Yaml;
return true;
}
else
{
// default to Designer process
type = ProcessType.Designer;
return true;
}
}
}
}

View File

@@ -1,247 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents resources used by a build process.
/// </summary>
[DataContract]
public sealed class BuildProcessResources : BaseSecuredObject
{
public BuildProcessResources()
{
}
internal BuildProcessResources(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// Information about the queues used by the process.
/// </summary>
public IList<AgentPoolQueueReference> Queues
{
get
{
if (m_queues == null)
{
m_queues = new List<AgentPoolQueueReference>();
}
return m_queues;
}
set
{
m_queues = new List<AgentPoolQueueReference>(value);
}
}
/// <summary>
/// Information about the endpoints used by the process.
/// </summary>
public IList<ServiceEndpointReference> Endpoints
{
get
{
if (m_endpoints == null)
{
m_endpoints = new List<ServiceEndpointReference>();
}
return m_endpoints;
}
set
{
m_endpoints = new List<ServiceEndpointReference>(value);
}
}
/// <summary>
/// Information about the secure files used by the process.
/// </summary>
public IList<SecureFileReference> Files
{
get
{
if (m_files == null)
{
m_files = new List<SecureFileReference>();
}
return m_files;
}
set
{
m_files = new List<SecureFileReference>(value);
}
}
/// <summary>
/// Information about the variable groups used by the process.
/// </summary>
public IList<VariableGroupReference> VariableGroups
{
get
{
if (m_variableGroups == null)
{
m_variableGroups = new List<VariableGroupReference>();
}
return m_variableGroups;
}
set
{
m_variableGroups = new List<VariableGroupReference>(value);
}
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_queues?.Count == 0)
{
m_queues = null;
}
if (m_endpoints?.Count == 0)
{
m_endpoints = null;
}
if (m_files?.Count == 0)
{
m_files = null;
}
if (m_variableGroups?.Count == 0)
{
m_variableGroups = null;
}
}
[DataMember(Name = "Queues", EmitDefaultValue = false)]
private List<AgentPoolQueueReference> m_queues;
[DataMember(Name = "Endpoints", EmitDefaultValue = false)]
private List<ServiceEndpointReference> m_endpoints;
[DataMember(Name = "Files", EmitDefaultValue = false)]
private List<SecureFileReference> m_files;
[DataMember(Name = "VariableGroups", EmitDefaultValue = false)]
private List<VariableGroupReference> m_variableGroups;
}
/// <summary>
/// Represents a reference to a resource.
/// </summary>
[DataContract]
public abstract class ResourceReference : BaseSecuredObject
{
public ResourceReference()
{
}
protected ResourceReference(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// An alias to be used when referencing the resource.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Alias
{
get;
set;
}
}
/// <summary>
/// Represents a reference to an agent queue.
/// </summary>
[DataContract]
public class AgentPoolQueueReference : ResourceReference
{
public AgentPoolQueueReference()
: this(null)
{
}
internal AgentPoolQueueReference(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The ID of the queue.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 Id
{
get;
set;
}
}
/// <summary>
/// Represents a referenec to a service endpoint.
/// </summary>
[DataContract]
public class ServiceEndpointReference : ResourceReference
{
public ServiceEndpointReference()
: this(null)
{
}
internal ServiceEndpointReference(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The ID of the service endpoint.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Guid Id
{
get;
set;
}
}
/// <summary>
/// Represents a reference to a secure file.
/// </summary>
[DataContract]
public class SecureFileReference : ResourceReference
{
public SecureFileReference()
: this(null)
{
}
internal SecureFileReference(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The ID of the secure file.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Guid Id
{
get;
set;
}
}
}

View File

@@ -1,138 +0,0 @@
using GitHub.Services.WebApi;
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a reference to a build.
/// </summary>
[DataContract]
public class BuildReference : BaseSecuredObject
{
public BuildReference()
{
}
internal BuildReference(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The ID of the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
[Key]
public Int32 Id
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// The build number.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String BuildNumber
{
get;
set;
}
/// <summary>
/// The build status.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildStatus? Status
{
get;
set;
}
/// <summary>
/// The build result.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildResult? Result
{
get;
set;
}
/// <summary>
/// The time that the build was queued.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime? QueueTime
{
get;
set;
}
/// <summary>
/// The time that the build was started.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime? StartTime
{
get;
set;
}
/// <summary>
/// The time that the build was completed.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime? FinishTime
{
get;
set;
}
/// <summary>
/// The identity on whose behalf the build was queued.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IdentityRef RequestedFor
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// Indicates whether the build has been deleted.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean Deleted
{
get;
set;
}
/// <summary>
/// The links to other objects related to this object.
/// </summary>
public ReferenceLinks Links
{
get
{
if (m_links == null)
{
m_links = new ReferenceLinks();
}
return m_links;
}
}
[DataMember(Name = "_links", EmitDefaultValue = false)]
private ReferenceLinks m_links;
}
}

View File

@@ -1,55 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents information about a build report.
/// </summary>
[DataContract]
public class BuildReportMetadata
{
public BuildReportMetadata()
{
}
public BuildReportMetadata(Int32 buildId, String type)
{
this.BuildId = buildId;
this.Type = type;
}
/// <summary>
/// The Id of the build.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 BuildId
{
get;
set;
}
/// <summary>
/// The content of the report.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Content
{
get;
set;
}
/// <summary>
/// The type of the report.
/// </summary>
/// <remarks>
/// See <see cref="ReportTypes" /> for a list of supported report types.
/// </remarks>
[DataMember(EmitDefaultValue = false)]
public String Type
{
get;
set;
}
}
}

View File

@@ -1,185 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a repository used by a build definition.
/// </summary>
[DataContract]
public class BuildRepository : BaseSecuredObject
{
public BuildRepository()
{
}
internal BuildRepository(
ISecuredObject securedObject)
: base(securedObject)
{
}
private BuildRepository(
BuildRepository toClone)
: base(toClone)
{
this.Id = toClone.Id;
this.Type = toClone.Type;
this.Name = toClone.Name;
this.Url = toClone.Url;
this.DefaultBranch = toClone.DefaultBranch;
this.RootFolder = toClone.RootFolder;
this.Clean = toClone.Clean;
this.CheckoutSubmodules = toClone.CheckoutSubmodules;
if (toClone.m_properties != null)
{
foreach (var property in toClone.m_properties)
{
this.Properties.Add(property.Key, property.Value);
}
}
}
/// <summary>
/// The ID of the repository.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Id
{
get;
set;
}
/// <summary>
/// The type of the repository.
/// </summary>
[DataMember]
public String Type
{
get;
set;
}
/// <summary>
/// The friendly name of the repository.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Name
{
get;
set;
}
/// <summary>
/// The URL of the repository.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Uri Url
{
get;
set;
}
/// <summary>
/// The name of the default branch.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String DefaultBranch
{
get;
set;
}
/// <summary>
/// The root folder.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String RootFolder
{
get;
set;
}
/// <summary>
/// Indicates whether to clean the target folder when getting code from the repository.
/// </summary>
/// <remarks>
/// This is a String so that it can reference variables.
/// </remarks>
[DataMember(EmitDefaultValue = true)]
public String Clean
{
get;
set;
}
/// <summary>
/// Indicates whether to checkout submodules.
/// </summary>
[DataMember(EmitDefaultValue = true)]
public Boolean CheckoutSubmodules
{
get;
set;
}
/// <summary>
/// A dictionary that holds additional information about the repository.
/// </summary>
public IDictionary<String, String> Properties
{
// Warning: This can contain secrets too. As part of #952656, we resolve secrets, it was done considering the fact that this is not a "DataMember"
// If it's ever made a "DataMember" please be cautious, we would be leaking secrets
get
{
if (m_properties == null)
{
m_properties = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);
}
return m_properties;
}
internal set
{
m_properties = new Dictionary<String, String>(value, StringComparer.OrdinalIgnoreCase);
}
}
/// <summary>
/// Clones this object.
/// </summary>
/// <returns></returns>
public BuildRepository Clone()
{
return new BuildRepository(this);
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
SerializationHelper.Copy(ref m_serializedProperties, ref m_properties, StringComparer.OrdinalIgnoreCase, true);
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
SerializationHelper.Copy(ref m_properties, ref m_serializedProperties, StringComparer.OrdinalIgnoreCase);
}
[OnSerialized]
private void OnSerialized(StreamingContext context)
{
m_serializedProperties = null;
}
[DataMember(Name = "Properties", EmitDefaultValue = false)]
private IDictionary<String, String> m_serializedProperties;
// Warning: This can contain secrets too. As part of #952656, we resolve secrets, it was done considering the fact that this is not a "DataMember"
// If it's ever made a "DataMember" please be cautious, we would be leaking secrets
private IDictionary<String, String> m_properties;
}
}

View File

@@ -1,43 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents the result of validating a build request.
/// </summary>
[DataContract]
public class BuildRequestValidationResult : BaseSecuredObject
{
public BuildRequestValidationResult()
{
}
public BuildRequestValidationResult(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The result.
/// </summary>
[DataMember]
public ValidationResult Result
{
get;
set;
}
/// <summary>
/// The message associated with the result.
/// </summary>
[DataMember]
public String Message
{
get;
set;
}
}
}

View File

@@ -1,64 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents information about resources used by builds in the system.
/// </summary>
[DataContract]
public sealed class BuildResourceUsage
{
internal BuildResourceUsage()
{
}
internal BuildResourceUsage(Int32 xaml, Int32 dtAgents, Int32 paidAgentSlots, Boolean isThrottlingEnabled = false)
{
this.XamlControllers = xaml;
this.DistributedTaskAgents = dtAgents;
this.TotalUsage = this.XamlControllers + (isThrottlingEnabled ? 0 : this.DistributedTaskAgents);
this.PaidPrivateAgentSlots = paidAgentSlots;
}
/// <summary>
/// The number of XAML controllers.
/// </summary>
[DataMember]
public Int32 XamlControllers
{
get;
internal set;
}
/// <summary>
/// The number of build agents.
/// </summary>
[DataMember]
public Int32 DistributedTaskAgents
{
get;
internal set;
}
/// <summary>
/// The total usage.
/// </summary>
[DataMember]
public Int32 TotalUsage
{
get;
internal set;
}
/// <summary>
/// The number of paid private agent slots.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 PaidPrivateAgentSlots
{
get;
internal set;
}
}
}

View File

@@ -1,54 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents system-wide build settings.
/// </summary>
[DataContract]
public class BuildSettings : BaseSecuredObject
{
public BuildSettings()
: this(null)
{
}
public BuildSettings(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The default retention policy.
/// </summary>
[DataMember]
public RetentionPolicy DefaultRetentionPolicy
{
get;
set;
}
/// <summary>
/// The maximum retention policy.
/// </summary>
[DataMember]
public RetentionPolicy MaximumRetentionPolicy
{
get;
set;
}
/// <summary>
/// The number of days to keep records of deleted builds.
/// </summary>
[DataMember]
public Int32 DaysToKeepDeletedBuildsBeforeDestroy
{
get;
set;
}
}
}

View File

@@ -1,446 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a trigger for a buld definition.
/// </summary>
[DataContract]
[KnownType(typeof(ContinuousIntegrationTrigger))]
[KnownType(typeof(GatedCheckInTrigger))]
[KnownType(typeof(ScheduleTrigger))]
[KnownType(typeof(PullRequestTrigger))]
[JsonConverter(typeof(BuildTriggerJsonConverter))]
public abstract class BuildTrigger : BaseSecuredObject
{
protected BuildTrigger(DefinitionTriggerType triggerType)
: this(triggerType, null)
{
}
protected internal BuildTrigger(
DefinitionTriggerType triggerType,
ISecuredObject securedObject)
: base(securedObject)
{
this.TriggerType = triggerType;
}
/// <summary>
/// The type of the trigger.
/// </summary>
[DataMember]
public DefinitionTriggerType TriggerType
{
get;
private set;
}
}
/// <summary>
/// Represents a continuous integration (CI) trigger.
/// </summary>
[DataContract]
public sealed class ContinuousIntegrationTrigger : BuildTrigger
{
public ContinuousIntegrationTrigger()
: this(null)
{
}
internal ContinuousIntegrationTrigger(
ISecuredObject securedObject)
: base(DefinitionTriggerType.ContinuousIntegration, securedObject)
{
MaxConcurrentBuildsPerBranch = 1;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public Int32 SettingsSourceType
{
get
{
if (m_settingsSourceType == 0)
{
m_settingsSourceType = WebApi.SettingsSourceType.Definition;
}
return m_settingsSourceType;
}
set
{
m_settingsSourceType = value;
}
}
/// <summary>
/// Indicates whether changes should be batched while another CI build is running.
/// </summary>
/// <remarks>
/// If this is true, then changes submitted while a CI build is running will be batched and built in one new CI build when the current build finishes.
/// If this is false, then a new CI build will be triggered for each change to the repository.
/// </remarks>
[DataMember]
public Boolean BatchChanges
{
get;
set;
}
/// <summary>
/// The maximum number of simultaneous CI builds that will run per branch.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public Int32 MaxConcurrentBuildsPerBranch
{
get;
set;
}
/// <summary>
/// A list of filters that describe which branches will trigger builds.
/// </summary>
public List<String> BranchFilters
{
get
{
if (m_branchFilters == null)
{
m_branchFilters = new List<String>();
}
return m_branchFilters;
}
internal set
{
m_branchFilters = value;
}
}
// added in 3.0
/// <summary>
/// A list of filters that describe which paths will trigger builds.
/// </summary>
public List<String> PathFilters
{
get
{
if (m_pathFilters == null)
{
m_pathFilters = new List<String>();
}
return m_pathFilters;
}
internal set
{
m_pathFilters = value;
}
}
/// <summary>
/// The polling interval, in seconds.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public Int32? PollingInterval
{
get;
set;
}
/// <summary>
/// The ID of the job used to poll an external repository.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public Guid PollingJobId
{
// This is the ID of the polling job that polls the external repository.
// Once the build definition is saved/updated, this value is set.
get;
set;
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_settingsSourceType == WebApi.SettingsSourceType.Definition)
{
m_settingsSourceType = 0;
}
}
[DataMember(Name = "BranchFilters", EmitDefaultValue = false)]
private List<String> m_branchFilters;
[DataMember(Name = "PathFilters", EmitDefaultValue = false)]
private List<String> m_pathFilters;
[DataMember(Name = "SettingsSourceType", EmitDefaultValue = false)]
private Int32 m_settingsSourceType;
}
/// <summary>
/// Represents a gated check-in trigger.
/// </summary>
[DataContract]
public sealed class GatedCheckInTrigger : BuildTrigger
{
public GatedCheckInTrigger()
: this(null)
{
}
internal GatedCheckInTrigger(
ISecuredObject securedObject)
: base(DefinitionTriggerType.GatedCheckIn, securedObject)
{
}
/// <summary>
/// Indicates whether CI triggers should run after the gated check-in succeeds.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean RunContinuousIntegration
{
get;
set;
}
/// <summary>
/// Indicates whether to take workspace mappings into account when determining whether a build should run.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean UseWorkspaceMappings
{
get;
set;
}
/// <summary>
/// A list of filters that describe which paths will trigger builds.
/// </summary>
public List<String> PathFilters
{
get
{
if (m_pathFilters == null)
{
m_pathFilters = new List<String>();
}
return m_pathFilters;
}
internal set
{
m_pathFilters = value;
}
}
[DataMember(Name = "PathFilters", EmitDefaultValue = false)]
private List<String> m_pathFilters;
}
/// <summary>
/// Represents a schedule trigger.
/// </summary>
[DataContract]
public sealed class ScheduleTrigger : BuildTrigger
{
public ScheduleTrigger()
: this(null)
{
}
internal ScheduleTrigger(
ISecuredObject securedObject)
: base(DefinitionTriggerType.Schedule, securedObject)
{
}
/// <summary>
/// A list of schedule entries that describe when builds should run.
/// </summary>
public List<Schedule> Schedules
{
get
{
if (m_schedules == null)
{
m_schedules = new List<Schedule>();
}
return m_schedules;
}
set
{
m_schedules = value;
}
}
[DataMember(Name = "Schedules", EmitDefaultValue = false)]
private List<Schedule> m_schedules;
}
/// <summary>
/// Represents a pull request trigger.
/// </summary>
[DataContract]
public class PullRequestTrigger : BuildTrigger
{
public PullRequestTrigger()
: this(null)
{
}
internal PullRequestTrigger(
ISecuredObject securedObject)
: base(DefinitionTriggerType.PullRequest, securedObject)
{
}
[EditorBrowsable(EditorBrowsableState.Never)]
public Int32 SettingsSourceType
{
get
{
if (m_settingsSourceType == 0)
{
m_settingsSourceType = WebApi.SettingsSourceType.Definition;
}
return m_settingsSourceType;
}
set
{
m_settingsSourceType = value;
}
}
/// <summary>
/// Describes if forks of a selected repository should build and use secrets.
/// </summary>
public Forks Forks
{
get
{
if (m_forks == null)
{
m_forks = new Forks();
}
return m_forks;
}
set
{
m_forks = value;
}
}
/// <summary>
/// A list of filters that describe which branches will trigger builds.
/// </summary>
public List<String> BranchFilters
{
get
{
if (m_branchFilters == null)
{
m_branchFilters = new List<String>();
}
return m_branchFilters;
}
set
{
m_branchFilters = value;
}
}
/// <summary>
/// A list of filters that describe which paths will trigger builds.
/// </summary>
public List<String> PathFilters
{
get
{
if (m_pathFilters == null)
{
m_pathFilters = new List<String>();
}
return m_pathFilters;
}
set
{
m_pathFilters = value;
}
}
/// <summary>
/// Indicates if an update to a PR should delete current in-progress builds.
/// </summary>
[DataMember(Name = "AutoCancel", EmitDefaultValue = false)]
public Boolean? AutoCancel { get; set; }
[DataMember(Name = "RequireCommentsForNonTeamMembersOnly")]
public Boolean RequireCommentsForNonTeamMembersOnly { get; set; }
[DataMember(Name = "IsCommentRequiredForPullRequest")]
public Boolean IsCommentRequiredForPullRequest { get; set; }
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_settingsSourceType == WebApi.SettingsSourceType.Definition)
{
m_settingsSourceType = 0;
}
}
[DataMember(Name = "SettingsSourceType", EmitDefaultValue = false)]
private Int32 m_settingsSourceType;
[DataMember(Name = "BranchFilters", EmitDefaultValue = false)]
private List<String> m_branchFilters;
[DataMember(Name = "Forks", EmitDefaultValue = false)]
private Forks m_forks;
[DataMember(Name = "PathFilters", EmitDefaultValue = false)]
private List<String> m_pathFilters;
}
/// <summary>
/// Represents a build completion trigger.
/// </summary>
[DataContract]
public class BuildCompletionTrigger : BuildTrigger
{
public BuildCompletionTrigger()
: this(null)
{
}
public BuildCompletionTrigger(
ISecuredObject securedObject)
: base(DefinitionTriggerType.BuildCompletion, securedObject)
{
}
/// <summary>
/// A reference to the definition that should trigger builds for this definition.
/// </summary>
[DataMember]
public DefinitionReference Definition { get; set; }
[DataMember]
public Boolean RequiresSuccessfulBuild { get; set; }
[DataMember]
public List<String> BranchFilters { get; set; }
}
}

View File

@@ -1,115 +0,0 @@
using System;
using System.Reflection;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace GitHub.Build.WebApi
{
internal sealed class BuildTriggerJsonConverter : VssSecureJsonConverter
{
public override Boolean CanConvert(Type objectType)
{
return typeof(BuildTrigger).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override Boolean CanRead
{
get
{
return true;
}
}
public override Boolean CanWrite
{
get
{
return false;
}
}
public override Object ReadJson(
JsonReader reader,
Type objectType,
Object existingValue,
JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartObject)
{
return null;
}
var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract;
if (contract == null)
{
return existingValue;
}
JsonProperty property = contract.Properties.GetClosestMatchProperty("TriggerType");
if (property == null)
{
return existingValue;
}
JToken itemTypeValue;
DefinitionTriggerType triggerType;
JObject value = JObject.Load(reader);
if (!value.TryGetValue(property.PropertyName, StringComparison.OrdinalIgnoreCase, out itemTypeValue))
{
return existingValue;
}
else
{
if (itemTypeValue.Type == JTokenType.Integer)
{
triggerType = (DefinitionTriggerType)(Int32)itemTypeValue;
}
else if (itemTypeValue.Type != JTokenType.String ||
!Enum.TryParse<DefinitionTriggerType>((String)itemTypeValue, true, out triggerType))
{
return existingValue;
}
}
Object returnValue = null;
switch (triggerType)
{
case DefinitionTriggerType.ContinuousIntegration:
returnValue = new ContinuousIntegrationTrigger();
break;
case DefinitionTriggerType.GatedCheckIn:
returnValue = new GatedCheckInTrigger();
break;
case DefinitionTriggerType.Schedule:
returnValue = new ScheduleTrigger();
break;
case DefinitionTriggerType.PullRequest:
returnValue = new PullRequestTrigger();
break;
case DefinitionTriggerType.BuildCompletion:
returnValue = new BuildCompletionTrigger();
break;
}
if (value != null && returnValue != null)
{
using (JsonReader objectReader = value.CreateReader())
{
serializer.Populate(objectReader, returnValue);
}
}
return returnValue;
}
public override void WriteJson(
JsonWriter writer,
Object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,68 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents an entry in a workspace mapping.
/// </summary>
[DataContract]
public class MappingDetails
{
/// <summary>
/// The server path.
/// </summary>
[DataMember(Name = "serverPath")]
public String ServerPath
{
get;
set;
}
/// <summary>
/// The mapping type.
/// </summary>
[DataMember(Name = "mappingType")]
public String MappingType
{
get;
set;
}
/// <summary>
/// The local path.
/// </summary>
[DataMember(Name = "localPath")]
public String LocalPath
{
get;
set;
}
}
/// <summary>
/// Represents a workspace mapping.
/// </summary>
[DataContract]
public class BuildWorkspace
{
/// <summary>
/// The list of workspace mapping entries.
/// </summary>
public List<MappingDetails> Mappings
{
get
{
if (m_mappings == null)
{
m_mappings = new List<MappingDetails>();
}
return m_mappings;
}
}
[DataMember(Name = "mappings")]
private List<MappingDetails> m_mappings;
}
}

View File

@@ -1,114 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a change associated with a build.
/// </summary>
[DataContract]
public class Change : BaseSecuredObject
{
public Change()
: this(null)
{
}
internal Change(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The identifier for the change. For a commit, this would be the SHA1. For a TFVC changeset, this would be the changeset ID.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Id
{
get;
set;
}
/// <summary>
/// The description of the change. This might be a commit message or changeset description.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Message
{
get;
set;
}
/// <summary>
/// The type of change. "commit", "changeset", etc.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Type
{
get;
set;
}
/// <summary>
/// The author of the change.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IdentityRef Author
{
get;
set;
}
/// <summary>
/// The timestamp for the change.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime? Timestamp
{
get;
set;
}
/// <summary>
/// The location of the full representation of the resource.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Uri Location
{
get;
set;
}
/// <summary>
/// Indicates whether the message was truncated.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean MessageTruncated
{
get;
set;
}
/// <summary>
/// The location of a user-friendly representation of the resource.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Uri DisplayUri
{
get;
set;
}
/// <summary>
/// The person or process that pushed the change.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Pusher
{
get;
set;
}
}
}

View File

@@ -1,195 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Core.WebApi;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a reference to a definition.
/// </summary>
[DataContract]
[KnownType(typeof(BuildDefinition))]
[KnownType(typeof(BuildDefinitionReference))]
[JsonConverter(typeof(DefinitionReferenceJsonConverter))]
#pragma warning disable 618
public class DefinitionReference : ShallowReference, ISecuredObject
#pragma warning restore 618
{
/// <summary>
/// The ID of the referenced definition.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public new Int32 Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
/// <summary>
/// The name of the referenced definition.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public new String Name
{
get
{
return base.Name;
}
set
{
base.Name = value;
}
}
/// <summary>
/// The REST URL of the definition.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public new String Url
{
get
{
return base.Url;
}
set
{
base.Url = value;
}
}
/// <summary>
/// The definition's URI.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public Uri Uri
{
get;
set;
}
/// <summary>
/// The folder path of the definition.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public String Path
{
get;
set;
}
/// <summary>
/// The type of the definition.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = false)]
public DefinitionType Type
{
get;
set;
}
/// <summary>
/// A value that indicates whether builds can be queued against this definition.
/// </summary>
[DataMember(IsRequired = false, EmitDefaultValue = true)]
public DefinitionQueueStatus QueueStatus
{
get;
set;
}
/// <summary>
/// The definition revision number.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32? Revision
{
get;
set;
}
/// <summary>
/// The date this version of the definition was created.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public DateTime CreatedDate
{
get;
set;
}
/// <summary>
/// A reference to the project.
/// </summary>
[DataMember(EmitDefaultValue = false, Order = 1000)]
public TeamProjectReference Project
{
get;
set;
}
#region ISecuredObject implementation
Guid ISecuredObject.NamespaceId => Security.BuildNamespaceId;
Int32 ISecuredObject.RequiredPermissions => m_requiredPermissions;
String ISecuredObject.GetToken()
{
if (!String.IsNullOrEmpty(m_nestingSecurityToken))
{
return m_nestingSecurityToken;
}
return GetToken(this.Project, this.Path, this.Id);
}
internal void SetRequiredPermissions(
Int32 newValue)
{
m_requiredPermissions = newValue;
}
internal void SetNestingSecurityToken(
String tokenValue)
{
// For anything more detailed than a DefinitionReference,
// we don't let you use a nesting security token.
if (this is BuildDefinitionReference)
{
// Debug.Fail("Nesting security tokens is not allowed for anything more detailed than a DefinitionReference");
m_nestingSecurityToken = String.Empty;
return;
}
m_nestingSecurityToken = tokenValue;
}
internal static String GetToken(
TeamProjectReference project,
String path,
Int32 definitionId)
{
return GetToken(project?.Id, path, definitionId);
}
internal static String GetToken(
Guid? projectId,
String path,
Int32 definitionId)
{
return String.Concat(projectId?.ToString("D") ?? String.Empty, Security.GetSecurityTokenPath(path ?? String.Empty), definitionId);
}
private Int32 m_requiredPermissions = BuildPermissions.ViewBuildDefinition;
private String m_nestingSecurityToken = String.Empty;
#endregion
}
}

View File

@@ -1,61 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
[DataContract]
public class DefinitionResourceReference : BaseSecuredObject
{
public DefinitionResourceReference()
: this(null)
{
}
internal DefinitionResourceReference(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// A friendly name for the resource.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Name
{
get;
set;
}
/// <summary>
/// The type of the resource.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Type
{
get;
set;
}
/// <summary>
/// The id of the resource.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Id
{
get;
set;
}
/// <summary>
/// Indicates whether the resource is authorized for use.
/// </summary>
[DataMember]
public Boolean Authorized
{
get;
set;
}
}
}

View File

@@ -1,117 +0,0 @@
using System;
using System.Text.RegularExpressions;
using GitHub.Services.Common;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a demand used by a definition or build.
/// </summary>
[JsonConverter(typeof(DemandJsonConverter))]
public abstract class Demand : BaseSecuredObject
{
protected Demand(
String name,
String value)
: this(name, value, null)
{
}
protected Demand(
String name,
String value,
ISecuredObject securedObject)
: base(securedObject)
{
ArgumentUtility.CheckStringForNullOrEmpty(name, "name");
this.Name = name;
this.Value = value;
}
/// <summary>
/// The name of the capability referenced by the demand.
/// </summary>
public String Name
{
get;
private set;
}
/// <summary>
/// The demanded value.
/// </summary>
public String Value
{
get;
private set;
}
public override sealed Boolean Equals(Object obj)
{
Demand demand = obj as Demand;
return demand != null && demand.ToString().Equals(this.ToString(), StringComparison.OrdinalIgnoreCase);
}
public override sealed Int32 GetHashCode()
{
return this.ToString().ToUpperInvariant().GetHashCode();
}
public override sealed String ToString()
{
return GetExpression();
}
/// <summary>
/// Clones this object.
/// </summary>
/// <returns></returns>
public abstract Demand Clone();
protected abstract String GetExpression();
/// <summary>
/// Parses a string into a Demand instance.
/// </summary>
/// <param name="input"></param>
/// <param name="demand"></param>
/// <returns></returns>
public static Boolean TryParse(
String input,
out Demand demand)
{
demand = null;
Match match = s_demandRegex.Match(input);
if (!match.Success)
{
return false;
}
String name = match.Groups["name"].Value;
String opcode = match.Groups["opcode"].Value;
String value = match.Groups["value"].Value;
if (String.IsNullOrEmpty(opcode))
{
demand = new DemandExists(name);
}
else
{
switch (opcode)
{
case "equals":
demand = new DemandEquals(name, value);
break;
}
}
return demand != null;
}
private static readonly Regex s_demandRegex = new Regex(@"^(?<name>[^ ]+)([ ]+\-(?<opcode>[^ ]+)[ ]+(?<value>.*))?$",
RegexOptions.Compiled);
}
}

View File

@@ -1,46 +0,0 @@
using System;
using System.Globalization;
using GitHub.Services.Common;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents an "equals" demand.
/// </summary>
/// <remarks>
/// This demand is satisfied when the value of the named capability matches the value stored in the demand.
/// </remarks>
public sealed class DemandEquals : Demand
{
public DemandEquals(
String name,
String value)
: this(name, value, null)
{
}
public DemandEquals(
String name,
String value,
ISecuredObject securedObject)
: base(name, value, securedObject)
{
ArgumentUtility.CheckStringForNullOrEmpty(value, "value");
}
/// <summary>
/// Clones this object.
/// </summary>
/// <returns></returns>
public override Demand Clone()
{
return new DemandEquals(this.Name, this.Value);
}
protected override String GetExpression()
{
return String.Format(CultureInfo.InvariantCulture, "{0} -equals {1}", this.Name, this.Value);
}
}
}

View File

@@ -1,41 +0,0 @@
using System;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents an "exists" demand.
/// </summary>
/// <remarks>
/// This demand is satisfied as long as the named capability exists, regardless of its value.
/// </remarks>
public sealed class DemandExists : Demand
{
public DemandExists(
String name)
: this(name, null)
{
}
public DemandExists(
String name,
ISecuredObject securedObject)
: base(name, null, securedObject)
{
}
/// <summary>
/// Clones this object.
/// </summary>
/// <returns></returns>
public override Demand Clone()
{
return new DemandExists(this.Name);
}
protected override String GetExpression()
{
return this.Name;
}
}
}

View File

@@ -1,46 +0,0 @@
using GitHub.Services.WebApi;
using Newtonsoft.Json;
using System;
using System.Reflection;
namespace GitHub.Build.WebApi
{
internal sealed class DemandJsonConverter : VssSecureJsonConverter
{
public override Boolean CanConvert(Type objectType)
{
return typeof(Demand).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override Object ReadJson(
JsonReader reader,
Type objectType,
Object existingValue,
JsonSerializer serializer)
{
if (existingValue == null && reader.TokenType == JsonToken.String)
{
Demand demand;
if (Demand.TryParse((String)reader.Value, out demand))
{
existingValue = demand;
}
}
return existingValue;
}
public override void WriteJson(
JsonWriter writer,
Object value,
JsonSerializer serializer)
{
base.WriteJson(writer, value, serializer);
if (value != null)
{
writer.WriteValue(value.ToString());
}
}
}
}

View File

@@ -1,43 +0,0 @@
using System;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// Represents a dependency.
/// </summary>
[DataContract]
public class Dependency : BaseSecuredObject
{
public Dependency()
{
}
internal Dependency(
ISecuredObject securedObject)
: base(securedObject)
{
}
/// <summary>
/// The scope. This names the object referenced by the dependency.
/// </summary>
[DataMember]
public String Scope
{
get;
set;
}
/// <summary>
/// The event. The dependency is satisfied when the referenced object emits this event.
/// </summary>
[DataMember]
public String Event
{
get;
set;
}
}
}

View File

@@ -1,441 +0,0 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Common.Contracts;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi.Internals
{
/// <summary>
/// For back-compat with extensions that use the old Steps format instead of Process and Phases
/// </summary>
[DataContract]
[EditorBrowsable(EditorBrowsableState.Never)]
public class BuildDefinition3_2 : BuildDefinitionReference3_2
{
public BuildDefinition3_2()
{
this.JobAuthorizationScope = BuildAuthorizationScope.ProjectCollection;
}
/// <summary>
/// The build number format
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String BuildNumberFormat
{
get;
set;
}
/// <summary>
/// The comment entered when saving the definition
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Comment
{
get;
set;
}
/// <summary>
/// The description
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String Description
{
get;
set;
}
/// <summary>
/// The drop location for the definition
/// </summary>
[DataMember(EmitDefaultValue = false)]
public String DropLocation
{
get;
set;
}
/// <summary>
/// The job authorization scope for builds which are queued against this definition
/// </summary>
[DataMember]
public BuildAuthorizationScope JobAuthorizationScope
{
get;
set;
}
/// <summary>
/// The job execution timeout in minutes for builds which are queued against this definition
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 JobTimeoutInMinutes
{
get;
set;
}
/// <summary>
/// The job cancel timeout in minutes for builds which are cancelled by user for this definition
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Int32 JobCancelTimeoutInMinutes
{
get;
set;
}
/// <summary>
/// Indicates whether badges are enabled for this definition
/// </summary>
[DataMember(EmitDefaultValue = false)]
public Boolean BadgeEnabled
{
get;
set;
}
/// <summary>
/// The list of steps for this definition.
/// </summary>
public List<BuildDefinitionStep> Steps
{
get
{
if (m_steps == null)
{
m_steps = new List<BuildDefinitionStep>();
}
return m_steps;
}
}
/// <summary>
/// Build options
/// </summary>
public List<BuildOption> Options
{
get
{
if (m_options == null)
{
m_options = new List<BuildOption>();
}
return m_options;
}
}
/// <summary>
/// The repository
/// </summary>
[DataMember(EmitDefaultValue = false)]
public BuildRepository Repository
{
get;
set;
}
/// <summary>
/// Process Parameters
/// </summary>
[DataMember(EmitDefaultValue = false)]
public ProcessParameters ProcessParameters
{
get;
set;
}
/// <summary>
/// The triggers
/// </summary>
public List<BuildTrigger> Triggers
{
get
{
if (m_triggers == null)
{
m_triggers = new List<BuildTrigger>();
}
return m_triggers;
}
}
/// <summary>
/// The variables.
/// </summary>
public IDictionary<String, BuildDefinitionVariable> Variables
{
get
{
if (m_variables == null)
{
m_variables = new Dictionary<String, BuildDefinitionVariable>(StringComparer.OrdinalIgnoreCase);
}
return m_variables;
}
}
/// <summary>
/// The demands.
/// </summary>
public List<Demand> Demands
{
get
{
if (m_demands == null)
{
m_demands = new List<Demand>();
}
return m_demands;
}
}
/// <summary>
/// The retention rules.
/// </summary>
public List<RetentionPolicy> RetentionRules
{
get
{
if (m_retentionRules == null)
{
m_retentionRules = new List<RetentionPolicy>();
}
return m_retentionRules;
}
}
/// <summary>
/// A collection of properties which may be used to extend the storage fields available
/// for a given definition.
/// </summary>
public PropertiesCollection Properties
{
get
{
if (m_properties == null)
{
m_properties = new PropertiesCollection();
}
return m_properties;
}
internal set
{
m_properties = value;
}
}
/// <summary>
/// A collection of tags associated with the build definition.
/// </summary>
public List<String> Tags
{
get
{
if (m_tags == null)
{
m_tags = new List<String>();
}
return m_tags;
}
}
/// <summary>
/// The latest build for this definition.
/// </summary>
public Build LatestBuild
{
get
{
return m_latestBuild;
}
internal set
{
m_latestBuild = value;
}
}
/// <summary>
/// The latest completed build for this definition.
/// </summary>
public Build LatestCompletedBuild
{
get
{
return m_latestCompletedBuild;
}
internal set
{
m_latestCompletedBuild = value;
}
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
SerializationHelper.Copy(ref m_serializedOptions, ref m_options, true);
SerializationHelper.Copy(ref m_serializedSteps, ref m_steps, true);
SerializationHelper.Copy(ref m_serializedTriggers, ref m_triggers, true);
SerializationHelper.Copy(ref m_serializedVariables, ref m_variables, StringComparer.OrdinalIgnoreCase, true);
SerializationHelper.Copy(ref m_serializedDemands, ref m_demands, true);
SerializationHelper.Copy(ref m_serializedRetentionRules, ref m_retentionRules, true);
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
SerializationHelper.Copy(ref m_options, ref m_serializedOptions);
SerializationHelper.Copy(ref m_steps, ref m_serializedSteps);
SerializationHelper.Copy(ref m_triggers, ref m_serializedTriggers);
SerializationHelper.Copy(ref m_variables, ref m_serializedVariables, StringComparer.OrdinalIgnoreCase);
SerializationHelper.Copy(ref m_demands, ref m_serializedDemands);
SerializationHelper.Copy(ref m_retentionRules, ref m_serializedRetentionRules);
}
[OnSerialized]
private void OnSerialized(StreamingContext context)
{
m_serializedSteps = null;
m_serializedOptions = null;
m_serializedTriggers = null;
m_serializedVariables = null;
m_serializedRetentionRules = null;
}
[DataMember(Name = "Build", EmitDefaultValue = false)]
private List<BuildDefinitionStep> m_serializedSteps;
[DataMember(Name = "Options", EmitDefaultValue = false)]
private List<BuildOption> m_serializedOptions;
[DataMember(Name = "Triggers", EmitDefaultValue = false)]
private List<BuildTrigger> m_serializedTriggers;
[DataMember(Name = "Variables", EmitDefaultValue = false)]
private IDictionary<String, BuildDefinitionVariable> m_serializedVariables;
[DataMember(Name = "Demands", EmitDefaultValue = false)]
private List<Demand> m_serializedDemands;
[DataMember(Name = "RetentionRules", EmitDefaultValue = false)]
private List<RetentionPolicy> m_serializedRetentionRules;
[DataMember(IsRequired = false, EmitDefaultValue = false, Name = "Properties")]
private PropertiesCollection m_properties;
[DataMember(EmitDefaultValue = false, Name = "Tags")]
private List<String> m_tags;
[DataMember(EmitDefaultValue = false, Name = "LatestBuild")]
private Build m_latestBuild;
[DataMember(EmitDefaultValue = false, Name = "LatestCompletedBuild")]
private Build m_latestCompletedBuild;
private List<Demand> m_demands;
private List<BuildOption> m_options;
private List<BuildTrigger> m_triggers;
private List<RetentionPolicy> m_retentionRules;
private List<BuildDefinitionStep> m_steps;
private IDictionary<String, BuildDefinitionVariable> m_variables;
}
internal static class BuildDefinition3_2Extensions
{
public static BuildDefinition ToBuildDefinition(
this BuildDefinition3_2 source)
{
if (source == null)
{
return null;
}
var result = new BuildDefinition()
{
AuthoredBy = source.AuthoredBy,
BadgeEnabled = source.BadgeEnabled,
BuildNumberFormat = source.BuildNumberFormat,
Comment = source.Comment,
CreatedDate = source.CreatedDate,
DefinitionQuality = source.DefinitionQuality,
Description = source.Description,
DropLocation = source.DropLocation,
Id = source.Id,
JobAuthorizationScope = source.JobAuthorizationScope,
JobCancelTimeoutInMinutes = source.JobCancelTimeoutInMinutes,
JobTimeoutInMinutes = source.JobTimeoutInMinutes,
LatestBuild = source.LatestBuild,
LatestCompletedBuild = source.LatestCompletedBuild,
Name = source.Name,
ParentDefinition = source.ParentDefinition,
Path = source.Path,
ProcessParameters = source.ProcessParameters,
Project = source.Project,
Queue = source.Queue,
QueueStatus = source.QueueStatus,
Repository = source.Repository,
Revision = source.Revision,
Type = source.Type,
Uri = source.Uri,
Url = source.Url
};
if (source.Demands.Count > 0)
{
result.Demands.AddRange(source.Demands);
}
if (source.Metrics.Count > 0)
{
result.Metrics.AddRange(source.Metrics);
}
if (source.Options.Count > 0)
{
result.Options.AddRange(source.Options);
}
var process = new DesignerProcess();
result.Process = process;
var phase = new Phase();
process.Phases.Add(phase);
if (source.Steps.Count > 0)
{
phase.Steps.AddRange(source.Steps);
}
foreach (var property in source.Properties)
{
result.Properties.Add(property.Key, property.Value);
}
if (source.RetentionRules.Count > 0)
{
result.RetentionRules.AddRange(source.RetentionRules);
}
if (source.Tags.Count > 0)
{
result.Tags.AddRange(source.Tags);
}
if (source.Triggers.Count > 0)
{
result.Triggers.AddRange(source.Triggers);
}
foreach (var variablePair in source.Variables)
{
result.Variables.Add(variablePair.Key, variablePair.Value);
}
return result;
}
}
}

View File

@@ -1,127 +0,0 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.Services.WebApi;
namespace GitHub.Build.WebApi
{
/// <summary>
/// For back-compat with extensions that use the old Steps format instead of Process and Phases
/// </summary>
[DataContract]
[EditorBrowsable(EditorBrowsableState.Never)]
public class BuildDefinitionReference3_2 : DefinitionReference
{
public BuildDefinitionReference3_2()
{
Type = DefinitionType.Build;
QueueStatus = DefinitionQueueStatus.Enabled;
}
/// <summary>
/// The quality of the definition document (draft, etc.)
/// </summary>
[DataMember(EmitDefaultValue = false, Name = "Quality")]
public DefinitionQuality? DefinitionQuality
{
get;
set;
}
/// <summary>
/// The author of the definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public IdentityRef AuthoredBy
{
get;
[EditorBrowsable(EditorBrowsableState.Never)]
set;
}
/// <summary>
/// A reference to the definition that this definition is a draft of, if this is a draft definition.
/// </summary>
[DataMember(EmitDefaultValue = false, Name = "draftOf")]
public DefinitionReference ParentDefinition
{
get;
set;
}
/// <summary>
/// The list of drafts associated with this definition, if this is not a draft definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public List<DefinitionReference> Drafts
{
get
{
return m_drafts ?? (m_drafts = new List<DefinitionReference>());
}
internal set
{
m_drafts = value;
}
}
/// <summary>
/// The default queue for builds run against this definition.
/// </summary>
[DataMember(EmitDefaultValue = false)]
public AgentPoolQueue Queue
{
get;
set;
}
/// <summary>
/// The metrics for this definition.
/// </summary>
public List<BuildMetric> Metrics
{
get
{
return m_metrics ?? (m_metrics = new List<BuildMetric>());
}
internal set
{
m_metrics = value;
}
}
/// <summary>
/// The links to other objects related to this object.
/// </summary>
public ReferenceLinks Links => m_links ?? (m_links = new ReferenceLinks());
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
SerializationHelper.Copy(ref m_serializedMetrics, ref m_metrics, true);
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
SerializationHelper.Copy(ref m_metrics, ref m_serializedMetrics);
}
[OnSerialized]
private void OnSerialized(StreamingContext context)
{
m_serializedMetrics = null;
}
[DataMember(Name = "Metrics", EmitDefaultValue = false)]
private List<BuildMetric> m_serializedMetrics;
[DataMember(Name = "_links", EmitDefaultValue = false)]
private ReferenceLinks m_links;
private List<BuildMetric> m_metrics;
private List<DefinitionReference> m_drafts;
}
}

Some files were not shown because too many files have changed in this diff Show More