Skip to content
Merged
37 changes: 36 additions & 1 deletion datadog-responder/dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31556,6 +31556,7 @@ exports.createOrUpdateFile = createOrUpdateFile;
exports.createBranch = createBranch;
exports.getDefaultBranch = getDefaultBranch;
exports.getRepoTree = getRepoTree;
exports.listReleaseNotesBetween = listReleaseNotesBetween;
const github = __importStar(__nccwpck_require__(2146));
function getOctokitClient(token) {
return github.getOctokit(token);
Expand Down Expand Up @@ -31591,6 +31592,7 @@ async function getPullRequest(octokit, owner, repo, prNumber) {
number: prResponse.data.number,
title: prResponse.data.title,
body: prResponse.data.body,
author: prResponse.data.user?.login ?? "",
diff: diffResponse.data,
files: filesResponse.data.map((f) => ({
filename: f.filename,
Expand Down Expand Up @@ -31699,6 +31701,38 @@ async function getRepoTree(octokit, owner, repo, sha, recursive = true) {
type: item.type,
}));
}
async function listReleaseNotesBetween(octokit, owner, repo, fromVersion, toVersion) {
try {
const { data: releases } = await octokit.rest.repos.listReleases({
owner,
repo,
per_page: 100,
});
const normalize = (v) => v.replace(/^v/, "");
const from = normalize(fromVersion);
const to = normalize(toVersion);
const relevant = [];
let foundTo = false;
for (const release of releases) {
const tag = normalize(release.tag_name);
if (tag === to)
foundTo = true;
if (foundTo && tag !== from) {
if (release.body) {
relevant.push({ tag: release.tag_name, body: release.body });
}
}
if (tag === from)
break;
}
if (relevant.length === 0)
return null;
return relevant.map((r) => `### ${r.tag}\n${r.body}`).join("\n\n");
}
catch {
return null;
}
}
//# sourceMappingURL=github.js.map

/***/ }),
Expand All @@ -31709,7 +31743,7 @@ async function getRepoTree(octokit, owner, repo, sha, recursive = true) {
"use strict";

Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getRepoTree = exports.getDefaultBranch = exports.createBranch = exports.createOrUpdateFile = exports.createReview = exports.createPullRequest = exports.postComment = exports.getFileContent = exports.getPullRequest = exports.getIssue = exports.getRepoContext = exports.getOctokitClient = exports.truncateText = exports.countTokens = exports.generateContent = exports.createGeminiModel = void 0;
exports.listReleaseNotesBetween = exports.getRepoTree = exports.getDefaultBranch = exports.createBranch = exports.createOrUpdateFile = exports.createReview = exports.createPullRequest = exports.postComment = exports.getFileContent = exports.getPullRequest = exports.getIssue = exports.getRepoContext = exports.getOctokitClient = exports.truncateText = exports.countTokens = exports.generateContent = exports.createGeminiModel = void 0;
var gemini_1 = __nccwpck_require__(9700);
Object.defineProperty(exports, "createGeminiModel", ({ enumerable: true, get: function () { return gemini_1.createGeminiModel; } }));
Object.defineProperty(exports, "generateContent", ({ enumerable: true, get: function () { return gemini_1.generateContent; } }));
Expand All @@ -31728,6 +31762,7 @@ Object.defineProperty(exports, "createOrUpdateFile", ({ enumerable: true, get: f
Object.defineProperty(exports, "createBranch", ({ enumerable: true, get: function () { return github_1.createBranch; } }));
Object.defineProperty(exports, "getDefaultBranch", ({ enumerable: true, get: function () { return github_1.getDefaultBranch; } }));
Object.defineProperty(exports, "getRepoTree", ({ enumerable: true, get: function () { return github_1.getRepoTree; } }));
Object.defineProperty(exports, "listReleaseNotesBetween", ({ enumerable: true, get: function () { return github_1.listReleaseNotesBetween; } }));
//# sourceMappingURL=index.js.map

/***/ }),
Expand Down
24 changes: 21 additions & 3 deletions dependency-impact/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Deterministic tools can tell you a major version changed. Only an LLM can read a
- **pip** — `requirements.txt` / `Pipfile`
- **Go** — `go.mod`
- **Terraform** — `.terraform.lock.hcl`
- **Composer** — `composer.json` / `composer.lock`

## Usage

Expand All @@ -35,6 +36,8 @@ on:
- "requirements.txt"
- "go.mod"
- ".terraform.lock.hcl"
- "composer.json"
- "composer.lock"

jobs:
analyze:
Expand All @@ -50,8 +53,23 @@ jobs:

## How it works

1. Fetches the PR diff and parses dependency version changes
1. Fetches the PR diff and parses dependency version changes from lock files and manifests
2. Scans source files (up to 100) for imports of the changed dependencies
3. Extracts the relevant usage lines for context
4. Asks Gemini to analyze breaking changes, affected files, migration steps, and risk level
3. Fetches release notes using a layered approach:
- For bot PRs (Dependabot, Renovate) with a populated body, uses the PR body directly — these bots already aggregate changelogs between versions
- Otherwise, tries the GitHub Releases API for dependencies with resolvable repos (Go modules, Terraform providers, npm packages, Composer packages)
- Falls back to the PR body if GitHub Releases yields nothing
4. Sends a tailored prompt to Gemini depending on whether usage was found:
- **With usage**: cross-references release notes against actual code. Reports only confirmed breaking changes, required actions, and risk
- **Without usage**: summarizes release notes highlights. No fabricated impact analysis
5. Posts the analysis as a PR comment

## Release notes sourcing

The action needs real release notes to produce useful output — without them, any LLM will fill the gap with speculation. Release notes are resolved in priority order:

| Priority | Source | When used |
|----------|--------|-----------|
| 1 | PR body | PR author is a bot (`[bot]` suffix) and body has meaningful content (>50 chars) |
| 2 | GitHub Releases API | Dependency maps to a GitHub repo (Go modules via path, Terraform providers via registry convention, npm via registry lookup, Composer via Packagist lookup) |
| 3 | PR body (fallback) | GitHub Releases returned nothing but the PR body has content |
Loading