Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
b39c237989 Bump actions/checkout from 5 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-21 04:17:02 +00:00
24 changed files with 31 additions and 328 deletions

View File

@@ -14,9 +14,6 @@ on:
paths-ignore: paths-ignore:
- '**.md' - '**.md'
permissions:
contents: read
jobs: jobs:
build: build:
strategy: strategy:
@@ -83,48 +80,3 @@ jobs:
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

@@ -1,75 +0,0 @@
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

@@ -16,7 +16,7 @@ jobs:
# 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 uses: actions/github-script@v8.0.0
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} github-token: ${{secrets.GITHUB_TOKEN}}
script: | script: |
@@ -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 uses: actions/github-script@v8.0.0
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} github-token: ${{secrets.GITHUB_TOKEN}}
script: | script: |
@@ -300,7 +300,7 @@ jobs:
- name: Compute image version - name: Compute image version
id: image id: image
uses: actions/github-script@v8 uses: actions/github-script@v8.0.0
with: with:
script: | script: |
const fs = require('fs'); const fs = require('fs');
@@ -334,9 +334,8 @@ 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.licenses=MIT
annotations: |
org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }} org.opencontainers.image.description=https://github.com/actions/runner/releases/tag/v${{ steps.image.outputs.version }}
org.opencontainers.image.licenses=MIT
- 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-noble AS build FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy AS build
ARG TARGETOS ARG TARGETOS
ARG TARGETARCH ARG TARGETARCH
ARG RUNNER_VERSION ARG RUNNER_VERSION
ARG RUNNER_CONTAINER_HOOKS_VERSION=0.7.0 ARG RUNNER_CONTAINER_HOOKS_VERSION=0.7.0
ARG DOCKER_VERSION=29.0.2 ARG DOCKER_VERSION=29.0.1
ARG BUILDX_VERSION=0.30.1 ARG BUILDX_VERSION=0.30.0
RUN apt update -y && apt install curl unzip -y RUN apt update -y && apt install curl unzip -y
@@ -36,12 +36,12 @@ RUN export RUNNER_ARCH=${TARGETARCH} \
"https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-${TARGETARCH}" \ "https://github.com/docker/buildx/releases/download/v${BUILDX_VERSION}/buildx-v${BUILDX_VERSION}.linux-${TARGETARCH}" \
&& chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx && chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-noble FROM mcr.microsoft.com/dotnet/runtime-deps:8.0-jammy
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
ENV RUNNER_MANUALLY_TRAP_SIG=1 ENV RUNNER_MANUALLY_TRAP_SIG=1
ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1 ENV ACTIONS_RUNNER_PRINT_LOG_TO_STDOUT=1
ENV ImageOS=ubuntu24 ENV ImageOS=ubuntu22
# 'gpg-agent' and 'software-properties-common' are needed for the 'add-apt-repository' command that follows # 'gpg-agent' and 'software-properties-common' are needed for the 'add-apt-repository' command that follows
RUN apt update -y \ RUN apt update -y \
@@ -54,6 +54,8 @@ 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 \
@@ -62,8 +64,6 @@ 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

@@ -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.6" NODE20_VERSION="20.19.5"
NODE24_VERSION="24.12.0" NODE24_VERSION="24.11.1"
get_abs_path() { get_abs_path() {
# exploits the fact that pwd will print abs path when no args # exploits the fact that pwd will print abs path when no args

View File

@@ -1,36 +1,5 @@
#!/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

@@ -316,7 +316,6 @@ 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,7 +315,6 @@ 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

@@ -9,7 +9,6 @@ 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,10 +17,9 @@ 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, allowCaseFunction); var context = new ParseContext(expression, trace, namedValues, functions);
context.Trace.Info($"Parsing expression: <{expression}>"); context.Trace.Info($"Parsing expression: <{expression}>");
return CreateTree(context); return CreateTree(context);
} }
@@ -350,10 +349,6 @@ 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>
@@ -416,12 +411,6 @@ 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);
} }
@@ -429,7 +418,6 @@ 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);
@@ -445,8 +433,7 @@ 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)
@@ -467,7 +454,6 @@ 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,9 +29,6 @@ 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,7 +6,6 @@
ExceededMaxLength, ExceededMaxLength,
TooFewParameters, TooFewParameters,
TooManyParameters, TooManyParameters,
EvenParameters,
UnexpectedEndOfExpression, UnexpectedEndOfExpression,
UnexpectedSymbol, UnexpectedSymbol,
UnrecognizedFunction, UnrecognizedFunction,

View File

@@ -1,45 +0,0 @@
#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,12 +86,6 @@ 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, allowCaseFunction: context.AllowCaseFunction); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
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, allowCaseFunction: context.AllowCaseFunction); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
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, allowCaseFunction: context.AllowCaseFunction); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
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, allowCaseFunction: context.AllowCaseFunction); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
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, allowCaseFunction: context.AllowCaseFunction) as ExpressionNode; node = expressionParser.CreateTree(condition, null, namedValues, functions) as ExpressionNode;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -10,7 +10,6 @@ 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,10 +17,9 @@ 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, allowCaseFunction: allowCaseFunction); var context = new ParseContext(expression, trace, namedValues, functions);
context.Trace.Info($"Parsing expression: <{expression}>"); context.Trace.Info($"Parsing expression: <{expression}>");
return CreateTree(context); return CreateTree(context);
} }
@@ -350,10 +349,6 @@ 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>
@@ -416,12 +411,6 @@ 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);
} }
@@ -429,7 +418,6 @@ 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);
@@ -445,8 +433,7 @@ 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)
@@ -467,7 +454,6 @@ 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

View File

@@ -29,9 +29,6 @@ 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,7 +6,6 @@ namespace GitHub.Actions.Expressions
ExceededMaxLength, ExceededMaxLength,
TooFewParameters, TooFewParameters,
TooManyParameters, TooManyParameters,
EvenParameters,
UnexpectedEndOfExpression, UnexpectedEndOfExpression,
UnexpectedSymbol, UnexpectedSymbol,
UnrecognizedFunction, UnrecognizedFunction,

View File

@@ -1,45 +0,0 @@
#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

@@ -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, allowCaseFunction: context.AllowCaseFunction) as ExpressionNode; node = expressionParser.CreateTree(condition, null, namedValues, functions) as ExpressionNode;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -113,12 +113,6 @@ 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, allowCaseFunction: context.AllowCaseFunction); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
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, allowCaseFunction: context.AllowCaseFunction); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
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, allowCaseFunction: context.AllowCaseFunction); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
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, allowCaseFunction: context.AllowCaseFunction); var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
var options = new EvaluationOptions var options = new EvaluationOptions
{ {
MaxMemory = context.Memory.MaxBytes, MaxMemory = context.Memory.MaxBytes,