fix: respect all manifest locations in smoke-test post-install verification (#1952)

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>
This commit is contained in:
Aaron Powell
2026-06-08 23:57:34 -07:00
committed by GitHub
parent 27bc67adbb
commit ecf170397b
2 changed files with 12 additions and 4 deletions
+10 -3
View File
@@ -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}`,
};
}
+2 -1
View File
@@ -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) {