Compare commits

...

46 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
Julio Barba
b061ec410f Revert: Switch publish agent package task to direct to new pool 2019-12-02 16:54:19 -05:00
Julio Barba
2b63b9c379 Prepare 2.162.0 runner release (#204) 2019-12-02 16:16:35 -05:00
Julio Barba
d93fb70a3e Fix PrepareActions_DownloadActionFromGraph test (#205) 2019-12-02 16:03:42 -05:00
Julio Barba
4ce1bfb58a Switch publish agent package task to direct to new pool 2019-12-02 14:51:46 -05:00
Julio Barba
7a6d9dc5c8 Implement new runner service name convention (#193)
* Limit service name to 80 characters
* Add L0 tests
* New service name convention
* Make RepoOrOrgName a computed property
* Add service name sanitizing logic with L0 test
2019-11-27 14:44:29 -05:00
Julio Barba
de29a39d14 Support downloading/publishing artifacts from Pipelines endpoint (#188)
* Support downloading/publishing artifacts from Pipelines endpoint
* Remove `Path` from everywhere
* Remove unused JobId argument
* PR feedback
* More PR feedback
2019-11-25 13:30:44 -05:00
eric sciple
7d505f7f77 problem matcher default severity (#203) 2019-11-22 13:21:46 -05:00
Thomas Boop
159e4c506a Updated Release Notes to use CLI Downloads (#196)
* Updated Release Notes to use CLI Downloads

* Add Config and Run steps

* Specify Root Drive for Windows

* Remove unactionable steps from readme config
2019-11-19 16:27:36 -05:00
David Kale
45c19eb7cb 150: Support more cpu architectures (#184)
* Cross compile for win-x86, linux-arm, linux-arm64

* Build with actions instead

* Remove win-x86

* Preserve CURRENT_PLATFORM in dev.sh

* build.yaml

* Fix formatting. Remove piplines

* Use 4 space indent consistently

* x32 -> x86

* TEMP: Only test when platform === target runtime

Fix arm64 node externals url

* win-x86 externals

* Temporarily bench rhel

* Add RHEL6, skip L0 on arm for now

* Add stub for downloading new node externals when they are ready

* Remove RHEL6

* Package based on new runtime names

* Remove unused rhel from matrix includes

* Update release, add packages

* RID typo

* Cant cross test arm on x64 hosts

* New arch is a feature

Dont release x86 until we have an e2e test machine

* Fix version

* Get version from file to avoid exec error during package on x64 host for arm package

* Update Release Notes for 2.161.0 (#195)

* More cleanup

* Update release notes
2019-11-13 11:26:06 -05:00
Thomas Boop
9ba971592b V 2.160.2 Release notes and version bump (#190) 2019-11-11 15:45:37 -05:00
Thomas Boop
b27cfb18e6 2.160.1 Runner Release Notes (#171)
* 2.160.1 Runner Release Notes

* Minor verbiage updates to be consistent
2019-11-11 15:45:13 -05:00
Thomas Boop
ced4c2ca50 add-mask is leaking a secret in master if debug or ::echo::on is set (#158)
* Output after processing command to avoid leaking mask

* Remove extra noise output from echo changes

* Omit Echoing of add-mask command

* avoid echoing on debug/warning/error
2019-11-11 15:40:18 -05:00
Thomas Boop
54f21c641f Update dependency docs for OSX and Windows (#162)
* Update Dependency docs for .net 3.0 depedencies

* Update Supported Windows Versions

* Update Supported Mac OS link

* Update docs/start/envosx.md

Fix typo in OSX Version

Co-Authored-By: Lucas Costi <lucascosti@users.noreply.github.com>
2019-11-11 13:43:02 -05:00
Thomas Boop
c5cbac9796 Runner fails to run as a service on windows: Disable Delay Signing on the Runner Service (#185)
* test release

* Reverse test release changes

* Remove Unused Public Keys from Runner Service
2019-11-08 09:49:20 -05:00
eric sciple
8911283cdb fix problem matcher to treat fromPath as a file path (#183) 2019-11-07 11:26:29 -05:00
Eilon Lipton
76078b5c44 Add SLN file and make projects build in VS (#173)
* Add SLN file and make projects build in VS

- Added new ActionsRunner.sln file with all the CSPROJ's in it
- Added Directory.Build.props that gets auto-included in all CSPROJ files under it
- Made default runtime platforms for Windows and Linux (to be 64bit) so that you don't have to specify it in order to build

* Remove extra invalid parens
2019-11-06 16:57:46 -05:00
Eilon Lipton
ec9cb6c68d Fix build warnings in Test project (#178)
The build warnings were of these type (mostly reported by xUnit's Code Analyzers):
- Fixed wrong parameter order in xUnit assertions (can lead to poor error reporting in test failures)
- Unused code was removed
- Correct assertions were used (e.g. Assert.True/.Contains/.EndsWith)
- Public non-test methods on test classes were made private
2019-11-06 08:52:51 -05:00
Eilon Lipton
bcac4557a0 Fix Runner.Worker build warnings (#174)
Most of these warnings show up on only certain build OSes because of #ifdefs in the code. The fix is to suppress these warnings.
2019-11-06 08:47:56 -05:00
Eilon Lipton
19580bdaf8 Update contribute.md (#175)
Small cleanup of contribution guide
2019-11-05 17:21:50 -05:00
Eilon Lipton
96d3288553 Remove unused dreamlifter section (#177) 2019-11-05 17:21:00 -05:00
Tingluo Huang
5b6f9d3b93 Stop job container after all post actions. (#165)
* stop job container after all post actions.

* c

* c
2019-11-04 13:19:21 -05:00
eric sciple
51581ac865 root search pattern for hashfiles and allow forward slash on windows (#151) 2019-10-29 13:23:30 -04:00
eric sciple
e7dd2c6cc2 Update dotnet install script (#155) 2019-10-29 13:13:58 -04:00
Thomas Boop
08b9f6e045 Merge release 160.0 into master (#153)
* Update to Version 2.160.0 (#144)

* Revert "remove issue generation on warning/error commands (#137)" (#147)

* Revert "remove issue generation on warning/error commands (#137)"

This reverts commit 53da198867.

* Updated Release notes

* Users/thboop/port directory changes (#152)

* Clear action cache for local runner

* update release notes for actions directory cache changes
2019-10-28 15:45:27 -04:00
eric sciple
0129e8111f Clear action cache for local runner (#148) 2019-10-28 11:56:12 -04:00
eric sciple
ccca13ac07 restrict hashFiles to basic globbing and globstar (#150) 2019-10-28 11:52:22 -04:00
923 changed files with 5202 additions and 123936 deletions

View File

@@ -13,14 +13,28 @@ jobs:
build: build:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] runtime: [ linux-x64, linux-arm64, linux-arm, win-x64, osx-x64 ]
include: include:
- os: ubuntu-latest - runtime: linux-x64
os: ubuntu-latest
devScript: ./dev.sh devScript: ./dev.sh
- os: macOS-latest
- runtime: linux-arm64
os: ubuntu-latest
devScript: ./dev.sh devScript: ./dev.sh
- os: windows-latest
- 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 devScript: ./dev
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
@@ -28,7 +42,7 @@ jobs:
# Build runner layout # Build runner layout
- name: Build & Layout Release - name: Build & Layout Release
run: | run: |
${{ matrix.devScript }} layout Release ${{ matrix.devScript }} layout Release ${{ matrix.runtime }}
working-directory: src working-directory: src
# Run tests # Run tests
@@ -36,6 +50,7 @@ jobs:
run: | run: |
${{ matrix.devScript }} test ${{ matrix.devScript }} test
working-directory: src working-directory: src
if: matrix.runtime != 'linux-arm64' && matrix.runtime != 'linux-arm'
# Create runner package tar.gz/zip # Create runner package tar.gz/zip
- name: Package Release - name: Package Release
@@ -49,5 +64,5 @@ jobs:
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: runner-package-${{ matrix.os }} name: runner-package-${{ matrix.runtime }}
path: _package path: _package

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

1
.gitignore vendored
View File

@@ -3,7 +3,6 @@
**/libs **/libs
**/*.xproj **/*.xproj
**/*.xproj.user **/*.xproj.user
**/*.sln
**/.vs **/.vs
**/.vscode **/.vscode
**/*.error **/*.error

73
ActionsRunner.sln Normal file
View File

@@ -0,0 +1,73 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29411.138
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runner.Common", "src\Runner.Common\Runner.Common.csproj", "{084289A3-CD7A-42E0-9219-4348B4B7E19B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runner.Listener", "src\Runner.Listener\Runner.Listener.csproj", "{7D461AEE-BF2A-4855-BD96-56921160B36A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runner.PluginHost", "src\Runner.PluginHost\Runner.PluginHost.csproj", "{D0320EB1-CB6D-4179-BFDC-2F2B664A370C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runner.Plugins", "src\Runner.Plugins\Runner.Plugins.csproj", "{C23AFD6F-4DCD-4243-BC61-865BE31B9168}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runner.Sdk", "src\Runner.Sdk\Runner.Sdk.csproj", "{D0484633-DA97-4C34-8E47-1DADE212A57A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RunnerService", "src\Runner.Service\Windows\RunnerService.csproj", "{D12EBD71-0464-46D0-8394-40BCFBA0A6F2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Runner.Worker", "src\Runner.Worker\Runner.Worker.csproj", "{C2F5B9FA-2621-411F-8EB2-273ED276F503}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sdk", "src\Sdk\Sdk.csproj", "{D2EE812B-E4DF-49BB-AE87-12BC49949B5F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "src\Test\Test.csproj", "{C932061F-F6A1-4F1E-B854-A6C6B30DC3EF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{084289A3-CD7A-42E0-9219-4348B4B7E19B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{084289A3-CD7A-42E0-9219-4348B4B7E19B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{084289A3-CD7A-42E0-9219-4348B4B7E19B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{084289A3-CD7A-42E0-9219-4348B4B7E19B}.Release|Any CPU.Build.0 = Release|Any CPU
{7D461AEE-BF2A-4855-BD96-56921160B36A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D461AEE-BF2A-4855-BD96-56921160B36A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D461AEE-BF2A-4855-BD96-56921160B36A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D461AEE-BF2A-4855-BD96-56921160B36A}.Release|Any CPU.Build.0 = Release|Any CPU
{D0320EB1-CB6D-4179-BFDC-2F2B664A370C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0320EB1-CB6D-4179-BFDC-2F2B664A370C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0320EB1-CB6D-4179-BFDC-2F2B664A370C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0320EB1-CB6D-4179-BFDC-2F2B664A370C}.Release|Any CPU.Build.0 = Release|Any CPU
{C23AFD6F-4DCD-4243-BC61-865BE31B9168}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C23AFD6F-4DCD-4243-BC61-865BE31B9168}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C23AFD6F-4DCD-4243-BC61-865BE31B9168}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C23AFD6F-4DCD-4243-BC61-865BE31B9168}.Release|Any CPU.Build.0 = Release|Any CPU
{D0484633-DA97-4C34-8E47-1DADE212A57A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0484633-DA97-4C34-8E47-1DADE212A57A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0484633-DA97-4C34-8E47-1DADE212A57A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0484633-DA97-4C34-8E47-1DADE212A57A}.Release|Any CPU.Build.0 = Release|Any CPU
{D12EBD71-0464-46D0-8394-40BCFBA0A6F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D12EBD71-0464-46D0-8394-40BCFBA0A6F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D12EBD71-0464-46D0-8394-40BCFBA0A6F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D12EBD71-0464-46D0-8394-40BCFBA0A6F2}.Release|Any CPU.Build.0 = Release|Any CPU
{C2F5B9FA-2621-411F-8EB2-273ED276F503}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C2F5B9FA-2621-411F-8EB2-273ED276F503}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C2F5B9FA-2621-411F-8EB2-273ED276F503}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C2F5B9FA-2621-411F-8EB2-273ED276F503}.Release|Any CPU.Build.0 = Release|Any CPU
{D2EE812B-E4DF-49BB-AE87-12BC49949B5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2EE812B-E4DF-49BB-AE87-12BC49949B5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2EE812B-E4DF-49BB-AE87-12BC49949B5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D2EE812B-E4DF-49BB-AE87-12BC49949B5F}.Release|Any CPU.Build.0 = Release|Any CPU
{C932061F-F6A1-4F1E-B854-A6C6B30DC3EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C932061F-F6A1-4F1E-B854-A6C6B30DC3EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C932061F-F6A1-4F1E-B854-A6C6B30DC3EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C932061F-F6A1-4F1E-B854-A6C6B30DC3EF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4A831DDA-3860-45E5-930E-BB3A7833AE80}
EndGlobalSection
EndGlobal

View File

@@ -16,5 +16,17 @@
"platform": "linux-x64", "platform": "linux-x64",
"version": "<RUNNER_VERSION>", "version": "<RUNNER_VERSION>",
"downloadUrl": "https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz" "downloadUrl": "https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz"
},
{
"name": "actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz",
"platform": "linux-arm64",
"version": "<RUNNER_VERSION>",
"downloadUrl": "https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz"
},
{
"name": "actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz",
"platform": "linux-arm",
"version": "<RUNNER_VERSION>",
"downloadUrl": "https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz"
} }
] ]

View File

@@ -11,6 +11,8 @@ stages:
# Steps template for windows platform # Steps template for windows platform
- template: windows.template.yml - template: windows.template.yml
parameters:
targetRuntime: win-x64
# Package dotnet core windows dependency (VC++ Redistributable) # Package dotnet core windows dependency (VC++ Redistributable)
- powershell: | - powershell: |
@@ -28,13 +30,13 @@ stages:
displayName: Package UCRT displayName: Package UCRT
# Create agent package zip # Create agent package zip
- script: dev.cmd package Release - script: dev.cmd package Release win-x64
workingDirectory: src workingDirectory: src
displayName: Package Release displayName: Package Release
# Upload agent package zip as build artifact # Upload agent package zip as build artifact
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
displayName: Publish Artifact (Windows) displayName: Publish Artifact (Windows x64)
inputs: inputs:
pathToPublish: _package pathToPublish: _package
artifactName: runners artifactName: runners
@@ -50,22 +52,76 @@ stages:
# Steps template for non-windows platform # Steps template for non-windows platform
- template: nonwindows.template.yml - template: nonwindows.template.yml
parameters:
targetRuntime: linux-x64
# Create agent package zip # Create agent package zip
- script: ./dev.sh package Release - script: ./dev.sh package Release linux-x64
workingDirectory: src workingDirectory: src
displayName: Package Release displayName: Package Release
# Upload agent package zip as build artifact # Upload agent package zip as build artifact
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
displayName: Publish Artifact (Linux) displayName: Publish Artifact (Linux x64)
inputs: inputs:
pathToPublish: _package pathToPublish: _package
artifactName: runners artifactName: runners
artifactType: container artifactType: container
################################################################################ ################################################################################
- job: build_osx_agent - job: build_linux_agent_arm64
################################################################################
displayName: Linux Agent (arm64)
pool:
vmImage: ubuntu-16.04
steps:
# Steps template for non-windows platform
- template: nonwindows.template.yml
parameters:
targetRuntime: linux-arm64
# Create agent package zip
- script: ./dev.sh package Release linux-arm64
workingDirectory: src
displayName: Package Release
# Upload agent package zip as build artifact
- task: PublishBuildArtifacts@1
displayName: Publish Artifact (Linux ARM64)
inputs:
pathToPublish: _package
artifactName: runners
artifactType: container
################################################################################
- job: build_linux_agent_arm
################################################################################
displayName: Linux Agent (arm)
pool:
vmImage: ubuntu-16.04
steps:
# Steps template for non-windows platform
- template: nonwindows.template.yml
parameters:
targetRuntime: linux-arm
# Create agent package zip
- script: ./dev.sh package Release linux-arm
workingDirectory: src
displayName: Package Release
# Upload agent package zip as build artifact
- task: PublishBuildArtifacts@1
displayName: Publish Artifact (Linux ARM)
inputs:
pathToPublish: _package
artifactName: runners
artifactType: container
################################################################################
- job: build_osx_agent_x64
################################################################################ ################################################################################
displayName: macOS Agent (x64) displayName: macOS Agent (x64)
pool: pool:
@@ -74,15 +130,17 @@ stages:
# Steps template for non-windows platform # Steps template for non-windows platform
- template: nonwindows.template.yml - template: nonwindows.template.yml
parameters:
targetRuntime: osx-x64
# Create agent package zip # Create agent package zip
- script: ./dev.sh package Release - script: ./dev.sh package Release osx-x64
workingDirectory: src workingDirectory: src
displayName: Package Release displayName: Package Release
# Upload agent package zip as build artifact # Upload agent package zip as build artifact
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
displayName: Publish Artifact (OSX) displayName: Publish Artifact (OSX x64)
inputs: inputs:
pathToPublish: _package pathToPublish: _package
artifactName: runners artifactName: runners
@@ -163,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

@@ -1,12 +1,12 @@
# Contribute (Dev) # Contribution guide for developers
## Dev Dependencies ## Required Dev Dependencies
![Win](res/win_sm.png) Git for Windows [Install Here](https://git-scm.com/downloads) (needed for dev sh script) ![Win](res/win_sm.png) Git for Windows [Install Here](https://git-scm.com/downloads) (needed for dev sh script)
## Build, Test, Layout ## To Build, Test, Layout
From src: Navigate to the `src` directory and run the following command:
![Win](res/win_sm.png) `dev {command}` ![Win](res/win_sm.png) `dev {command}`
@@ -14,13 +14,12 @@ From src:
**Commands:** **Commands:**
`layout` (`l`): Run first time to create a full agent layout in {root}/_layout * `layout` (`l`): Run first time to create a full agent layout in `{root}/_layout`
* `build` (`b`): Build everything and update agent layout folder
* `test` (`t`): Build agent binaries and run unit tests
`build` (`b`): build everything and update agent layout folder Sample developer flow:
`test` (`t`): build agent binaries and run unit tests
Normal dev flow:
```bash ```bash
git clone https://github.com/actions/runner git clone https://github.com/actions/runner
cd ./src cd ./src
@@ -37,5 +36,5 @@ cd ./src
## Styling ## Styling
We use the dotnet foundation and CoreCLR style guidelines [located here]( We use the .NET Foundation and CoreCLR style guidelines [located here](
https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md) https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md)

View File

@@ -4,7 +4,7 @@
## Supported Versions ## Supported Versions
- macOS Sierra (10.12) and later versions - macOS High Sierra (10.13) and later versions
## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore2x) ## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore30)

View File

@@ -5,8 +5,8 @@
- Windows 7 64-bit - Windows 7 64-bit
- Windows 8.1 64-bit - Windows 8.1 64-bit
- Windows 10 64-bit - Windows 10 64-bit
- Windows Server 2008 R2 SP1 64-bit
- Windows Server 2012 R2 64-bit - Windows Server 2012 R2 64-bit
- Windows Server 2016 64-bit - Windows Server 2016 64-bit
- Windows Server 2019 64-bit
## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x) ## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore30)

View File

@@ -1,14 +1,18 @@
parameters:
targetRuntime: ''
steps: steps:
# Build agent layout # Build agent layout
- script: ./dev.sh layout Release - script: ./dev.sh layout Release ${{ parameters.targetRuntime }}
workingDirectory: src workingDirectory: src
displayName: Build & Layout Release displayName: Build & Layout Release ${{ parameters.targetRuntime }}
# Run test # Run test
- script: ./dev.sh test - script: ./dev.sh test
workingDirectory: src workingDirectory: src
displayName: Test displayName: Test
condition: and(ne('${{ parameters.targetRuntime }}', 'linux-arm64'), ne('${{ parameters.targetRuntime }}', 'linux-arm'))
# # Publish test results # # Publish test results
# - task: PublishTestResults@2 # - task: PublishTestResults@2

View File

@@ -1,40 +1,69 @@
## Features ## Features
- Set Default shell to powershell for windows runners (#135) - Added the "severity" keyword to allow action authors to set the default severity for problem matchers (#203)
- Use Powershell as fallback if Powershell Core is not available for default shell on windows (#142)
## Bugs ## Bugs
- Removed unintended additional fields on error and warning commands (#137) - Fixed generated self-hosted runner names to never go over 80 characters (helps Windows customers) (#193)
- Fixed `PrepareActions_DownloadActionFromGraph` test by pointing to an active Actions repository (#205)
## Misc ## Misc
- N/A - Updated the publish and download artifact actions to use the v2 endpoint (#188)
- Updated the service name on self-hosted runner name to include repository or organization information (#193)
## Agent Downloads
| | Package |
| ------- | ----------------------------------------------------------------------------------------------------------- |
| Windows x64 | [actions-runner-win-x64-<RUNNER_VERSION>.zip](https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-win-x64-<RUNNER_VERSION>.zip) |
| macOS | [actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz](https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz) |
| Linux x64 | [actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz](https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz) |
After Download:
## Windows x64 ## Windows x64
We recommend configuring the runner under "<DRIVE>:\actions-runner". This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows
``` bash ```
C:\> mkdir myagent && cd myagent // Create a folder under the drive root
C:\myagent> Add-Type -AssemblyName System.IO.Compression.FileSystem ; [System.IO.Compression.ZipFile]::ExtractToDirectory("$HOME\Downloads\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD") mkdir \actions-runner ; cd \actions-runner
// Download the latest runner package
Invoke-WebRequest -Uri https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-win-x64-<RUNNER_VERSION>.zip -OutFile actions-runner-win-x64-<RUNNER_VERSION>.zip
// Extract the installer
Add-Type -AssemblyName System.IO.Compression.FileSystem ;
[System.IO.Compression.ZipFile]::ExtractToDirectory("$HOME\Downloads\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD")
``` ```
## OSX ## OSX
``` bash ``` bash
~/$ mkdir myagent && cd myagent // Create a folder
~/myagent$ tar xzf ~/Downloads/actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz mkdir actions-runner && cd actions-runner
// Download the latest runner package
curl -O https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz
// Extract the installer
tar xzf ./actions-runner-osx-x64-<RUNNER_VERSION>.tar.gz
``` ```
## Linux x64 ## Linux x64
``` bash ``` bash
~/$ mkdir myagent && cd myagent // Create a folder
~/myagent$ tar xzf ~/Downloads/actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz mkdir actions-runner && cd actions-runner
// Download the latest runner package
curl -O https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
// Extract the installer
tar xzf ./actions-runner-linux-x64-<RUNNER_VERSION>.tar.gz
``` ```
## Linux arm64 (Pre-release)
``` bash
// Create a folder
mkdir actions-runner && cd actions-runner
// Download the latest runner package
curl -O https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
// Extract the installer
tar xzf ./actions-runner-linux-arm64-<RUNNER_VERSION>.tar.gz
```
## Linux arm (Pre-release)
``` bash
// Create a folder
mkdir actions-runner && cd actions-runner
// Download the latest runner package
curl -O https://githubassets.azureedge.net/runners/<RUNNER_VERSION>/actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz
// Extract the installer
tar xzf ./actions-runner-linux-arm-<RUNNER_VERSION>.tar.gz
```
## Using your self hosted runner
For additional details about configuring, running, or shutting down the runner please check out our [product docs.](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/adding-self-hosted-runners)

49
src/Directory.Build.props Normal file
View File

@@ -0,0 +1,49 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Detect OS for build -->
<PropertyGroup>
<BUILD_OS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">Windows</BUILD_OS>
<BUILD_OS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">OSX</BUILD_OS>
<BUILD_OS Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">Linux</BUILD_OS>
</PropertyGroup>
<!-- Set OS vars -->
<PropertyGroup Condition="'$(BUILD_OS)' == 'Windows'">
<DefineConstants>$(DefineConstants);OS_WINDOWS</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_OS)' == 'OSX'">
<DefineConstants>$(DefineConstants);OS_OSX</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_OS)' == 'Linux'">
<DefineConstants>$(DefineConstants);OS_LINUX</DefineConstants>
</PropertyGroup>
<!-- Set Platform/bitness vars -->
<PropertyGroup Condition="'$(BUILD_OS)' == 'Windows' AND ('$(PackageRuntime)' == 'win-x64' OR '$(PackageRuntime)' == '')">
<DefineConstants>$(DefineConstants);X64</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_OS)' == 'Windows' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>$(DefineConstants);X86</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_OS)' == 'OSX'">
<DefineConstants>$(DefineConstants);X64</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_OS)' == 'Linux' AND ('$(PackageRuntime)' == 'linux-x64' OR '$(PackageRuntime)' == '')">
<DefineConstants>$(DefineConstants);X64</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_OS)' == 'Linux' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>$(DefineConstants);ARM</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(BUILD_OS)' == 'Linux' AND '$(PackageRuntime)' == 'linux-arm64'">
<DefineConstants>$(DefineConstants);ARM64</DefineConstants>
</PropertyGroup>
<!-- Set TRACE/DEBUG vars -->
<PropertyGroup>
<DefineConstants>$(DefineConstants);TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>$(DefineConstants);DEBUG</DefineConstants>
</PropertyGroup>
</Project>

View File

@@ -37,10 +37,7 @@
.PARAMETER SharedRuntime .PARAMETER SharedRuntime
This parameter is obsolete and may be removed in a future version of this script. This parameter is obsolete and may be removed in a future version of this script.
The recommended alternative is '-Runtime dotnet'. The recommended alternative is '-Runtime dotnet'.
Default: false
Installs just the shared runtime bits, not the entire SDK. Installs just the shared runtime bits, not the entire SDK.
This is equivalent to specifying `-Runtime dotnet`.
.PARAMETER Runtime .PARAMETER Runtime
Installs just a shared runtime, not the entire SDK. Installs just a shared runtime, not the entire SDK.
Possible values: Possible values:
@@ -77,11 +74,15 @@
Skips installing non-versioned files if they already exist, such as dotnet.exe. Skips installing non-versioned files if they already exist, such as dotnet.exe.
.PARAMETER NoCdn .PARAMETER NoCdn
Disable downloading from the Azure CDN, and use the uncached feed directly. Disable downloading from the Azure CDN, and use the uncached feed directly.
.PARAMETER JSonFile
Determines the SDK version from a user specified global.json file
Note: global.json must have a value for 'SDK:Version'
#> #>
[cmdletbinding()] [cmdletbinding()]
param( param(
[string]$Channel="LTS", [string]$Channel="LTS",
[string]$Version="Latest", [string]$Version="Latest",
[string]$JSonFile,
[string]$InstallDir="<auto>", [string]$InstallDir="<auto>",
[string]$Architecture="<auto>", [string]$Architecture="<auto>",
[ValidateSet("dotnet", "aspnetcore", "windowsdesktop", IgnoreCase = $false)] [ValidateSet("dotnet", "aspnetcore", "windowsdesktop", IgnoreCase = $false)]
@@ -166,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" }
} }
} }
@@ -258,7 +259,6 @@ function GetHTTPResponse([Uri] $Uri)
}) })
} }
function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Coherent) { function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Coherent) {
Say-Invocation $MyInvocation Say-Invocation $MyInvocation
@@ -304,20 +304,59 @@ function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Co
return $VersionInfo return $VersionInfo
} }
function Parse-Jsonfile-For-Version([string]$JSonFile) {
function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel, [string]$Version) {
Say-Invocation $MyInvocation Say-Invocation $MyInvocation
switch ($Version.ToLower()) { If (-Not (Test-Path $JSonFile)) {
{ $_ -eq "latest" } { throw "Unable to find '$JSonFile'"
$LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $False }
return $LatestVersionInfo.Version try {
$JSonContent = Get-Content($JSonFile) -Raw | ConvertFrom-Json | Select-Object -expand "sdk" -ErrorAction SilentlyContinue
}
catch {
throw "Json file unreadable: '$JSonFile'"
}
if ($JSonContent) {
try {
$JSonContent.PSObject.Properties | ForEach-Object {
$PropertyName = $_.Name
if ($PropertyName -eq "version") {
$Version = $_.Value
Say-Verbose "Version = $Version"
}
}
} }
{ $_ -eq "coherent" } { catch {
$LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $True throw "Unable to parse the SDK node in '$JSonFile'"
return $LatestVersionInfo.Version
} }
default { return $Version } }
else {
throw "Unable to find the SDK node in '$JSonFile'"
}
If ($Version -eq $null) {
throw "Unable to find the SDK:version node in '$JSonFile'"
}
return $Version
}
function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel, [string]$Version, [string]$JSonFile) {
Say-Invocation $MyInvocation
if (-not $JSonFile) {
switch ($Version.ToLower()) {
{ $_ -eq "latest" } {
$LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $False
return $LatestVersionInfo.Version
}
{ $_ -eq "coherent" } {
$LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $True
return $LatestVersionInfo.Version
}
default { return $Version }
}
}
else {
return Parse-Jsonfile-For-Version $JSonFile
} }
} }
@@ -382,28 +421,11 @@ function Resolve-Installation-Path([string]$InstallDir) {
return $InstallDir return $InstallDir
} }
function Get-Version-Info-From-Version-File([string]$InstallRoot, [string]$RelativePathToVersionFile) {
Say-Invocation $MyInvocation
$VersionFile = Join-Path -Path $InstallRoot -ChildPath $RelativePathToVersionFile
Say-Verbose "Local version file: $VersionFile"
if (Test-Path $VersionFile) {
$VersionText = cat $VersionFile
Say-Verbose "Local version file text: $VersionText"
return Get-Version-Info-From-Version-Text $VersionText
}
Say-Verbose "Local version file not found."
return $null
}
function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) { function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) {
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
} }
@@ -534,7 +556,7 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolde
} }
$CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture $CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture
$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version $SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version -JSonFile $JSonFile
$DownloadLink = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture $DownloadLink = Get-Download-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
$LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture $LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
@@ -636,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
} }
@@ -435,11 +448,53 @@ get_latest_version_info() {
return $? return $?
} }
# args:
# json_file - $1
parse_jsonfile_for_version() {
eval $invocation
local json_file="$1"
if [ ! -f "$json_file" ]; then
say_err "Unable to find \`$json_file\`"
return 1
fi
sdk_section=$(cat $json_file | awk '/"sdk"/,/}/')
if [ -z "$sdk_section" ]; then
say_err "Unable to parse the SDK node in \`$json_file\`"
return 1
fi
sdk_list=$(echo $sdk_section | awk -F"[{}]" '{print $2}')
sdk_list=${sdk_list//[\" ]/}
sdk_list=${sdk_list//,/$'\n'}
sdk_list="$(echo -e "${sdk_list}" | tr -d '[[:space:]]')"
local version_info=""
while read -r line; do
IFS=:
while read -r key value; do
if [[ "$key" == "version" ]]; then
version_info=$value
fi
done <<< "$line"
done <<< "$sdk_list"
if [ -z "$version_info" ]; then
say_err "Unable to find the SDK:version node in \`$json_file\`"
return 1
fi
unset IFS;
echo "$version_info"
return 0
}
# args: # args:
# azure_feed - $1 # azure_feed - $1
# channel - $2 # channel - $2
# normalized_architecture - $3 # normalized_architecture - $3
# version - $4 # version - $4
# json_file - $5
get_specific_version_from_version() { get_specific_version_from_version() {
eval $invocation eval $invocation
@@ -447,27 +502,35 @@ get_specific_version_from_version() {
local channel="$2" local channel="$2"
local normalized_architecture="$3" local normalized_architecture="$3"
local version="$(to_lowercase "$4")" local version="$(to_lowercase "$4")"
local json_file="$5"
case "$version" in if [ -z "$json_file" ]; then
latest) case "$version" in
local version_info latest)
version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1 local version_info
say_verbose "get_specific_version_from_version: version_info=$version_info" version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1
echo "$version_info" | get_version_from_version_info say_verbose "get_specific_version_from_version: version_info=$version_info"
return 0 echo "$version_info" | get_version_from_version_info
;; return 0
coherent) ;;
local version_info coherent)
version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" true)" || return 1 local version_info
say_verbose "get_specific_version_from_version: version_info=$version_info" version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" true)" || return 1
echo "$version_info" | get_version_from_version_info say_verbose "get_specific_version_from_version: version_info=$version_info"
return 0 echo "$version_info" | get_version_from_version_info
;; return 0
*) ;;
echo "$version" *)
return 0 echo "$version"
;; return 0
esac ;;
esac
else
local version_info
version_info="$(parse_jsonfile_for_version "$json_file")" || return 1
echo "$version_info"
return 0
fi
} }
# args: # args:
@@ -558,24 +621,6 @@ resolve_installation_path() {
return 0 return 0
} }
# args:
# install_root - $1
get_installed_version_info() {
eval $invocation
local install_root="$1"
local version_file="$(combine_paths "$install_root" "$local_version_file_relative_path")"
say_verbose "Local version file: $version_file"
if [ ! -z "$version_file" ] | [ -r "$version_file" ]; then
local version_info="$(cat "$version_file")"
echo "$version_info"
return 0
fi
say_verbose "Local version file not found."
return 0
}
# args: # args:
# relative_or_absolute_path - $1 # relative_or_absolute_path - $1
get_absolute_path() { get_absolute_path() {
@@ -600,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";
@@ -724,7 +769,7 @@ calculate_vars() {
normalized_architecture="$(get_normalized_architecture_from_architecture "$architecture")" normalized_architecture="$(get_normalized_architecture_from_architecture "$architecture")"
say_verbose "normalized_architecture=$normalized_architecture" say_verbose "normalized_architecture=$normalized_architecture"
specific_version="$(get_specific_version_from_version "$azure_feed" "$channel" "$normalized_architecture" "$version")" specific_version="$(get_specific_version_from_version "$azure_feed" "$channel" "$normalized_architecture" "$version" "$json_file")"
say_verbose "specific_version=$specific_version" say_verbose "specific_version=$specific_version"
if [ -z "$specific_version" ]; then if [ -z "$specific_version" ]; then
say_err "Could not resolve version information." say_err "Could not resolve version information."
@@ -809,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=("$@")
@@ -826,6 +885,7 @@ temporary_file_template="${TMPDIR:-/tmp}/dotnet.XXXXXXXXX"
channel="LTS" channel="LTS"
version="Latest" version="Latest"
json_file=""
install_dir="<auto>" install_dir="<auto>"
architecture="<auto>" architecture="<auto>"
dry_run=false dry_run=false
@@ -912,6 +972,10 @@ do
runtime_id="$1" runtime_id="$1"
non_dynamic_parameters+=" $name "\""$1"\""" non_dynamic_parameters+=" $name "\""$1"\"""
;; ;;
--jsonfile|-[Jj][Ss]on[Ff]ile)
shift
json_file="$1"
;;
--skip-non-versioned-files|-[Ss]kip[Nn]on[Vv]ersioned[Ff]iles) --skip-non-versioned-files|-[Ss]kip[Nn]on[Vv]ersioned[Ff]iles)
override_non_versioned_files=false override_non_versioned_files=false
non_dynamic_parameters+=" $name" non_dynamic_parameters+=" $name"
@@ -953,22 +1017,25 @@ do
echo " Possible values:" echo " Possible values:"
echo " - dotnet - the Microsoft.NETCore.App shared runtime" echo " - dotnet - the Microsoft.NETCore.App shared runtime"
echo " - aspnetcore - the Microsoft.AspNetCore.App shared runtime" echo " - aspnetcore - the Microsoft.AspNetCore.App shared runtime"
echo " --skip-non-versioned-files Skips non-versioned files if they already exist, such as the dotnet executable."
echo " -SkipNonVersionedFiles"
echo " --dry-run,-DryRun Do not perform installation. Display download link." echo " --dry-run,-DryRun Do not perform installation. Display download link."
echo " --no-path, -NoPath Do not set PATH for the current process." echo " --no-path, -NoPath Do not set PATH for the current process."
echo " --verbose,-Verbose Display diagnostics information." echo " --verbose,-Verbose Display diagnostics information."
echo " --azure-feed,-AzureFeed Azure feed location. Defaults to $azure_feed, This parameter typically is not changed by the user." echo " --azure-feed,-AzureFeed Azure feed location. Defaults to $azure_feed, This parameter typically is not changed by the user."
echo " --uncached-feed,-UncachedFeed Uncached feed location. This parameter typically is not changed by the user." echo " --uncached-feed,-UncachedFeed Uncached feed location. This parameter typically is not changed by the user."
echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly."
echo " --feed-credential,-FeedCredential Azure feed shared access token. This parameter typically is not specified." echo " --feed-credential,-FeedCredential Azure feed shared access token. This parameter typically is not specified."
echo " --skip-non-versioned-files Skips non-versioned files if they already exist, such as the dotnet executable."
echo " -SkipNonVersionedFiles"
echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly."
echo " --jsonfile <JSONFILE> Determines the SDK version from a user specified global.json file."
echo " Note: global.json must have a value for 'SDK:Version'"
echo " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)." echo " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)."
echo " -RuntimeId" echo " -RuntimeId"
echo " -?,--?,-h,--help,-Help Shows this help message" echo " -?,--?,-h,--help,-Help Shows this help message"
echo "" echo ""
echo "Obsolete parameters:" echo "Obsolete parameters:"
echo " --shared-runtime The recommended alternative is '--runtime dotnet'." echo " --shared-runtime The recommended alternative is '--runtime dotnet'."
echo " -SharedRuntime Installs just the shared runtime bits, not the entire SDK." echo " This parameter is obsolete and may be removed in a future version of this script."
echo " Installs just the shared runtime bits, not the entire SDK."
echo "" echo ""
echo "Install Location:" echo "Install Location:"
echo " Location is chosen in following order:" echo " Location is chosen in following order:"

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
@@ -123,9 +123,9 @@ function acquireExternalTool() {
} }
# Download the external tools only for Windows. # Download the external tools only for Windows.
if [[ "$PACKAGERUNTIME" == "win-x64" ]]; then if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/win-x64/node.exe" node12/bin acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.exe" node12/bin
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/win-x64/node.lib" node12/bin acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/$PACKAGERUNTIME/node.lib" node12/bin
if [[ "$PRECACHE" != "" ]]; then if [[ "$PRECACHE" != "" ]]; then
acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere
fi fi
@@ -136,11 +136,14 @@ if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-darwin-x64.tar.gz" node12 fix_nested_dir acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-darwin-x64.tar.gz" node12 fix_nested_dir
fi fi
# Download the external tools common across Linux PACKAGERUNTIMEs (excluding OSX). # Download the external tools for Linux PACKAGERUNTIMEs.
if [[ "$PACKAGERUNTIME" == "linux-x64" || "$PACKAGERUNTIME" == "rhel.6-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 fi
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
acquireExternalTool "$NODE_URL/v${NODE12_VERSION}/node-v${NODE12_VERSION}-linux-arm64.tar.gz" node12 fix_nested_dir
fi fi
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then

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

@@ -1,6 +1,8 @@
using GitHub.Runner.Common.Util; using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using System;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -13,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; }
@@ -51,6 +41,34 @@ namespace GitHub.Runner.Common
[DataMember(EmitDefaultValue = false)] [DataMember(EmitDefaultValue = false)]
public string MonitorSocketAddress { get; set; } public string MonitorSocketAddress { get; set; }
/// <summary>
// Computed property for convenience. Can either return:
// 1. If runner was configured at the repo level, returns something like: "myorg/myrepo"
// 2. If runner was configured at the org level, returns something like: "myorg"
/// </summary>
public string RepoOrOrgName
{
get
{
Uri accountUri = new Uri(this.ServerUrl);
string repoOrOrgName = string.Empty;
if (accountUri.Host.EndsWith(".githubusercontent.com", StringComparison.OrdinalIgnoreCase))
{
Uri gitHubUrl = new Uri(this.GitHubUrl);
// Use the "NWO part" from the GitHub URL path
repoOrOrgName = gitHubUrl.AbsolutePath.Trim('/');
}
else
{
repoOrOrgName = accountUri.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
}
return repoOrOrgName;
}
}
} }
[DataContract] [DataContract]
@@ -121,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);
@@ -131,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}");
@@ -147,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);
@@ -177,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))
{ {
@@ -193,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))
{ {
@@ -209,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

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm;rhel.6-x64;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback> <AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
@@ -28,42 +28,4 @@
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
<DefineConstants>OS_OSX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true' AND '$(Configuration)' == 'Debug'">
<DefineConstants>OS_OSX;DEBUG;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
</Project> </Project>

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

@@ -54,6 +54,8 @@ namespace GitHub.Runner.Common.Util
return "X64"; return "X64";
case Constants.Architecture.Arm: case Constants.Architecture.Arm:
return "ARM"; return "ARM";
case Constants.Architecture.Arm64:
return "ARM64";
default: default:
throw new NotSupportedException(); // Should never reach here. throw new NotSupportedException(); // Should never reach here.
} }

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

