· Joseph · AI & Machine Learning  · 6 min read

AI code review with n8n

Previously I read a post "Automate and Accelerate GitLab Code Reviews with OpenAI and n8n.io".

This made me wonder: If I don’t choose GitHub Copilot for code reviews, can I still integrate AI and n8n with GitHub PR reviews?

I haven’t written a blog in a long time—it’s time to start again!

Previously I read a post “Automate and Accelerate GitLab Code Reviews with OpenAI and n8n.io”.

This made me wonder: If I don’t choose GitHub Copilot for code reviews, can I still integrate AI and n8n with GitHub PR reviews?

I haven’t written a blog in a long time—it’s time to start again!

TOC

Prerequirement

  1. n8n
  2. Github
  3. Groq

I need to use n8n to build an automated workflow that reviews and comments on GitHub PRs. Additionally, I need a Github Personal access token and a Groq API key so that the n8n workflow can authenticate with them.

Workflow

workflow

This is the complete workflow, from start to finish. First, when I trigger the test workflow, it fetches all my PRs from GitHub and selects the latest one. Next, it retrieves the diff files, separates them, and splits them into individual items. The workflow then processes the diff by removing empty items, filtering out deleted file changes, and eliminating renamed items. After that, a random selection of files is made and organized. These selected file pairs are then passed to an AI model, which analyzes the changes. Finally, the AI-generated feedback is posted as a comment on the PR.

Let’s dig into each stage now.

Top stage: fetch PR

first stage

In my Github Node, I set an access token to the Github account, send an operation to Get Pull Requests to retrive Repository resource and assign the owner and name. github account github node

The next two nodes filter pull requests by my name and retrieve my latest PR. filter my pr my latest pr

Once this stage is complete, the output is my most recent GitHub pull request, which is then passed to the next stage.

Second stage: organize diffs

second stage

At this stage, Firstly, I send an HTTP Request to fetch the PR diff, using an Auth Header to prevent an Unauthorized error. However, I must highlight that the Authorization value must be prefixed with Bearer string before your GitHub token. auth header http request

Soon, you will notice that the HTTP Request output is a string value. So I have to convert to string array by ‘git —diff’ and then separating the array list into multiple items.

$json.data.split('diff --git');

separate to array split into items

Third stage: clean and select files

third stage

There are four filter nodes which is:

Node NameCondition
Remove first empty item{{ $itemIndex }} is not equal to 0
Remove deleted file change{{ $json.data }} does not contain deleted file mode
Remove rename item{{ $json.data }} does not contain rename from
AND
{{ $json.data }} does not contain rename to

When this step is completed, all items should be modified in the PR. In this demo I just randomly choose five files to review, so I simply use a Random Node to shuffle those items, and filter out first five items.

# Random selection
 `{{ $itemIndex }}` `is less then` **5**

The important step Organize diff should write some code. I refer to the blog and copy the whole code from gist. After some try and error, I made some modification as blow:

The important step, Organize Diff, requires writing some code. I referred to the blog and copied the entire code from the gist. After some trial and error, I made some modifications as follows.

function getLastDiff(inputDiff) {
  const cleanedDiff = inputDiff.replace(/\n\\ No newline at end of file/, '');
  const cleanedDiffLines = cleanedDiff.trimEnd().split('\n').reverse();

  const fileNameLine = cleanedDiffLines.find((line) => /^ a\/(.+) b\/(.+)/g.test(line));
  const [, oldFileName, newFileName] = fileNameLine.match(/^ a\/(.+) b\/(.+)/);
  const lastDiffHeaderLine = cleanedDiffLines.find((line) => /^@@ -\d+,\d+ \+(\d+),(\d+) @@/g.test(line));

  let oldFileTotalLineCount, newFileTotalLineCount;
  if (lastDiffHeaderLine) {
    const [, oldStartLineCount, oldEndLineCount, newStartLineCount, newEndLineCount] = lastDiffHeaderLine.match(
      /@@ -(\d+),(\d+) \+(\d+),(\d+) @@/
    );
    oldFileTotalLineCount = parseInt(oldStartLineCount, 10) + parseInt(oldEndLineCount, 10);
    newFileTotalLineCount = parseInt(newStartLineCount, 10) + parseInt(newEndLineCount, 10);
  } else {
    oldFileTotalLineCount = -1;
    newFileTotalLineCount = -1;
  }

  const firstCharOfLastLine = cleanedDiffLines[0]?.[0];
  const lastOldFileLine =
    oldFileTotalLineCount >= 0 ? (firstCharOfLastLine === '+' ? null : oldFileTotalLineCount - 1) : -1;
  const lastNewFileLine =
    newFileTotalLineCount >= 0 ? (firstCharOfLastLine === '-' ? null : newFileTotalLineCount - 1) : -1;

  // 5. Return the parsed data
  return {
    lastOldFileLine,
    lastNewFileLine,
    oldFileName,
    newFileName,
    cleanedDiff,
  };
}

