Compare commits

..

1 Commits

Author SHA1 Message Date
Aiqiao Yan
71ed7cd462 Update releaseVersion 2025-06-02 14:42:04 -04:00
55 changed files with 440 additions and 1982 deletions

View File

@@ -4,7 +4,7 @@
"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": "8.0.413" "version": "8.0.410"
}, },
"ghcr.io/devcontainers/features/node:1": { "ghcr.io/devcontainers/features/node:1": {
"version": "20" "version": "20"

View File

@@ -41,7 +41,7 @@ jobs:
devScript: ./dev.sh devScript: ./dev.sh
- runtime: win-x64 - runtime: win-x64
os: windows-latest os: windows-2019
devScript: ./dev devScript: ./dev
- runtime: win-arm64 - runtime: win-arm64
@@ -50,7 +50,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
# Build runner layout # Build runner layout
- name: Build & Layout Release - name: Build & Layout Release

View File

@@ -23,7 +23,7 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL

View File

@@ -17,7 +17,7 @@ jobs:
BUILDX_CURRENT_VERSION: ${{ steps.check_buildx_version.outputs.CURRENT_VERSION }} BUILDX_CURRENT_VERSION: ${{ steps.check_buildx_version.outputs.CURRENT_VERSION }}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Check Docker version - name: Check Docker version
id: check_docker_version id: check_docker_version
@@ -89,7 +89,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Update Docker version - name: Update Docker version
shell: bash shell: bash

View File

@@ -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@v5 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
@@ -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@v5 - 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

View File

@@ -11,7 +11,7 @@ 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@v5 - 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
@@ -77,7 +77,7 @@ jobs:
devScript: ./dev.sh devScript: ./dev.sh
- runtime: win-x64 - runtime: win-x64
os: windows-latest os: windows-2019
devScript: ./dev devScript: ./dev
- runtime: win-arm64 - runtime: win-arm64
@@ -86,7 +86,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
# Build runner layout # Build runner layout
- name: Build & Layout Release - name: Build & Layout Release
@@ -129,41 +129,41 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - 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 (win-x64) - name: Download Artifact (win-x64)
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
name: runner-packages-win-x64 name: runner-packages-win-x64
path: ./ path: ./
- name: Download Artifact (win-arm64) - name: Download Artifact (win-arm64)
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
name: runner-packages-win-arm64 name: runner-packages-win-arm64
path: ./ path: ./
- name: Download Artifact (osx-x64) - name: Download Artifact (osx-x64)
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
name: runner-packages-osx-x64 name: runner-packages-osx-x64
path: ./ path: ./
- name: Download Artifact (osx-arm64) - name: Download Artifact (osx-arm64)
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
name: runner-packages-osx-arm64 name: runner-packages-osx-arm64
path: ./ path: ./
- name: Download Artifact (linux-x64) - name: Download Artifact (linux-x64)
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
name: runner-packages-linux-x64 name: runner-packages-linux-x64
path: ./ path: ./
- name: Download Artifact (linux-arm) - name: Download Artifact (linux-arm)
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
name: runner-packages-linux-arm name: runner-packages-linux-arm
path: ./ path: ./
- name: Download Artifact (linux-arm64) - name: Download Artifact (linux-arm64)
uses: actions/download-artifact@v5 uses: actions/download-artifact@v4
with: with:
name: runner-packages-linux-arm64 name: runner-packages-linux-arm64
path: ./ path: ./
@@ -296,7 +296,7 @@ jobs:
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@v5 uses: actions/checkout@v4
- name: Compute image version - name: Compute image version
id: image id: image
@@ -338,7 +338,7 @@ jobs:
org.opencontainers.image.licenses=MIT org.opencontainers.image.licenses=MIT
- name: Generate attestation - name: Generate attestation
uses: actions/attest-build-provenance@v3 uses: actions/attest-build-provenance@v2
with: with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build-and-push.outputs.digest }} subject-digest: ${{ steps.build-and-push.outputs.digest }}

View File