@@ -12,8 +12,8 @@ namespace GitHub.Runner.Listener.Configuration
public class OsxServiceControlManager : ServiceControlManager, ILinuxServiceControlManager public class OsxServiceControlManager : ServiceControlManager, ILinuxServiceControlManager
{ {
// This is the name you would see when you do `systemctl list-units | grep runner` // This is the name you would see when you do `systemctl list-units | grep runner`
private const string _svcNamePattern = "actions.runner.{0}.{1}.{2}"; private const string _svcNamePattern = "actions.runner.{0}.{1}";
private const string _svcDisplayPattern = "GitHub Actions Runner ({0}.{1}.{2})"; private const string _svcDisplayPattern = "GitHub Actions Runner ({0}.{1})";
private const string _shTemplate = "darwin.svc.sh.template"; private const string _shTemplate = "darwin.svc.sh.template";
private const string _svcShName = "svc.sh"; private const string _svcShName = "svc.sh";

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using GitHub.Runner.Common; using GitHub.Runner.Common;
using GitHub.Runner.Common.Util; using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
@@ -37,25 +38,38 @@ namespace GitHub.Runner.Listener.Configuration
serviceName = string.Empty; serviceName = string.Empty;
serviceDisplayName = string.Empty; serviceDisplayName = string.Empty;
Uri accountUri = new Uri(settings.ServerUrl); if (string.IsNullOrEmpty(settings.RepoOrOrgName))
string accountName = string.Empty;
if (accountUri.Host.EndsWith(".githubusercontent.com", StringComparison.OrdinalIgnoreCase))
{ {
accountName = accountUri.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); throw new InvalidOperationException($"Cannot find GitHub repository/organization name from server url: '{settings.ServerUrl}'");
}
else
{
accountName = accountUri.Host.Split('.').FirstOrDefault();
} }
if (string.IsNullOrEmpty(accountName)) // For the service name, replace any characters outside of the alpha-numeric set and ".", "_", "-" with "-"
Regex regex = new Regex(@"[^0-9a-zA-Z._\-]");
string repoOrOrgName = regex.Replace(settings.RepoOrOrgName, "-");
serviceName = StringUtil.Format(serviceNamePattern, repoOrOrgName, settings.AgentName);
if (serviceName.Length > 80)
{ {
throw new InvalidOperationException($"Cannot find GitHub organization name from server url: '{settings.ServerUrl}'"); Trace.Verbose($"Calculated service name is too long (> 80 chars). Trying again by calculating a shorter name.");
int exceededCharLength = serviceName.Length - 80;
string repoOrOrgNameSubstring = StringUtil.SubstringPrefix(repoOrOrgName, 45);
exceededCharLength -= repoOrOrgName.Length - repoOrOrgNameSubstring.Length;
string runnerNameSubstring = settings.AgentName;
// Only trim runner name if it's really necessary
if (exceededCharLength > 0)
{
runnerNameSubstring = StringUtil.SubstringPrefix(settings.AgentName, settings.AgentName.Length - exceededCharLength);
}
serviceName = StringUtil.Format(serviceNamePattern, repoOrOrgNameSubstring, runnerNameSubstring);
} }
serviceName = StringUtil.Format(serviceNamePattern, accountName, settings.PoolName, settings.AgentName); serviceDisplayName = StringUtil.Format(serviceDisplayNamePattern, repoOrOrgName, settings.AgentName);
serviceDisplayName = StringUtil.Format(serviceDisplayNamePattern, accountName, settings.PoolName, settings.AgentName);
Trace.Info($"Service name '{serviceName}' display name '{serviceDisplayName}' will be used for service configuration."); Trace.Info($"Service name '{serviceName}' display name '{serviceDisplayName}' will be used for service configuration.");
} }

