mirror of
https://github.com/github/awesome-copilot.git
synced 2026-08-13 12:49:49 +00:00
chore: publish from main
This commit is contained in:
@@ -214,6 +214,34 @@ jobs:
|
|||||||
.replace(/>/g, '>')
|
.replace(/>/g, '>')
|
||||||
.replace(/"/g, '"')
|
.replace(/"/g, '"')
|
||||||
.replace(/'/g, ''');
|
.replace(/'/g, ''');
|
||||||
|
const escapeMarkdownTableCell = (value) =>
|
||||||
|
String(value ?? '')
|
||||||
|
.replace(/\r\n?|\n/g, '\n')
|
||||||
|
.split('\n')
|
||||||
|
.map((line) =>
|
||||||
|
Array.from(line, (character) =>
|
||||||
|
/^[A-Za-z0-9 .-]$/.test(character)
|
||||||
|
? character
|
||||||
|
: `&#${character.codePointAt(0)};`
|
||||||
|
).join('')
|
||||||
|
)
|
||||||
|
.join('<br>');
|
||||||
|
const normalizeGitHubUrl = (value) => {
|
||||||
|
const raw = String(value || '').trim();
|
||||||
|
if (!raw) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const parsed = new URL(raw);
|
||||||
|
if (parsed.protocol !== 'https:' || parsed.hostname !== 'github.com') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return parsed.toString();
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
const TRUNCATED_OUTPUT_MARKER = '\n...output truncated...';
|
const TRUNCATED_OUTPUT_MARKER = '\n...output truncated...';
|
||||||
const truncateGateOutput = (rawOutput) => {
|
const truncateGateOutput = (rawOutput) => {
|
||||||
const normalized = escapeHtml(String(rawOutput || '').trim());
|
const normalized = escapeHtml(String(rawOutput || '').trim());
|
||||||
@@ -243,12 +271,17 @@ jobs:
|
|||||||
|
|
||||||
const rows = checkedPlugins.length > 0
|
const rows = checkedPlugins.length > 0
|
||||||
? checkedPlugins.map((entry) => {
|
? checkedPlugins.map((entry) => {
|
||||||
const name = String(entry?.name || 'unknown');
|
const name = escapeMarkdownTableCell(entry?.name || 'unknown');
|
||||||
const quality = entry?.quality || {};
|
const quality = entry?.quality || {};
|
||||||
const sourceUrl = String(entry?.source_tree_url || '');
|
const sourceUrl = normalizeGitHubUrl(entry?.source_tree_url || '');
|
||||||
const locator = String(entry?.source?.sha || entry?.source?.ref || 'repository');
|
const locator = escapeMarkdownTableCell(entry?.source?.sha || entry?.source?.ref || 'repository');
|
||||||
const sourceCell = sourceUrl ? `[${locator}](${sourceUrl})` : locator;
|
const sourceCell = sourceUrl ? `[${locator}](<${sourceUrl}>)` : locator;
|
||||||
return `| ${name} | ${quality.vally_lint_status || 'not_run'} | ${quality.smoke_status || 'not_run'} | ${quality.version_match_status || 'not_run'} | ${quality.canvas_structure_status || 'not_run'} | ${quality.overall_status || 'not_run'} | ${sourceCell} |`;
|
const vallyLintStatus = escapeMarkdownTableCell(quality.vally_lint_status || 'not_run');
|
||||||
|
const smokeStatus = escapeMarkdownTableCell(quality.smoke_status || 'not_run');
|
||||||
|
const versionMatchStatus = escapeMarkdownTableCell(quality.version_match_status || 'not_run');
|
||||||
|
const canvasStructureStatus = escapeMarkdownTableCell(quality.canvas_structure_status || 'not_run');
|
||||||
|
const overallStatus = escapeMarkdownTableCell(quality.overall_status || 'not_run');
|
||||||
|
return `| ${name} | ${vallyLintStatus} | ${smokeStatus} | ${versionMatchStatus} | ${canvasStructureStatus} | ${overallStatus} | ${sourceCell} |`;
|
||||||
})
|
})
|
||||||
: ['| _none_ | not_run | not_run | not_run | not_run | not_run | _n/a_ |'];
|
: ['| _none_ | not_run | not_run | not_run | not_run | not_run | _n/a_ |'];
|
||||||
const failureDetails = checkedPlugins.flatMap((entry) => {
|
const failureDetails = checkedPlugins.flatMap((entry) => {
|
||||||
@@ -296,7 +329,9 @@ jobs:
|
|||||||
'',
|
'',
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
String(qualityResult.summary || '').trim() || '_No summary provided._',
|
String(qualityResult.summary || '').trim()
|
||||||
|
? `<pre><code>${escapeHtml(String(qualityResult.summary).trim())}</code></pre>`
|
||||||
|
: '_No summary provided._',
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
await intakeState.upsertExternalPluginIntakeComment({
|
await intakeState.upsertExternalPluginIntakeComment({
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { runExternalPluginQualityGates } from "./external-plugin-quality-gates.mjs";
|
import { runExternalPluginQualityGates } from "./external-plugin-quality-gates.mjs";
|
||||||
|
import { validateExternalPlugin } from "./external-plugin-validation.mjs";
|
||||||
|
|
||||||
function normalizePluginPath(pluginPath) {
|
function normalizePluginPath(pluginPath) {
|
||||||
if (!pluginPath || pluginPath === "/") {
|
if (!pluginPath || pluginPath === "/") {
|
||||||
@@ -66,13 +67,33 @@ function aggregateResultStatus(pluginResults) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createValidationFailureQuality(errors) {
|
||||||
|
const output = errors.map((error) => `- ${error}`).join("\n");
|
||||||
|
return {
|
||||||
|
overall_status: "fail",
|
||||||
|
vally_lint_status: "fail",
|
||||||
|
smoke_status: "not_run",
|
||||||
|
version_match_status: "not_run",
|
||||||
|
canvas_structure_status: "not_run",
|
||||||
|
failure_class: "submitter_fixes",
|
||||||
|
summary: "Plugin entry failed external.json validation. Fix the listed errors and re-run quality checks.",
|
||||||
|
vally_lint_output: output,
|
||||||
|
smoke_output: "Install smoke test skipped due to external.json validation errors.",
|
||||||
|
version_match_output: "Version match skipped due to external.json validation errors.",
|
||||||
|
canvas_structure_output: "Canvas structure check skipped due to external.json validation errors.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function runExternalPluginPrQualityGates(plugins) {
|
export async function runExternalPluginPrQualityGates(plugins) {
|
||||||
if (!Array.isArray(plugins)) {
|
if (!Array.isArray(plugins)) {
|
||||||
throw new Error("plugins must be an array");
|
throw new Error("plugins must be an array");
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkedPlugins = await Promise.all(plugins.map(async (plugin) => {
|
const checkedPlugins = await Promise.all(plugins.map(async (plugin) => {
|
||||||
const quality = await runExternalPluginQualityGates(plugin);
|
const validation = validateExternalPlugin(plugin, "changed-plugin", { policy: "marketplace" });
|
||||||
|
const quality = validation.errors.length > 0
|
||||||
|
? createValidationFailureQuality(validation.errors)
|
||||||
|
: await runExternalPluginQualityGates(plugin);
|
||||||
return {
|
return {
|
||||||
name: plugin?.name ?? "unknown",
|
name: plugin?.name ?? "unknown",
|
||||||
source: plugin?.source ?? {},
|
source: plugin?.source ?? {},
|
||||||
|
|||||||
@@ -60,12 +60,20 @@ function validatePluginName(name, prefix, errors) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.length > 50) {
|
if (name.length > 64) {
|
||||||
errors.push(`${prefix}: "name" must be 50 characters or fewer`);
|
errors.push(`${prefix}: "name" must be 64 characters or fewer`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!/^[a-z0-9-]+$/.test(name)) {
|
if (!/^[a-z0-9.-]+$/.test(name)) {
|
||||||
errors.push(`${prefix}: "name" must contain only lowercase letters, numbers, and hyphens`);
|
errors.push(`${prefix}: "name" must contain only lowercase letters, numbers, hyphens, and periods`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[a-z0-9].*[a-z0-9]$/.test(name) && !/^[a-z0-9]$/.test(name)) {
|
||||||
|
errors.push(`${prefix}: "name" must start and end with a lowercase letter or number`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.includes("--") || name.includes("..")) {
|
||||||
|
errors.push(`${prefix}: "name" must not contain consecutive hyphens or periods`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user