@@ -20,20 +20,6 @@ Runner releases:
![linux](docs/res/linux_sm.png) [Pre-reqs](docs/start/envlinux.md) | [Download](https://github.com/actions/runner/releases) ![linux](docs/res/linux_sm.png) [Pre-reqs](docs/start/envlinux.md) | [Download](https://github.com/actions/runner/releases)
### Note ## Contribute
Thank you for your interest in this GitHub repo, however, right now we are not taking contributions. We accept contributions in the form of issues and pull requests. The runner typically requires changes across the entire system and we aim for issues in the runner to be entirely self contained and fixable here. Therefore, we will primarily handle bug issues opened in this repo and we kindly request you to create all feature and enhancement requests on the [GitHub Feedback](https://github.com/community/community/discussions/categories/actions-and-packages) page. [Read more about our guidelines here](docs/contribute.md) before contributing.
We continue to focus our resources on strategic areas that help our customers be successful while making developers' lives easier. While GitHub Actions remains a key part of this vision, we are allocating resources towards other areas of Actions and are not taking contributions to this repository at this time. The GitHub public roadmap is the best place to follow along for any updates on features were working on and what stage theyre in.
We are taking the following steps to better direct requests related to GitHub Actions, including:
1. We will be directing questions and support requests to our [Community Discussions area](https://github.com/orgs/community/discussions/categories/actions)
2. High Priority bugs can be reported through Community Discussions or you can report these to our support team https://support.github.com/contact/bug-report.
3. Security Issues should be handled as per our [security.md](security.md)
We will still provide security updates for this project and fix major breaking changes during this time.
You are welcome to still raise bugs in this repo.

View File

@@ -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 copies of node.js executables under `<runner_root>/externals/node20/` and `<runner_root>/externals/node24/`. 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 either `<runner_root>/externals/node20/` or `<runner_root>/externals/node24/` depending on the version specified in the action's metadata. 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`

View File

@@ -4,7 +4,7 @@
## Supported Distributions and Versions ## Supported Distributions and Versions
Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/reference/runners/self-hosted-runners#linux)." Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#linux)."
## Install .Net Core 3.x Linux Dependencies ## Install .Net Core 3.x Linux Dependencies

View File

@@ -4,6 +4,6 @@
## Supported Versions ## Supported Versions
Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/reference/runners/self-hosted-runners#macos)." Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#macos)."
## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore30) ## [More .Net Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/macos-prerequisites?tabs=netcore30)

View File

@@ -2,6 +2,6 @@
## Supported Versions ## Supported Versions
Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/reference/runners/self-hosted-runners#windows)." Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#windows)."
## [More .NET Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore30) ## [More .NET Core Prerequisites Information](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore30)

View File

@@ -5,8 +5,8 @@ ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
ARG RUNNER_VERSION ARG RUNNER_VERSION
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.7.0 ARG RUNNER_CONTAINER_HOOKS_VERSION=0.7.0
ARG DOCKER_VERSION=28.3.3 ARG DOCKER_VERSION=28.2.1
ARG BUILDX_VERSION=0.27.0 ARG BUILDX_VERSION=0.24.0
RUN apt update -y && apt install curl unzip -y RUN apt update -y && apt install curl unzip -y

View File

@@ -1,20 +1,13 @@
## What's Changed ## What's Changed
* Update Docker to v28.3.2 and Buildx to v0.26.1 by @github-actions[bot] in https://github.com/actions/runner/pull/3953 * Create schedule workflow to upgrade docker and buildx version. by @TingluoHuang in https://github.com/actions/runner/pull/3859
* Fix if statement structure in update script and variable reference by @salmanmkc in https://github.com/actions/runner/pull/3956 * Update dotnet sdk to latest version @8.0.409 by @github-actions in https://github.com/actions/runner/pull/3860
* Add V2 flow for runner deletion by @Samirat in https://github.com/actions/runner/pull/3954 * Allow runner to use authv2 during config. by @TingluoHuang in https://github.com/actions/runner/pull/3866
* Node 20 -> Node 24 migration feature flagging, opt-in and opt-out environment variables by @salmanmkc in https://github.com/actions/runner/pull/3948 * show helpful error message when resolving actions directly with launch by @aiqiaoy in https://github.com/actions/runner/pull/3874
* Update Node20 and Node24 to latest by @djs-intel in https://github.com/actions/runner/pull/3972 * Update dotnet sdk to latest version @8.0.410 by @github-actions in https://github.com/actions/runner/pull/3871
* Redirect supported OS doc section to current public Docs location by @corycalahan in https://github.com/actions/runner/pull/3979 * Update Docker to v28.2.1 and Buildx to v0.24.0 by @github-actions in https://github.com/actions/runner/pull/3881
* Bump Microsoft.NET.Test.Sdk from 17.13.0 to 17.14.1 by @dependabot[bot] in https://github.com/actions/runner/pull/3975 * Allow NO_SSL_VERIFY in RawHttpMessageHandler. by @TingluoHuang in https://github.com/actions/runner/pull/3883
* Bump Azure.Storage.Blobs from 12.24.0 to 12.25.0 by @dependabot[bot] in https://github.com/actions/runner/pull/3974
* Bump actions/download-artifact from 4 to 5 by @dependabot[bot] in https://github.com/actions/runner/pull/3973
* Bump actions/checkout from 4 to 5 by @dependabot[bot] in https://github.com/actions/runner/pull/3982
## New Contributors **Full Changelog**: https://github.com/actions/runner/compare/v2.324.0...v2.325.0
* @Samirat made their first contribution in https://github.com/actions/runner/pull/3954
* @djs-intel made their first contribution in https://github.com/actions/runner/pull/3972
**Full Changelog**: https://github.com/actions/runner/compare/v2.327.1...v2.328.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.

View File

@@ -1 +1 @@
<Update to ./src/runnerversion when creating release> 2.325.0

File diff suppressed because it is too large Load Diff

View File

@@ -36,13 +36,13 @@
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.6.2", "@types/node": "^20.6.2",
"@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/eslint-plugin": "^6.7.2",
"@typescript-eslint/parser": "^6.7.2", "@typescript-eslint/parser": "^6.7.2",
"@vercel/ncc": "^0.38.3", "@vercel/ncc": "^0.38.0",
"eslint": "^8.47.0", "eslint": "^8.47.0",
"eslint-plugin-github": "^4.10.2", "eslint-plugin-github": "^4.10.0",
"eslint-plugin-prettier": "^5.0.0", "eslint-plugin-prettier": "^5.0.0",
"husky": "^9.1.7", "husky": "^8.0.3",
"lint-staged": "^15.5.0", "lint-staged": "^15.5.0",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"typescript": "^5.2.2" "typescript": "^5.2.2"

View File

@@ -6,8 +6,7 @@ NODE_URL=https://nodejs.org/dist
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
NODE20_VERSION="20.19.4" NODE20_VERSION="20.19.1"
NODE24_VERSION="24.5.0"
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 +139,6 @@ function acquireExternalTool() {
if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then
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
acquireExternalTool "$NODE_URL/v${NODE24_VERSION}/$PACKAGERUNTIME/node.exe" node24/bin
acquireExternalTool "$NODE_URL/v${NODE24_VERSION}/$PACKAGERUNTIME/node.lib" node24/bin
if [[ "$PRECACHE" != "" ]]; then if [[ "$PRECACHE" != "" ]]; then
acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere
fi fi
@@ -152,8 +149,6 @@ if [[ "$PACKAGERUNTIME" == "win-arm64" ]]; then
# todo: replace these with official release when available # todo: replace these with official release when available
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
acquireExternalTool "$NODE_URL/v${NODE24_VERSION}/$PACKAGERUNTIME/node.exe" node24/bin
acquireExternalTool "$NODE_URL/v${NODE24_VERSION}/$PACKAGERUNTIME/node.lib" node24/bin
if [[ "$PRECACHE" != "" ]]; then if [[ "$PRECACHE" != "" ]]; then
acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere
fi fi
@@ -162,26 +157,21 @@ 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${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
acquireExternalTool "$NODE_URL/v${NODE24_VERSION}/node-v${NODE24_VERSION}-darwin-x64.tar.gz" node24 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${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
acquireExternalTool "$NODE_URL/v${NODE24_VERSION}/node-v${NODE24_VERSION}-darwin-arm64.tar.gz" node24 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${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
acquireExternalTool "$NODE_URL/v${NODE24_VERSION}/node-v${NODE24_VERSION}-linux-x64.tar.gz" node24 fix_nested_dir
acquireExternalTool "$NODE_ALPINE_URL/v${NODE24_VERSION}/node-v${NODE24_VERSION}-alpine-x64.tar.gz" node24_alpine
fi fi
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
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
acquireExternalTool "$NODE_URL/v${NODE24_VERSION}/node-v${NODE24_VERSION}-linux-arm64.tar.gz" node24 fix_nested_dir
fi fi
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then

View File

@@ -3299,7 +3299,7 @@ function expand(str, isTop) {
var isOptions = m.body.indexOf(',') >= 0; var isOptions = m.body.indexOf(',') >= 0;
if (!isSequence && !isOptions) { if (!isSequence && !isOptions) {
// {a},b} // {a},b}
if (m.post.match(/,(?!,).*\}/)) { if (m.post.match(/,.*\}/)) {
str = m.pre + '{' + m.body + escClose + m.post; str = m.pre + '{' + m.body + escClose + m.post;
return expand(str); return expand(str);
} }

View File

@@ -131,32 +131,7 @@ then
command -v dnf command -v dnf
if [ $? -eq 0 ] if [ $? -eq 0 ]
then then
dnf_with_fallbacks() { dnf install -y lttng-ust openssl-libs krb5-libs zlib libicu
dnf install -y $1
fail=$?
if [ $fail -eq 0 ]
then
return 0
fi
if [ $fail -ne 0 ]
then
shift
if [ -n "$1" ]
then
dnf_with_fallbacks "$@"
fi
fi
}
dnf_with_fallbacks lttng-ust1 lttng-ust0
if [ $? -ne 0 ]
then
echo "'dnf' failed with exit code '$?'"
print_errormessage
exit 1
fi
dnf install -y openssl-libs krb5-libs zlib libicu
if [ $? -ne 0 ] if [ $? -ne 0 ]
then then
echo "'dnf' failed with exit code '$?'" echo "'dnf' failed with exit code '$?'"

View File

@@ -123,7 +123,7 @@ fi
# fix upgrade issue with macOS when running as a service # fix upgrade issue with macOS when running as a service
attemptedtargetedfix=0 attemptedtargetedfix=0
currentplatform=$(uname | awk '{print tolower($0)}') currentplatform=$(uname | awk '{print tolower($0)}')
if [[ "$currentplatform" == 'darwin' && $restartinteractiverunner -eq 0 ]]; then if [[ "$currentplatform" == 'darwin' && restartinteractiverunner -eq 0 ]]; then
# We needed a fix for https://github.com/actions/runner/issues/743 # We needed a fix for https://github.com/actions/runner/issues/743
# We will recreate the ./externals/nodeXY/bin/node of the past runner version that launched the runnerlistener service # We will recreate the ./externals/nodeXY/bin/node of the past runner version that launched the runnerlistener service
# Otherwise mac gatekeeper kills the processes we spawn on creation as we are running a process with no backing file # Otherwise mac gatekeeper kills the processes we spawn on creation as we are running a process with no backing file
@@ -135,22 +135,16 @@ if [[ "$currentplatform" == 'darwin' && $restartinteractiverunner -eq 0 ]]; the
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
# Try finding node24 first, then fallback to earlier versions if needed nodever="node20"
nodever="node24"
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 node20 if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node16
then then
nodever="node20" 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 node16 if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node12
then then
nodever="node16" 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-)
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node12
then
nodever="node12"
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
fi
fi fi
fi fi
if [[ $? -eq 0 && -n "$path" ]] if [[ $? -eq 0 && -n "$path" ]]

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SECONDS=0 SECONDS=0
while [[ $SECONDS -lt $1 ]]; do while [[ $SECONDS != $1 ]]; do
: :
done done

View File

@@ -23,8 +23,6 @@ namespace GitHub.Runner.Common
Task<TaskAgentMessage> GetRunnerMessageAsync(Guid? sessionId, TaskAgentStatus status, string version, string os, string architecture, bool disableUpdate, CancellationToken token); Task<TaskAgentMessage> GetRunnerMessageAsync(Guid? sessionId, TaskAgentStatus status, string version, string os, string architecture, bool disableUpdate, CancellationToken token);
Task AcknowledgeRunnerRequestAsync(string runnerRequestId, Guid? sessionId, TaskAgentStatus status, string version, string os, string architecture, CancellationToken token);
Task UpdateConnectionIfNeeded(Uri serverUri, VssCredentials credentials); Task UpdateConnectionIfNeeded(Uri serverUri, VssCredentials credentials);
Task ForceRefreshConnection(VssCredentials credentials); Task ForceRefreshConnection(VssCredentials credentials);
@@ -69,17 +67,10 @@ namespace GitHub.Runner.Common
var brokerSession = RetryRequest<TaskAgentMessage>( var brokerSession = RetryRequest<TaskAgentMessage>(
async () => await _brokerHttpClient.GetRunnerMessageAsync(sessionId, version, status, os, architecture, disableUpdate, cancellationToken), cancellationToken, shouldRetry: ShouldRetryException); async () => await _brokerHttpClient.GetRunnerMessageAsync(sessionId, version, status, os, architecture, disableUpdate, cancellationToken), cancellationToken, shouldRetry: ShouldRetryException);
return brokerSession; return brokerSession;
} }
public async Task AcknowledgeRunnerRequestAsync(string runnerRequestId, Guid? sessionId, TaskAgentStatus status, string version, string os, string architecture, CancellationToken cancellationToken)
{
CheckConnection();
// No retries
await _brokerHttpClient.AcknowledgeRunnerRequestAsync(runnerRequestId, sessionId, version, status, os, architecture, cancellationToken);
}
public async Task DeleteSessionAsync(CancellationToken cancellationToken) public async Task DeleteSessionAsync(CancellationToken cancellationToken)
{ {
CheckConnection(); CheckConnection();

View File

@@ -170,22 +170,6 @@ namespace GitHub.Runner.Common
public static readonly string AddCheckRunIdToJobContext = "actions_add_check_run_id_to_job_context"; public static readonly string AddCheckRunIdToJobContext = "actions_add_check_run_id_to_job_context";
public static readonly string DisplayHelpfulActionsDownloadErrors = "actions_display_helpful_actions_download_errors"; public static readonly string DisplayHelpfulActionsDownloadErrors = "actions_display_helpful_actions_download_errors";
} }
// Node version migration related constants
public static class NodeMigration
{
// Node versions
public static readonly string Node20 = "node20";
public static readonly string Node24 = "node24";
// Environment variables for controlling node version selection
public static readonly string ForceNode24Variable = "FORCE_JAVASCRIPT_ACTIONS_TO_NODE24";
public static readonly string AllowUnsecureNodeVersionVariable = "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION";
// Feature flags for controlling the migration phases
public static readonly string UseNode24ByDefaultFlag = "actions.runner.usenode24bydefault";
public static readonly string RequireNode24Flag = "actions.runner.requirenode24";
}
public static readonly string InternalTelemetryIssueDataKey = "_internal_telemetry"; public static readonly string InternalTelemetryIssueDataKey = "_internal_telemetry";
public static readonly Guid TelemetryRecordId = new Guid("11111111-1111-1111-1111-111111111111"); public static readonly Guid TelemetryRecordId = new Guid("11111111-1111-1111-1111-111111111111");

View File

@@ -15,7 +15,6 @@ using System.Threading.Tasks;
using GitHub.DistributedTask.Logging; using GitHub.DistributedTask.Logging;
using GitHub.Runner.Common.Util; using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using GitHub.Services.WebApi.Jwt;
namespace GitHub.Runner.Common namespace GitHub.Runner.Common
{ {
@@ -307,36 +306,6 @@ namespace GitHub.Runner.Common
{ {
_userAgents.Add(new ProductInfoHeaderValue("ClientId", clientId)); _userAgents.Add(new ProductInfoHeaderValue("ClientId", clientId));
} }
// for Hosted runner, we can pull orchestrationId from JWT claims of the runner listening token.
if (credData != null &&
credData.Scheme == Constants.Configuration.OAuthAccessToken &&
credData.Data.TryGetValue(Constants.Runner.CommandLine.Args.Token, out var accessToken) &&
!string.IsNullOrEmpty(accessToken))
{
try
{
var jwt = JsonWebToken.Create(accessToken);
var claims = jwt.ExtractClaims();
var orchestrationId = claims.FirstOrDefault(x => string.Equals(x.Type, "orch_id", StringComparison.OrdinalIgnoreCase))?.Value;
if (string.IsNullOrEmpty(orchestrationId))
{
// fallback to orchid for C# actions-service
orchestrationId = claims.FirstOrDefault(x => string.Equals(x.Type, "orchid", StringComparison.OrdinalIgnoreCase))?.Value;
}
if (!string.IsNullOrEmpty(orchestrationId))
{
_trace.Info($"Pull OrchestrationId {orchestrationId} from runner JWT claims");
_userAgents.Insert(0, new ProductInfoHeaderValue("OrchestrationId", orchestrationId));
}
}
catch (Exception ex)
{
_trace.Error("Fail to extract OrchestrationId from runner JWT claims");
_trace.Error(ex);
}
}
} }
var runnerFile = GetConfigFile(WellKnownConfigFile.Runner); var runnerFile = GetConfigFile(WellKnownConfigFile.Runner);

View File

@@ -19,7 +19,6 @@ namespace GitHub.Runner.Common
Task<DistributedTask.WebApi.Runner> AddRunnerAsync(int runnerGroupId, TaskAgent agent, string githubUrl, string githubToken, string publicKey); Task<DistributedTask.WebApi.Runner> AddRunnerAsync(int runnerGroupId, TaskAgent agent, string githubUrl, string githubToken, string publicKey);
Task<DistributedTask.WebApi.Runner> ReplaceRunnerAsync(int runnerGroupId, TaskAgent agent, string githubUrl, string githubToken, string publicKey); Task<DistributedTask.WebApi.Runner> ReplaceRunnerAsync(int runnerGroupId, TaskAgent agent, string githubUrl, string githubToken, string publicKey);
Task DeleteRunnerAsync(string githubUrl, string githubToken, ulong runnerId);
Task<List<TaskAgentPool>> GetRunnerGroupsAsync(string githubUrl, string githubToken); Task<List<TaskAgentPool>> GetRunnerGroupsAsync(string githubUrl, string githubToken);
} }
@@ -44,15 +43,117 @@ namespace GitHub.Runner.Common
public async Task<List<TaskAgent>> GetRunnerByNameAsync(string githubUrl, string githubToken, string agentName) public async Task<List<TaskAgent>> GetRunnerByNameAsync(string githubUrl, string githubToken, string agentName)
{ {
var githubApiUrl = $"{GetEntityUrl(githubUrl)}/runners?name={Uri.EscapeDataString(agentName)}"; var githubApiUrl = "";
var gitHubUrlBuilder = new UriBuilder(githubUrl);
var path = gitHubUrlBuilder.Path.Split('/', '\\', StringSplitOptions.RemoveEmptyEntries);
var isOrgRunner = path.Length == 1;
var isRepoOrEnterpriseRunner = path.Length == 2;
var isRepoRunner = isRepoOrEnterpriseRunner && !string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase);
if (isOrgRunner)
{
// org runner
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/orgs/{path[0]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
}
else
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
}
}
else if (isRepoOrEnterpriseRunner)
{
// Repository runner
if (isRepoRunner)
{
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))
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
}
else
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runners?name={Uri.EscapeDataString(agentName)}";
}
}
}
else
{
throw new ArgumentException($"'{githubUrl}' should point to an org or enterprise.");
}
var runnersList = await RetryRequest<ListRunnersResponse>(githubApiUrl, githubToken, RequestType.Get, 3, "Failed to get agents pools"); var runnersList = await RetryRequest<ListRunnersResponse>(githubApiUrl, githubToken, RequestType.Get, 3, "Failed to get agents pools");
return runnersList.ToTaskAgents(); return runnersList.ToTaskAgents();
} }
public async Task<List<TaskAgentPool>> GetRunnerGroupsAsync(string githubUrl, string githubToken) public async Task<List<TaskAgentPool>> GetRunnerGroupsAsync(string githubUrl, string githubToken)
{ {
var githubApiUrl = $"{GetEntityUrl(githubUrl)}/runner-groups"; var githubApiUrl = "";
var gitHubUrlBuilder = new UriBuilder(githubUrl);
var path = gitHubUrlBuilder.Path.Split('/', '\\', StringSplitOptions.RemoveEmptyEntries);
var isOrgRunner = path.Length == 1;
var isRepoOrEnterpriseRunner = path.Length == 2;
var isRepoRunner = isRepoOrEnterpriseRunner && !string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase);
if (isOrgRunner)
{
// org runner
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/orgs/{path[0]}/actions/runner-groups";
}
else
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions/runner-groups";
}
}
else if (isRepoOrEnterpriseRunner)
{
// Repository Runner
if (isRepoRunner)
{
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))
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions/runner-groups";
}
else
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions/runner-groups";
}
}
}
else
{
throw new ArgumentException($"'{githubUrl}' should point to an org or enterprise.");
}
var agentPools = await RetryRequest<RunnerGroupList>(githubApiUrl, githubToken, RequestType.Get, 3, "Failed to get agents pools"); var agentPools = await RetryRequest<RunnerGroupList>(githubApiUrl, githubToken, RequestType.Get, 3, "Failed to get agents pools");
return agentPools?.ToAgentPoolList(); return agentPools?.ToAgentPoolList();
} }
@@ -103,12 +204,6 @@ namespace GitHub.Runner.Common
return await RetryRequest<DistributedTask.WebApi.Runner>(githubApiUrl, githubToken, RequestType.Post, 3, "Failed to add agent", body); return await RetryRequest<DistributedTask.WebApi.Runner>(githubApiUrl, githubToken, RequestType.Post, 3, "Failed to add agent", body);
} }
public async Task DeleteRunnerAsync(string githubUrl, string githubToken, ulong runnerId)
{
var githubApiUrl = $"{GetEntityUrl(githubUrl)}/runners/{runnerId}";
await RetryRequest<DistributedTask.WebApi.Runner>(githubApiUrl, githubToken, RequestType.Delete, 3, "Failed to delete agent");
}
private async Task<T> RetryRequest<T>(string githubApiUrl, string githubToken, RequestType requestType, int maxRetryAttemptsCount = 5, string errorMessage = null, StringContent body = null) private async Task<T> RetryRequest<T>(string githubApiUrl, string githubToken, RequestType requestType, int maxRetryAttemptsCount = 5, string errorMessage = null, StringContent body = null)
{ {
int retry = 0; int retry = 0;
@@ -125,22 +220,13 @@ namespace GitHub.Runner.Common
try try
{ {
HttpResponseMessage response = null; HttpResponseMessage response = null;
switch (requestType) if (requestType == RequestType.Get)
{ {
case RequestType.Get: response = await httpClient.GetAsync(githubApiUrl);
response = await httpClient.GetAsync(githubApiUrl); }
break; else
case RequestType.Post: {
response = await httpClient.PostAsync(githubApiUrl, body); response = await httpClient.PostAsync(githubApiUrl, body);
break;
case RequestType.Patch:
response = await httpClient.PatchAsync(githubApiUrl, body);
break;
case RequestType.Delete:
response = await httpClient.DeleteAsync(githubApiUrl);
break;
default:
throw new ArgumentOutOfRangeException(nameof(requestType), requestType, null);
} }
if (response != null) if (response != null)
@@ -175,61 +261,5 @@ namespace GitHub.Runner.Common
await Task.Delay(backOff); await Task.Delay(backOff);
} }
} }
private string GetEntityUrl(string githubUrl)
{
var githubApiUrl = "";
var gitHubUrlBuilder = new UriBuilder(githubUrl);
var path = gitHubUrlBuilder.Path.Split('/', '\\', StringSplitOptions.RemoveEmptyEntries);
var isOrgRunner = path.Length == 1;
var isRepoOrEnterpriseRunner = path.Length == 2;
var isRepoRunner = isRepoOrEnterpriseRunner && !string.Equals(path[0], "enterprises", StringComparison.OrdinalIgnoreCase);
if (isOrgRunner)
{
// org runner
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/orgs/{path[0]}/actions";
}
else
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/orgs/{path[0]}/actions";
}
}
else if (isRepoOrEnterpriseRunner)
{
// Repository Runner
if (isRepoRunner)
{
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/repos/{path[0]}/{path[1]}/actions";
}
else
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/repos/{path[0]}/{path[1]}/actions";
}
}
else
{
// Enterprise Runner
if (UrlUtil.IsHostedServer(gitHubUrlBuilder))
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://api.{gitHubUrlBuilder.Host}/{path[0]}/{path[1]}/actions";
}
else
{
githubApiUrl = $"{gitHubUrlBuilder.Scheme}://{gitHubUrlBuilder.Host}/api/v3/{path[0]}/{path[1]}/actions";
}
}
}
else
{
throw new ArgumentException($"'{githubUrl}' should point to an org or enterprise.");
}
return githubApiUrl;
}
} }
} }

