Compare commits

...

20 Commits

Author SHA1 Message Date
Allan Guigou
39f6c643d3 Update release version to 2.331.0 2026-01-09 12:22:08 -05:00
Allan Guigou
3f43560cb9 Prepare runner release 2.331.0 (#4190) 2026-01-09 12:15:39 -05:00
dependabot[bot]
73f7dbb681 Bump Azure.Storage.Blobs from 12.26.0 to 12.27.0 (#4189)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-09 14:54:40 +00:00
dependabot[bot]
f554a6446d Bump typescript from 5.9.2 to 5.9.3 in /src/Misc/expressionFunc/hashFiles (#4184)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2026-01-07 18:52:44 +00:00
Tingluo Huang
bdceac4ab3 Allow hosted VM report job telemetry via .setup_info file. (#4186) 2026-01-07 13:27:22 -05:00
Tingluo Huang
3f1dd45172 Set ACTIONS_ORCHESTRATION_ID as env to actions. (#4178)
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: TingluoHuang <1750815+TingluoHuang@users.noreply.github.com>
2026-01-06 14:06:47 -05:00
dependabot[bot]
cf8f50b4d8 Bump actions/upload-artifact from 5 to 6 (#4157)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2025-12-21 08:31:15 +00:00
dependabot[bot]
2cf22c4858 Bump actions/download-artifact from 6 to 7 (#4155)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2025-12-18 23:52:35 +00:00
eric sciple
04d77df0c7 Cleanup feature flag actions_container_action_runner_temp (#4163) 2025-12-18 14:53:43 -06:00
Allan Guigou
651077689d Add support for case function (#4147)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-17 15:57:05 +00:00
Tingluo Huang
c96dcd4729 Bump docker image to use ubuntu 24.04 (#4018) 2025-12-12 13:38:45 -05:00
github-actions[bot]
4b0058f15c chore: update Node versions (#4149)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-12 14:57:21 +00:00
dependabot[bot]
87d1dfb798 Bump actions/checkout from 5 to 6 (#4130)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2025-12-12 11:00:47 +00:00
dependabot[bot]
c992a2b406 Bump actions/github-script from 7 to 8 (#4137)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Salman Chishti <salmanmkc@GitHub.com>
2025-12-12 10:54:38 +00:00
Tingluo Huang
b2204f1fab Ensure safe_sleep tries alternative approaches (#4146) 2025-12-11 09:53:50 -05:00
github-actions[bot]
f99c3e6ee8 chore: update Node versions (#4144)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-08 16:52:16 +00:00
Tingluo Huang
463496e4fb Fix regex for validating runner version format (#4136) 2025-11-24 10:30:33 -05:00
Tingluo Huang
3f9f6f3994 Update workflow around runner docker image. (#4133) 2025-11-24 08:59:01 -05:00
github-actions[bot]
221f65874f Update Docker to v29.0.2 and Buildx to v0.30.1 (#4135)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-11-24 11:37:28 +00:00
Nikola Jokic
9a21440691 Fix owner of /home/runner directory (#4132) 2025-11-21 16:15:17 -05:00
43 changed files with 419 additions and 105 deletions

View File

@@ -14,6 +14,9 @@ on:
paths-ignore: paths-ignore:
- '**.md' - '**.md'
permissions:
contents: read
jobs: jobs:
build: build:
strategy: strategy:
@@ -50,7 +53,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# Build runner layout # Build runner layout
- name: Build & Layout Release - name: Build & Layout Release
@@ -75,8 +78,53 @@ jobs:
# Upload runner package tar.gz/zip as artifact # Upload runner package tar.gz/zip as artifact
- name: Publish Artifact - name: Publish Artifact
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v6
with: with:
name: runner-package-${{ matrix.runtime }} name: runner-package-${{ matrix.runtime }}
path: | path: |
_package _package
docker:
strategy:
matrix:
os: [ ubuntu-latest, ubuntu-24.04-arm ]
include:
- os: ubuntu-latest
docker_platform: linux/amd64
- os: ubuntu-24.04-arm
docker_platform: linux/arm64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- name: Get latest runner version
id: latest_runner
uses: actions/github-script@v8
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const release = await github.rest.repos.getLatestRelease({
owner: 'actions',
repo: 'runner',
});
const version = release.data.tag_name.replace(/^v/, '');
core.setOutput('version', version);
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
uses: docker/build-push-action@v6
with:
context: ./images
load: true
platforms: ${{ matrix.docker_platform }}
tags: |
${{ github.sha }}:latest
build-args: |
RUNNER_VERSION=${{ steps.latest_runner.outputs.version }}
- name: Test Docker image
run: |
docker run --rm ${{ github.sha }}:latest ./run.sh --version

View File

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

View File

@@ -29,7 +29,7 @@ jobs:
npm-vulnerabilities: ${{ steps.check-versions.outputs.npm-vulnerabilities }} npm-vulnerabilities: ${{ steps.check-versions.outputs.npm-vulnerabilities }}
open-dependency-prs: ${{ steps.check-prs.outputs.open-dependency-prs }} open-dependency-prs: ${{ steps.check-prs.outputs.open-dependency-prs }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:

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@v6
- 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@v6
- name: Update Docker version - name: Update Docker version
shell: bash shell: bash

75
.github/workflows/docker-publish.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
name: Publish DockerImage from Release Branch
on:
workflow_dispatch:
inputs:
releaseBranch:
description: 'Release Branch (releases/mXXX)'
required: true
jobs:
publish-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
attestations: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository_owner }}/actions-runner
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
ref: ${{ github.event.inputs.releaseBranch }}
- name: Compute image version
id: image
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
const runnerVersion = fs.readFileSync('${{ github.workspace }}/releaseVersion', 'utf8').replace(/\n$/g, '');
console.log(`Using runner version ${runnerVersion}`);
if (!/^\d+\.\d+\.\d+$/.test(runnerVersion)) {
throw new Error(`Invalid runner version: ${runnerVersion}`);
}
core.setOutput('version', runnerVersion);
- name: Setup Docker buildx
uses: docker/setup-buildx-action@v3
- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v6
with:
context: ./images
platforms: |
linux/amd64
linux/arm64
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.image.outputs.version }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
build-args: |
RUNNER_VERSION=${{ steps.image.outputs.version }}
push: true
labels: |
org.opencontainers.image.source=${{github.server_url}}/${{github.repository}}
org.opencontainers.image.licenses=MIT
annotations: |
org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }}
- name: Generate attestation
uses: actions/attest-build-provenance@v3
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build-and-push.outputs.digest }}
push-to-registry: true

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

@@ -9,7 +9,7 @@ jobs:
update-node: update-node:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Get latest Node versions - name: Get latest Node versions
id: node-versions id: node-versions
run: | run: |

View File

@@ -7,7 +7,7 @@ jobs:
npm-audit-with-ts-fix: npm-audit-with-ts-fix:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6
with: with:

View File

@@ -9,7 +9,7 @@ jobs:
npm-audit: npm-audit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v6 uses: actions/setup-node@v6

View File

@@ -11,12 +11,12 @@ jobs:
if: startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main' if: startsWith(github.ref, 'refs/heads/releases/') || github.ref == 'refs/heads/main'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# Make sure ./releaseVersion match ./src/runnerversion # Make sure ./releaseVersion match ./src/runnerversion
# Query GitHub release ensure version is not used # Query GitHub release ensure version is not used
- name: Check version - name: Check version
uses: actions/github-script@v8.0.0 uses: actions/github-script@v8
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} github-token: ${{secrets.GITHUB_TOKEN}}
script: | script: |
@@ -86,7 +86,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# Build runner layout # Build runner layout
- name: Build & Layout Release - name: Build & Layout Release
@@ -118,7 +118,7 @@ jobs:
# Upload runner package tar.gz/zip as artifact. # Upload runner package tar.gz/zip as artifact.
- name: Publish Artifact - name: Publish Artifact
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v6
with: with:
name: runner-packages-${{ matrix.runtime }} name: runner-packages-${{ matrix.runtime }}
path: | path: |
@@ -129,41 +129,41 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
# 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@v6 uses: actions/download-artifact@v7
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@v6 uses: actions/download-artifact@v7
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@v6 uses: actions/download-artifact@v7
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@v6 uses: actions/download-artifact@v7
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@v6 uses: actions/download-artifact@v7
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@v6 uses: actions/download-artifact@v7
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@v6 uses: actions/download-artifact@v7
with: with:
name: runner-packages-linux-arm64 name: runner-packages-linux-arm64
path: ./ path: ./
@@ -171,7 +171,7 @@ jobs:
# Create ReleaseNote file # Create ReleaseNote file
- name: Create ReleaseNote - name: Create ReleaseNote
id: releaseNote id: releaseNote
uses: actions/github-script@v8.0.0 uses: actions/github-script@v8
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} github-token: ${{secrets.GITHUB_TOKEN}}
script: | script: |
@@ -296,11 +296,11 @@ 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@v6
- name: Compute image version - name: Compute image version
id: image id: image
uses: actions/github-script@v8.0.0 uses: actions/github-script@v8
with: with:
script: | script: |
const fs = require('fs'); const fs = require('fs');
@@ -334,8 +334,9 @@ jobs:
push: true push: true
labels: | labels: |
org.opencontainers.image.source=${{github.server_url}}/${{github.repository}} org.opencontainers.image.source=${{github.server_url}}/${{github.repository}}
org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }}
org.opencontainers.image.licenses=MIT org.opencontainers.image.licenses=MIT
annotations: |
org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }}
- name: Generate attestation - name: Generate attestation
uses: actions/attest-build-provenance@v3 uses: actions/attest-build-provenance@v3

View File

@@ -1,12 +1,12 @@
# Source: https://github.com/dotnet/dotnet-docker # Source: https://github.com/dotnet/dotnet-docker
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy AS build FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-noble AS build
ARG TARGETOS 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=29.0.1 ARG DOCKER_VERSION=29.0.2
ARG BUILDX_VERSION=0.30.0 ARG BUILDX_VERSION=0.30.1
RUN apt update -y && apt install curl unzip -y RUN apt update -y && apt install curl unzip -y
@@ -33,15 +33,15 @@ RUN export RUNNER_ARCH=${TARGETARCH} \
&& rm -rf docker.tgz \ && rm -rf docker.tgz \
&& mkdir -p /usr/local/lib/docker/cli-plugins \ && mkdir -p /usr/local/lib/docker/cli-plugins \
&& curl -fLo /usr/local/lib/docker/cli-plugins/docker-buildx \ && curl -fLo /usr/local/lib/docker/cli-plugins/docker-buildx \
"https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-${TARGETARCH}" \ "https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-${TARGETARCH}" \
&& chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx && chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-noble
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
ENV RUNNER_MANUALLY_TRAP_SIG=1 ENV RUNNER_MANUALLY_TRAP_SIG=1
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1 ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
ENV ImageOS=ubuntu22 ENV ImageOS=ubuntu24
# 'gpg-agent' and 'software-properties-common' are needed for the 'add-apt-repository' command that follows # 'gpg-agent' and 'software-properties-common' are needed for the 'add-apt-repository' command that follows
RUN apt update -y \ RUN apt update -y \
@@ -54,8 +54,6 @@ RUN add-apt-repository ppa:git-core/ppa \
&& apt install -y git \ && apt install -y git \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
WORKDIR /home/runner
RUN adduser --disabled-password --gecos "" --uid 1001 runner \ RUN adduser --disabled-password --gecos "" --uid 1001 runner \
&& groupadd docker --gid 123 \ && groupadd docker --gid 123 \
&& usermod -aG sudo runner \ && usermod -aG sudo runner \
@@ -64,6 +62,8 @@ RUN adduser --disabled-password --gecos "" --uid 1001 runner \
&& echo "Defaults env_keep += \"DEBIAN_FRONTEND\"" >> /etc/sudoers \ && echo "Defaults env_keep += \"DEBIAN_FRONTEND\"" >> /etc/sudoers \
&& chmod 777 /home/runner && chmod 777 /home/runner
WORKDIR /home/runner
COPY --chown=runner:docker --from=build /actions-runner . COPY --chown=runner:docker --from=build /actions-runner .
COPY --from=build /usr/local/lib/docker/cli-plugins/docker-buildx /usr/local/lib/docker/cli-plugins/docker-buildx COPY --from=build /usr/local/lib/docker/cli-plugins/docker-buildx /usr/local/lib/docker/cli-plugins/docker-buildx

View File

@@ -1,30 +1,27 @@
## What's Changed ## What's Changed
* Custom Image: Preflight checks by @lawrencegripper in https://github.com/actions/runner/pull/4081 * Fix owner of /home/runner directory by @nikola-jokic in https://github.com/actions/runner/pull/4132
* Update dotnet sdk to latest version @8.0.415 by @github-actions[bot] in https://github.com/actions/runner/pull/4080 * Update Docker to v29.0.2 and Buildx to v0.30.1 by @github-actions[bot] in https://github.com/actions/runner/pull/4135
* Link to an extant discussion category by @jsoref in https://github.com/actions/runner/pull/4084 * Update workflow around runner docker image. by @TingluoHuang in https://github.com/actions/runner/pull/4133
* Improve logic around decide IsHostedServer. by @TingluoHuang in https://github.com/actions/runner/pull/4086 * Fix regex for validating runner version format by @TingluoHuang in https://github.com/actions/runner/pull/4136
* chore: update Node versions by @github-actions[bot] in https://github.com/actions/runner/pull/4093 * chore: update Node versions by @github-actions[bot] in https://github.com/actions/runner/pull/4144
* Compare updated template evaluator by @ericsciple in https://github.com/actions/runner/pull/4092 * Ensure safe_sleep tries alternative approaches by @TingluoHuang in https://github.com/actions/runner/pull/4146
* fix(dockerfile): set more lenient permissions on /home/runner by @caxu-rh in https://github.com/actions/runner/pull/4083 * Bump actions/github-script from 7 to 8 by @dependabot[bot] in https://github.com/actions/runner/pull/4137
* Add support for libicu73-76 for newer Debian/Ubuntu versions by @lets-build-an-ocean in https://github.com/actions/runner/pull/4098 * Bump actions/checkout from 5 to 6 by @dependabot[bot] in https://github.com/actions/runner/pull/4130
* Bump actions/download-artifact from 5 to 6 by @dependabot[bot] in https://github.com/actions/runner/pull/4089 * chore: update Node versions by @github-actions[bot] in https://github.com/actions/runner/pull/4149
* Bump actions/upload-artifact from 4 to 5 by @dependabot[bot] in https://github.com/actions/runner/pull/4088 * Bump docker image to use ubuntu 24.04 by @TingluoHuang in https://github.com/actions/runner/pull/4018
* Bump Azure.Storage.Blobs from 12.25.1 to 12.26.0 by @dependabot[bot] in https://github.com/actions/runner/pull/4077 * Add support for case function by @AllanGuigou in https://github.com/actions/runner/pull/4147
* Only start runner after network is online by @dupondje in https://github.com/actions/runner/pull/4094 * Cleanup feature flag actions_container_action_runner_temp by @ericsciple in https://github.com/actions/runner/pull/4163
* Retry http error related to DNS resolution failure. by @TingluoHuang in https://github.com/actions/runner/pull/4110 * Bump actions/download-artifact from 6 to 7 by @dependabot[bot] in https://github.com/actions/runner/pull/4155
* Update Docker to v29.0.1 and Buildx to v0.30.0 by @github-actions[bot] in https://github.com/actions/runner/pull/4114 * Bump actions/upload-artifact from 5 to 6 by @dependabot[bot] in https://github.com/actions/runner/pull/4157
* chore: update Node versions by @github-actions[bot] in https://github.com/actions/runner/pull/4115 * Set ACTIONS_ORCHESTRATION_ID as env to actions. by @TingluoHuang in https://github.com/actions/runner/pull/4178
* Update dotnet sdk to latest version @8.0.416 by @github-actions[bot] in https://github.com/actions/runner/pull/4116 * Allow hosted VM report job telemetry via .setup_info file. by @TingluoHuang in https://github.com/actions/runner/pull/4186
* Compare updated workflow parser for ActionManifestManager by @ericsciple in https://github.com/actions/runner/pull/4111 * Bump typescript from 5.9.2 to 5.9.3 in /src/Misc/expressionFunc/hashFiles by @dependabot[bot] in https://github.com/actions/runner/pull/4184
* Bump npm pkg version for hashFiles. by @TingluoHuang in https://github.com/actions/runner/pull/4122 * Bump Azure.Storage.Blobs from 12.26.0 to 12.27.0 by @dependabot[bot] in https://github.com/actions/runner/pull/4189
## New Contributors ## New Contributors
* @lawrencegripper made their first contribution in https://github.com/actions/runner/pull/4081 * @AllanGuigou made their first contribution in https://github.com/actions/runner/pull/4147
* @caxu-rh made their first contribution in https://github.com/actions/runner/pull/4083
* @lets-build-an-ocean made their first contribution in https://github.com/actions/runner/pull/4098
* @dupondje made their first contribution in https://github.com/actions/runner/pull/4094
**Full Changelog**: https://github.com/actions/runner/compare/v2.329.0...v2.330.0 **Full Changelog**: https://github.com/actions/runner/compare/v2.330.0...v2.331.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.331.0

View File

@@ -23,7 +23,7 @@
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^15.5.0", "lint-staged": "^15.5.0",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"typescript": "^5.9.2" "typescript": "^5.9.3"
} }
}, },
"node_modules/@aashutoshrathi/word-wrap": { "node_modules/@aashutoshrathi/word-wrap": {
@@ -4439,9 +4439,9 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.9.2", "version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true, "dev": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
@@ -7643,9 +7643,9 @@
} }
}, },
"typescript": { "typescript": {
"version": "5.9.2", "version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true "dev": true
}, },
"unbox-primitive": { "unbox-primitive": {

View File

@@ -46,6 +46,6 @@
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^15.5.0", "lint-staged": "^15.5.0",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"typescript": "^5.9.2" "typescript": "^5.9.3"
} }
} }

