Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/scripts/release-label-backfill.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { pathToFileURL } from "node:url";

const DEPENDENCIES_LABEL = {
name: "dependencies",
color: "0366d6",
description: "Dependency updates and version bumps",
};

async function githubRequest(token, path, { method = "GET", body } = {}) {
const response = await fetch(`https://api.github.com${path}`, {
method,
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${token}`,
"User-Agent": "kagent-release-label-backfill",
"X-GitHub-Api-Version": "2022-11-28",
},
body: body ? JSON.stringify(body) : undefined,
});

if (response.status === 204) {
return null;
}

const text = await response.text();
const payload = text ? JSON.parse(text) : null;

if (!response.ok) {
const message = payload?.message ?? response.statusText;
const error = new Error(`${method} ${path} failed: ${response.status} ${message}`);
error.status = response.status;
throw error;
}

return payload;
}

export async function ensureDependenciesLabel({
token = process.env.GITHUB_TOKEN,
repository = process.env.GITHUB_REPOSITORY,
} = {}) {
if (!token) {
throw new Error("GITHUB_TOKEN is required");
}
if (!repository) {
throw new Error("GITHUB_REPOSITORY is required");
}

try {
await githubRequest(
token,
`/repos/${repository}/labels/${encodeURIComponent(DEPENDENCIES_LABEL.name)}`,
);
console.log(`label exists: ${DEPENDENCIES_LABEL.name}`);
return;
} catch (error) {
if (error.status !== 404) {
throw error;
}
}

await githubRequest(token, `/repos/${repository}/labels`, {
method: "POST",
body: DEPENDENCIES_LABEL,
});
console.log(`created label: ${DEPENDENCIES_LABEL.name}`);
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
await ensureDependenciesLabel();
}
71 changes: 71 additions & 0 deletions .github/scripts/release-label-backfill.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import test from "node:test";
import assert from "node:assert/strict";

import { ensureDependenciesLabel } from "./release-label-backfill.mjs";

test("ensureDependenciesLabel skips creation when the label already exists", async () => {
const requests = [];
const originalFetch = global.fetch;

global.fetch = async (url, options = {}) => {
requests.push({ url, options });
return new Response(JSON.stringify({ name: "dependencies" }), {
status: 200,
headers: { "content-type": "application/json" },
});
};

try {
await ensureDependenciesLabel({
token: "test-token",
repository: "kagent-dev/kagent",
});
} finally {
global.fetch = originalFetch;
}

assert.equal(requests.length, 1);
assert.match(requests[0].url, /\/repos\/kagent-dev\/kagent\/labels\/dependencies$/);
assert.equal(requests[0].options.method ?? "GET", "GET");
});

test("ensureDependenciesLabel creates the label when it is missing", async () => {
const requests = [];
const originalFetch = global.fetch;

global.fetch = async (url, options = {}) => {
requests.push({ url, options });

if (requests.length === 1) {
return new Response(JSON.stringify({ message: "Not Found" }), {
status: 404,
headers: { "content-type": "application/json" },
});
}

return new Response(JSON.stringify({ name: "dependencies" }), {
status: 201,
headers: { "content-type": "application/json" },
});
};

try {
await ensureDependenciesLabel({
token: "test-token",
repository: "kagent-dev/kagent",
});
} finally {
global.fetch = originalFetch;
}

assert.equal(requests.length, 2);
assert.match(requests[0].url, /\/repos\/kagent-dev\/kagent\/labels\/dependencies$/);
assert.equal(requests[0].options.method ?? "GET", "GET");
assert.match(requests[1].url, /\/repos\/kagent-dev\/kagent\/labels$/);
assert.equal(requests[1].options.method, "POST");
assert.deepEqual(JSON.parse(requests[1].options.body), {
name: "dependencies",
color: "0366d6",
description: "Dependency updates and version bumps",
});
});
26 changes: 26 additions & 0 deletions .github/workflows/release-label-backfill.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Ensure Dependencies Label

on:
push:
branches: [main]
paths:
- ".github/workflows/release-label-backfill.yml"
- ".github/release.yml"
- ".github/dependabot.yml"
- ".github/scripts/release-label-backfill.mjs"
workflow_dispatch:

jobs:
ensure-dependencies-label:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Ensure dependencies label exists
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node .github/scripts/release-label-backfill.mjs
Loading