From 9c4bc5dc3524f872d535f83937f3c2ac89b55557 Mon Sep 17 00:00:00 2001 From: Cole Davis Date: Fri, 12 Sep 2025 15:21:34 -0400 Subject: [PATCH] Add label to template variables --- action.yml | 2 +- src/github/api/queries/github.ts | 10 +++++ src/github/operations/branch.ts | 12 ++++++ src/github/types.ts | 10 +++++ src/utils/branch-template.ts | 5 +++ test/branch-template.test.ts | 63 ++++++++++++++++++++++++++++++++ test/create-prompt.test.ts | 2 + test/data-formatter.test.ts | 6 +++ 8 files changed, 109 insertions(+), 1 deletion(-) diff --git a/action.yml b/action.yml index b3d4bed..f49eeeb 100644 --- a/action.yml +++ b/action.yml @@ -24,7 +24,7 @@ inputs: required: false default: "claude/" branch_name_template: - description: "Template for branch naming. Available variables: {{prefix}}, {{entityType}}, {{entityNumber}}, {{timestamp}}, {{year}}, {{month}}, {{day}}, {{hour}}, {{minute}}, {{sha}}. Default: '{{prefix}}{{entityType}}-{{entityNumber}}-{{timestamp}}'" + description: "Template for branch naming. Available variables: {{prefix}}, {{entityType}}, {{entityNumber}}, {{timestamp}}, {{year}}, {{month}}, {{day}}, {{hour}}, {{minute}}, {{sha}}, {{label}}. The {{label}} variable uses the first label from the issue/PR, falling back to {{entityType}} if no labels exist. Default: '{{prefix}}{{entityType}}-{{entityNumber}}-{{timestamp}}'" required: false default: "" allowed_bots: diff --git a/src/github/api/queries/github.ts b/src/github/api/queries/github.ts index 2341a55..65fed0d 100644 --- a/src/github/api/queries/github.ts +++ b/src/github/api/queries/github.ts @@ -16,6 +16,11 @@ export const PR_QUERY = ` additions deletions state + labels(first: 10) { + nodes { + name + } + } commits(first: 100) { totalCount nodes { @@ -97,6 +102,11 @@ export const ISSUE_QUERY = ` } createdAt state + labels(first: 10) { + nodes { + name + } + } comments(first: 100) { nodes { id diff --git a/src/github/operations/branch.ts b/src/github/operations/branch.ts index 3b7a038..9f428c8 100644 --- a/src/github/operations/branch.ts +++ b/src/github/operations/branch.ts @@ -14,6 +14,14 @@ import type { Octokits } from "../api/client"; import type { FetchDataResult } from "../data/fetcher"; import { generateBranchName } from "../../utils/branch-template"; +/** + * Extracts the first label from GitHub data, or returns undefined if no labels exist + */ +function extractFirstLabel(githubData: FetchDataResult): string | undefined { + const labels = githubData.contextData.labels?.nodes; + return labels && labels.length > 0 ? labels[0]?.name : undefined; +} + export type BranchInfo = { baseBranch: string; claudeBranch?: string; @@ -102,6 +110,9 @@ export async function setupBranch( sourceSHA = sourceBranchRef.data.object.sha; console.log(`Source branch SHA: ${sourceSHA}`); + // Extract first label from GitHub data + const firstLabel = extractFirstLabel(githubData); + // Generate branch name using template or default format const newBranch = generateBranchName( branchNameTemplate, @@ -109,6 +120,7 @@ export async function setupBranch( entityType, entityNumber, sourceSHA, + firstLabel, ); // For commit signing, defer branch creation to the file ops server diff --git a/src/github/types.ts b/src/github/types.ts index 41e0896..0b66875 100644 --- a/src/github/types.ts +++ b/src/github/types.ts @@ -61,6 +61,11 @@ export type GitHubPullRequest = { additions: number; deletions: number; state: string; + labels: { + nodes: Array<{ + name: string; + }>; + }; commits: { totalCount: number; nodes: Array<{ @@ -84,6 +89,11 @@ export type GitHubIssue = { author: GitHubAuthor; createdAt: string; state: string; + labels: { + nodes: Array<{ + name: string; + }>; + }; comments: { nodes: GitHubComment[]; }; diff --git a/src/utils/branch-template.ts b/src/utils/branch-template.ts index 8cc3344..956dbd1 100644 --- a/src/utils/branch-template.ts +++ b/src/utils/branch-template.ts @@ -15,6 +15,7 @@ export interface BranchTemplateVariables { hour: string; minute: string; sha?: string; + label?: string; } /** @@ -46,6 +47,7 @@ export function createBranchTemplateVariables( entityType: string, entityNumber: number, sha?: string, + label?: string, ): BranchTemplateVariables { const now = new Date(); @@ -60,6 +62,7 @@ export function createBranchTemplateVariables( hour: String(now.getHours()).padStart(2, "0"), minute: String(now.getMinutes()).padStart(2, "0"), sha: sha?.substring(0, 8), // First 8 characters of SHA + label: label || entityType, // Fall back to entityType if no label }; } @@ -72,12 +75,14 @@ export function generateBranchName( entityType: string, entityNumber: number, sha?: string, + label?: string, ): string { const variables = createBranchTemplateVariables( branchPrefix, entityType, entityNumber, sha, + label, ); let branchName: string; diff --git a/test/branch-template.test.ts b/test/branch-template.test.ts index 6d47f2c..762f06f 100644 --- a/test/branch-template.test.ts +++ b/test/branch-template.test.ts @@ -81,6 +81,7 @@ describe("branch template utilities", () => { expect(result.entityType).toBe("issue"); expect(result.entityNumber).toBe(123); expect(result.sha).toBe("abcdef12"); + expect(result.label).toBe("issue"); // fallback to entityType expect(result.timestamp).toMatch(/^\d{8}-\d{4}$/); expect(result.year).toMatch(/^\d{4}$/); expect(result.month).toMatch(/^\d{2}$/); @@ -103,6 +104,33 @@ describe("branch template utilities", () => { const result = createBranchTemplateVariables("test/", "pr", 456); expect(result.sha).toBeUndefined(); }); + + it("should use provided label when available", () => { + const result = createBranchTemplateVariables( + "test/", + "issue", + 123, + undefined, + "bug", + ); + expect(result.label).toBe("bug"); + }); + + it("should fallback to entityType when label is not provided", () => { + const result = createBranchTemplateVariables("test/", "pr", 456); + expect(result.label).toBe("pr"); + }); + + it("should fallback to entityType when label is empty string", () => { + const result = createBranchTemplateVariables( + "test/", + "issue", + 789, + undefined, + "", + ); + expect(result.label).toBe("issue"); + }); }); describe("generateBranchName", () => { @@ -153,5 +181,40 @@ describe("branch template utilities", () => { expect(result).toBe("fix/pr-789-abcdef12"); }); + + it("should use label in template when provided", () => { + const template = "{{prefix}}{{label}}/{{entityNumber}}"; + const result = generateBranchName( + template, + "feature/", + "issue", + 123, + undefined, + "bug", + ); + + expect(result).toBe("feature/bug/123"); + }); + + it("should fallback to entityType when label template is used but no label provided", () => { + const template = "{{prefix}}{{label}}-{{entityNumber}}"; + const result = generateBranchName(template, "fix/", "pr", 456); + + expect(result).toBe("fix/pr-456"); + }); + + it("should handle template with both label and entityType", () => { + const template = "{{prefix}}{{label}}-{{entityType}}_{{entityNumber}}"; + const result = generateBranchName( + template, + "dev/", + "issue", + 789, + undefined, + "enhancement", + ); + + expect(result).toBe("dev/enhancement-issue_789"); + }); }); }); diff --git a/test/create-prompt.test.ts b/test/create-prompt.test.ts index 06c46bb..905a6b4 100644 --- a/test/create-prompt.test.ts +++ b/test/create-prompt.test.ts @@ -61,6 +61,7 @@ describe("generatePrompt", () => { body: "This is a test PR", author: { login: "testuser" }, state: "OPEN", + labels: { nodes: [] }, createdAt: "2023-01-01T00:00:00Z", additions: 15, deletions: 5, @@ -475,6 +476,7 @@ describe("generatePrompt", () => { body: "The login form is not working", author: { login: "testuser" }, state: "OPEN", + labels: { nodes: [] }, createdAt: "2023-01-01T00:00:00Z", comments: { nodes: [], diff --git a/test/data-formatter.test.ts b/test/data-formatter.test.ts index 7ac455c..4c6b150 100644 --- a/test/data-formatter.test.ts +++ b/test/data-formatter.test.ts @@ -28,6 +28,9 @@ describe("formatContext", () => { additions: 50, deletions: 30, state: "OPEN", + labels: { + nodes: [], + }, commits: { totalCount: 3, nodes: [], @@ -63,6 +66,9 @@ Changed Files: 2 files`, author: { login: "test-user" }, createdAt: "2023-01-01T00:00:00Z", state: "OPEN", + labels: { + nodes: [], + }, comments: { nodes: [], },