View File

@@ -13,8 +13,8 @@ namespace GitHub.Runner.Listener.Configuration
public class SystemDControlManager : ServiceControlManager, ILinuxServiceControlManager public class SystemDControlManager : ServiceControlManager, ILinuxServiceControlManager
{ {
// This is the name you would see when you do `systemctl list-units | grep runner` // This is the name you would see when you do `systemctl list-units | grep runner`
private const string _svcNamePattern = "actions.runner.{0}.{1}.{2}.service"; private const string _svcNamePattern = "actions.runner.{0}.{1}.service";
private const string _svcDisplayPattern = "GitHub Actions Runner ({0}.{1}.{2})"; private const string _svcDisplayPattern = "GitHub Actions Runner ({0}.{1})";
private const string _shTemplate = "systemd.svc.sh.template"; private const string _shTemplate = "systemd.svc.sh.template";
private const string _shName = "svc.sh"; private const string _shName = "svc.sh";

View File

@@ -15,8 +15,8 @@ namespace GitHub.Runner.Listener.Configuration
{ {
public const string WindowsServiceControllerName = "RunnerService.exe"; public const string WindowsServiceControllerName = "RunnerService.exe";
private const string ServiceNamePattern = "actionsrunner.{0}.{1}.{2}"; private const string ServiceNamePattern = "actions.runner.{0}.{1}";
private const string ServiceDisplayNamePattern = "GitHub Actions Runner ({0}.{1}.{2})"; private const string ServiceDisplayNamePattern = "GitHub Actions Runner ({0}.{1})";
private INativeWindowsServiceHelper _windowsServiceHelper; private INativeWindowsServiceHelper _windowsServiceHelper;
private ITerminal _term; private ITerminal _term;

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

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm;rhel.6-x64;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback> <AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
@@ -24,49 +24,10 @@
<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' ">
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
<DefineConstants>OS_OSX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true' AND '$(Configuration)' == 'Debug'">
<DefineConstants>OS_OSX;DEBUG;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
</Project> </Project>

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

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm;rhel.6-x64;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback> <AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
@@ -24,42 +24,4 @@
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
<DefineConstants>OS_OSX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true' AND '$(Configuration)' == 'Debug'">
<DefineConstants>OS_OSX;DEBUG;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
</Project> </Project>

View File

@@ -50,29 +50,62 @@ namespace GitHub.Runner.Plugins.Artifact
throw new ArgumentException($"Run Id is not an Int32: {buildIdStr}"); throw new ArgumentException($"Run Id is not an Int32: {buildIdStr}");
} }
context.Output($"Download artifact '{artifactName}' to: '{targetPath}'"); // Determine whether to call Pipelines or Build endpoint to publish artifact based on variable setting
string usePipelinesArtifactEndpointVar = context.Variables.GetValueOrDefault("Runner.UseActionsArtifactsApis")?.Value;
bool.TryParse(usePipelinesArtifactEndpointVar, out bool usePipelinesArtifactEndpoint);
string containerPath;
long containerId;
BuildServer buildHelper = new BuildServer(context.VssConnection); context.Output($"Downloading artifact '{artifactName}' to: '{targetPath}'");
BuildArtifact buildArtifact = await buildHelper.GetArtifact(projectId, buildId, artifactName, token);
if (string.Equals(buildArtifact.Resource.Type, "Container", StringComparison.OrdinalIgnoreCase)) if (usePipelinesArtifactEndpoint)
{ {
string containerUrl = buildArtifact.Resource.Data; context.Debug("Downloading artifact using v2 endpoint");
string[] parts = containerUrl.Split(new[] { '/' }, 3);
if (parts.Length < 3 || !long.TryParse(parts[1], out long containerId)) // Definition ID is a dummy value only used by HTTP client routing purposes
int definitionId = 1;
var pipelinesHelper = new PipelinesServer(context.VssConnection);
var actionsStorageArtifact = await pipelinesHelper.GetActionsStorageArtifact(definitionId, buildId, artifactName, token);
if (actionsStorageArtifact == null)
{ {
throw new ArgumentOutOfRangeException($"Invalid container url '{containerUrl}' for artifact '{buildArtifact.Name}'"); throw new Exception($"The actions storage artifact for '{artifactName}' could not be found, or is no longer available");
} }
string containerPath = parts[2]; containerPath = actionsStorageArtifact.Name; // In actions storage artifacts, name equals the path
FileContainerServer fileContainerServer = new FileContainerServer(context.VssConnection, projectId, containerId, containerPath); containerId = actionsStorageArtifact.ContainerId;
await fileContainerServer.DownloadFromContainerAsync(context, targetPath, token);
} }
else else
{ {
throw new NotSupportedException($"Invalid artifact type: {buildArtifact.Resource.Type}"); context.Debug("Downloading artifact using v1 endpoint");
BuildServer buildHelper = new BuildServer(context.VssConnection);
BuildArtifact buildArtifact = await buildHelper.GetArtifact(projectId, buildId, artifactName, token);
if (string.Equals(buildArtifact.Resource.Type, "Container", StringComparison.OrdinalIgnoreCase) ||
// Artifact was published by Pipelines endpoint, check new type here to handle rollback scenario
string.Equals(buildArtifact.Resource.Type, "Actions_Storage", StringComparison.OrdinalIgnoreCase))
{
string containerUrl = buildArtifact.Resource.Data;
string[] parts = containerUrl.Split(new[] { '/' }, 3);
if (parts.Length < 3 || !long.TryParse(parts[1], out containerId))
{
throw new ArgumentOutOfRangeException($"Invalid container url '{containerUrl}' for artifact '{buildArtifact.Name}'");
}
containerPath = parts[2];
}
else
{
throw new NotSupportedException($"Invalid artifact type: {buildArtifact.Resource.Type}");
}
} }
FileContainerServer fileContainerServer = new FileContainerServer(context.VssConnection, projectId, containerId, containerPath);
await fileContainerServer.DownloadFromContainerAsync(context, targetPath, token);
context.Output("Artifact download finished."); context.Output("Artifact download finished.");
} }
} }