View File

@@ -70,7 +70,7 @@ namespace GitHub.Runner.Common
protected async Task RetryRequest(Func<Task> func, protected async Task RetryRequest(Func<Task> func,
CancellationToken cancellationToken, CancellationToken cancellationToken,
int maxAttempts = 5, int maxRetryAttemptsCount = 5,
Func<Exception, bool> shouldRetry = null Func<Exception, bool> shouldRetry = null
) )
{ {
@@ -79,31 +79,31 @@ namespace GitHub.Runner.Common
await func(); await func();
return Unit.Value; return Unit.Value;
} }
await RetryRequest<Unit>(wrappedFunc, cancellationToken, maxAttempts, shouldRetry); 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,
CancellationToken cancellationToken, CancellationToken cancellationToken,
int maxAttempts = 5, int maxRetryAttemptsCount = 5,
Func<Exception, bool> shouldRetry = null Func<Exception, bool> shouldRetry = null
) )
{ {
var attempt = 0; var retryCount = 0;
while (true) while (true)
{ {
attempt++; retryCount++;
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
try try
{ {
return await func(); return await func();
} }
// TODO: Add handling of non-retriable exceptions: https://github.com/github/actions-broker/issues/122 // TODO: Add handling of non-retriable exceptions: https://github.com/github/actions-broker/issues/122
catch (Exception ex) when (attempt < maxAttempts && (shouldRetry == null || shouldRetry(ex))) catch (Exception ex) when (retryCount < maxRetryAttemptsCount && (shouldRetry == null || shouldRetry(ex)))
{ {
Trace.Error("Catch exception during request"); Trace.Error("Catch exception during request");
Trace.Error(ex); Trace.Error(ex);
var backOff = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(15)); var backOff = BackoffTimerHelper.GetRandomBackoff(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(15));
Trace.Warning($"Back off {backOff.TotalSeconds} seconds before next retry. {maxAttempts - attempt} attempt left."); Trace.Warning($"Back off {backOff.TotalSeconds} seconds before next retry. {maxRetryAttemptsCount - retryCount} attempt left.");
await Task.Delay(backOff, cancellationToken); await Task.Delay(backOff, cancellationToken);
} }
} }

View File

@@ -1,33 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using GitHub.Runner.Sdk;
namespace GitHub.Runner.Common.Util namespace GitHub.Runner.Common.Util
{ {
public static class NodeUtil public static class NodeUtil
{ {
/// <summary>
/// Represents details about an environment variable, including its value and source
/// </summary>
private class EnvironmentVariableInfo
{
/// <summary>
/// Gets or sets whether the value evaluates to true
/// </summary>
public bool IsTrue { get; set; }
/// <summary>
/// Gets or sets whether the value came from the workflow environment
/// </summary>
public bool FromWorkflow { get; set; }
/// <summary>
/// Gets or sets whether the value came from the system environment
/// </summary>
public bool FromSystem { get; set; }
}
private const string _defaultNodeVersion = "node20"; private const string _defaultNodeVersion = "node20";
public static readonly ReadOnlyCollection<string> BuiltInNodeVersions = new(new[] { "node20" }); public static readonly ReadOnlyCollection<string> BuiltInNodeVersions = new(new[] { "node20" });
public static string GetInternalNodeVersion() public static string GetInternalNodeVersion()
@@ -41,122 +18,5 @@ namespace GitHub.Runner.Common.Util
} }
return _defaultNodeVersion; return _defaultNodeVersion;
} }
/// <summary>
/// Determines the appropriate Node version for Actions to use
/// </summary>
/// <param name="workflowEnvironment">Optional dictionary containing workflow-level environment variables</param>
/// <param name="useNode24ByDefault">Feature flag indicating if Node 24 should be the default</param>
/// <param name="requireNode24">Feature flag indicating if Node 24 is required</param>
/// <returns>The Node version to use (node20 or node24) and warning message if both env vars are set</returns>
public static (string nodeVersion, string warningMessage) DetermineActionsNodeVersion(
IDictionary<string, string> workflowEnvironment = null,
bool useNode24ByDefault = false,
bool requireNode24 = false)
{
// Phase 3: Always use Node 24 regardless of environment variables
if (requireNode24)
{
return (Constants.Runner.NodeMigration.Node24, null);
}
// Get environment variable details with source information
var forceNode24Details = GetEnvironmentVariableDetails(
Constants.Runner.NodeMigration.ForceNode24Variable, workflowEnvironment);
var allowUnsecureNodeDetails = GetEnvironmentVariableDetails(
Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable, workflowEnvironment);
bool forceNode24 = forceNode24Details.IsTrue;
bool allowUnsecureNode = allowUnsecureNodeDetails.IsTrue;
string warningMessage = null;
// Check if both flags are set from the same source
bool bothFromWorkflow = forceNode24Details.IsTrue && allowUnsecureNodeDetails.IsTrue &&
forceNode24Details.FromWorkflow && allowUnsecureNodeDetails.FromWorkflow;
bool bothFromSystem = forceNode24Details.IsTrue && allowUnsecureNodeDetails.IsTrue &&
forceNode24Details.FromSystem && allowUnsecureNodeDetails.FromSystem;
// Handle the case when both are set in the same source
if (bothFromWorkflow || bothFromSystem)
{
string source = bothFromWorkflow ? "workflow" : "system";
string defaultVersion = useNode24ByDefault ? Constants.Runner.NodeMigration.Node24 : Constants.Runner.NodeMigration.Node20;
warningMessage = $"Both {Constants.Runner.NodeMigration.ForceNode24Variable} and {Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable} environment variables are set to true in the {source} environment. This is likely a configuration error. Using the default Node version: {defaultVersion}.";
return (defaultVersion, warningMessage);
}
// Phase 2: Node 24 is the default
if (useNode24ByDefault)
{
if (allowUnsecureNode)
{
return (Constants.Runner.NodeMigration.Node20, null);
}
return (Constants.Runner.NodeMigration.Node24, null);
}
// Phase 1: Node 20 is the default
if (forceNode24)
{
return (Constants.Runner.NodeMigration.Node24, null);
}
return (Constants.Runner.NodeMigration.Node20, null);
}
/// <summary>
/// Checks if Node24 is requested but running on ARM32 Linux, and determines if fallback is needed.
/// </summary>
/// <param name="preferredVersion">The preferred Node version</param>
/// <returns>A tuple containing the adjusted node version and an optional warning message</returns>
public static (string nodeVersion, string warningMessage) CheckNodeVersionForLinuxArm32(string preferredVersion)
{
if (string.Equals(preferredVersion, Constants.Runner.NodeMigration.Node24, StringComparison.OrdinalIgnoreCase) &&
Constants.Runner.PlatformArchitecture.Equals(Constants.Architecture.Arm) &&
Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
{
return (Constants.Runner.NodeMigration.Node20, "Node 24 is not supported on Linux ARM32 platforms. Falling back to Node 20.");
}
return (preferredVersion, null);
}
/// <summary>
/// Gets detailed information about an environment variable from both workflow and system environments
/// </summary>
/// <param name="variableName">The name of the environment variable</param>
/// <param name="workflowEnvironment">Optional dictionary containing workflow-level environment variables</param>
/// <returns>An EnvironmentVariableInfo object containing details about the variable from both sources</returns>
private static EnvironmentVariableInfo GetEnvironmentVariableDetails(string variableName, IDictionary<string, string> workflowEnvironment)
{
var info = new EnvironmentVariableInfo();
// Check workflow environment
bool foundInWorkflow = false;
string workflowValue = null;
if (workflowEnvironment != null && workflowEnvironment.TryGetValue(variableName, out workflowValue))
{
foundInWorkflow = true;
info.FromWorkflow = true;
info.IsTrue = StringUtil.ConvertToBoolean(workflowValue); // Workflow value takes precedence for the boolean value
}
// Also check system environment
string systemValue = Environment.GetEnvironmentVariable(variableName);
bool foundInSystem = !string.IsNullOrEmpty(systemValue);
info.FromSystem = foundInSystem;
// If not found in workflow, use system values
if (!foundInWorkflow)
{
info.IsTrue = StringUtil.ConvertToBoolean(systemValue);
}
return info;
}
} }
} }