View File

@@ -6,8 +6,8 @@ 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.5" NODE20_VERSION="20.19.6"
NODE24_VERSION="24.11.1" NODE24_VERSION="24.12.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

View File

@@ -1,5 +1,36 @@
#!/bin/bash #!/bin/bash
# try to use sleep if available
if [ -x "$(command -v sleep)" ]; then
sleep "$1"
exit 0
fi
# try to use ping if available
if [ -x "$(command -v ping)" ]; then
ping -c $(( $1 + 1 )) 127.0.0.1 > /dev/null
exit 0
fi
# try to use read -t from stdin/stdout/stderr if we are in bash
if [ -n "$BASH_VERSION" ]; then
if command -v read >/dev/null 2>&1; then
if [ -t 0 ]; then
read -t "$1" -u 0 || :;
exit 0
fi
if [ -t 1 ]; then
read -t "$1" -u 1 || :;
exit 0
fi
if [ -t 2 ]; then
read -t "$1" -u 2 || :;
exit 0
fi
fi
fi
# fallback to a busy wait
SECONDS=0 SECONDS=0
while [[ $SECONDS -lt $1 ]]; do while [[ $SECONDS -lt $1 ]]; do
: :

View File

@@ -169,23 +169,23 @@ namespace GitHub.Runner.Common
public static readonly string AllowRunnerContainerHooks = "DistributedTask.AllowRunnerContainerHooks"; public static readonly string AllowRunnerContainerHooks = "DistributedTask.AllowRunnerContainerHooks";
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";
public static readonly string ContainerActionRunnerTemp = "actions_container_action_runner_temp";
public static readonly string SnapshotPreflightHostedRunnerCheck = "actions_snapshot_preflight_hosted_runner_check"; public static readonly string SnapshotPreflightHostedRunnerCheck = "actions_snapshot_preflight_hosted_runner_check";
public static readonly string SnapshotPreflightImageGenPoolCheck = "actions_snapshot_preflight_image_gen_pool_check"; public static readonly string SnapshotPreflightImageGenPoolCheck = "actions_snapshot_preflight_image_gen_pool_check";
public static readonly string CompareWorkflowParser = "actions_runner_compare_workflow_parser"; public static readonly string CompareWorkflowParser = "actions_runner_compare_workflow_parser";
public static readonly string SetOrchestrationIdEnvForActions = "actions_set_orchestration_id_env_for_actions";
} }
// Node version migration related constants // Node version migration related constants
public static class NodeMigration public static class NodeMigration
{ {
// Node versions // Node versions
public static readonly string Node20 = "node20"; public static readonly string Node20 = "node20";
public static readonly string Node24 = "node24"; public static readonly string Node24 = "node24";
// Environment variables for controlling node version selection // Environment variables for controlling node version selection
public static readonly string ForceNode24Variable = "FORCE_JAVASCRIPT_ACTIONS_TO_NODE24"; public static readonly string ForceNode24Variable = "FORCE_JAVASCRIPT_ACTIONS_TO_NODE24";
public static readonly string AllowUnsecureNodeVersionVariable = "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION"; public static readonly string AllowUnsecureNodeVersionVariable = "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION";
// Feature flags for controlling the migration phases // Feature flags for controlling the migration phases
public static readonly string UseNode24ByDefaultFlag = "actions.runner.usenode24bydefault"; public static readonly string UseNode24ByDefaultFlag = "actions.runner.usenode24bydefault";
public static readonly string RequireNode24Flag = "actions.runner.requirenode24"; public static readonly string RequireNode24Flag = "actions.runner.requirenode24";

View File

@@ -316,6 +316,7 @@ namespace GitHub.Runner.Worker
Schema = _actionManifestSchema, Schema = _actionManifestSchema,
// TODO: Switch to real tracewriter for cutover // TODO: Switch to real tracewriter for cutover
TraceWriter = new GitHub.Actions.WorkflowParser.ObjectTemplating.EmptyTraceWriter(), TraceWriter = new GitHub.Actions.WorkflowParser.ObjectTemplating.EmptyTraceWriter(),
AllowCaseFunction = false,
}; };
// Expression values from execution context // Expression values from execution context