View File

@@ -0,0 +1,60 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using GitHub.Actions.Pipelines.WebApi;
using GitHub.Services.WebApi;
using GitHub.Runner.Sdk;
using Pipelines = GitHub.Actions.Pipelines.WebApi;
namespace GitHub.Runner.Plugins.Artifact
{
// A client wrapper interacting with Pipelines's Artifact API
public class PipelinesServer
{
private readonly PipelinesHttpClient _pipelinesHttpClient;
public PipelinesServer(VssConnection connection)
{
ArgUtil.NotNull(connection, nameof(connection));
_pipelinesHttpClient = connection.GetClient<PipelinesHttpClient>();
}
// Associate the specified Actions Storage artifact with a pipeline
public async Task<Pipelines.ActionsStorageArtifact> AssociateActionsStorageArtifactAsync(
int pipelineId,
int runId,
long containerId,
string name,
long size,
CancellationToken cancellationToken = default(CancellationToken))
{
CreateArtifactParameters parameters = new CreateActionsStorageArtifactParameters()
{
Name = name,
ContainerId = containerId,
Size = size
};
return await _pipelinesHttpClient.CreateArtifactAsync(
parameters,
pipelineId,
runId,
cancellationToken: cancellationToken) as Pipelines.ActionsStorageArtifact;
}
// Get named Actions Storage artifact for a pipeline
public async Task<Pipelines.ActionsStorageArtifact> GetActionsStorageArtifact(
int pipelineId,
int runId,
string name,
CancellationToken cancellationToken)
{
return await _pipelinesHttpClient.GetArtifactAsync(
pipelineId,
runId,
name,
cancellationToken: cancellationToken) as Pipelines.ActionsStorageArtifact;
}
}
}