View File

@@ -23,7 +23,7 @@ namespace GitHub.Runner.Listener
private RunnerSettings _settings; private RunnerSettings _settings;
private ITerminal _term; private ITerminal _term;
private TimeSpan _getNextMessageRetryInterval; private TimeSpan _getNextMessageRetryInterval;
private TaskAgentStatus _runnerStatus = TaskAgentStatus.Online; private TaskAgentStatus runnerStatus = TaskAgentStatus.Online;
private CancellationTokenSource _getMessagesTokenSource; private CancellationTokenSource _getMessagesTokenSource;
private VssCredentials _creds; private VssCredentials _creds;
private VssCredentials _credsV2; private VssCredentials _credsV2;
@@ -258,7 +258,7 @@ namespace GitHub.Runner.Listener
public void OnJobStatus(object sender, JobStatusEventArgs e) public void OnJobStatus(object sender, JobStatusEventArgs e)
{ {
Trace.Info("Received job status event. JobState: {0}", e.Status); Trace.Info("Received job status event. JobState: {0}", e.Status);
_runnerStatus = e.Status; runnerStatus = e.Status;
try try
{ {
_getMessagesTokenSource?.Cancel(); _getMessagesTokenSource?.Cancel();
@@ -291,7 +291,7 @@ namespace GitHub.Runner.Listener
} }
message = await _brokerServer.GetRunnerMessageAsync(_session.SessionId, message = await _brokerServer.GetRunnerMessageAsync(_session.SessionId,
_runnerStatus, runnerStatus,
BuildConstants.RunnerPackage.Version, BuildConstants.RunnerPackage.Version,
VarUtil.OS, VarUtil.OS,
VarUtil.OSArchitecture, VarUtil.OSArchitecture,
@@ -417,21 +417,6 @@ namespace GitHub.Runner.Listener
await Task.CompletedTask; await Task.CompletedTask;
} }
public async Task AcknowledgeMessageAsync(string runnerRequestId, CancellationToken cancellationToken)
{
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); // Short timeout
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
Trace.Info($"Acknowledging runner request '{runnerRequestId}'.");
await _brokerServer.AcknowledgeRunnerRequestAsync(
runnerRequestId,
_session.SessionId,
_runnerStatus,
BuildConstants.RunnerPackage.Version,
VarUtil.OS,
VarUtil.OSArchitecture,
linkedCts.Token);
}
private bool IsGetNextMessageExceptionRetriable(Exception ex) private bool IsGetNextMessageExceptionRetriable(Exception ex)
{ {
if (ex is TaskAgentNotFoundException || if (ex is TaskAgentNotFoundException ||

View File

@@ -537,50 +537,41 @@ namespace GitHub.Runner.Listener.Configuration
if (isConfigured && hasCredentials) if (isConfigured && hasCredentials)
{ {
RunnerSettings settings = _store.GetSettings(); RunnerSettings settings = _store.GetSettings();
var credentialManager = HostContext.GetService<ICredentialManager>();
if (settings.UseV2Flow) // Get the credentials
VssCredentials creds = null;
if (string.IsNullOrEmpty(settings.GitHubUrl))
{ {
var deletionToken = await GetRunnerTokenAsync(command, settings.GitHubUrl, "remove"); var credProvider = GetCredentialProvider(command, settings.ServerUrl);
await _dotcomServer.DeleteRunnerAsync(settings.GitHubUrl, deletionToken, settings.AgentId); creds = credProvider.GetVssCredentials(HostContext, allowAuthUrlV2: false);
Trace.Info("legacy vss cred retrieved");
} }
else else
{ {
var credentialManager = HostContext.GetService<ICredentialManager>(); var deletionToken = await GetRunnerTokenAsync(command, settings.GitHubUrl, "remove");
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, deletionToken, Constants.RunnerEvent.Remove);
// Get the credentials creds = authResult.ToVssCredentials();
VssCredentials creds = null; Trace.Info("cred retrieved via GitHub auth");
if (string.IsNullOrEmpty(settings.GitHubUrl))
{
var credProvider = GetCredentialProvider(command, settings.ServerUrl);
creds = credProvider.GetVssCredentials(HostContext, allowAuthUrlV2: false);
Trace.Info("legacy vss cred retrieved");
}
else
{
var deletionToken = await GetRunnerTokenAsync(command, settings.GitHubUrl, "remove");
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, deletionToken, Constants.RunnerEvent.Remove);
creds = authResult.ToVssCredentials();
Trace.Info("cred retrieved via GitHub auth");
}
// Determine the service deployment type based on connection data. (Hosted/OnPremises)
await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds);
var agents = await _runnerServer.GetAgentsAsync(settings.AgentName);
Trace.Verbose("Returns {0} agents", agents.Count);
TaskAgent agent = agents.FirstOrDefault();
if (agent == null)
{
_term.WriteLine("Does not exist. Skipping " + currentAction);
}
else
{
await _runnerServer.DeleteAgentAsync(settings.AgentId);
}
} }
_term.WriteLine(); // Determine the service deployment type based on connection data. (Hosted/OnPremises)
_term.WriteSuccessMessage("Runner removed successfully"); await _runnerServer.ConnectAsync(new Uri(settings.ServerUrl), creds);
var agents = await _runnerServer.GetAgentsAsync(settings.AgentName);
Trace.Verbose("Returns {0} agents", agents.Count);
TaskAgent agent = agents.FirstOrDefault();
if (agent == null)
{
_term.WriteLine("Does not exist. Skipping " + currentAction);
}
else
{
await _runnerServer.DeleteAgentAsync(settings.AgentId);
_term.WriteLine();
_term.WriteSuccessMessage("Runner removed successfully");
}
} }
else else
{ {

View File

@@ -110,12 +110,7 @@ namespace GitHub.Runner.Listener
{ {
var jwt = JsonWebToken.Create(accessToken); var jwt = JsonWebToken.Create(accessToken);
var claims = jwt.ExtractClaims(); var claims = jwt.ExtractClaims();
orchestrationId = claims.FirstOrDefault(x => string.Equals(x.Type, "orch_id", StringComparison.OrdinalIgnoreCase))?.Value; orchestrationId = claims.FirstOrDefault(x => string.Equals(x.Type, "orchid", StringComparison.OrdinalIgnoreCase))?.Value;
if (string.IsNullOrEmpty(orchestrationId))
{
orchestrationId = claims.FirstOrDefault(x => string.Equals(x.Type, "orchid", StringComparison.OrdinalIgnoreCase))?.Value;
}
if (!string.IsNullOrEmpty(orchestrationId)) if (!string.IsNullOrEmpty(orchestrationId))
{ {
Trace.Info($"Pull OrchestrationId {orchestrationId} from JWT claims"); Trace.Info($"Pull OrchestrationId {orchestrationId} from JWT claims");

View File

@@ -32,7 +32,6 @@ namespace GitHub.Runner.Listener
Task DeleteSessionAsync(); Task DeleteSessionAsync();
Task<TaskAgentMessage> GetNextMessageAsync(CancellationToken token); Task<TaskAgentMessage> GetNextMessageAsync(CancellationToken token);
Task DeleteMessageAsync(TaskAgentMessage message); Task DeleteMessageAsync(TaskAgentMessage message);
Task AcknowledgeMessageAsync(string runnerRequestId, CancellationToken cancellationToken);
Task RefreshListenerTokenAsync(); Task RefreshListenerTokenAsync();
void OnJobStatus(object sender, JobStatusEventArgs e); void OnJobStatus(object sender, JobStatusEventArgs e);
@@ -53,7 +52,7 @@ namespace GitHub.Runner.Listener
private readonly TimeSpan _sessionConflictRetryLimit = TimeSpan.FromMinutes(4); private readonly TimeSpan _sessionConflictRetryLimit = TimeSpan.FromMinutes(4);
private readonly TimeSpan _clockSkewRetryLimit = TimeSpan.FromMinutes(30); private readonly TimeSpan _clockSkewRetryLimit = TimeSpan.FromMinutes(30);
private readonly Dictionary<string, int> _sessionCreationExceptionTracker = new(); private readonly Dictionary<string, int> _sessionCreationExceptionTracker = new();
private TaskAgentStatus _runnerStatus = TaskAgentStatus.Online; private TaskAgentStatus runnerStatus = TaskAgentStatus.Online;
private CancellationTokenSource _getMessagesTokenSource; private CancellationTokenSource _getMessagesTokenSource;
private VssCredentials _creds; private VssCredentials _creds;
private VssCredentials _credsV2; private VssCredentials _credsV2;
@@ -218,7 +217,7 @@ namespace GitHub.Runner.Listener
public void OnJobStatus(object sender, JobStatusEventArgs e) public void OnJobStatus(object sender, JobStatusEventArgs e)
{ {
Trace.Info("Received job status event. JobState: {0}", e.Status); Trace.Info("Received job status event. JobState: {0}", e.Status);
_runnerStatus = e.Status; runnerStatus = e.Status;
try try
{ {
_getMessagesTokenSource?.Cancel(); _getMessagesTokenSource?.Cancel();
@@ -251,7 +250,7 @@ namespace GitHub.Runner.Listener
message = await _runnerServer.GetAgentMessageAsync(_settings.PoolId, message = await _runnerServer.GetAgentMessageAsync(_settings.PoolId,
_session.SessionId, _session.SessionId,
_lastMessageId, _lastMessageId,
_runnerStatus, runnerStatus,
BuildConstants.RunnerPackage.Version, BuildConstants.RunnerPackage.Version,
VarUtil.OS, VarUtil.OS,
VarUtil.OSArchitecture, VarUtil.OSArchitecture,
@@ -275,7 +274,7 @@ namespace GitHub.Runner.Listener
} }
message = await _brokerServer.GetRunnerMessageAsync(_session.SessionId, message = await _brokerServer.GetRunnerMessageAsync(_session.SessionId,
_runnerStatus, runnerStatus,
BuildConstants.RunnerPackage.Version, BuildConstants.RunnerPackage.Version,
VarUtil.OS, VarUtil.OS,
VarUtil.OSArchitecture, VarUtil.OSArchitecture,
@@ -438,21 +437,6 @@ namespace GitHub.Runner.Listener
await _brokerServer.ForceRefreshConnection(_credsV2); await _brokerServer.ForceRefreshConnection(_credsV2);
} }
public async Task AcknowledgeMessageAsync(string runnerRequestId, CancellationToken cancellationToken)
{
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); // Short timeout
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token);
Trace.Info($"Acknowledging runner request '{runnerRequestId}'.");
await _brokerServer.AcknowledgeRunnerRequestAsync(
runnerRequestId,
_session.SessionId,
_runnerStatus,
BuildConstants.RunnerPackage.Version,
VarUtil.OS,
VarUtil.OSArchitecture,
linkedCts.Token);
}
private TaskAgentMessage DecryptMessage(TaskAgentMessage message) private TaskAgentMessage DecryptMessage(TaskAgentMessage message)
{ {
if (_session.EncryptionKey == null || if (_session.EncryptionKey == null ||

View File

@@ -654,42 +654,22 @@ namespace GitHub.Runner.Listener
else else
{ {
var messageRef = StringUtil.ConvertFromJson<RunnerJobRequestRef>(message.Body); var messageRef = StringUtil.ConvertFromJson<RunnerJobRequestRef>(message.Body);
// Acknowledge (best-effort)
if (messageRef.ShouldAcknowledge) // Temporary feature flag
{
try
{
await _listener.AcknowledgeMessageAsync(messageRef.RunnerRequestId, messageQueueLoopTokenSource.Token);
}
catch (Exception ex)
{
Trace.Error($"Best-effort acknowledge failed for request '{messageRef.RunnerRequestId}'");
Trace.Error(ex);
}
}
Pipelines.AgentJobRequestMessage jobRequestMessage = null; Pipelines.AgentJobRequestMessage jobRequestMessage = null;
// Create connection
var credMgr = HostContext.GetService<ICredentialManager>();
if (string.IsNullOrEmpty(messageRef.RunServiceUrl)) if (string.IsNullOrEmpty(messageRef.RunServiceUrl))
{ {
// Connect
var credMgr = HostContext.GetService<ICredentialManager>();
var creds = credMgr.LoadCredentials(allowAuthUrlV2: false); var creds = credMgr.LoadCredentials(allowAuthUrlV2: false);
var actionsRunServer = HostContext.CreateService<IActionsRunServer>(); var actionsRunServer = HostContext.CreateService<IActionsRunServer>();
await actionsRunServer.ConnectAsync(new Uri(settings.ServerUrl), creds); await actionsRunServer.ConnectAsync(new Uri(settings.ServerUrl), creds);
// Get job message
jobRequestMessage = await actionsRunServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageQueueLoopTokenSource.Token); jobRequestMessage = await actionsRunServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageQueueLoopTokenSource.Token);
} }
else else
{ {
// Connect
var credMgr = HostContext.GetService<ICredentialManager>();
var credsV2 = credMgr.LoadCredentials(allowAuthUrlV2: true); var credsV2 = credMgr.LoadCredentials(allowAuthUrlV2: true);
var runServer = HostContext.CreateService<IRunServer>(); var runServer = HostContext.CreateService<IRunServer>();
await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), credsV2); await runServer.ConnectAsync(new Uri(messageRef.RunServiceUrl), credsV2);
// Get job message
try try
{ {
jobRequestMessage = await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageRef.BillingOwnerId, messageQueueLoopTokenSource.Token); jobRequestMessage = await runServer.GetJobMessageAsync(messageRef.RunnerRequestId, messageRef.BillingOwnerId, messageQueueLoopTokenSource.Token);
@@ -718,10 +698,7 @@ namespace GitHub.Runner.Listener
} }
} }
// Dispatch
jobDispatcher.Run(jobRequestMessage, runOnce); jobDispatcher.Run(jobRequestMessage, runOnce);
// Run once?
if (runOnce) if (runOnce)
{ {
Trace.Info("One time used runner received job message."); Trace.Info("One time used runner received job message.");

View File

@@ -10,9 +10,6 @@ namespace GitHub.Runner.Listener
[DataMember(Name = "runner_request_id")] [DataMember(Name = "runner_request_id")]
public string RunnerRequestId { get; set; } public string RunnerRequestId { get; set; }
[DataMember(Name = "should_acknowledge")]
public bool ShouldAcknowledge { get; set; }
[DataMember(Name = "run_service_url")] [DataMember(Name = "run_service_url")]
public string RunServiceUrl { get; set; } public string RunServiceUrl { get; set; }

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup> </startup>
</configuration> </configuration>

View File

@@ -18,7 +18,7 @@
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion> <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(PackageRuntime)' != 'win-arm64' "> <PropertyGroup Condition=" '$(PackageRuntime)' != 'win-arm64' ">
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>

View File

@@ -450,8 +450,7 @@ namespace GitHub.Runner.Worker
} }
else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase) || else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase) ||
string.Equals(usingToken.Value, "node16", StringComparison.OrdinalIgnoreCase) || string.Equals(usingToken.Value, "node16", StringComparison.OrdinalIgnoreCase) ||
string.Equals(usingToken.Value, "node20", StringComparison.OrdinalIgnoreCase) || string.Equals(usingToken.Value, "node20", StringComparison.OrdinalIgnoreCase))
string.Equals(usingToken.Value, "node24", StringComparison.OrdinalIgnoreCase))
{ {
if (string.IsNullOrEmpty(mainToken?.Value)) if (string.IsNullOrEmpty(mainToken?.Value))
{ {
@@ -491,7 +490,7 @@ namespace GitHub.Runner.Worker
} }
else else
{ {
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker', 'node12', 'node16', 'node20' or 'node24' instead."); throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker', 'node12', 'node16' or 'node20' instead.");
} }
} }
else if (pluginToken != null) else if (pluginToken != null)
@@ -502,7 +501,7 @@ namespace GitHub.Runner.Worker
}; };
} }
throw new NotSupportedException("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12', 'node16', 'node20' or 'node24'."); throw new NotSupportedException("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12', 'node16' or 'node20'.");
} }
private void ConvertInputs( private void ConvertInputs(

View File

@@ -58,41 +58,10 @@ namespace GitHub.Runner.Worker.Handlers
var nodeData = data as NodeJSActionExecutionData; var nodeData = data as NodeJSActionExecutionData;
// With node12 EoL in 04/2022 and node16 EoL in 09/23, we want to execute all JS actions using node20 // With node12 EoL in 04/2022 and node16 EoL in 09/23, we want to execute all JS actions using node20
// With node20 EoL approaching, we're preparing to migrate to node24
if (string.Equals(nodeData.NodeVersion, "node12", StringComparison.InvariantCultureIgnoreCase) || if (string.Equals(nodeData.NodeVersion, "node12", StringComparison.InvariantCultureIgnoreCase) ||
string.Equals(nodeData.NodeVersion, "node16", StringComparison.InvariantCultureIgnoreCase)) string.Equals(nodeData.NodeVersion, "node16", StringComparison.InvariantCultureIgnoreCase))
{ {
nodeData.NodeVersion = Common.Constants.Runner.NodeMigration.Node20; nodeData.NodeVersion = "node20";
}
// Check if node20 was explicitly specified in the action
// We don't modify if node24 was explicitly specified
if (string.Equals(nodeData.NodeVersion, Constants.Runner.NodeMigration.Node20, StringComparison.InvariantCultureIgnoreCase))
{
bool useNode24ByDefault = executionContext.Global.Variables?.GetBoolean(Constants.Runner.NodeMigration.UseNode24ByDefaultFlag) ?? false;
bool requireNode24 = executionContext.Global.Variables?.GetBoolean(Constants.Runner.NodeMigration.RequireNode24Flag) ?? false;
var (nodeVersion, configWarningMessage) = NodeUtil.DetermineActionsNodeVersion(environment, useNode24ByDefault, requireNode24);
var (finalNodeVersion, platformWarningMessage) = NodeUtil.CheckNodeVersionForLinuxArm32(nodeVersion);
nodeData.NodeVersion = finalNodeVersion;
if (!string.IsNullOrEmpty(configWarningMessage))
{
executionContext.Warning(configWarningMessage);
}
if (!string.IsNullOrEmpty(platformWarningMessage))
{
executionContext.Warning(platformWarningMessage);
}
// Show information about Node 24 migration in Phase 2
if (useNode24ByDefault && !requireNode24 && string.Equals(finalNodeVersion, Constants.Runner.NodeMigration.Node24, StringComparison.OrdinalIgnoreCase))
{
string infoMessage = "Node 20 is being deprecated. This workflow is running with Node 24 by default. " +
"If you need to temporarily use Node 20, you can set the ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true environment variable.";
executionContext.Output(infoMessage);
}
} }
(handler as INodeScriptActionHandler).Data = nodeData; (handler as INodeScriptActionHandler).Data = nodeData;

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using GitHub.DistributedTask.Pipelines.ContextData;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -8,6 +9,7 @@ using GitHub.Runner.Common;
using GitHub.Runner.Sdk; using GitHub.Runner.Sdk;
using System.Linq; using System.Linq;
using GitHub.Runner.Worker.Container.ContainerHooks; using GitHub.Runner.Worker.Container.ContainerHooks;
using System.IO;
using System.Threading.Channels; using System.Threading.Channels;
namespace GitHub.Runner.Worker.Handlers namespace GitHub.Runner.Worker.Handlers
@@ -58,14 +60,7 @@ namespace GitHub.Runner.Worker.Handlers
public Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion) public Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion)
{ {
// Use NodeUtil to check if Node24 is requested but we're on ARM32 Linux return Task.FromResult<string>(preferredVersion);
var (nodeVersion, warningMessage) = Common.Util.NodeUtil.CheckNodeVersionForLinuxArm32(preferredVersion);
if (!string.IsNullOrEmpty(warningMessage))
{
executionContext.Warning(warningMessage);
}
return Task.FromResult(nodeVersion);
} }
public async Task<int> ExecuteAsync(IExecutionContext context, public async Task<int> ExecuteAsync(IExecutionContext context,
@@ -142,12 +137,8 @@ namespace GitHub.Runner.Worker.Handlers
public async Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion) public async Task<string> DetermineNodeRuntimeVersion(IExecutionContext executionContext, string preferredVersion)
{ {
// Use NodeUtil to check if Node24 is requested but we're on ARM32 Linux // Optimistically use the default
var (nodeExternal, warningMessage) = Common.Util.NodeUtil.CheckNodeVersionForLinuxArm32(preferredVersion); string nodeExternal = preferredVersion;
if (!string.IsNullOrEmpty(warningMessage))
{
executionContext.Warning(warningMessage);
}
if (FeatureManager.IsContainerHooksEnabled(executionContext.Global.Variables)) if (FeatureManager.IsContainerHooksEnabled(executionContext.Global.Variables))
{ {
@@ -273,14 +264,7 @@ namespace GitHub.Runner.Worker.Handlers
private string CheckPlatformForAlpineContainer(IExecutionContext executionContext, string preferredVersion) private string CheckPlatformForAlpineContainer(IExecutionContext executionContext, string preferredVersion)
{ {
// Use NodeUtil to check if Node24 is requested but we're on ARM32 Linux string nodeExternal = preferredVersion;
var (nodeExternal, warningMessage) = Common.Util.NodeUtil.CheckNodeVersionForLinuxArm32(preferredVersion);
if (!string.IsNullOrEmpty(warningMessage))
{
executionContext.Warning(warningMessage);
}
// Check for Alpine container compatibility
if (!Constants.Runner.PlatformArchitecture.Equals(Constants.Architecture.X64)) if (!Constants.Runner.PlatformArchitecture.Equals(Constants.Architecture.X64))
{ {
var os = Constants.Runner.Platform.ToString(); var os = Constants.Runner.Platform.ToString();

View File

@@ -50,11 +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))
{ {
if (!HostContext.UserAgents.Any(x => string.Equals(x.Product?.Name, "OrchestrationId", StringComparison.OrdinalIgnoreCase))) // 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 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);

View File

@@ -14,7 +14,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.25.0" /> <PackageReference Include="Azure.Storage.Blobs" Version="12.24.0" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.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="6.0.0" /> <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="6.0.0" />

View File

@@ -79,7 +79,6 @@ namespace GitHub.Actions.RunService.WebApi
{ {
queryParams.Add("status", status.Value.ToString()); queryParams.Add("status", status.Value.ToString());
} }
if (runnerVersion != null) if (runnerVersion != null)
{ {
queryParams.Add("runnerVersion", runnerVersion); queryParams.Add("runnerVersion", runnerVersion);
@@ -143,6 +142,7 @@ namespace GitHub.Actions.RunService.WebApi
} }
public async Task<TaskAgentSession> CreateSessionAsync( public async Task<TaskAgentSession> CreateSessionAsync(
TaskAgentSession session, TaskAgentSession session,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
@@ -191,76 +191,6 @@ 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}");
} }
public async Task AcknowledgeRunnerRequestAsync(
string runnerRequestId,
Guid? sessionId,
string runnerVersion,
TaskAgentStatus? status,
string os = null,
string architecture = null,
CancellationToken cancellationToken = default)
{
// URL
var requestUri = new Uri(Client.BaseAddress, "acknowledge");
// Query parameters
List<KeyValuePair<string, string>> queryParams = new List<KeyValuePair<string, string>>();
if (sessionId != null)
{
queryParams.Add("sessionId", sessionId.Value.ToString());
}
if (status != null)
{
queryParams.Add("status", status.Value.ToString());
}
if (runnerVersion != null)
{
queryParams.Add("runnerVersion", runnerVersion);
}
if (os != null)
{
queryParams.Add("os", os);
}
if (architecture != null)
{
queryParams.Add("architecture", architecture);
}
// Body
var payload = new Dictionary<string, string>
{
["runnerRequestId"] = runnerRequestId,
};
var requestContent = new ObjectContent<Dictionary<string, string>>(payload, new VssJsonMediaTypeFormatter(true));
// POST
var result = await SendAsync<object>(
new HttpMethod("POST"),
requestUri: requestUri,
queryParameters: queryParams,
content: requestContent,
readErrorBody: true,
cancellationToken: cancellationToken);
if (result.IsSuccess)
{
return;
}
if (TryParseErrorBody(result.ErrorBody, out BrokerError brokerError))
{
switch (brokerError.ErrorKind)
{
case BrokerErrorKind.RunnerNotFound:
throw new RunnerNotFoundException(brokerError.Message);
default:
break;
}
}
throw new Exception($"Failed to acknowledge runner request. Request to {requestUri} failed with status: {result.StatusCode}. Error message {result.Error}");
}
private static bool TryParseErrorBody(string errorBody, out BrokerError error) private static bool TryParseErrorBody(string errorBody, out BrokerError error)
{ {
if (!string.IsNullOrEmpty(errorBody)) if (!string.IsNullOrEmpty(errorBody))

View File

@@ -520,8 +520,8 @@ namespace GitHub.Services.Results.Client
Number = r.Order.GetValueOrDefault(), Number = r.Order.GetValueOrDefault(),
Name = r.Name, Name = r.Name,
Status = ConvertStateToStatus(r.State.GetValueOrDefault()), Status = ConvertStateToStatus(r.State.GetValueOrDefault()),
StartedAt = r.StartTime?.ToString(Constants.TimestampFormat, CultureInfo.InvariantCulture), StartedAt = r.StartTime?.ToString(Constants.TimestampFormat),
CompletedAt = r.FinishTime?.ToString(Constants.TimestampFormat, CultureInfo.InvariantCulture), CompletedAt = r.FinishTime?.ToString(Constants.TimestampFormat),
Conclusion = ConvertResultToConclusion(r.Result) Conclusion = ConvertResultToConclusion(r.Result)
}; };
} }

View File

@@ -978,7 +978,7 @@ namespace GitHub.Runner.Common.Tests.Listener
_messageListener.Verify(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()), Times.AtLeast(2)); _messageListener.Verify(x => x.GetNextMessageAsync(It.IsAny<CancellationToken>()), Times.AtLeast(2));
_messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeast(2)); _messageListener.Verify(x => x.DeleteMessageAsync(It.IsAny<TaskAgentMessage>()), Times.AtLeast(2));
_messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once()); _messageListener.Verify(x => x.DeleteSessionAsync(), Times.Once());
_credentialManager.Verify(x => x.LoadCredentials(true), Times.AtLeast(2)); _credentialManager.Verify(x => x.LoadCredentials(true), Times.Exactly(2));
Assert.False(hc.AllowAuthMigration); Assert.False(hc.AllowAuthMigration);
} }

View File

@@ -1,120 +0,0 @@
using System;
using System.Collections.Generic;
using GitHub.Runner.Common;
using GitHub.Runner.Common.Util;
using Xunit;
namespace GitHub.Runner.Common.Tests.Util
{
public class NodeUtilL0
{
// We're testing the logic with feature flags
[Theory]
[InlineData(false, false, false, false, "node20", false)] // Phase 1: No env vars
[InlineData(false, false, false, true, "node20", false)] // Phase 1: Allow unsecure (redundant)
[InlineData(false, false, true, false, "node24", false)] // Phase 1: Force node24
[InlineData(false, false, true, true, "node20", true)] // Phase 1: Both flags (use phase default + warning)
[InlineData(false, true, false, false, "node24", false)] // Phase 2: No env vars
[InlineData(false, true, false, true, "node20", false)] // Phase 2: Allow unsecure
[InlineData(false, true, true, false, "node24", false)] // Phase 2: Force node24 (redundant)
[InlineData(false, true, true, true, "node24", true)] // Phase 2: Both flags (use phase default + warning)
[InlineData(true, false, false, false, "node24", false)] // Phase 3: Always Node 24 regardless of env vars
[InlineData(true, false, false, true, "node24", false)] // Phase 3: Always Node 24 regardless of env vars
[InlineData(true, false, true, false, "node24", false)] // Phase 3: Always Node 24 regardless of env vars
[InlineData(true, false, true, true, "node24", false)] // Phase 3: Always Node 24 regardless of env vars, no warnings in Phase 3
public void TestNodeVersionLogic(bool requireNode24, bool useNode24ByDefault, bool forceNode24, bool allowUnsecureNode, string expectedVersion, bool expectWarning)
{
try
{
Environment.SetEnvironmentVariable(Constants.Runner.NodeMigration.ForceNode24Variable, forceNode24 ? "true" : null);
Environment.SetEnvironmentVariable(Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable, allowUnsecureNode ? "true" : null);
// Call the actual method
var (actualVersion, warningMessage) = NodeUtil.DetermineActionsNodeVersion(null, useNode24ByDefault, requireNode24);
// Assert
Assert.Equal(expectedVersion, actualVersion);
if (expectWarning)
{
Assert.NotNull(warningMessage);
Assert.Contains("Both", warningMessage);
Assert.Contains("are set to true", warningMessage);
}
else
{
Assert.Null(warningMessage);
}
}
finally
{
// Cleanup
Environment.SetEnvironmentVariable(Constants.Runner.NodeMigration.ForceNode24Variable, null);
Environment.SetEnvironmentVariable(Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable, null);
}
}
[Theory]
[InlineData(false, false, false, false, false, true, "node20", false)] // Phase 1: System env: none, Workflow env: allow=true
[InlineData(false, false, true, false, false, false, "node24", false)] // Phase 1: System env: force node24, Workflow env: none
[InlineData(false, true, false, false, true, false, "node24", false)] // Phase 1: System env: none, Workflow env: force node24
[InlineData(false, false, false, true, false, true, "node20", false)] // Phase 1: System env: allow=true, Workflow env: allow=true (workflow takes precedence)
[InlineData(false, false, true, true, false, false, "node20", true)] // Phase 1: System env: both true, Workflow env: none (use phase default + warning)
[InlineData(false, false, false, false, true, true, "node20", true)] // Phase 1: System env: none, Workflow env: both (use phase default + warning)
[InlineData(true, false, false, false, false, false, "node24", false)] // Phase 2: System env: none, Workflow env: none
[InlineData(true, false, false, true, false, false, "node20", false)] // Phase 2: System env: allow=true, Workflow env: none
[InlineData(true, false, false, false, false, true, "node20", false)] // Phase 2: System env: none, Workflow env: allow unsecure
[InlineData(true, false, true, false, false, true, "node20", false)] // Phase 2: System env: force node24, Workflow env: allow unsecure
[InlineData(true, false, true, true, false, false, "node24", true)] // Phase 2: System env: both true, Workflow env: none (use phase default + warning)
[InlineData(true, false, false, false, true, true, "node24", true)] // Phase 2: System env: none, Workflow env: both (phase default + warning)
[InlineData(false, true, false, false, false, true, "node24", false)] // Phase 3: System env: none, Workflow env: allow=true (always Node 24 in Phase 3)
[InlineData(false, true, true, true, false, false, "node24", false)] // Phase 3: System env: both true, Workflow env: none (always Node 24 in Phase 3, no warning)
[InlineData(false, true, false, false, true, true, "node24", false)] // Phase 3: System env: none, Workflow env: both (always Node 24 in Phase 3, no warning)
public void TestNodeVersionLogicWithWorkflowEnvironment(bool useNode24ByDefault, bool requireNode24,
bool systemForceNode24, bool systemAllowUnsecure,
bool workflowForceNode24, bool workflowAllowUnsecure,
string expectedVersion, bool expectWarning)
{
try
{
// Set system environment variables
Environment.SetEnvironmentVariable(Constants.Runner.NodeMigration.ForceNode24Variable, systemForceNode24 ? "true" : null);
Environment.SetEnvironmentVariable(Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable, systemAllowUnsecure ? "true" : null);
// Set workflow environment variables
var workflowEnv = new Dictionary<string, string>();
if (workflowForceNode24)
{
workflowEnv[Constants.Runner.NodeMigration.ForceNode24Variable] = "true";
}
if (workflowAllowUnsecure)
{
workflowEnv[Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable] = "true";
}
// Call the actual method with our test parameters
var (actualVersion, warningMessage) = NodeUtil.DetermineActionsNodeVersion(workflowEnv, useNode24ByDefault, requireNode24);
// Assert
Assert.Equal(expectedVersion, actualVersion);
if (expectWarning)
{
Assert.NotNull(warningMessage);
Assert.Contains("Both", warningMessage);
Assert.Contains("are set to true", warningMessage);
}
else
{
Assert.Null(warningMessage);
}
}
finally
{
// Cleanup
Environment.SetEnvironmentVariable(Constants.Runner.NodeMigration.ForceNode24Variable, null);
Environment.SetEnvironmentVariable(Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable, null);
}
}
}
}

View File

@@ -1659,76 +1659,6 @@ runs:
Teardown(); Teardown();
} }
} }
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void LoadsNode24ActionDefinition()
{
try
{
// Arrange.
Setup();
const string Content = @"
# Container action
name: 'Hello World'
description: 'Greet the world and record the time'
author: 'GitHub'
inputs:
greeting: # id of input
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
required: true
default: 'Hello'
entryPoint: # id of input
description: 'optional docker entrypoint overwrite.'
required: false
outputs:
time: # id of output
description: 'The time we did the greeting'
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
color: 'green' # optional, decorates the entry in the GitHub Marketplace
runs:
using: 'node24'
main: 'task.js'
";
Pipelines.ActionStep instance;
string directory;
CreateAction(yamlContent: Content, instance: out instance, directory: out directory);
// Act.
Definition definition = _actionManager.LoadAction(_ec.Object, instance);
// Assert.
Assert.NotNull(definition);
Assert.Equal(directory, definition.Directory);
Assert.NotNull(definition.Data);
Assert.NotNull(definition.Data.Inputs); // inputs
Dictionary<string, string> inputDefaults = new(StringComparer.OrdinalIgnoreCase);
foreach (var input in definition.Data.Inputs)
{
var name = input.Key.AssertString("key").Value;
var value = input.Value.AssertScalar("value").ToString();
_hc.GetTrace().Info($"Default: {name} = {value}");
inputDefaults[name] = value;
}
Assert.Equal(2, inputDefaults.Count);
Assert.True(inputDefaults.ContainsKey("greeting"));
Assert.Equal("Hello", inputDefaults["greeting"]);
Assert.True(string.IsNullOrEmpty(inputDefaults["entryPoint"]));
Assert.NotNull(definition.Data.Execution); // execution
Assert.NotNull(definition.Data.Execution as NodeJSActionExecutionData);
Assert.Equal("task.js", (definition.Data.Execution as NodeJSActionExecutionData).Script);
Assert.Equal("node24", (definition.Data.Execution as NodeJSActionExecutionData).NodeVersion);
}
finally
{
Teardown();
}
}
[Fact] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]

