MCPcopy Create free account
hub / github.com/github/gh-aw / createCloseEntityHandler

Function createCloseEntityHandler

actions/setup/js/close_entity_helpers.cjs:265–455  ·  view source on GitHub ↗

* Create a message-level close-entity handler function. * * Centralises the common close-flow pipeline: * 1. Max-count gating * 2. Comment body resolution (item.body → config.comment fallback) * 3. Content sanitization * 4. Target repository / entity number resolution (via callbacks.re

(config, entityConfig, callbacks, githubClient)

Source from the content-addressed store, hash-verified

263 * @returns {import('./types/handler-factory').MessageHandlerFunction} Message handler function
264 */
265function createCloseEntityHandler(config, entityConfig, callbacks, githubClient) {
266 const requiredLabels = config.required_labels || [];
267 const requiredTitlePrefix = config.required_title_prefix || "";
268 const maxCount = config.max || 10;
269 const comment = config.comment || "";
270 const isStaged = isStagedMode(config);
271 const { defaultTargetRepo, allowedRepos } = resolveTargetRepoConfig(config);
272 const allowBody = config.allow_body !== false; // default true; false only when explicitly set to false
273
274 let processedCount = 0;
275
276 return async function handleCloseEntity(message, resolvedTemporaryIds) {
277 // 1. Max-count gating
278 if (processedCount >= maxCount) {
279 core.warning(`Skipping ${entityConfig.itemType}: max count of ${maxCount} reached`);
280 return { success: false, error: `Max count of ${maxCount} reached` };
281 }
282 processedCount++;
283
284 const item = message;
285
286 // Log message structure for debugging (avoid logging body content)
287 const logFields = { has_body: !!item.body, body_length: item.body ? item.body.length : 0 };
288 if (item[entityConfig.numberField] !== undefined) {
289 logFields[entityConfig.numberField] = item[entityConfig.numberField];
290 }
291 if (item.repo !== undefined) {
292 logFields.has_repo = true;
293 }
294 core.info(`Processing ${entityConfig.itemType} message: ${JSON.stringify(logFields)}`);
295
296 // 2. Comment body resolution
297 /** @type {string|undefined} */
298 let commentToPost;
299 /** @type {string} */
300 let commentSource = "unknown";
301
302 if (!allowBody) {
303 // allow-body: false — drop any body the agent provided and skip the comment
304 if (typeof item.body === "string" && item.body.trim() !== "") {
305 core.warning(`${entityConfig.itemType}: allow-body is false — dropping non-empty body (length=${item.body.length}) and closing without a comment`);
306 } else {
307 core.info(`${entityConfig.itemType}: allow-body is false — closing without a comment`);
308 }
309 commentToPost = undefined;
310 } else if (typeof item.body === "string" && item.body.trim() !== "") {
311 commentToPost = item.body;
312 commentSource = "item.body";
313 } else if (typeof comment === "string" && comment.trim() !== "") {
314 commentToPost = comment;
315 commentSource = "config.comment";
316 } else {
317 core.warning("No comment body provided in message and no default comment configured");
318 return { success: false, error: "No comment body provided" };
319 }
320
321 if (commentToPost !== undefined) {
322 core.info(`Comment body determined: length=${commentToPost.length}, source=${commentSource}`);

Callers 3

mainFunction · 0.85
mainFunction · 0.85

Calls 8

isStagedModeFunction · 0.85
resolveTargetRepoConfigFunction · 0.85
sanitizeContentFunction · 0.85
validateTargetRepoFunction · 0.85
checkTitlePrefixFilterFunction · 0.85
logStagedPreviewInfoFunction · 0.85
errorMethod · 0.80
getErrorMessageFunction · 0.70

Tested by

no test coverage detected