View File

@@ -68,27 +68,59 @@ namespace GitHub.Runner.Plugins.Artifact
string containerIdStr = context.Variables.GetValueOrDefault(BuildVariables.ContainerId)?.Value ?? string.Empty; string containerIdStr = context.Variables.GetValueOrDefault(BuildVariables.ContainerId)?.Value ?? string.Empty;
if (!long.TryParse(containerIdStr, out long containerId)) if (!long.TryParse(containerIdStr, out long containerId))
{ {
throw new ArgumentException($"Container Id is not a Int64: {containerIdStr}"); throw new ArgumentException($"Container Id is not an Int64: {containerIdStr}");
} }
context.Output($"Uploading artifact '{artifactName}' from '{fullPath}' for run #{buildId}"); context.Output($"Uploading artifact '{artifactName}' from '{fullPath}' for run #{buildId}");
FileContainerServer fileContainerHelper = new FileContainerServer(context.VssConnection, projectId, containerId, artifactName); FileContainerServer fileContainerHelper = new FileContainerServer(context.VssConnection, projectId, containerId, artifactName);
var propertiesDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); var propertiesDictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
long size = 0;
try try
{ {
long size = await fileContainerHelper.CopyToContainerAsync(context, fullPath, token); size = await fileContainerHelper.CopyToContainerAsync(context, fullPath, token);
propertiesDictionary.Add("artifactsize", size.ToString()); propertiesDictionary.Add("artifactsize", size.ToString());
context.Output($"Uploaded '{size}' bytes from '{fullPath}' to server"); context.Output($"Uploaded '{size}' bytes from '{fullPath}' to server");
} }
// if any of the results were successful, make sure to attach them to the build // if any of the results were successful, make sure to attach them to the build
finally finally
{ {
string fileContainerFullPath = StringUtil.Format($"#/{containerId}/{artifactName}"); // Determine whether to call Pipelines or Build endpoint to publish artifact based on variable setting
BuildServer buildHelper = new BuildServer(context.VssConnection); string usePipelinesArtifactEndpointVar = context.Variables.GetValueOrDefault("Runner.UseActionsArtifactsApis")?.Value;
string jobId = context.Variables.GetValueOrDefault(WellKnownDistributedTaskVariables.JobId).Value ?? string.Empty; bool.TryParse(usePipelinesArtifactEndpointVar, out bool usePipelinesArtifactEndpoint);
var artifact = await buildHelper.AssociateArtifact(projectId, buildId, jobId, artifactName, ArtifactResourceTypes.Container, fileContainerFullPath, propertiesDictionary, token);
context.Output($"Associated artifact {artifactName} ({artifact.Id}) with run #{buildId}"); if (usePipelinesArtifactEndpoint)
{
// Definition ID is a dummy value only used by HTTP client routing purposes
int definitionId = 1;
PipelinesServer pipelinesHelper = new PipelinesServer(context.VssConnection);
var artifact = await pipelinesHelper.AssociateActionsStorageArtifactAsync(
definitionId,
buildId,
containerId,
artifactName,
size,
token);
context.Output($"Associated artifact {artifactName} ({artifact.ContainerId}) with run #{buildId}");
context.Debug($"Associated artifact using v2 endpoint");
}
else
{
string fileContainerFullPath = StringUtil.Format($"#/{containerId}/{artifactName}");
BuildServer buildHelper = new BuildServer(context.VssConnection);
string jobId = context.Variables.GetValueOrDefault(WellKnownDistributedTaskVariables.JobId).Value ?? string.Empty;
var artifact = await buildHelper.AssociateArtifact(projectId, buildId, jobId, artifactName, ArtifactResourceTypes.Container, fileContainerFullPath, propertiesDictionary, token);
context.Output($"Associated artifact {artifactName} ({artifact.Id}) with run #{buildId}");
context.Debug($"Associated artifact using v1 endpoint");
}
} }
} }
} }

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

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm;rhel.6-x64;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback> <AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
@@ -20,42 +20,4 @@
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
<DefineConstants>OS_OSX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true' AND '$(Configuration)' == 'Debug'">
<DefineConstants>OS_OSX;DEBUG;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
</Project> </Project>

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

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm;rhel.6-x64;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback> <AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
@@ -25,42 +25,4 @@
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
<DefineConstants>OS_OSX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true' AND '$(Configuration)' == 'Debug'">
<DefineConstants>OS_OSX;DEBUG;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
</Project> </Project>

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