View File

@@ -502,49 +502,6 @@ namespace GitHub.Runner.Common.Tests.Worker
} }
} }
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Load_Node24Action()
{
try
{
//Arrange
Setup();
var actionManifest = new ActionManifestManager();
actionManifest.Initialize(_hc);
//Act
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "node24action.yml"));
//Assert
Assert.Equal("Hello World", result.Name);
Assert.Equal("Greet the world and record the time", result.Description);
Assert.Equal(2, result.Inputs.Count);
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
Assert.Equal(1, result.Deprecated.Count);
Assert.True(result.Deprecated.ContainsKey("greeting"));
result.Deprecated.TryGetValue("greeting", out string value);
Assert.Equal("This property has been deprecated", value);
Assert.Equal(ActionExecutionType.NodeJS, result.Execution.ExecutionType);
var nodeAction = result.Execution as NodeJSActionExecutionData;
Assert.Equal("main.js", nodeAction.Script);
Assert.Equal("node24", nodeAction.NodeVersion);
}
finally
{
Teardown();
}
}
[Fact] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
@@ -801,7 +758,7 @@ namespace GitHub.Runner.Common.Tests.Worker
//Assert //Assert
var err = Assert.Throws<ArgumentException>(() => actionManifest.Load(_ec.Object, action_path)); var err = Assert.Throws<ArgumentException>(() => actionManifest.Load(_ec.Object, action_path));
Assert.Contains($"Failed to load {action_path}", err.Message); Assert.Contains($"Failed to load {action_path}", err.Message);
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12', 'node16', 'node20' or 'node24'.")), It.IsAny<ExecutionContextLogOptions>()), Times.Once); _ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12', 'node16' or 'node20'.")), It.IsAny<ExecutionContextLogOptions>()), Times.Once);
} }
finally finally
{ {

View File

@@ -33,7 +33,6 @@ namespace GitHub.Runner.Common.Tests.Worker
[InlineData("node12", "node20")] [InlineData("node12", "node20")]
[InlineData("node16", "node20")] [InlineData("node16", "node20")]
[InlineData("node20", "node20")] [InlineData("node20", "node20")]
[InlineData("node24", "node24")]
public void IsNodeVersionUpgraded(string inputVersion, string expectedVersion) public void IsNodeVersionUpgraded(string inputVersion, string expectedVersion)
{ {
using (TestHostContext hc = CreateTestContext()) using (TestHostContext hc = CreateTestContext())
@@ -42,7 +41,7 @@ namespace GitHub.Runner.Common.Tests.Worker
var hf = new HandlerFactory(); var hf = new HandlerFactory();
hf.Initialize(hc); hf.Initialize(hc);
// Setup variables // Server Feature Flag
var variables = new Dictionary<string, VariableValue>(); var variables = new Dictionary<string, VariableValue>();
Variables serverVariables = new(hc, variables); Variables serverVariables = new(hc, variables);
@@ -73,48 +72,5 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal(expectedVersion, handler.Data.NodeVersion); Assert.Equal(expectedVersion, handler.Data.NodeVersion);
} }
} }
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void Node24ExplicitlyRequested_HonoredByDefault()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var hf = new HandlerFactory();
hf.Initialize(hc);
// Basic variables setup
var variables = new Dictionary<string, VariableValue>();
Variables serverVariables = new(hc, variables);
_ec.Setup(x => x.Global).Returns(new GlobalContext()
{
Variables = serverVariables,
EnvironmentVariables = new Dictionary<string, string>()
});
// Act - Node 24 explicitly requested in action.yml
var data = new NodeJSActionExecutionData();
data.NodeVersion = "node24";
var handler = hf.Create(
_ec.Object,
new ScriptReference(),
new Mock<IStepHost>().Object,
data,
new Dictionary<string, string>(),
new Dictionary<string, string>(),
new Variables(hc, new Dictionary<string, VariableValue>()),
"",
new List<JobExtensionRunner>()
) as INodeScriptActionHandler;
// Assert - should be node24 as requested
Assert.Equal("node24", handler.Data.NodeVersion);
}
}
} }
} }

