Create new Node.js patch #331
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |