mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
74 Commits
v2.319.0
...
legacy-url
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a755837bc8 | ||
|
|
08b22bff1d | ||
|
|
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 | ||
|
|
36c66c8083 | ||
|
|
99b464e102 | ||
|
|
e1fa1fcbc3 | ||
|
|
2979fbad94 | ||
|
|
a77fe8a53f |
@@ -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
|
|
||||||
70
.github/workflows/release.yml
vendored
70
.github/workflows/release.yml
vendored
@@ -11,12 +11,12 @@ 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: |
|
||||||
@@ -87,7 +87,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 +117,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,19 +130,49 @@ 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: |
|
||||||
@@ -262,16 +291,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 +311,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 +322,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 +338,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,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
|
||||||
@@ -41,12 +41,14 @@ ENV ImageOS=ubuntu22
|
|||||||
|
|
||||||
# 'gpg-agent' and 'software-properties-common' are needed for the 'add-apt-repository' command that follows
|
# 'gpg-agent' and 'software-properties-common' are needed for the 'add-apt-repository' command that follows
|
||||||
RUN apt update -y \
|
RUN apt update -y \
|
||||||
&& apt install -y --no-install-recommends sudo lsb-release gpg-agent software-properties-common \
|
&& apt install -y --no-install-recommends sudo lsb-release gpg-agent software-properties-common curl jq unzip \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# 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 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.
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
1342
src/Misc/dotnet-install.ps1
vendored
1342
src/Misc/dotnet-install.ps1
vendored
File diff suppressed because it is too large
Load Diff
1222
src/Misc/dotnet-install.sh
vendored
1222
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,6 +135,10 @@ 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="node20"
|
||||||
|
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 node16
|
||||||
|
then
|
||||||
nodever="node16"
|
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
|
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node12
|
||||||
@@ -142,6 +146,7 @@ if [[ "$currentplatform" == 'darwin' && restartinteractiverunner -eq 0 ]]; then
|
|||||||
nodever="node12"
|
nodever="node12"
|
||||||
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-)
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
if [[ $? -eq 0 && -n "$path" ]]
|
if [[ $? -eq 0 && -n "$path" ]]
|
||||||
then
|
then
|
||||||
# trim the last 5 characters of the path '/node'
|
# trim the last 5 characters of the path '/node'
|
||||||
@@ -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,8 +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 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))
|
||||||
{
|
{
|
||||||
@@ -244,6 +252,11 @@ namespace GitHub.Runner.Common
|
|||||||
_trace.Info($"Adding extra user agent '{extraUserAgentHeader}' to all HTTP requests.");
|
_trace.Info($"Adding extra user agent '{extraUserAgentHeader}' to all HTTP requests.");
|
||||||
_userAgents.Add(extraUserAgentHeader);
|
_userAgents.Add(extraUserAgentHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var currentProcess = Process.GetCurrentProcess();
|
||||||
|
_userAgents.Add(new ProductInfoHeaderValue("Pid", currentProcess.Id.ToString()));
|
||||||
|
_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)
|
||||||
@@ -330,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),
|
||||||
@@ -616,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,14 +62,23 @@ 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)}";
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/repos/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Enterprise runner
|
||||||
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
||||||
{
|
{
|
||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
||||||
@@ -75,6 +88,7 @@ namespace GitHub.Runner.Common
|
|||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"'{githubUrl}' should point to an org or enterprise.");
|
throw new ArgumentException($"'{githubUrl}' should point to an org or enterprise.");
|
||||||
@@ -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,14 +120,23 @@ 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";
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/repos/{path[0]}/{path[1]}/actions/runner-groups";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Enterprise Runner
|
||||||
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
|
||||||
{
|
{
|
||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runner-groups";
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runner-groups";
|
||||||
@@ -119,6 +146,7 @@ namespace GitHub.Runner.Common
|
|||||||
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runner-groups";
|
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runner-groups";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"'{githubUrl}' should point to an org or enterprise.");
|
throw new ArgumentException($"'{githubUrl}' should point to an org or enterprise.");
|
||||||
|
|||||||
@@ -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))
|
||||||
@@ -69,7 +73,8 @@ namespace GitHub.Runner.Listener
|
|||||||
Version = BuildConstants.RunnerPackage.Version,
|
Version = BuildConstants.RunnerPackage.Version,
|
||||||
OSDescription = RuntimeInformation.OSDescription,
|
OSDescription = RuntimeInformation.OSDescription,
|
||||||
};
|
};
|
||||||
string sessionName = $"{Environment.MachineName ?? "RUNNER"}";
|
var currentProcess = Process.GetCurrentProcess();
|
||||||
|
string sessionName = $"{Environment.MachineName ?? "RUNNER"} (PID: {currentProcess.Id})";
|
||||||
var taskAgentSession = new TaskAgentSession(sessionName, agent);
|
var taskAgentSession = new TaskAgentSession(sessionName, agent);
|
||||||
|
|
||||||
string errorMessage = string.Empty;
|
string errorMessage = string.Empty;
|
||||||
@@ -82,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();
|
||||||
@@ -129,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))
|
||||||
{
|
{
|
||||||
@@ -236,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.");
|
||||||
@@ -323,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}");
|
||||||
|
|||||||
@@ -135,7 +135,8 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
runnerSettings.GitHubUrl = inputUrl;
|
runnerSettings.GitHubUrl = inputUrl;
|
||||||
registerToken = await GetRunnerTokenAsync(command, inputUrl, "registration");
|
registerToken = await GetRunnerTokenAsync(command, inputUrl, "registration");
|
||||||
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
|
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
|
||||||
runnerSettings.ServerUrl = authResult.TenantUrl;
|
var hasLegacyUrl = !string.IsNullOrEmpty(authResult.LegacyUrl);
|
||||||
|
runnerSettings.ServerUrl = hasLegacyUrl ? authResult.LegacyUrl : authResult.TenantUrl;
|
||||||
runnerSettings.UseV2Flow = authResult.UseV2Flow;
|
runnerSettings.UseV2Flow = authResult.UseV2Flow;
|
||||||
Trace.Info($"Using V2 flow: {runnerSettings.UseV2Flow}");
|
Trace.Info($"Using V2 flow: {runnerSettings.UseV2Flow}");
|
||||||
creds = authResult.ToVssCredentials();
|
creds = authResult.ToVssCredentials();
|
||||||
@@ -404,6 +405,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
|
||||||
|
|||||||
@@ -96,14 +96,23 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
[DataMember(Name = "use_v2_flow")]
|
[DataMember(Name = "use_v2_flow")]
|
||||||
public bool UseV2Flow { get; set; }
|
public bool UseV2Flow { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "legacy_url")]
|
||||||
|
public string LegacyUrl { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "legacy_token")]
|
||||||
|
public string LegacyToken { get; set; }
|
||||||
|
|
||||||
public VssCredentials ToVssCredentials()
|
public VssCredentials ToVssCredentials()
|
||||||
{
|
{
|
||||||
ArgUtil.NotNullOrEmpty(TokenSchema, nameof(TokenSchema));
|
ArgUtil.NotNullOrEmpty(TokenSchema, nameof(TokenSchema));
|
||||||
ArgUtil.NotNullOrEmpty(Token, nameof(Token));
|
ArgUtil.NotNullOrEmpty(Token, nameof(Token));
|
||||||
|
|
||||||
|
var hasLegacyToken = !string.IsNullOrEmpty(LegacyToken);
|
||||||
|
var token = hasLegacyToken ? LegacyToken : Token;
|
||||||
|
|
||||||
if (string.Equals(TokenSchema, "OAuthAccessToken", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(TokenSchema, "OAuthAccessToken", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return new VssCredentials(new VssOAuthAccessTokenCredential(Token), CredentialPromptType.DoNotPrompt);
|
return new VssCredentials(new VssOAuthAccessTokenCredential(token), CredentialPromptType.DoNotPrompt);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,6 +545,8 @@ 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.");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
var jobServer = await InitializeJobServerAsync(systemConnection);
|
var jobServer = await InitializeJobServerAsync(systemConnection);
|
||||||
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = detailInfo };
|
var unhandledExceptionIssue = new Issue() { Type = IssueType.Error, Message = detailInfo };
|
||||||
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
unhandledExceptionIssue.Data[Constants.Runner.InternalTelemetryIssueDataKey] = Constants.Runner.WorkerCrash;
|
||||||
@@ -569,6 +571,12 @@ namespace GitHub.Runner.Listener
|
|||||||
throw new NotSupportedException($"JobServer type '{jobServer.GetType().Name}' is not supported.");
|
throw new NotSupportedException($"JobServer type '{jobServer.GetType().Name}' is not supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Trace.Error($"Catch exception during log worker process unhandled exception.");
|
||||||
|
Trace.Error(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TaskResult result = TaskResultUtil.TranslateFromReturnCode(returnCode);
|
TaskResult result = TaskResultUtil.TranslateFromReturnCode(returnCode);
|
||||||
Trace.Info($"finish job request for job {message.JobId} with result: {result}");
|
Trace.Info($"finish job request for job {message.JobId} with result: {result}");
|
||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -88,7 +88,8 @@ namespace GitHub.Runner.Listener
|
|||||||
Version = BuildConstants.RunnerPackage.Version,
|
Version = BuildConstants.RunnerPackage.Version,
|
||||||
OSDescription = RuntimeInformation.OSDescription,
|
OSDescription = RuntimeInformation.OSDescription,
|
||||||
};
|
};
|
||||||
string sessionName = $"{Environment.MachineName ?? "RUNNER"}";
|
var currentProcess = Process.GetCurrentProcess();
|
||||||
|
string sessionName = $"{Environment.MachineName ?? "RUNNER"} (PID: {currentProcess.Id})";
|
||||||
var taskAgentSession = new TaskAgentSession(sessionName, agent);
|
var taskAgentSession = new TaskAgentSession(sessionName, agent);
|
||||||
|
|
||||||
string errorMessage = string.Empty;
|
string errorMessage = string.Empty;
|
||||||
@@ -263,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);
|
||||||
@@ -305,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.");
|
||||||
@@ -458,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}");
|
||||||
@@ -524,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,8 +775,20 @@ 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);
|
||||||
|
|
||||||
|
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})");
|
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
|
||||||
string tempDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), "_temp_" + Guid.NewGuid());
|
string tempDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Actions), "_temp_" + Guid.NewGuid());
|
||||||
@@ -1102,6 +1114,7 @@ namespace GitHub.Runner.Worker
|
|||||||
int timeoutSeconds = 20 * 60;
|
int timeoutSeconds = 20 * 60;
|
||||||
while (retryCount < 3)
|
while (retryCount < 3)
|
||||||
{
|
{
|
||||||
|
string requestId = string.Empty;
|
||||||
using (var actionDownloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
|
using (var actionDownloadTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)))
|
||||||
using (var actionDownloadCancellation = CancellationTokenSource.CreateLinkedTokenSource(actionDownloadTimeout.Token, executionContext.CancellationToken))
|
using (var actionDownloadCancellation = CancellationTokenSource.CreateLinkedTokenSource(actionDownloadTimeout.Token, executionContext.CancellationToken))
|
||||||
{
|
{
|
||||||
@@ -1117,7 +1130,7 @@ namespace GitHub.Runner.Worker
|
|||||||
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
httpClient.DefaultRequestHeaders.UserAgent.AddRange(HostContext.UserAgents);
|
||||||
using (var response = await httpClient.GetAsync(downloadUrl))
|
using (var response = await httpClient.GetAsync(downloadUrl))
|
||||||
{
|
{
|
||||||
var requestId = UrlUtil.GetGitHubRequestId(response.Headers);
|
requestId = UrlUtil.GetGitHubRequestId(response.Headers);
|
||||||
if (!string.IsNullOrEmpty(requestId))
|
if (!string.IsNullOrEmpty(requestId))
|
||||||
{
|
{
|
||||||
Trace.Info($"Request URL: {downloadUrl} X-GitHub-Request-Id: {requestId} Http Status: {response.StatusCode}");
|
Trace.Info($"Request URL: {downloadUrl} X-GitHub-Request-Id: {requestId} Http Status: {response.StatusCode}");
|
||||||
@@ -1155,7 +1168,7 @@ namespace GitHub.Runner.Worker
|
|||||||
catch (OperationCanceledException ex) when (!executionContext.CancellationToken.IsCancellationRequested && retryCount >= 2)
|
catch (OperationCanceledException ex) when (!executionContext.CancellationToken.IsCancellationRequested && retryCount >= 2)
|
||||||
{
|
{
|
||||||
Trace.Info($"Action download final retry timeout after {timeoutSeconds} seconds.");
|
Trace.Info($"Action download final retry timeout after {timeoutSeconds} seconds.");
|
||||||
throw new TimeoutException($"Action '{downloadUrl}' download has timed out. Error: {ex.Message}");
|
throw new TimeoutException($"Action '{downloadUrl}' download has timed out. Error: {ex.Message} {requestId}");
|
||||||
}
|
}
|
||||||
catch (ActionNotFoundException)
|
catch (ActionNotFoundException)
|
||||||
{
|
{
|
||||||
@@ -1170,11 +1183,11 @@ namespace GitHub.Runner.Worker
|
|||||||
if (actionDownloadTimeout.Token.IsCancellationRequested)
|
if (actionDownloadTimeout.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
// action download didn't finish within timeout
|
// action download didn't finish within timeout
|
||||||
executionContext.Warning($"Action '{downloadUrl}' didn't finish download within {timeoutSeconds} seconds.");
|
executionContext.Warning($"Action '{downloadUrl}' didn't finish download within {timeoutSeconds} seconds. {requestId}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
executionContext.Warning($"Failed to download action '{downloadUrl}'. Error: {ex.Message}");
|
executionContext.Warning($"Failed to download action '{downloadUrl}'. Error: {ex.Message} {requestId}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ namespace GitHub.Runner.Worker
|
|||||||
// Initialize
|
// Initialize
|
||||||
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
|
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
|
||||||
void CancelToken();
|
void CancelToken();
|
||||||
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, ActionRunStage stage, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, CancellationTokenSource cancellationTokenSource = null, Guid embeddedId = default(Guid), string siblingScopeName = null, TimeSpan? timeout = null);
|
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, ActionRunStage stage, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, List<Issue> embeddedIssueCollector = null, CancellationTokenSource cancellationTokenSource = null, Guid embeddedId = default(Guid), string siblingScopeName = null, TimeSpan? timeout = null);
|
||||||
IExecutionContext CreateEmbeddedChild(string scopeName, string contextName, Guid embeddedId, ActionRunStage stage, Dictionary<string, string> intraActionState = null, string siblingScopeName = null);
|
IExecutionContext CreateEmbeddedChild(string scopeName, string contextName, Guid embeddedId, ActionRunStage stage, Dictionary<string, string> intraActionState = null, string siblingScopeName = null);
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
@@ -135,7 +135,6 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
private readonly TimelineRecord _record = new();
|
private readonly TimelineRecord _record = new();
|
||||||
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
|
private readonly Dictionary<Guid, TimelineRecord> _detailRecords = new();
|
||||||
private readonly List<Issue> _embeddedIssueCollector;
|
|
||||||
private readonly object _loggerLock = new();
|
private readonly object _loggerLock = new();
|
||||||
private readonly object _matchersLock = new();
|
private readonly object _matchersLock = new();
|
||||||
private readonly ExecutionContext _parentExecutionContext;
|
private readonly ExecutionContext _parentExecutionContext;
|
||||||
@@ -154,6 +153,7 @@ namespace GitHub.Runner.Worker
|
|||||||
private CancellationTokenSource _cancellationTokenSource;
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
private TaskCompletionSource<int> _forceCompleted = new();
|
private TaskCompletionSource<int> _forceCompleted = new();
|
||||||
private bool _throttlingReported = false;
|
private bool _throttlingReported = false;
|
||||||
|
private List<Issue> _embeddedIssueCollector;
|
||||||
|
|
||||||
// only job level ExecutionContext will track throttling delay.
|
// only job level ExecutionContext will track throttling delay.
|
||||||
private long _totalThrottlingDelayInMilliseconds = 0;
|
private long _totalThrottlingDelayInMilliseconds = 0;
|
||||||
@@ -356,6 +356,7 @@ namespace GitHub.Runner.Worker
|
|||||||
int? recordOrder = null,
|
int? recordOrder = null,
|
||||||
IPagingLogger logger = null,
|
IPagingLogger logger = null,
|
||||||
bool isEmbedded = false,
|
bool isEmbedded = false,
|
||||||
|
List<Issue> embeddedIssueCollector = null,
|
||||||
CancellationTokenSource cancellationTokenSource = null,
|
CancellationTokenSource cancellationTokenSource = null,
|
||||||
Guid embeddedId = default(Guid),
|
Guid embeddedId = default(Guid),
|
||||||
string siblingScopeName = null,
|
string siblingScopeName = null,
|
||||||
@@ -365,6 +366,10 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
var child = new ExecutionContext(this, isEmbedded);
|
var child = new ExecutionContext(this, isEmbedded);
|
||||||
child.Initialize(HostContext);
|
child.Initialize(HostContext);
|
||||||
|
if ((Global.Variables.GetBoolean("RunService.FixEmbeddedIssues") ?? false) && embeddedIssueCollector != null)
|
||||||
|
{
|
||||||
|
child._embeddedIssueCollector = embeddedIssueCollector;
|
||||||
|
}
|
||||||
child.Global = Global;
|
child.Global = Global;
|
||||||
child.ScopeName = scopeName;
|
child.ScopeName = scopeName;
|
||||||
child.ContextName = contextName;
|
child.ContextName = contextName;
|
||||||
@@ -433,7 +438,7 @@ namespace GitHub.Runner.Worker
|
|||||||
Dictionary<string, string> intraActionState = null,
|
Dictionary<string, string> intraActionState = null,
|
||||||
string siblingScopeName = null)
|
string siblingScopeName = null)
|
||||||
{
|
{
|
||||||
return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, stage, logger: _logger, isEmbedded: true, cancellationTokenSource: null, intraActionState: intraActionState, embeddedId: embeddedId, siblingScopeName: siblingScopeName, timeout: GetRemainingTimeout(), recordOrder: _record.Order);
|
return Root.CreateChild(_record.Id, _record.Name, _record.Id.ToString("N"), scopeName, contextName, stage, logger: _logger, isEmbedded: true, embeddedIssueCollector: _embeddedIssueCollector, cancellationTokenSource: null, intraActionState: intraActionState, embeddedId: embeddedId, siblingScopeName: siblingScopeName, timeout: GetRemainingTimeout(), recordOrder: _record.Order);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Start(string currentOperation = null)
|
public void Start(string currentOperation = null)
|
||||||
@@ -503,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>()
|
||||||
@@ -520,7 +528,6 @@ namespace GitHub.Runner.Worker
|
|||||||
Global.StepsResult.Add(stepResult);
|
Global.StepsResult.Add(stepResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (Root != this)
|
if (Root != this)
|
||||||
{
|
{
|
||||||
// only dispose TokenSource for step level ExecutionContext
|
// only dispose TokenSource for step level ExecutionContext
|
||||||
@@ -808,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);
|
||||||
|
|
||||||
@@ -837,7 +839,6 @@ namespace GitHub.Runner.Worker
|
|||||||
// Actions environment
|
// Actions environment
|
||||||
ActionsEnvironment = message.ActionsEnvironment;
|
ActionsEnvironment = message.ActionsEnvironment;
|
||||||
|
|
||||||
|
|
||||||
// Service container info
|
// Service container info
|
||||||
Global.ServiceContainers = new List<ContainerInfo>();
|
Global.ServiceContainers = new List<ContainerInfo>();
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
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";
|
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,96 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
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
|
|
||||||
{
|
|
||||||
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 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(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(output)}'" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetShortOutput(List<string> output)
|
|
||||||
{
|
|
||||||
var outputStr = string.Join("\n", output).Trim();
|
|
||||||
return outputStr.Length > 200 ? string.Concat(outputStr.Substring(0, 200), "[...]") : 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,12 +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_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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,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);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ namespace GitHub.Runner.Common.Tests
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
#if OS_WINDOWS
|
#if OS_WINDOWS
|
||||||
string node = Path.Combine(TestUtil.GetSrcPath(), @"..\_layout\externals\node16\bin\node");
|
string node = Path.Combine(TestUtil.GetSrcPath(), @"..\_layout\externals\node20\bin\node");
|
||||||
#else
|
#else
|
||||||
string node = Path.Combine(TestUtil.GetSrcPath(), @"../_layout/externals/node16/bin/node");
|
string node = Path.Combine(TestUtil.GetSrcPath(), @"../_layout/externals/node20/bin/node");
|
||||||
hc.EnqueueInstance<IProcessInvoker>(new ProcessInvokerWrapper());
|
hc.EnqueueInstance<IProcessInvoker>(new ProcessInvokerWrapper());
|
||||||
#endif
|
#endif
|
||||||
var startInfo = new ProcessStartInfo(node, "-e \"setTimeout(function(){{}}, 15 * 1000);\"");
|
var startInfo = new ProcessStartInfo(node, "-e \"setTimeout(function(){{}}, 15 * 1000);\"");
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user