View File

@@ -1,35 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Sdk;
using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Handlers;
using Moq;
using Xunit;
namespace GitHub.Runner.Common.Tests.Worker.Handlers
{
public sealed class NodeHandlerL0
{
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void NodeJSActionExecutionDataSupportsNode24()
{
// Create NodeJSActionExecutionData with node24
var nodeJSData = new NodeJSActionExecutionData
{
NodeVersion = "node24",
Script = "test.js"
};
// Act & Assert
Assert.Equal("node24", nodeJSData.NodeVersion);
Assert.Equal(ActionExecutionType.NodeJS, nodeJSData.ExecutionType);
}
}
}

View File

@@ -162,60 +162,6 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal("node20", nodeVersion); Assert.Equal("node20", nodeVersion);
} }
} }
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task DetermineNode24RuntimeVersionInAlpineContainerAsync()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var sh = new ContainerStepHost();
sh.Initialize(hc);
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
.Callback((IExecutionContext ec, string id, string options, string command, List<string> output) =>
{
output.Add("alpine");
})
.ReturnsAsync(0);
// Act.
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node24");
// Assert.
Assert.Equal("node24_alpine", nodeVersion);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task DetermineNode24RuntimeVersionInUnknownContainerAsync()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var sh = new ContainerStepHost();
sh.Initialize(hc);
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
.Callback((IExecutionContext ec, string id, string options, string command, List<string> output) =>
{
output.Add("github");
})
.ReturnsAsync(0);
// Act.
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node24");
// Assert.
Assert.Equal("node24", nodeVersion);
}
}
#endif #endif
} }
} }