@@ -122,5 +122,10 @@ namespace GitHub.Runner.Sdk
return format; return format;
} }
} }
public static string SubstringPrefix(string value, int count)
{
return value?.Substring(0, Math.Min(value.Length, count));
}
} }
} }

View File

@@ -9,9 +9,8 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>RunnerService</RootNamespace> <RootNamespace>RunnerService</RootNamespace>
<AssemblyName>RunnerService</AssemblyName> <AssemblyName>RunnerService</AssemblyName>
<SignAssembly>true</SignAssembly> <SignAssembly>false</SignAssembly>
<AssemblyOriginatorKeyFile>FinalPublicKey.snk</AssemblyOriginatorKeyFile> <DelaySign>false</DelaySign>
<DelaySign>true</DelaySign>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
@@ -64,7 +63,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />
<None Include="FinalPublicKey.snk" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Resource.resx"> <EmbeddedResource Include="Resource.resx">

View File

@@ -73,7 +73,7 @@ namespace GitHub.Runner.Worker
return false; return false;
} }
// process action command in serialize oreder. // process action command in serialize order.
lock (_commandSerializeLock) lock (_commandSerializeLock)
{ {
if (_stopProcessCommand) if (_stopProcessCommand)
@@ -107,32 +107,19 @@ namespace GitHub.Runner.Worker
} }
else if (_commandExtensions.TryGetValue(actionCommand.Command, out IActionCommandExtension extension)) else if (_commandExtensions.TryGetValue(actionCommand.Command, out IActionCommandExtension extension))
{ {
bool commandHasBeenOutput = false; if (context.EchoOnActionCommand && !extension.OmitEcho)
{
context.Output(input);
}
try try
{ {
if (context.EchoOnActionCommand)
{
context.Output(input);
context.Debug($"Processing command '{actionCommand.Command}'");
commandHasBeenOutput = true;
}
extension.ProcessCommand(context, input, actionCommand); extension.ProcessCommand(context, input, actionCommand);
if (context.EchoOnActionCommand)
{
context.Debug($"Processed command '{actionCommand.Command}' successfully");
}
} }
catch (Exception ex) catch (Exception ex)
{ {
if (!commandHasBeenOutput) var commandInformation = extension.OmitEcho ? extension.Command : input;
{ context.Error($"Unable to process command '{commandInformation}' successfully.");
context.Output(input);
}
context.Error($"Unable to process command '{input}' successfully.");
context.Error(ex); context.Error(ex);
context.CommandResult = TaskResult.Failed; context.CommandResult = TaskResult.Failed;
} }
@@ -151,6 +138,7 @@ namespace GitHub.Runner.Worker
public interface IActionCommandExtension : IExtension public interface IActionCommandExtension : IExtension
{ {
string Command { get; } string Command { get; }
bool OmitEcho { get; }
void ProcessCommand(IExecutionContext context, string line, ActionCommand command); void ProcessCommand(IExecutionContext context, string line, ActionCommand command);
} }
@@ -158,6 +146,7 @@ namespace GitHub.Runner.Worker
public sealed class InternalPluginSetRepoPathCommandExtension : RunnerService, IActionCommandExtension public sealed class InternalPluginSetRepoPathCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "internal-set-repo-path"; public string Command => "internal-set-repo-path";
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -187,6 +176,7 @@ namespace GitHub.Runner.Worker
public sealed class SetEnvCommandExtension : RunnerService, IActionCommandExtension public sealed class SetEnvCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "set-env"; public string Command => "set-env";
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -211,6 +201,7 @@ namespace GitHub.Runner.Worker
public sealed class SetOutputCommandExtension : RunnerService, IActionCommandExtension public sealed class SetOutputCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "set-output"; public string Command => "set-output";
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -234,6 +225,7 @@ namespace GitHub.Runner.Worker
public sealed class SaveStateCommandExtension : RunnerService, IActionCommandExtension public sealed class SaveStateCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "save-state"; public string Command => "save-state";
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -257,6 +249,7 @@ namespace GitHub.Runner.Worker
public sealed class AddMaskCommandExtension : RunnerService, IActionCommandExtension public sealed class AddMaskCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "add-mask"; public string Command => "add-mask";
public bool OmitEcho => true;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -268,6 +261,11 @@ namespace GitHub.Runner.Worker
} }
else else
{ {
if (context.EchoOnActionCommand)
{
context.Output($"::{Command}::***");
}
HostContext.SecretMasker.AddValue(command.Data); HostContext.SecretMasker.AddValue(command.Data);
Trace.Info($"Add new secret mask with length of {command.Data.Length}"); Trace.Info($"Add new secret mask with length of {command.Data.Length}");
} }
@@ -277,6 +275,7 @@ namespace GitHub.Runner.Worker
public sealed class AddPathCommandExtension : RunnerService, IActionCommandExtension public sealed class AddPathCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "add-path"; public string Command => "add-path";
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -291,6 +290,7 @@ namespace GitHub.Runner.Worker
public sealed class AddMatcherCommandExtension : RunnerService, IActionCommandExtension public sealed class AddMatcherCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "add-matcher"; public string Command => "add-matcher";
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -337,6 +337,7 @@ namespace GitHub.Runner.Worker
public sealed class RemoveMatcherCommandExtension : RunnerService, IActionCommandExtension public sealed class RemoveMatcherCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "remove-matcher"; public string Command => "remove-matcher";
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -404,6 +405,7 @@ namespace GitHub.Runner.Worker
public sealed class DebugCommandExtension : RunnerService, IActionCommandExtension public sealed class DebugCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "debug"; public string Command => "debug";
public bool OmitEcho => true;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -431,11 +433,16 @@ namespace GitHub.Runner.Worker
{ {
public abstract IssueType Type { get; } public abstract IssueType Type { get; }
public abstract string Command { get; } public abstract string Command { get; }
public bool OmitEcho => true;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command) public void ProcessCommand(IExecutionContext context, string inputLine, ActionCommand command)
{ {
command.Properties.TryGetValue(IssueCommandProperties.File, out string file);
command.Properties.TryGetValue(IssueCommandProperties.Line, out string line);
command.Properties.TryGetValue(IssueCommandProperties.Column, out string column);
Issue issue = new Issue() Issue issue = new Issue()
{ {
Category = "General", Category = "General",
@@ -443,8 +450,54 @@ namespace GitHub.Runner.Worker
Message = command.Data Message = command.Data
}; };
if (!string.IsNullOrEmpty(file))
{
issue.Category = "Code";
if (context.Container != null)
{
// Translate file path back from container path
file = context.Container.TranslateToHostPath(file);
command.Properties[IssueCommandProperties.File] = file;
}
// Get the values that represent the server path given a local path
string repoName = context.GetGitHubContext("repository");
var repoPath = context.GetGitHubContext("workspace");
string relativeSourcePath = IOUtil.MakeRelative(file, repoPath);
if (!string.Equals(relativeSourcePath, file, IOUtil.FilePathStringComparison))
{
// add repo info
if (!string.IsNullOrEmpty(repoName))
{
command.Properties["repo"] = repoName;
}
if (!string.IsNullOrEmpty(relativeSourcePath))
{
// replace sourcePath with the new relative path
// prefer `/` on all platforms
command.Properties[IssueCommandProperties.File] = relativeSourcePath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}
}
}
foreach (var property in command.Properties)
{
issue.Data[property.Key] = property.Value;
}
context.AddIssue(issue); context.AddIssue(issue);
} }
private static class IssueCommandProperties
{
public const String File = "file";
public const String Line = "line";
public const String Column = "col";
}
} }
public sealed class GroupCommandExtension : GroupingCommandExtension public sealed class GroupCommandExtension : GroupingCommandExtension
@@ -460,6 +513,8 @@ namespace GitHub.Runner.Worker
public abstract class GroupingCommandExtension : RunnerService, IActionCommandExtension public abstract class GroupingCommandExtension : RunnerService, IActionCommandExtension
{ {
public abstract string Command { get; } public abstract string Command { get; }
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
public void ProcessCommand(IExecutionContext context, string line, ActionCommand command) public void ProcessCommand(IExecutionContext context, string line, ActionCommand command)
@@ -472,6 +527,7 @@ namespace GitHub.Runner.Worker
public sealed class EchoCommandExtension : RunnerService, IActionCommandExtension public sealed class EchoCommandExtension : RunnerService, IActionCommandExtension
{ {
public string Command => "echo"; public string Command => "echo";
public bool OmitEcho => false;
public Type ExtensionType => typeof(IActionCommandExtension); public Type ExtensionType => typeof(IActionCommandExtension);
@@ -491,7 +547,6 @@ namespace GitHub.Runner.Worker
break; break;
default: default:
throw new Exception($"Invalid echo command value. Possible values can be: 'on', 'off'. Current value is: '{command.Data}'."); throw new Exception($"Invalid echo command value. Possible values can be: 'on', 'off'. Current value is: '{command.Data}'.");
break;
} }
} }
} }