View File

@@ -315,6 +315,7 @@ namespace GitHub.Runner.Worker
maxBytes: 10 * 1024 * 1024), maxBytes: 10 * 1024 * 1024),
Schema = _actionManifestSchema, Schema = _actionManifestSchema,
TraceWriter = executionContext.ToTemplateTraceWriter(), TraceWriter = executionContext.ToTemplateTraceWriter(),
AllowCaseFunction = false,
}; };
// Expression values from execution context // Expression values from execution context

View File

@@ -11,10 +11,5 @@ namespace GitHub.Runner.Worker
var isContainerHooksPathSet = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(Constants.Hooks.ContainerHooksPath)); var isContainerHooksPathSet = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(Constants.Hooks.ContainerHooksPath));
return isContainerHookFeatureFlagSet && isContainerHooksPathSet; return isContainerHookFeatureFlagSet && isContainerHooksPathSet;
} }
public static bool IsContainerActionRunnerTempEnabled(Variables variables)
{
return variables?.GetBoolean(Constants.Runner.Features.ContainerActionRunnerTemp) ?? false;
}
} }
} }

View File

@@ -191,19 +191,13 @@ namespace GitHub.Runner.Worker.Handlers
ArgUtil.Directory(tempWorkflowDirectory, nameof(tempWorkflowDirectory)); ArgUtil.Directory(tempWorkflowDirectory, nameof(tempWorkflowDirectory));
container.MountVolumes.Add(new MountVolume("/var/run/docker.sock", "/var/run/docker.sock")); container.MountVolumes.Add(new MountVolume("/var/run/docker.sock", "/var/run/docker.sock"));
if (FeatureManager.IsContainerActionRunnerTempEnabled(ExecutionContext.Global.Variables)) container.MountVolumes.Add(new MountVolume(tempDirectory, "/github/runner_temp"));
{
container.MountVolumes.Add(new MountVolume(tempDirectory, "/github/runner_temp"));
}
container.MountVolumes.Add(new MountVolume(tempHomeDirectory, "/github/home")); container.MountVolumes.Add(new MountVolume(tempHomeDirectory, "/github/home"));
container.MountVolumes.Add(new MountVolume(tempWorkflowDirectory, "/github/workflow")); container.MountVolumes.Add(new MountVolume(tempWorkflowDirectory, "/github/workflow"));
container.MountVolumes.Add(new MountVolume(tempFileCommandDirectory, "/github/file_commands")); container.MountVolumes.Add(new MountVolume(tempFileCommandDirectory, "/github/file_commands"));
container.MountVolumes.Add(new MountVolume(defaultWorkingDirectory, "/github/workspace")); container.MountVolumes.Add(new MountVolume(defaultWorkingDirectory, "/github/workspace"));
if (FeatureManager.IsContainerActionRunnerTempEnabled(ExecutionContext.Global.Variables)) container.AddPathTranslateMapping(tempDirectory, "/github/runner_temp");
{
container.AddPathTranslateMapping(tempDirectory, "/github/runner_temp");
}
container.AddPathTranslateMapping(tempHomeDirectory, "/github/home"); container.AddPathTranslateMapping(tempHomeDirectory, "/github/home");
container.AddPathTranslateMapping(tempWorkflowDirectory, "/github/workflow"); container.AddPathTranslateMapping(tempWorkflowDirectory, "/github/workflow");
container.AddPathTranslateMapping(tempFileCommandDirectory, "/github/file_commands"); container.AddPathTranslateMapping(tempFileCommandDirectory, "/github/file_commands");
@@ -245,6 +239,14 @@ namespace GitHub.Runner.Worker.Handlers
Environment["ACTIONS_RESULTS_URL"] = resultsUrl; Environment["ACTIONS_RESULTS_URL"] = resultsUrl;
} }
if (ExecutionContext.Global.Variables.GetBoolean(Constants.Runner.Features.SetOrchestrationIdEnvForActions) ?? false)
{
if (ExecutionContext.Global.Variables.TryGetValue(Constants.Variables.System.OrchestrationId, out var orchestrationId) && !string.IsNullOrEmpty(orchestrationId))
{
Environment["ACTIONS_ORCHESTRATION_ID"] = orchestrationId;
}
}
foreach (var variable in this.Environment) foreach (var variable in this.Environment)
{ {
container.ContainerEnvironmentVariables[variable.Key] = container.TranslateToContainerPath(variable.Value); container.ContainerEnvironmentVariables[variable.Key] = container.TranslateToContainerPath(variable.Value);

View File

@@ -77,6 +77,14 @@ namespace GitHub.Runner.Worker.Handlers
Environment["ACTIONS_CACHE_SERVICE_V2"] = bool.TrueString; Environment["ACTIONS_CACHE_SERVICE_V2"] = bool.TrueString;
} }
if (ExecutionContext.Global.Variables.GetBoolean(Constants.Runner.Features.SetOrchestrationIdEnvForActions) ?? false)
{
if (ExecutionContext.Global.Variables.TryGetValue(Constants.Variables.System.OrchestrationId, out var orchestrationId) && !string.IsNullOrEmpty(orchestrationId))
{
Environment["ACTIONS_ORCHESTRATION_ID"] = orchestrationId;
}
}
// Resolve the target script. // Resolve the target script.
string target = null; string target = null;
if (stage == ActionRunStage.Main) if (stage == ActionRunStage.Main)

