test-package-codex-plugin.sh 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
  4. REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
  5. SCRIPT_UNDER_TEST="$REPO_ROOT/scripts/package-codex-plugin.sh"
  6. FAILURES=0
  7. TEST_ROOT="$(mktemp -d)"
  8. cleanup() {
  9. rm -rf "$TEST_ROOT"
  10. }
  11. trap cleanup EXIT
  12. pass() {
  13. echo " [PASS] $1"
  14. }
  15. fail() {
  16. echo " [FAIL] $1"
  17. FAILURES=$((FAILURES + 1))
  18. }
  19. assert_equals() {
  20. local actual="$1"
  21. local expected="$2"
  22. local description="$3"
  23. if [[ "$actual" == "$expected" ]]; then
  24. pass "$description"
  25. else
  26. fail "$description"
  27. echo " expected: $expected"
  28. echo " actual: $actual"
  29. fi
  30. }
  31. assert_contains() {
  32. local haystack="$1"
  33. local needle="$2"
  34. local description="$3"
  35. if printf '%s' "$haystack" | grep -Fq -- "$needle"; then
  36. pass "$description"
  37. else
  38. fail "$description"
  39. echo " expected to find: $needle"
  40. fi
  41. }
  42. assert_not_matches() {
  43. local haystack="$1"
  44. local pattern="$2"
  45. local description="$3"
  46. if printf '%s' "$haystack" | grep -Eq -- "$pattern"; then
  47. fail "$description"
  48. echo " did not expect to match: $pattern"
  49. else
  50. pass "$description"
  51. fi
  52. }
  53. write_metadata_fixture() {
  54. local destination="$1"
  55. local skill
  56. while IFS= read -r skill; do
  57. mkdir -p "$destination/skills/$skill/agents"
  58. cat >"$destination/skills/$skill/agents/openai.yaml" <<EOF
  59. interface:
  60. display_name: "$skill"
  61. short_description: "Fixture metadata for $skill"
  62. EOF
  63. done < <(find "$REPO_ROOT/skills" -mindepth 1 -maxdepth 1 -type d -print | sed 's#.*/##' | sort)
  64. }
  65. echo "Codex package archive tests"
  66. metadata_source="$TEST_ROOT/metadata-source"
  67. archive="$TEST_ROOT/superpowers.tar.gz"
  68. extracted="$TEST_ROOT/extracted"
  69. write_metadata_fixture "$metadata_source"
  70. if output="$("$SCRIPT_UNDER_TEST" --allow-dirty --metadata-source "$metadata_source" --output "$archive" 2>&1)"; then
  71. pass "package script exits successfully"
  72. else
  73. fail "package script exits successfully"
  74. printf '%s\n' "$output" | sed 's/^/ /'
  75. fi
  76. if [[ -f "$archive" ]]; then
  77. pass "package script writes archive"
  78. else
  79. fail "package script writes archive"
  80. fi
  81. assert_contains "$output" "Archive:" "reports archive path"
  82. assert_contains "$output" "SHA-256:" "reports archive checksum"
  83. mkdir -p "$extracted"
  84. tar -xzf "$archive" -C "$extracted"
  85. archive_paths="$(tar -tzf "$archive" | sort)"
  86. unexpected_pattern='(^superpowers/|^\.agents/|^hooks/|package\.json$|^\.git|^\.pytest_cache|^\.ruff_cache|^scripts/|^tests/|^docs/|^evals/|^lib/|^\.claude|^\.cursor|^\.kimi|^\.opencode|^\.pi|^AGENTS\.md$|^CLAUDE\.md$|^GEMINI\.md$|^RELEASE-NOTES\.md$|^CHANGELOG\.md$)'
  87. assert_not_matches "$archive_paths" "$unexpected_pattern" "archive excludes source-only paths"
  88. assert_contains "$archive_paths" ".codex-plugin/plugin.json" "archive includes Codex manifest"
  89. assert_contains "$archive_paths" "skills/brainstorming/SKILL.md" "archive includes skills"
  90. assert_contains "$archive_paths" "skills/brainstorming/agents/openai.yaml" "archive includes OpenAI skill metadata"
  91. assert_contains "$archive_paths" "assets/app-icon.png" "archive includes app icon"
  92. assert_contains "$archive_paths" "assets/superpowers-small.svg" "archive includes composer icon"
  93. manifest_summary="$(tar -xOf "$archive" .codex-plugin/plugin.json | python3 -c 'import json,sys; data=json.load(sys.stdin); print("\t".join([data["name"], data["version"], data["skills"], str(data.get("hooks"))]))')"
  94. expected_version="$(python3 -c 'import json; print(json.load(open("'"$REPO_ROOT"'/.codex-plugin/plugin.json"))["version"])')"
  95. assert_equals "$manifest_summary" "superpowers $expected_version ./skills/ None" "archive manifest is current and hook-free"
  96. skill_count="$(find "$extracted/skills" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')"
  97. metadata_count="$(find "$extracted/skills" -path '*/agents/openai.yaml' -type f | wc -l | tr -d ' ')"
  98. assert_equals "$metadata_count" "$skill_count" "every packaged skill has OpenAI metadata"
  99. task_brief_mode="$(tar -tzvf "$archive" skills/subagent-driven-development/scripts/task-brief | awk '{print $1}')"
  100. assert_equals "$task_brief_mode" "-rwxr-xr-x" "archive preserves executable script mode"
  101. metadata_times="$(tar -tzvf "$archive" | awk '{print $6, $7, $8}' | sort -u)"
  102. assert_equals "$metadata_times" "Dec 31 1969" "archive normalizes entry timestamps"
  103. if [[ "$FAILURES" -eq 0 ]]; then
  104. echo "All Codex package archive tests passed"
  105. else
  106. echo "$FAILURES Codex package archive test(s) failed"
  107. exit 1
  108. fi