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 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Checkout nodejs and create new patch run: | set +e # Don't exit on errors, we want to handle them ourselves # find the patch file by checking the patch file matching major version input node version 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 # extract patch version from PATCH_FILE. E.g. node.v20.11.1.cpp.patch --> 20.11.1 PATCH_VERSION=$(echo $PATCH_FILE | grep -oP 'node.v\K[0-9]+\.[0-9]+\.[0-9]+') echo "Patch file: $PATCH_FILE" # check if patch file exists if [ ! -f "$PATCH_FILE" ]; then echo "No patch file found for Node.js version ${{ inputs.nodeVersion }}" exit 1 fi cd .. echo "Cloning Node.js repository" git clone -b v${{ inputs.nodeVersion }} --single-branch https://github.com/nodejs/node.git cd node # Try to apply the patch cleanly first echo "Applying patch $PATCH_FILE" git apply --check ../pkg-fetch/$PATCH_FILE PATCH_CHECK_RESULT=$? if [ $PATCH_CHECK_RESULT -eq 0 ]; then echo "✅ Patch $PATCH_FILE applies cleanly" git apply ../pkg-fetch/$PATCH_FILE echo "PATCH_STATUS=clean" >> $GITHUB_ENV else echo "⚠️ Patch $PATCH_FILE does not apply cleanly, attempting conflict resolution" # Apply patch with --reject to create .rej files git apply --reject ../pkg-fetch/$PATCH_FILE || true # Check if we have any .rej files REJECT_COUNT=$(find . -name "*.rej" -type f | wc -l) if [ $REJECT_COUNT -eq 0 ]; then echo "ℹ️ No reject files found, patch applied partially" echo "PATCH_STATUS=partial" >> $GITHUB_ENV else echo "🤖 Found $REJECT_COUNT reject files, using AI to resolve conflicts" # Use our Python script to resolve conflicts if [ -n "${{ secrets.OPENAI_KEY }}" ]; then echo "Using OpenAI API for conflict resolution" python3 ../pkg-fetch/.github/scripts/openai_resolver.py . "${{ secrets.OPENAI_KEY }}" > resolution_output.txt 2>&1 RESOLUTION_RESULT=$? else echo "No OpenAI API key found, creating manual resolution files only" python3 ../pkg-fetch/.github/scripts/openai_resolver.py . "dummy" "create_manual_only" > resolution_output.txt 2>&1 RESOLUTION_RESULT=1 fi # Extract results from Python script output CONFLICTS_RESOLVED=$(grep "CONFLICTS_RESOLVED=" resolution_output.txt | cut -d'=' -f2) TOTAL_CONFLICTS=$(grep "TOTAL_CONFLICTS=" resolution_output.txt | cut -d'=' -f2) HAS_UNRESOLVED=$(grep "HAS_UNRESOLVED=" resolution_output.txt | cut -d'=' -f2) FAILED_FILES_LINE=$(grep "FAILED_FILES=" resolution_output.txt | cut -d'=' -f2) echo "PATCH_STATUS=conflicts" >> $GITHUB_ENV echo "CONFLICTS_RESOLVED=${CONFLICTS_RESOLVED:-0}" >> $GITHUB_ENV echo "TOTAL_CONFLICTS=${TOTAL_CONFLICTS:-0}" >> $GITHUB_ENV # Show resolution summary cat resolution_output.txt # Check if we have unresolved conflicts - if so, exit without creating PR if [ "${HAS_UNRESOLVED:-false}" = "True" ]; then echo "❌ There are unresolved conflicts. Exiting without creating PR." echo "Please resolve the conflicts manually and re-run the workflow." exit 1 fi fi fi # Only proceed with patch creation if we got here (no unresolved conflicts) echo "✅ All conflicts resolved or patch applied successfully. Creating new patch file." # Store resolution output for PR body if it exists if [ -f "../node/resolution_output.txt" ]; then echo "RESOLUTION_OUTPUT<> $GITHUB_ENV cat ../node/resolution_output.txt >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV # delete resolution output file rm -f ../node/resolution_output.txt else echo "RESOLUTION_OUTPUT=" >> $GITHUB_ENV fi # delete old patch file and create new one rm -rf ../pkg-fetch/$PATCH_FILE git add -A git diff --staged --src-prefix=node/ --dst-prefix=node/ > ../pkg-fetch/patches/node.v${{ inputs.nodeVersion }}.cpp.patch # Update patches.json echo "Updating patches.json" cd ../pkg-fetch/patches sed -i "s/\"v$PATCH_VERSION\": \[\"node.v$PATCH_VERSION.cpp.patch\"\]/\"v${{ inputs.nodeVersion }}\": \[\"node.v${{ inputs.nodeVersion }}.cpp.patch\"\]/" patches.json # Set default values for environment variables if not already set echo "PATCH_STATUS=${PATCH_STATUS:-clean}" >> $GITHUB_ENV echo "HAS_UNRESOLVED=${HAS_UNRESOLVED:-false}" >> $GITHUB_ENV echo "CREATE_PR=true" >> $GITHUB_ENV cd .. - name: Create Pull Request if: env.CREATE_PR == 'true' 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 }}. The workflow automatically attempts to resolve patch conflicts using AI when the OpenAI API key is available. ${{ env.RESOLUTION_OUTPUT && '### AI Resolution Details' || '' }} ${{ env.RESOLUTION_OUTPUT }} branch: "nodejs-v${{ inputs.nodeVersion }}" base: "main" delete-branch: true labels: "enhancement,nodejs" draft: false