mirror of
https://github.com/actions/runner.git
synced 2025-12-10 12:36:23 +00:00
Compare commits
31 Commits
v2.327.1
...
salmanmkc/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9227a2561 | ||
|
|
f9c4e17fd9 | ||
|
|
646da708ba | ||
|
|
bf8236344b | ||
|
|
720f16aef6 | ||
|
|
f77066a6a8 | ||
|
|
df83df2a32 | ||
|
|
97b2254146 | ||
|
|
7f72ba9e48 | ||
|
|
f8ae5bb1a7 | ||
|
|
a5631456a2 | ||
|
|
65dfa460ba | ||
|
|
80ee51f164 | ||
|
|
c95883f28e | ||
|
|
6e940643a9 | ||
|
|
629f2384a4 | ||
|
|
c3bf70becb | ||
|
|
8b65f5f9df | ||
|
|
5f1efec208 | ||
|
|
20d82ad357 | ||
|
|
0ebdf9e83d | ||
|
|
6543bf206b | ||
|
|
a942627965 | ||
|
|
83539166c9 | ||
|
|
1c1e8bfd18 | ||
|
|
59177fa379 | ||
|
|
2d7635a7f0 | ||
|
|
0203cf24d3 | ||
|
|
5e74a4d8e4 | ||
|
|
6ca97eeb88 | ||
|
|
8a9b96806d |
@@ -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.412"
|
"version": "8.0.413"
|
||||||
},
|
},
|
||||||
"ghcr.io/devcontainers/features/node:1": {
|
"ghcr.io/devcontainers/features/node:1": {
|
||||||
"version": "20"
|
"version": "20"
|
||||||
|
|||||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
|||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
# Build runner layout
|
# Build runner layout
|
||||||
- name: Build & Layout Release
|
- name: Build & Layout Release
|
||||||
|
|||||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
|
|||||||
59
.github/workflows/docker-buildx-upgrade.yml
vendored
59
.github/workflows/docker-buildx-upgrade.yml
vendored
@@ -2,8 +2,8 @@ name: "Docker/Buildx Version Upgrade"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 1' # Run every Monday at midnight
|
- cron: "0 9 * * 1" # Weekly on Monday at 9 AM UTC (independent of other dependencies)
|
||||||
workflow_dispatch: # Allow manual triggering
|
workflow_dispatch: # Allow manual triggering
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-versions:
|
check-versions:
|
||||||
@@ -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@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Check Docker version
|
- name: Check Docker version
|
||||||
id: check_docker_version
|
id: check_docker_version
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
echo "Failed to retrieve a valid Docker version"
|
echo "Failed to retrieve a valid Docker version"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
should_update=0
|
should_update=0
|
||||||
[ "$current_version" != "$latest_version" ] && should_update=1
|
[ "$current_version" != "$latest_version" ] && should_update=1
|
||||||
|
|
||||||
@@ -64,17 +64,17 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
docker_should_update="${{ steps.check_docker_version.outputs.SHOULD_UPDATE }}"
|
docker_should_update="${{ steps.check_docker_version.outputs.SHOULD_UPDATE }}"
|
||||||
buildx_should_update="${{ steps.check_buildx_version.outputs.SHOULD_UPDATE }}"
|
buildx_should_update="${{ steps.check_buildx_version.outputs.SHOULD_UPDATE }}"
|
||||||
|
|
||||||
# Show annotation if only Docker needs update
|
# Show annotation if only Docker needs update
|
||||||
if [[ "$docker_should_update" == "1" && "$buildx_should_update" == "0" ]]; then
|
if [[ "$docker_should_update" == "1" && "$buildx_should_update" == "0" ]]; then
|
||||||
echo "::warning ::Docker version (${{ steps.check_docker_version.outputs.LATEST_VERSION }}) needs update but Buildx is current. Only updating when both need updates."
|
echo "::warning ::Docker version (${{ steps.check_docker_version.outputs.LATEST_VERSION }}) needs update but Buildx is current. Only updating when both need updates."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Show annotation if only Buildx needs update
|
# Show annotation if only Buildx needs update
|
||||||
if [[ "$docker_should_update" == "0" && "$buildx_should_update" == "1" ]]; then
|
if [[ "$docker_should_update" == "0" && "$buildx_should_update" == "1" ]]; then
|
||||||
echo "::warning ::Buildx version (${{ steps.check_buildx_version.outputs.LATEST_VERSION }}) needs update but Docker is current. Only updating when both need updates."
|
echo "::warning ::Buildx version (${{ steps.check_buildx_version.outputs.LATEST_VERSION }}) needs update but Docker is current. Only updating when both need updates."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Show annotation when both are current
|
# Show annotation when both are current
|
||||||
if [[ "$docker_should_update" == "0" && "$buildx_should_update" == "0" ]]; then
|
if [[ "$docker_should_update" == "0" && "$buildx_should_update" == "0" ]]; then
|
||||||
echo "::warning ::Latest Docker version is ${{ steps.check_docker_version.outputs.LATEST_VERSION }} and Buildx version is ${{ steps.check_buildx_version.outputs.LATEST_VERSION }}. No updates needed."
|
echo "::warning ::Latest Docker version is ${{ steps.check_docker_version.outputs.LATEST_VERSION }} and Buildx version is ${{ steps.check_buildx_version.outputs.LATEST_VERSION }}. No updates needed."
|
||||||
@@ -89,26 +89,26 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Update Docker version
|
- name: Update Docker version
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
latest_version="${{ needs.check-versions.outputs.DOCKER_LATEST_VERSION }}"
|
latest_version="${{ needs.check-versions.outputs.DOCKER_LATEST_VERSION }}"
|
||||||
current_version="${{ needs.check-versions.outputs.DOCKER_CURRENT_VERSION }}"
|
current_version="${{ needs.check-versions.outputs.DOCKER_CURRENT_VERSION }}"
|
||||||
|
|
||||||
# Update version in Dockerfile
|
# Update version in Dockerfile
|
||||||
sed -i "s/ARG DOCKER_VERSION=$current_version/ARG DOCKER_VERSION=$latest_version/g" ./images/Dockerfile
|
sed -i "s/ARG DOCKER_VERSION=$current_version/ARG DOCKER_VERSION=$latest_version/g" ./images/Dockerfile
|
||||||
|
|
||||||
- name: Update Buildx version
|
- name: Update Buildx version
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
latest_version="${{ needs.check-versions.outputs.BUILDX_LATEST_VERSION }}"
|
latest_version="${{ needs.check-versions.outputs.BUILDX_LATEST_VERSION }}"
|
||||||
current_version="${{ needs.check-versions.outputs.BUILDX_CURRENT_VERSION }}"
|
current_version="${{ needs.check-versions.outputs.BUILDX_CURRENT_VERSION }}"
|
||||||
|
|
||||||
# Update version in Dockerfile
|
# Update version in Dockerfile
|
||||||
sed -i "s/ARG BUILDX_VERSION=$current_version/ARG BUILDX_VERSION=$latest_version/g" ./images/Dockerfile
|
sed -i "s/ARG BUILDX_VERSION=$current_version/ARG BUILDX_VERSION=$latest_version/g" ./images/Dockerfile
|
||||||
|
|
||||||
- name: Commit changes and create Pull Request
|
- name: Commit changes and create Pull Request
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -117,7 +117,7 @@ jobs:
|
|||||||
branch_name="feature/docker-buildx-upgrade"
|
branch_name="feature/docker-buildx-upgrade"
|
||||||
commit_message="Upgrade Docker to v${{ needs.check-versions.outputs.DOCKER_LATEST_VERSION }} and Buildx to v${{ needs.check-versions.outputs.BUILDX_LATEST_VERSION }}"
|
commit_message="Upgrade Docker to v${{ needs.check-versions.outputs.DOCKER_LATEST_VERSION }} and Buildx to v${{ needs.check-versions.outputs.BUILDX_LATEST_VERSION }}"
|
||||||
pr_title="Update Docker to v${{ needs.check-versions.outputs.DOCKER_LATEST_VERSION }} and Buildx to v${{ needs.check-versions.outputs.BUILDX_LATEST_VERSION }}"
|
pr_title="Update Docker to v${{ needs.check-versions.outputs.DOCKER_LATEST_VERSION }} and Buildx to v${{ needs.check-versions.outputs.BUILDX_LATEST_VERSION }}"
|
||||||
|
|
||||||
# Configure git
|
# Configure git
|
||||||
git config --global user.name "github-actions[bot]"
|
git config --global user.name "github-actions[bot]"
|
||||||
git config --global user.email "<41898282+github-actions[bot]@users.noreply.github.com>"
|
git config --global user.email "<41898282+github-actions[bot]@users.noreply.github.com>"
|
||||||
@@ -129,16 +129,35 @@ jobs:
|
|||||||
else
|
else
|
||||||
git checkout -b "$branch_name"
|
git checkout -b "$branch_name"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Commit and push changes
|
# Commit and push changes
|
||||||
git commit -a -m "$commit_message"
|
git commit -a -m "$commit_message"
|
||||||
git push --force origin "$branch_name"
|
git push --force origin "$branch_name"
|
||||||
|
|
||||||
|
# Create PR body using here-doc for proper formatting
|
||||||
|
cat > pr_body.txt << 'EOF'
|
||||||
|
Automated Docker and Buildx version update:
|
||||||
|
|
||||||
|
- Docker: ${{ needs.check-versions.outputs.DOCKER_CURRENT_VERSION }} → ${{ needs.check-versions.outputs.DOCKER_LATEST_VERSION }}
|
||||||
|
- Buildx: ${{ needs.check-versions.outputs.BUILDX_CURRENT_VERSION }} → ${{ needs.check-versions.outputs.BUILDX_LATEST_VERSION }}
|
||||||
|
|
||||||
|
This update ensures we're using the latest stable Docker and Buildx versions for security and performance improvements.
|
||||||
|
|
||||||
|
**Release notes:** https://docs.docker.com/engine/release-notes/
|
||||||
|
|
||||||
|
**Next steps:**
|
||||||
|
- Review the version changes
|
||||||
|
- Verify container builds work as expected
|
||||||
|
- Test multi-platform builds if applicable
|
||||||
|
- Merge when ready
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Autogenerated by [Docker/Buildx Version Upgrade Workflow](https://github.com/actions/runner/blob/main/.github/workflows/docker-buildx-upgrade.yml)
|
||||||
|
EOF
|
||||||
|
|
||||||
# Create PR
|
# Create PR
|
||||||
pr_body="Upgrades Docker version from ${{ needs.check-versions.outputs.DOCKER_CURRENT_VERSION }} to ${{ needs.check-versions.outputs.DOCKER_LATEST_VERSION }} and Docker Buildx version from ${{ needs.check-versions.outputs.BUILDX_CURRENT_VERSION }} to ${{ needs.check-versions.outputs.BUILDX_LATEST_VERSION }}.\n\n"
|
|
||||||
pr_body+="Release notes: https://docs.docker.com/engine/release-notes/\n\n"
|
|
||||||
pr_body+="---\n\nAutogenerated by [Docker/Buildx Version Upgrade Workflow](https://github.com/actions/runner/blob/main/.github/workflows/docker-buildx-upgrade.yml)"
|
|
||||||
|
|
||||||
gh pr create -B main -H "$branch_name" \
|
gh pr create -B main -H "$branch_name" \
|
||||||
--title "$pr_title" \
|
--title "$pr_title" \
|
||||||
--body "$pr_body"
|
--label "dependencies" \
|
||||||
|
--body-file pr_body.txt
|
||||||
|
|||||||
34
.github/workflows/dotnet-upgrade.yml
vendored
34
.github/workflows/dotnet-upgrade.yml
vendored
@@ -2,20 +2,20 @@ name: "DotNet SDK Upgrade"
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 1'
|
- cron: "0 8 * * 1" # Weekly on Monday at 8 AM UTC (independent of Node.js/NPM)
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
dotnet-update:
|
dotnet-update:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
SHOULD_UPDATE: ${{ steps.fetch_latest_version.outputs.SHOULD_UPDATE }}
|
SHOULD_UPDATE: ${{ steps.fetch_latest_version.outputs.SHOULD_UPDATE }}
|
||||||
BRANCH_EXISTS: ${{ steps.fetch_latest_version.outputs.BRANCH_EXISTS }}
|
BRANCH_EXISTS: ${{ steps.fetch_latest_version.outputs.BRANCH_EXISTS }}
|
||||||
DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION: ${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}
|
DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION: ${{ steps.fetch_latest_version.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}
|
||||||
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@v4
|
uses: actions/checkout@v5
|
||||||
- name: Get current major minor version
|
- name: Get current major minor version
|
||||||
id: fetch_current_version
|
id: fetch_current_version
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
|
|
||||||
# check if git branch already exists for the upgrade
|
# check if git branch already exists for the upgrade
|
||||||
branch_already_exists=0
|
branch_already_exists=0
|
||||||
|
|
||||||
if git ls-remote --heads --exit-code origin refs/heads/feature/dotnetsdk-upgrade/${latest_patch_version};
|
if git ls-remote --heads --exit-code origin refs/heads/feature/dotnetsdk-upgrade/${latest_patch_version};
|
||||||
then
|
then
|
||||||
branch_already_exists=1
|
branch_already_exists=1
|
||||||
@@ -89,17 +89,17 @@ 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@v4
|
- uses: actions/checkout@v5
|
||||||
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
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
gh pr create -B main -H feature/dotnetsdk-upgrade/${{ needs.dotnet-update.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }} --title "Update dotnet sdk to latest version @${{ needs.dotnet-update.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}" --body "
|
gh pr create -B main -H feature/dotnetsdk-upgrade/${{ needs.dotnet-update.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }} --title "Update dotnet sdk to latest version @${{ needs.dotnet-update.outputs.DOTNET_LATEST_MAJOR_MINOR_PATCH_VERSION }}" --label "dependencies" --body "
|
||||||
https://dotnetcli.blob.core.windows.net/dotnet/Sdk/${{ needs.dotnet-update.outputs.DOTNET_CURRENT_MAJOR_MINOR_VERSION }}/latest.version
|
https://dotnetcli.blob.core.windows.net/dotnet/Sdk/${{ needs.dotnet-update.outputs.DOTNET_CURRENT_MAJOR_MINOR_VERSION }}/latest.version
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
---
|
||||||
Autogenerated by [DotNet SDK Upgrade Workflow](https://github.com/actions/runner/blob/main/.github/workflows/dotnet-upgrade.yml)"
|
|
||||||
|
Autogenerated by [DotNet SDK Upgrade Workflow](https://github.com/actions/runner/blob/main/.github/workflows/dotnet-upgrade.yml)"
|
||||||
|
|||||||
126
.github/workflows/node-upgrade.yml
vendored
Normal file
126
.github/workflows/node-upgrade.yml
vendored
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
name: Auto Update Node Version
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 6 * * 1" # Weekly, every Monday
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-node:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Get latest Node versions
|
||||||
|
id: node-versions
|
||||||
|
run: |
|
||||||
|
# Get latest Node.js releases from official GitHub releases
|
||||||
|
echo "Fetching latest Node.js releases..."
|
||||||
|
|
||||||
|
# Get latest v20.x release
|
||||||
|
LATEST_NODE20=$(curl -s https://api.github.com/repos/nodejs/node/releases | \
|
||||||
|
jq -r '.[] | select(.tag_name | startswith("v20.")) | .tag_name' | \
|
||||||
|
head -1 | sed 's/^v//')
|
||||||
|
|
||||||
|
# Get latest v24.x release
|
||||||
|
LATEST_NODE24=$(curl -s https://api.github.com/repos/nodejs/node/releases | \
|
||||||
|
jq -r '.[] | select(.tag_name | startswith("v24.")) | .tag_name' | \
|
||||||
|
head -1 | sed 's/^v//')
|
||||||
|
|
||||||
|
echo "Found Node.js releases: 20=$LATEST_NODE20, 24=$LATEST_NODE24"
|
||||||
|
|
||||||
|
# Verify these versions are available in alpine_nodejs releases
|
||||||
|
echo "Verifying availability in alpine_nodejs..."
|
||||||
|
ALPINE_RELEASES=$(curl -s https://api.github.com/repos/actions/alpine_nodejs/releases | jq -r '.[].tag_name')
|
||||||
|
|
||||||
|
if ! echo "$ALPINE_RELEASES" | grep -q "^node20-$LATEST_NODE20$"; then
|
||||||
|
echo "::warning title=Node 20 Fallback::Node 20 version $LATEST_NODE20 not found in alpine_nodejs releases, using fallback"
|
||||||
|
# Fall back to latest available alpine_nodejs v20 release
|
||||||
|
LATEST_NODE20=$(echo "$ALPINE_RELEASES" | grep "^node20-" | head -1 | sed 's/^node20-//')
|
||||||
|
echo "Using latest available alpine_nodejs Node 20: $LATEST_NODE20"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! echo "$ALPINE_RELEASES" | grep -q "^node24-$LATEST_NODE24$"; then
|
||||||
|
echo "::warning title=Node 24 Fallback::Node 24 version $LATEST_NODE24 not found in alpine_nodejs releases, using fallback"
|
||||||
|
# Fall back to latest available alpine_nodejs v24 release
|
||||||
|
LATEST_NODE24=$(echo "$ALPINE_RELEASES" | grep "^node24-" | head -1 | sed 's/^node24-//')
|
||||||
|
echo "Using latest available alpine_nodejs Node 24: $LATEST_NODE24"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "latest_node20=$LATEST_NODE20" >> $GITHUB_OUTPUT
|
||||||
|
echo "latest_node24=$LATEST_NODE24" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Check current versions in externals.sh
|
||||||
|
CURRENT_NODE20=$(grep "NODE20_VERSION=" src/Misc/externals.sh | cut -d'"' -f2)
|
||||||
|
CURRENT_NODE24=$(grep "NODE24_VERSION=" src/Misc/externals.sh | cut -d'"' -f2)
|
||||||
|
|
||||||
|
echo "current_node20=$CURRENT_NODE20" >> $GITHUB_OUTPUT
|
||||||
|
echo "current_node24=$CURRENT_NODE24" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Determine if updates are needed
|
||||||
|
NEEDS_UPDATE20="false"
|
||||||
|
NEEDS_UPDATE24="false"
|
||||||
|
|
||||||
|
if [ "$CURRENT_NODE20" != "$LATEST_NODE20" ]; then
|
||||||
|
NEEDS_UPDATE20="true"
|
||||||
|
echo "::notice title=Node 20 Update Available::Current: $CURRENT_NODE20 → Latest: $LATEST_NODE20"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$CURRENT_NODE24" != "$LATEST_NODE24" ]; then
|
||||||
|
NEEDS_UPDATE24="true"
|
||||||
|
echo "::notice title=Node 24 Update Available::Current: $CURRENT_NODE24 → Latest: $LATEST_NODE24"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$NEEDS_UPDATE20" == "false" ] && [ "$NEEDS_UPDATE24" == "false" ]; then
|
||||||
|
echo "::notice title=No Updates Needed::All Node.js versions are up to date"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "needs_update20=$NEEDS_UPDATE20" >> $GITHUB_OUTPUT
|
||||||
|
echo "needs_update24=$NEEDS_UPDATE24" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Update externals.sh and create PR
|
||||||
|
if: steps.node-versions.outputs.needs_update20 == 'true' || steps.node-versions.outputs.needs_update24 == 'true'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
# Update the files
|
||||||
|
if [ "${{ steps.node-versions.outputs.needs_update20 }}" == "true" ]; then
|
||||||
|
sed -i 's/NODE20_VERSION="[^"]*"/NODE20_VERSION="${{ steps.node-versions.outputs.latest_node20 }}"/' src/Misc/externals.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${{ steps.node-versions.outputs.needs_update24 }}" == "true" ]; then
|
||||||
|
sed -i 's/NODE24_VERSION="[^"]*"/NODE24_VERSION="${{ steps.node-versions.outputs.latest_node24 }}"/' src/Misc/externals.sh
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configure git
|
||||||
|
git config --global user.name "github-actions[bot]"
|
||||||
|
git config --global user.email "<41898282+github-actions[bot]@users.noreply.github.com>"
|
||||||
|
|
||||||
|
# Create branch and commit changes
|
||||||
|
branch_name="chore/update-node"
|
||||||
|
git checkout -b "$branch_name"
|
||||||
|
git commit -a -m "chore: update Node versions (20: ${{ steps.node-versions.outputs.latest_node20 }}, 24: ${{ steps.node-versions.outputs.latest_node24 }})"
|
||||||
|
git push --force origin "$branch_name"
|
||||||
|
|
||||||
|
# Create PR body using here-doc for proper formatting
|
||||||
|
cat > pr_body.txt << 'EOF'
|
||||||
|
Automated Node.js version update:
|
||||||
|
|
||||||
|
- Node 20: ${{ steps.node-versions.outputs.current_node20 }} → ${{ steps.node-versions.outputs.latest_node20 }}
|
||||||
|
- Node 24: ${{ steps.node-versions.outputs.current_node24 }} → ${{ steps.node-versions.outputs.latest_node24 }}
|
||||||
|
|
||||||
|
This update ensures we're using the latest stable Node.js versions for security and performance improvements.
|
||||||
|
|
||||||
|
**Note**: When updating Node versions, remember to also create a new release of alpine_nodejs at the updated version following the instructions at: https://github.com/actions/alpine_nodejs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Autogenerated by [Node Version Upgrade Workflow](https://github.com/actions/runner/blob/main/.github/workflows/node-upgrade.yml)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Create PR
|
||||||
|
gh pr create -B main -H "$branch_name" \
|
||||||
|
--title "chore: update Node versions" \
|
||||||
|
--label "dependencies" \
|
||||||
|
--body-file pr_body.txt
|
||||||
|
|
||||||
|
echo "::notice title=PR Created::Successfully created Node.js version update PR on branch $branch_name"
|
||||||
235
.github/workflows/npm-audit-typescript.yml
vendored
Normal file
235
.github/workflows/npm-audit-typescript.yml
vendored
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
name: NPM Audit Fix with TypeScript Auto-Fix
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
npm-audit-with-ts-fix:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
- name: NPM install and audit fix with TypeScript auto-repair
|
||||||
|
working-directory: src/Misc/expressionFunc/hashFiles
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Check for vulnerabilities first
|
||||||
|
echo "Checking for npm vulnerabilities..."
|
||||||
|
if npm audit --audit-level=moderate; then
|
||||||
|
echo "✅ No moderate or higher vulnerabilities found"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "⚠️ Vulnerabilities found, attempting npm audit fix..."
|
||||||
|
|
||||||
|
# Attempt audit fix and capture the result
|
||||||
|
if npm audit fix; then
|
||||||
|
echo "✅ npm audit fix completed successfully"
|
||||||
|
AUDIT_FIX_STATUS="success"
|
||||||
|
else
|
||||||
|
echo "⚠️ npm audit fix failed or had issues"
|
||||||
|
AUDIT_FIX_STATUS="failed"
|
||||||
|
|
||||||
|
# Try audit fix with --force as a last resort for critical/high vulns only
|
||||||
|
echo "Checking if critical/high vulnerabilities remain..."
|
||||||
|
if ! npm audit --audit-level=high; then
|
||||||
|
echo "🚨 Critical/high vulnerabilities remain, attempting --force fix..."
|
||||||
|
if npm audit fix --force; then
|
||||||
|
echo "⚠️ npm audit fix --force completed (may have breaking changes)"
|
||||||
|
AUDIT_FIX_STATUS="force-fixed"
|
||||||
|
else
|
||||||
|
echo "❌ npm audit fix --force also failed"
|
||||||
|
AUDIT_FIX_STATUS="force-failed"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✅ Only moderate/low vulnerabilities remain after failed fix"
|
||||||
|
AUDIT_FIX_STATUS="partial-success"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "AUDIT_FIX_STATUS=$AUDIT_FIX_STATUS" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Try to fix TypeScript issues automatically
|
||||||
|
echo "Attempting to fix TypeScript compatibility issues..."
|
||||||
|
|
||||||
|
# Check if build fails
|
||||||
|
if ! npm run build 2>/dev/null; then
|
||||||
|
echo "Build failed, attempting automated fixes..."
|
||||||
|
|
||||||
|
# Common fix 1: Update @types/node to latest compatible version
|
||||||
|
echo "Trying to update @types/node to latest version..."
|
||||||
|
npm update @types/node
|
||||||
|
|
||||||
|
# Common fix 2: If that doesn't work, try installing a specific known-good version
|
||||||
|
if ! npm run build 2>/dev/null; then
|
||||||
|
echo "Trying specific @types/node version..."
|
||||||
|
# Try Node 20 compatible version
|
||||||
|
npm install --save-dev @types/node@^20.0.0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Common fix 3: Clear node_modules and reinstall if still failing
|
||||||
|
if ! npm run build 2>/dev/null; then
|
||||||
|
echo "Clearing node_modules and reinstalling..."
|
||||||
|
rm -rf node_modules package-lock.json
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Re-run audit fix after clean install if it was successful before
|
||||||
|
if [[ "$AUDIT_FIX_STATUS" == "success" || "$AUDIT_FIX_STATUS" == "force-fixed" ]]; then
|
||||||
|
echo "Re-running npm audit fix after clean install..."
|
||||||
|
npm audit fix || echo "Audit fix failed on second attempt"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Common fix 4: Try updating TypeScript itself
|
||||||
|
if ! npm run build 2>/dev/null; then
|
||||||
|
echo "Trying to update TypeScript..."
|
||||||
|
npm update typescript
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Final check
|
||||||
|
if npm run build 2>/dev/null; then
|
||||||
|
echo "✅ Successfully fixed TypeScript issues automatically"
|
||||||
|
else
|
||||||
|
echo "⚠️ Could not automatically fix TypeScript issues"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✅ Build passes after audit fix"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create PR if changes exist
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
HUSKY: 0 # Disable husky hooks for automated commits
|
||||||
|
run: |
|
||||||
|
# Check if there are any changes
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
# Configure git
|
||||||
|
git config --global user.name "github-actions[bot]"
|
||||||
|
git config --global user.email "<41898282+github-actions[bot]@users.noreply.github.com>"
|
||||||
|
|
||||||
|
# Create branch and commit changes
|
||||||
|
branch_name="chore/npm-audit-fix-with-ts-repair"
|
||||||
|
git checkout -b "$branch_name"
|
||||||
|
|
||||||
|
# Commit with --no-verify to skip husky hooks
|
||||||
|
git commit -a -m "chore: npm audit fix with automated TypeScript compatibility fixes" --no-verify
|
||||||
|
git push --force origin "$branch_name"
|
||||||
|
|
||||||
|
# Check final build status and gather info about what was changed
|
||||||
|
build_status="✅ Build passes"
|
||||||
|
fixes_applied=""
|
||||||
|
cd src/Misc/expressionFunc/hashFiles
|
||||||
|
|
||||||
|
# Check what packages were updated
|
||||||
|
if git diff HEAD~1 package.json | grep -q "@types/node"; then
|
||||||
|
fixes_applied+="\n- Updated @types/node version for TypeScript compatibility"
|
||||||
|
fi
|
||||||
|
if git diff HEAD~1 package.json | grep -q "typescript"; then
|
||||||
|
fixes_applied+="\n- Updated TypeScript version"
|
||||||
|
fi
|
||||||
|
if git diff HEAD~1 package-lock.json | grep -q "resolved"; then
|
||||||
|
fixes_applied+="\n- Updated package dependencies via npm audit fix"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! npm run build 2>/dev/null; then
|
||||||
|
build_status="⚠️ Build fails - manual review required"
|
||||||
|
fi
|
||||||
|
cd - > /dev/null
|
||||||
|
|
||||||
|
# Create enhanced PR body using here-doc for proper formatting
|
||||||
|
audit_status_msg=""
|
||||||
|
case "$AUDIT_FIX_STATUS" in
|
||||||
|
"success")
|
||||||
|
audit_status_msg="✅ **Audit Fix**: Completed successfully"
|
||||||
|
;;
|
||||||
|
"partial-success")
|
||||||
|
audit_status_msg="⚠️ **Audit Fix**: Partial success (only moderate/low vulnerabilities remain)"
|
||||||
|
;;
|
||||||
|
"force-fixed")
|
||||||
|
audit_status_msg="⚠️ **Audit Fix**: Completed with --force (may have breaking changes)"
|
||||||
|
;;
|
||||||
|
"failed"|"force-failed")
|
||||||
|
audit_status_msg="❌ **Audit Fix**: Failed to resolve vulnerabilities"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
audit_status_msg="❓ **Audit Fix**: Status unknown"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "$build_status" == *"fails"* ]]; then
|
||||||
|
cat > pr_body.txt << EOF
|
||||||
|
Automated npm audit fix with TypeScript auto-repair for hashFiles dependencies.
|
||||||
|
|
||||||
|
**Build Status**: ⚠️ Build fails - manual review required
|
||||||
|
$audit_status_msg
|
||||||
|
|
||||||
|
This workflow attempts to automatically fix TypeScript compatibility issues that may arise from npm audit fixes.
|
||||||
|
|
||||||
|
⚠️ **Manual Review Required**: The build is currently failing after automated fixes were attempted.
|
||||||
|
|
||||||
|
Common issues and solutions:
|
||||||
|
- Check for TypeScript version compatibility with Node.js types
|
||||||
|
- Review breaking changes in updated dependencies
|
||||||
|
- Consider pinning problematic dependency versions temporarily
|
||||||
|
- Review tsconfig.json for compatibility settings
|
||||||
|
|
||||||
|
**Automated Fix Strategy**:
|
||||||
|
1. Run npm audit fix with proper error handling
|
||||||
|
2. Update @types/node to latest compatible version
|
||||||
|
3. Try Node 20 specific @types/node version if needed
|
||||||
|
4. Clean reinstall dependencies if conflicts persist
|
||||||
|
5. Update TypeScript compiler if necessary
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Autogenerated by [NPM Audit Fix with TypeScript Auto-Fix Workflow](https://github.com/actions/runner/blob/main/.github/workflows/npm-audit-ts-fix.yml)
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat > pr_body.txt << EOF
|
||||||
|
Automated npm audit fix with TypeScript auto-repair for hashFiles dependencies.
|
||||||
|
|
||||||
|
**Build Status**: ✅ Build passes
|
||||||
|
$audit_status_msg
|
||||||
|
|
||||||
|
This workflow attempts to automatically fix TypeScript compatibility issues that may arise from npm audit fixes.
|
||||||
|
|
||||||
|
✅ **Ready to Merge**: All automated fixes were successful and the build passes.
|
||||||
|
|
||||||
|
**Automated Fix Strategy**:
|
||||||
|
1. Run npm audit fix with proper error handling
|
||||||
|
2. Update @types/node to latest compatible version
|
||||||
|
3. Try Node 20 specific @types/node version if needed
|
||||||
|
4. Clean reinstall dependencies if conflicts persist
|
||||||
|
5. Update TypeScript compiler if necessary
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Autogenerated by [NPM Audit Fix with TypeScript Auto-Fix Workflow](https://github.com/actions/runner/blob/main/.github/workflows/npm-audit-ts-fix.yml)
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$fixes_applied" ]; then
|
||||||
|
# Add the fixes applied section to the file
|
||||||
|
sed -i "/This workflow attempts/a\\
|
||||||
|
\\
|
||||||
|
**Automated Fixes Applied**:$fixes_applied" pr_body.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create PR with appropriate labels
|
||||||
|
labels="dependency,typescript"
|
||||||
|
if [[ "$build_status" == *"fails"* ]]; then
|
||||||
|
labels="dependency,typescript,needs-manual-review"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create PR
|
||||||
|
gh pr create -B main -H "$branch_name" \
|
||||||
|
--title "chore: npm audit fix with TypeScript auto-repair" \
|
||||||
|
--label "$labels" \
|
||||||
|
--body-file pr_body.txt
|
||||||
|
else
|
||||||
|
echo "No changes to commit"
|
||||||
|
fi
|
||||||
132
.github/workflows/npm-audit.yml
vendored
Normal file
132
.github/workflows/npm-audit.yml
vendored
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
name: NPM Audit Fix
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 7 * * 1" # Weekly on Monday at 7 AM UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
npm-audit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "20"
|
||||||
|
|
||||||
|
- name: NPM install and audit fix
|
||||||
|
working-directory: src/Misc/expressionFunc/hashFiles
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Check what vulnerabilities exist
|
||||||
|
echo "=== Checking current vulnerabilities ==="
|
||||||
|
npm audit || true
|
||||||
|
|
||||||
|
# Apply audit fix --force to get security updates
|
||||||
|
echo "=== Applying npm audit fix --force ==="
|
||||||
|
npm audit fix --force
|
||||||
|
|
||||||
|
# Test if build still works and set status
|
||||||
|
echo "=== Testing build compatibility ==="
|
||||||
|
if npm run all; then
|
||||||
|
echo "✅ Build successful after audit fix"
|
||||||
|
echo "AUDIT_FIX_STATUS=success" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "❌ Build failed after audit fix - will create PR with fix instructions"
|
||||||
|
echo "AUDIT_FIX_STATUS=build_failed" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create PR if changes exist
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
# Check if there are any changes
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
# Configure git
|
||||||
|
git config --global user.name "github-actions[bot]"
|
||||||
|
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
# Create branch and commit changes
|
||||||
|
branch_name="chore/npm-audit-fix-$(date +%Y%m%d)"
|
||||||
|
git checkout -b "$branch_name"
|
||||||
|
git add .
|
||||||
|
git commit -m "chore: npm audit fix for hashFiles dependencies" --no-verify
|
||||||
|
git push origin "$branch_name"
|
||||||
|
|
||||||
|
# Create PR body based on what actually happened
|
||||||
|
if [ "$AUDIT_FIX_STATUS" = "success" ]; then
|
||||||
|
cat > pr_body.txt << 'EOF'
|
||||||
|
Automated npm audit fix for security vulnerabilities in hashFiles dependencies.
|
||||||
|
|
||||||
|
**✅ Full Fix Applied Successfully**
|
||||||
|
This update addresses npm security advisories and ensures dependencies are secure and up-to-date.
|
||||||
|
|
||||||
|
**Changes made:**
|
||||||
|
- Applied `npm audit fix --force` to resolve security vulnerabilities
|
||||||
|
- Updated package-lock.json with security patches
|
||||||
|
- Verified build compatibility with `npm run all`
|
||||||
|
|
||||||
|
**Next steps:**
|
||||||
|
- Review the dependency changes
|
||||||
|
- Verify the hashFiles functionality still works as expected
|
||||||
|
- Merge when ready
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Autogenerated by [NPM Audit Fix Workflow](https://github.com/actions/runner/blob/main/.github/workflows/npm-audit.yml)
|
||||||
|
EOF
|
||||||
|
elif [ "$AUDIT_FIX_STATUS" = "build_failed" ]; then
|
||||||
|
cat > pr_body.txt << 'EOF'
|
||||||
|
Automated npm audit fix for security vulnerabilities in hashFiles dependencies.
|
||||||
|
|
||||||
|
**⚠️ Security Fixes Applied - Build Issues Need Manual Resolution**
|
||||||
|
This update applies important security patches but causes build failures that require manual fixes.
|
||||||
|
|
||||||
|
**Changes made:**
|
||||||
|
- Applied `npm audit fix --force` to resolve security vulnerabilities
|
||||||
|
- Updated package-lock.json with security patches
|
||||||
|
|
||||||
|
**⚠️ Build Issues Detected:**
|
||||||
|
The build fails after applying security fixes, likely due to TypeScript compatibility issues with updated `@types/node`.
|
||||||
|
|
||||||
|
**Required Manual Fixes:**
|
||||||
|
1. Review TypeScript compilation errors in the build output
|
||||||
|
2. Update TypeScript configuration if needed
|
||||||
|
3. Consider pinning `@types/node` to a compatible version
|
||||||
|
4. Run `npm run all` locally to verify fixes
|
||||||
|
|
||||||
|
**Next steps:**
|
||||||
|
- **DO NOT merge until build issues are resolved**
|
||||||
|
- Apply manual fixes for TypeScript compatibility
|
||||||
|
- Test the hashFiles functionality still works as expected
|
||||||
|
- Merge when build passes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Autogenerated by [NPM Audit Fix Workflow](https://github.com/actions/runner/blob/main/.github/workflows/npm-audit.yml)
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
# Fallback case
|
||||||
|
cat > pr_body.txt << 'EOF'
|
||||||
|
Automated npm audit attempted for security vulnerabilities in hashFiles dependencies.
|
||||||
|
|
||||||
|
**ℹ️ No Changes Applied**
|
||||||
|
No security vulnerabilities were found or no changes were needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Autogenerated by [NPM Audit Fix Workflow](https://github.com/actions/runner/blob/main/.github/workflows/npm-audit.yml)
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create PR
|
||||||
|
gh pr create -B main -H "$branch_name" \
|
||||||
|
--title "chore: npm audit fix for hashFiles dependencies" \
|
||||||
|
--label "dependencies" \
|
||||||
|
--body-file pr_body.txt
|
||||||
|
else
|
||||||
|
echo "✅ No changes to commit - npm audit fix did not modify any files"
|
||||||
|
fi
|
||||||
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@@ -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@v4
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
# 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
|
||||||
@@ -86,7 +86,7 @@ jobs:
|
|||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
# 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@v4
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
# 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@v4
|
uses: actions/download-artifact@v5
|
||||||
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@v4
|
uses: actions/download-artifact@v5
|
||||||
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@v4
|
uses: actions/download-artifact@v5
|
||||||
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@v4
|
uses: actions/download-artifact@v5
|
||||||
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@v4
|
uses: actions/download-artifact@v5
|
||||||
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@v4
|
uses: actions/download-artifact@v5
|
||||||
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@v4
|
uses: actions/download-artifact@v5
|
||||||
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@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- 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@v2
|
uses: actions/attest-build-provenance@v3
|
||||||
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 }}
|
||||||
|
|||||||
@@ -1,6 +1 @@
|
|||||||
#!/usr/bin/env sh
|
cd src/Misc/expressionFunc/hashFiles && npx lint-staged
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
cd src/Misc/expressionFunc/hashFiles
|
|
||||||
|
|
||||||
npx lint-staged
|
|
||||||
|
|||||||
@@ -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/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#linux)."
|
Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/reference/runners/self-hosted-runners#linux)."
|
||||||
|
|
||||||
## Install .Net Core 3.x Linux Dependencies
|
## Install .Net Core 3.x Linux Dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -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/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#macos)."
|
Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/reference/runners/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)
|
||||||
|
|||||||
@@ -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/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#windows)."
|
Please see "[Supported architectures and operating systems for self-hosted runners](https://docs.github.com/en/actions/reference/runners/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)
|
||||||
|
|||||||
@@ -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.0
|
ARG DOCKER_VERSION=28.4.0
|
||||||
ARG BUILDX_VERSION=0.25.0
|
ARG BUILDX_VERSION=0.28.0
|
||||||
|
|
||||||
RUN apt update -y && apt install curl unzip -y
|
RUN apt update -y && apt install curl unzip -y
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
## What's Changed
|
## What's Changed
|
||||||
* Try add orchestrationid into user-agent using token claim. by @TingluoHuang in https://github.com/actions/runner/pull/3945
|
* Update Docker to v28.3.2 and Buildx to v0.26.1 by @github-actions[bot] in https://github.com/actions/runner/pull/3953
|
||||||
* Fix null reference exception in user agent handling by @salmanmkc in https://github.com/actions/runner/pull/3946
|
* Fix if statement structure in update script and variable reference by @salmanmkc in https://github.com/actions/runner/pull/3956
|
||||||
* Runner Support for executing Node24 Actions by @salmanmkc in https://github.com/actions/runner/pull/3940
|
* Add V2 flow for runner deletion by @Samirat in https://github.com/actions/runner/pull/3954
|
||||||
* Update dotnet sdk to latest version @8.0.412 by @github-actions[bot] in https://github.com/actions/runner/pull/3941
|
* Node 20 -> Node 24 migration feature flagging, opt-in and opt-out environment variables by @salmanmkc in https://github.com/actions/runner/pull/3948
|
||||||
|
* Update Node20 and Node24 to latest by @djs-intel in https://github.com/actions/runner/pull/3972
|
||||||
|
* Redirect supported OS doc section to current public Docs location by @corycalahan in https://github.com/actions/runner/pull/3979
|
||||||
|
* Bump Microsoft.NET.Test.Sdk from 17.13.0 to 17.14.1 by @dependabot[bot] in https://github.com/actions/runner/pull/3975
|
||||||
|
* 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
|
## New Contributors
|
||||||
* @salmanmkc made their first contribution in https://github.com/actions/runner/pull/3946
|
* @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.326.0...v2.327.0
|
**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.
|
||||||
|
|||||||
1084
src/Misc/expressionFunc/hashFiles/package-lock.json
generated
1084
src/Misc/expressionFunc/hashFiles/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@
|
|||||||
"lint": "eslint src/**/*.ts",
|
"lint": "eslint src/**/*.ts",
|
||||||
"pack": "ncc build -o ../../layoutbin/hashFiles",
|
"pack": "ncc build -o ../../layoutbin/hashFiles",
|
||||||
"all": "npm run format && npm run lint && npm run build && npm run pack",
|
"all": "npm run format && npm run lint && npm run build && npm run pack",
|
||||||
"prepare": "cd ../../../../ && husky install"
|
"prepare": "cd ../../../../ && husky"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -36,15 +36,15 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.6.2",
|
"@types/node": "^20.6.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.7.2",
|
"@typescript-eslint/parser": "^6.7.2",
|
||||||
"@vercel/ncc": "^0.38.0",
|
"@vercel/ncc": "^0.38.3",
|
||||||
"eslint": "^8.47.0",
|
"eslint": "^8.47.0",
|
||||||
"eslint-plugin-github": "^4.10.0",
|
"eslint-plugin-github": "^4.10.2",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^15.5.0",
|
"lint-staged": "^15.5.0",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.3"
|
NODE20_VERSION="20.19.5"
|
||||||
NODE24_VERSION="24.4.0"
|
NODE24_VERSION="24.7.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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -146,10 +146,11 @@ if [[ "$currentplatform" == 'darwin' && restartinteractiverunner -eq 0 ]]; then
|
|||||||
then
|
then
|
||||||
nodever="node16"
|
nodever="node16"
|
||||||
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
||||||
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node12
|
if [[ $? -ne 0 || -z "$path" ]] # Fallback if RunnerService.js was started with node12
|
||||||
then
|
then
|
||||||
nodever="node12"
|
nodever="node12"
|
||||||
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
path=$(lsof -a -g "$procgroup" -F n | grep $nodever/bin/node | grep externals | tail -1 | cut -c2-)
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if [[ $? -eq 0 && -n "$path" ]]
|
if [[ $? -eq 0 && -n "$path" ]]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
SECONDS=0
|
SECONDS=0
|
||||||
while [[ $SECONDS != $1 ]]; do
|
while [[ $SECONDS -lt $1 ]]; do
|
||||||
:
|
:
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ 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);
|
||||||
@@ -67,10 +69,17 @@ 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();
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ namespace GitHub.Runner.Common
|
|||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public bool UseV2Flow { get; set; }
|
public bool UseV2Flow { get; set; }
|
||||||
|
|
||||||
|
[DataMember(EmitDefaultValue = false)]
|
||||||
|
public bool UseRunnerAdminFlow { get; set; }
|
||||||
|
|
||||||
[DataMember(EmitDefaultValue = false)]
|
[DataMember(EmitDefaultValue = false)]
|
||||||
public string ServerUrlV2 { get; set; }
|
public string ServerUrlV2 { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -169,6 +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";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,117 +44,15 @@ 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 = "";
|
var githubApiUrl = $"{GetEntityUrl(githubUrl)}/runners?name={Uri.EscapeDataString(agentName)}";
|
||||||
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 = "";
|
var githubApiUrl = $"{GetEntityUrl(githubUrl)}/runner-groups";
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,6 +103,12 @@ 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;
|
||||||
@@ -220,13 +125,22 @@ namespace GitHub.Runner.Common
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = null;
|
HttpResponseMessage response = null;
|
||||||
if (requestType == RequestType.Get)
|
switch (requestType)
|
||||||
{
|
{
|
||||||
response = await httpClient.GetAsync(githubApiUrl);
|
case RequestType.Get:
|
||||||
}
|
response = await httpClient.GetAsync(githubApiUrl);
|
||||||
else
|
break;
|
||||||
{
|
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)
|
||||||
@@ -261,5 +175,61 @@ 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 maxRetryAttemptsCount = 5,
|
int maxAttempts = 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, maxRetryAttemptsCount, shouldRetry);
|
await RetryRequest<Unit>(wrappedFunc, cancellationToken, maxAttempts, 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 maxRetryAttemptsCount = 5,
|
int maxAttempts = 5,
|
||||||
Func<Exception, bool> shouldRetry = null
|
Func<Exception, bool> shouldRetry = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var retryCount = 0;
|
var attempt = 0;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
retryCount++;
|
attempt++;
|
||||||
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 (retryCount < maxRetryAttemptsCount && (shouldRetry == null || shouldRetry(ex)))
|
catch (Exception ex) when (attempt < maxAttempts && (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. {maxRetryAttemptsCount - retryCount} attempt left.");
|
Trace.Warning($"Back off {backOff.TotalSeconds} seconds before next retry. {maxAttempts - attempt} attempt left.");
|
||||||
await Task.Delay(backOff, cancellationToken);
|
await Task.Delay(backOff, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,33 @@
|
|||||||
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()
|
||||||
@@ -18,6 +41,70 @@ 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>
|
/// <summary>
|
||||||
/// Checks if Node24 is requested but running on ARM32 Linux, and determines if fallback is needed.
|
/// Checks if Node24 is requested but running on ARM32 Linux, and determines if fallback is needed.
|
||||||
@@ -26,14 +113,50 @@ namespace GitHub.Runner.Common.Util
|
|||||||
/// <returns>A tuple containing the adjusted node version and an optional warning message</returns>
|
/// <returns>A tuple containing the adjusted node version and an optional warning message</returns>
|
||||||
public static (string nodeVersion, string warningMessage) CheckNodeVersionForLinuxArm32(string preferredVersion)
|
public static (string nodeVersion, string warningMessage) CheckNodeVersionForLinuxArm32(string preferredVersion)
|
||||||
{
|
{
|
||||||
if (string.Equals(preferredVersion, "node24", StringComparison.OrdinalIgnoreCase) &&
|
if (string.Equals(preferredVersion, Constants.Runner.NodeMigration.Node24, StringComparison.OrdinalIgnoreCase) &&
|
||||||
Constants.Runner.PlatformArchitecture.Equals(Constants.Architecture.Arm) &&
|
Constants.Runner.PlatformArchitecture.Equals(Constants.Architecture.Arm) &&
|
||||||
Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
|
Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
|
||||||
{
|
{
|
||||||
return ("node20", "Node 24 is not supported on Linux ARM32 platforms. Falling back to Node 20.");
|
return (Constants.Runner.NodeMigration.Node20, "Node 24 is not supported on Linux ARM32 platforms. Falling back to Node 20.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (preferredVersion, null);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,6 +417,21 @@ 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 ||
|
||||||
|
|||||||
@@ -153,8 +153,8 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
registerToken = await GetRunnerTokenAsync(command, inputUrl, "registration");
|
registerToken = await GetRunnerTokenAsync(command, inputUrl, "registration");
|
||||||
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
|
GitHubAuthResult authResult = await GetTenantCredential(inputUrl, registerToken, Constants.RunnerEvent.Register);
|
||||||
runnerSettings.ServerUrl = authResult.TenantUrl;
|
runnerSettings.ServerUrl = authResult.TenantUrl;
|
||||||
runnerSettings.UseV2Flow = authResult.UseV2Flow;
|
runnerSettings.UseRunnerAdminFlow = authResult.UseRunnerAdminFlow;
|
||||||
Trace.Info($"Using V2 flow: {runnerSettings.UseV2Flow}");
|
Trace.Info($"Using runner-admin flow: {runnerSettings.UseRunnerAdminFlow}");
|
||||||
creds = authResult.ToVssCredentials();
|
creds = authResult.ToVssCredentials();
|
||||||
Trace.Info("cred retrieved via GitHub auth");
|
Trace.Info("cred retrieved via GitHub auth");
|
||||||
}
|
}
|
||||||
@@ -211,7 +211,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
string poolName = null;
|
string poolName = null;
|
||||||
TaskAgentPool agentPool = null;
|
TaskAgentPool agentPool = null;
|
||||||
List<TaskAgentPool> agentPools;
|
List<TaskAgentPool> agentPools;
|
||||||
if (runnerSettings.UseV2Flow)
|
if (runnerSettings.UseRunnerAdminFlow)
|
||||||
{
|
{
|
||||||
agentPools = await _dotcomServer.GetRunnerGroupsAsync(runnerSettings.GitHubUrl, registerToken);
|
agentPools = await _dotcomServer.GetRunnerGroupsAsync(runnerSettings.GitHubUrl, registerToken);
|
||||||
}
|
}
|
||||||
@@ -259,7 +259,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
var userLabels = command.GetLabels();
|
var userLabels = command.GetLabels();
|
||||||
_term.WriteLine();
|
_term.WriteLine();
|
||||||
List<TaskAgent> agents;
|
List<TaskAgent> agents;
|
||||||
if (runnerSettings.UseV2Flow)
|
if (runnerSettings.UseRunnerAdminFlow)
|
||||||
{
|
{
|
||||||
agents = await _dotcomServer.GetRunnerByNameAsync(runnerSettings.GitHubUrl, registerToken, runnerSettings.AgentName);
|
agents = await _dotcomServer.GetRunnerByNameAsync(runnerSettings.GitHubUrl, registerToken, runnerSettings.AgentName);
|
||||||
}
|
}
|
||||||
@@ -280,7 +280,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (runnerSettings.UseV2Flow)
|
if (runnerSettings.UseRunnerAdminFlow)
|
||||||
{
|
{
|
||||||
var runner = await _dotcomServer.ReplaceRunnerAsync(runnerSettings.PoolId, agent, runnerSettings.GitHubUrl, registerToken, publicKeyXML);
|
var runner = await _dotcomServer.ReplaceRunnerAsync(runnerSettings.PoolId, agent, runnerSettings.GitHubUrl, registerToken, publicKeyXML);
|
||||||
runnerSettings.ServerUrlV2 = runner.RunnerAuthorization.ServerUrl;
|
runnerSettings.ServerUrlV2 = runner.RunnerAuthorization.ServerUrl;
|
||||||
@@ -330,7 +330,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (runnerSettings.UseV2Flow)
|
if (runnerSettings.UseRunnerAdminFlow)
|
||||||
{
|
{
|
||||||
var runner = await _dotcomServer.AddRunnerAsync(runnerSettings.PoolId, agent, runnerSettings.GitHubUrl, registerToken, publicKeyXML);
|
var runner = await _dotcomServer.AddRunnerAsync(runnerSettings.PoolId, agent, runnerSettings.GitHubUrl, registerToken, publicKeyXML);
|
||||||
runnerSettings.ServerUrlV2 = runner.RunnerAuthorization.ServerUrl;
|
runnerSettings.ServerUrlV2 = runner.RunnerAuthorization.ServerUrl;
|
||||||
@@ -400,13 +400,26 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
throw new NotSupportedException("Message queue listen OAuth token.");
|
throw new NotSupportedException("Message queue listen OAuth token.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// allow the server to override the serverUrlV2 and useV2Flow
|
||||||
|
if (agent.Properties.TryGetValue("ServerUrlV2", out string serverUrlV2) &&
|
||||||
|
!string.IsNullOrEmpty(serverUrlV2))
|
||||||
|
{
|
||||||
|
Trace.Info($"Service enforced serverUrlV2: {serverUrlV2}");
|
||||||
|
runnerSettings.ServerUrlV2 = serverUrlV2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agent.Properties.TryGetValue("UseV2Flow", out bool useV2Flow) && useV2Flow)
|
||||||
|
{
|
||||||
|
Trace.Info($"Service enforced useV2Flow: {useV2Flow}");
|
||||||
|
runnerSettings.UseV2Flow = useV2Flow;
|
||||||
|
}
|
||||||
|
|
||||||
// Testing agent connection, detect any potential connection issue, like local clock skew that cause OAuth token expired.
|
// Testing agent connection, detect any potential connection issue, like local clock skew that cause OAuth token expired.
|
||||||
|
|
||||||
if (!runnerSettings.UseV2Flow)
|
if (!runnerSettings.UseV2Flow && !runnerSettings.UseRunnerAdminFlow)
|
||||||
{
|
{
|
||||||
var credMgr = HostContext.GetService<ICredentialManager>();
|
var credMgr = HostContext.GetService<ICredentialManager>();
|
||||||
VssCredentials credential = credMgr.LoadCredentials(allowAuthUrlV2: false);
|
VssCredentials credential = credMgr.LoadCredentials(allowAuthUrlV2: false);
|
||||||
@@ -429,20 +442,6 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow the server to override the serverUrlV2 and useV2Flow
|
|
||||||
if (agent.Properties.TryGetValue("ServerUrlV2", out string serverUrlV2) &&
|
|
||||||
!string.IsNullOrEmpty(serverUrlV2))
|
|
||||||
{
|
|
||||||
Trace.Info($"Service enforced serverUrlV2: {serverUrlV2}");
|
|
||||||
runnerSettings.ServerUrlV2 = serverUrlV2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (agent.Properties.TryGetValue("UseV2Flow", out bool useV2Flow) && useV2Flow)
|
|
||||||
{
|
|
||||||
Trace.Info($"Service enforced useV2Flow: {useV2Flow}");
|
|
||||||
runnerSettings.UseV2Flow = useV2Flow;
|
|
||||||
}
|
|
||||||
|
|
||||||
_term.WriteSection("Runner settings");
|
_term.WriteSection("Runner settings");
|
||||||
|
|
||||||
// We will Combine() what's stored with root. Defaults to string a relative path
|
// We will Combine() what's stored with root. Defaults to string a relative path
|
||||||
@@ -537,41 +536,50 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
if (isConfigured && hasCredentials)
|
if (isConfigured && hasCredentials)
|
||||||
{
|
{
|
||||||
RunnerSettings settings = _store.GetSettings();
|
RunnerSettings settings = _store.GetSettings();
|
||||||
var credentialManager = HostContext.GetService<ICredentialManager>();
|
|
||||||
|
|
||||||
// Get the credentials
|
if (settings.UseRunnerAdminFlow)
|
||||||
VssCredentials creds = null;
|
|
||||||
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");
|
var deletionToken = await GetRunnerTokenAsync(command, settings.GitHubUrl, "remove");
|
||||||
GitHubAuthResult authResult = await GetTenantCredential(settings.GitHubUrl, deletionToken, Constants.RunnerEvent.Remove);
|
await _dotcomServer.DeleteRunnerAsync(settings.GitHubUrl, deletionToken, settings.AgentId);
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
await _runnerServer.DeleteAgentAsync(settings.AgentId);
|
var credentialManager = HostContext.GetService<ICredentialManager>();
|
||||||
|
|
||||||
_term.WriteLine();
|
// Get the credentials
|
||||||
_term.WriteSuccessMessage("Runner removed successfully");
|
VssCredentials creds = null;
|
||||||
|
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();
|
||||||
|
_term.WriteSuccessMessage("Runner removed successfully");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
|
|
||||||
[DataMember(Name = "use_v2_flow")]
|
[DataMember(Name = "use_v2_flow")]
|
||||||
public bool UseV2Flow { get; set; }
|
public bool UseRunnerAdminFlow { get; set; }
|
||||||
|
|
||||||
public VssCredentials ToVssCredentials()
|
public VssCredentials ToVssCredentials()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ 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);
|
||||||
@@ -52,7 +53,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;
|
||||||
@@ -217,7 +218,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();
|
||||||
@@ -250,7 +251,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,
|
||||||
@@ -274,7 +275,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,
|
||||||
@@ -437,6 +438,21 @@ 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 ||
|
||||||
|
|||||||
@@ -654,22 +654,42 @@ namespace GitHub.Runner.Listener
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
var messageRef = StringUtil.ConvertFromJson<RunnerJobRequestRef>(message.Body);
|
var messageRef = StringUtil.ConvertFromJson<RunnerJobRequestRef>(message.Body);
|
||||||
Pipelines.AgentJobRequestMessage jobRequestMessage = null;
|
|
||||||
|
|
||||||
// Create connection
|
// Acknowledge (best-effort)
|
||||||
var credMgr = HostContext.GetService<ICredentialManager>();
|
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;
|
||||||
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);
|
||||||
@@ -698,7 +718,10 @@ 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.");
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ 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; }
|
||||||
|
|||||||
@@ -11,5 +11,10 @@ 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,11 +191,19 @@ 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(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(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");
|
||||||
|
|||||||
@@ -58,10 +58,41 @@ 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 = "node20";
|
nodeData.NodeVersion = Common.Constants.Runner.NodeMigration.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;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Azure.Storage.Blobs" Version="12.24.0" />
|
<PackageReference Include="Azure.Storage.Blobs" Version="12.25.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" />
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ 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);
|
||||||
@@ -142,7 +143,6 @@ 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,6 +191,76 @@ 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))
|
||||||
|
|||||||
@@ -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.Exactly(2));
|
_credentialManager.Verify(x => x.LoadCredentials(true), Times.AtLeast(2));
|
||||||
|
|
||||||
Assert.False(hc.AllowAuthMigration);
|
Assert.False(hc.AllowAuthMigration);
|
||||||
}
|
}
|
||||||
|
|||||||
120
src/Test/L0/Util/NodeUtilL0.cs
Normal file
120
src/Test/L0/Util/NodeUtilL0.cs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||||
<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" />
|
||||||
|
|||||||
@@ -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.412"
|
DOTNETSDK_VERSION="8.0.413"
|
||||||
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
|
DOTNETSDK_INSTALLDIR="$DOTNETSDK_ROOT/$DOTNETSDK_VERSION"
|
||||||
RUNNER_VERSION=$(cat runnerversion)
|
RUNNER_VERSION=$(cat runnerversion)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "8.0.412"
|
"version": "8.0.413"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
2.327.0
|
2.328.0
|
||||||
|
|||||||
Reference in New Issue
Block a user