View File

@@ -318,6 +318,14 @@ namespace GitHub.Runner.Worker.Handlers
Environment["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = systemConnection.Authorization.Parameters[EndpointAuthorizationParameters.AccessToken]; Environment["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] = systemConnection.Authorization.Parameters[EndpointAuthorizationParameters.AccessToken];
} }
if (ExecutionContext.Global.Variables.GetBoolean(Constants.Runner.Features.SetOrchestrationIdEnvForActions) ?? false)
{
if (ExecutionContext.Global.Variables.TryGetValue(Constants.Variables.System.OrchestrationId, out var orchestrationId) && !string.IsNullOrEmpty(orchestrationId))
{
Environment["ACTIONS_ORCHESTRATION_ID"] = orchestrationId;
}
}
ExecutionContext.Debug($"{fileName} {arguments}"); ExecutionContext.Debug($"{fileName} {arguments}");
Inputs.TryGetValue("standardInInput", out var standardInInput); Inputs.TryGetValue("standardInInput", out var standardInInput);

View File

@@ -112,6 +112,13 @@ namespace GitHub.Runner.Worker
groupName = "Machine Setup Info"; groupName = "Machine Setup Info";
} }
// not output internal groups
if (groupName.StartsWith("_internal_", StringComparison.OrdinalIgnoreCase))
{
jobContext.Global.JobTelemetry.Add(new JobTelemetry() { Type = JobTelemetryType.General, Message = info.Detail });
continue;
}
context.Output($"##[group]{groupName}"); context.Output($"##[group]{groupName}");
var multiLines = info.Detail.Replace("\r\n", "\n").TrimEnd('\n').Split('\n'); var multiLines = info.Detail.Replace("\r\n", "\n").TrimEnd('\n').Split('\n');
foreach (var line in multiLines) foreach (var line in multiLines)

