diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 11beb4aa54b..17e6aa13853 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -1110,8 +1110,9 @@ jobs: } } function getMaxAllowedForType(itemType, config) { - if (config && config[itemType] && typeof config[itemType] === "object" && config[itemType].max) { - return config[itemType].max; + const itemConfig = config?.[itemType]; + if (itemConfig && typeof itemConfig === "object" && "max" in itemConfig && itemConfig.max) { + return itemConfig.max; } switch (itemType) { case "create-issue": diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 927403edbb6..4bd52391649 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -1241,8 +1241,9 @@ jobs: } } function getMaxAllowedForType(itemType, config) { - if (config && config[itemType] && typeof config[itemType] === "object" && config[itemType].max) { - return config[itemType].max; + const itemConfig = config?.[itemType]; + if (itemConfig && typeof itemConfig === "object" && "max" in itemConfig && itemConfig.max) { + return itemConfig.max; } switch (itemType) { case "create-issue": diff --git a/pkg/workflow/js/collect_ndjson_output.js b/pkg/workflow/js/collect_ndjson_output.js index 592a921b436..bb36f10cd34 100644 --- a/pkg/workflow/js/collect_ndjson_output.js +++ b/pkg/workflow/js/collect_ndjson_output.js @@ -60,8 +60,9 @@ async function main() { } } function getMaxAllowedForType(itemType, config) { - if (config && config[itemType] && typeof config[itemType] === "object" && config[itemType].max) { - return config[itemType].max; + const itemConfig = config?.[itemType]; + if (itemConfig && typeof itemConfig === "object" && "max" in itemConfig && itemConfig.max) { + return itemConfig.max; } switch (itemType) { case "create-issue": diff --git a/pkg/workflow/js/collect_ndjson_output.ts b/pkg/workflow/js/collect_ndjson_output.ts index f01b4e857ec..2b82903095d 100644 --- a/pkg/workflow/js/collect_ndjson_output.ts +++ b/pkg/workflow/js/collect_ndjson_output.ts @@ -1,5 +1,20 @@ import type { SafeOutputItem, SafeOutputItems } from "./types/safe-outputs"; -import type { SafeOutputConfigs } from "./types/safe-outputs-config"; +import type { + SafeOutputConfigs, + SafeOutputConfig, + SpecificSafeOutputConfig, + CreateIssueConfig, + CreateDiscussionConfig, + AddCommentConfig, + CreatePullRequestConfig, + CreatePullRequestReviewCommentConfig, + CreateCodeScanningAlertConfig, + AddLabelsConfig, + UpdateIssueConfig, + PushToPullRequestBranchConfig, + UploadAssetConfig, + MissingToolConfig, +} from "./types/safe-outputs-config"; async function main() { const fs = require("fs"); @@ -141,13 +156,14 @@ async function main() { /** * Gets the maximum allowed count for a given output type * @param {string} itemType - The output item type - * @param {any} config - The safe-outputs configuration + * @param {SafeOutputConfigs} config - The safe-outputs configuration * @returns {number} The maximum allowed count */ - function getMaxAllowedForType(itemType: string, config: SafeOutputConfigs) { + function getMaxAllowedForType(itemType: string, config: SafeOutputConfigs): number { // Check if max is explicitly specified in config - if (config && config[itemType] && typeof config[itemType] === "object" && config[itemType].max) { - return config[itemType].max; + const itemConfig = config?.[itemType]; + if (itemConfig && typeof itemConfig === "object" && "max" in itemConfig && itemConfig.max) { + return itemConfig.max; } // Use default limits for plural-supported types @@ -191,7 +207,6 @@ async function main() { // U+0014 (DC4) — represented here as "\u0014" // Escape control characters not allowed in JSON strings (U+0000 through U+001F) // Preserve common JSON escapes for \b, \f, \n, \r, \t and use \uXXXX for the rest. - /** @type {Record} */ const _ctrl: Record = { 8: "\\b", 9: "\\t", 10: "\\n", 12: "\\f", 13: "\\r" }; repaired = repaired.replace(/[\u0000-\u001F]/g, ch => { const c = ch.charCodeAt(0); @@ -398,9 +413,9 @@ async function main() { /** * Attempts to parse JSON with repair fallback * @param {string} jsonStr - The JSON string to parse - * @returns {Object|undefined} The parsed JSON object, or undefined if parsing fails + * @returns {any|undefined} The parsed JSON object, or undefined if parsing fails */ - function parseJsonWithRepair(jsonStr: string) { + function parseJsonWithRepair(jsonStr: string): any | undefined { try { // First, try normal JSON.parse return JSON.parse(jsonStr); @@ -444,11 +459,10 @@ async function main() { core.info(`Raw output content length: ${outputContent.length}`); // Parse the safe-outputs configuration - /** @type {any} */ - let expectedOutputTypes = {}; + let expectedOutputTypes: SafeOutputConfigs = {}; if (safeOutputsConfig) { try { - expectedOutputTypes = JSON.parse(safeOutputsConfig); + expectedOutputTypes = JSON.parse(safeOutputsConfig) as SafeOutputConfigs; core.info(`Expected output types: ${JSON.stringify(Object.keys(expectedOutputTypes))}`); } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); @@ -465,8 +479,7 @@ async function main() { const line = lines[i].trim(); if (line === "") continue; // Skip empty lines try { - /** @type {any} */ - const item = parseJsonWithRepair(line); + const item = parseJsonWithRepair(line) as any; // If item is undefined (failed to parse), add error and process next line if (item === undefined) { @@ -512,7 +525,7 @@ async function main() { item.body = sanitizeContent(item.body); // Sanitize labels if present if (item.labels && Array.isArray(item.labels)) { - item.labels = item.labels.map(/** @param {any} label */ label => (typeof label === "string" ? sanitizeContent(label) : label)); + item.labels = item.labels.map((label: any) => (typeof label === "string" ? sanitizeContent(label) : label)); } break; @@ -550,7 +563,7 @@ async function main() { item.branch = sanitizeContent(item.branch); // Sanitize labels if present if (item.labels && Array.isArray(item.labels)) { - item.labels = item.labels.map(/** @param {any} label */ label => (typeof label === "string" ? sanitizeContent(label) : label)); + item.labels = item.labels.map((label: any) => (typeof label === "string" ? sanitizeContent(label) : label)); } break; @@ -559,7 +572,7 @@ async function main() { errors.push(`Line ${i + 1}: add_labels requires a 'labels' array field`); continue; } - if (item.labels.some(/** @param {any} label */ label => typeof label !== "string")) { + if (item.labels.some((label: any) => typeof label !== "string")) { errors.push(`Line ${i + 1}: add_labels labels array must contain only strings`); continue; } @@ -570,7 +583,7 @@ async function main() { continue; } // Sanitize label strings - item.labels = item.labels.map(/** @param {any} label */ label => sanitizeContent(label)); + item.labels = item.labels.map((label: any) => sanitizeContent(label)); break; case "update-issue": diff --git a/pkg/workflow/js/types/safe-outputs-config.d.ts b/pkg/workflow/js/types/safe-outputs-config.d.ts index 581e9daf71d..ca35a0acd71 100644 --- a/pkg/workflow/js/types/safe-outputs-config.d.ts +++ b/pkg/workflow/js/types/safe-outputs-config.d.ts @@ -1,8 +1,148 @@ +// Base interface for all safe output configurations interface SafeOutputConfig { type: string; max?: number; } -type SafeOutputConfigs = Record; +// === Specific Safe Output Configuration Interfaces === -export { SafeOutputConfig, SafeOutputConfigs }; +/** + * Configuration for creating GitHub issues + */ +interface CreateIssueConfig extends SafeOutputConfig { + "title-prefix"?: string; + labels?: string[]; + max?: number; + "github-token"?: string; +} + +/** + * Configuration for creating GitHub discussions + */ +interface CreateDiscussionConfig extends SafeOutputConfig { + "title-prefix"?: string; + "category-id"?: string; + max?: number; + "github-token"?: string; +} + +/** + * Configuration for adding comments to issues or PRs + */ +interface AddCommentConfig extends SafeOutputConfig { + max?: number; + target?: string; + "github-token"?: string; +} + +/** + * Configuration for creating pull requests + */ +interface CreatePullRequestConfig extends SafeOutputConfig { + "title-prefix"?: string; + labels?: string[]; + draft?: boolean; + max?: number; + "if-no-changes"?: string; + "github-token"?: string; +} + +/** + * Configuration for creating pull request review comments + */ +interface CreatePullRequestReviewCommentConfig extends SafeOutputConfig { + max?: number; + side?: string; + "github-token"?: string; +} + +/** + * Configuration for creating code scanning alerts + */ +interface CreateCodeScanningAlertConfig extends SafeOutputConfig { + max?: number; + driver?: string; + "github-token"?: string; +} + +/** + * Configuration for adding labels to issues or PRs + */ +interface AddLabelsConfig extends SafeOutputConfig { + allowed?: string[]; + max?: number; + "github-token"?: string; +} + +/** + * Configuration for updating issues + */ +interface UpdateIssueConfig extends SafeOutputConfig { + status?: boolean; + target?: string; + title?: boolean; + body?: boolean; + max?: number; + "github-token"?: string; +} + +/** + * Configuration for pushing to pull request branches + */ +interface PushToPullRequestBranchConfig extends SafeOutputConfig { + target?: string; + "if-no-changes"?: string; + "github-token"?: string; +} + +/** + * Configuration for uploading assets + */ +interface UploadAssetConfig extends SafeOutputConfig { + branch?: string; + "max-size"?: number; + "allowed-exts"?: string[]; + "github-token"?: string; +} + +/** + * Configuration for reporting missing tools + */ +interface MissingToolConfig extends SafeOutputConfig { + max?: number; + "github-token"?: string; +} + +// Union type of all specific safe output configurations +type SpecificSafeOutputConfig = + | CreateIssueConfig + | CreateDiscussionConfig + | AddCommentConfig + | CreatePullRequestConfig + | CreatePullRequestReviewCommentConfig + | CreateCodeScanningAlertConfig + | AddLabelsConfig + | UpdateIssueConfig + | PushToPullRequestBranchConfig + | UploadAssetConfig + | MissingToolConfig; + +type SafeOutputConfigs = Record; + +export { + SafeOutputConfig, + SafeOutputConfigs, + // Specific configuration types + CreateIssueConfig, + CreateDiscussionConfig, + AddCommentConfig, + CreatePullRequestConfig, + CreatePullRequestReviewCommentConfig, + CreateCodeScanningAlertConfig, + AddLabelsConfig, + UpdateIssueConfig, + PushToPullRequestBranchConfig, + UploadAssetConfig, + MissingToolConfig, + SpecificSafeOutputConfig, +};