// Processes the cleaned diff information to separate the lines of code added, deleted, and unchanged in the old and new code
function extractCodeFromDiff(cleanedDiff) {
  // 1. Split the cleaned diff into lines, removing any trailing whitespace
  const diffLines = cleanedDiff.trimEnd().split('\n');

  // 2. Initialize an object to store parsed code lines
  const parsedCodeLines = {
    original: [], // Array to hold lines from the original code
    new: [], // Array to hold lines from the new code
  };

  diffLines.forEach((line) => {
    if (line.startsWith('-')) {
      parsedCodeLines.original.push(line);
    } else if (line.startsWith('+')) {
      parsedCodeLines.new.push(line);
    } else {
      parsedCodeLines.original.push(line);
      parsedCodeLines.new.push(line);
    }
  });

  return {
    originalCode: parsedCodeLines.original.join('\n'),
    newCode: parsedCodeLines.new.join('\n'),
  };
}

function parseGitDiff() {
  const lastDiff = getLastDiff($input.item.json.data);
  const extracCode = extractCodeFromDiff(lastDiff.cleanedDiff);
  return {
    lastOldFileLine: lastDiff.lastOldFileLine,
    lastNewFileLine: lastDiff.lastNewFileLine,
    originalCode: extracCode.originalCode,
    newCode: extracCode.newCode,
    fileName: lastDiff.newFileName,
  };
}
return parseGitDiff();

The output contains fileName, originalCode, and newCode, allowing me to write a prompt and ask AI to review my codes.

Final stage: ask AI agent

final stage

All the prompts and system messages can be referred to the original blog. I only write down the different part.

Here I integrate the Groq chat model and choose llama3 as the AI model. Just paste Groq api key to check whether the connection is successfully or not.

Groq Groq api

Lastly, I send an HTTP POST request with the following information:

NameValue
URL{{ $('GitHub').item.json._links.review_comments.href }}
AuthenticationPredefined Credential Type
Cretential TypeGitHub API
GitHub APITo create an credential account with your UserName and Access token
Send Header{Accept: application/vnd.github+json}
Send BodyJSON and specify body by Using Fields Below

Body parameters:

Namevalue
body{{ $json.text }}
commit_id{{ $('My PRs').item.json.head.sha }}
path{{ $('Organize Diff').item.json.fileName }}
subject_typefile

In this step, I use the review_comments.href from $(‘GitHub’) node, the text from the previous LLM model node, the sha from $(‘My PRs’) node, and the fileName from $(‘Organize Diff’) node. Additionally, I have created a GitHub API Credential using my username and access token.

post body

Okay, now let’s take a look of the review. result

Amazing! I don’t need to handle either GitHub authorization or the API request/response of the AI chat model. Just a few lines of code can implement a code review workflow with an AI model. If you care about privacy, you can even try n8n docker and ollama on your machine—that’s so cool!

I hope this article is helpful, and I will try to design more n8n workflows with AI model. Thanks for reading!

Reference

  1. Automate and Accelerate GitLab Code Reviews with OpenAI and n8n.io
  2. n8n
  3. groq
Share:
Back to Blog

Related Posts

View All Posts »
Use Grafana MCP with Gemini and n8n

Use Grafana MCP with Gemini and n8n

The Model Context Protocol (MCP) is extremely useful. An AI assistant helps you decide when and how to use connected tools, so you only need to configure them. After integrating MCP logging management systems into several of my projects, it has saved me a significant amount of time. In this article, I'm going to integrate Grafana with the Gemini CLI and n8n. I will chat with the Gemini CLI and n8n and have them invoke the Grafana MCP server. structure TOC

Use Figma MCP server with Gemini CLI

Use Figma MCP server with Gemini CLI

In this article, I won't introduce what MCP is. Instead, I will explain how to set up the Figma MCP server and use Gemini as an MCP client to work with Figma. I will also show you how to run a prompt to get a Figma design with Gemini. TOC

Install Gemini CLI

Install Gemini CLI

Introduction Gemini CLI has been one of the most popular AI agents in the first half of 2025. It's similar to Claude Code, bringing its power directly into your terminal. Although other terminal AI agents exist, their pricing plans are quite different. Gemini CLI provides a free tier with 100 requests per day using Gemini 2.5 Pro, and you can unlock Tier 1 by upgrading to a paid plan. Prerequisites I'm going to use npm to install Gemini. My Node.js version is v24.4.1, and my npm version is 11.4.2. Gemini needs Node.js version 20 or higher installed. If you're using macOS, you can also choose Homebrew to install the Gemini CLI. Installation Now, let's install it using npm. After installation, you can run gemini directly in your terminal. npm install -g @google/gemini-cli installation I'm using the Use Gemini API key authentication method, so I need to generate a key from Google AI Studio and set it in .zshrc (or .bashrc) by adding this line: And then you can try Gemini now! Run some examples example Prompt: give me suggestions for the socket functionality of this project? Response: Conclusion: The Gemini installation is very simple. Although I am using Neovim with Avante, Gemini gives me more power to use the terminal. Next, I will explore how to use Gemini with an MCP server and integrate the workflow into my daily tasks.

Use ChatGPT to translate new react-dev doc

Use ChatGPT to translate new react-dev doc

react.dev was released on March 17. I've read the beta version for a while. I love the Escape Hatches section which has many correct and recommended usages about react hooks. After new react.dev released, I noticed that there's no translation. I haven'n played OpenAI API yet, so I think this is a good opportunity to play ChatGPT with its translation feature for react.dev. TOC