From ecf170397b28e005bfd2438e1406f72cba457c86 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Mon, 8 Jun 2026 23:57:34 -0700 Subject: [PATCH] fix: respect all manifest locations in smoke-test post-install verification (#1952) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The install smoke gate was hardcoding .github/plugin/plugin.json as the expected manifest path after copilot plugin install, which caused a false ail for plugins whose manifests live at plugin.json (root) or .plugins/plugin.json instead of the Copilot CLI convention. Replace the hardcoded path with a call to the existing indPluginJson() helper that already probes all three candidate locations in priority order. Separate the 'install directory missing' check from 'no manifest found' so error messages surface the actual root cause. Also fix a .plugin/ → .plugins/ typo in EXTERNAL_PLUGIN_ROOT_MANIFEST_PATHS (external-plugin-validation.mjs) which caused the error message shown to submitters to reference a path that indPluginJson never actually checks. Add cross-reference comments on both constants so they stay in sync. Closes: reported in issue #1837 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/external-plugin-quality-gates.mjs | 13 ++++++++++--- eng/external-plugin-validation.mjs | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/eng/external-plugin-quality-gates.mjs b/eng/external-plugin-quality-gates.mjs index 6bf6ed77..06edfcd3 100644 --- a/eng/external-plugin-quality-gates.mjs +++ b/eng/external-plugin-quality-gates.mjs @@ -161,6 +161,7 @@ function downloadSkillValidator(workDir) { // both the Copilot CLI and many external repos use nested conventions. We read the // manifest ourselves so skill/agent paths can be resolved from the plugin root // consistently, regardless of where the manifest lives. +// 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"], @@ -307,11 +308,17 @@ function runInstallSmokeGate(workDir, plugin) { } const installedPluginPath = path.join(homeDir, ".copilot", "installed-plugins", "external-plugin-intake", plugin.name); - const pluginManifestPath = path.join(installedPluginPath, ".github", "plugin", "plugin.json"); - if (!fs.existsSync(installedPluginPath) || !fs.existsSync(pluginManifestPath)) { + if (!fs.existsSync(installedPluginPath)) { return { status: "fail", - output: `Plugin installed but expected files were missing at ${installedPluginPath}`, + output: `Plugin installed but install directory was not found at ${installedPluginPath}`, + }; + } + const pluginManifestPath = findPluginJson(installedPluginPath); + if (!pluginManifestPath) { + return { + status: "fail", + output: `Plugin installed but no plugin.json was found in any recognized location under ${installedPluginPath}`, }; } diff --git a/eng/external-plugin-validation.mjs b/eng/external-plugin-validation.mjs index 1a49bff4..87bc271e 100644 --- a/eng/external-plugin-validation.mjs +++ b/eng/external-plugin-validation.mjs @@ -23,10 +23,11 @@ export const EXTERNAL_PLUGIN_POLICIES = Object.freeze({ }), }); +// NOTE: Keep in sync with PLUGIN_JSON_CANDIDATES in external-plugin-quality-gates.mjs const EXTERNAL_PLUGIN_ROOT_MANIFEST_PATHS = Object.freeze([ "plugin.json", ".github/plugin/plugin.json", - ".plugin/plugin.json", + ".plugins/plugin.json", ]); function resolvePolicy(policy) {