mirror of
https://github.com/actions/runner.git
synced 2025-12-10 20:36:49 +00:00
Compare commits
70 Commits
thboop/add
...
v2.323.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8b82f44a9 | ||
|
|
6069f198b7 | ||
|
|
eb20b914ea | ||
|
|
6654f6b3de | ||
|
|
f5e4e7e47c | ||
|
|
68ca457917 | ||
|
|
77700abf81 | ||
|
|
a0ba8fd399 | ||
|
|
6b08f23b6c | ||
|
|
8131246933 | ||
|
|
7211a53c9e | ||
|
|
07310cabc0 | ||
|
|
0195d7ca77 | ||
|
|
259af3eda2 | ||
|
|
0ce29d09c6 | ||
|
|
a84e1c2b15 | ||
|
|
de51cd0ed6 | ||
|
|
3333de3a36 | ||
|
|
b065e5abbe | ||
|
|
bae52e28f9 | ||
|
|
c2c91438e8 | ||
|
|
3486c54ccb | ||
|
|
a61328a7e7 | ||
|
|
52dc98b10f | ||
|
|
a7b319530e | ||
|
|
54f082722f | ||
|
|
ed9d8fc9f7 | ||
|
|
fccbe8fb0b | ||
|
|
e3bc10a931 | ||
|
|
ba50bf6482 | ||
|
|
8eef71d93d | ||
|
|
7ae9fc03a2 | ||
|
|
8e97ad4d86 | ||
|
|
aa76aa476f | ||
|
|
0738df9702 | ||
|
|
8bf52ffe7d | ||
|
|
9df3fc825d | ||
|
|
fde5227fbf | ||
|
|
27f6ca8177 | ||
|
|
078eb3b381 | ||
|
|
c46dac6736 | ||
|
|
e640a9fef3 | ||
|
|
6d266a7c44 | ||
|
|
4700649bb5 | ||
|
|
27580ef8de | ||
|
|
6c94f78f37 | ||
|
|
074d9c0922 | ||
|
|
59f2be2158 | ||
|
|
1e1f7845fa | ||
|
|
694ae12b23 | ||
|
|
d16fb2c593 | ||
|
|
d37a7ae14d | ||
|
|
6ef5803f24 | ||
|
|
2c03d74f11 | ||
|
|
3d34a3c6d6 | ||
|
|
59ec9b4139 | ||
|
|
4a99838fa2 | ||
|
|
af8dee51e1 | ||
|
|
9b3b554758 | ||
|
|
4d8402c260 | ||
|
|
aa0ee2bf64 | ||
|
|
dcc64fead2 | ||
|
|
149123c232 | ||
|
|
e292ec220e | ||
|
|
3696b7d89f | ||
|
|
6d7446a45e | ||
|
|
ddf41af767 | ||
|
|
0b0cb5520d | ||
|
|
4c0a43f0e4 | ||
|
|
65764d9ddc |
@@ -4,10 +4,10 @@
|
|||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
|
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
|
||||||
"ghcr.io/devcontainers/features/dotnet": {
|
"ghcr.io/devcontainers/features/dotnet": {
|
||||||
"version": "6.0.421"
|
"version": "8.0.407"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "16"
|
"version": "20"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/sshd:1": {
|
"ghcr.io/devcontainers/features/sshd:1": {
|
||||||
"version": "latest"
|
"version": "latest"
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -7,7 +7,7 @@ contact_links:
|
|||||||
url: https://github.community/c/code-to-cloud/52
|
url: https://github.community/c/code-to-cloud/52
|
||||||
about: If you have questions about GitHub Actions or need support writing workflows, please ask in the GitHub Community Support forum.
|
about: If you have questions about GitHub Actions or need support writing workflows, please ask in the GitHub Community Support forum.
|
||||||
- name: ✅ Feedback and suggestions for GitHub Actions
|
- name: ✅ Feedback and suggestions for GitHub Actions
|
||||||
url: https://github.com/github/feedback/discussions/categories/actions-and-packages-feedback
|
url: https://github.com/github/feedback/discussions/categories/actions
|
||||||
about: If you have feedback or suggestions about GitHub Actions, please open a discussion (or add to an existing one) in the GitHub Actions Feedback. GitHub Actions Product Managers and Engineers monitor the feedback forum.
|
about: If you have feedback or suggestions about GitHub Actions, please open a discussion (or add to an existing one) in the GitHub Actions Feedback. GitHub Actions Product Managers and Engineers monitor the feedback forum.
|
||||||
- name: ‼️ GitHub Security Bug Bounty
|
- name: ‼️ GitHub Security Bug Bounty
|
||||||
url: https://bounty.github.com/
|
url: https://bounty.github.com/
|
||||||
|
|||||||
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@@ -5,6 +5,11 @@ updates:
|
|||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "daily"
|
||||||
target-branch: "main"
|
target-branch: "main"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
target-branch: "main"
|
||||||
- package-ecosystem: "nuget"
|
- package-ecosystem: "nuget"
|
||||||
directory: "/src"
|
directory: "/src"
|
||||||
schedule:
|
schedule:
|
||||||
|
|||||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
|||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
# Build runner layout
|
# Build runner layout
|
||||||
- name: Build & Layout Release
|
- name: Build & Layout Release
|
||||||
@@ -69,13 +69,13 @@ jobs:
|
|||||||
- name: Package Release
|
- name: Package Release
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
run: |
|
run: |
|
||||||
${{ matrix.devScript }} package Release
|
${{ matrix.devScript }} package Release ${{ matrix.runtime }}
|
||||||
working-directory: src
|
working-directory: src
|
||||||
|
|
||||||
# Upload runner package tar.gz/zip as artifact
|
# Upload runner package tar.gz/zip as artifact
|
||||||
- name: Publish Artifact
|
- name: Publish Artifact
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: runner-package-${{ matrix.runtime }}
|
name: runner-package-${{ matrix.runtime }}
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
2
.github/workflows/close-bugs-bot.yml
vendored
2
.github/workflows/close-bugs-bot.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v8
|
- uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
close-issue-message: "This issue does not seem to be a problem with the runner application, it concerns the GitHub actions platform more generally. Could you please post your feedback on the [GitHub Community Support Forum](https://github.com/orgs/community/discussions/categories/actions) which is actively monitored. Using the forum ensures that we route your problem to the correct team. 😃"
|
close-issue-message: "This issue does not seem to be a problem with the runner application, it concerns the GitHub actions platform more generally. Could you please post your feedback on the [GitHub Community Support Forum](https://github.com/orgs/community/discussions/categories/actions) which is actively monitored. Using the forum ensures that we route your problem to the correct team. 😃"
|
||||||
exempt-issue-labels: "keep"
|
exempt-issue-labels: "keep"
|
||||||
|
|||||||
2
.github/workflows/close-features-bot.yml
vendored
2
.github/workflows/close-features-bot.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v8
|
- uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
close-issue-message: "Thank you for your interest in the runner application and taking the time to provide your valuable feedback. We kindly ask you to redirect this feedback to the [GitHub Community Support Forum](https://github.com/orgs/community/discussions/categories/actions-and-packages) which our team actively monitors and would be a better place to start a discussion for new feature requests in GitHub Actions. For more information on this policy please [read our contribution guidelines](https://github.com/actions/runner#contribute). 😃"
|
close-issue-message: "Thank you for your interest in the runner application and taking the time to provide your valuable feedback. We kindly ask you to redirect this feedback to the [GitHub Community Support Forum](https://github.com/orgs/community/discussions/categories/actions-and-packages) which our team actively monitors and would be a better place to start a discussion for new feature requests in GitHub Actions. For more information on this policy please [read our contribution guidelines](https://github.com/actions/runner#contribute). 😃"
|
||||||
exempt-issue-labels: "keep"
|
exempt-issue-labels: "keep"
|
||||||
|
|||||||
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
@@ -23,11 +23,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
# Override language selection by uncommenting this and choosing your languages
|
# Override language selection by uncommenting this and choosing your languages
|
||||||
# with:
|
# with:
|
||||||
# languages: go, javascript, csharp, python, cpp, java
|
# languages: go, javascript, csharp, python, cpp, java
|
||||||
@@ -38,4 +38,4 @@ jobs:
|
|||||||
working-directory: src
|
working-directory: src
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
|
|||||||
6
.github/workflows/dotnet-upgrade.yml
vendored
6
.github/workflows/dotnet-upgrade.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
DOTNET_CURRENT_MAJOR_MINOR_VERSION: ${{ steps.fetch_current_version.outputs.DOTNET_CURRENT_MAJOR_MINOR_VERSION }}
|
DOTNET_CURRENT_MAJOR_MINOR_VERSION: ${{ steps.fetch_current_version.outputs.DOTNET_CURRENT_MAJOR_MINOR_VERSION }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
- name: Get current major minor version
|
- name: Get current major minor version
|
||||||
id: fetch_current_version
|
id: fetch_current_version
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
run: echo "::error links::feature/dotnet-sdk-upgrade${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }} https://github.com/actions/runner/tree/feature/dotnet-sdk-upgrade${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}::Branch feature/dotnetsdk-upgrade/${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }} already exists. Please take a look and delete that branch if you wish to recreate"
|
run: echo "::error links::feature/dotnet-sdk-upgrade${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }} https://github.com/actions/runner/tree/feature/dotnet-sdk-upgrade${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}::Branch feature/dotnetsdk-upgrade/${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }} already exists. Please take a look and delete that branch if you wish to recreate"
|
||||||
- name: Create a warning annotation if no need to update
|
- name: Create a warning annotation if no need to update
|
||||||
if: ${{ steps.fetch_latest_version.outputs.SHOULD_UPDATE == 0 && steps.fetch_latest_version.outputs.BRANCH_EXISTS == 0 }}
|
if: ${{ steps.fetch_latest_version.outputs.SHOULD_UPDATE == 0 && steps.fetch_latest_version.outputs.BRANCH_EXISTS == 0 }}
|
||||||
run: echo "::warning ::Latest DotNet SDK patch is ${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}, and we are on ${{ steps.fetch_latest_version.outputs.DOTNET_CURRENT_MAJOR_MINOR_PATCH_VERSION }}. No need to update"
|
run: echo "::warning ::Latest DotNet SDK patch is ${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}, and we are on ${{ steps.fetch_current_version.outputs.DOTNET_CURRENT_MAJOR_MINOR_PATCH_VERSION }}. No need to update"
|
||||||
- name: Update patch version
|
- name: Update patch version
|
||||||
if: ${{ steps.fetch_latest_version.outputs.SHOULD_UPDATE == 1 && steps.fetch_latest_version.outputs.BRANCH_EXISTS == 0 }}
|
if: ${{ steps.fetch_latest_version.outputs.SHOULD_UPDATE == 1 && steps.fetch_latest_version.outputs.BRANCH_EXISTS == 0 }}
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
if: ${{ needs.dotnet-update.outputs.SHOULD_UPDATE == 1 && needs.dotnet-update.outputs.BRANCH_EXISTS == 0 }}
|
if: ${{ needs.dotnet-update.outputs.SHOULD_UPDATE == 1 && needs.dotnet-update.outputs.BRANCH_EXISTS == 0 }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: feature/dotnetsdk-upgrade/${{ needs.dotnet-update.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}
|
ref: feature/dotnetsdk-upgrade/${{ needs.dotnet-update.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
|
|||||||
24
.github/workflows/lint.yml
vendored
24
.github/workflows/lint.yml
vendored
@@ -1,24 +0,0 @@
|
|||||||
name: Lint
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
# Ensure full list of changed files within `super-linter`
|
|
||||||
fetch-depth: 0
|
|
||||||
- name: Run linters
|
|
||||||
uses: github/super-linter@v4
|
|
||||||
env:
|
|
||||||
DEFAULT_BRANCH: ${{ github.base_ref }}
|
|
||||||
EDITORCONFIG_FILE_NAME: .editorconfig
|
|
||||||
LINTER_RULES_PATH: /src/
|
|
||||||
VALIDATE_ALL_CODEBASE: false
|
|
||||||
VALIDATE_CSHARP: true
|
|
||||||
70
.github/workflows/publish-image.yml
vendored
70
.github/workflows/publish-image.yml
vendored
@@ -1,70 +0,0 @@
|
|||||||
name: Publish Runner Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
runnerVersion:
|
|
||||||
type: string
|
|
||||||
description: Version of the runner being installed
|
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: ghcr.io
|
|
||||||
IMAGE_NAME: ${{ github.repository_owner }}/actions-runner
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Compute image version
|
|
||||||
id: image
|
|
||||||
uses: actions/github-script@v6
|
|
||||||
env:
|
|
||||||
RUNNER_VERSION: ${{ github.event.inputs.runnerVersion }}
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const fs = require('fs');
|
|
||||||
const inputRunnerVersion = process.env.RUNNER_VERSION;
|
|
||||||
if (inputRunnerVersion) {
|
|
||||||
console.log(`Using input runner version ${inputRunnerVersion}`)
|
|
||||||
core.setOutput('version', inputRunnerVersion);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const runnerVersion = fs.readFileSync('${{ github.workspace }}/src/runnerversion', 'utf8').replace(/\n$/g, '')
|
|
||||||
console.log(`Using runner version ${runnerVersion}`)
|
|
||||||
core.setOutput('version', runnerVersion);
|
|
||||||
|
|
||||||
- name: Setup Docker buildx
|
|
||||||
uses: docker/setup-buildx-action@v2
|
|
||||||
|
|
||||||
- name: Log into registry ${{ env.REGISTRY }}
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: ${{ env.REGISTRY }}
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
id: build-and-push
|
|
||||||
uses: docker/build-push-action@v3
|
|
||||||
with:
|
|
||||||
context: ./images
|
|
||||||
platforms: |
|
|
||||||
linux/amd64
|
|
||||||
linux/arm64
|
|
||||||
tags: |
|
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.image.outputs.version }}
|
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
|
|
||||||
build-args: |
|
|
||||||
RUNNER_VERSION=${{ steps.image.outputs.version }}
|
|
||||||
push: true
|
|
||||||
labels: |
|
|
||||||
org.opencontainers.image.source=${{github.server_url}}/${{github.repository}}
|
|
||||||
org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }}
|
|
||||||
org.opencontainers.image.licenses=MIT
|
|
||||||
74
.github/workflows/release.yml
vendored
74
.github/workflows/release.yml
vendored
@@ -11,16 +11,15 @@ jobs:
|
|||||||
if: startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main'
|
if: startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
# Make sure ./releaseVersion match ./src/runnerversion
|
# Make sure ./releaseVersion match ./src/runnerversion
|
||||||
# Query GitHub release ensure version is not used
|
# Query GitHub release ensure version is not used
|
||||||
- name: Check version
|
- name: Check version
|
||||||
uses: actions/github-script@0.3.0
|
uses: actions/github-script@v7.0.1
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
const core = require('@actions/core')
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const runnerVersion = fs.readFileSync('${{ github.workspace }}/src/runnerversion', 'utf8').replace(/\n$/g, '')
|
const runnerVersion = fs.readFileSync('${{ github.workspace }}/src/runnerversion', 'utf8').replace(/\n$/g, '')
|
||||||
const releaseVersion = fs.readFileSync('${{ github.workspace }}/releaseVersion', 'utf8').replace(/\n$/g, '')
|
const releaseVersion = fs.readFileSync('${{ github.workspace }}/releaseVersion', 'utf8').replace(/\n$/g, '')
|
||||||
@@ -30,7 +29,7 @@ jobs:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const release = await github.repos.getReleaseByTag({
|
const release = await github.rest.repos.getReleaseByTag({
|
||||||
owner: '${{ github.event.repository.owner.name }}',
|
owner: '${{ github.event.repository.owner.name }}',
|
||||||
repo: '${{ github.event.repository.name }}',
|
repo: '${{ github.event.repository.name }}',
|
||||||
tag: 'v' + runnerVersion
|
tag: 'v' + runnerVersion
|
||||||
@@ -87,7 +86,7 @@ jobs:
|
|||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
# Build runner layout
|
# Build runner layout
|
||||||
- name: Build & Layout Release
|
- name: Build & Layout Release
|
||||||
@@ -117,12 +116,11 @@ jobs:
|
|||||||
working-directory: _package
|
working-directory: _package
|
||||||
|
|
||||||
# Upload runner package tar.gz/zip as artifact.
|
# 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
|
- name: Publish Artifact
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: runner-packages
|
name: runner-packages-${{ matrix.runtime }}
|
||||||
path: |
|
path: |
|
||||||
_package
|
_package
|
||||||
|
|
||||||
@@ -131,23 +129,52 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
# Download runner package tar.gz/zip produced by 'build' job
|
# Download runner package tar.gz/zip produced by 'build' job
|
||||||
- name: Download Artifact
|
- name: Download Artifact (win-x64)
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: runner-packages
|
name: runner-packages-win-x64
|
||||||
|
path: ./
|
||||||
|
- name: Download Artifact (win-arm64)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: runner-packages-win-arm64
|
||||||
|
path: ./
|
||||||
|
- name: Download Artifact (osx-x64)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: runner-packages-osx-x64
|
||||||
|
path: ./
|
||||||
|
- name: Download Artifact (osx-arm64)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: runner-packages-osx-arm64
|
||||||
|
path: ./
|
||||||
|
- name: Download Artifact (linux-x64)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: runner-packages-linux-x64
|
||||||
|
path: ./
|
||||||
|
- name: Download Artifact (linux-arm)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: runner-packages-linux-arm
|
||||||
|
path: ./
|
||||||
|
- name: Download Artifact (linux-arm64)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: runner-packages-linux-arm64
|
||||||
path: ./
|
path: ./
|
||||||
|
|
||||||
# Create ReleaseNote file
|
# Create ReleaseNote file
|
||||||
- name: Create ReleaseNote
|
- name: Create ReleaseNote
|
||||||
id: releaseNote
|
id: releaseNote
|
||||||
uses: actions/github-script@0.3.0
|
uses: actions/github-script@v7.0.1
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||||
script: |
|
script: |
|
||||||
const core = require('@actions/core')
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const runnerVersion = fs.readFileSync('${{ github.workspace }}/src/runnerversion', 'utf8').replace(/\n$/g, '')
|
const runnerVersion = fs.readFileSync('${{ github.workspace }}/src/runnerversion', 'utf8').replace(/\n$/g, '')
|
||||||
var releaseNote = fs.readFileSync('${{ github.workspace }}/releaseNote.md', 'utf8').replace(/<RUNNER_VERSION>/g, runnerVersion)
|
var releaseNote = fs.readFileSync('${{ github.workspace }}/releaseNote.md', 'utf8').replace(/<RUNNER_VERSION>/g, runnerVersion)
|
||||||
@@ -262,16 +289,18 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
id-token: write
|
||||||
|
attestations: write
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
IMAGE_NAME: ${{ github.repository_owner }}/actions-runner
|
IMAGE_NAME: ${{ github.repository_owner }}/actions-runner
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Compute image version
|
- name: Compute image version
|
||||||
id: image
|
id: image
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v7.0.1
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
@@ -280,10 +309,10 @@ jobs:
|
|||||||
core.setOutput('version', runnerVersion);
|
core.setOutput('version', runnerVersion);
|
||||||
|
|
||||||
- name: Setup Docker buildx
|
- name: Setup Docker buildx
|
||||||
uses: docker/setup-buildx-action@v2
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Log into registry ${{ env.REGISTRY }}
|
- name: Log into registry ${{ env.REGISTRY }}
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -291,7 +320,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: docker/build-push-action@v3
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: ./images
|
context: ./images
|
||||||
platforms: |
|
platforms: |
|
||||||
@@ -307,3 +336,10 @@ jobs:
|
|||||||
org.opencontainers.image.source=${{github.server_url}}/${{github.repository}}
|
org.opencontainers.image.source=${{github.server_url}}/${{github.repository}}
|
||||||
org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }}
|
org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }}
|
||||||
org.opencontainers.image.licenses=MIT
|
org.opencontainers.image.licenses=MIT
|
||||||
|
|
||||||
|
- name: Generate attestation
|
||||||
|
uses: actions/attest-build-provenance@v2
|
||||||
|
with:
|
||||||
|
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
subject-digest: ${{ steps.build-and-push.outputs.digest }}
|
||||||
|
push-to-registry: true
|
||||||
|
|||||||
2
.github/workflows/stale-bot.yml
vendored
2
.github/workflows/stale-bot.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v8
|
- uses: actions/stale@v9
|
||||||
with:
|
with:
|
||||||
stale-issue-message: "This issue is stale because it has been open 365 days with no activity. Remove stale label or comment or this will be closed in 15 days."
|
stale-issue-message: "This issue is stale because it has been open 365 days with no activity. Remove stale label or comment or this will be closed in 15 days."
|
||||||
close-issue-message: "This issue was closed because it has been stalled for 15 days with no activity."
|
close-issue-message: "This issue was closed because it has been stalled for 15 days with no activity."
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -26,4 +26,5 @@ _dotnetsdk
|
|||||||
TestResults
|
TestResults
|
||||||
TestLogs
|
TestLogs
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.mono
|
||||||
**/*.DotSettings.user
|
**/*.DotSettings.user
|
||||||
@@ -23,7 +23,7 @@ This feature is mainly intended for self hosted runner administrators.
|
|||||||
- `ACTIONS_RUNNER_HOOK_JOB_STARTED`
|
- `ACTIONS_RUNNER_HOOK_JOB_STARTED`
|
||||||
- `ACTIONS_RUNNER_HOOK_JOB_COMPLETED`
|
- `ACTIONS_RUNNER_HOOK_JOB_COMPLETED`
|
||||||
|
|
||||||
You can set these variables to the **absolute** path of a a `.sh` or `.ps1` file.
|
You can set these variables to the **absolute** path of a `.sh` or `.ps1` file.
|
||||||
|
|
||||||
We will execute `pwsh` (fallback to `powershell`) or `bash` (fallback to `sh`) as appropriate.
|
We will execute `pwsh` (fallback to `powershell`) or `bash` (fallback to `sh`) as appropriate.
|
||||||
- `.sh` files will execute with the args `-e {pathtofile}`
|
- `.sh` files will execute with the args `-e {pathtofile}`
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
Make sure the built-in node.js has access to GitHub.com or GitHub Enterprise Server.
|
Make sure the built-in node.js has access to GitHub.com or GitHub Enterprise Server.
|
||||||
|
|
||||||
The runner carries its own copy of node.js executable under `<runner_root>/externals/node16/`.
|
The runner carries its own copy of node.js executable under `<runner_root>/externals/node20/`.
|
||||||
|
|
||||||
All javascript base Actions will get executed by the built-in `node` at `<runner_root>/externals/node16/`.
|
All javascript base Actions will get executed by the built-in `node` at `<runner_root>/externals/node20/`.
|
||||||
|
|
||||||
> Not the `node` from `$PATH`
|
> Not the `node` from `$PATH`
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Contributions
|
# Contributions
|
||||||
|
|
||||||
We welcome contributions in the form of issues and pull requests. We view the contributions and the process as the same for github and external contributors.Please note the runner typically requires changes across the entire system and we aim for issues in the runner to be entirely self contained and fixable here. Therefore, we will primarily handle bug issues opened in this repo and we kindly request you to create all feature and enhancement requests on the [GitHub Feedback](https://github.com/community/community/discussions/categories/actions-and-packages) page.
|
We welcome contributions in the form of issues and pull requests. We view the contributions and the process as the same for github and external contributors. Please note the runner typically requires changes across the entire system and we aim for issues in the runner to be entirely self contained and fixable here. Therefore, we will primarily handle bug issues opened in this repo and we kindly request you to create all feature and enhancement requests on the [GitHub Feedback](https://github.com/community/community/discussions/categories/actions-and-packages) page.
|
||||||
|
|
||||||
> IMPORTANT: Building your own runner is critical for the dev inner loop process when contributing changes. However, only runners built and distributed by GitHub (releases) are supported in production. Be aware that workflows and orchestrations run service side with the runner being a remote process to run steps. For that reason, the service can pull the runner forward so customizations can be lost.
|
> IMPORTANT: Building your own runner is critical for the dev inner loop process when contributing changes. However, only runners built and distributed by GitHub (releases) are supported in production. Be aware that workflows and orchestrations run service side with the runner being a remote process to run steps. For that reason, the service can pull the runner forward so customizations can be lost.
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Source: https://github.com/dotnet/dotnet-docker
|
# Source: https://github.com/dotnet/dotnet-docker
|
||||||
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-jammy as build
|
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy AS build
|
||||||
|
|
||||||
ARG TARGETOS
|
ARG TARGETOS
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ARG RUNNER_VERSION
|
ARG RUNNER_VERSION
|
||||||
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.1
|
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.6.1
|
||||||
ARG DOCKER_VERSION=27.1.1
|
ARG DOCKER_VERSION=28.0.1
|
||||||
ARG BUILDX_VERSION=0.16.2
|
ARG BUILDX_VERSION=0.21.2
|
||||||
|
|
||||||
RUN apt update -y && apt install curl unzip -y
|
RUN apt update -y && apt install curl unzip -y
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ RUN export RUNNER_ARCH=${TARGETARCH} \
|
|||||||
"https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-${TARGETARCH}" \
|
"https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-${TARGETARCH}" \
|
||||||
&& chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx
|
&& chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/runtime-deps:6.0-jammy
|
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
ENV RUNNER_MANUALLY_TRAP_SIG=1
|
ENV RUNNER_MANUALLY_TRAP_SIG=1
|
||||||
@@ -47,7 +47,8 @@ RUN apt update -y \
|
|||||||
# Configure git-core/ppa based on guidance here: https://git-scm.com/download/linux
|
# Configure git-core/ppa based on guidance here: https://git-scm.com/download/linux
|
||||||
RUN add-apt-repository ppa:git-core/ppa \
|
RUN add-apt-repository ppa:git-core/ppa \
|
||||||
&& apt update -y \
|
&& apt update -y \
|
||||||
&& apt install -y --no-install-recommends git
|
&& apt install -y git \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN adduser --disabled-password --gecos "" --uid 1001 runner \
|
RUN adduser --disabled-password --gecos "" --uid 1001 runner \
|
||||||
&& groupadd docker --gid 123 \
|
&& groupadd docker --gid 123 \
|
||||||
|
|||||||
@@ -1,10 +1,36 @@
|
|||||||
## What's Changed
|
## What's Changed
|
||||||
|
* Bump docker/login-action from 2 to 3 by @dependabot in https://github.com/actions/runner/pull/3673
|
||||||
|
* Bump actions/stale from 8 to 9 by @dependabot in https://github.com/actions/runner/pull/3554
|
||||||
|
* Bump docker/build-push-action from 3 to 6 by @dependabot in https://github.com/actions/runner/pull/3674
|
||||||
|
* update node version from 20.18.0 -> 20.18.2 by @aiqiaoy in https://github.com/actions/runner/pull/3682
|
||||||
|
* Pass BillingOwnerId through Acquire/Complete calls by @luketomlinson in https://github.com/actions/runner/pull/3689
|
||||||
|
* Do not retry CompleteJobAsync for known non-retryable errors by @ericsciple in https://github.com/actions/runner/pull/3696
|
||||||
|
* Update dotnet sdk to latest version @8.0.406 by @github-actions in https://github.com/actions/runner/pull/3712
|
||||||
|
* Update Dockerfile with new docker and buildx versions by @thboop in https://github.com/actions/runner/pull/3680
|
||||||
|
* chore: remove redundant words by @finaltrip in https://github.com/actions/runner/pull/3705
|
||||||
|
* fix: actions feedback link is incorrect by @Yaminyam in https://github.com/actions/runner/pull/3165
|
||||||
|
* Bump actions/github-script from 0.3.0 to 7.0.1 by @dependabot in https://github.com/actions/runner/pull/3557
|
||||||
|
* Docker container provenance by @paveliak in https://github.com/actions/runner/pull/3736
|
||||||
|
* Add request-id to http eventsource trace. by @TingluoHuang in https://github.com/actions/runner/pull/3740
|
||||||
|
* Update Bocker and Buildx version to mitigate images scanners alerts by @Blizter in https://github.com/actions/runner/pull/3750
|
||||||
|
* Fix typo, add invariant culture to timestamp for workflow log reporting by @GhadimiR in https://github.com/actions/runner/pull/3749
|
||||||
|
* Create vssconnection to actions service when URL provided. by @TingluoHuang in https://github.com/actions/runner/pull/3751
|
||||||
|
* Housekeeping: Update npm packages and node version by @thboop in https://github.com/actions/runner/pull/3752
|
||||||
|
* Improve the out-of-date warning message. by @tecimovic in https://github.com/actions/runner/pull/3595
|
||||||
|
* Update dotnet sdk to latest version @8.0.407 by @github-actions in https://github.com/actions/runner/pull/3753
|
||||||
|
* Exit hosted runner cleanly during deprovisioning. by @TingluoHuang in https://github.com/actions/runner/pull/3755
|
||||||
|
* Send annotation title to run-service. by @TingluoHuang in https://github.com/actions/runner/pull/3757
|
||||||
|
* Allow server enforce runner settings. by @TingluoHuang in https://github.com/actions/runner/pull/3758
|
||||||
|
* Support refresh runner configs with pipelines service. by @TingluoHuang in https://github.com/actions/runner/pull/3706
|
||||||
|
|
||||||
- .NET 8 OS compatibility test https://github.com/actions/runner/pull/3422
|
## New Contributors
|
||||||
- Ignore ssl cert on websocket client https://github.com/actions/runner/pull/3423
|
* @finaltrip made their first contribution in https://github.com/actions/runner/pull/3705
|
||||||
- Revert "Bump runner to dotnet 8" https://github.com/actions/runner/pull/3412
|
* @Yaminyam made their first contribution in https://github.com/actions/runner/pull/3165
|
||||||
|
* @Blizter made their first contribution in https://github.com/actions/runner/pull/3750
|
||||||
|
* @GhadimiR made their first contribution in https://github.com/actions/runner/pull/3749
|
||||||
|
* @tecimovic made their first contribution in https://github.com/actions/runner/pull/3595
|
||||||
|
|
||||||
**Full Changelog**: https://github.com/actions/runner/compare/v2.318.0...v2.319.0
|
**Full Changelog**: https://github.com/actions/runner/compare/v2.322.0...v2.323.0
|
||||||
|
|
||||||
_Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet.
|
_Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet.
|
||||||
To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository.
|
To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository.
|
||||||
@@ -26,9 +52,7 @@ Add-Type -AssemblyName System.IO.Compression.FileSystem ;
|
|||||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$PWD\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD")
|
[System.IO.Compression.ZipFile]::ExtractToDirectory("$PWD\actions-runner-win-x64-<RUNNER_VERSION>.zip", "$PWD")
|
||||||
```
|
```
|
||||||
|
|
||||||
## [Pre-release] Windows arm64
|
## Windows arm64
|
||||||
|
|
||||||
**Warning:** Windows arm64 runners are currently in preview status and use [unofficial versions of nodejs](https://unofficial-builds.nodejs.org/). They are not intended for production workflows.
|
|
||||||
|
|
||||||
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
We recommend configuring the runner in a root folder of the Windows drive (e.g. "C:\actions-runner"). This will help avoid issues related to service identity folder permissions and long file path restrictions on Windows.
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<Update to ./src/runnerversion when creating release>
|
2.323.0
|
||||||
|
|||||||
@@ -57,4 +57,13 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- Enable NuGet package auditing -->
|
||||||
|
<NuGetAudit>true</NuGetAudit>
|
||||||
|
<!-- Audit direct and transitive packages -->
|
||||||
|
<NuGetAuditMode>all</NuGetAuditMode>
|
||||||
|
<!-- Report low, moderate, high and critical advisories -->
|
||||||
|
<NuGetAuditLevel>moderate</NuGetAuditLevel>
|
||||||
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
1488
src/Misc/dotnet-install.ps1
vendored
1488
src/Misc/dotnet-install.ps1
vendored
File diff suppressed because it is too large
Load Diff
1256
src/Misc/dotnet-install.sh
vendored
1256
src/Misc/dotnet-install.sh
vendored
File diff suppressed because it is too large
Load Diff
824
src/Misc/expressionFunc/hashFiles/package-lock.json
generated
824
src/Misc/expressionFunc/hashFiles/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,6 @@
|
|||||||
"pack": "ncc build -o ../../layoutbin/hashFiles",
|
"pack": "ncc build -o ../../layoutbin/hashFiles",
|
||||||
"all": "npm run format && npm run lint && npm run build && npm run pack",
|
"all": "npm run format && npm run lint && npm run build && npm run pack",
|
||||||
"prepare": "cd ../../../../ && husky install"
|
"prepare": "cd ../../../../ && husky install"
|
||||||
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -43,9 +42,9 @@
|
|||||||
"eslint": "^8.47.0",
|
"eslint": "^8.47.0",
|
||||||
"eslint-plugin-github": "^4.10.0",
|
"eslint-plugin-github": "^4.10.0",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"prettier": "^3.0.3",
|
|
||||||
"typescript": "^5.2.2",
|
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"lint-staged": "^14.0.0"
|
"lint-staged": "^15.5.0",
|
||||||
|
"prettier": "^3.0.3",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,10 @@ PACKAGERUNTIME=$1
|
|||||||
PRECACHE=$2
|
PRECACHE=$2
|
||||||
|
|
||||||
NODE_URL=https://nodejs.org/dist
|
NODE_URL=https://nodejs.org/dist
|
||||||
UNOFFICIAL_NODE_URL=https://unofficial-builds.nodejs.org/download/release
|
|
||||||
NODE_ALPINE_URL=https://github.com/actions/alpine_nodejs/releases/download
|
NODE_ALPINE_URL=https://github.com/actions/alpine_nodejs/releases/download
|
||||||
# When you update Node versions you must also create a new release of alpine_nodejs at that updated version.
|
# When you update Node versions you must also create a new release of alpine_nodejs at that updated version.
|
||||||
# Follow the instructions here: https://github.com/actions/alpine_nodejs?tab=readme-ov-file#getting-started
|
# Follow the instructions here: https://github.com/actions/alpine_nodejs?tab=readme-ov-file#getting-started
|
||||||
NODE16_VERSION="16.20.2"
|
NODE20_VERSION="20.19.0"
|
||||||
NODE20_VERSION="20.13.1"
|
|
||||||
NODE16_UNOFFICIAL_VERSION="16.20.0" # used only for win-arm64, remove node16 unofficial version when official version is available
|
|
||||||
|
|
||||||
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
|
||||||
@@ -140,8 +137,6 @@ function acquireExternalTool() {
|
|||||||
|
|
||||||
# Download the external tools only for Windows.
|
# Download the external tools only for Windows.
|
||||||
if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then
|
if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then
|
||||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.exe" node16/bin
|
|
||||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.lib" node16/bin
|
|
||||||
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.exe" node20/bin
|
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.exe" node20/bin
|
||||||
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.lib" node20/bin
|
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.lib" node20/bin
|
||||||
if [[ "$PRECACHE" != "" ]]; then
|
if [[ "$PRECACHE" != "" ]]; then
|
||||||
@@ -152,8 +147,6 @@ fi
|
|||||||
# Download the external tools only for Windows.
|
# Download the external tools only for Windows.
|
||||||
if [[ "$PACKAGERUNTIME" == "win-arm64" ]]; then
|
if [[ "$PACKAGERUNTIME" == "win-arm64" ]]; then
|
||||||
# todo: replace these with official release when available
|
# todo: replace these with official release when available
|
||||||
acquireExternalTool "$UNOFFICIAL_NODE_URL/v${NODE16_UNOFFICIAL_VERSION}/$PACKAGERUNTIME/node.exe" node16/bin
|
|
||||||
acquireExternalTool "$UNOFFICIAL_NODE_URL/v${NODE16_UNOFFICIAL_VERSION}/$PACKAGERUNTIME/node.lib" node16/bin
|
|
||||||
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.exe" node20/bin
|
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.exe" node20/bin
|
||||||
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.lib" node20/bin
|
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.lib" node20/bin
|
||||||
if [[ "$PRECACHE" != "" ]]; then
|
if [[ "$PRECACHE" != "" ]]; then
|
||||||
@@ -163,30 +156,24 @@ fi
|
|||||||
|
|
||||||
# Download the external tools only for OSX.
|
# Download the external tools only for OSX.
|
||||||
if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then
|
if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then
|
||||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-darwin-x64.tar.gz" node16 fix_nested_dir
|
|
||||||
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-darwin-x64.tar.gz" node20 fix_nested_dir
|
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-darwin-x64.tar.gz" node20 fix_nested_dir
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$PACKAGERUNTIME" == "osx-arm64" ]]; then
|
if [[ "$PACKAGERUNTIME" == "osx-arm64" ]]; then
|
||||||
# node.js v12 doesn't support macOS on arm64.
|
# node.js v12 doesn't support macOS on arm64.
|
||||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-darwin-arm64.tar.gz" node16 fix_nested_dir
|
|
||||||
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-darwin-arm64.tar.gz" node20 fix_nested_dir
|
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-darwin-arm64.tar.gz" node20 fix_nested_dir
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Download the external tools for Linux PACKAGERUNTIMEs.
|
# Download the external tools for Linux PACKAGERUNTIMEs.
|
||||||
if [[ "$PACKAGERUNTIME" == "linux-x64" ]]; then
|
if [[ "$PACKAGERUNTIME" == "linux-x64" ]]; then
|
||||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-x64.tar.gz" node16 fix_nested_dir
|
|
||||||
acquireExternalTool "$NODE_ALPINE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-alpine-x64.tar.gz" node16_alpine
|
|
||||||
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-x64.tar.gz" node20 fix_nested_dir
|
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-x64.tar.gz" node20 fix_nested_dir
|
||||||
acquireExternalTool "$NODE_ALPINE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-alpine-x64.tar.gz" node20_alpine
|
acquireExternalTool "$NODE_ALPINE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-alpine-x64.tar.gz" node20_alpine
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
|
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
|
||||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-arm64.tar.gz" node16 fix_nested_dir
|
|
||||||
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-arm64.tar.gz" node20 fix_nested_dir
|
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-arm64.tar.gz" node20 fix_nested_dir
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
|
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
|
||||||
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-armv7l.tar.gz" node16 fix_nested_dir
|
|
||||||
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-armv7l.tar.gz" node20 fix_nested_dir
|
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-armv7l.tar.gz" node20 fix_nested_dir
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ if [ -f ".path" ]; then
|
|||||||
echo ".path=${PATH}"
|
echo ".path=${PATH}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
nodever=${GITHUB_ACTIONS_RUNNER_FORCED_NODE_VERSION:-node16}
|
nodever="node20"
|
||||||
|
|
||||||
# insert anything to setup env when running as a service
|
# insert anything to setup env when running as a service
|
||||||
# run the host process which keep the listener alive
|
# run the host process which keep the listener alive
|
||||||
|
|||||||
@@ -135,12 +135,17 @@ if [[ "$currentplatform" == 'darwin' && restartinteractiverunner -eq 0 ]]; then
|
|||||||
then
|
then
|
||||||
# inspect the open file handles to find the node process
|
# inspect the open file handles to find the node process
|
||||||
# we can't actually inspect the process using ps because it uses relative paths and doesn't follow symlinks
|
# we can't actually inspect the process using ps because it uses relative paths and doesn't follow symlinks
|
||||||
nodever="node16"
|
nodever="node20"
|
||||||
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
||||||
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node12
|
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node16
|
||||||
then
|
then
|
||||||
nodever="node12"
|
nodever="node16"
|
||||||
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
||||||
|
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node12
|
||||||
|
then
|
||||||
|
nodever="node12"
|
||||||
|
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
if [[ $? -eq 0 && -n "$path" ]]
|
if [[ $? -eq 0 && -n "$path" ]]
|
||||||
then
|
then
|
||||||
@@ -178,6 +183,19 @@ if [[ "$currentplatform" == 'darwin' && restartinteractiverunner -eq 0 ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# update runsvc.sh
|
||||||
|
if [ -f "$rootfolder/runsvc.sh" ]
|
||||||
|
then
|
||||||
|
date "+[%F %T-%4N] Update runsvc.sh" >> "$logfile" 2>&1
|
||||||
|
cat "$rootfolder/bin/runsvc.sh" > "$rootfolder/runsvc.sh"
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
date "+[%F %T-%4N] Can't update $rootfolder/runsvc.sh using $rootfolder/bin/runsvc.sh" >> "$logfile" 2>&1
|
||||||
|
mv -fv "$logfile" "$logfile.failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
date "+[%F %T-%4N] Update succeed" >> "$logfile"
|
date "+[%F %T-%4N] Update succeed" >> "$logfile"
|
||||||
|
|
||||||
touch update.finished
|
touch update.finished
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using GitHub.DistributedTask.Pipelines;
|
|||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
|
using GitHub.Services.WebApi;
|
||||||
using Sdk.RSWebApi.Contracts;
|
using Sdk.RSWebApi.Contracts;
|
||||||
using Sdk.WebApi.WebApi.RawClient;
|
using Sdk.WebApi.WebApi.RawClient;
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ namespace GitHub.Runner.Common
|
|||||||
|
|
||||||
public bool ShouldRetryException(Exception ex)
|
public bool ShouldRetryException(Exception ex)
|
||||||
{
|
{
|
||||||
if (ex is AccessDeniedException ade && ade.ErrorCode == 1)
|
if (ex is AccessDeniedException || ex is RunnerNotFoundException || ex is HostedRunnerDeprovisionedException)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,8 +119,11 @@ namespace GitHub.Runner.Common
|
|||||||
CredentialData GetCredentials();
|
CredentialData GetCredentials();
|
||||||
CredentialData GetMigratedCredentials();
|
CredentialData GetMigratedCredentials();
|
||||||
RunnerSettings GetSettings();
|
RunnerSettings GetSettings();
|
||||||
|
RunnerSettings GetMigratedSettings();
|
||||||
void SaveCredential(CredentialData credential);
|
void SaveCredential(CredentialData credential);
|
||||||
|
void SaveMigratedCredential(CredentialData credential);
|
||||||
void SaveSettings(RunnerSettings settings);
|
void SaveSettings(RunnerSettings settings);
|
||||||
|
void SaveMigratedSettings(RunnerSettings settings);
|
||||||
void DeleteCredential();
|
void DeleteCredential();
|
||||||
void DeleteMigratedCredential();
|
void DeleteMigratedCredential();
|
||||||
void DeleteSettings();
|
void DeleteSettings();
|
||||||
@@ -130,6 +133,7 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
private string _binPath;
|
private string _binPath;
|
||||||
private string _configFilePath;
|
private string _configFilePath;
|
||||||
|
private string _migratedConfigFilePath;
|
||||||
private string _credFilePath;
|
private string _credFilePath;
|
||||||
private string _migratedCredFilePath;
|
private string _migratedCredFilePath;
|
||||||
private string _serviceConfigFilePath;
|
private string _serviceConfigFilePath;
|
||||||
@@ -137,6 +141,7 @@ namespace GitHub.Runner.Common
|
|||||||
private CredentialData _creds;
|
private CredentialData _creds;
|
||||||
private CredentialData _migratedCreds;
|
private CredentialData _migratedCreds;
|
||||||
private RunnerSettings _settings;
|
private RunnerSettings _settings;
|
||||||
|
private RunnerSettings _migratedSettings;
|
||||||
|
|
||||||
public override void Initialize(IHostContext hostContext)
|
public override void Initialize(IHostContext hostContext)
|
||||||
{
|
{
|
||||||
@@ -154,6 +159,9 @@ namespace GitHub.Runner.Common
|
|||||||
_configFilePath = hostContext.GetConfigFile(WellKnownConfigFile.Runner);
|
_configFilePath = hostContext.GetConfigFile(WellKnownConfigFile.Runner);
|
||||||
Trace.Info("ConfigFilePath: {0}", _configFilePath);
|
Trace.Info("ConfigFilePath: {0}", _configFilePath);
|
||||||
|
|
||||||
|
_migratedConfigFilePath = hostContext.GetConfigFile(WellKnownConfigFile.MigratedRunner);
|
||||||
|
Trace.Info("MigratedConfigFilePath: {0}", _migratedConfigFilePath);
|
||||||
|
|
||||||
_credFilePath = hostContext.GetConfigFile(WellKnownConfigFile.Credentials);
|
_credFilePath = hostContext.GetConfigFile(WellKnownConfigFile.Credentials);
|
||||||
Trace.Info("CredFilePath: {0}", _credFilePath);
|
Trace.Info("CredFilePath: {0}", _credFilePath);
|
||||||
|
|
||||||
@@ -169,7 +177,7 @@ namespace GitHub.Runner.Common
|
|||||||
public bool HasCredentials()
|
public bool HasCredentials()
|
||||||
{
|
{
|
||||||
Trace.Info("HasCredentials()");
|
Trace.Info("HasCredentials()");
|
||||||
bool credsStored = (new FileInfo(_credFilePath)).Exists || (new FileInfo(_migratedCredFilePath)).Exists;
|
bool credsStored = new FileInfo(_credFilePath).Exists || new FileInfo(_migratedCredFilePath).Exists;
|
||||||
Trace.Info("stored {0}", credsStored);
|
Trace.Info("stored {0}", credsStored);
|
||||||
return credsStored;
|
return credsStored;
|
||||||
}
|
}
|
||||||
@@ -177,7 +185,7 @@ namespace GitHub.Runner.Common
|
|||||||
public bool IsConfigured()
|
public bool IsConfigured()
|
||||||
{
|
{
|
||||||
Trace.Info("IsConfigured()");
|
Trace.Info("IsConfigured()");
|
||||||
bool configured = new FileInfo(_configFilePath).Exists;
|
bool configured = new FileInfo(_configFilePath).Exists || new FileInfo(_migratedConfigFilePath).Exists;
|
||||||
Trace.Info("IsConfigured: {0}", configured);
|
Trace.Info("IsConfigured: {0}", configured);
|
||||||
return configured;
|
return configured;
|
||||||
}
|
}
|
||||||
@@ -185,7 +193,7 @@ namespace GitHub.Runner.Common
|
|||||||
public bool IsServiceConfigured()
|
public bool IsServiceConfigured()
|
||||||
{
|
{
|
||||||
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}");
|
||||||
return serviceConfigured;
|
return serviceConfigured;
|
||||||
}
|
}
|
||||||
@@ -229,6 +237,25 @@ namespace GitHub.Runner.Common
|
|||||||
return _settings;
|
return _settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RunnerSettings GetMigratedSettings()
|
||||||
|
{
|
||||||
|
if (_migratedSettings == null)
|
||||||
|
{
|
||||||
|
RunnerSettings configuredSettings = null;
|
||||||
|
if (File.Exists(_migratedConfigFilePath))
|
||||||
|
{
|
||||||
|
string json = File.ReadAllText(_migratedConfigFilePath, Encoding.UTF8);
|
||||||
|
Trace.Info($"Read migrated setting file: {json.Length} chars");
|
||||||
|
configuredSettings = StringUtil.ConvertFromJson<RunnerSettings>(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArgUtil.NotNull(configuredSettings, nameof(configuredSettings));
|
||||||
|
_migratedSettings = configuredSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _migratedSettings;
|
||||||
|
}
|
||||||
|
|
||||||
public void SaveCredential(CredentialData credential)
|
public void SaveCredential(CredentialData credential)
|
||||||
{
|
{
|
||||||
Trace.Info("Saving {0} credential @ {1}", credential.Scheme, _credFilePath);
|
Trace.Info("Saving {0} credential @ {1}", credential.Scheme, _credFilePath);
|
||||||
@@ -244,6 +271,21 @@ namespace GitHub.Runner.Common
|
|||||||
File.SetAttributes(_credFilePath, File.GetAttributes(_credFilePath) | FileAttributes.Hidden);
|
File.SetAttributes(_credFilePath, File.GetAttributes(_credFilePath) | FileAttributes.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SaveMigratedCredential(CredentialData credential)
|
||||||
|
{
|
||||||
|
Trace.Info("Saving {0} migrated credential @ {1}", credential.Scheme, _migratedCredFilePath);
|
||||||
|
if (File.Exists(_migratedCredFilePath))
|
||||||
|
{
|
||||||
|
// Delete existing credential file first, since the file is hidden and not able to overwrite.
|
||||||
|
Trace.Info("Delete exist runner migrated credential file.");
|
||||||
|
IOUtil.DeleteFile(_migratedCredFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
IOUtil.SaveObject(credential, _migratedCredFilePath);
|
||||||
|
Trace.Info("Migrated Credentials Saved.");
|
||||||
|
File.SetAttributes(_migratedCredFilePath, File.GetAttributes(_migratedCredFilePath) | FileAttributes.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
public void SaveSettings(RunnerSettings settings)
|
public void SaveSettings(RunnerSettings settings)
|
||||||
{
|
{
|
||||||
Trace.Info("Saving runner settings.");
|
Trace.Info("Saving runner settings.");
|
||||||
@@ -259,6 +301,21 @@ namespace GitHub.Runner.Common
|
|||||||
File.SetAttributes(_configFilePath, File.GetAttributes(_configFilePath) | FileAttributes.Hidden);
|
File.SetAttributes(_configFilePath, File.GetAttributes(_configFilePath) | FileAttributes.Hidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SaveMigratedSettings(RunnerSettings settings)
|
||||||
|
{
|
||||||
|
Trace.Info("Saving runner migrated settings");
|
||||||
|
if (File.Exists(_migratedConfigFilePath))
|
||||||
|
{
|
||||||
|
// Delete existing settings file first, since the file is hidden and not able to overwrite.
|
||||||
|
Trace.Info("Delete exist runner migrated settings file.");
|
||||||
|
IOUtil.DeleteFile(_migratedConfigFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
IOUtil.SaveObject(settings, _migratedConfigFilePath);
|
||||||
|
Trace.Info("Migrated Settings Saved.");
|
||||||
|
File.SetAttributes(_migratedConfigFilePath, File.GetAttributes(_migratedConfigFilePath) | FileAttributes.Hidden);
|
||||||
|
}
|
||||||
|
|
||||||
public void DeleteCredential()
|
public void DeleteCredential()
|
||||||
{
|
{
|
||||||
IOUtil.Delete(_credFilePath, default(CancellationToken));
|
IOUtil.Delete(_credFilePath, default(CancellationToken));
|
||||||
@@ -273,6 +330,12 @@ namespace GitHub.Runner.Common
|
|||||||
public void DeleteSettings()
|
public void DeleteSettings()
|
||||||
{
|
{
|
||||||
IOUtil.Delete(_configFilePath, default(CancellationToken));
|
IOUtil.Delete(_configFilePath, default(CancellationToken));
|
||||||
|
IOUtil.Delete(_migratedConfigFilePath, default(CancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteMigratedSettings()
|
||||||
|
{
|
||||||
|
IOUtil.Delete(_migratedConfigFilePath, default(CancellationToken));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace GitHub.Runner.Common
|
|||||||
public enum WellKnownConfigFile
|
public enum WellKnownConfigFile
|
||||||
{
|
{
|
||||||
Runner,
|
Runner,
|
||||||
|
MigratedRunner,
|
||||||
Credentials,
|
Credentials,
|
||||||
MigratedCredentials,
|
MigratedCredentials,
|
||||||
RSACredentials,
|
RSACredentials,
|
||||||
@@ -159,8 +160,8 @@ namespace GitHub.Runner.Common
|
|||||||
public static class Features
|
public static class Features
|
||||||
{
|
{
|
||||||
public static readonly string DiskSpaceWarning = "runner.diskspace.warning";
|
public static readonly string DiskSpaceWarning = "runner.diskspace.warning";
|
||||||
public static readonly string Node16Warning = "DistributedTask.AddWarningToNode16Action";
|
|
||||||
public static readonly string LogTemplateErrorsAsDebugMessages = "DistributedTask.LogTemplateErrorsAsDebugMessages";
|
public static readonly string LogTemplateErrorsAsDebugMessages = "DistributedTask.LogTemplateErrorsAsDebugMessages";
|
||||||
|
public static readonly string SkipRetryCompleteJobUponKnownErrors = "actions_skip_retry_complete_job_upon_known_errors";
|
||||||
public static readonly string UseContainerPathForTemplate = "DistributedTask.UseContainerPathForTemplate";
|
public static readonly string UseContainerPathForTemplate = "DistributedTask.UseContainerPathForTemplate";
|
||||||
public static readonly string AllowRunnerContainerHooks = "DistributedTask.AllowRunnerContainerHooks";
|
public static readonly string AllowRunnerContainerHooks = "DistributedTask.AllowRunnerContainerHooks";
|
||||||
}
|
}
|
||||||
@@ -176,14 +177,6 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string UnsupportedStopCommandTokenDisabled = "You cannot use a endToken that is an empty string, the string 'pause-logging', or another workflow command. For more information see: https://docs.github.com/actions/learn-github-actions/workflow-commands-for-github-actions#example-stopping-and-starting-workflow-commands or opt into insecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS` environment variable to `true`.";
|
public static readonly string UnsupportedStopCommandTokenDisabled = "You cannot use a endToken that is an empty string, the string 'pause-logging', or another workflow command. For more information see: https://docs.github.com/actions/learn-github-actions/workflow-commands-for-github-actions#example-stopping-and-starting-workflow-commands or opt into insecure command execution by setting the `ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS` environment variable to `true`.";
|
||||||
public static readonly string UnsupportedSummarySize = "$GITHUB_STEP_SUMMARY upload aborted, supports content up to a size of {0}k, got {1}k. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
public static readonly string UnsupportedSummarySize = "$GITHUB_STEP_SUMMARY upload aborted, supports content up to a size of {0}k, got {1}k. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
||||||
public static readonly string SummaryUploadError = "$GITHUB_STEP_SUMMARY upload aborted, an error occurred when uploading the summary. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
public static readonly string SummaryUploadError = "$GITHUB_STEP_SUMMARY upload aborted, an error occurred when uploading the summary. For more information see: https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary";
|
||||||
public static readonly string DetectedNodeAfterEndOfLifeMessage = "Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: {0}. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/.";
|
|
||||||
public static readonly string DeprecatedNodeDetectedAfterEndOfLifeActions = "DeprecatedNodeActionsMessageWarnings";
|
|
||||||
public static readonly string DeprecatedNodeVersion = "node16";
|
|
||||||
public static readonly string EnforcedNode12DetectedAfterEndOfLife = "The following actions uses node12 which is deprecated and will be forced to run on node16: {0}. For more info: https://github.blog/changelog/2023-06-13-github-actions-all-actions-will-run-on-node16-instead-of-node12-by-default/";
|
|
||||||
public static readonly string EnforcedNode12DetectedAfterEndOfLifeEnvVariable = "Node16ForceActionsWarnings";
|
|
||||||
public static readonly string EnforcedNode16DetectedAfterEndOfLife = "The following actions use a deprecated Node.js version and will be forced to run on node20: {0}. For more info: https://github.blog/changelog/2024-03-07-github-actions-all-actions-will-run-on-node20-instead-of-node16-by-default/";
|
|
||||||
public static readonly string EnforcedNode16DetectedAfterEndOfLifeEnvVariable = "Node20ForceActionsWarnings";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RunnerEvent
|
public static class RunnerEvent
|
||||||
@@ -254,20 +247,17 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string RequireJobContainer = "ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER";
|
public static readonly string RequireJobContainer = "ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER";
|
||||||
public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG";
|
public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG";
|
||||||
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
|
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
|
||||||
public static readonly string AllowActionsUseUnsecureNodeVersion = "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION";
|
|
||||||
public static readonly string ManualForceActionsToNode20 = "FORCE_JAVASCRIPT_ACTIONS_TO_NODE20";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Agent
|
public static class Agent
|
||||||
{
|
{
|
||||||
public static readonly string ToolsDirectory = "agent.ToolsDirectory";
|
public static readonly string ToolsDirectory = "agent.ToolsDirectory";
|
||||||
|
|
||||||
// Set this env var to "node12" to downgrade the node version for internal functions (e.g hashfiles). This does NOT affect the version of node actions.
|
// Set this env var to "nodeXY" to downgrade the node version for internal functions (e.g hashfiles). This does NOT affect the version of node actions.
|
||||||
public static readonly string ForcedInternalNodeVersion = "ACTIONS_RUNNER_FORCED_INTERNAL_NODE_VERSION";
|
public static readonly string ForcedInternalNodeVersion = "ACTIONS_RUNNER_FORCED_INTERNAL_NODE_VERSION";
|
||||||
public static readonly string ForcedActionsNodeVersion = "ACTIONS_RUNNER_FORCE_ACTIONS_NODE_VERSION";
|
public static readonly string ForcedActionsNodeVersion = "ACTIONS_RUNNER_FORCE_ACTIONS_NODE_VERSION";
|
||||||
public static readonly string PrintLogToStdout = "ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT";
|
public static readonly string PrintLogToStdout = "ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT";
|
||||||
public static readonly string ActionArchiveCacheDirectory = "ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE";
|
public static readonly string ActionArchiveCacheDirectory = "ACTIONS_RUNNER_ACTION_ARCHIVE_CACHE";
|
||||||
public static readonly string ManualForceActionsToNode20 = "FORCE_JAVASCRIPT_ACTIONS_TO_NODE20";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class System
|
public static class System
|
||||||
@@ -280,10 +270,6 @@ namespace GitHub.Runner.Common
|
|||||||
public static readonly string PhaseDisplayName = "system.phaseDisplayName";
|
public static readonly string PhaseDisplayName = "system.phaseDisplayName";
|
||||||
public static readonly string JobRequestType = "system.jobRequestType";
|
public static readonly string JobRequestType = "system.jobRequestType";
|
||||||
public static readonly string OrchestrationId = "system.orchestrationId";
|
public static readonly string OrchestrationId = "system.orchestrationId";
|
||||||
public static readonly string TestDotNet8Compatibility = "system.testDotNet8Compatibility";
|
|
||||||
public static readonly string DotNet8CompatibilityOutputLength = "system.dotNet8CompatibilityOutputLength";
|
|
||||||
public static readonly string DotNet8CompatibilityOutputPattern = "system.dotNet8CompatibilityOutputPattern";
|
|
||||||
public static readonly string DotNet8CompatibilityWarning = "system.dotNet8CompatibilityWarning";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ namespace GitHub.Runner.Common
|
|||||||
event EventHandler Unloading;
|
event EventHandler Unloading;
|
||||||
void ShutdownRunner(ShutdownReason reason);
|
void ShutdownRunner(ShutdownReason reason);
|
||||||
void WritePerfCounter(string counter);
|
void WritePerfCounter(string counter);
|
||||||
|
void LoadDefaultUserAgents();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StartupType
|
public enum StartupType
|
||||||
@@ -67,6 +68,7 @@ namespace GitHub.Runner.Common
|
|||||||
private StartupType _startupType;
|
private StartupType _startupType;
|
||||||
private string _perfFile;
|
private string _perfFile;
|
||||||
private RunnerWebProxy _webProxy = new();
|
private RunnerWebProxy _webProxy = new();
|
||||||
|
private string _hostType = string.Empty;
|
||||||
|
|
||||||
public event EventHandler Unloading;
|
public event EventHandler Unloading;
|
||||||
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
|
public CancellationToken RunnerShutdownToken => _runnerShutdownTokenSource.Token;
|
||||||
@@ -78,6 +80,7 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
// Validate args.
|
// Validate args.
|
||||||
ArgUtil.NotNullOrEmpty(hostType, nameof(hostType));
|
ArgUtil.NotNullOrEmpty(hostType, nameof(hostType));
|
||||||
|
_hostType = hostType;
|
||||||
|
|
||||||
_loadContext = AssemblyLoadContext.GetLoadContext(typeof(HostContext).GetTypeInfo().Assembly);
|
_loadContext = AssemblyLoadContext.GetLoadContext(typeof(HostContext).GetTypeInfo().Assembly);
|
||||||
_loadContext.Unloading += LoadContext_Unloading;
|
_loadContext.Unloading += LoadContext_Unloading;
|
||||||
@@ -196,6 +199,16 @@ namespace GitHub.Runner.Common
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY")))
|
||||||
|
{
|
||||||
|
_trace.Warning($"Runner is running under insecure mode: HTTPS server certificate validation has been turned off by GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY environment variable.");
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadDefaultUserAgents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadDefaultUserAgents()
|
||||||
|
{
|
||||||
if (string.IsNullOrEmpty(WebProxy.HttpProxyAddress) && string.IsNullOrEmpty(WebProxy.HttpsProxyAddress))
|
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)");
|
_trace.Info($"No proxy settings were found based on environmental variables (http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY)");
|
||||||
@@ -205,11 +218,6 @@ namespace GitHub.Runner.Common
|
|||||||
_userAgents.Add(new ProductInfoHeaderValue("HttpProxyConfigured", bool.TrueString));
|
_userAgents.Add(new ProductInfoHeaderValue("HttpProxyConfigured", bool.TrueString));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable("GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY")))
|
|
||||||
{
|
|
||||||
_trace.Warning($"Runner is running under insecure mode: HTTPS server certificate validation has been turned off by GITHUB_ACTIONS_RUNNER_TLS_NO_VERIFY environment variable.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var credFile = GetConfigFile(WellKnownConfigFile.Credentials);
|
var credFile = GetConfigFile(WellKnownConfigFile.Credentials);
|
||||||
if (File.Exists(credFile))
|
if (File.Exists(credFile))
|
||||||
{
|
{
|
||||||
@@ -248,6 +256,7 @@ namespace GitHub.Runner.Common
|
|||||||
var currentProcess = Process.GetCurrentProcess();
|
var currentProcess = Process.GetCurrentProcess();
|
||||||
_userAgents.Add(new ProductInfoHeaderValue("Pid", currentProcess.Id.ToString()));
|
_userAgents.Add(new ProductInfoHeaderValue("Pid", currentProcess.Id.ToString()));
|
||||||
_userAgents.Add(new ProductInfoHeaderValue("CreationTime", Uri.EscapeDataString(DateTime.UtcNow.ToString("O"))));
|
_userAgents.Add(new ProductInfoHeaderValue("CreationTime", Uri.EscapeDataString(DateTime.UtcNow.ToString("O"))));
|
||||||
|
_userAgents.Add(new ProductInfoHeaderValue($"({_hostType})"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetDirectory(WellKnownDirectory directory)
|
public string GetDirectory(WellKnownDirectory directory)
|
||||||
@@ -334,6 +343,12 @@ namespace GitHub.Runner.Common
|
|||||||
".runner");
|
".runner");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case WellKnownConfigFile.MigratedRunner:
|
||||||
|
path = Path.Combine(
|
||||||
|
GetDirectory(WellKnownDirectory.Root),
|
||||||
|
".runner_migrated");
|
||||||
|
break;
|
||||||
|
|
||||||
case WellKnownConfigFile.Credentials:
|
case WellKnownConfigFile.Credentials:
|
||||||
path = Path.Combine(
|
path = Path.Combine(
|
||||||
GetDirectory(WellKnownDirectory.Root),
|
GetDirectory(WellKnownDirectory.Root),
|
||||||
@@ -620,7 +635,7 @@ namespace GitHub.Runner.Common
|
|||||||
payload[0] = Enum.Parse(typeof(GitHub.Services.Common.VssCredentialsType), ((int)payload[0]).ToString());
|
payload[0] = Enum.Parse(typeof(GitHub.Services.Common.VssCredentialsType), ((int)payload[0]).ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.Length > 0)
|
if (payload.Length > 0 && !string.IsNullOrEmpty(eventData.Message))
|
||||||
{
|
{
|
||||||
message = String.Format(eventData.Message.Replace("%n", Environment.NewLine), payload);
|
message = String.Format(eventData.Message.Replace("%n", Environment.NewLine), payload);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
|
using GitHub.Runner.Sdk;
|
||||||
|
using GitHub.Services.Common;
|
||||||
using GitHub.Services.Launch.Client;
|
using GitHub.Services.Launch.Client;
|
||||||
using GitHub.Services.WebApi;
|
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
{
|
{
|
||||||
@@ -23,8 +24,21 @@ namespace GitHub.Runner.Common
|
|||||||
|
|
||||||
public void InitializeLaunchClient(Uri uri, string token)
|
public void InitializeLaunchClient(Uri uri, string token)
|
||||||
{
|
{
|
||||||
var httpMessageHandler = HostContext.CreateHttpClientHandler();
|
// Using default 100 timeout
|
||||||
this._launchClient = new LaunchHttpClient(uri, httpMessageHandler, token, disposeHandler: true);
|
RawClientHttpRequestSettings settings = VssUtil.GetHttpRequestSettings(null);
|
||||||
|
|
||||||
|
// Create retry handler
|
||||||
|
IEnumerable<DelegatingHandler> delegatingHandlers = new List<DelegatingHandler>();
|
||||||
|
if (settings.MaxRetryRequest > 0)
|
||||||
|
{
|
||||||
|
delegatingHandlers = new DelegatingHandler[] { new VssHttpRetryMessageHandler(settings.MaxRetryRequest) };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup RawHttpMessageHandler without credentials
|
||||||
|
var httpMessageHandler = new RawHttpMessageHandler(new NoOpCredentials(null), settings);
|
||||||
|
var pipeline = HttpClientFactory.CreatePipeline(httpMessageHandler, delegatingHandlers);
|
||||||
|
|
||||||
|
this._launchClient = new LaunchHttpClient(uri, pipeline, token, disposeHandler: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ActionDownloadInfoCollection> ResolveActionsDownloadInfoAsync(Guid planId, Guid jobId, ActionReferenceList actionReferenceList,
|
public Task<ActionDownloadInfoCollection> ResolveActionsDownloadInfoAsync(Guid planId, Guid jobId, ActionReferenceList actionReferenceList,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace GitHub.Runner.Common
|
|||||||
{
|
{
|
||||||
Task ConnectAsync(Uri serverUrl, VssCredentials credentials);
|
Task ConnectAsync(Uri serverUrl, VssCredentials credentials);
|
||||||
|
|
||||||
Task<AgentJobRequestMessage> GetJobMessageAsync(string id, CancellationToken token);
|
Task<AgentJobRequestMessage> GetJobMessageAsync(string id, string billingOwnerId, CancellationToken token);
|
||||||
|
|
||||||
Task CompleteJobAsync(
|
Task CompleteJobAsync(
|
||||||
Guid planId,
|
Guid planId,
|
||||||
@@ -28,6 +28,20 @@ namespace GitHub.Runner.Common
|
|||||||
IList<StepResult> stepResults,
|
IList<StepResult> stepResults,
|
||||||
IList<Annotation> jobAnnotations,
|
IList<Annotation> jobAnnotations,
|
||||||
string environmentUrl,
|
string environmentUrl,
|
||||||
|
IList<Telemetry> telemetry,
|
||||||
|
string billingOwnerId,
|
||||||
|
CancellationToken token);
|
||||||
|
|
||||||
|
Task CompleteJob2Async(
|
||||||
|
Guid planId,
|
||||||
|
Guid jobId,
|
||||||
|
TaskResult result,
|
||||||
|
Dictionary<String, VariableValue> outputs,
|
||||||
|
IList<StepResult> stepResults,
|
||||||
|
IList<Annotation> jobAnnotations,
|
||||||
|
string environmentUrl,
|
||||||
|
IList<Telemetry> telemetry,
|
||||||
|
string billingOwnerId,
|
||||||
CancellationToken token);
|
CancellationToken token);
|
||||||
|
|
||||||
Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken token);
|
Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken token);
|
||||||
@@ -57,17 +71,18 @@ namespace GitHub.Runner.Common
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<AgentJobRequestMessage> GetJobMessageAsync(string id, CancellationToken cancellationToken)
|
public Task<AgentJobRequestMessage> GetJobMessageAsync(string id, string billingOwnerId, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CheckConnection();
|
CheckConnection();
|
||||||
return RetryRequest<AgentJobRequestMessage>(
|
return RetryRequest<AgentJobRequestMessage>(
|
||||||
async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, VarUtil.OS, cancellationToken), cancellationToken,
|
async () => await _runServiceHttpClient.GetJobMessageAsync(requestUri, id, VarUtil.OS, billingOwnerId, cancellationToken), cancellationToken,
|
||||||
shouldRetry: ex =>
|
shouldRetry: ex =>
|
||||||
ex is not TaskOrchestrationJobNotFoundException && // HTTP status 404
|
ex is not TaskOrchestrationJobNotFoundException && // HTTP status 404
|
||||||
ex is not TaskOrchestrationJobAlreadyAcquiredException && // HTTP status 409
|
ex is not TaskOrchestrationJobAlreadyAcquiredException && // HTTP status 409
|
||||||
ex is not TaskOrchestrationJobUnprocessableException); // HTTP status 422
|
ex is not TaskOrchestrationJobUnprocessableException); // HTTP status 422
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Legacy will be deleted when SkipRetryCompleteJobUponKnownErrors is cleaned up
|
||||||
public Task CompleteJobAsync(
|
public Task CompleteJobAsync(
|
||||||
Guid planId,
|
Guid planId,
|
||||||
Guid jobId,
|
Guid jobId,
|
||||||
@@ -76,11 +91,33 @@ namespace GitHub.Runner.Common
|
|||||||
IList<StepResult> stepResults,
|
IList<StepResult> stepResults,
|
||||||
IList<Annotation> jobAnnotations,
|
IList<Annotation> jobAnnotations,
|
||||||
string environmentUrl,
|
string environmentUrl,
|
||||||
|
IList<Telemetry> telemetry,
|
||||||
|
string billingOwnerId,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
CheckConnection();
|
CheckConnection();
|
||||||
return RetryRequest(
|
return RetryRequest(
|
||||||
async () => await _runServiceHttpClient.CompleteJobAsync(requestUri, planId, jobId, result, outputs, stepResults, jobAnnotations, environmentUrl, cancellationToken), cancellationToken);
|
async () => await _runServiceHttpClient.CompleteJobAsync(requestUri, planId, jobId, result, outputs, stepResults, jobAnnotations, environmentUrl, telemetry, billingOwnerId, cancellationToken), cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task CompleteJob2Async(
|
||||||
|
Guid planId,
|
||||||
|
Guid jobId,
|
||||||
|
TaskResult result,
|
||||||
|
Dictionary<String, VariableValue> outputs,
|
||||||
|
IList<StepResult> stepResults,
|
||||||
|
IList<Annotation> jobAnnotations,
|
||||||
|
string environmentUrl,
|
||||||
|
IList<Telemetry> telemetry,
|
||||||
|
string billingOwnerId,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
CheckConnection();
|
||||||
|
return RetryRequest(
|
||||||
|
async () => await _runServiceHttpClient.CompleteJobAsync(requestUri, planId, jobId, result, outputs, stepResults, jobAnnotations, environmentUrl, telemetry, billingOwnerId, cancellationToken), cancellationToken,
|
||||||
|
shouldRetry: ex =>
|
||||||
|
ex is not VssUnauthorizedException && // HTTP status 401
|
||||||
|
ex is not TaskOrchestrationJobNotFoundException); // HTTP status 404
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken cancellationToken)
|
public Task<RenewJobResponse> RenewJobAsync(Guid planId, Guid jobId, CancellationToken cancellationToken)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<NoWarn>NU1701;NU1603</NoWarn>
|
<NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
|
||||||
<Version>$(Version)</Version>
|
<Version>$(Version)</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -15,11 +15,11 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" />
|
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||||
<PackageReference Include="System.Threading.Channels" Version="4.4.0" />
|
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
|||||||
@@ -46,7 +46,11 @@ namespace GitHub.Runner.Common
|
|||||||
var githubApiUrl = "";
|
var githubApiUrl = "";
|
||||||
var gitHubUrlBuilder = new UriBuilder(githubUrl);
|
var gitHubUrlBuilder = new UriBuilder(githubUrl);
|
||||||
var path = gitHubUrlBuilder.Path.Split('/', '\\', StringSplitOptions.RemoveEmptyEntries);
|
var path = gitHubUrlBuilder.Path.Split('/', '\\', StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (path.Length == 1)
|
var isOrgRunner = path.Length == 1;
|
||||||
|
var isRepoOrEnterpriseRunner = path.Length == 2;
|
||||||
|
var isRepoRunner = isRepoOrEnterpriseRunner && !string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (isOrgRunner)
|
||||||
{
|
{
|
||||||
// org runner
|
// org runner
|
||||||
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
||||||
@@ -58,21 +62,31 @@ namespace GitHub.Runner.Common
|
|||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (path.Length == 2)
|
else if (isRepoOrEnterpriseRunner)
|
||||||
{
|
{
|
||||||
// repo or enterprise runner.
|
// Repository runner
|
||||||
if (!string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase))
|
if (isRepoRunner)
|
||||||
{
|
{
|
||||||
return null;
|
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
||||||
}
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/repos/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
||||||
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
}
|
||||||
{
|
else
|
||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/repos/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
// Enterprise runner
|
||||||
|
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -90,7 +104,11 @@ namespace GitHub.Runner.Common
|
|||||||
var githubApiUrl = "";
|
var githubApiUrl = "";
|
||||||
var gitHubUrlBuilder = new UriBuilder(githubUrl);
|
var gitHubUrlBuilder = new UriBuilder(githubUrl);
|
||||||
var path = gitHubUrlBuilder.Path.Split('/', '\\', StringSplitOptions.RemoveEmptyEntries);
|
var path = gitHubUrlBuilder.Path.Split('/', '\\', StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (path.Length == 1)
|
var isOrgRunner = path.Length == 1;
|
||||||
|
var isRepoOrEnterpriseRunner = path.Length == 2;
|
||||||
|
var isRepoRunner = isRepoOrEnterpriseRunner && !string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
if (isOrgRunner)
|
||||||
{
|
{
|
||||||
// org runner
|
// org runner
|
||||||
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
||||||
@@ -102,21 +120,31 @@ namespace GitHub.Runner.Common
|
|||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions/runner-groups";
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions/runner-groups";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (path.Length == 2)
|
else if (isRepoOrEnterpriseRunner)
|
||||||
{
|
{
|
||||||
// repo or enterprise runner.
|
// Repository Runner
|
||||||
if (!string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase))
|
if (isRepoRunner)
|
||||||
{
|
{
|
||||||
return null;
|
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
||||||
}
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/repos/{path[0]}/{path[1]}/actions/runner-groups";
|
||||||
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
}
|
||||||
{
|
else
|
||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runner-groups";
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/repos/{path[0]}/{path[1]}/actions/runner-groups";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runner-groups";
|
// Enterprise Runner
|
||||||
|
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runner-groups";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runner-groups";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using GitHub.DistributedTask.WebApi;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Services.Common;
|
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
|
using GitHub.Services.Common;
|
||||||
|
using GitHub.Services.WebApi;
|
||||||
|
|
||||||
namespace GitHub.Runner.Common
|
namespace GitHub.Runner.Common
|
||||||
{
|
{
|
||||||
@@ -50,7 +50,10 @@ namespace GitHub.Runner.Common
|
|||||||
Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, bool includeToken, CancellationToken cancellationToken);
|
Task<PackageMetadata> GetPackageAsync(string packageType, string platform, string version, bool includeToken, CancellationToken cancellationToken);
|
||||||
|
|
||||||
// agent update
|
// agent update
|
||||||
Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, ulong agentId, string currentState, string trace);
|
Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, ulong agentId, string currentState, string trace, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
// runner config refresh
|
||||||
|
Task<string> RefreshRunnerConfigAsync(int agentId, string configType, string encodedRunnerConfig, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class RunnerServer : RunnerService, IRunnerServer
|
public sealed class RunnerServer : RunnerService, IRunnerServer
|
||||||
@@ -315,10 +318,17 @@ namespace GitHub.Runner.Common
|
|||||||
return _genericTaskAgentClient.GetPackageAsync(packageType, platform, version, includeToken, cancellationToken: cancellationToken);
|
return _genericTaskAgentClient.GetPackageAsync(packageType, platform, version, includeToken, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, ulong agentId, string currentState, string trace)
|
public Task<TaskAgent> UpdateAgentUpdateStateAsync(int agentPoolId, ulong agentId, string currentState, string trace, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
CheckConnection(RunnerConnectionType.Generic);
|
CheckConnection(RunnerConnectionType.Generic);
|
||||||
return _genericTaskAgentClient.UpdateAgentUpdateStateAsync(agentPoolId, agentId, currentState, trace);
|
return _genericTaskAgentClient.UpdateAgentUpdateStateAsync(agentPoolId, agentId, currentState, trace, cancellationToken: cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// runner config refresh
|
||||||
|
public Task<string> RefreshRunnerConfigAsync(int agentId, string configType, string encodedRunnerConfig, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
CheckConnection(RunnerConnectionType.Generic);
|
||||||
|
return _genericTaskAgentClient.RefreshRunnerConfigAsync(agentId, configType, encodedRunnerConfig, cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,8 @@ namespace GitHub.Runner.Common
|
|||||||
|
|
||||||
protected async Task RetryRequest(Func<Task> func,
|
protected async Task RetryRequest(Func<Task> func,
|
||||||
CancellationToken cancellationToken,
|
CancellationToken cancellationToken,
|
||||||
int maxRetryAttemptsCount = 5
|
int maxRetryAttemptsCount = 5,
|
||||||
|
Func<Exception, bool> shouldRetry = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
async Task<Unit> wrappedFunc()
|
async Task<Unit> wrappedFunc()
|
||||||
@@ -78,7 +79,7 @@ namespace GitHub.Runner.Common
|
|||||||
await func();
|
await func();
|
||||||
return Unit.Value;
|
return Unit.Value;
|
||||||
}
|
}
|
||||||
await RetryRequest<Unit>(wrappedFunc, cancellationToken, maxRetryAttemptsCount);
|
await RetryRequest<Unit>(wrappedFunc, cancellationToken, maxRetryAttemptsCount, shouldRetry);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async Task<T> RetryRequest<T>(Func<Task<T>> func,
|
protected async Task<T> RetryRequest<T>(Func<Task<T>> func,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ namespace GitHub.Runner.Common.Util
|
|||||||
{
|
{
|
||||||
public static class NodeUtil
|
public static class NodeUtil
|
||||||
{
|
{
|
||||||
private const string _defaultNodeVersion = "node16";
|
private const string _defaultNodeVersion = "node20";
|
||||||
public static readonly ReadOnlyCollection<string> BuiltInNodeVersions = new(new[] { "node16", "node20" });
|
public static readonly ReadOnlyCollection<string> BuiltInNodeVersions = new(new[] { "node20" });
|
||||||
public static string GetInternalNodeVersion()
|
public static string GetInternalNodeVersion()
|
||||||
{
|
{
|
||||||
var forcedInternalNodeVersion = Environment.GetEnvironmentVariable(Constants.Variables.Agent.ForcedInternalNodeVersion);
|
var forcedInternalNodeVersion = Environment.GetEnvironmentVariable(Constants.Variables.Agent.ForcedInternalNodeVersion);
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
using GitHub.Runner.Common;
|
using GitHub.Runner.Common;
|
||||||
|
using GitHub.Runner.Common.Util;
|
||||||
using GitHub.Runner.Listener.Configuration;
|
using GitHub.Runner.Listener.Configuration;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
using GitHub.Runner.Common.Util;
|
|
||||||
using GitHub.Services.OAuth;
|
using GitHub.Services.OAuth;
|
||||||
|
using GitHub.Services.WebApi;
|
||||||
|
|
||||||
namespace GitHub.Runner.Listener
|
namespace GitHub.Runner.Listener
|
||||||
{
|
{
|
||||||
@@ -26,6 +27,7 @@ namespace GitHub.Runner.Listener
|
|||||||
private CancellationTokenSource _getMessagesTokenSource;
|
private CancellationTokenSource _getMessagesTokenSource;
|
||||||
private VssCredentials _creds;
|
private VssCredentials _creds;
|
||||||
private TaskAgentSession _session;
|
private TaskAgentSession _session;
|
||||||
|
private IRunnerServer _runnerServer;
|
||||||
private IBrokerServer _brokerServer;
|
private IBrokerServer _brokerServer;
|
||||||
private readonly Dictionary<string, int> _sessionCreationExceptionTracker = new();
|
private readonly Dictionary<string, int> _sessionCreationExceptionTracker = new();
|
||||||
private bool _accessTokenRevoked = false;
|
private bool _accessTokenRevoked = false;
|
||||||
@@ -39,6 +41,7 @@ namespace GitHub.Runner.Listener
|
|||||||
base.Initialize(hostContext);
|
base.Initialize(hostContext);
|
||||||
|
|
||||||
_term = HostContext.GetService<ITerminal>();
|
_term = HostContext.GetService<ITerminal>();
|
||||||
|
_runnerServer = HostContext.GetService<IRunnerServer>();
|
||||||
_brokerServer = HostContext.GetService<IBrokerServer>();
|
_brokerServer = HostContext.GetService<IBrokerServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +52,8 @@ namespace GitHub.Runner.Listener
|
|||||||
// Settings
|
// Settings
|
||||||
var configManager = HostContext.GetService<IConfigurationManager>();
|
var configManager = HostContext.GetService<IConfigurationManager>();
|
||||||
_settings = configManager.LoadSettings();
|
_settings = configManager.LoadSettings();
|
||||||
var serverUrl = _settings.ServerUrlV2;
|
var serverUrlV2 = _settings.ServerUrlV2;
|
||||||
|
var serverUrl = _settings.ServerUrl;
|
||||||
Trace.Info(_settings);
|
Trace.Info(_settings);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_settings.ServerUrlV2))
|
if (string.IsNullOrEmpty(_settings.ServerUrlV2))
|
||||||
@@ -83,9 +87,17 @@ namespace GitHub.Runner.Listener
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Trace.Info("Connecting to the Broker Server...");
|
Trace.Info("Connecting to the Broker Server...");
|
||||||
await _brokerServer.ConnectAsync(new Uri(serverUrl), _creds);
|
await _brokerServer.ConnectAsync(new Uri(serverUrlV2), _creds);
|
||||||
Trace.Info("VssConnection created");
|
Trace.Info("VssConnection created");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(serverUrl) &&
|
||||||
|
!string.Equals(serverUrl, serverUrlV2, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Trace.Info("Connecting to the Runner server...");
|
||||||
|
await _runnerServer.ConnectAsync(new Uri(serverUrl), _creds);
|
||||||
|
Trace.Info("VssConnection created");
|
||||||
|
}
|
||||||
|
|
||||||
_term.WriteLine();
|
_term.WriteLine();
|
||||||
_term.WriteSuccessMessage("Connected to GitHub");
|
_term.WriteSuccessMessage("Connected to GitHub");
|
||||||
_term.WriteLine();
|
_term.WriteLine();
|
||||||
@@ -130,7 +142,7 @@ namespace GitHub.Runner.Listener
|
|||||||
// Check whether we get 401 because the runner registration already removed by the service.
|
// Check whether we get 401 because the runner registration already removed by the service.
|
||||||
// If the runner registration get deleted, we can't exchange oauth token.
|
// If the runner registration get deleted, we can't exchange oauth token.
|
||||||
Trace.Error("Test oauth app registration.");
|
Trace.Error("Test oauth app registration.");
|
||||||
var oauthTokenProvider = new VssOAuthTokenProvider(vssOAuthCred, new Uri(serverUrl));
|
var oauthTokenProvider = new VssOAuthTokenProvider(vssOAuthCred, new Uri(serverUrlV2));
|
||||||
var authError = await oauthTokenProvider.ValidateCredentialAsync(token);
|
var authError = await oauthTokenProvider.ValidateCredentialAsync(token);
|
||||||
if (string.Equals(authError, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(authError, "invalid_client", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
@@ -237,10 +249,19 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Info("Runner OAuth token has been revoked. Unable to pull message.");
|
Trace.Info("Runner OAuth token has been revoked. Unable to pull message.");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
catch (HostedRunnerDeprovisionedException)
|
||||||
|
{
|
||||||
|
Trace.Info("Hosted runner has been deprovisioned.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
catch (AccessDeniedException e) when (e.ErrorCode == 1)
|
catch (AccessDeniedException e) when (e.ErrorCode == 1)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
catch (RunnerNotFoundException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Trace.Error("Catch exception during get next message.");
|
Trace.Error("Catch exception during get next message.");
|
||||||
@@ -324,6 +345,7 @@ namespace GitHub.Runner.Listener
|
|||||||
ex is TaskAgentPoolNotFoundException ||
|
ex is TaskAgentPoolNotFoundException ||
|
||||||
ex is TaskAgentSessionExpiredException ||
|
ex is TaskAgentSessionExpiredException ||
|
||||||
ex is AccessDeniedException ||
|
ex is AccessDeniedException ||
|
||||||
|
ex is RunnerNotFoundException ||
|
||||||
ex is VssUnauthorizedException)
|
ex is VssUnauthorizedException)
|
||||||
{
|
{
|
||||||
Trace.Info($"Non-retriable exception: {ex.Message}");
|
Trace.Info($"Non-retriable exception: {ex.Message}");
|
||||||
|
|||||||
@@ -404,6 +404,20 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allow the server to override the serverUrlV2 and useV2Flow
|
||||||
|
if (agent.Properties.TryGetValue("ServerUrlV2", out string serverUrlV2) &&
|
||||||
|
!string.IsNullOrEmpty(serverUrlV2))
|
||||||
|
{
|
||||||
|
Trace.Info($"Service enforced serverUrlV2: {serverUrlV2}");
|
||||||
|
runnerSettings.ServerUrlV2 = serverUrlV2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agent.Properties.TryGetValue("UseV2Flow", out bool useV2Flow) && useV2Flow)
|
||||||
|
{
|
||||||
|
Trace.Info($"Service enforced useV2Flow: {useV2Flow}");
|
||||||
|
runnerSettings.UseV2Flow = useV2Flow;
|
||||||
|
}
|
||||||
|
|
||||||
_term.WriteSection("Runner settings");
|
_term.WriteSection("Runner settings");
|
||||||
|
|
||||||
// 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
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
|
#pragma warning disable CA1416
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -84,4 +85,5 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA1416
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -545,28 +545,36 @@ namespace GitHub.Runner.Listener
|
|||||||
detailInfo = string.Join(Environment.NewLine, workerOutput);
|
detailInfo = string.Join(Environment.NewLine, workerOutput);
|
||||||
Trace.Info($"Return code {returnCode} indicate worker encounter an unhandled exception or app crash, attach worker stdout/stderr to JobRequest result.");
|
Trace.Info($"Return code {returnCode} indicate worker encounter an unhandled exception or app crash, attach worker stdout/stderr to JobRequest result.");
|
||||||
|
|
||||||
var jobServer = await InitializeJobServerAsync(systemConnection);
|
try
|
||||||
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = detailInfo };
|
|
||||||
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
|
||||||
switch (jobServer)
|
|
||||||
{
|
{
|
||||||
case IJobServer js:
|
var jobServer = await InitializeJobServerAsync(systemConnection);
|
||||||
{
|
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = detailInfo };
|
||||||
await LogWorkerProcessUnhandledException(js, message, unhandledExceptionIssue);
|
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
||||||
// Go ahead to finish the job with result 'Failed' if the STDERR from worker is System.IO.IOException, since it typically means we are running out of disk space.
|
switch (jobServer)
|
||||||
if (detailInfo.Contains(typeof(System.IO.IOException).ToString(), StringComparison.OrdinalIgnoreCase))
|
{
|
||||||
|
case IJobServer js:
|
||||||
{
|
{
|
||||||
Trace.Info($"Finish job with result 'Failed' due to IOException.");
|
await LogWorkerProcessUnhandledException(js, message, unhandledExceptionIssue);
|
||||||
await ForceFailJob(js, message);
|
// Go ahead to finish the job with result 'Failed' if the STDERR from worker is System.IO.IOException, since it typically means we are running out of disk space.
|
||||||
}
|
if (detailInfo.Contains(typeof(System.IO.IOException).ToString(), StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Trace.Info($"Finish job with result 'Failed' due to IOException.");
|
||||||
|
await ForceFailJob(js, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IRunServer rs:
|
||||||
|
await ForceFailJob(rs, message, unhandledExceptionIssue);
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
case IRunServer rs:
|
throw new NotSupportedException($"JobServer type '{jobServer.GetType().Name}' is not supported.");
|
||||||
await ForceFailJob(rs, message, unhandledExceptionIssue);
|
}
|
||||||
break;
|
}
|
||||||
default:
|
catch (Exception ex)
|
||||||
throw new NotSupportedException($"JobServer type '{jobServer.GetType().Name}' is not supported.");
|
{
|
||||||
|
Trace.Error($"Catch exception during log worker process unhandled exception.");
|
||||||
|
Trace.Error(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1198,7 +1206,7 @@ namespace GitHub.Runner.Listener
|
|||||||
jobAnnotations.Add(annotation.Value);
|
jobAnnotations.Add(annotation.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
await runServer.CompleteJobAsync(message.Plan.PlanId, message.JobId, TaskResult.Failed, outputs: null, stepResults: null, jobAnnotations: jobAnnotations, environmentUrl: null, CancellationToken.None);
|
await runServer.CompleteJobAsync(message.Plan.PlanId, message.JobId, TaskResult.Failed, outputs: null, stepResults: null, jobAnnotations: jobAnnotations, environmentUrl: null, telemetry: null, billingOwnerId: message.BillingOwnerId, CancellationToken.None);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -264,8 +264,6 @@ namespace GitHub.Runner.Listener
|
|||||||
|
|
||||||
if (message != null && message.MessageType == BrokerMigrationMessage.MessageType)
|
if (message != null && message.MessageType == BrokerMigrationMessage.MessageType)
|
||||||
{
|
{
|
||||||
Trace.Info("BrokerMigration message received. Polling Broker for messages...");
|
|
||||||
|
|
||||||
var migrationMessage = JsonUtility.FromString<BrokerMigrationMessage>(message.Body);
|
var migrationMessage = JsonUtility.FromString<BrokerMigrationMessage>(message.Body);
|
||||||
|
|
||||||
await _brokerServer.UpdateConnectionIfNeeded(migrationMessage.BrokerBaseUrl, _creds);
|
await _brokerServer.UpdateConnectionIfNeeded(migrationMessage.BrokerBaseUrl, _creds);
|
||||||
@@ -306,10 +304,19 @@ namespace GitHub.Runner.Listener
|
|||||||
_accessTokenRevoked = true;
|
_accessTokenRevoked = true;
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
catch (HostedRunnerDeprovisionedException)
|
||||||
|
{
|
||||||
|
Trace.Info("Hosted runner has been deprovisioned.");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
catch (AccessDeniedException e) when (e.ErrorCode == 1)
|
catch (AccessDeniedException e) when (e.ErrorCode == 1)
|
||||||
{
|
{
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
catch (RunnerNotFoundException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Trace.Error("Catch exception during get next message.");
|
Trace.Error("Catch exception during get next message.");
|
||||||
@@ -459,6 +466,7 @@ namespace GitHub.Runner.Listener
|
|||||||
ex is TaskAgentPoolNotFoundException ||
|
ex is TaskAgentPoolNotFoundException ||
|
||||||
ex is TaskAgentSessionExpiredException ||
|
ex is TaskAgentSessionExpiredException ||
|
||||||
ex is AccessDeniedException ||
|
ex is AccessDeniedException ||
|
||||||
|
ex is RunnerNotFoundException ||
|
||||||
ex is VssUnauthorizedException)
|
ex is VssUnauthorizedException)
|
||||||
{
|
{
|
||||||
Trace.Info($"Non-retriable exception: {ex.Message}");
|
Trace.Info($"Non-retriable exception: {ex.Message}");
|
||||||
@@ -525,7 +533,8 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
else if (ex is TaskAgentPoolNotFoundException ||
|
else if (ex is TaskAgentPoolNotFoundException ||
|
||||||
ex is AccessDeniedException ||
|
ex is AccessDeniedException ||
|
||||||
ex is VssUnauthorizedException)
|
ex is VssUnauthorizedException ||
|
||||||
|
(ex is VssOAuthTokenRequestException oauthEx && oauthEx.Error != "server_error"))
|
||||||
{
|
{
|
||||||
Trace.Info($"Non-retriable exception: {ex.Message}");
|
Trace.Info($"Non-retriable exception: {ex.Message}");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Reflection;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.DistributedTask.WebApi;
|
using GitHub.DistributedTask.WebApi;
|
||||||
|
using GitHub.Services.WebApi;
|
||||||
|
|
||||||
namespace GitHub.Runner.Listener
|
namespace GitHub.Runner.Listener
|
||||||
{
|
{
|
||||||
@@ -144,6 +145,12 @@ namespace GitHub.Runner.Listener
|
|||||||
trace.Error(e);
|
trace.Error(e);
|
||||||
return Constants.Runner.ReturnCode.TerminatedError;
|
return Constants.Runner.ReturnCode.TerminatedError;
|
||||||
}
|
}
|
||||||
|
catch (RunnerNotFoundException e)
|
||||||
|
{
|
||||||
|
terminal.WriteError($"An error occurred: {e.Message}");
|
||||||
|
trace.Error(e);
|
||||||
|
return Constants.Runner.ReturnCode.TerminatedError;
|
||||||
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
terminal.WriteError($"An error occurred: {e.Message}");
|
terminal.WriteError($"An error occurred: {e.Message}");
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<NoWarn>NU1701;NU1603</NoWarn>
|
<NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
|
||||||
<Version>$(Version)</Version>
|
<Version>$(Version)</Version>
|
||||||
<PredefinedCulturesOnly>false</PredefinedCulturesOnly>
|
<PredefinedCulturesOnly>false</PredefinedCulturesOnly>
|
||||||
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
|
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
|
||||||
@@ -18,11 +19,11 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.4.0" />
|
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" />
|
||||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" />
|
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
|
||||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.4.0" />
|
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
|||||||
@@ -228,15 +228,21 @@ namespace GitHub.Runner.Listener
|
|||||||
var configFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), config.Key);
|
var configFile = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), config.Key);
|
||||||
var configContent = Convert.FromBase64String(config.Value);
|
var configContent = Convert.FromBase64String(config.Value);
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
|
#pragma warning disable CA1416
|
||||||
if (configFile == HostContext.GetConfigFile(WellKnownConfigFile.RSACredentials))
|
if (configFile == HostContext.GetConfigFile(WellKnownConfigFile.RSACredentials))
|
||||||
{
|
{
|
||||||
configContent = ProtectedData.Protect(configContent, null, DataProtectionScope.LocalMachine);
|
configContent = ProtectedData.Protect(configContent, null, DataProtectionScope.LocalMachine);
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CA1416
|
||||||
#endif
|
#endif
|
||||||
File.WriteAllBytes(configFile, configContent);
|
File.WriteAllBytes(configFile, configContent);
|
||||||
File.SetAttributes(configFile, File.GetAttributes(configFile) | FileAttributes.Hidden);
|
File.SetAttributes(configFile, File.GetAttributes(configFile) | FileAttributes.Hidden);
|
||||||
Trace.Info($"Saved {configContent.Length} bytes to '{configFile}'.");
|
Trace.Info($"Saved {configContent.Length} bytes to '{configFile}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make sure we have the right user agent data added from the jitconfig
|
||||||
|
HostContext.LoadDefaultUserAgents();
|
||||||
|
VssUtil.InitializeVssClientSettings(HostContext.UserAgents, HostContext.WebProxy);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -354,7 +360,7 @@ namespace GitHub.Runner.Listener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IMessageListener GetMesageListener(RunnerSettings settings)
|
private IMessageListener GetMessageListener(RunnerSettings settings)
|
||||||
{
|
{
|
||||||
if (settings.UseV2Flow)
|
if (settings.UseV2Flow)
|
||||||
{
|
{
|
||||||
@@ -373,7 +379,7 @@ namespace GitHub.Runner.Listener
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Trace.Info(nameof(RunAsync));
|
Trace.Info(nameof(RunAsync));
|
||||||
_listener = GetMesageListener(settings);
|
_listener = GetMessageListener(settings);
|
||||||
CreateSessionResult createSessionResult = await _listener.CreateSessionAsync(HostContext.RunnerShutdownToken);
|
CreateSessionResult createSessionResult = await _listener.CreateSessionAsync(HostContext.RunnerShutdownToken);
|
||||||
if (createSessionResult == CreateSessionResult.SessionConflict)
|
if (createSessionResult == CreateSessionResult.SessionConflict)
|
||||||
{
|
{
|
||||||
@@ -578,7 +584,7 @@ namespace GitHub.Runner.Listener
|
|||||||
await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), creds);
|
await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), creds);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
jobRequestMessage = await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageQueueLoopTokenSource.Token);
|
jobRequestMessage = await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageRef.BillingOwnerId, messageQueueLoopTokenSource.Token);
|
||||||
_acquireJobThrottler.Reset();
|
_acquireJobThrottler.Reset();
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (
|
catch (Exception ex) when (
|
||||||
@@ -629,6 +635,17 @@ namespace GitHub.Runner.Listener
|
|||||||
Trace.Info("Received ForceTokenRefreshMessage");
|
Trace.Info("Received ForceTokenRefreshMessage");
|
||||||
await _listener.RefreshListenerTokenAsync(messageQueueLoopTokenSource.Token);
|
await _listener.RefreshListenerTokenAsync(messageQueueLoopTokenSource.Token);
|
||||||
}
|
}
|
||||||
|
else if (string.Equals(message.MessageType, RunnerRefreshConfigMessage.MessageType))
|
||||||
|
{
|
||||||
|
var runnerRefreshConfigMessage = JsonUtility.FromString<RunnerRefreshConfigMessage>(message.Body);
|
||||||
|
Trace.Info($"Received RunnerRefreshConfigMessage for '{runnerRefreshConfigMessage.ConfigType}' config file");
|
||||||
|
var configUpdater = HostContext.GetService<IRunnerConfigUpdater>();
|
||||||
|
await configUpdater.UpdateRunnerConfigAsync(
|
||||||
|
runnerQualifiedId: runnerRefreshConfigMessage.RunnerQualifiedId,
|
||||||
|
configType: runnerRefreshConfigMessage.ConfigType,
|
||||||
|
serviceType: runnerRefreshConfigMessage.ServiceType,
|
||||||
|
configRefreshUrl: runnerRefreshConfigMessage.ConfigRefreshUrl);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Trace.Error($"Received message {message.MessageId} with unsupported message type {message.MessageType}.");
|
Trace.Error($"Received message {message.MessageId} with unsupported message type {message.MessageType}.");
|
||||||
@@ -691,6 +708,10 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
Trace.Info("Runner OAuth token has been revoked. Shutting down.");
|
Trace.Info("Runner OAuth token has been revoked. Shutting down.");
|
||||||
}
|
}
|
||||||
|
catch (HostedRunnerDeprovisionedException)
|
||||||
|
{
|
||||||
|
Trace.Info("Hosted runner has been deprovisioned. Shutting down.");
|
||||||
|
}
|
||||||
|
|
||||||
return Constants.Runner.ReturnCode.Success;
|
return Constants.Runner.ReturnCode.Success;
|
||||||
}
|
}
|
||||||
|
|||||||
267
src/Runner.Listener/RunnerConfigUpdater.cs
Normal file
267
src/Runner.Listener/RunnerConfigUpdater.cs
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using GitHub.Runner.Common;
|
||||||
|
using GitHub.Runner.Sdk;
|
||||||
|
using GitHub.Services.Common;
|
||||||
|
|
||||||
|
namespace GitHub.Runner.Listener
|
||||||
|
{
|
||||||
|
[ServiceLocator(Default = typeof(RunnerConfigUpdater))]
|
||||||
|
public interface IRunnerConfigUpdater : IRunnerService
|
||||||
|
{
|
||||||
|
Task UpdateRunnerConfigAsync(string runnerQualifiedId, string configType, string serviceType, string configRefreshUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class RunnerConfigUpdater : RunnerService, IRunnerConfigUpdater
|
||||||
|
{
|
||||||
|
private RunnerSettings _settings;
|
||||||
|
private CredentialData _credData;
|
||||||
|
private IRunnerServer _runnerServer;
|
||||||
|
private IConfigurationStore _store;
|
||||||
|
|
||||||
|
public override void Initialize(IHostContext hostContext)
|
||||||
|
{
|
||||||
|
base.Initialize(hostContext);
|
||||||
|
_store = hostContext.GetService<IConfigurationStore>();
|
||||||
|
_settings = _store.GetSettings();
|
||||||
|
_credData = _store.GetCredentials();
|
||||||
|
_runnerServer = HostContext.GetService<IRunnerServer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateRunnerConfigAsync(string runnerQualifiedId, string configType, string serviceType, string configRefreshUrl)
|
||||||
|
{
|
||||||
|
Trace.Entering();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ArgUtil.NotNullOrEmpty(runnerQualifiedId, nameof(runnerQualifiedId));
|
||||||
|
ArgUtil.NotNullOrEmpty(configType, nameof(configType));
|
||||||
|
ArgUtil.NotNullOrEmpty(serviceType, nameof(serviceType));
|
||||||
|
ArgUtil.NotNullOrEmpty(configRefreshUrl, nameof(configRefreshUrl));
|
||||||
|
|
||||||
|
// make sure the runner qualified id matches the current runner
|
||||||
|
if (!await VerifyRunnerQualifiedId(runnerQualifiedId))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep the timeout short to avoid blocking the main thread
|
||||||
|
using (var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
|
||||||
|
{
|
||||||
|
switch (configType.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "runner":
|
||||||
|
await UpdateRunnerSettingsAsync(serviceType, configRefreshUrl, tokenSource.Token);
|
||||||
|
break;
|
||||||
|
case "credentials":
|
||||||
|
await UpdateRunnerCredentialsAsync(serviceType, configRefreshUrl, tokenSource.Token);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Trace.Error($"Invalid config type '{configType}'.");
|
||||||
|
await ReportTelemetryAsync($"Invalid config type '{configType}'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Failed to update runner '{configType}' config.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
await ReportTelemetryAsync($"Failed to update runner '{configType}' config: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateRunnerSettingsAsync(string serviceType, string configRefreshUrl, CancellationToken token)
|
||||||
|
{
|
||||||
|
Trace.Entering();
|
||||||
|
// read the current runner settings and encode with base64
|
||||||
|
var runnerConfig = HostContext.GetConfigFile(WellKnownConfigFile.Runner);
|
||||||
|
string runnerConfigContent = File.ReadAllText(runnerConfig, Encoding.UTF8);
|
||||||
|
var encodedConfig = Convert.ToBase64String(Encoding.UTF8.GetBytes(runnerConfigContent));
|
||||||
|
if (string.IsNullOrEmpty(encodedConfig))
|
||||||
|
{
|
||||||
|
await ReportTelemetryAsync("Failed to get encoded runner settings.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// exchange the encoded runner settings with the service
|
||||||
|
string refreshedEncodedConfig = await RefreshRunnerConfigAsync(encodedConfig, serviceType, "runner", configRefreshUrl, token);
|
||||||
|
if (string.IsNullOrEmpty(refreshedEncodedConfig))
|
||||||
|
{
|
||||||
|
// service will return empty string if there is no change in the config
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var decodedConfig = Encoding.UTF8.GetString(Convert.FromBase64String(refreshedEncodedConfig));
|
||||||
|
RunnerSettings refreshedRunnerConfig;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
refreshedRunnerConfig = StringUtil.ConvertFromJson<RunnerSettings>(decodedConfig);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Failed to convert runner config from json '{decodedConfig}'.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
await ReportTelemetryAsync($"Failed to convert runner config '{decodedConfig}' from json: {ex}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the runner id and name in the refreshed config match the current runner
|
||||||
|
if (refreshedRunnerConfig?.AgentId != _settings.AgentId)
|
||||||
|
{
|
||||||
|
Trace.Error($"Runner id in refreshed config '{refreshedRunnerConfig?.AgentId.ToString() ?? "Empty"}' does not match the current runner '{_settings.AgentId}'.");
|
||||||
|
await ReportTelemetryAsync($"Runner id in refreshed config '{refreshedRunnerConfig?.AgentId.ToString() ?? "Empty"}' does not match the current runner '{_settings.AgentId}'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshedRunnerConfig?.AgentName != _settings.AgentName)
|
||||||
|
{
|
||||||
|
Trace.Error($"Runner name in refreshed config '{refreshedRunnerConfig?.AgentName ?? "Empty"}' does not match the current runner '{_settings.AgentName}'.");
|
||||||
|
await ReportTelemetryAsync($"Runner name in refreshed config '{refreshedRunnerConfig?.AgentName ?? "Empty"}' does not match the current runner '{_settings.AgentName}'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the refreshed runner settings as a separate file
|
||||||
|
_store.SaveMigratedSettings(refreshedRunnerConfig);
|
||||||
|
await ReportTelemetryAsync("Runner settings updated successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task UpdateRunnerCredentialsAsync(string serviceType, string configRefreshUrl, CancellationToken token)
|
||||||
|
{
|
||||||
|
Trace.Entering();
|
||||||
|
// read the current runner credentials and encode with base64
|
||||||
|
var credConfig = HostContext.GetConfigFile(WellKnownConfigFile.Credentials);
|
||||||
|
string credConfigContent = File.ReadAllText(credConfig, Encoding.UTF8);
|
||||||
|
var encodedConfig = Convert.ToBase64String(Encoding.UTF8.GetBytes(credConfigContent));
|
||||||
|
if (string.IsNullOrEmpty(encodedConfig))
|
||||||
|
{
|
||||||
|
await ReportTelemetryAsync("Failed to get encoded credentials.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CredentialData currentCred = _store.GetCredentials();
|
||||||
|
if (currentCred == null)
|
||||||
|
{
|
||||||
|
await ReportTelemetryAsync("Failed to get current credentials.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only support refreshing OAuth credentials which is used by self-hosted runners.
|
||||||
|
if (currentCred.Scheme != Constants.Configuration.OAuth)
|
||||||
|
{
|
||||||
|
await ReportTelemetryAsync($"Not supported credential scheme '{currentCred.Scheme}'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// exchange the encoded runner credentials with the service
|
||||||
|
string refreshedEncodedConfig = await RefreshRunnerConfigAsync(encodedConfig, serviceType, "credentials", configRefreshUrl, token);
|
||||||
|
if (string.IsNullOrEmpty(refreshedEncodedConfig))
|
||||||
|
{
|
||||||
|
// service will return empty string if there is no change in the config
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var decodedConfig = Encoding.UTF8.GetString(Convert.FromBase64String(refreshedEncodedConfig));
|
||||||
|
CredentialData refreshedCredConfig;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
refreshedCredConfig = StringUtil.ConvertFromJson<CredentialData>(decodedConfig);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Failed to convert credentials config from json '{decodedConfig}'.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
await ReportTelemetryAsync($"Failed to convert credentials config '{decodedConfig}' from json: {ex}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the credential scheme in the refreshed config match the current credential scheme
|
||||||
|
if (refreshedCredConfig?.Scheme != _credData.Scheme)
|
||||||
|
{
|
||||||
|
Trace.Error($"Credential scheme in refreshed config '{refreshedCredConfig?.Scheme ?? "Empty"}' does not match the current credential scheme '{_credData.Scheme}'.");
|
||||||
|
await ReportTelemetryAsync($"Credential scheme in refreshed config '{refreshedCredConfig?.Scheme ?? "Empty"}' does not match the current credential scheme '{_credData.Scheme}'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_credData.Scheme == Constants.Configuration.OAuth)
|
||||||
|
{
|
||||||
|
// make sure the credential clientId in the refreshed config match the current credential clientId for OAuth auth scheme
|
||||||
|
var clientId = _credData.Data.GetValueOrDefault("clientId", null);
|
||||||
|
var refreshedClientId = refreshedCredConfig.Data.GetValueOrDefault("clientId", null);
|
||||||
|
if (clientId != refreshedClientId)
|
||||||
|
{
|
||||||
|
Trace.Error($"Credential clientId in refreshed config '{refreshedClientId ?? "Empty"}' does not match the current credential clientId '{clientId}'.");
|
||||||
|
await ReportTelemetryAsync($"Credential clientId in refreshed config '{refreshedClientId ?? "Empty"}' does not match the current credential clientId '{clientId}'.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the refreshed runner credentials as a separate file
|
||||||
|
_store.SaveMigratedCredential(refreshedCredConfig);
|
||||||
|
await ReportTelemetryAsync("Runner credentials updated successfully.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> VerifyRunnerQualifiedId(string runnerQualifiedId)
|
||||||
|
{
|
||||||
|
Trace.Entering();
|
||||||
|
Trace.Info($"Verifying runner qualified id: {runnerQualifiedId}");
|
||||||
|
var idParts = runnerQualifiedId.Split("/", StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (idParts.Length != 4 || idParts[3] != _settings.AgentId.ToString())
|
||||||
|
{
|
||||||
|
Trace.Error($"Runner qualified id '{runnerQualifiedId}' does not match the current runner '{_settings.AgentId}'.");
|
||||||
|
await ReportTelemetryAsync($"Runner qualified id '{runnerQualifiedId}' does not match the current runner '{_settings.AgentId}'.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> RefreshRunnerConfigAsync(string encodedConfig, string serviceType, string configType, string configRefreshUrl, CancellationToken token)
|
||||||
|
{
|
||||||
|
string refreshedEncodedConfig;
|
||||||
|
switch (serviceType.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "pipelines":
|
||||||
|
try
|
||||||
|
{
|
||||||
|
refreshedEncodedConfig = await _runnerServer.RefreshRunnerConfigAsync((int)_settings.AgentId, configType, encodedConfig, token);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Failed to refresh runner {configType} config with service.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
await ReportTelemetryAsync($"Failed to refresh {configType} config: {ex}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "runner-admin":
|
||||||
|
throw new NotSupportedException("Runner admin service is not supported.");
|
||||||
|
default:
|
||||||
|
Trace.Error($"Invalid service type '{serviceType}'.");
|
||||||
|
await ReportTelemetryAsync($"Invalid service type '{serviceType}'.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return refreshedEncodedConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ReportTelemetryAsync(string telemetry)
|
||||||
|
{
|
||||||
|
Trace.Entering();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
|
||||||
|
{
|
||||||
|
await _runnerServer.UpdateAgentUpdateStateAsync(_settings.PoolId, _settings.AgentId, "RefreshConfig", telemetry, tokenSource.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error("Failed to report telemetry.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,9 +7,14 @@ namespace GitHub.Runner.Listener
|
|||||||
{
|
{
|
||||||
[DataMember(Name = "id")]
|
[DataMember(Name = "id")]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
[DataMember(Name = "runner_request_id")]
|
[DataMember(Name = "runner_request_id")]
|
||||||
public string RunnerRequestId { get; set; }
|
public string RunnerRequestId { get; set; }
|
||||||
|
|
||||||
[DataMember(Name = "run_service_url")]
|
[DataMember(Name = "run_service_url")]
|
||||||
public string RunServiceUrl { get; set; }
|
public string RunServiceUrl { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "billing_owner_id")]
|
||||||
|
public string BillingOwnerId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<NoWarn>NU1701;NU1603</NoWarn>
|
<NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
|
||||||
<Version>$(Version)</Version>
|
<Version>$(Version)</Version>
|
||||||
<PredefinedCulturesOnly>false</PredefinedCulturesOnly>
|
<PredefinedCulturesOnly>false</PredefinedCulturesOnly>
|
||||||
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
|
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<NoWarn>NU1701;NU1603</NoWarn>
|
<NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
|
||||||
<Version>$(Version)</Version>
|
<Version>$(Version)</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<NoWarn>NU1701;NU1603</NoWarn>
|
<NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
|
||||||
<Version>$(Version)</Version>
|
<Version>$(Version)</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@@ -14,9 +15,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||||
<PackageReference Include="System.Threading.Channels" Version="4.4.0" />
|
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
|||||||
@@ -60,5 +60,15 @@ namespace GitHub.Runner.Sdk
|
|||||||
}
|
}
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetVssRequestId(HttpResponseHeaders headers)
|
||||||
|
{
|
||||||
|
if (headers != null &&
|
||||||
|
headers.TryGetValues("x-vss-e2eid", out var headerValues))
|
||||||
|
{
|
||||||
|
return headerValues.FirstOrDefault();
|
||||||
|
}
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -775,7 +775,19 @@ namespace GitHub.Runner.Worker
|
|||||||
// make sure we get a 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 '{downloadInfo.NameWithOwner}@{downloadInfo.Ref}' (SHA:{downloadInfo.ResolvedSha})");
|
|
||||||
|
if (downloadInfo.PackageDetails != null)
|
||||||
|
{
|
||||||
|
executionContext.Output($"##[group]Download immutable action package '{downloadInfo.NameWithOwner}@{downloadInfo.Ref}'");
|
||||||
|
executionContext.Output($"Version: {downloadInfo.PackageDetails.Version}");
|
||||||
|
executionContext.Output($"Digest: {downloadInfo.PackageDetails.ManifestDigest}");
|
||||||
|
executionContext.Output($"Source commit SHA: {downloadInfo.ResolvedSha}");
|
||||||
|
executionContext.Output("##[endgroup]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
executionContext.Output($"Download action repository '{downloadInfo.NameWithOwner}@{downloadInfo.Ref}' (SHA:{downloadInfo.ResolvedSha})");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//download and extract action in a temp folder and rename it on success
|
//download and extract action in a temp folder and rename it on success
|
||||||
|
|||||||
@@ -508,6 +508,9 @@ namespace GitHub.Runner.Worker
|
|||||||
Status = _record.State,
|
Status = _record.State,
|
||||||
Number = _record.Order,
|
Number = _record.Order,
|
||||||
Name = _record.Name,
|
Name = _record.Name,
|
||||||
|
ActionName = StepTelemetry?.Action,
|
||||||
|
Ref = StepTelemetry?.Ref,
|
||||||
|
Type = StepTelemetry?.Type,
|
||||||
StartedAt = _record.StartTime,
|
StartedAt = _record.StartTime,
|
||||||
CompletedAt = _record.FinishTime,
|
CompletedAt = _record.FinishTime,
|
||||||
Annotations = new List<Annotation>()
|
Annotations = new List<Annotation>()
|
||||||
@@ -812,11 +815,6 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
Global.Variables = new Variables(HostContext, variables);
|
Global.Variables = new Variables(HostContext, variables);
|
||||||
|
|
||||||
if (Global.Variables.GetBoolean("DistributedTask.ForceInternalNodeVersionOnRunnerTo16") ?? false)
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable(Constants.Variables.Agent.ForcedInternalNodeVersion, "node16");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Environment variables shared across all actions
|
// Environment variables shared across all actions
|
||||||
Global.EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer);
|
Global.EnvironmentVariables = new Dictionary<string, string>(VarUtil.EnvironmentVariableKeyComparer);
|
||||||
|
|
||||||
|
|||||||
@@ -57,72 +57,13 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
handler = HostContext.CreateService<INodeScriptActionHandler>();
|
handler = HostContext.CreateService<INodeScriptActionHandler>();
|
||||||
var nodeData = data as NodeJSActionExecutionData;
|
var nodeData = data as NodeJSActionExecutionData;
|
||||||
|
|
||||||
// With node12 EoL in 04/2022, we want to be able to uniformly upgrade all JS actions to node16 from the server
|
// With node12 EoL in 04/2022 and node16 EoL in 09/23, we want to execute all JS actions using node20
|
||||||
if (string.Equals(nodeData.NodeVersion, "node12", StringComparison.InvariantCultureIgnoreCase))
|
if (string.Equals(nodeData.NodeVersion, "node12", StringComparison.InvariantCultureIgnoreCase) ||
|
||||||
|
string.Equals(nodeData.NodeVersion, "node16", StringComparison.InvariantCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
var repoAction = action as Pipelines.RepositoryPathReference;
|
nodeData.NodeVersion = "node20";
|
||||||
if (repoAction != null)
|
|
||||||
{
|
|
||||||
var warningActions = new HashSet<string>();
|
|
||||||
if (executionContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode12DetectedAfterEndOfLifeEnvVariable, out var node16ForceWarnings))
|
|
||||||
{
|
|
||||||
warningActions = StringUtil.ConvertFromJson<HashSet<string>>(node16ForceWarnings);
|
|
||||||
}
|
|
||||||
|
|
||||||
string repoActionFullName;
|
|
||||||
if (string.IsNullOrEmpty(repoAction.Name))
|
|
||||||
{
|
|
||||||
repoActionFullName = repoAction.Path; // local actions don't have a 'Name'
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
repoActionFullName = $"{repoAction.Name}/{repoAction.Path ?? string.Empty}".TrimEnd('/') + $"@{repoAction.Ref}";
|
|
||||||
}
|
|
||||||
|
|
||||||
warningActions.Add(repoActionFullName);
|
|
||||||
executionContext.Global.Variables.Set("Node16ForceActionsWarnings", StringUtil.ConvertToJson(warningActions));
|
|
||||||
}
|
|
||||||
nodeData.NodeVersion = "node16";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var localForceActionsToNode20 = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Agent.ManualForceActionsToNode20));
|
|
||||||
executionContext.Global.EnvironmentVariables.TryGetValue(Constants.Variables.Actions.ManualForceActionsToNode20, out var workflowForceActionsToNode20);
|
|
||||||
var enforceNode20Locally = !string.IsNullOrWhiteSpace(workflowForceActionsToNode20) ? StringUtil.ConvertToBoolean(workflowForceActionsToNode20) : localForceActionsToNode20;
|
|
||||||
if (string.Equals(nodeData.NodeVersion, "node16")
|
|
||||||
&& ((executionContext.Global.Variables.GetBoolean("DistributedTask.ForceGithubJavascriptActionsToNode20") ?? false) || enforceNode20Locally))
|
|
||||||
{
|
|
||||||
executionContext.Global.EnvironmentVariables.TryGetValue(Constants.Variables.Actions.AllowActionsUseUnsecureNodeVersion, out var workflowOptOut);
|
|
||||||
var isWorkflowOptOutSet = !string.IsNullOrWhiteSpace(workflowOptOut);
|
|
||||||
var isLocalOptOut = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowActionsUseUnsecureNodeVersion));
|
|
||||||
bool isOptOut = isWorkflowOptOutSet ? StringUtil.ConvertToBoolean(workflowOptOut) : isLocalOptOut;
|
|
||||||
|
|
||||||
if (!isOptOut)
|
|
||||||
{
|
|
||||||
var repoAction = action as Pipelines.RepositoryPathReference;
|
|
||||||
if (repoAction != null)
|
|
||||||
{
|
|
||||||
var warningActions = new HashSet<string>();
|
|
||||||
if (executionContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode16DetectedAfterEndOfLifeEnvVariable, out var node20ForceWarnings))
|
|
||||||
{
|
|
||||||
warningActions = StringUtil.ConvertFromJson<HashSet<string>>(node20ForceWarnings);
|
|
||||||
}
|
|
||||||
|
|
||||||
string repoActionFullName;
|
|
||||||
if (string.IsNullOrEmpty(repoAction.Name))
|
|
||||||
{
|
|
||||||
repoActionFullName = repoAction.Path; // local actions don't have a 'Name'
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
repoActionFullName = $"{repoAction.Name}/{repoAction.Path ?? string.Empty}".TrimEnd('/') + $"@{repoAction.Ref}";
|
|
||||||
}
|
|
||||||
|
|
||||||
warningActions.Add(repoActionFullName);
|
|
||||||
executionContext.Global.Variables.Set(Constants.Runner.EnforcedNode16DetectedAfterEndOfLifeEnvVariable, StringUtil.ConvertToJson(warningActions));
|
|
||||||
}
|
|
||||||
nodeData.NodeVersion = "node20";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(handler as INodeScriptActionHandler).Data = nodeData;
|
(handler as INodeScriptActionHandler).Data = nodeData;
|
||||||
}
|
}
|
||||||
else if (data.ExecutionType == ActionExecutionType.Script)
|
else if (data.ExecutionType == ActionExecutionType.Script)
|
||||||
|
|||||||
@@ -72,6 +72,11 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
Environment["ACTIONS_RESULTS_URL"] = resultsUrl;
|
Environment["ACTIONS_RESULTS_URL"] = resultsUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ExecutionContext.Global.Variables.GetBoolean("actions_uses_cache_service_v2") ?? false)
|
||||||
|
{
|
||||||
|
Environment["ACTIONS_CACHE_SERVICE_V2"] = bool.TrueString;
|
||||||
|
}
|
||||||
|
|
||||||
// Resolve the target script.
|
// Resolve the target script.
|
||||||
string target = null;
|
string target = null;
|
||||||
if (stage == ActionRunStage.Main)
|
if (stage == ActionRunStage.Main)
|
||||||
@@ -93,7 +98,6 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
ExecutionContext.StepTelemetry.HasPreStep = Data.HasPre;
|
ExecutionContext.StepTelemetry.HasPreStep = Data.HasPre;
|
||||||
ExecutionContext.StepTelemetry.HasPostStep = Data.HasPost;
|
ExecutionContext.StepTelemetry.HasPostStep = Data.HasPost;
|
||||||
}
|
}
|
||||||
ExecutionContext.StepTelemetry.Type = Data.NodeVersion;
|
|
||||||
|
|
||||||
ArgUtil.NotNullOrEmpty(target, nameof(target));
|
ArgUtil.NotNullOrEmpty(target, nameof(target));
|
||||||
target = Path.Combine(ActionDirectory, target);
|
target = Path.Combine(ActionDirectory, target);
|
||||||
@@ -106,24 +110,8 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
|
workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(Data.NodeVersion, "node12", StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
Constants.Runner.PlatformArchitecture.Equals(Constants.Architecture.Arm64))
|
|
||||||
{
|
|
||||||
ExecutionContext.Output($"The node12 is not supported. Use node16 instead.");
|
|
||||||
Data.NodeVersion = "node16";
|
|
||||||
}
|
|
||||||
|
|
||||||
string forcedNodeVersion = System.Environment.GetEnvironmentVariable(Constants.Variables.Agent.ForcedActionsNodeVersion);
|
|
||||||
if (forcedNodeVersion == "node16" && Data.NodeVersion != "node16")
|
|
||||||
{
|
|
||||||
Data.NodeVersion = "node16";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forcedNodeVersion == "node20" && Data.NodeVersion != "node20")
|
|
||||||
{
|
|
||||||
Data.NodeVersion = "node20";
|
|
||||||
}
|
|
||||||
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext, Data.NodeVersion);
|
var nodeRuntimeVersion = await StepHost.DetermineNodeRuntimeVersion(ExecutionContext, Data.NodeVersion);
|
||||||
|
ExecutionContext.StepTelemetry.Type = nodeRuntimeVersion;
|
||||||
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), nodeRuntimeVersion, "bin", $"node{IOUtil.ExeExtension}");
|
||||||
|
|
||||||
// Format the arguments passed to node.
|
// Format the arguments passed to node.
|
||||||
@@ -143,28 +131,6 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
// Remove environment variable that may cause conflicts with the node within the runner.
|
// Remove environment variable that may cause conflicts with the node within the runner.
|
||||||
Environment.Remove("NODE_ICU_DATA"); // https://github.com/actions/runner/issues/795
|
Environment.Remove("NODE_ICU_DATA"); // https://github.com/actions/runner/issues/795
|
||||||
|
|
||||||
if (string.Equals(Data.NodeVersion, Constants.Runner.DeprecatedNodeVersion, StringComparison.OrdinalIgnoreCase) && (ExecutionContext.Global.Variables.GetBoolean(Constants.Runner.Features.Node16Warning) ?? false))
|
|
||||||
{
|
|
||||||
var repoAction = Action as RepositoryPathReference;
|
|
||||||
var warningActions = new HashSet<string>();
|
|
||||||
if (ExecutionContext.Global.Variables.TryGetValue(Constants.Runner.DeprecatedNodeDetectedAfterEndOfLifeActions, out var deprecatedNodeWarnings))
|
|
||||||
{
|
|
||||||
warningActions = StringUtil.ConvertFromJson<HashSet<string>>(deprecatedNodeWarnings);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(repoAction.Name))
|
|
||||||
{
|
|
||||||
// local actions don't have a 'Name'
|
|
||||||
warningActions.Add(repoAction.Path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
warningActions.Add($"{repoAction.Name}/{repoAction.Path ?? string.Empty}".TrimEnd('/') + $"@{repoAction.Ref}");
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecutionContext.Global.Variables.Set(Constants.Runner.DeprecatedNodeDetectedAfterEndOfLifeActions, StringUtil.ConvertToJson(warningActions));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var stdoutManager = new OutputManager(ExecutionContext, ActionCommandManager))
|
using (var stdoutManager = new OutputManager(ExecutionContext, ActionCommandManager))
|
||||||
using (var stderrManager = new OutputManager(ExecutionContext, ActionCommandManager))
|
using (var stderrManager = new OutputManager(ExecutionContext, ActionCommandManager))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using GitHub.Runner.Common;
|
|||||||
using GitHub.Runner.Common.Util;
|
using GitHub.Runner.Common.Util;
|
||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
namespace GitHub.Runner.Worker
|
||||||
@@ -42,11 +43,13 @@ namespace GitHub.Runner.Worker
|
|||||||
public sealed class JobExtension : RunnerService, IJobExtension
|
public sealed class JobExtension : RunnerService, IJobExtension
|
||||||
{
|
{
|
||||||
private readonly HashSet<string> _existingProcesses = new(StringComparer.OrdinalIgnoreCase);
|
private readonly HashSet<string> _existingProcesses = new(StringComparer.OrdinalIgnoreCase);
|
||||||
private readonly List<Task<string>> _connectivityCheckTasks = new();
|
private readonly List<Task<CheckResult>> _connectivityCheckTasks = new();
|
||||||
private bool _processCleanup;
|
private bool _processCleanup;
|
||||||
private string _processLookupId = $"github_{Guid.NewGuid()}";
|
private string _processLookupId = $"github_{Guid.NewGuid()}";
|
||||||
private CancellationTokenSource _diskSpaceCheckToken = new();
|
private CancellationTokenSource _diskSpaceCheckToken = new();
|
||||||
private Task _diskSpaceCheckTask = null;
|
private Task _diskSpaceCheckTask = null;
|
||||||
|
private CancellationTokenSource _serviceConnectivityCheckToken = new();
|
||||||
|
private Task _serviceConnectivityCheckTask = null;
|
||||||
|
|
||||||
// Download all required actions.
|
// Download all required actions.
|
||||||
// Make sure all condition inputs are valid.
|
// Make sure all condition inputs are valid.
|
||||||
@@ -127,10 +130,6 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check OS warning
|
|
||||||
var osWarningChecker = HostContext.GetService<IOSWarningChecker>();
|
|
||||||
await osWarningChecker.CheckOSAsync(context);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var tokenPermissions = jobContext.Global.Variables.Get("system.github.token.permissions") ?? "";
|
var tokenPermissions = jobContext.Global.Variables.Get("system.github.token.permissions") ?? "";
|
||||||
@@ -403,7 +402,7 @@ namespace GitHub.Runner.Worker
|
|||||||
var snapshotOperationProvider = HostContext.GetService<ISnapshotOperationProvider>();
|
var snapshotOperationProvider = HostContext.GetService<ISnapshotOperationProvider>();
|
||||||
jobContext.RegisterPostJobStep(new JobExtensionRunner(
|
jobContext.RegisterPostJobStep(new JobExtensionRunner(
|
||||||
runAsync: (executionContext, _) => snapshotOperationProvider.CreateSnapshotRequestAsync(executionContext, snapshotRequest),
|
runAsync: (executionContext, _) => snapshotOperationProvider.CreateSnapshotRequestAsync(executionContext, snapshotRequest),
|
||||||
condition: $"{PipelineTemplateConstants.Success}()",
|
condition: snapshotRequest.Condition,
|
||||||
displayName: $"Create custom image",
|
displayName: $"Create custom image",
|
||||||
data: null));
|
data: null));
|
||||||
}
|
}
|
||||||
@@ -458,11 +457,14 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
foreach (var checkUrl in checkUrls)
|
foreach (var checkUrl in checkUrls)
|
||||||
{
|
{
|
||||||
_connectivityCheckTasks.Add(CheckConnectivity(checkUrl));
|
_connectivityCheckTasks.Add(CheckConnectivity(checkUrl, accessToken: string.Empty, timeoutInSeconds: 5, token: CancellationToken.None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Trace.Info($"Start checking service connectivity in background.");
|
||||||
|
_serviceConnectivityCheckTask = CheckServiceConnectivityAsync(context, _serviceConnectivityCheckToken.Token);
|
||||||
|
|
||||||
return steps;
|
return steps;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException ex) when (jobContext.CancellationToken.IsCancellationRequested)
|
catch (OperationCanceledException ex) when (jobContext.CancellationToken.IsCancellationRequested)
|
||||||
@@ -696,7 +698,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
var result = await check;
|
var result = await check;
|
||||||
Trace.Info($"Connectivity check result: {result}");
|
Trace.Info($"Connectivity check result: {result}");
|
||||||
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.ConnectivityCheck, Message = result });
|
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.ConnectivityCheck, Message = $"{result.EndpointUrl}: {result.StatusCode}" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -706,6 +708,22 @@ namespace GitHub.Runner.Worker
|
|||||||
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.ConnectivityCheck, Message = $"Fail to check server connectivity. {ex.Message}" });
|
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.ConnectivityCheck, Message = $"Fail to check server connectivity. {ex.Message}" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect service connectivity check result
|
||||||
|
if (_serviceConnectivityCheckTask != null)
|
||||||
|
{
|
||||||
|
_serviceConnectivityCheckToken.Cancel();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _serviceConnectivityCheckTask;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Fail to check service connectivity.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.ConnectivityCheck, Message = $"Fail to check service connectivity. {ex.Message}" });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -721,11 +739,13 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> CheckConnectivity(string endpointUrl)
|
private async Task<CheckResult> CheckConnectivity(string endpointUrl, string accessToken, int timeoutInSeconds, CancellationToken token)
|
||||||
{
|
{
|
||||||
Trace.Info($"Check server connectivity for {endpointUrl}.");
|
Trace.Info($"Check server connectivity for {endpointUrl}.");
|
||||||
string result = string.Empty;
|
CheckResult result = new CheckResult() { EndpointUrl = endpointUrl };
|
||||||
using (var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
|
var stopwatch = Stopwatch.StartNew();
|
||||||
|
using (var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
|
||||||
|
using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutTokenSource.Token))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -733,21 +753,44 @@ namespace GitHub.Runner.Worker
|
|||||||
using (var httpClient = new HttpClient(httpClientHandler))
|
using (var httpClient = new HttpClient(httpClientHandler))
|
||||||
{
|
{
|
||||||
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||||
var response = await httpClient.GetAsync(endpointUrl, timeoutTokenSource.Token);
|
if (!string.IsNullOrEmpty(accessToken))
|
||||||
result = $"{endpointUrl}: {response.StatusCode}";
|
{
|
||||||
|
httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {accessToken}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await httpClient.GetAsync(endpointUrl, linkedTokenSource.Token);
|
||||||
|
result.StatusCode = $"{response.StatusCode}";
|
||||||
|
|
||||||
|
var githubRequestId = UrlUtil.GetGitHubRequestId(response.Headers);
|
||||||
|
var vssRequestId = UrlUtil.GetVssRequestId(response.Headers);
|
||||||
|
if (!string.IsNullOrEmpty(githubRequestId))
|
||||||
|
{
|
||||||
|
result.RequestId = githubRequestId;
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(vssRequestId))
|
||||||
|
{
|
||||||
|
result.RequestId = vssRequestId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex) when (ex is OperationCanceledException && token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Trace.Error($"Request canceled during connectivity check: {ex}");
|
||||||
|
result.StatusCode = "canceled";
|
||||||
|
}
|
||||||
catch (Exception ex) when (ex is OperationCanceledException && timeoutTokenSource.IsCancellationRequested)
|
catch (Exception ex) when (ex is OperationCanceledException && timeoutTokenSource.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Trace.Error($"Request timeout during connectivity check: {ex}");
|
Trace.Error($"Request timeout during connectivity check: {ex}");
|
||||||
result = $"{endpointUrl}: timeout";
|
result.StatusCode = "timeout";
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Trace.Error($"Catch exception during connectivity check: {ex}");
|
Trace.Error($"Catch exception during connectivity check: {ex}");
|
||||||
result = $"{endpointUrl}: {ex.Message}";
|
result.StatusCode = $"{ex.Message}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stopwatch.Stop();
|
||||||
|
result.DurationInMs = (int)stopwatch.ElapsedMilliseconds;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -785,6 +828,84 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task CheckServiceConnectivityAsync(IExecutionContext context, CancellationToken token)
|
||||||
|
{
|
||||||
|
var connectionTest = context.Global.Variables.Get(WellKnownDistributedTaskVariables.RunnerServiceConnectivityTest);
|
||||||
|
if (string.IsNullOrEmpty(connectionTest))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceConnectivityCheckInput checkConnectivityInfo;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
checkConnectivityInfo = StringUtil.ConvertFromJson<ServiceConnectivityCheckInput>(connectionTest);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $"Fail to parse JSON. {ex.Message}" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkConnectivityInfo == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure interval is at least 10 seconds
|
||||||
|
checkConnectivityInfo.IntervalInSecond = Math.Max(10, checkConnectivityInfo.IntervalInSecond);
|
||||||
|
|
||||||
|
var systemConnection = context.Global.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
|
var accessToken = systemConnection.Authorization.Parameters[EndpointAuthorizationParameters.AccessToken];
|
||||||
|
|
||||||
|
var testResult = new ServiceConnectivityCheckResult();
|
||||||
|
while (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
foreach (var endpoint in checkConnectivityInfo.Endpoints)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(endpoint.Key) || string.IsNullOrEmpty(endpoint.Value))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!testResult.EndpointsResult.ContainsKey(endpoint.Key))
|
||||||
|
{
|
||||||
|
testResult.EndpointsResult[endpoint.Key] = new List<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await CheckConnectivity(endpoint.Value, accessToken: accessToken, timeoutInSeconds: checkConnectivityInfo.RequestTimeoutInSecond, token);
|
||||||
|
testResult.EndpointsResult[endpoint.Key].Add($"{result.StartTime:s}: {result.StatusCode} - {result.RequestId} - {result.DurationInMs}ms");
|
||||||
|
if (!testResult.HasFailure &&
|
||||||
|
result.StatusCode != "OK" &&
|
||||||
|
result.StatusCode != "canceled")
|
||||||
|
{
|
||||||
|
// track if any endpoint is not reachable
|
||||||
|
testResult.HasFailure = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
testResult.EndpointsResult[endpoint.Key].Add($"{DateTime.UtcNow:s}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(checkConnectivityInfo.IntervalInSecond), token);
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var telemetryData = StringUtil.ConvertToJson(testResult, Formatting.None);
|
||||||
|
Trace.Verbose($"Connectivity check result: {telemetryData}");
|
||||||
|
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.ConnectivityCheck, Message = telemetryData });
|
||||||
|
}
|
||||||
|
|
||||||
private Dictionary<int, Process> SnapshotProcesses()
|
private Dictionary<int, Process> SnapshotProcesses()
|
||||||
{
|
{
|
||||||
Dictionary<int, Process> snapshot = new();
|
Dictionary<int, Process> snapshot = new();
|
||||||
@@ -816,5 +937,23 @@ namespace GitHub.Runner.Worker
|
|||||||
throw new ArgumentException("Jobs without a job container are forbidden on this runner, please add a 'container:' to your job or contact your self-hosted runner administrator.");
|
throw new ArgumentException("Jobs without a job container are forbidden on this runner, please add a 'container:' to your job or contact your self-hosted runner administrator.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class CheckResult
|
||||||
|
{
|
||||||
|
public CheckResult()
|
||||||
|
{
|
||||||
|
StartTime = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string EndpointUrl { get; set; }
|
||||||
|
|
||||||
|
public DateTime StartTime { get; set; }
|
||||||
|
|
||||||
|
public string StatusCode { get; set; }
|
||||||
|
|
||||||
|
public string RequestId { get; set; }
|
||||||
|
|
||||||
|
public int DurationInMs { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ using GitHub.Runner.Common.Util;
|
|||||||
using GitHub.Runner.Sdk;
|
using GitHub.Runner.Sdk;
|
||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
using GitHub.Services.WebApi;
|
using GitHub.Services.WebApi;
|
||||||
|
using Sdk.RSWebApi.Contracts;
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
namespace GitHub.Runner.Worker
|
||||||
@@ -49,7 +50,8 @@ namespace GitHub.Runner.Worker
|
|||||||
if (message.Variables.TryGetValue(Constants.Variables.System.OrchestrationId, out VariableValue orchestrationId) &&
|
if (message.Variables.TryGetValue(Constants.Variables.System.OrchestrationId, out VariableValue orchestrationId) &&
|
||||||
!string.IsNullOrEmpty(orchestrationId.Value))
|
!string.IsNullOrEmpty(orchestrationId.Value))
|
||||||
{
|
{
|
||||||
HostContext.UserAgents.Add(new ProductInfoHeaderValue("OrchestrationId", orchestrationId.Value));
|
// make the orchestration id the first item in the user-agent header to avoid get truncated in server log.
|
||||||
|
HostContext.UserAgents.Insert(0, new ProductInfoHeaderValue("OrchestrationId", orchestrationId.Value));
|
||||||
|
|
||||||
// make sure orchestration id is in the user-agent header.
|
// make sure orchestration id is in the user-agent header.
|
||||||
VssUtil.InitializeVssClientSettings(HostContext.UserAgents, HostContext.WebProxy);
|
VssUtil.InitializeVssClientSettings(HostContext.UserAgents, HostContext.WebProxy);
|
||||||
@@ -278,26 +280,14 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
jobContext.Debug($"Finishing: {message.JobDisplayName}");
|
jobContext.Debug($"Finishing: {message.JobDisplayName}");
|
||||||
TaskResult result = jobContext.Complete(taskResult);
|
TaskResult result = jobContext.Complete(taskResult);
|
||||||
if (jobContext.Global.Variables.TryGetValue(Constants.Runner.DeprecatedNodeDetectedAfterEndOfLifeActions, out var deprecatedNodeWarnings))
|
|
||||||
{
|
|
||||||
var actions = string.Join(", ", StringUtil.ConvertFromJson<HashSet<string>>(deprecatedNodeWarnings));
|
|
||||||
jobContext.Warning(string.Format(Constants.Runner.DetectedNodeAfterEndOfLifeMessage, actions));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jobContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode12DetectedAfterEndOfLifeEnvVariable, out var node16ForceWarnings))
|
var jobQueueTelemetry = await ShutdownQueue(throwOnFailure: false);
|
||||||
|
// include any job telemetry from the background upload process.
|
||||||
|
if (jobQueueTelemetry?.Count > 0)
|
||||||
{
|
{
|
||||||
var actions = string.Join(", ", StringUtil.ConvertFromJson<HashSet<string>>(node16ForceWarnings));
|
jobContext.Global.JobTelemetry.AddRange(jobQueueTelemetry);
|
||||||
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode12DetectedAfterEndOfLife, actions));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jobContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode16DetectedAfterEndOfLifeEnvVariable, out var node20ForceWarnings) && (jobContext.Global.Variables.GetBoolean("DistributedTask.ForceGithubJavascriptActionsToNode20") ?? false))
|
|
||||||
{
|
|
||||||
var actions = string.Join(", ", StringUtil.ConvertFromJson<HashSet<string>>(node20ForceWarnings));
|
|
||||||
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode16DetectedAfterEndOfLife, actions));
|
|
||||||
}
|
|
||||||
|
|
||||||
await ShutdownQueue(throwOnFailure: false);
|
|
||||||
|
|
||||||
// Make sure to clean temp after file upload since they may be pending fileupload still use the TEMP dir.
|
// Make sure to clean temp after file upload since they may be pending fileupload still use the TEMP dir.
|
||||||
_tempDirectoryManager?.CleanupTempDirectory();
|
_tempDirectoryManager?.CleanupTempDirectory();
|
||||||
|
|
||||||
@@ -314,6 +304,13 @@ namespace GitHub.Runner.Worker
|
|||||||
environmentUrl = urlStringToken.Value;
|
environmentUrl = urlStringToken.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get telemetry
|
||||||
|
IList<Telemetry> telemetry = null;
|
||||||
|
if (jobContext.Global.JobTelemetry.Count > 0)
|
||||||
|
{
|
||||||
|
telemetry = jobContext.Global.JobTelemetry.Select(x => new Telemetry { Type = x.Type.ToString(), Message = x.Message, }).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
Trace.Info($"Raising job completed against run service");
|
Trace.Info($"Raising job completed against run service");
|
||||||
var completeJobRetryLimit = 5;
|
var completeJobRetryLimit = 5;
|
||||||
var exceptions = new List<Exception>();
|
var exceptions = new List<Exception>();
|
||||||
@@ -321,9 +318,30 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await runServer.CompleteJobAsync(message.Plan.PlanId, message.JobId, result, jobContext.JobOutputs, jobContext.Global.StepsResult, jobContext.Global.JobAnnotations, environmentUrl, default);
|
if (jobContext.Global.Variables.GetBoolean(Constants.Runner.Features.SkipRetryCompleteJobUponKnownErrors) ?? false)
|
||||||
|
{
|
||||||
|
await runServer.CompleteJob2Async(message.Plan.PlanId, message.JobId, result, jobContext.JobOutputs, jobContext.Global.StepsResult, jobContext.Global.JobAnnotations, environmentUrl, telemetry, billingOwnerId: message.BillingOwnerId, default);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await runServer.CompleteJobAsync(message.Plan.PlanId, message.JobId, result, jobContext.JobOutputs, jobContext.Global.StepsResult, jobContext.Global.JobAnnotations, environmentUrl, telemetry, billingOwnerId: message.BillingOwnerId, default);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
catch (VssUnauthorizedException ex) when (jobContext.Global.Variables.GetBoolean(Constants.Runner.Features.SkipRetryCompleteJobUponKnownErrors) ?? false)
|
||||||
|
{
|
||||||
|
Trace.Error($"Catch exception while attempting to complete job {message.JobId}, job request {message.RequestId}.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
exceptions.Add(ex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (TaskOrchestrationJobNotFoundException ex) when (jobContext.Global.Variables.GetBoolean(Constants.Runner.Features.SkipRetryCompleteJobUponKnownErrors) ?? false)
|
||||||
|
{
|
||||||
|
Trace.Error($"Catch exception while attempting to complete job {message.JobId}, job request {message.RequestId}.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
exceptions.Add(ex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Trace.Error($"Catch exception while attempting to complete job {message.JobId}, job request {message.RequestId}.");
|
Trace.Error($"Catch exception while attempting to complete job {message.JobId}, job request {message.RequestId}.");
|
||||||
@@ -346,74 +364,14 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
if (_runnerSettings.DisableUpdate == true)
|
if (_runnerSettings.DisableUpdate == true)
|
||||||
{
|
{
|
||||||
try
|
await WarningOutdatedRunnerAsync(jobContext, message, result);
|
||||||
{
|
|
||||||
var currentVersion = new PackageVersion(BuildConstants.RunnerPackage.Version);
|
|
||||||
ServiceEndpoint systemConnection = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
|
||||||
VssCredentials serverCredential = VssUtil.GetVssCredential(systemConnection);
|
|
||||||
|
|
||||||
var runnerServer = HostContext.GetService<IRunnerServer>();
|
|
||||||
await runnerServer.ConnectAsync(systemConnection.Url, serverCredential);
|
|
||||||
var serverPackages = await runnerServer.GetPackagesAsync("agent", BuildConstants.RunnerPackage.PackageName, 5, includeToken: false, cancellationToken: CancellationToken.None);
|
|
||||||
if (serverPackages.Count > 0)
|
|
||||||
{
|
|
||||||
serverPackages = serverPackages.OrderByDescending(x => x.Version).ToList();
|
|
||||||
Trace.Info($"Newer packages {StringUtil.ConvertToJson(serverPackages.Select(x => x.Version.ToString()))}");
|
|
||||||
|
|
||||||
var warnOnFailedJob = false; // any minor/patch version behind.
|
|
||||||
var warnOnOldRunnerVersion = false; // >= 2 minor version behind
|
|
||||||
if (serverPackages.Any(x => x.Version.CompareTo(currentVersion) > 0))
|
|
||||||
{
|
|
||||||
Trace.Info($"Current runner version {currentVersion} is behind the latest runner version {serverPackages[0].Version}.");
|
|
||||||
warnOnFailedJob = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serverPackages.Where(x => x.Version.Major == currentVersion.Major && x.Version.Minor > currentVersion.Minor).Count() > 1)
|
|
||||||
{
|
|
||||||
Trace.Info($"Current runner version {currentVersion} is way behind the latest runner version {serverPackages[0].Version}.");
|
|
||||||
warnOnOldRunnerVersion = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == TaskResult.Failed && warnOnFailedJob)
|
|
||||||
{
|
|
||||||
jobContext.Warning($"This job failure may be caused by using an out of date self-hosted runner. You are currently using runner version {currentVersion}. Please update to the latest version {serverPackages[0].Version}");
|
|
||||||
}
|
|
||||||
else if (warnOnOldRunnerVersion)
|
|
||||||
{
|
|
||||||
jobContext.Warning($"This self-hosted runner is currently using runner version {currentVersion}. This version is out of date. Please update to the latest version {serverPackages[0].Version}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// Ignore any error since suggest runner update is best effort.
|
|
||||||
Trace.Error($"Caught exception during runner version check: {ex}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jobContext.Global.Variables.TryGetValue(Constants.Runner.DeprecatedNodeDetectedAfterEndOfLifeActions, out var deprecatedNodeWarnings))
|
|
||||||
{
|
|
||||||
var actions = string.Join(", ", StringUtil.ConvertFromJson<HashSet<string>>(deprecatedNodeWarnings));
|
|
||||||
jobContext.Warning(string.Format(Constants.Runner.DetectedNodeAfterEndOfLifeMessage, actions));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jobContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode12DetectedAfterEndOfLifeEnvVariable, out var node16ForceWarnings))
|
|
||||||
{
|
|
||||||
var actions = string.Join(", ", StringUtil.ConvertFromJson<HashSet<string>>(node16ForceWarnings));
|
|
||||||
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode12DetectedAfterEndOfLife, actions));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jobContext.Global.Variables.TryGetValue(Constants.Runner.EnforcedNode16DetectedAfterEndOfLifeEnvVariable, out var node20ForceWarnings))
|
|
||||||
{
|
|
||||||
var actions = string.Join(", ", StringUtil.ConvertFromJson<HashSet<string>>(node20ForceWarnings));
|
|
||||||
jobContext.Warning(string.Format(Constants.Runner.EnforcedNode16DetectedAfterEndOfLife, actions));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var jobQueueTelemetry = await ShutdownQueue(throwOnFailure: true);
|
var jobQueueTelemetry = await ShutdownQueue(throwOnFailure: true);
|
||||||
// include any job telemetry from the background upload process.
|
// include any job telemetry from the background upload process.
|
||||||
if (jobQueueTelemetry.Count > 0)
|
if (jobQueueTelemetry?.Count > 0)
|
||||||
{
|
{
|
||||||
jobContext.Global.JobTelemetry.AddRange(jobQueueTelemetry);
|
jobContext.Global.JobTelemetry.AddRange(jobQueueTelemetry);
|
||||||
}
|
}
|
||||||
@@ -541,5 +499,52 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
return Array.Empty<JobTelemetry>();
|
return Array.Empty<JobTelemetry>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task WarningOutdatedRunnerAsync(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message, TaskResult result)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var currentVersion = new PackageVersion(BuildConstants.RunnerPackage.Version);
|
||||||
|
ServiceEndpoint systemConnection = message.Resources.Endpoints.Single(x => string.Equals(x.Name, WellKnownServiceEndpointNames.SystemVssConnection, StringComparison.OrdinalIgnoreCase));
|
||||||
|
VssCredentials serverCredential = VssUtil.GetVssCredential(systemConnection);
|
||||||
|
|
||||||
|
var runnerServer = HostContext.GetService<IRunnerServer>();
|
||||||
|
await runnerServer.ConnectAsync(systemConnection.Url, serverCredential);
|
||||||
|
var serverPackages = await runnerServer.GetPackagesAsync("agent", BuildConstants.RunnerPackage.PackageName, 5, includeToken: false, cancellationToken: CancellationToken.None);
|
||||||
|
if (serverPackages.Count > 0)
|
||||||
|
{
|
||||||
|
serverPackages = serverPackages.OrderByDescending(x => x.Version).ToList();
|
||||||
|
Trace.Info($"Newer packages {StringUtil.ConvertToJson(serverPackages.Select(x => x.Version.ToString()))}");
|
||||||
|
|
||||||
|
var warnOnFailedJob = false; // any minor/patch version behind.
|
||||||
|
var warnOnOldRunnerVersion = false; // >= 2 minor version behind
|
||||||
|
if (serverPackages.Any(x => x.Version.CompareTo(currentVersion) > 0))
|
||||||
|
{
|
||||||
|
Trace.Info($"Current runner version {currentVersion} is behind the latest runner version {serverPackages[0].Version}.");
|
||||||
|
warnOnFailedJob = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverPackages.Where(x => x.Version.Major == currentVersion.Major && x.Version.Minor > currentVersion.Minor).Count() > 1)
|
||||||
|
{
|
||||||
|
Trace.Info($"Current runner version {currentVersion} is way behind the latest runner version {serverPackages[0].Version}.");
|
||||||
|
warnOnOldRunnerVersion = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == TaskResult.Failed && warnOnFailedJob)
|
||||||
|
{
|
||||||
|
jobContext.Warning($"This job failure may be caused by using an out of date version of GitHub runner on your self-hosted runner. You are currently using GitHub runner version {currentVersion}. Please update to the latest version {serverPackages[0].Version}");
|
||||||
|
}
|
||||||
|
else if (warnOnOldRunnerVersion)
|
||||||
|
{
|
||||||
|
jobContext.Warning($"This self-hosted runner is currently using runner version {currentVersion}. This version is out of date. Please update to the latest version {serverPackages[0].Version}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Ignore any error since suggest runner update is best effort.
|
||||||
|
Trace.Error($"Caught exception during runner version check: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using GitHub.DistributedTask.WebApi;
|
|
||||||
using GitHub.Runner.Common;
|
|
||||||
using GitHub.Runner.Sdk;
|
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
|
||||||
{
|
|
||||||
[ServiceLocator(Default = typeof(OSWarningChecker))]
|
|
||||||
public interface IOSWarningChecker : IRunnerService
|
|
||||||
{
|
|
||||||
Task CheckOSAsync(IExecutionContext context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class OSWarningChecker : RunnerService, IOSWarningChecker
|
|
||||||
{
|
|
||||||
private static TimeSpan s_regexTimeout = TimeSpan.FromSeconds(1);
|
|
||||||
|
|
||||||
public async Task CheckOSAsync(IExecutionContext context)
|
|
||||||
{
|
|
||||||
ArgUtil.NotNull(context, nameof(context));
|
|
||||||
if (!context.Global.Variables.System_TestDotNet8Compatibility)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Output("Testing runner upgrade compatibility");
|
|
||||||
List<string> output = new();
|
|
||||||
object outputLock = new();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var process = HostContext.CreateService<IProcessInvoker>())
|
|
||||||
{
|
|
||||||
process.OutputDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stdout)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(stdout.Data))
|
|
||||||
{
|
|
||||||
lock (outputLock)
|
|
||||||
{
|
|
||||||
output.Add(stdout.Data);
|
|
||||||
Trace.Info(stdout.Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
process.ErrorDataReceived += delegate (object sender, ProcessDataReceivedEventArgs stderr)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(stderr.Data))
|
|
||||||
{
|
|
||||||
lock (outputLock)
|
|
||||||
{
|
|
||||||
output.Add(stderr.Data);
|
|
||||||
Trace.Error(stderr.Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using (var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
|
|
||||||
{
|
|
||||||
int exitCode = await process.ExecuteAsync(
|
|
||||||
workingDirectory: HostContext.GetDirectory(WellKnownDirectory.Root),
|
|
||||||
fileName: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), "testDotNet8Compatibility", $"TestDotNet8Compatibility{IOUtil.ExeExtension}"),
|
|
||||||
arguments: string.Empty,
|
|
||||||
environment: null,
|
|
||||||
cancellationToken: cancellationTokenSource.Token);
|
|
||||||
|
|
||||||
var outputStr = string.Join("\n", output).Trim();
|
|
||||||
if (exitCode != 0 || !string.Equals(outputStr, "Hello from .NET 8!", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
var pattern = context.Global.Variables.System_DotNet8CompatibilityOutputPattern;
|
|
||||||
if (!string.IsNullOrEmpty(pattern))
|
|
||||||
{
|
|
||||||
var regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant, s_regexTimeout);
|
|
||||||
if (!regex.IsMatch(outputStr))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var warningMessage = context.Global.Variables.System_DotNet8CompatibilityWarning;
|
|
||||||
if (!string.IsNullOrEmpty(warningMessage))
|
|
||||||
{
|
|
||||||
context.Warning(warningMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $".NET 8 OS compatibility test failed with exit code '{exitCode}' and output: {GetShortOutput(context, output)}" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Trace.Error("An error occurred while testing .NET 8 compatibility'");
|
|
||||||
Trace.Error(ex);
|
|
||||||
context.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = $".NET 8 OS compatibility test encountered exception type '{ex.GetType().FullName}', message: '{ex.Message}', process output: '{GetShortOutput(context, output)}'" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetShortOutput(IExecutionContext context, List<string> output)
|
|
||||||
{
|
|
||||||
var length = context.Global.Variables.System_DotNet8CompatibilityOutputLength ?? 200;
|
|
||||||
var outputStr = string.Join("\n", output).Trim();
|
|
||||||
return outputStr.Length > length ? string.Concat(outputStr.Substring(0, length), "[...]") : outputStr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
||||||
|
<SelfContained>true</SelfContained>
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<NoWarn>NU1701;NU1603</NoWarn>
|
<NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
|
||||||
<Version>$(Version)</Version>
|
<Version>$(Version)</Version>
|
||||||
<PredefinedCulturesOnly>false</PredefinedCulturesOnly>
|
<PredefinedCulturesOnly>false</PredefinedCulturesOnly>
|
||||||
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
|
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
|
||||||
@@ -18,9 +19,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" />
|
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
|
||||||
<PackageReference Include="System.ServiceProcess.ServiceController" Version="4.4.0" />
|
<PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
|
||||||
<PackageReference Include="System.Threading.Channels" Version="4.4.0" />
|
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
|
||||||
<PackageReference Include="YamlDotNet.Signed" Version="5.3.0" />
|
<PackageReference Include="YamlDotNet.Signed" Version="5.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -72,16 +72,8 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
public bool? Step_Debug => GetBoolean(Constants.Variables.Actions.StepDebug);
|
public bool? Step_Debug => GetBoolean(Constants.Variables.Actions.StepDebug);
|
||||||
|
|
||||||
public string System_DotNet8CompatibilityWarning => Get(Constants.Variables.System.DotNet8CompatibilityWarning);
|
|
||||||
|
|
||||||
public string System_DotNet8CompatibilityOutputPattern => Get(Constants.Variables.System.DotNet8CompatibilityOutputPattern);
|
|
||||||
|
|
||||||
public int? System_DotNet8CompatibilityOutputLength => GetInt(Constants.Variables.System.DotNet8CompatibilityOutputLength);
|
|
||||||
|
|
||||||
public string System_PhaseDisplayName => Get(Constants.Variables.System.PhaseDisplayName);
|
public string System_PhaseDisplayName => Get(Constants.Variables.System.PhaseDisplayName);
|
||||||
|
|
||||||
public bool System_TestDotNet8Compatibility => GetBoolean(Constants.Variables.System.TestDotNet8Compatibility) ?? false;
|
|
||||||
|
|
||||||
public string Get(string name)
|
public string Get(string name)
|
||||||
{
|
{
|
||||||
Variable variable;
|
Variable variable;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.Tracing;
|
using System.Diagnostics.Tracing;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
@@ -335,7 +336,25 @@ namespace GitHub.Services.Common.Diagnostics
|
|||||||
if (IsEnabled())
|
if (IsEnabled())
|
||||||
{
|
{
|
||||||
SetActivityId(activity);
|
SetActivityId(activity);
|
||||||
HttpRequestStop(response.RequestMessage.GetHttpMethod(), response.RequestMessage.RequestUri.AbsoluteUri, (Int32)response.StatusCode);
|
var requestId = "NoExpectedHeader";
|
||||||
|
if (response.Headers != null)
|
||||||
|
{
|
||||||
|
if (response.Headers.TryGetValues("x-github-request-id", out var headerValues) && headerValues != null)
|
||||||
|
{
|
||||||
|
requestId = headerValues.FirstOrDefault();
|
||||||
|
}
|
||||||
|
else if (response.Headers.TryGetValues("x-vss-e2eid", out headerValues) && headerValues != null)
|
||||||
|
{
|
||||||
|
requestId = headerValues.FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(requestId))
|
||||||
|
{
|
||||||
|
requestId = "NoExpectedHeader";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestStop(response.RequestMessage.GetHttpMethod(), response.RequestMessage.RequestUri.AbsoluteUri, (Int32)response.StatusCode, requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,15 +766,16 @@ namespace GitHub.Services.Common.Diagnostics
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Event(24, Level = EventLevel.Verbose, Task = Tasks.HttpRequest, Opcode = EventOpcode.Stop, Message = "Finished {0} request to {1} with status code {2}")]
|
[Event(24, Level = EventLevel.Verbose, Task = Tasks.HttpRequest, Opcode = EventOpcode.Stop, Message = "Finished {0} request to {1} with status code {2} ({3})")]
|
||||||
private void HttpRequestStop(
|
private void HttpRequestStop(
|
||||||
VssHttpMethod method,
|
VssHttpMethod method,
|
||||||
String url,
|
String url,
|
||||||
Int32 statusCode)
|
Int32 statusCode,
|
||||||
|
String requestId)
|
||||||
{
|
{
|
||||||
if (IsEnabled())
|
if (IsEnabled())
|
||||||
{
|
{
|
||||||
WriteEvent(24, (Int32)method, url, statusCode);
|
WriteEvent(24, (Int32)method, url, statusCode, requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ namespace GitHub.Services.Common
|
|||||||
|
|
||||||
public String PropertyName { get; set; }
|
public String PropertyName { get; set; }
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
[SecurityCritical]
|
[SecurityCritical]
|
||||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ namespace GitHub.Services.Common
|
|||||||
EventId = (int)info.GetValue("m_eventId", typeof(int));
|
EventId = (int)info.GetValue("m_eventId", typeof(int));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
[SecurityCritical]
|
[SecurityCritical]
|
||||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ using System.IO;
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Net.Http.Formatting;
|
using System.Net.Http.Formatting;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using GitHub.Services.Common;
|
using GitHub.Services.Common;
|
||||||
@@ -827,5 +827,36 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
userState: userState,
|
userState: userState,
|
||||||
cancellationToken: cancellationToken);
|
cancellationToken: cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// [Preview API]
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentId"></param>
|
||||||
|
/// <param name="configType"></param>
|
||||||
|
/// <param name="encodedRunnerConfig"></param>
|
||||||
|
/// <param name="userState"></param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
|
||||||
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
|
public virtual Task<string> RefreshRunnerConfigAsync(
|
||||||
|
int agentId,
|
||||||
|
string configType,
|
||||||
|
string encodedRunnerConfig,
|
||||||
|
object userState = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
HttpMethod httpMethod = new HttpMethod("POST");
|
||||||
|
Guid locationId = new Guid("13b5d709-74aa-470b-a8e9-bf9f3ded3f18");
|
||||||
|
object routeValues = new { agentId = agentId, configType = configType };
|
||||||
|
HttpContent content = new ObjectContent<string>(encodedRunnerConfig, new VssJsonMediaTypeFormatter(true));
|
||||||
|
|
||||||
|
return SendAsync<string>(
|
||||||
|
httpMethod,
|
||||||
|
locationId,
|
||||||
|
routeValues: routeValues,
|
||||||
|
version: new ApiResourceVersion(6.0, 1),
|
||||||
|
userState: userState,
|
||||||
|
cancellationToken: cancellationToken,
|
||||||
|
content: content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,6 +246,13 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public String BillingOwnerId
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the collection of variables associated with the current context.
|
/// Gets the collection of variables associated with the current context.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
public const String If = "if";
|
public const String If = "if";
|
||||||
public const String Image = "image";
|
public const String Image = "image";
|
||||||
public const String ImageName = "image-name";
|
public const String ImageName = "image-name";
|
||||||
|
public const String CustomImageVersion = "version";
|
||||||
public const String Include = "include";
|
public const String Include = "include";
|
||||||
public const String Inputs = "inputs";
|
public const String Inputs = "inputs";
|
||||||
public const String Job = "job";
|
public const String Job = "job";
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using GitHub.DistributedTask.Expressions2;
|
using GitHub.DistributedTask.Expressions2;
|
||||||
using GitHub.DistributedTask.Expressions2.Sdk;
|
using GitHub.DistributedTask.Expressions2.Sdk;
|
||||||
@@ -349,6 +350,10 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
internal static Snapshot ConvertToJobSnapshotRequest(TemplateContext context, TemplateToken token)
|
internal static Snapshot ConvertToJobSnapshotRequest(TemplateContext context, TemplateToken token)
|
||||||
{
|
{
|
||||||
string imageName = null;
|
string imageName = null;
|
||||||
|
string version = "1.*";
|
||||||
|
string versionString = string.Empty;
|
||||||
|
var condition = $"{PipelineTemplateConstants.Success}()";
|
||||||
|
|
||||||
if (token is StringToken snapshotStringLiteral)
|
if (token is StringToken snapshotStringLiteral)
|
||||||
{
|
{
|
||||||
imageName = snapshotStringLiteral.Value;
|
imageName = snapshotStringLiteral.Value;
|
||||||
@@ -359,11 +364,19 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
foreach (var snapshotPropertyPair in snapshotMapping)
|
foreach (var snapshotPropertyPair in snapshotMapping)
|
||||||
{
|
{
|
||||||
var propertyName = snapshotPropertyPair.Key.AssertString($"{PipelineTemplateConstants.Snapshot} key");
|
var propertyName = snapshotPropertyPair.Key.AssertString($"{PipelineTemplateConstants.Snapshot} key");
|
||||||
|
var propertyValue = snapshotPropertyPair.Value;
|
||||||
switch (propertyName.Value)
|
switch (propertyName.Value)
|
||||||
{
|
{
|
||||||
case PipelineTemplateConstants.ImageName:
|
case PipelineTemplateConstants.ImageName:
|
||||||
imageName = snapshotPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Snapshot} {propertyName}").Value;
|
imageName = snapshotPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Snapshot} {propertyName}").Value;
|
||||||
break;
|
break;
|
||||||
|
case PipelineTemplateConstants.If:
|
||||||
|
condition = ConvertToIfCondition(context, propertyValue, false);
|
||||||
|
break;
|
||||||
|
case PipelineTemplateConstants.CustomImageVersion:
|
||||||
|
versionString = propertyValue.AssertString($"job {PipelineTemplateConstants.Snapshot} {PipelineTemplateConstants.CustomImageVersion}").Value;
|
||||||
|
version = IsSnapshotImageVersionValid(versionString) ? versionString : null;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Snapshot} key");
|
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Snapshot} key");
|
||||||
break;
|
break;
|
||||||
@@ -376,7 +389,26 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Snapshot(imageName);
|
return new Snapshot(imageName)
|
||||||
|
{
|
||||||
|
Condition = condition,
|
||||||
|
Version = version
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsSnapshotImageVersionValid(string versionString)
|
||||||
|
{
|
||||||
|
var versionSegments = versionString.Split(".");
|
||||||
|
|
||||||
|
if (versionSegments.Length != 2 ||
|
||||||
|
!versionSegments[1].Equals("*") ||
|
||||||
|
!Int32.TryParse(versionSegments[0], NumberStyles.None, CultureInfo.InvariantCulture, result: out int parsedMajor) ||
|
||||||
|
parsedMajor < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ActionStep ConvertToStep(
|
private static ActionStep ConvertToStep(
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
{
|
{
|
||||||
if (!NameValidation.IsValid(value, allowHyphens: true) && value.Length < PipelineConstants.MaxNodeNameLength)
|
if (!NameValidation.IsValid(value, allowHyphens: true) && value.Length < PipelineConstants.MaxNodeNameLength)
|
||||||
{
|
{
|
||||||
error = $"The identifier '{value}' is invalid. IDs may only contain alphanumeric characters, '_', and '-'. IDs must start with a letter or '_' and and must be less than {PipelineConstants.MaxNodeNameLength} characters.";
|
error = $"The identifier '{value}' is invalid. IDs may only contain alphanumeric characters, '_', and '-'. IDs must start with a letter or '_' and must be less than {PipelineConstants.MaxNodeNameLength} characters.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (!m_distinctNames.Add(value))
|
else if (!m_distinctNames.Add(value))
|
||||||
|
|||||||
@@ -1,17 +1,27 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
||||||
|
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
|
||||||
|
|
||||||
namespace GitHub.DistributedTask.Pipelines
|
namespace GitHub.DistributedTask.Pipelines
|
||||||
{
|
{
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class Snapshot
|
public class Snapshot
|
||||||
{
|
{
|
||||||
public Snapshot(string imageName)
|
public Snapshot(string imageName, string condition = null, string version = null)
|
||||||
{
|
{
|
||||||
ImageName = imageName;
|
ImageName = imageName;
|
||||||
|
Condition = condition ?? $"{PipelineTemplateConstants.Success}()";
|
||||||
|
Version = version ?? "1.*";
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public String ImageName { get; set; }
|
public String ImageName { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public String Condition { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public String Version { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,11 +169,28 @@
|
|||||||
"image-name": {
|
"image-name": {
|
||||||
"type": "non-empty-string",
|
"type": "non-empty-string",
|
||||||
"required": true
|
"required": true
|
||||||
|
},
|
||||||
|
"if": "snapshot-if",
|
||||||
|
"version": {
|
||||||
|
"type": "non-empty-string",
|
||||||
|
"required": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"snapshot-if": {
|
||||||
|
"context": [
|
||||||
|
"github",
|
||||||
|
"inputs",
|
||||||
|
"vars",
|
||||||
|
"needs",
|
||||||
|
"strategy",
|
||||||
|
"matrix"
|
||||||
|
],
|
||||||
|
"string": {}
|
||||||
|
},
|
||||||
|
|
||||||
"runs-on": {
|
"runs-on": {
|
||||||
"context": [
|
"context": [
|
||||||
"github",
|
"github",
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public ActionDownloadAuthentication Authentication { get; set; }
|
public ActionDownloadAuthentication Authentication { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public ActionDownloadPackageDetails PackageDetails { get; set; }
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string NameWithOwner { get; set; }
|
public string NameWithOwner { get; set; }
|
||||||
|
|
||||||
@@ -37,4 +40,14 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DataContract]
|
||||||
|
public class ActionDownloadPackageDetails
|
||||||
|
{
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public string Version { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public string ManifestDigest { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The url to connect to to poll for messages
|
/// The url to connect to poll for messages
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("server_url")]
|
[JsonProperty("server_url")]
|
||||||
public string ServerUrl
|
public string ServerUrl
|
||||||
|
|||||||
58
src/Sdk/DTWebApi/WebApi/RunnerRefreshConfigMessage.cs
Normal file
58
src/Sdk/DTWebApi/WebApi/RunnerRefreshConfigMessage.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using GitHub.Services.WebApi;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace GitHub.DistributedTask.WebApi
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
public sealed class RunnerRefreshConfigMessage
|
||||||
|
{
|
||||||
|
public static readonly String MessageType = "RunnerRefreshConfig";
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
internal RunnerRefreshConfigMessage()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RunnerRefreshConfigMessage(
|
||||||
|
string runnerQualifiedId,
|
||||||
|
string configType,
|
||||||
|
string serviceType,
|
||||||
|
string configRefreshUrl)
|
||||||
|
{
|
||||||
|
this.RunnerQualifiedId = runnerQualifiedId;
|
||||||
|
this.ConfigType = configType;
|
||||||
|
this.ServiceType = serviceType;
|
||||||
|
this.ConfigRefreshUrl = configRefreshUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember(Name = "runnerQualifiedId")]
|
||||||
|
public String RunnerQualifiedId
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember(Name = "configType")]
|
||||||
|
public String ConfigType
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember(Name = "serviceType")]
|
||||||
|
public String ServiceType
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember(Name = "configRefreshURL")]
|
||||||
|
public String ConfigRefreshUrl
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/Sdk/DTWebApi/WebApi/ServiceConnectivityCheck.cs
Normal file
42
src/Sdk/DTWebApi/WebApi/ServiceConnectivityCheck.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace GitHub.DistributedTask.WebApi
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
public class ServiceConnectivityCheckInput
|
||||||
|
{
|
||||||
|
[JsonConstructor]
|
||||||
|
public ServiceConnectivityCheckInput()
|
||||||
|
{
|
||||||
|
Endpoints = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public Dictionary<string, string> Endpoints { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public int IntervalInSecond { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public int RequestTimeoutInSecond { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataContract]
|
||||||
|
public class ServiceConnectivityCheckResult
|
||||||
|
{
|
||||||
|
[JsonConstructor]
|
||||||
|
public ServiceConnectivityCheckResult()
|
||||||
|
{
|
||||||
|
EndpointsResult = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember(Order = 1, EmitDefaultValue = true)]
|
||||||
|
public bool HasFailure { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Order = 2, EmitDefaultValue = false)]
|
||||||
|
public Dictionary<string, List<string>> EndpointsResult { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,5 +7,6 @@ namespace GitHub.DistributedTask.WebApi
|
|||||||
public static readonly String JobId = "system.jobId";
|
public static readonly String JobId = "system.jobId";
|
||||||
public static readonly String RunnerLowDiskspaceThreshold = "system.runner.lowdiskspacethreshold";
|
public static readonly String RunnerLowDiskspaceThreshold = "system.runner.lowdiskspacethreshold";
|
||||||
public static readonly String RunnerEnvironment = "system.runnerEnvironment";
|
public static readonly String RunnerEnvironment = "system.runnerEnvironment";
|
||||||
|
public static readonly String RunnerServiceConnectivityTest = "system.runner.serviceconnectivitycheckinput";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,5 +10,8 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
|
|
||||||
[DataMember(Name = "runnerOS", EmitDefaultValue = false)]
|
[DataMember(Name = "runnerOS", EmitDefaultValue = false)]
|
||||||
public string RunnerOS { get; set; }
|
public string RunnerOS { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "billingOwnerId", EmitDefaultValue = false)]
|
||||||
|
public string BillingOwnerId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ namespace Sdk.RSWebApi.Contracts
|
|||||||
[DataMember(Name = "message", EmitDefaultValue = false)]
|
[DataMember(Name = "message", EmitDefaultValue = false)]
|
||||||
public string Message;
|
public string Message;
|
||||||
|
|
||||||
|
[DataMember(Name = "title", EmitDefaultValue = false)]
|
||||||
|
public string Title;
|
||||||
|
|
||||||
[DataMember(Name = "rawDetails", EmitDefaultValue = false)]
|
[DataMember(Name = "rawDetails", EmitDefaultValue = false)]
|
||||||
public string RawDetails;
|
public string RawDetails;
|
||||||
|
|
||||||
@@ -31,5 +34,8 @@ namespace Sdk.RSWebApi.Contracts
|
|||||||
|
|
||||||
[DataMember(Name = "endColumn", EmitDefaultValue = false)]
|
[DataMember(Name = "endColumn", EmitDefaultValue = false)]
|
||||||
public long EndColumn;
|
public long EndColumn;
|
||||||
|
|
||||||
|
[DataMember(Name = "stepNumber", EmitDefaultValue = false)]
|
||||||
|
public long StepNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/Sdk/RSWebApi/Contracts/BrokerError.cs
Normal file
20
src/Sdk/RSWebApi/Contracts/BrokerError.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace GitHub.Actions.RunService.WebApi
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
public class BrokerError
|
||||||
|
{
|
||||||
|
[DataMember(Name = "source", EmitDefaultValue = false)]
|
||||||
|
public string Source { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "errorKind", EmitDefaultValue = false)]
|
||||||
|
public string ErrorKind { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "statusCode", EmitDefaultValue = false)]
|
||||||
|
public int StatusCode { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "errorMessage", EmitDefaultValue = false)]
|
||||||
|
public string Message { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/Sdk/RSWebApi/Contracts/BrokerErrorKind.cs
Normal file
12
src/Sdk/RSWebApi/Contracts/BrokerErrorKind.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace GitHub.Actions.RunService.WebApi
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
public class BrokerErrorKind
|
||||||
|
{
|
||||||
|
public const string RunnerNotFound = "RunnerNotFound";
|
||||||
|
public const string RunnerVersionTooOld = "RunnerVersionTooOld";
|
||||||
|
public const string HostedRunnerDeprovisioned = "HostedRunnerDeprovisioned";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,13 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
[DataMember(Name = "annotations", EmitDefaultValue = false)]
|
[DataMember(Name = "annotations", EmitDefaultValue = false)]
|
||||||
public IList<Annotation> Annotations { get; set; }
|
public IList<Annotation> Annotations { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "telemetry", EmitDefaultValue = false)]
|
||||||
|
public IList<Telemetry> Telemetry { get; set; }
|
||||||
|
|
||||||
[DataMember(Name = "environmentUrl", EmitDefaultValue = false)]
|
[DataMember(Name = "environmentUrl", EmitDefaultValue = false)]
|
||||||
public string EnvironmentUrl { get; set; }
|
public string EnvironmentUrl { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "billingOwnerId", EmitDefaultValue = false)]
|
||||||
|
public string BillingOwnerId { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ namespace Sdk.RSWebApi.Contracts
|
|||||||
var columnNumber = GetAnnotationNumber(issue, RunIssueKeys.Col) ?? 0;
|
var columnNumber = GetAnnotationNumber(issue, RunIssueKeys.Col) ?? 0;
|
||||||
var endColumnNumber = GetAnnotationNumber(issue, RunIssueKeys.EndColumn) ?? columnNumber;
|
var endColumnNumber = GetAnnotationNumber(issue, RunIssueKeys.EndColumn) ?? columnNumber;
|
||||||
var logLineNumber = GetAnnotationNumber(issue, RunIssueKeys.LogLineNumber) ?? 0;
|
var logLineNumber = GetAnnotationNumber(issue, RunIssueKeys.LogLineNumber) ?? 0;
|
||||||
|
var stepNumber = GetAnnotationNumber(issue, RunIssueKeys.StepNumber) ?? 0;
|
||||||
|
var title = GetAnnotationField(issue, RunIssueKeys.Title);
|
||||||
|
|
||||||
if (path == null && lineNumber == 0 && logLineNumber != 0)
|
if (path == null && lineNumber == 0 && logLineNumber != 0)
|
||||||
{
|
{
|
||||||
@@ -33,11 +35,13 @@ namespace Sdk.RSWebApi.Contracts
|
|||||||
{
|
{
|
||||||
Level = annotationLevel,
|
Level = annotationLevel,
|
||||||
Message = issueMessage,
|
Message = issueMessage,
|
||||||
|
Title = title,
|
||||||
Path = path,
|
Path = path,
|
||||||
StartLine = lineNumber,
|
StartLine = lineNumber,
|
||||||
EndLine = endLineNumber,
|
EndLine = endLineNumber,
|
||||||
StartColumn = columnNumber,
|
StartColumn = columnNumber,
|
||||||
EndColumn = endColumnNumber,
|
EndColumn = endColumnNumber,
|
||||||
|
StepNumber = stepNumber,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,7 @@
|
|||||||
public const string EndLine = "endLine";
|
public const string EndLine = "endLine";
|
||||||
public const string EndColumn = "endColumn";
|
public const string EndColumn = "endColumn";
|
||||||
public const string LogLineNumber = "logFileLineNumber";
|
public const string LogLineNumber = "logFileLineNumber";
|
||||||
|
public const string StepNumber = "stepNumber";
|
||||||
|
public const string Title = "title";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -16,9 +16,20 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
[DataMember(Name = "number", EmitDefaultValue = false)]
|
[DataMember(Name = "number", EmitDefaultValue = false)]
|
||||||
public int? Number { get; set; }
|
public int? Number { get; set; }
|
||||||
|
|
||||||
|
// Example: "Run actions/checkout@v3"
|
||||||
[DataMember(Name = "name", EmitDefaultValue = false)]
|
[DataMember(Name = "name", EmitDefaultValue = false)]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
// Example: "actions/checkout"
|
||||||
|
[DataMember(Name = "action_name", EmitDefaultValue = false)]
|
||||||
|
public string ActionName { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "ref", EmitDefaultValue = false)]
|
||||||
|
public string Ref { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "type", EmitDefaultValue = false)]
|
||||||
|
public string Type { get; set; }
|
||||||
|
|
||||||
[DataMember(Name = "status")]
|
[DataMember(Name = "status")]
|
||||||
public TimelineRecordState? Status { get; set; }
|
public TimelineRecordState? Status { get; set; }
|
||||||
|
|
||||||
|
|||||||
20
src/Sdk/RSWebApi/Contracts/Telemetry.cs
Normal file
20
src/Sdk/RSWebApi/Contracts/Telemetry.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
|
namespace Sdk.RSWebApi.Contracts
|
||||||
|
{
|
||||||
|
[DataContract]
|
||||||
|
public struct Telemetry
|
||||||
|
{
|
||||||
|
public Telemetry(string message, string type)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember(Name = "message", EmitDefaultValue = false)]
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "type", EmitDefaultValue = false)]
|
||||||
|
public string Type { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -70,13 +71,15 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
Uri requestUri,
|
Uri requestUri,
|
||||||
string messageId,
|
string messageId,
|
||||||
string runnerOS,
|
string runnerOS,
|
||||||
|
string billingOwnerId,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
HttpMethod httpMethod = new HttpMethod("POST");
|
HttpMethod httpMethod = new HttpMethod("POST");
|
||||||
var payload = new AcquireJobRequest
|
var payload = new AcquireJobRequest
|
||||||
{
|
{
|
||||||
JobMessageId = messageId,
|
JobMessageId = messageId,
|
||||||
RunnerOS = runnerOS
|
RunnerOS = runnerOS,
|
||||||
|
BillingOwnerId = billingOwnerId,
|
||||||
};
|
};
|
||||||
|
|
||||||
requestUri = new Uri(requestUri, "acquirejob");
|
requestUri = new Uri(requestUri, "acquirejob");
|
||||||
@@ -107,15 +110,6 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary back compat
|
|
||||||
switch (result.StatusCode)
|
|
||||||
{
|
|
||||||
case HttpStatusCode.NotFound:
|
|
||||||
throw new TaskOrchestrationJobNotFoundException($"Job message not found: {messageId}");
|
|
||||||
case HttpStatusCode.Conflict:
|
|
||||||
throw new TaskOrchestrationJobAlreadyAcquiredException($"Job message already acquired: {messageId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.ErrorBody))
|
if (!string.IsNullOrEmpty(result.ErrorBody))
|
||||||
{
|
{
|
||||||
throw new Exception($"Failed to get job message: {result.Error}. {Truncate(result.ErrorBody)}");
|
throw new Exception($"Failed to get job message: {result.Error}. {Truncate(result.ErrorBody)}");
|
||||||
@@ -135,6 +129,8 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
IList<StepResult> stepResults,
|
IList<StepResult> stepResults,
|
||||||
IList<Annotation> jobAnnotations,
|
IList<Annotation> jobAnnotations,
|
||||||
string environmentUrl,
|
string environmentUrl,
|
||||||
|
IList<Telemetry> telemetry,
|
||||||
|
string billingOwnerId,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
HttpMethod httpMethod = new HttpMethod("POST");
|
HttpMethod httpMethod = new HttpMethod("POST");
|
||||||
@@ -147,6 +143,8 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
StepResults = stepResults,
|
StepResults = stepResults,
|
||||||
Annotations = jobAnnotations,
|
Annotations = jobAnnotations,
|
||||||
EnvironmentUrl = environmentUrl,
|
EnvironmentUrl = environmentUrl,
|
||||||
|
Telemetry = telemetry,
|
||||||
|
BillingOwnerId = billingOwnerId,
|
||||||
};
|
};
|
||||||
|
|
||||||
requestUri = new Uri(requestUri, "completejob");
|
requestUri = new Uri(requestUri, "completejob");
|
||||||
@@ -171,13 +169,6 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary back compat
|
|
||||||
switch (result.StatusCode)
|
|
||||||
{
|
|
||||||
case HttpStatusCode.NotFound:
|
|
||||||
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.ErrorBody))
|
if (!string.IsNullOrEmpty(result.ErrorBody))
|
||||||
{
|
{
|
||||||
throw new Exception($"Failed to complete job: {result.Error}. {Truncate(result.ErrorBody)}");
|
throw new Exception($"Failed to complete job: {result.Error}. {Truncate(result.ErrorBody)}");
|
||||||
@@ -225,13 +216,6 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary back compat
|
|
||||||
switch (result.StatusCode)
|
|
||||||
{
|
|
||||||
case HttpStatusCode.NotFound:
|
|
||||||
throw new TaskOrchestrationJobNotFoundException($"Job not found: {jobId}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.ErrorBody))
|
if (!string.IsNullOrEmpty(result.ErrorBody))
|
||||||
{
|
{
|
||||||
throw new Exception($"Failed to renew job: {result.Error}. {Truncate(result.ErrorBody)}");
|
throw new Exception($"Failed to renew job: {result.Error}. {Truncate(result.ErrorBody)}");
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;win-x86;linux-x64;linux-arm64;linux-arm;osx-x64;osx-arm64;win-arm64</RuntimeIdentifiers>
|
||||||
|
<!-- <SelfContained>true</SelfContained> -->
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<NoWarn>NU1701;NU1603</NoWarn>
|
<NoWarn>NU1701;NU1603;SYSLIB0050;SYSLIB0051</NoWarn>
|
||||||
<Version>$(Version)</Version>
|
<Version>$(Version)</Version>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>8.0</LangVersion>
|
||||||
@@ -13,18 +14,19 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Storage.Blobs" Version="12.19.1" />
|
<PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" />
|
||||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.4.0" />
|
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.2.1" />
|
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
|
||||||
<PackageReference Include="System.Security.Cryptography.Cng" Version="4.4.0" />
|
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="8.0.0" />
|
||||||
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="4.4.0" />
|
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
|
||||||
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="4.4.0" />
|
|
||||||
<PackageReference Include="Minimatch" Version="2.0.0" />
|
<PackageReference Include="Minimatch" Version="2.0.0" />
|
||||||
<PackageReference Include="YamlDotNet.Signed" Version="5.3.0" />
|
<PackageReference Include="YamlDotNet.Signed" Version="5.3.0" />
|
||||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
||||||
|
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
|
||||||
|
<PackageReference Include="System.Formats.Asn1" Version="8.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
new HttpMethod("GET"),
|
new HttpMethod("GET"),
|
||||||
requestUri: requestUri,
|
requestUri: requestUri,
|
||||||
queryParameters: queryParams,
|
queryParameters: queryParams,
|
||||||
|
readErrorBody: true,
|
||||||
cancellationToken: cancellationToken);
|
cancellationToken: cancellationToken);
|
||||||
|
|
||||||
if (result.IsSuccess)
|
if (result.IsSuccess)
|
||||||
@@ -110,8 +111,25 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
return result.Value;
|
return result.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the only time we throw a `Forbidden` exception from Listener /messages is when the runner is
|
if (TryParseErrorBody(result.ErrorBody, out BrokerError brokerError))
|
||||||
// disable_update and is too old to poll
|
{
|
||||||
|
switch (brokerError.ErrorKind)
|
||||||
|
{
|
||||||
|
case BrokerErrorKind.RunnerNotFound:
|
||||||
|
throw new RunnerNotFoundException(brokerError.Message);
|
||||||
|
case BrokerErrorKind.RunnerVersionTooOld:
|
||||||
|
throw new AccessDeniedException(brokerError.Message)
|
||||||
|
{
|
||||||
|
ErrorCode = 1
|
||||||
|
};
|
||||||
|
case BrokerErrorKind.HostedRunnerDeprovisioned:
|
||||||
|
throw new HostedRunnerDeprovisionedException(brokerError.Message);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// temporary back compat
|
||||||
if (result.StatusCode == HttpStatusCode.Forbidden)
|
if (result.StatusCode == HttpStatusCode.Forbidden)
|
||||||
{
|
{
|
||||||
throw new AccessDeniedException($"{result.Error} Runner version v{runnerVersion} is deprecated and cannot receive messages.")
|
throw new AccessDeniedException($"{result.Error} Runner version v{runnerVersion} is deprecated and cannot receive messages.")
|
||||||
@@ -120,7 +138,7 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception($"Failed to get job message: {result.Error}");
|
throw new Exception($"Failed to get job message. Request to {requestUri} failed with status: {result.StatusCode}. Error message {result.Error}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TaskAgentSession> CreateSessionAsync(
|
public async Task<TaskAgentSession> CreateSessionAsync(
|
||||||
@@ -172,5 +190,26 @@ namespace GitHub.Actions.RunService.WebApi
|
|||||||
|
|
||||||
throw new Exception($"Failed to delete broker session: {result.Error}");
|
throw new Exception($"Failed to delete broker session: {result.Error}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryParseErrorBody(string errorBody, out BrokerError error)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(errorBody))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
error = JsonUtility.FromString<BrokerError>(errorBody);
|
||||||
|
if (error?.Source == "actions-broker-listener")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GitHub.Services.WebApi
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public sealed class HostedRunnerDeprovisionedException : Exception
|
||||||
|
{
|
||||||
|
public HostedRunnerDeprovisionedException()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public HostedRunnerDeprovisionedException(String message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public HostedRunnerDeprovisionedException(String message, Exception innerException)
|
||||||
|
: base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/Sdk/WebApi/WebApi/Exceptions/RunnerNotFoundException.cs
Normal file
26
src/Sdk/WebApi/WebApi/Exceptions/RunnerNotFoundException.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using GitHub.Services.Common;
|
||||||
|
using GitHub.Services.WebApi;
|
||||||
|
|
||||||
|
namespace GitHub.Services.WebApi
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public sealed class RunnerNotFoundException : Exception
|
||||||
|
{
|
||||||
|
public RunnerNotFoundException()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RunnerNotFoundException(String message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RunnerNotFoundException(String message, Exception innerException)
|
||||||
|
: base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,9 @@ namespace GitHub.Services.Launch.Contracts
|
|||||||
{
|
{
|
||||||
[DataMember(EmitDefaultValue = false, Name = "authentication")]
|
[DataMember(EmitDefaultValue = false, Name = "authentication")]
|
||||||
public ActionDownloadAuthenticationResponse Authentication { get; set; }
|
public ActionDownloadAuthenticationResponse Authentication { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false, Name = "package_details")]
|
||||||
|
public ActionDownloadPackageDetailsResponse PackageDetails { get; set; }
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false, Name = "name")]
|
[DataMember(EmitDefaultValue = false, Name = "name")]
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
@@ -59,6 +62,17 @@ namespace GitHub.Services.Launch.Contracts
|
|||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[DataContract]
|
||||||
|
public class ActionDownloadPackageDetailsResponse
|
||||||
|
{
|
||||||
|
[DataMember(EmitDefaultValue = false, Name = "version")]
|
||||||
|
public string Version { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false, Name = "manifest_digest")]
|
||||||
|
public string ManifestDigest { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[DataContract]
|
[DataContract]
|
||||||
public class ActionDownloadInfoResponseCollection
|
public class ActionDownloadInfoResponseCollection
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ namespace GitHub.Services.Launch.Client
|
|||||||
TarballUrl = actionDownloadInfoResponse.TarUrl,
|
TarballUrl = actionDownloadInfoResponse.TarUrl,
|
||||||
Ref = actionDownloadInfoResponse.Version,
|
Ref = actionDownloadInfoResponse.Version,
|
||||||
ZipballUrl = actionDownloadInfoResponse.ZipUrl,
|
ZipballUrl = actionDownloadInfoResponse.ZipUrl,
|
||||||
|
PackageDetails = ToServerData(actionDownloadInfoResponse.PackageDetails)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +109,21 @@ namespace GitHub.Services.Launch.Client
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static ActionDownloadPackageDetails? ToServerData(ActionDownloadPackageDetailsResponse? actionDownloadPackageDetails)
|
||||||
|
{
|
||||||
|
if (actionDownloadPackageDetails == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ActionDownloadPackageDetails
|
||||||
|
{
|
||||||
|
Version = actionDownloadPackageDetails.Version,
|
||||||
|
ManifestDigest = actionDownloadPackageDetails.ManifestDigest
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private MediaTypeFormatter m_formatter;
|
private MediaTypeFormatter m_formatter;
|
||||||
private Uri m_launchServiceUrl;
|
private Uri m_launchServiceUrl;
|
||||||
private string m_token;
|
private string m_token;
|
||||||
|
|||||||
@@ -677,7 +677,7 @@ namespace GitHub.Services.WebApi.Location
|
|||||||
Int32 lastChangeId = m_locationDataCacheManager.GetLastChangeId();
|
Int32 lastChangeId = m_locationDataCacheManager.GetLastChangeId();
|
||||||
|
|
||||||
// If we have -1 then that means we have no disk cache yet or it means that we recently hit an exception trying to reload
|
// If we have -1 then that means we have no disk cache yet or it means that we recently hit an exception trying to reload
|
||||||
// the the cache from disk (see Exception catch block in EnsureDiskCacheLoaded).
|
// the cache from disk (see Exception catch block in EnsureDiskCacheLoaded).
|
||||||
// Either way, we cannot make a call to the server with -1 and pass None.
|
// Either way, we cannot make a call to the server with -1 and pass None.
|
||||||
// If we do, the resulting payload (which would have ClientCacheFresh=false but include no ServiceDefinitions)
|
// If we do, the resulting payload (which would have ClientCacheFresh=false but include no ServiceDefinitions)
|
||||||
// would leave the in-memory cache in an inconsistent state
|
// would leave the in-memory cache in an inconsistent state
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ namespace GitHub.Services.OAuth
|
|||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||||
{
|
{
|
||||||
base.GetObjectData(info, context);
|
base.GetObjectData(info, context);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
@@ -132,7 +133,7 @@ namespace GitHub.Services.Results.Client
|
|||||||
|
|
||||||
private async Task StepSummaryUploadCompleteAsync(string planId, string jobId, Guid stepId, long size, CancellationToken cancellationToken)
|
private async Task StepSummaryUploadCompleteAsync(string planId, string jobId, Guid stepId, long size, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat);
|
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat, CultureInfo.InvariantCulture);
|
||||||
var request = new StepSummaryMetadataCreate()
|
var request = new StepSummaryMetadataCreate()
|
||||||
{
|
{
|
||||||
WorkflowJobRunBackendId = jobId,
|
WorkflowJobRunBackendId = jobId,
|
||||||
@@ -148,7 +149,7 @@ namespace GitHub.Services.Results.Client
|
|||||||
|
|
||||||
private async Task StepLogUploadCompleteAsync(string planId, string jobId, Guid stepId, long lineCount, CancellationToken cancellationToken)
|
private async Task StepLogUploadCompleteAsync(string planId, string jobId, Guid stepId, long lineCount, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat);
|
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat, CultureInfo.InvariantCulture);
|
||||||
var request = new StepLogsMetadataCreate()
|
var request = new StepLogsMetadataCreate()
|
||||||
{
|
{
|
||||||
WorkflowJobRunBackendId = jobId,
|
WorkflowJobRunBackendId = jobId,
|
||||||
@@ -164,7 +165,7 @@ namespace GitHub.Services.Results.Client
|
|||||||
|
|
||||||
private async Task JobLogUploadCompleteAsync(string planId, string jobId, long lineCount, CancellationToken cancellationToken)
|
private async Task JobLogUploadCompleteAsync(string planId, string jobId, long lineCount, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat);
|
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat, CultureInfo.InvariantCulture);
|
||||||
var request = new JobLogsMetadataCreate()
|
var request = new JobLogsMetadataCreate()
|
||||||
{
|
{
|
||||||
WorkflowJobRunBackendId = jobId,
|
WorkflowJobRunBackendId = jobId,
|
||||||
@@ -565,7 +566,7 @@ namespace GitHub.Services.Results.Client
|
|||||||
|
|
||||||
public async Task UpdateWorkflowStepsAsync(Guid planId, IEnumerable<TimelineRecord> records, CancellationToken cancellationToken)
|
public async Task UpdateWorkflowStepsAsync(Guid planId, IEnumerable<TimelineRecord> records, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat);
|
var timestamp = DateTime.UtcNow.ToString(Constants.TimestampFormat, CultureInfo.InvariantCulture);
|
||||||
var stepRecords = records.Where(r => String.Equals(r.RecordType, "Task", StringComparison.Ordinal));
|
var stepRecords = records.Where(r => String.Equals(r.RecordType, "Task", StringComparison.Ordinal));
|
||||||
var stepUpdateRequests = stepRecords.GroupBy(r => r.ParentId).Select(sg => new StepsUpdateRequest()
|
var stepUpdateRequests = stepRecords.GroupBy(r => r.ParentId).Select(sg => new StepsUpdateRequest()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace GitHub.Services.WebApi
|
|||||||
HttpStatusCode = (HttpStatusCode)info.GetInt32("HttpStatusCode");
|
HttpStatusCode = (HttpStatusCode)info.GetInt32("HttpStatusCode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
[SecurityCritical]
|
[SecurityCritical]
|
||||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
private readonly RunnerSettings _settings;
|
private readonly RunnerSettings _settings;
|
||||||
private readonly Mock<IConfigurationManager> _config;
|
private readonly Mock<IConfigurationManager> _config;
|
||||||
private readonly Mock<IBrokerServer> _brokerServer;
|
private readonly Mock<IBrokerServer> _brokerServer;
|
||||||
|
private readonly Mock<IRunnerServer> _runnerServer;
|
||||||
private readonly Mock<ICredentialManager> _credMgr;
|
private readonly Mock<ICredentialManager> _credMgr;
|
||||||
private Mock<IConfigurationStore> _store;
|
private Mock<IConfigurationStore> _store;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_credMgr = new Mock<ICredentialManager>();
|
_credMgr = new Mock<ICredentialManager>();
|
||||||
_store = new Mock<IConfigurationStore>();
|
_store = new Mock<IConfigurationStore>();
|
||||||
_brokerServer = new Mock<IBrokerServer>();
|
_brokerServer = new Mock<IBrokerServer>();
|
||||||
|
_runnerServer = new Mock<IRunnerServer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -75,6 +77,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
tc.SetSingleton<ICredentialManager>(_credMgr.Object);
|
tc.SetSingleton<ICredentialManager>(_credMgr.Object);
|
||||||
tc.SetSingleton<IConfigurationStore>(_store.Object);
|
tc.SetSingleton<IConfigurationStore>(_store.Object);
|
||||||
tc.SetSingleton<IBrokerServer>(_brokerServer.Object);
|
tc.SetSingleton<IBrokerServer>(_brokerServer.Object);
|
||||||
|
tc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
return tc;
|
return tc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,20 +36,23 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_configurationStore = new Mock<IConfigurationStore>();
|
_configurationStore = new Mock<IConfigurationStore>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pipelines.AgentJobRequestMessage CreateJobRequestMessage()
|
private Pipelines.AgentJobRequestMessage CreateJobRequestMessage(string billingOwnerId = null)
|
||||||
{
|
{
|
||||||
TaskOrchestrationPlanReference plan = new();
|
TaskOrchestrationPlanReference plan = new();
|
||||||
TimelineReference timeline = null;
|
TimelineReference timeline = null;
|
||||||
Guid jobId = Guid.NewGuid();
|
Guid jobId = Guid.NewGuid();
|
||||||
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
|
var result = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, "someJob", "someJob", null, null, null, new Dictionary<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), null, null, null, null, null);
|
||||||
result.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
|
result.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData();
|
||||||
|
result.BillingOwnerId = billingOwnerId;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
[Trait("Level", "L0")]
|
[Trait("Level", "L0")]
|
||||||
[Trait("Category", "Runner")]
|
[Trait("Category", "Runner")]
|
||||||
public async void DispatchesJobRequest()
|
[InlineData(null)]
|
||||||
|
[InlineData("billingOwnerId")]
|
||||||
|
public async void DispatchesJobRequest(string billingOwnerId)
|
||||||
{
|
{
|
||||||
//Arrange
|
//Arrange
|
||||||
using (var hc = new TestHostContext(this))
|
using (var hc = new TestHostContext(this))
|
||||||
@@ -65,7 +68,7 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
jobDispatcher.Initialize(hc);
|
jobDispatcher.Initialize(hc);
|
||||||
|
|
||||||
var ts = new CancellationTokenSource();
|
var ts = new CancellationTokenSource();
|
||||||
Pipelines.AgentJobRequestMessage message = CreateJobRequestMessage();
|
Pipelines.AgentJobRequestMessage message = CreateJobRequestMessage(billingOwnerId);
|
||||||
string strMessage = JsonUtility.ToString(message);
|
string strMessage = JsonUtility.ToString(message);
|
||||||
|
|
||||||
_processInvoker.Setup(x => x.ExecuteAsync(It.IsAny<String>(), It.IsAny<String>(), "spawnclient 1 2", null, It.IsAny<CancellationToken>()))
|
_processInvoker.Setup(x => x.ExecuteAsync(It.IsAny<String>(), It.IsAny<String>(), "spawnclient 1 2", null, It.IsAny<CancellationToken>()))
|
||||||
|
|||||||
579
src/Test/L0/Listener/RunnerConfigUpdaterTests.cs
Normal file
579
src/Test/L0/Listener/RunnerConfigUpdaterTests.cs
Normal file
@@ -0,0 +1,579 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using GitHub.Runner.Listener;
|
||||||
|
using GitHub.Runner.Common;
|
||||||
|
using GitHub.Runner.Sdk;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
using System.Threading;
|
||||||
|
using GitHub.Runner.Common.Tests;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace GitHub.Runner.Tests.Listener
|
||||||
|
{
|
||||||
|
public class RunnerConfigUpdaterL0
|
||||||
|
{
|
||||||
|
private Mock<IConfigurationStore> _configurationStore;
|
||||||
|
private Mock<IRunnerServer> _runnerServer;
|
||||||
|
|
||||||
|
public RunnerConfigUpdaterL0()
|
||||||
|
{
|
||||||
|
_configurationStore = new Mock<IConfigurationStore>();
|
||||||
|
_runnerServer = new Mock<IRunnerServer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_InvalidRunnerQualifiedId_ShouldReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var invalidRunnerQualifiedId = "invalid/runner/qualified/id";
|
||||||
|
var configType = "runner";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(invalidRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>((s) => s.Contains("Runner qualified id")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_ValidRunnerQualifiedId_ShouldNotReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "runner";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>((s) => s.Contains("Runner qualified id")), It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_InvalidConfigType_ShouldReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var invalidConfigType = "invalidConfigType";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, invalidConfigType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>((s) => s.Contains("Invalid config type")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_UpdateRunnerSettings_ShouldSucceed()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
|
||||||
|
var encodedConfig = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringUtil.ConvertToJson(setting)));
|
||||||
|
_runnerServer.Setup(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.Is<string>(s => s == "runner"), It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(encodedConfig);
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "runner";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.RefreshRunnerConfigAsync(1, "runner", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>(s => s.Contains("Runner settings updated successfully")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedSettings(It.IsAny<RunnerSettings>()), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_UpdateRunnerSettings_IgnoredEmptyRefreshResult()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "runner";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.RefreshRunnerConfigAsync(1, "runner", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>(s => s.Contains("Runner settings updated successfully")), It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_UpdateRunnerCredentials_ShouldSucceed()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
var credData = new CredentialData
|
||||||
|
{
|
||||||
|
Scheme = "OAuth"
|
||||||
|
};
|
||||||
|
credData.Data.Add("ClientId", "12345");
|
||||||
|
_configurationStore.Setup(x => x.GetCredentials()).Returns(credData);
|
||||||
|
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
IOUtil.SaveObject(credData, hc.GetConfigFile(WellKnownConfigFile.Credentials));
|
||||||
|
|
||||||
|
var encodedConfig = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringUtil.ConvertToJson(credData)));
|
||||||
|
_runnerServer.Setup(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.Is<string>(s => s == "credentials"), It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(encodedConfig);
|
||||||
|
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "credentials";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.RefreshRunnerConfigAsync(1, "credentials", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>(s => s.Contains("Runner credentials updated successfully")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedCredential(It.IsAny<CredentialData>()), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_UpdateRunnerCredentials_IgnoredEmptyRefreshResult()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
var credData = new CredentialData
|
||||||
|
{
|
||||||
|
Scheme = "OAuth"
|
||||||
|
};
|
||||||
|
credData.Data.Add("ClientId", "12345");
|
||||||
|
_configurationStore.Setup(x => x.GetCredentials()).Returns(credData);
|
||||||
|
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
IOUtil.SaveObject(credData, hc.GetConfigFile(WellKnownConfigFile.Credentials));
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "credentials";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.RefreshRunnerConfigAsync(1, "credentials", It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>(s => s.Contains("Runner credentials updated successfully")), It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedCredential(It.IsAny<CredentialData>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_RefreshRunnerSettingsFailure_ShouldReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
_runnerServer.Setup(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.Is<string>(s => s == "runner"), It.IsAny<string>(), It.IsAny<CancellationToken>())).ThrowsAsync(new Exception("Refresh failed"));
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "runner";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>((s) => s.Contains("Failed to refresh")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_RefreshRunnerCredetialsFailure_ShouldReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
var credData = new CredentialData
|
||||||
|
{
|
||||||
|
Scheme = "OAuth"
|
||||||
|
};
|
||||||
|
credData.Data.Add("ClientId", "12345");
|
||||||
|
_configurationStore.Setup(x => x.GetCredentials()).Returns(credData);
|
||||||
|
|
||||||
|
_runnerServer.Setup(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.Is<string>(s => s == "credentials"), It.IsAny<string>(), It.IsAny<CancellationToken>())).ThrowsAsync(new Exception("Refresh failed"));
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "credentials";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>((s) => s.Contains("Failed to refresh")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_RefreshRunnerSettingsWithDifferentRunnerId_ShouldReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
|
||||||
|
var differentRunnerSetting = new RunnerSettings { AgentId = 2, AgentName = "agent1" };
|
||||||
|
var encodedConfig = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringUtil.ConvertToJson(differentRunnerSetting)));
|
||||||
|
_runnerServer.Setup(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.Is<string>(s => s == "runner"), It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(encodedConfig);
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "runner";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>(s => s.Contains("Runner id in refreshed config")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_RefreshRunnerSettingsWithDifferentRunnerName_ShouldReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
|
||||||
|
var differentRunnerSetting = new RunnerSettings { AgentId = 1, AgentName = "agent2" };
|
||||||
|
var encodedConfig = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringUtil.ConvertToJson(differentRunnerSetting)));
|
||||||
|
_runnerServer.Setup(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.Is<string>(s => s == "runner"), It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(encodedConfig);
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "runner";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>(s => s.Contains("Runner name in refreshed config")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_RefreshCredentialsWithDifferentScheme_ShouldReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
var credData = new CredentialData
|
||||||
|
{
|
||||||
|
Scheme = "OAuth"
|
||||||
|
};
|
||||||
|
credData.Data.Add("ClientId", "12345");
|
||||||
|
_configurationStore.Setup(x => x.GetCredentials()).Returns(credData);
|
||||||
|
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
IOUtil.SaveObject(credData, hc.GetConfigFile(WellKnownConfigFile.Credentials));
|
||||||
|
|
||||||
|
var differentCredData = new CredentialData
|
||||||
|
{
|
||||||
|
Scheme = "PAT"
|
||||||
|
};
|
||||||
|
differentCredData.Data.Add("ClientId", "12345");
|
||||||
|
var encodedConfig = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringUtil.ConvertToJson(differentCredData)));
|
||||||
|
_runnerServer.Setup(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.Is<string>(s => s == "credentials"), It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(encodedConfig);
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "credentials";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>(s => s.Contains("Credential scheme in refreshed config")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedCredential(It.IsAny<CredentialData>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_RefreshOAuthCredentialsWithDifferentClientId_ShouldReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
var credData = new CredentialData
|
||||||
|
{
|
||||||
|
Scheme = "OAuth"
|
||||||
|
};
|
||||||
|
credData.Data.Add("clientId", "12345");
|
||||||
|
_configurationStore.Setup(x => x.GetCredentials()).Returns(credData);
|
||||||
|
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
IOUtil.SaveObject(credData, hc.GetConfigFile(WellKnownConfigFile.Credentials));
|
||||||
|
|
||||||
|
var differentCredData = new CredentialData
|
||||||
|
{
|
||||||
|
Scheme = "OAuth"
|
||||||
|
};
|
||||||
|
differentCredData.Data.Add("clientId", "67890");
|
||||||
|
var encodedConfig = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringUtil.ConvertToJson(differentCredData)));
|
||||||
|
_runnerServer.Setup(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.Is<string>(s => s == "credentials"), It.IsAny<string>(), It.IsAny<CancellationToken>())).ReturnsAsync(encodedConfig);
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "credentials";
|
||||||
|
var serviceType = "pipelines";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>(s => s.Contains("Credential clientId in refreshed config")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedCredential(It.IsAny<CredentialData>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_UnsupportedServiceType_ShouldReportTelemetry()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "runner";
|
||||||
|
var serviceType = "unsupported-service";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>((s) => s.Contains("Invalid service type")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_runnerServer.Verify(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Runner")]
|
||||||
|
public async Task UpdateRunnerConfigAsync_RunnerAdminService_ShouldThrowNotSupported()
|
||||||
|
{
|
||||||
|
using (var hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
hc.SetSingleton<IConfigurationStore>(_configurationStore.Object);
|
||||||
|
hc.SetSingleton<IRunnerServer>(_runnerServer.Object);
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var setting = new RunnerSettings { AgentId = 1, AgentName = "agent1" };
|
||||||
|
_configurationStore.Setup(x => x.GetSettings()).Returns(setting);
|
||||||
|
IOUtil.SaveObject(setting, hc.GetConfigFile(WellKnownConfigFile.Runner));
|
||||||
|
|
||||||
|
var _runnerConfigUpdater = new RunnerConfigUpdater();
|
||||||
|
_runnerConfigUpdater.Initialize(hc);
|
||||||
|
|
||||||
|
var validRunnerQualifiedId = "valid/runner/qualifiedid/1";
|
||||||
|
var configType = "runner";
|
||||||
|
var serviceType = "runner-admin";
|
||||||
|
var configRefreshUrl = "http://example.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await _runnerConfigUpdater.UpdateRunnerConfigAsync(validRunnerQualifiedId, configType, serviceType, configRefreshUrl);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
_runnerServer.Verify(x => x.UpdateAgentUpdateStateAsync(It.IsAny<int>(), It.IsAny<ulong>(), It.IsAny<string>(), It.Is<string>((s) => s.Contains("Runner admin service is not supported")), It.IsAny<CancellationToken>()), Times.Once);
|
||||||
|
_runnerServer.Verify(x => x.RefreshRunnerConfigAsync(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
|
||||||
|
_configurationStore.Verify(x => x.SaveMigratedSettings(It.IsAny<RunnerSettings>()), Times.Never);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -107,8 +107,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
hc.EnqueueInstance<IProcessInvoker>(p3);
|
hc.EnqueueInstance<IProcessInvoker>(p3);
|
||||||
updater.Initialize(hc);
|
updater.Initialize(hc);
|
||||||
|
|
||||||
_runnerServer.Setup(x => x.UpdateAgentUpdateStateAsync(1, 1, It.IsAny<string>(), It.IsAny<string>()))
|
_runnerServer.Setup(x => x.UpdateAgentUpdateStateAsync(1, 1, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
.Callback((int p, ulong a, string s, string t) =>
|
.Callback((int p, ulong a, string s, string t, CancellationToken token) =>
|
||||||
{
|
{
|
||||||
hc.GetTrace().Info(t);
|
hc.GetTrace().Info(t);
|
||||||
})
|
})
|
||||||
@@ -168,8 +168,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
_runnerServer.Setup(x => x.GetPackageAsync("agent", BuildConstants.RunnerPackage.PackageName, "2.200.0", true, It.IsAny<CancellationToken>()))
|
_runnerServer.Setup(x => x.GetPackageAsync("agent", BuildConstants.RunnerPackage.PackageName, "2.200.0", true, It.IsAny<CancellationToken>()))
|
||||||
.Returns(Task.FromResult(new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.200.0"), DownloadUrl = _packageUrl }));
|
.Returns(Task.FromResult(new PackageMetadata() { Platform = BuildConstants.RunnerPackage.PackageName, Version = new PackageVersion("2.200.0"), DownloadUrl = _packageUrl }));
|
||||||
|
|
||||||
_runnerServer.Setup(x => x.UpdateAgentUpdateStateAsync(1, 1, It.IsAny<string>(), It.IsAny<string>()))
|
_runnerServer.Setup(x => x.UpdateAgentUpdateStateAsync(1, 1, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
.Callback((int p, ulong a, string s, string t) =>
|
.Callback((int p, ulong a, string s, string t, CancellationToken token) =>
|
||||||
{
|
{
|
||||||
hc.GetTrace().Info(t);
|
hc.GetTrace().Info(t);
|
||||||
})
|
})
|
||||||
@@ -220,8 +220,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
hc.EnqueueInstance<IProcessInvoker>(p3);
|
hc.EnqueueInstance<IProcessInvoker>(p3);
|
||||||
updater.Initialize(hc);
|
updater.Initialize(hc);
|
||||||
|
|
||||||
_runnerServer.Setup(x => x.UpdateAgentUpdateStateAsync(1, 1, It.IsAny<string>(), It.IsAny<string>()))
|
_runnerServer.Setup(x => x.UpdateAgentUpdateStateAsync(1, 1, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
.Callback((int p, ulong a, string s, string t) =>
|
.Callback((int p, ulong a, string s, string t, CancellationToken token) =>
|
||||||
{
|
{
|
||||||
hc.GetTrace().Info(t);
|
hc.GetTrace().Info(t);
|
||||||
})
|
})
|
||||||
@@ -273,8 +273,8 @@ namespace GitHub.Runner.Common.Tests.Listener
|
|||||||
hc.EnqueueInstance<IProcessInvoker>(p3);
|
hc.EnqueueInstance<IProcessInvoker>(p3);
|
||||||
updater.Initialize(hc);
|
updater.Initialize(hc);
|
||||||
|
|
||||||
_runnerServer.Setup(x => x.UpdateAgentUpdateStateAsync(1, 1, It.IsAny<string>(), It.IsAny<string>()))
|
_runnerServer.Setup(x => x.UpdateAgentUpdateStateAsync(1, 1, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()))
|
||||||
.Callback((int p, ulong a, string s, string t) =>
|
.Callback((int p, ulong a, string s, string t, CancellationToken token) =>
|
||||||
{
|
{
|
||||||
hc.GetTrace().Info(t);
|
hc.GetTrace().Info(t);
|
||||||
})
|
})
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user