View File

@@ -1,63 +0,0 @@
using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Handlers;
using Moq;
using System;
using System.Runtime.InteropServices;
using Xunit;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class StepHostNodeVersionL0
{
private Mock<IExecutionContext> _ec;
private DefaultStepHost _defaultStepHost;
public StepHostNodeVersionL0()
{
_ec = new Mock<IExecutionContext>();
_defaultStepHost = new DefaultStepHost();
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void CheckNodeVersionForArm32_Node24OnArm32Linux()
{
// Test via NodeUtil directly
string preferredVersion = "node24";
var (nodeVersion, warningMessage) = Common.Util.NodeUtil.CheckNodeVersionForLinuxArm32(preferredVersion);
// On ARM32 Linux, we should fall back to node20
bool isArm32 = RuntimeInformation.ProcessArchitecture == Architecture.Arm ||
Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")?.Contains("ARM") == true;
bool isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
if (isArm32 && isLinux)
{
// Should downgrade to node20 on ARM32 Linux
Assert.Equal("node20", nodeVersion);
Assert.NotNull(warningMessage);
Assert.Contains("Node 24 is not supported on Linux ARM32 platforms", warningMessage);
}
else
{
// On non-ARM32 platforms, should pass through the version unmodified
Assert.Equal("node24", nodeVersion);
Assert.Null(warningMessage);
}
}
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void CheckNodeVersionForArm32_PassThroughNonNode24Versions()
{
string preferredVersion = "node20";
var (nodeVersion, warningMessage) = Common.Util.NodeUtil.CheckNodeVersionForLinuxArm32(preferredVersion);
// Should never modify the version for non-node24 inputs
Assert.Equal("node20", nodeVersion);
Assert.Null(warningMessage);
}
}
}