View File

@@ -58,6 +58,9 @@ namespace GitHub.Runner.Worker
executionContext.Warning("The 'PREVIEW_ACTION_TOKEN' secret is depreciated. Please remove it from the repository's secrets"); executionContext.Warning("The 'PREVIEW_ACTION_TOKEN' secret is depreciated. Please remove it from the repository's secrets");
} }
// Clear the cache (local runner)
IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken);
foreach (var action in actions) foreach (var action in actions)
{ {
if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry) if (action.Reference.Type == Pipelines.ActionSourceType.ContainerRegistry)
@@ -445,7 +448,7 @@ namespace GitHub.Runner.Worker
} }
else else
{ {
// make sure we get an clean folder ready to use. // make sure we get a clean folder ready to use.
IOUtil.DeleteDirectory(destDirectory, executionContext.CancellationToken); IOUtil.DeleteDirectory(destDirectory, executionContext.CancellationToken);
Directory.CreateDirectory(destDirectory); Directory.CreateDirectory(destDirectory);
executionContext.Output($"Download action repository '{repositoryReference.Name}@{repositoryReference.Ref}'"); executionContext.Output($"Download action repository '{repositoryReference.Name}@{repositoryReference.Ref}'");

View File

@@ -94,7 +94,21 @@ namespace GitHub.Runner.Worker
{ {
postDisplayName = $"Post {this.DisplayName}"; postDisplayName = $"Post {this.DisplayName}";
} }
ExecutionContext.RegisterPostJobAction(postDisplayName, handlerData.CleanupCondition, Action);
var repositoryReference = Action.Reference as RepositoryPathReference;
var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}";
var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" :
$"{repositoryReference.Name}{pathString}@{repositoryReference.Ref}";
ExecutionContext.Debug($"Register post job cleanup for action: {repoString}");
var actionRunner = HostContext.CreateService<IActionRunner>();
actionRunner.Action = Action;
actionRunner.Stage = ActionRunStage.Post;
actionRunner.Condition = handlerData.CleanupCondition;
actionRunner.DisplayName = postDisplayName;
ExecutionContext.RegisterPostJobStep($"{actionRunner.Action.Name}_post", actionRunner);
} }
IStepHost stepHost = HostContext.CreateService<IDefaultStepHost>(); IStepHost stepHost = HostContext.CreateService<IDefaultStepHost>();

View File

@@ -276,7 +276,9 @@ namespace GitHub.Runner.Worker.Container
return await ExecuteDockerCommandAsync(context, "exec", $"{options} {containerId} {command}", context.CancellationToken); return await ExecuteDockerCommandAsync(context, "exec", $"{options} {containerId} {command}", context.CancellationToken);
} }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously (method has async logic on only certain platforms)
public async Task<int> DockerExec(IExecutionContext context, string containerId, string options, string command, List<string> output) public async Task<int> DockerExec(IExecutionContext context, string containerId, string options, string command, List<string> output)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{ {
ArgUtil.NotNull(output, nameof(output)); ArgUtil.NotNull(output, nameof(output));
@@ -337,7 +339,9 @@ namespace GitHub.Runner.Worker.Container
return ExecuteDockerCommandAsync(context, command, options, null, cancellationToken); return ExecuteDockerCommandAsync(context, command, options, null, cancellationToken);
} }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously (method has async logic on only certain platforms)
private async Task<int> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options, IDictionary<string, string> environment, EventHandler<ProcessDataReceivedEventArgs> stdoutDataReceived, EventHandler<ProcessDataReceivedEventArgs> stderrDataReceived, CancellationToken cancellationToken = default(CancellationToken)) private async Task<int> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options, IDictionary<string, string> environment, EventHandler<ProcessDataReceivedEventArgs> stdoutDataReceived, EventHandler<ProcessDataReceivedEventArgs> stderrDataReceived, CancellationToken cancellationToken = default(CancellationToken))
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{ {
string arg = $"{command} {options}".Trim(); string arg = $"{command} {options}".Trim();
context.Command($"{DockerPath} {arg}"); context.Command($"{DockerPath} {arg}");
@@ -362,7 +366,9 @@ namespace GitHub.Runner.Worker.Container
#endif #endif
} }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously (method has async logic on only certain platforms)
private async Task<int> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options, string workingDirectory, CancellationToken cancellationToken = default(CancellationToken)) private async Task<int> ExecuteDockerCommandAsync(IExecutionContext context, string command, string options, string workingDirectory, CancellationToken cancellationToken = default(CancellationToken))
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{ {
string arg = $"{command} {options}".Trim(); string arg = $"{command} {options}".Trim();
context.Command($"{DockerPath} {arg}"); context.Command($"{DockerPath} {arg}");

View File

@@ -11,6 +11,7 @@ using GitHub.Runner.Common;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using GitHub.DistributedTask.Pipelines.ContextData; using GitHub.DistributedTask.Pipelines.ContextData;
using Microsoft.Win32; using Microsoft.Win32;
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
namespace GitHub.Runner.Worker namespace GitHub.Runner.Worker
{ {
@@ -38,6 +39,14 @@ namespace GitHub.Runner.Worker
List<ContainerInfo> containers = data as List<ContainerInfo>; List<ContainerInfo> containers = data as List<ContainerInfo>;
ArgUtil.NotNull(containers, nameof(containers)); ArgUtil.NotNull(containers, nameof(containers));
var postJobStep = new JobExtensionRunner(runAsync: this.StopContainersAsync,
condition: $"{PipelineTemplateConstants.Always}()",
displayName: "Stop containers",
data: data);
executionContext.Debug($"Register post job cleanup for stoping/deleting containers.");
executionContext.RegisterPostJobStep(nameof(StopContainersAsync), postJobStep);
// Check whether we are inside a container. // Check whether we are inside a container.
// Our container feature requires to map working directory from host to the container. // Our container feature requires to map working directory from host to the container.
// If we are already inside a container, we will not able to find out the real working direcotry path on the host. // If we are already inside a container, we will not able to find out the real working direcotry path on the host.
@@ -48,9 +57,6 @@ namespace GitHub.Runner.Worker
{ {
throw new NotSupportedException("Container feature is not supported when runner is already running inside container."); throw new NotSupportedException("Container feature is not supported when runner is already running inside container.");
} }
#elif OS_RHEL6
// Red Hat and CentOS 6 do not support the container feature
throw new NotSupportedException("Runner does not support the container feature on Red Hat Enterprise Linux 6 or CentOS 6.");
#else #else
var initProcessCgroup = File.ReadLines("/proc/1/cgroup"); var initProcessCgroup = File.ReadLines("/proc/1/cgroup");
if (initProcessCgroup.Any(x => x.IndexOf(":/docker/", StringComparison.OrdinalIgnoreCase) >= 0)) if (initProcessCgroup.Any(x => x.IndexOf(":/docker/", StringComparison.OrdinalIgnoreCase) >= 0))

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,10 +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";
public async Task UploadDiagnosticLogsAsync(IExecutionContext executionContext, public void UploadDiagnosticLogs(IExecutionContext executionContext,
IExecutionContext parentContext, IExecutionContext parentContext,
Pipelines.AgentJobRequestMessage message, Pipelines.AgentJobRequestMessage message,
DateTime jobStartTimeUtc) 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; }
@@ -98,7 +97,7 @@ namespace GitHub.Runner.Worker
// others // others
void ForceTaskComplete(); void ForceTaskComplete();
void RegisterPostJobAction(string displayName, string condition, Pipelines.ActionStep action); void RegisterPostJobStep(string refName, IStep step);
} }
public sealed class ExecutionContext : RunnerService, IExecutionContext public sealed class ExecutionContext : RunnerService, IExecutionContext
@@ -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;
@@ -240,27 +238,10 @@ namespace GitHub.Runner.Worker
}); });
} }
public void RegisterPostJobAction(string displayName, string condition, Pipelines.ActionStep action) public void RegisterPostJobStep(string refName, IStep step)
{ {
if (action.Reference.Type != ActionSourceType.Repository) step.ExecutionContext = Root.CreatePostChild(step.DisplayName, refName, IntraActionState);
{ Root.PostJobSteps.Push(step);
throw new NotSupportedException("Only action that has `action.yml` can define post job execution.");
}
var repositoryReference = action.Reference as RepositoryPathReference;
var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}";
var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" :
$"{repositoryReference.Name}{pathString}@{repositoryReference.Ref}";
this.Debug($"Register post job cleanup for action: {repoString}");
var actionRunner = HostContext.CreateService<IActionRunner>();
actionRunner.Action = action;
actionRunner.Stage = ActionRunStage.Post;
actionRunner.Condition = condition;
actionRunner.DisplayName = displayName;
actionRunner.ExecutionContext = Root.CreatePostChild(displayName, $"{actionRunner.Action.Name}_post", IntraActionState);
Root.PostJobSteps.Push(actionRunner);
} }
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null) public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null)
@@ -274,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);
@@ -566,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);
@@ -633,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)
@@ -891,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

