diff --git a/.github/workflows/external-plugin-pr-quality-gates.yml b/.github/workflows/external-plugin-pr-quality-gates.yml index f194e6e39..cb05e1390 100644 --- a/.github/workflows/external-plugin-pr-quality-gates.yml +++ b/.github/workflows/external-plugin-pr-quality-gates.yml @@ -200,6 +200,18 @@ jobs: : qualityResult.overall_status === 'pass' || !shouldRun ? '## ✅ External plugin PR checks passed' : '## ⚠️ External plugin PR checks need maintainer follow-up'; + const formatGateOutput = (pluginName, gateName, gateStatus, rawOutput) => { + const output = String(rawOutput || '').trim() || '_No output captured._'; + return [ + '
', + `${pluginName} — ${gateName} (${gateStatus || 'not_run'})`, + '', + '```text', + output, + '```', + '
', + ].join('\n'); + }; const rows = checkedPlugins.length > 0 ? checkedPlugins.map((entry) => { @@ -211,6 +223,21 @@ jobs: return `| ${name} | ${quality.vally_lint_status || 'not_run'} | ${quality.smoke_status || 'not_run'} | ${quality.overall_status || 'not_run'} | ${sourceCell} |`; }) : ['| _none_ | not_run | not_run | not_run | _n/a_ |']; + const failureDetails = checkedPlugins.flatMap((entry) => { + const name = String(entry?.name || 'unknown'); + const quality = entry?.quality || {}; + const shouldShowVally = quality.vally_lint_status === 'fail' || quality.vally_lint_status === 'infra_error' || String(quality.vally_lint_output || '').trim().length > 0; + const shouldShowSmoke = quality.smoke_status === 'fail' || quality.smoke_status === 'infra_error' || String(quality.smoke_output || '').trim().length > 0; + + const details = []; + if (shouldShowVally) { + details.push(formatGateOutput(name, 'vally lint', quality.vally_lint_status, quality.vally_lint_output)); + } + if (shouldShowSmoke) { + details.push(formatGateOutput(name, 'install smoke test', quality.smoke_status, quality.smoke_output)); + } + return details; + }); const body = [ marker, @@ -225,6 +252,14 @@ jobs: '|---|---|---|---|---|', ...rows, '', + ...(failureDetails.length > 0 + ? [ + '### Gate output details', + '', + ...failureDetails, + '', + ] + : []), String(qualityResult.summary || '').trim() || '_No summary provided._', ].join('\n'); diff --git a/eng/external-plugin-quality-gates.mjs b/eng/external-plugin-quality-gates.mjs index ce866331b..6b857be09 100644 --- a/eng/external-plugin-quality-gates.mjs +++ b/eng/external-plugin-quality-gates.mjs @@ -140,7 +140,7 @@ function cloneSubmissionRepository(workDir, plugin) { // NOTE: Keep in sync with EXTERNAL_PLUGIN_ROOT_MANIFEST_PATHS in external-plugin-validation.mjs const PLUGIN_JSON_CANDIDATES = [ [".github", "plugin", "plugin.json"], - [".plugins", "plugin.json"], + [".plugin", "plugin.json"], ["plugin.json"], ]; diff --git a/eng/external-plugin-validation.mjs b/eng/external-plugin-validation.mjs index 87bc271ee..846458832 100644 --- a/eng/external-plugin-validation.mjs +++ b/eng/external-plugin-validation.mjs @@ -27,7 +27,7 @@ export const EXTERNAL_PLUGIN_POLICIES = Object.freeze({ const EXTERNAL_PLUGIN_ROOT_MANIFEST_PATHS = Object.freeze([ "plugin.json", ".github/plugin/plugin.json", - ".plugins/plugin.json", + ".plugin/plugin.json", ]); function resolvePolicy(policy) {