View File

@@ -9,6 +9,7 @@ namespace GitHub.DistributedTask.Expressions2
{ {
static ExpressionConstants() static ExpressionConstants()
{ {
AddFunction<Case>("case", 3, Byte.MaxValue);
AddFunction<Contains>("contains", 2, 2); AddFunction<Contains>("contains", 2, 2);
AddFunction<EndsWith>("endsWith", 2, 2); AddFunction<EndsWith>("endsWith", 2, 2);
AddFunction<Format>("format", 1, Byte.MaxValue); AddFunction<Format>("format", 1, Byte.MaxValue);

View File

@@ -17,9 +17,10 @@ namespace GitHub.DistributedTask.Expressions2
String expression, String expression,
ITraceWriter trace, ITraceWriter trace,
IEnumerable<INamedValueInfo> namedValues, IEnumerable<INamedValueInfo> namedValues,
IEnumerable<IFunctionInfo> functions) IEnumerable<IFunctionInfo> functions,
Boolean allowCaseFunction = true)
{ {
var context = new ParseContext(expression, trace, namedValues, functions); var context = new ParseContext(expression, trace, namedValues, functions, allowCaseFunction);
context.Trace.Info($"Parsing expression: <{expression}>"); context.Trace.Info($"Parsing expression: <{expression}>");
return CreateTree(context); return CreateTree(context);
} }
@@ -349,6 +350,10 @@ namespace GitHub.DistributedTask.Expressions2
{ {
throw new ParseException(ParseExceptionKind.TooManyParameters, token: @operator, expression: context.Expression); throw new ParseException(ParseExceptionKind.TooManyParameters, token: @operator, expression: context.Expression);
} }
else if (functionInfo.Name.Equals("case", StringComparison.OrdinalIgnoreCase) && function.Parameters.Count % 2 == 0)
{
throw new ParseException(ParseExceptionKind.EvenParameters, token: @operator, expression: context.Expression);
}
} }
/// <summary> /// <summary>
@@ -411,6 +416,12 @@ namespace GitHub.DistributedTask.Expressions2
String name, String name,
out IFunctionInfo functionInfo) out IFunctionInfo functionInfo)
{ {
if (String.Equals(name, "case", StringComparison.OrdinalIgnoreCase) && !context.AllowCaseFunction)
{
functionInfo = null;
return false;
}
return ExpressionConstants.WellKnownFunctions.TryGetValue(name, out functionInfo) || return ExpressionConstants.WellKnownFunctions.TryGetValue(name, out functionInfo) ||
context.ExtensionFunctions.TryGetValue(name, out functionInfo); context.ExtensionFunctions.TryGetValue(name, out functionInfo);
} }
@@ -418,6 +429,7 @@ namespace GitHub.DistributedTask.Expressions2
private sealed class ParseContext private sealed class ParseContext
{ {
public Boolean AllowUnknownKeywords; public Boolean AllowUnknownKeywords;
public Boolean AllowCaseFunction;
public readonly String Expression; public readonly String Expression;
public readonly Dictionary<String, IFunctionInfo> ExtensionFunctions = new Dictionary<String, IFunctionInfo>(StringComparer.OrdinalIgnoreCase); public readonly Dictionary<String, IFunctionInfo> ExtensionFunctions = new Dictionary<String, IFunctionInfo>(StringComparer.OrdinalIgnoreCase);
public readonly Dictionary<String, INamedValueInfo> ExtensionNamedValues = new Dictionary<String, INamedValueInfo>(StringComparer.OrdinalIgnoreCase); public readonly Dictionary<String, INamedValueInfo> ExtensionNamedValues = new Dictionary<String, INamedValueInfo>(StringComparer.OrdinalIgnoreCase);
@@ -433,7 +445,8 @@ namespace GitHub.DistributedTask.Expressions2
ITraceWriter trace, ITraceWriter trace,
IEnumerable<INamedValueInfo> namedValues, IEnumerable<INamedValueInfo> namedValues,
IEnumerable<IFunctionInfo> functions, IEnumerable<IFunctionInfo> functions,
Boolean allowUnknownKeywords = false) Boolean allowUnknownKeywords = false,
Boolean allowCaseFunction = true)
{ {
Expression = expression ?? String.Empty; Expression = expression ?? String.Empty;
if (Expression.Length > ExpressionConstants.MaxLength) if (Expression.Length > ExpressionConstants.MaxLength)
@@ -454,6 +467,7 @@ namespace GitHub.DistributedTask.Expressions2
LexicalAnalyzer = new LexicalAnalyzer(Expression); LexicalAnalyzer = new LexicalAnalyzer(Expression);
AllowUnknownKeywords = allowUnknownKeywords; AllowUnknownKeywords = allowUnknownKeywords;
AllowCaseFunction = allowCaseFunction;
} }
private class NoOperationTraceWriter : ITraceWriter private class NoOperationTraceWriter : ITraceWriter

