refactor: improve discriminated union implementation based on review feedback

- Use eventName checks instead of 'in' operator for more robust type guards
- Remove unnecessary type assertions - TypeScript's control flow analysis works correctly
- Remove redundant runtime checks for entityNumber and isPR
- Simplify code by using context directly after type guard

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
km-anthropic
2025-07-29 13:26:35 -07:00
parent 9c8ca262b6
commit 5bdfd090e0
2 changed files with 28 additions and 38 deletions

View File

@@ -10,7 +10,6 @@ import {
parseGitHubContext, parseGitHubContext,
isPullRequestReviewCommentEvent, isPullRequestReviewCommentEvent,
isEntityContext, isEntityContext,
type ParsedGitHubContext,
} from "../github/context"; } from "../github/context";
import { GITHUB_SERVER_URL } from "../github/api/config"; import { GITHUB_SERVER_URL } from "../github/api/config";
import { checkAndCommitOrDeleteBranch } from "../github/operations/branch-cleanup"; import { checkAndCommitOrDeleteBranch } from "../github/operations/branch-cleanup";
@@ -31,9 +30,7 @@ async function run() {
throw new Error("update-comment-link requires an entity context"); throw new Error("update-comment-link requires an entity context");
} }
// TypeScript needs a type assertion here due to control flow limitations const { owner, repo } = context.repository;
const entityContext = context as ParsedGitHubContext;
const { owner, repo } = entityContext.repository;
const octokit = createOctokit(githubToken); const octokit = createOctokit(githubToken);
@@ -46,7 +43,7 @@ async function run() {
try { try {
// GitHub has separate ID namespaces for review comments and issue comments // GitHub has separate ID namespaces for review comments and issue comments
// We need to use the correct API based on the event type // We need to use the correct API based on the event type
if (isPullRequestReviewCommentEvent(entityContext)) { if (isPullRequestReviewCommentEvent(context)) {
// For PR review comments, use the pulls API // For PR review comments, use the pulls API
console.log(`Fetching PR review comment ${commentId}`); console.log(`Fetching PR review comment ${commentId}`);
const { data: prComment } = await octokit.rest.pulls.getReviewComment({ const { data: prComment } = await octokit.rest.pulls.getReviewComment({
@@ -75,16 +72,16 @@ async function run() {
// If all attempts fail, try to determine more information about the comment // If all attempts fail, try to determine more information about the comment
console.error("Failed to fetch comment. Debug info:"); console.error("Failed to fetch comment. Debug info:");
console.error(`Comment ID: ${commentId}`); console.error(`Comment ID: ${commentId}`);
console.error(`Event name: ${entityContext.eventName}`); console.error(`Event name: ${context.eventName}`);
console.error(`Entity number: ${entityContext.entityNumber}`); console.error(`Entity number: ${context.entityNumber}`);
console.error(`Repository: ${entityContext.repository.full_name}`); console.error(`Repository: ${context.repository.full_name}`);
// Try to get the PR info to understand the comment structure // Try to get the PR info to understand the comment structure
try { try {
const { data: pr } = await octokit.rest.pulls.get({ const { data: pr } = await octokit.rest.pulls.get({
owner, owner,
repo, repo,
pull_number: entityContext.entityNumber, pull_number: context.entityNumber,
}); });
console.log(`PR state: ${pr.state}`); console.log(`PR state: ${pr.state}`);
console.log(`PR comments count: ${pr.comments}`); console.log(`PR comments count: ${pr.comments}`);
@@ -136,12 +133,12 @@ async function run() {
comparison.total_commits > 0 || comparison.total_commits > 0 ||
(comparison.files && comparison.files.length > 0) (comparison.files && comparison.files.length > 0)
) { ) {
const entityType = entityContext.isPR ? "PR" : "Issue"; const entityType = context.isPR ? "PR" : "Issue";
const prTitle = encodeURIComponent( const prTitle = encodeURIComponent(
`${entityType} #${entityContext.entityNumber}: Changes from Claude`, `${entityType} #${context.entityNumber}: Changes from Claude`,
); );
const prBody = encodeURIComponent( const prBody = encodeURIComponent(
`This PR addresses ${entityType.toLowerCase()} #${entityContext.entityNumber}\n\nGenerated with [Claude Code](https://claude.ai/code)`, `This PR addresses ${entityType.toLowerCase()} #${context.entityNumber}\n\nGenerated with [Claude Code](https://claude.ai/code)`,
); );
const prUrl = `${serverUrl}/${owner}/${repo}/compare/${baseBranch}...${claudeBranch}?quick_pull=1&title=${prTitle}&body=${prBody}`; const prUrl = `${serverUrl}/${owner}/${repo}/compare/${baseBranch}...${claudeBranch}?quick_pull=1&title=${prTitle}&body=${prBody}`;
prLink = `\n[Create a PR](${prUrl})`; prLink = `\n[Create a PR](${prUrl})`;

View File

@@ -8,10 +8,7 @@ import { configureGitAuth } from "../../github/operations/git-config";
import { prepareMcpConfig } from "../../mcp/install-mcp-server"; import { prepareMcpConfig } from "../../mcp/install-mcp-server";
import { fetchGitHubData } from "../../github/data/fetcher"; import { fetchGitHubData } from "../../github/data/fetcher";
import { createPrompt } from "../../create-prompt"; import { createPrompt } from "../../create-prompt";
import { import { isEntityContext } from "../../github/context";
isEntityContext,
type ParsedGitHubContext,
} from "../../github/context";
/** /**
* Tag mode implementation. * Tag mode implementation.
@@ -59,37 +56,33 @@ export const tagMode: Mode = {
octokit, octokit,
githubToken, githubToken,
}: ModeOptions): Promise<ModeResult> { }: ModeOptions): Promise<ModeResult> {
// Tag mode handles entity-based events (issues, PRs, comments) // Tag mode only handles entity-based events
// We know context is ParsedGitHubContext for tag mode if (!isEntityContext(context)) {
const entityContext = context as ParsedGitHubContext; throw new Error("Tag mode requires entity context");
}
// Check if actor is human // Check if actor is human
await checkHumanActor(octokit.rest, entityContext); await checkHumanActor(octokit.rest, context);
// Create initial tracking comment // Create initial tracking comment
const commentData = await createInitialComment(octokit.rest, entityContext); const commentData = await createInitialComment(octokit.rest, context);
const commentId = commentData.id; const commentId = commentData.id;
// Fetch GitHub data - entity events always have entityNumber and isPR
if (!entityContext.entityNumber || entityContext.isPR === undefined) {
throw new Error("Entity events must have entityNumber and isPR defined");
}
const githubData = await fetchGitHubData({ const githubData = await fetchGitHubData({
octokits: octokit, octokits: octokit,
repository: `${entityContext.repository.owner}/${entityContext.repository.repo}`, repository: `${context.repository.owner}/${context.repository.repo}`,
prNumber: entityContext.entityNumber.toString(), prNumber: context.entityNumber.toString(),
isPR: entityContext.isPR, isPR: context.isPR,
triggerUsername: entityContext.actor, triggerUsername: context.actor,
}); });
// Setup branch // Setup branch
const branchInfo = await setupBranch(octokit, githubData, entityContext); const branchInfo = await setupBranch(octokit, githubData, context);
// Configure git authentication if not using commit signing // Configure git authentication if not using commit signing
if (!context.inputs.useCommitSigning) { if (!context.inputs.useCommitSigning) {
try { try {
await configureGitAuth(githubToken, entityContext, commentData.user); await configureGitAuth(githubToken, context, commentData.user);
} catch (error) { } catch (error) {
console.error("Failed to configure git authentication:", error); console.error("Failed to configure git authentication:", error);
throw error; throw error;
@@ -97,26 +90,26 @@ export const tagMode: Mode = {
} }
// Create prompt file // Create prompt file
const modeContext = this.prepareContext(entityContext, { const modeContext = this.prepareContext(context, {
commentId, commentId,
baseBranch: branchInfo.baseBranch, baseBranch: branchInfo.baseBranch,
claudeBranch: branchInfo.claudeBranch, claudeBranch: branchInfo.claudeBranch,
}); });
await createPrompt(tagMode, modeContext, githubData, entityContext); await createPrompt(tagMode, modeContext, githubData, context);
// Get MCP configuration // Get MCP configuration
const additionalMcpConfig = process.env.MCP_CONFIG || ""; const additionalMcpConfig = process.env.MCP_CONFIG || "";
const mcpConfig = await prepareMcpConfig({ const mcpConfig = await prepareMcpConfig({
githubToken, githubToken,
owner: entityContext.repository.owner, owner: context.repository.owner,
repo: entityContext.repository.repo, repo: context.repository.repo,
branch: branchInfo.claudeBranch || branchInfo.currentBranch, branch: branchInfo.claudeBranch || branchInfo.currentBranch,
baseBranch: branchInfo.baseBranch, baseBranch: branchInfo.baseBranch,
additionalMcpConfig, additionalMcpConfig,
claudeCommentId: commentId.toString(), claudeCommentId: commentId.toString(),
allowedTools: entityContext.inputs.allowedTools, allowedTools: context.inputs.allowedTools,
context: entityContext, context,
}); });
core.setOutput("mcp_config", mcpConfig); core.setOutput("mcp_config", mcpConfig);