Skip to content

Create new Node.js patch #331

Create new Node.js patch

Create new Node.js patch #331

Workflow file for this run

name: Create new Node.js patch
on:
workflow_dispatch:
inputs:
nodeVersion:
description: 'Node.js version (e.g. 20.11.0)'
default: ''
type: string
required: true
patchFile:
description: 'Patch version to use (e.g. 20.11.0). Leave empty to use the matching major patch'
default: ''
type: string
required: false
jobs:
build:
permissions:
contents: write
pull-requests: write
models: read
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Apply existing patch
id: apply
run: |
set -e
MAJOR_VERSION=$(echo "${{ inputs.nodeVersion }}" | cut -d'.' -f1)
echo "Node.js major version: $MAJOR_VERSION"
if [ -z "${{ inputs.patchFile }}" ]; then
PATCH_FILE=$(ls patches/node.v$MAJOR_VERSION.*.patch)
else
PATCH_FILE="patches/node.v${{ inputs.patchFile }}.cpp.patch"
fi
if [ ! -f "$PATCH_FILE" ]; then
echo "No patch file found for Node.js version ${{ inputs.nodeVersion }}"
exit 1
fi
PATCH_VERSION=$(echo "$PATCH_FILE" | grep -oP 'node\.v\K[0-9]+\.[0-9]+\.[0-9]+')
echo "Patch file: $PATCH_FILE"
echo "Patch version: $PATCH_VERSION"
echo "PATCH_FILE=$PATCH_FILE" >> "$GITHUB_ENV"
echo "PATCH_VERSION=$PATCH_VERSION" >> "$GITHUB_ENV"
# Default so later `${{ env.RESOLUTION_OUTPUT }}` is always defined.
echo "RESOLUTION_OUTPUT=" >> "$GITHUB_ENV"
cd ..
echo "Cloning Node.js v${{ inputs.nodeVersion }}"
git clone -b "v${{ inputs.nodeVersion }}" --single-branch https://github.com/nodejs/node.git
cd node
if git apply --check "../pkg-fetch/$PATCH_FILE" 2>/dev/null; then
echo "✅ Patch applies cleanly"
git apply "../pkg-fetch/$PATCH_FILE"
echo "needs_ai=false" >> "$GITHUB_OUTPUT"
else
echo "⚠️ Patch does not apply cleanly, falling back to --reject"
git apply --reject "../pkg-fetch/$PATCH_FILE" || true
REJECT_COUNT=$(find . -name '*.rej' -type f | wc -l)
if [ "$REJECT_COUNT" -eq 0 ]; then
echo "ℹ️ No reject files produced; patch applied partially"
echo "needs_ai=false" >> "$GITHUB_OUTPUT"
else
echo "🤖 $REJECT_COUNT reject file(s); will use AI to resolve"
echo "needs_ai=true" >> "$GITHUB_OUTPUT"
fi
fi
- name: Build AI prompt
if: steps.apply.outputs.needs_ai == 'true'
run: python3 .github/scripts/ai_resolver.py prepare ../node ai_prompt.md
- name: Resolve conflicts with AI
if: steps.apply.outputs.needs_ai == 'true'
id: ai
uses: actions/ai-inference@v2
with:
prompt-file: ai_prompt.md
system-prompt: |
You are an expert C/C++/JavaScript developer maintaining a Git patch against the Node.js source tree.
Output ONLY the requested search/replace blocks in the exact format the user specifies.
Preserve whitespace and indentation byte-for-byte. No markdown fences, no commentary, no preamble.
# gpt-4.1 outperforms gpt-4o on coding + instruction-following
# (key for the strict SEARCH/REPLACE output format) and is not a
# reasoning model, so its output budget isn't shared with hidden
# CoT tokens. 1M input / 32K output / "high" rate-limit tier.
model: openai/gpt-4.1
max-completion-tokens: 16000
- name: Apply AI resolutions
if: steps.apply.outputs.needs_ai == 'true'
run: |
set -eo pipefail
# Python exits non-zero on unresolved conflicts; pipefail+set -e
# ensures the whole step fails so the PR step is skipped.
python3 .github/scripts/ai_resolver.py apply ../node "${{ steps.ai.outputs.response-file }}" \
| tee resolution_output.txt
{
echo "RESOLUTION_OUTPUT<<EOF"
cat resolution_output.txt
echo "EOF"
} >> "$GITHUB_ENV"
- name: Generate new patch & update patches.json
run: |
set -e
cd ../node
git add -A
git diff --staged --src-prefix=node/ --dst-prefix=node/ \
> "../pkg-fetch/patches/node.v${{ inputs.nodeVersion }}.cpp.patch"
cd ../pkg-fetch
# Replace the old version entry with the new one. If a `patchFile`
# input was supplied, $PATCH_VERSION refers to that source patch.
sed -i \
"s/\"v$PATCH_VERSION\": \[\"node.v$PATCH_VERSION.cpp.patch\"\]/\"v${{ inputs.nodeVersion }}\": \[\"node.v${{ inputs.nodeVersion }}.cpp.patch\"\]/" \
patches/patches.json
# Remove the old patch file unless we just overwrote it in place.
if [ "$PATCH_FILE" != "patches/node.v${{ inputs.nodeVersion }}.cpp.patch" ]; then
rm -f "$PATCH_FILE"
fi
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "feat: add v${{ inputs.nodeVersion }} patch"
title: "feat: add v${{ inputs.nodeVersion }} patch"
body: |
## Node.js Patch Update to v${{ inputs.nodeVersion }}
This PR updates the Node.js patch to version ${{ inputs.nodeVersion }}.
Patch conflicts (if any) were resolved with `actions/ai-inference@v1`
using GitHub Models — no third-party API key required.
${{ env.RESOLUTION_OUTPUT && '### AI Resolution Details' || '' }}
${{ env.RESOLUTION_OUTPUT }}
branch: "nodejs-v${{ inputs.nodeVersion }}"
base: "main"
delete-branch: true
labels: "enhancement,nodejs"
draft: false