View File

@@ -29,6 +29,9 @@ namespace GitHub.DistributedTask.Expressions2
case ParseExceptionKind.TooManyParameters: case ParseExceptionKind.TooManyParameters:
description = "Too many parameters supplied"; description = "Too many parameters supplied";
break; break;
case ParseExceptionKind.EvenParameters:
description = "Even number of parameters supplied, requires an odd number of parameters";
break;
case ParseExceptionKind.UnexpectedEndOfExpression: case ParseExceptionKind.UnexpectedEndOfExpression:
description = "Unexpected end of expression"; description = "Unexpected end of expression";
break; break;

View File

@@ -6,6 +6,7 @@
ExceededMaxLength, ExceededMaxLength,
TooFewParameters, TooFewParameters,
TooManyParameters, TooManyParameters,
EvenParameters,
UnexpectedEndOfExpression, UnexpectedEndOfExpression,
UnexpectedSymbol, UnexpectedSymbol,
UnrecognizedFunction, UnrecognizedFunction,

View File

@@ -0,0 +1,45 @@
#nullable disable // Consider removing in the future to minimize likelihood of NullReferenceException; refer https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references
using System;
using GitHub.Actions.Expressions.Data;
namespace GitHub.DistributedTask.Expressions2.Sdk.Functions
{
internal sealed class Case : Function
{
protected sealed override Object EvaluateCore(
EvaluationContext context,
out ResultMemory resultMemory)
{
resultMemory = null;
// Validate argument count - must be odd (pairs of predicate-result plus default)
if (Parameters.Count % 2 == 0)
{
throw new InvalidOperationException("case requires an odd number of arguments");
}
// Evaluate predicate-result pairs
for (var i = 0; i < Parameters.Count - 1; i += 2)
{
var predicate = Parameters[i].Evaluate(context);
// Predicate must be a boolean
if (predicate.Kind != ValueKind.Boolean)
{
throw new InvalidOperationException("case predicate must evaluate to a boolean value");
}
// If predicate is true, return the corresponding result
if ((Boolean)predicate.Value)
{
var result = Parameters[i + 1].Evaluate(context);
return result.Value;
}
}
// No predicate matched, return default (last argument)
var defaultResult = Parameters[Parameters.Count - 1].Evaluate(context);
return defaultResult.Value;
}
}
}

View File

