refactor: update branch naming convention for Kubernetes compatibility (#249)

* refactor: update branch naming convention for Kubernetes compatibility

- Changed timestamp format in branch names to a shorter, Kubernetes-compatible style (lowercase, hyphens only).
- Updated related tests to reflect new branch name format.
- Ensured branch names are limited to a maximum of 50 characters to comply with Kubernetes naming requirements.

* refactor: clean up timestamp formatting in branch naming logic

- Removed unnecessary whitespace and standardized string formatting for the Kubernetes-compatible timestamp in branch names.
- Ensured consistency in the use of double quotes for string literals.
This commit is contained in:
Jay Derinbogaz
2025-07-12 20:30:49 +02:00
committed by GitHub
parent b6868bfc27
commit b92e56a96b
5 changed files with 70 additions and 66 deletions

View File

@@ -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

View File

@@ -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",
);
});
});

View File

@@ -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… <img src='spinner.gif' />",
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)",
);
});

View File

@@ -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
},
};

View File

@@ -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");
});