ok
This commit is contained in:
@@ -5,13 +5,61 @@ Shows the repository layout, skill locations, and what flat names would be gener
|
||||
"""
|
||||
|
||||
import re
|
||||
import io
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
MS_REPO = "https://github.com/microsoft/skills.git"
|
||||
|
||||
|
||||
def create_clone_target(prefix: str) -> Path:
|
||||
"""Return a writable, non-existent path for git clone destination."""
|
||||
repo_tmp_root = Path(__file__).resolve().parents[2] / ".tmp" / "tests"
|
||||
candidate_roots = (repo_tmp_root, Path(tempfile.gettempdir()))
|
||||
last_error: OSError | None = None
|
||||
|
||||
for root in candidate_roots:
|
||||
try:
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
probe_file = root / f".{prefix}write-probe-{uuid.uuid4().hex}.tmp"
|
||||
with probe_file.open("xb"):
|
||||
pass
|
||||
probe_file.unlink()
|
||||
return root / f"{prefix}{uuid.uuid4().hex}"
|
||||
except OSError as exc:
|
||||
last_error = exc
|
||||
|
||||
if last_error is not None:
|
||||
raise last_error
|
||||
raise OSError("Unable to determine clone destination")
|
||||
|
||||
|
||||
def configure_utf8_output() -> None:
|
||||
"""Best-effort UTF-8 stdout/stderr on Windows without dropping diagnostics."""
|
||||
for stream_name in ("stdout", "stderr"):
|
||||
stream = getattr(sys, stream_name)
|
||||
try:
|
||||
stream.reconfigure(encoding="utf-8", errors="backslashreplace")
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
buffer = getattr(stream, "buffer", None)
|
||||
if buffer is not None:
|
||||
setattr(
|
||||
sys,
|
||||
stream_name,
|
||||
io.TextIOWrapper(
|
||||
buffer, encoding="utf-8", errors="backslashreplace"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def extract_skill_name(skill_md_path: Path) -> str | None:
|
||||
"""Extract the 'name' field from SKILL.md YAML frontmatter."""
|
||||
try:
|
||||
@@ -37,18 +85,26 @@ def inspect_repo():
|
||||
print("🔍 Inspecting Microsoft Skills Repository Structure")
|
||||
print("=" * 60)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_path = Path(temp_dir)
|
||||
repo_path: Path | None = None
|
||||
try:
|
||||
repo_path = create_clone_target(prefix="ms-skills-")
|
||||
|
||||
print("\n1️⃣ Cloning repository...")
|
||||
subprocess.run(
|
||||
["git", "clone", "--depth", "1", MS_REPO, str(temp_path)],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
try:
|
||||
subprocess.run(
|
||||
["git", "clone", "--depth", "1", MS_REPO, str(repo_path)],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
print("\n❌ git clone failed.", file=sys.stderr)
|
||||
if exc.stderr:
|
||||
print(exc.stderr.strip(), file=sys.stderr)
|
||||
raise
|
||||
|
||||
# Find all SKILL.md files
|
||||
all_skill_mds = list(temp_path.rglob("SKILL.md"))
|
||||
all_skill_mds = list(repo_path.rglob("SKILL.md"))
|
||||
print(f"\n2️⃣ Total SKILL.md files found: {len(all_skill_mds)}")
|
||||
|
||||
# Show flat name mapping
|
||||
@@ -59,7 +115,7 @@ def inspect_repo():
|
||||
|
||||
for skill_md in sorted(all_skill_mds, key=lambda p: str(p)):
|
||||
try:
|
||||
rel = skill_md.parent.relative_to(temp_path)
|
||||
rel = skill_md.parent.relative_to(repo_path)
|
||||
except ValueError:
|
||||
rel = skill_md.parent
|
||||
|
||||
@@ -87,12 +143,18 @@ def inspect_repo():
|
||||
f"\n4️⃣ ✅ No name collisions — all {len(names_seen)} names are unique!")
|
||||
|
||||
print("\n✨ Inspection complete!")
|
||||
finally:
|
||||
if repo_path is not None:
|
||||
shutil.rmtree(repo_path, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
configure_utf8_output()
|
||||
try:
|
||||
inspect_repo()
|
||||
except subprocess.CalledProcessError as exc:
|
||||
sys.exit(exc.returncode or 1)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print(f"\n❌ Error: {e}", file=sys.stderr)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
76
scripts/tests/run-test-suite.js
Normal file
76
scripts/tests/run-test-suite.js
Normal file
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { spawnSync } = require("child_process");
|
||||
|
||||
const NETWORK_TEST_ENV = "ENABLE_NETWORK_TESTS";
|
||||
const ENABLED_VALUES = new Set(["1", "true", "yes", "on"]);
|
||||
const LOCAL_TEST_COMMANDS = [
|
||||
["scripts/tests/validate_skills_headings.test.js"],
|
||||
["scripts/run-python.js", "scripts/tests/test_validate_skills_headings.py"],
|
||||
];
|
||||
const NETWORK_TEST_COMMANDS = [
|
||||
["scripts/run-python.js", "scripts/tests/inspect_microsoft_repo.py"],
|
||||
["scripts/run-python.js", "scripts/tests/test_comprehensive_coverage.py"],
|
||||
];
|
||||
|
||||
function isNetworkTestsEnabled() {
|
||||
const value = process.env[NETWORK_TEST_ENV];
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
return ENABLED_VALUES.has(String(value).trim().toLowerCase());
|
||||
}
|
||||
|
||||
function runNodeCommand(args) {
|
||||
const result = spawnSync(process.execPath, args, { stdio: "inherit" });
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
|
||||
if (result.signal) {
|
||||
process.kill(process.pid, result.signal);
|
||||
}
|
||||
|
||||
if (typeof result.status !== "number") {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status);
|
||||
}
|
||||
}
|
||||
|
||||
function runCommandSet(commands) {
|
||||
for (const commandArgs of commands) {
|
||||
runNodeCommand(commandArgs);
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const mode = process.argv[2];
|
||||
|
||||
if (mode === "--local") {
|
||||
runCommandSet(LOCAL_TEST_COMMANDS);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === "--network") {
|
||||
runCommandSet(NETWORK_TEST_COMMANDS);
|
||||
return;
|
||||
}
|
||||
|
||||
runCommandSet(LOCAL_TEST_COMMANDS);
|
||||
|
||||
if (!isNetworkTestsEnabled()) {
|
||||
console.log(
|
||||
`[tests] Skipping network integration tests. Set ${NETWORK_TEST_ENV}=1 to enable.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[tests] ${NETWORK_TEST_ENV} enabled; running network integration tests.`);
|
||||
runCommandSet(NETWORK_TEST_COMMANDS);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -5,14 +5,62 @@ Ensures all skills are captured and no directory name collisions exist.
|
||||
"""
|
||||
|
||||
import re
|
||||
import io
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from collections import defaultdict
|
||||
|
||||
MS_REPO = "https://github.com/microsoft/skills.git"
|
||||
|
||||
|
||||
def create_clone_target(prefix: str) -> Path:
|
||||
"""Return a writable, non-existent path for git clone destination."""
|
||||
repo_tmp_root = Path(__file__).resolve().parents[2] / ".tmp" / "tests"
|
||||
candidate_roots = (repo_tmp_root, Path(tempfile.gettempdir()))
|
||||
last_error: OSError | None = None
|
||||
|
||||
for root in candidate_roots:
|
||||
try:
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
probe_file = root / f".{prefix}write-probe-{uuid.uuid4().hex}.tmp"
|
||||
with probe_file.open("xb"):
|
||||
pass
|
||||
probe_file.unlink()
|
||||
return root / f"{prefix}{uuid.uuid4().hex}"
|
||||
except OSError as exc:
|
||||
last_error = exc
|
||||
|
||||
if last_error is not None:
|
||||
raise last_error
|
||||
raise OSError("Unable to determine clone destination")
|
||||
|
||||
|
||||
def configure_utf8_output() -> None:
|
||||
"""Best-effort UTF-8 stdout/stderr on Windows without dropping diagnostics."""
|
||||
for stream_name in ("stdout", "stderr"):
|
||||
stream = getattr(sys, stream_name)
|
||||
try:
|
||||
stream.reconfigure(encoding="utf-8", errors="backslashreplace")
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
buffer = getattr(stream, "buffer", None)
|
||||
if buffer is not None:
|
||||
setattr(
|
||||
sys,
|
||||
stream_name,
|
||||
io.TextIOWrapper(
|
||||
buffer, encoding="utf-8", errors="backslashreplace"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def extract_skill_name(skill_md_path: Path) -> str | None:
|
||||
"""Extract the 'name' field from SKILL.md YAML frontmatter."""
|
||||
try:
|
||||
@@ -41,27 +89,35 @@ def analyze_skill_locations():
|
||||
print("🔬 Comprehensive Skill Coverage & Uniqueness Analysis")
|
||||
print("=" * 60)
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
temp_path = Path(temp_dir)
|
||||
repo_path: Path | None = None
|
||||
try:
|
||||
repo_path = create_clone_target(prefix="ms-skills-")
|
||||
|
||||
print("\n1️⃣ Cloning repository...")
|
||||
subprocess.run(
|
||||
["git", "clone", "--depth", "1", MS_REPO, str(temp_path)],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
try:
|
||||
subprocess.run(
|
||||
["git", "clone", "--depth", "1", MS_REPO, str(repo_path)],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as exc:
|
||||
print("\n❌ git clone failed.", file=sys.stderr)
|
||||
if exc.stderr:
|
||||
print(exc.stderr.strip(), file=sys.stderr)
|
||||
raise
|
||||
|
||||
# Find ALL SKILL.md files
|
||||
all_skill_files = list(temp_path.rglob("SKILL.md"))
|
||||
all_skill_files = list(repo_path.rglob("SKILL.md"))
|
||||
print(f"\n2️⃣ Total SKILL.md files found: {len(all_skill_files)}")
|
||||
|
||||
# Categorize by location
|
||||
location_types = defaultdict(list)
|
||||
for skill_file in all_skill_files:
|
||||
path_str = str(skill_file)
|
||||
if ".github/skills" in path_str:
|
||||
path_str = skill_file.as_posix()
|
||||
if ".github/skills/" in path_str:
|
||||
location_types["github_skills"].append(skill_file)
|
||||
elif ".github/plugins" in path_str:
|
||||
elif ".github/plugins/" in path_str:
|
||||
location_types["github_plugins"].append(skill_file)
|
||||
elif "/skills/" in path_str:
|
||||
location_types["skills_dir"].append(skill_file)
|
||||
@@ -81,7 +137,7 @@ def analyze_skill_locations():
|
||||
|
||||
for skill_file in all_skill_files:
|
||||
try:
|
||||
rel = skill_file.parent.relative_to(temp_path)
|
||||
rel = skill_file.parent.relative_to(repo_path)
|
||||
except ValueError:
|
||||
rel = skill_file.parent
|
||||
|
||||
@@ -163,9 +219,13 @@ def analyze_skill_locations():
|
||||
"invalid_names": len(invalid_names),
|
||||
"passed": is_pass,
|
||||
}
|
||||
finally:
|
||||
if repo_path is not None:
|
||||
shutil.rmtree(repo_path, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
configure_utf8_output()
|
||||
try:
|
||||
results = analyze_skill_locations()
|
||||
|
||||
@@ -176,14 +236,18 @@ if __name__ == "__main__":
|
||||
if results["passed"]:
|
||||
print("\n✅ V4 FLAT STRUCTURE IS VALID")
|
||||
print(" All names are unique and valid directory names!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\n⚠️ V4 FLAT STRUCTURE NEEDS FIXES")
|
||||
if results["collisions"] > 0:
|
||||
print(f" {results['collisions']} name collisions to resolve")
|
||||
if results["invalid_names"] > 0:
|
||||
print(f" {results['invalid_names']} invalid directory names")
|
||||
sys.exit(1)
|
||||
|
||||
except subprocess.CalledProcessError as exc:
|
||||
sys.exit(exc.returncode or 1)
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print(f"\n❌ Error: {e}", file=sys.stderr)
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
Reference in New Issue
Block a user