View File

@@ -15,7 +15,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="xunit" Version="2.7.1" /> <PackageReference Include="xunit" Version="2.7.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" /> <PackageReference Include="System.Reflection.TypeExtensions" Version="4.7.0" />

View File

@@ -1,20 +0,0 @@
name: 'Hello World'
description: 'Greet the world and record the time'
author: 'Test Corporation'
inputs:
greeting: # id of input
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
required: true
default: 'Hello'
deprecationMessage: 'This property has been deprecated'
entryPoint: # id of input
description: 'optional docker entrypoint overwrite.'
required: false
outputs:
time: # id of output
description: 'The time we did the greeting'
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
color: 'green' # optional, decorates the entry in the GitHub Marketplace
runs:
using: 'node24'
main: 'main.js'

View File

@@ -17,7 +17,7 @@ LAYOUT_DIR="$SCRIPT_DIR/../_layout"
DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x" DOWNLOAD_DIR="$SCRIPT_DIR/../_downloads/netcore2x"
PACKAGE_DIR="$SCRIPT_DIR/../_package" PACKAGE_DIR="$SCRIPT_DIR/../_package"
DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk" DOTNETSDK_ROOT="$SCRIPT_DIR/../_dotnetsdk"
DOTNETSDK_VERSION="8.0.413" DOTNETSDK_VERSION="8.0.410"
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION" DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
RUNNER_VERSION=$(cat runnerversion) RUNNER_VERSION=$(cat runnerversion)

View File

@@ -1,5 +1,5 @@
{ {
"sdk": { "sdk": {
"version": "8.0.413" "version": "8.0.410"
} }
} }

View File

@@ -1 +1 @@
2.328.0 2.325.0