@@ -22,7 +22,9 @@ namespace GitHub.Runner.Worker.Handlers
{ {
public ContainerActionExecutionData Data { get; set; } public ContainerActionExecutionData Data { get; set; }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously (method has async logic on only certain platforms)
public async Task RunAsync(ActionRunStage stage) public async Task RunAsync(ActionRunStage stage)
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{ {
// Validate args. // Validate args.
Trace.Entering(); Trace.Entering();

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,47 +259,45 @@ 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))
{ {
file = Path.Combine(match.FromPath, file); var fromDirectory = Path.GetDirectoryName(match.FromPath);
if (!string.IsNullOrWhiteSpace(fromDirectory))
{
file = Path.Combine(fromDirectory, file);
}
} }
// Root using system.defaultWorkingDirectory // Root using workspace
if (!Path.IsPathRooted(file)) if (!Path.IsPathFullyQualified(file))
{ {
var githubContext = _executionContext.ExpressionValues["github"] as GitHubContext; var workspace = _executionContext.GetGitHubContext("workspace");
ArgUtil.NotNull(githubContext, nameof(githubContext));
var workspace = githubContext["workspace"].ToString();
ArgUtil.NotNullOrEmpty(workspace, "workspace"); ArgUtil.NotNullOrEmpty(workspace, "workspace");
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");
@@ -315,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

@@ -20,6 +20,7 @@ namespace GitHub.Runner.Worker
public sealed class IssueMatcher public sealed class IssueMatcher
{ {
private string _defaultSeverity;
private string _owner; private string _owner;
private IssuePattern[] _patterns; private IssuePattern[] _patterns;
private IssueMatch[] _state; private IssueMatch[] _state;
@@ -27,6 +28,7 @@ namespace GitHub.Runner.Worker
public IssueMatcher(IssueMatcherConfig config, TimeSpan timeout) public IssueMatcher(IssueMatcherConfig config, TimeSpan timeout)
{ {
_owner = config.Owner; _owner = config.Owner;
_defaultSeverity = config.Severity;
_patterns = config.Patterns.Select(x => new IssuePattern(x , timeout)).ToArray(); _patterns = config.Patterns.Select(x => new IssuePattern(x , timeout)).ToArray();
Reset(); Reset();
} }
@@ -37,13 +39,26 @@ namespace GitHub.Runner.Worker
{ {
if (_owner == null) if (_owner == null)
{ {
_owner = String.Empty; _owner = string.Empty;
} }
return _owner; return _owner;
} }
} }
public string DefaultSeverity
{
get
{
if (_defaultSeverity == null)
{
_defaultSeverity = string.Empty;
}
return _defaultSeverity;
}
}
public IssueMatch Match(string line) public IssueMatch Match(string line)
{ {
// Single pattern // Single pattern
@@ -54,7 +69,7 @@ namespace GitHub.Runner.Worker
if (regexMatch.Success) if (regexMatch.Success)
{ {
return new IssueMatch(null, pattern, regexMatch.Groups); return new IssueMatch(null, pattern, regexMatch.Groups, DefaultSeverity);
} }
return null; return null;
@@ -95,7 +110,7 @@ namespace GitHub.Runner.Worker
} }
// Return // Return
return new IssueMatch(runningMatch, pattern, regexMatch.Groups); return new IssueMatch(runningMatch, pattern, regexMatch.Groups, DefaultSeverity);
} }
// Not the last pattern // Not the last pattern
else else
@@ -169,7 +184,7 @@ namespace GitHub.Runner.Worker
public sealed class IssueMatch public sealed class IssueMatch
{ {
public IssueMatch(IssueMatch runningMatch, IssuePattern pattern, GroupCollection groups) public IssueMatch(IssueMatch runningMatch, IssuePattern pattern, GroupCollection groups, string defaultSeverity = null)
{ {
File = runningMatch?.File ?? GetValue(groups, pattern.File); File = runningMatch?.File ?? GetValue(groups, pattern.File);
Line = runningMatch?.Line ?? GetValue(groups, pattern.Line); Line = runningMatch?.Line ?? GetValue(groups, pattern.Line);
@@ -178,6 +193,11 @@ namespace GitHub.Runner.Worker
Code = runningMatch?.Code ?? GetValue(groups, pattern.Code); Code = runningMatch?.Code ?? GetValue(groups, pattern.Code);
Message = runningMatch?.Message ?? GetValue(groups, pattern.Message); Message = runningMatch?.Message ?? GetValue(groups, pattern.Message);
FromPath = runningMatch?.FromPath ?? GetValue(groups, pattern.FromPath); FromPath = runningMatch?.FromPath ?? GetValue(groups, pattern.FromPath);
if (string.IsNullOrEmpty(Severity) && !string.IsNullOrEmpty(defaultSeverity))
{
Severity = defaultSeverity;
}
} }
public string File { get; } public string File { get; }
@@ -256,6 +276,9 @@ namespace GitHub.Runner.Worker
[DataMember(Name = "owner")] [DataMember(Name = "owner")]
private string _owner; private string _owner;
[DataMember(Name = "severity")]
private string _severity;
[DataMember(Name = "pattern")] [DataMember(Name = "pattern")]
private IssuePatternConfig[] _patterns; private IssuePatternConfig[] _patterns;
@@ -265,7 +288,7 @@ namespace GitHub.Runner.Worker
{ {
if (_owner == null) if (_owner == null)
{ {
_owner = String.Empty; _owner = string.Empty;
} }
return _owner; return _owner;
@@ -277,6 +300,24 @@ namespace GitHub.Runner.Worker
} }
} }
public string Severity
{
get
{
if (_severity == null)
{
_severity = string.Empty;
}
return _severity;
}
set
{
_severity = value;
}
}
public IssuePatternConfig[] Patterns public IssuePatternConfig[] Patterns
{ {
get get
@@ -303,6 +344,17 @@ namespace GitHub.Runner.Worker
throw new ArgumentException("Owner must not be empty"); throw new ArgumentException("Owner must not be empty");
} }
// Validate severity
switch ((_severity ?? string.Empty).ToUpperInvariant())
{
case "":
case "ERROR":
case "WARNING":
break;
default:
throw new ArgumentException($"Matcher '{_owner}' contains unexpected default severity '{_severity}'");
}
// Validate at least one pattern // Validate at least one pattern
if (_patterns == null || _patterns.Length == 0) if (_patterns == null || _patterns.Length == 0)
{ {

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
@@ -110,9 +112,7 @@ namespace GitHub.Runner.Worker
} }
} }
// Build up 3 lists of steps, pre-job, job, post-job // Build up 2 lists of steps, pre-job, job
var postJobStepsBuilder = new Stack<IStep>();
// Download actions not already in the cache // Download actions not already in the cache
Trace.Info("Downloading actions"); Trace.Info("Downloading actions");
var actionManager = HostContext.GetService<IActionManager>(); var actionManager = HostContext.GetService<IActionManager>();
@@ -134,10 +134,6 @@ namespace GitHub.Runner.Worker
condition: $"{PipelineTemplateConstants.Success}()", condition: $"{PipelineTemplateConstants.Success}()",
displayName: "Initialize containers", displayName: "Initialize containers",
data: (object)containers)); data: (object)containers));
postJobStepsBuilder.Push(new JobExtensionRunner(runAsync: containerProvider.StopContainersAsync,
condition: $"{PipelineTemplateConstants.Always}()",
displayName: "Stop containers",
data: (object)containers));
} }
// Add action steps // Add action steps
@@ -187,33 +183,9 @@ namespace GitHub.Runner.Worker
} }
} }
// Add post-job steps
Trace.Info("Adding post-job steps");
while (postJobStepsBuilder.Count > 0)
{
postJobSteps.Add(postJobStepsBuilder.Pop());
}
// Create execution context for post-job steps
foreach (var step in postJobSteps)
{
if (step is JobExtensionRunner)
{
JobExtensionRunner extensionStep = step as JobExtensionRunner;
ArgUtil.NotNull(extensionStep, extensionStep.DisplayName);
Guid stepId = Guid.NewGuid();
extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, stepId.ToString("N"), null, null);
}
}
List<IStep> steps = new List<IStep>(); List<IStep> steps = new List<IStep>();
steps.AddRange(preJobSteps); steps.AddRange(preJobSteps);
steps.AddRange(jobSteps); steps.AddRange(jobSteps);
steps.AddRange(postJobSteps);
// Start agent log plugin host process
// var logPlugin = HostContext.GetService<IAgentLogPlugin>();
// await logPlugin.StartAsync(context, steps, jobContext.CancellationToken);
// Prepare for orphan process cleanup // Prepare for orphan process cleanup
_processCleanup = jobContext.Variables.GetBoolean("process.clean") ?? true; _processCleanup = jobContext.Variables.GetBoolean("process.clean") ?? true;
@@ -258,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));
@@ -272,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.");
@@ -294,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

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework> <TargetFramework>netcoreapp3.0</TargetFramework>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm;rhel.6-x64;osx-x64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64</RuntimeIdentifiers>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch> <TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<AssetTargetFallback>portable-net45+win8</AssetTargetFallback> <AssetTargetFallback>portable-net45+win8</AssetTargetFallback>
<NoWarn>NU1701;NU1603</NoWarn> <NoWarn>NU1701;NU1603</NoWarn>
@@ -35,42 +35,4 @@
<DebugType>portable</DebugType> <DebugType>portable</DebugType>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x64'">
<DefineConstants>OS_WINDOWS;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'win-x86'">
<DefineConstants>OS_WINDOWS;X86;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
<DefineConstants>OS_OSX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true' AND '$(Configuration)' == 'Debug'">
<DefineConstants>OS_OSX;DEBUG;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-x64'">
<DefineConstants>OS_LINUX;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'rhel.6-x64'">
<DefineConstants>OS_LINUX;OS_RHEL6;X64;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true' AND '$(Configuration)' == 'Debug' AND '$(PackageRuntime)' == 'linux-arm'">
<DefineConstants>OS_LINUX;ARM;DEBUG;TRACE</DefineConstants>
</PropertyGroup>
</Project> </Project>

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;
}
}
}

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