diff --git a/src/github/operations/branch.ts b/src/github/operations/branch.ts index 32a6863..68e8b0e 100644 --- a/src/github/operations/branch.ts +++ b/src/github/operations/branch.ts @@ -86,14 +86,18 @@ export async function setupBranch( // Generate branch name for either an issue or closed/merged PR const entityType = isPR ? "pr" : "issue"; - const timestamp = new Date() - .toISOString() - .replace(/[:-]/g, "") - .replace(/\.\d{3}Z/, "") - .split("T") - .join("_"); - const newBranch = `${branchPrefix}${entityType}-${entityNumber}-${timestamp}`; + // Create Kubernetes-compatible timestamp: lowercase, hyphens only, shorter format + const now = new Date(); + const timestamp = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`; + + // Ensure branch name is Kubernetes-compatible: + // - Lowercase only + // - Alphanumeric with hyphens + // - No underscores + // - Max 50 chars (to allow for prefixes) + const branchName = `${branchPrefix}${entityType}-${entityNumber}-${timestamp}`; + const newBranch = branchName.toLowerCase().substring(0, 50); try { // Get the SHA of the source branch to verify it exists diff --git a/test/branch-cleanup.test.ts b/test/branch-cleanup.test.ts index b5a3df7..2837432 100644 --- a/test/branch-cleanup.test.ts +++ b/test/branch-cleanup.test.ts @@ -72,7 +72,7 @@ describe("checkAndCommitOrDeleteBranch", () => { mockOctokit, "owner", "repo", - "claude/issue-123-20240101_123456", + "claude/issue-123-20240101-1234", "main", true, // commit signing enabled ); @@ -80,7 +80,7 @@ describe("checkAndCommitOrDeleteBranch", () => { expect(result.shouldDeleteBranch).toBe(true); expect(result.branchLink).toBe(""); expect(consoleLogSpy).toHaveBeenCalledWith( - "Branch claude/issue-123-20240101_123456 has no commits from Claude, will delete it", + "Branch claude/issue-123-20240101-1234 has no commits from Claude, will delete it", ); }); @@ -90,14 +90,14 @@ describe("checkAndCommitOrDeleteBranch", () => { mockOctokit, "owner", "repo", - "claude/issue-123-20240101_123456", + "claude/issue-123-20240101-1234", "main", false, ); expect(result.shouldDeleteBranch).toBe(false); expect(result.branchLink).toBe( - `\n[View branch](${GITHUB_SERVER_URL}/owner/repo/tree/claude/issue-123-20240101_123456)`, + `\n[View branch](${GITHUB_SERVER_URL}/owner/repo/tree/claude/issue-123-20240101-1234)`, ); expect(consoleLogSpy).not.toHaveBeenCalledWith( expect.stringContaining("has no commits"), @@ -123,14 +123,14 @@ describe("checkAndCommitOrDeleteBranch", () => { mockOctokit, "owner", "repo", - "claude/issue-123-20240101_123456", + "claude/issue-123-20240101-1234", "main", false, ); expect(result.shouldDeleteBranch).toBe(false); expect(result.branchLink).toBe( - `\n[View branch](${GITHUB_SERVER_URL}/owner/repo/tree/claude/issue-123-20240101_123456)`, + `\n[View branch](${GITHUB_SERVER_URL}/owner/repo/tree/claude/issue-123-20240101-1234)`, ); expect(consoleErrorSpy).toHaveBeenCalledWith( "Error comparing commits on Claude branch:", @@ -146,7 +146,7 @@ describe("checkAndCommitOrDeleteBranch", () => { mockOctokit, "owner", "repo", - "claude/issue-123-20240101_123456", + "claude/issue-123-20240101-1234", "main", true, // commit signing enabled - will try to delete ); @@ -154,7 +154,7 @@ describe("checkAndCommitOrDeleteBranch", () => { expect(result.shouldDeleteBranch).toBe(true); expect(result.branchLink).toBe(""); expect(consoleErrorSpy).toHaveBeenCalledWith( - "Failed to delete branch claude/issue-123-20240101_123456:", + "Failed to delete branch claude/issue-123-20240101-1234:", deleteError, ); }); @@ -170,7 +170,7 @@ describe("checkAndCommitOrDeleteBranch", () => { mockOctokit, "owner", "repo", - "claude/issue-123-20240101_123456", + "claude/issue-123-20240101-1234", "main", false, ); @@ -178,10 +178,10 @@ describe("checkAndCommitOrDeleteBranch", () => { expect(result.shouldDeleteBranch).toBe(false); expect(result.branchLink).toBe(""); expect(consoleLogSpy).toHaveBeenCalledWith( - "Branch claude/issue-123-20240101_123456 does not exist remotely", + "Branch claude/issue-123-20240101-1234 does not exist remotely", ); expect(consoleLogSpy).toHaveBeenCalledWith( - "Branch claude/issue-123-20240101_123456 does not exist remotely, no branch link will be added", + "Branch claude/issue-123-20240101-1234 does not exist remotely, no branch link will be added", ); }); }); diff --git a/test/comment-logic.test.ts b/test/comment-logic.test.ts index 0500c08..f1b3754 100644 --- a/test/comment-logic.test.ts +++ b/test/comment-logic.test.ts @@ -103,12 +103,12 @@ describe("updateCommentBody", () => { it("adds branch name with link to header when provided", () => { const input = { ...baseInput, - branchName: "claude/issue-123-20240101_120000", + branchName: "claude/issue-123-20240101-1200", }; const result = updateCommentBody(input); expect(result).toContain( - "• [`claude/issue-123-20240101_120000`](https://github.com/owner/repo/tree/claude/issue-123-20240101_120000)", + "• [`claude/issue-123-20240101-1200`](https://github.com/owner/repo/tree/claude/issue-123-20240101-1200)", ); }); @@ -384,9 +384,9 @@ describe("updateCommentBody", () => { const input = { ...baseInput, currentBody: "Claude Code is working… ", - branchName: "claude/pr-456-20240101_120000", + branchName: "claude/pr-456-20240101-1200", prLink: - "\n[Create a PR](https://github.com/owner/repo/compare/main...claude/pr-456-20240101_120000)", + "\n[Create a PR](https://github.com/owner/repo/compare/main...claude/pr-456-20240101-1200)", triggerUsername: "jane-doe", }; @@ -394,7 +394,7 @@ describe("updateCommentBody", () => { // Should include the PR link in the formatted style expect(result).toContain( - "• [Create PR ➔](https://github.com/owner/repo/compare/main...claude/pr-456-20240101_120000)", + "• [Create PR ➔](https://github.com/owner/repo/compare/main...claude/pr-456-20240101-1200)", ); expect(result).toContain("**Claude finished @jane-doe's task**"); }); @@ -403,21 +403,21 @@ describe("updateCommentBody", () => { const input = { ...baseInput, currentBody: "Claude Code is working…", - branchName: "claude/issue-123-20240101_120000", + branchName: "claude/issue-123-20240101-1200", branchLink: - "\n[View branch](https://github.com/owner/repo/tree/claude/issue-123-20240101_120000)", + "\n[View branch](https://github.com/owner/repo/tree/claude/issue-123-20240101-1200)", prLink: - "\n[Create a PR](https://github.com/owner/repo/compare/main...claude/issue-123-20240101_120000)", + "\n[Create a PR](https://github.com/owner/repo/compare/main...claude/issue-123-20240101-1200)", }; const result = updateCommentBody(input); // Should include both links in formatted style expect(result).toContain( - "• [`claude/issue-123-20240101_120000`](https://github.com/owner/repo/tree/claude/issue-123-20240101_120000)", + "• [`claude/issue-123-20240101-1200`](https://github.com/owner/repo/tree/claude/issue-123-20240101-1200)", ); expect(result).toContain( - "• [Create PR ➔](https://github.com/owner/repo/compare/main...claude/issue-123-20240101_120000)", + "• [Create PR ➔](https://github.com/owner/repo/compare/main...claude/issue-123-20240101-1200)", ); }); diff --git a/test/create-prompt.test.ts b/test/create-prompt.test.ts index 4fd3591..de6c7ba 100644 --- a/test/create-prompt.test.ts +++ b/test/create-prompt.test.ts @@ -127,7 +127,7 @@ describe("generatePrompt", () => { commentId: "67890", isPR: false, baseBranch: "main", - claudeBranch: "claude/issue-67890-20240101_120000", + claudeBranch: "claude/issue-67890-20240101-1200", issueNumber: "67890", commentBody: "@claude please fix this", }, @@ -183,7 +183,7 @@ describe("generatePrompt", () => { isPR: false, issueNumber: "789", baseBranch: "main", - claudeBranch: "claude/issue-789-20240101_120000", + claudeBranch: "claude/issue-789-20240101-1200", }, }; @@ -210,7 +210,7 @@ describe("generatePrompt", () => { isPR: false, issueNumber: "999", baseBranch: "develop", - claudeBranch: "claude/issue-999-20240101_120000", + claudeBranch: "claude/issue-999-20240101-1200", assigneeTrigger: "claude-bot", }, }; @@ -237,7 +237,7 @@ describe("generatePrompt", () => { isPR: false, issueNumber: "888", baseBranch: "main", - claudeBranch: "claude/issue-888-20240101_120000", + claudeBranch: "claude/issue-888-20240101-1200", labelTrigger: "claude-task", }, }; @@ -265,7 +265,7 @@ describe("generatePrompt", () => { isPR: false, issueNumber: "789", baseBranch: "main", - claudeBranch: "claude/issue-789-20240101_120000", + claudeBranch: "claude/issue-789-20240101-1200", }, }; @@ -312,7 +312,7 @@ describe("generatePrompt", () => { isPR: false, issueNumber: "123", baseBranch: "main", - claudeBranch: "claude/issue-67890-20240101_120000", + claudeBranch: "claude/issue-67890-20240101-1200", commentBody: "@claude please fix this", }, }; @@ -334,7 +334,7 @@ describe("generatePrompt", () => { isPR: false, issueNumber: "123", baseBranch: "main", - claudeBranch: "claude/issue-67890-20240101_120000", + claudeBranch: "claude/issue-67890-20240101-1200", commentBody: "@claude please fix this", }, }; @@ -388,7 +388,7 @@ describe("generatePrompt", () => { isPR: false, issueNumber: "789", baseBranch: "main", - claudeBranch: "claude/issue-789-20240101_120000", + claudeBranch: "claude/issue-789-20240101-1200", }, }; @@ -396,10 +396,10 @@ describe("generatePrompt", () => { // Should contain Issue-specific instructions expect(prompt).toContain( - "You are already on the correct branch (claude/issue-789-20240101_120000)", + "You are already on the correct branch (claude/issue-789-20240101-1200)", ); expect(prompt).toContain( - "IMPORTANT: You are already on the correct branch (claude/issue-789-20240101_120000)", + "IMPORTANT: You are already on the correct branch (claude/issue-789-20240101-1200)", ); expect(prompt).toContain("Create a PR](https://github.com/"); expect(prompt).toContain( @@ -426,7 +426,7 @@ describe("generatePrompt", () => { isPR: false, issueNumber: "123", baseBranch: "main", - claudeBranch: "claude/issue-123-20240101_120000", + claudeBranch: "claude/issue-123-20240101-1200", commentBody: "@claude please fix this", }, }; @@ -435,13 +435,13 @@ describe("generatePrompt", () => { // Should contain the actual branch name with timestamp expect(prompt).toContain( - "You are already on the correct branch (claude/issue-123-20240101_120000)", + "You are already on the correct branch (claude/issue-123-20240101-1200)", ); expect(prompt).toContain( - "IMPORTANT: You are already on the correct branch (claude/issue-123-20240101_120000)", + "IMPORTANT: You are already on the correct branch (claude/issue-123-20240101-1200)", ); expect(prompt).toContain( - "The branch-name is the current branch: claude/issue-123-20240101_120000", + "The branch-name is the current branch: claude/issue-123-20240101-1200", ); }); @@ -456,7 +456,7 @@ describe("generatePrompt", () => { isPR: true, prNumber: "456", commentBody: "@claude please fix this", - claudeBranch: "claude/pr-456-20240101_120000", + claudeBranch: "claude/pr-456-20240101-1200", baseBranch: "main", }, }; @@ -465,13 +465,13 @@ describe("generatePrompt", () => { // Should contain branch-specific instructions like issues expect(prompt).toContain( - "You are already on the correct branch (claude/pr-456-20240101_120000)", + "You are already on the correct branch (claude/pr-456-20240101-1200)", ); expect(prompt).toContain( "Create a PR](https://github.com/owner/repo/compare/main", ); expect(prompt).toContain( - "The branch-name is the current branch: claude/pr-456-20240101_120000", + "The branch-name is the current branch: claude/pr-456-20240101-1200", ); expect(prompt).toContain("Reference to the original PR"); expect(prompt).toContain( @@ -525,7 +525,7 @@ describe("generatePrompt", () => { isPR: true, prNumber: "789", commentBody: "@claude please update this", - claudeBranch: "claude/pr-789-20240101_123000", + claudeBranch: "claude/pr-789-20240101-1230", baseBranch: "develop", }, }; @@ -534,7 +534,7 @@ describe("generatePrompt", () => { // Should contain new branch instructions expect(prompt).toContain( - "You are already on the correct branch (claude/pr-789-20240101_123000)", + "You are already on the correct branch (claude/pr-789-20240101-1230)", ); expect(prompt).toContain( "Create a PR](https://github.com/owner/repo/compare/develop", @@ -553,7 +553,7 @@ describe("generatePrompt", () => { prNumber: "999", commentId: "review-comment-123", commentBody: "@claude fix this issue", - claudeBranch: "claude/pr-999-20240101_140000", + claudeBranch: "claude/pr-999-20240101-1400", baseBranch: "main", }, }; @@ -562,7 +562,7 @@ describe("generatePrompt", () => { // Should contain new branch instructions expect(prompt).toContain( - "You are already on the correct branch (claude/pr-999-20240101_140000)", + "You are already on the correct branch (claude/pr-999-20240101-1400)", ); expect(prompt).toContain("Create a PR](https://github.com/"); expect(prompt).toContain("Reference to the original PR"); @@ -581,7 +581,7 @@ describe("generatePrompt", () => { eventAction: "closed", isPR: true, prNumber: "555", - claudeBranch: "claude/pr-555-20240101_150000", + claudeBranch: "claude/pr-555-20240101-1500", baseBranch: "main", }, }; @@ -590,7 +590,7 @@ describe("generatePrompt", () => { // Should contain new branch instructions expect(prompt).toContain( - "You are already on the correct branch (claude/pr-555-20240101_150000)", + "You are already on the correct branch (claude/pr-555-20240101-1500)", ); expect(prompt).toContain("Create a PR](https://github.com/"); expect(prompt).toContain("Reference to the original PR"); @@ -683,7 +683,7 @@ describe("getEventTypeAndContext", () => { isPR: false, issueNumber: "999", baseBranch: "main", - claudeBranch: "claude/issue-999-20240101_120000", + claudeBranch: "claude/issue-999-20240101-1200", assigneeTrigger: "claude-bot", }, }; @@ -705,7 +705,7 @@ describe("getEventTypeAndContext", () => { isPR: false, issueNumber: "888", baseBranch: "main", - claudeBranch: "claude/issue-888-20240101_120000", + claudeBranch: "claude/issue-888-20240101-1200", labelTrigger: "claude-task", }, }; @@ -728,7 +728,7 @@ describe("getEventTypeAndContext", () => { isPR: false, issueNumber: "999", baseBranch: "main", - claudeBranch: "claude/issue-999-20240101_120000", + claudeBranch: "claude/issue-999-20240101-1200", // No assigneeTrigger when using directPrompt }, }; diff --git a/test/prepare-context.test.ts b/test/prepare-context.test.ts index 904dd37..fb2e9d0 100644 --- a/test/prepare-context.test.ts +++ b/test/prepare-context.test.ts @@ -35,7 +35,7 @@ describe("parseEnvVarsWithContext", () => { process.env = { ...BASE_ENV, BASE_BRANCH: "main", - CLAUDE_BRANCH: "claude/issue-67890-20240101_120000", + CLAUDE_BRANCH: "claude/issue-67890-20240101-1200", }; }); @@ -44,7 +44,7 @@ describe("parseEnvVarsWithContext", () => { mockIssueCommentContext, "12345", "main", - "claude/issue-67890-20240101_120000", + "claude/issue-67890-20240101-1200", ); expect(result.repository).toBe("test-owner/test-repo"); @@ -60,7 +60,7 @@ describe("parseEnvVarsWithContext", () => { expect(result.eventData.issueNumber).toBe("55"); expect(result.eventData.commentId).toBe("12345678"); expect(result.eventData.claudeBranch).toBe( - "claude/issue-67890-20240101_120000", + "claude/issue-67890-20240101-1200", ); expect(result.eventData.baseBranch).toBe("main"); expect(result.eventData.commentBody).toBe( @@ -81,7 +81,7 @@ describe("parseEnvVarsWithContext", () => { mockIssueCommentContext, "12345", undefined, - "claude/issue-67890-20240101_120000", + "claude/issue-67890-20240101-1200", ), ).toThrow("BASE_BRANCH is required for issue_comment event"); }); @@ -152,7 +152,7 @@ describe("parseEnvVarsWithContext", () => { process.env = { ...BASE_ENV, BASE_BRANCH: "main", - CLAUDE_BRANCH: "claude/issue-42-20240101_120000", + CLAUDE_BRANCH: "claude/issue-42-20240101-1200", }; }); @@ -161,7 +161,7 @@ describe("parseEnvVarsWithContext", () => { mockIssueOpenedContext, "12345", "main", - "claude/issue-42-20240101_120000", + "claude/issue-42-20240101-1200", ); expect(result.eventData.eventName).toBe("issues"); @@ -174,7 +174,7 @@ describe("parseEnvVarsWithContext", () => { expect(result.eventData.issueNumber).toBe("42"); expect(result.eventData.baseBranch).toBe("main"); expect(result.eventData.claudeBranch).toBe( - "claude/issue-42-20240101_120000", + "claude/issue-42-20240101-1200", ); } }); @@ -184,7 +184,7 @@ describe("parseEnvVarsWithContext", () => { mockIssueAssignedContext, "12345", "main", - "claude/issue-123-20240101_120000", + "claude/issue-123-20240101-1200", ); expect(result.eventData.eventName).toBe("issues"); @@ -197,7 +197,7 @@ describe("parseEnvVarsWithContext", () => { expect(result.eventData.issueNumber).toBe("123"); expect(result.eventData.baseBranch).toBe("main"); expect(result.eventData.claudeBranch).toBe( - "claude/issue-123-20240101_120000", + "claude/issue-123-20240101-1200", ); expect(result.eventData.assigneeTrigger).toBe("@claude-bot"); } @@ -215,7 +215,7 @@ describe("parseEnvVarsWithContext", () => { mockIssueOpenedContext, "12345", undefined, - "claude/issue-42-20240101_120000", + "claude/issue-42-20240101-1200", ), ).toThrow("BASE_BRANCH is required for issues event"); }); @@ -234,7 +234,7 @@ describe("parseEnvVarsWithContext", () => { contextWithDirectPrompt, "12345", "main", - "claude/issue-123-20240101_120000", + "claude/issue-123-20240101-1200", ); expect(result.eventData.eventName).toBe("issues"); @@ -264,7 +264,7 @@ describe("parseEnvVarsWithContext", () => { contextWithoutTriggers, "12345", "main", - "claude/issue-123-20240101_120000", + "claude/issue-123-20240101-1200", ), ).toThrow("ASSIGNEE_TRIGGER is required for issue assigned event"); });