@@ -86,6 +86,12 @@ namespace GitHub.DistributedTask.ObjectTemplating
internal ITraceWriter TraceWriter { get; set; } internal ITraceWriter TraceWriter { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the case expression function is allowed.
/// Defaults to true. Set to false to disable the case function.
/// </summary>
internal Boolean AllowCaseFunction { get; set; } = true;
private IDictionary<String, Int32> FileIds private IDictionary<String, Int32> FileIds
{ {
get get

View File

@@ -57,7 +57,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
var originalBytes = context.Memory.CurrentBytes; var originalBytes = context.Memory.CurrentBytes;
try try
{ {
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions, allowCaseFunction: context.AllowCaseFunction);
var options = new EvaluationOptions var options = new EvaluationOptions
{ {
MaxMemory = context.Memory.MaxBytes, MaxMemory = context.Memory.MaxBytes,
@@ -94,7 +94,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
var originalBytes = context.Memory.CurrentBytes; var originalBytes = context.Memory.CurrentBytes;
try try
{ {
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions, allowCaseFunction: context.AllowCaseFunction);
var options = new EvaluationOptions var options = new EvaluationOptions
{ {
MaxMemory = context.Memory.MaxBytes, MaxMemory = context.Memory.MaxBytes,
@@ -123,7 +123,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
var originalBytes = context.Memory.CurrentBytes; var originalBytes = context.Memory.CurrentBytes;
try try
{ {
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions, allowCaseFunction: context.AllowCaseFunction);
var options = new EvaluationOptions var options = new EvaluationOptions
{ {
MaxMemory = context.Memory.MaxBytes, MaxMemory = context.Memory.MaxBytes,
@@ -152,7 +152,7 @@ namespace GitHub.DistributedTask.ObjectTemplating.Tokens
var originalBytes = context.Memory.CurrentBytes; var originalBytes = context.Memory.CurrentBytes;
try try
{ {
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions, allowCaseFunction: context.AllowCaseFunction);
var options = new EvaluationOptions var options = new EvaluationOptions
{ {
MaxMemory = context.Memory.MaxBytes, MaxMemory = context.Memory.MaxBytes,

View File

@@ -663,7 +663,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
var node = default(ExpressionNode); var node = default(ExpressionNode);
try try
{ {
node = expressionParser.CreateTree(condition, null, namedValues, functions) as ExpressionNode; node = expressionParser.CreateTree(condition, null, namedValues, functions, allowCaseFunction: context.AllowCaseFunction) as ExpressionNode;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -10,6 +10,7 @@ namespace GitHub.Actions.Expressions
{ {
static ExpressionConstants() static ExpressionConstants()
{ {
AddFunction<Case>("case", 3, Byte.MaxValue);
AddFunction<Contains>("contains", 2, 2); AddFunction<Contains>("contains", 2, 2);
AddFunction<EndsWith>("endsWith", 2, 2); AddFunction<EndsWith>("endsWith", 2, 2);
AddFunction<Format>("format", 1, Byte.MaxValue); AddFunction<Format>("format", 1, Byte.MaxValue);

View File

@@ -17,9 +17,10 @@ namespace GitHub.Actions.Expressions
String expression, String expression,
ITraceWriter trace, ITraceWriter trace,
IEnumerable<INamedValueInfo> namedValues, IEnumerable<INamedValueInfo> namedValues,
IEnumerable<IFunctionInfo> functions) IEnumerable<IFunctionInfo> functions,
Boolean allowCaseFunction = true)
{ {
var context = new ParseContext(expression, trace, namedValues, functions); var context = new ParseContext(expression, trace, namedValues, functions, allowCaseFunction: allowCaseFunction);
context.Trace.Info($"Parsing expression: <{expression}>"); context.Trace.Info($"Parsing expression: <{expression}>");
return CreateTree(context); return CreateTree(context);
} }
@@ -349,6 +350,10 @@ namespace GitHub.Actions.Expressions
{ {
throw new ParseException(ParseExceptionKind.TooManyParameters, token: @operator, expression: context.Expression); throw new ParseException(ParseExceptionKind.TooManyParameters, token: @operator, expression: context.Expression);
} }
else if (functionInfo.Name.Equals("case", StringComparison.OrdinalIgnoreCase) && function.Parameters.Count % 2 == 0)
{
throw new ParseException(ParseExceptionKind.EvenParameters, token: @operator, expression: context.Expression);
}
} }
/// <summary> /// <summary>
@@ -411,6 +416,12 @@ namespace GitHub.Actions.Expressions
String name, String name,
out IFunctionInfo functionInfo) out IFunctionInfo functionInfo)
{ {
if (String.Equals(name, "case", StringComparison.OrdinalIgnoreCase) && !context.AllowCaseFunction)
{
functionInfo = null;
return false;
}
return ExpressionConstants.WellKnownFunctions.TryGetValue(name, out functionInfo) || return ExpressionConstants.WellKnownFunctions.TryGetValue(name, out functionInfo) ||
context.ExtensionFunctions.TryGetValue(name, out functionInfo); context.ExtensionFunctions.TryGetValue(name, out functionInfo);
} }
@@ -418,6 +429,7 @@ namespace GitHub.Actions.Expressions
private sealed class ParseContext private sealed class ParseContext
{ {
public Boolean AllowUnknownKeywords; public Boolean AllowUnknownKeywords;
public Boolean AllowCaseFunction;
public readonly String Expression; public readonly String Expression;
public readonly Dictionary<String, IFunctionInfo> ExtensionFunctions = new Dictionary<String, IFunctionInfo>(StringComparer.OrdinalIgnoreCase); public readonly Dictionary<String, IFunctionInfo> ExtensionFunctions = new Dictionary<String, IFunctionInfo>(StringComparer.OrdinalIgnoreCase);
public readonly Dictionary<String, INamedValueInfo> ExtensionNamedValues = new Dictionary<String, INamedValueInfo>(StringComparer.OrdinalIgnoreCase); public readonly Dictionary<String, INamedValueInfo> ExtensionNamedValues = new Dictionary<String, INamedValueInfo>(StringComparer.OrdinalIgnoreCase);
@@ -433,7 +445,8 @@ namespace GitHub.Actions.Expressions
ITraceWriter trace, ITraceWriter trace,
IEnumerable<INamedValueInfo> namedValues, IEnumerable<INamedValueInfo> namedValues,
IEnumerable<IFunctionInfo> functions, IEnumerable<IFunctionInfo> functions,
Boolean allowUnknownKeywords = false) Boolean allowUnknownKeywords = false,
Boolean allowCaseFunction = true)
{ {
Expression = expression ?? String.Empty; Expression = expression ?? String.Empty;
if (Expression.Length > ExpressionConstants.MaxLength) if (Expression.Length > ExpressionConstants.MaxLength)
@@ -454,6 +467,7 @@ namespace GitHub.Actions.Expressions
LexicalAnalyzer = new LexicalAnalyzer(Expression); LexicalAnalyzer = new LexicalAnalyzer(Expression);
AllowUnknownKeywords = allowUnknownKeywords; AllowUnknownKeywords = allowUnknownKeywords;
AllowCaseFunction = allowCaseFunction;
} }
private class NoOperationTraceWriter : ITraceWriter private class NoOperationTraceWriter : ITraceWriter
@@ -468,4 +482,4 @@ namespace GitHub.Actions.Expressions
} }
} }
} }
} }

View File

@@ -29,6 +29,9 @@ namespace GitHub.Actions.Expressions
case ParseExceptionKind.TooManyParameters: case ParseExceptionKind.TooManyParameters:
description = "Too many parameters supplied"; description = "Too many parameters supplied";
break; break;
case ParseExceptionKind.EvenParameters:
description = "Even number of parameters supplied, requires an odd number of parameters";
break;
case ParseExceptionKind.UnexpectedEndOfExpression: case ParseExceptionKind.UnexpectedEndOfExpression:
description = "Unexpected end of expression"; description = "Unexpected end of expression";
break; break;

View File

@@ -6,6 +6,7 @@ namespace GitHub.Actions.Expressions
ExceededMaxLength, ExceededMaxLength,
TooFewParameters, TooFewParameters,
TooManyParameters, TooManyParameters,
EvenParameters,
UnexpectedEndOfExpression, UnexpectedEndOfExpression,
UnexpectedSymbol, UnexpectedSymbol,
UnrecognizedFunction, UnrecognizedFunction,

View File

@@ -0,0 +1,45 @@
#nullable disable // Consider removing in the future to minimize likelihood of NullReferenceException; refer https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references
using System;
using GitHub.Actions.Expressions.Data;
namespace GitHub.Actions.Expressions.Sdk.Functions
{
internal sealed class Case : Function
{
protected sealed override Object EvaluateCore(
EvaluationContext context,
out ResultMemory resultMemory)
{
resultMemory = null;
// Validate argument count - must be odd (pairs of predicate-result plus default)
if (Parameters.Count % 2 == 0)
{
throw new InvalidOperationException("case requires an odd number of arguments");
}
// Evaluate predicate-result pairs
for (var i = 0; i < Parameters.Count - 1; i += 2)
{
var predicate = Parameters[i].Evaluate(context);
// Predicate must be a boolean
if (predicate.Kind != ValueKind.Boolean)
{
throw new InvalidOperationException("case predicate must evaluate to a boolean value");
}
// If predicate is true, return the corresponding result
if ((Boolean)predicate.Value)
{
var result = Parameters[i + 1].Evaluate(context);
return result.Value;
}
}
// No predicate matched, return default (last argument)
var defaultResult = Parameters[Parameters.Count - 1].Evaluate(context);
return defaultResult.Value;
}
}
}

View File

@@ -18,7 +18,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.26.0" /> <PackageReference Include="Azure.Storage.Blobs" Version="12.27.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

@@ -1775,7 +1775,7 @@ namespace GitHub.Actions.WorkflowParser.Conversion
var node = default(ExpressionNode); var node = default(ExpressionNode);
try try
{ {
node = expressionParser.CreateTree(condition, null, namedValues, functions) as ExpressionNode; node = expressionParser.CreateTree(condition, null, namedValues, functions, allowCaseFunction: context.AllowCaseFunction) as ExpressionNode;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -113,6 +113,12 @@ namespace GitHub.Actions.WorkflowParser.ObjectTemplating
/// </summary> /// </summary>
internal Boolean StrictJsonParsing { get; set; } internal Boolean StrictJsonParsing { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the case expression function is allowed.
/// Defaults to true. Set to false to disable the case function.
/// </summary>
internal Boolean AllowCaseFunction { get; set; } = true;
internal ITraceWriter TraceWriter { get; set; } internal ITraceWriter TraceWriter { get; set; }
private IDictionary<String, Int32> FileIds private IDictionary<String, Int32> FileIds

View File

@@ -55,7 +55,7 @@ namespace GitHub.Actions.WorkflowParser.ObjectTemplating.Tokens
var originalBytes = context.Memory.CurrentBytes; var originalBytes = context.Memory.CurrentBytes;
try try
{ {
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions, allowCaseFunction: context.AllowCaseFunction);
var options = new EvaluationOptions var options = new EvaluationOptions
{ {
MaxMemory = context.Memory.MaxBytes, MaxMemory = context.Memory.MaxBytes,
@@ -93,7 +93,7 @@ namespace GitHub.Actions.WorkflowParser.ObjectTemplating.Tokens
var originalBytes = context.Memory.CurrentBytes; var originalBytes = context.Memory.CurrentBytes;
try try
{ {
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions, allowCaseFunction: context.AllowCaseFunction);
var options = new EvaluationOptions var options = new EvaluationOptions
{ {
MaxMemory = context.Memory.MaxBytes, MaxMemory = context.Memory.MaxBytes,
@@ -123,7 +123,7 @@ namespace GitHub.Actions.WorkflowParser.ObjectTemplating.Tokens
var originalBytes = context.Memory.CurrentBytes; var originalBytes = context.Memory.CurrentBytes;
try try
{ {
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions, allowCaseFunction: context.AllowCaseFunction);
var options = new EvaluationOptions var options = new EvaluationOptions
{ {
MaxMemory = context.Memory.MaxBytes, MaxMemory = context.Memory.MaxBytes,
@@ -153,7 +153,7 @@ namespace GitHub.Actions.WorkflowParser.ObjectTemplating.Tokens
var originalBytes = context.Memory.CurrentBytes; var originalBytes = context.Memory.CurrentBytes;
try try
{ {
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions, allowCaseFunction: context.AllowCaseFunction);
var options = new EvaluationOptions var options = new EvaluationOptions
{ {
MaxMemory = context.Memory.MaxBytes, MaxMemory = context.Memory.MaxBytes,

View File

@@ -1 +1 @@
2.330.0 2.331.0