Skip to content

feat: publish on skills.sh + rename skills with purchasely- prefix (1… #52

feat: publish on skills.sh + rename skills with purchasely- prefix (1…

feat: publish on skills.sh + rename skills with purchasely- prefix (1… #52

Workflow file for this run

name: validate
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
structure:
name: Plugin structure & JSON
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Validate plugin.json
run: |
test -f .claude-plugin/plugin.json
python3 -c "import json; json.load(open('.claude-plugin/plugin.json'))"
test -f purchasely/.claude-plugin/plugin.json
python3 -c "import json; json.load(open('purchasely/.claude-plugin/plugin.json'))"
test -f purchasely/.codex-plugin/plugin.json
python3 -c "import json; json.load(open('purchasely/.codex-plugin/plugin.json'))"
test -f .cursor-plugin/plugin.json
python3 -c "import json; json.load(open('.cursor-plugin/plugin.json'))"
test -f purchasely/.cursor-plugin/plugin.json
python3 -c "import json; json.load(open('purchasely/.cursor-plugin/plugin.json'))"
- name: Validate marketplace.json
run: |
test -f .claude-plugin/marketplace.json
python3 -c "import json; json.load(open('.claude-plugin/marketplace.json'))"
test -f .agents/plugins/marketplace.json
python3 -c "import json; json.load(open('.agents/plugins/marketplace.json'))"
python3 - <<'PY'
import json
marketplace = json.load(open('.agents/plugins/marketplace.json'))
plugin = marketplace['plugins'][0]
assert plugin['name'] == 'purchasely'
assert plugin['source']['path'] == './purchasely'
PY
test -f .cursor-plugin/marketplace.json
python3 -c "import json; json.load(open('.cursor-plugin/marketplace.json'))"
python3 - <<'PY'
import json
marketplace = json.load(open('.cursor-plugin/marketplace.json'))
assert marketplace['metadata']['pluginRoot'] == '.'
plugin = marketplace['plugins'][0]
assert plugin['name'] == 'purchasely'
assert plugin['source'] == 'purchasely'
PY
- name: Validate Gemini extension
run: |
test -f gemini-extension.json
python3 -c "import json; json.load(open('gemini-extension.json'))"
- name: Validate package.json
run: |
test -f package.json
python3 -c "import json; json.load(open('package.json'))"
- name: Check required files
run: |
for f in README.md LICENSE CONTRIBUTING.md SECURITY.md CODE_OF_CONDUCT.md CHANGELOG.md .gitignore; do
test -f "$f" || { echo "::error file=$f::Missing required file"; exit 1; }
done
- name: Check all skills have SKILL.md
run: |
for d in skills/*/; do
test -f "$d/SKILL.md" || { echo "::error file=$d::Missing SKILL.md"; exit 1; }
done
- name: Check plugin folder links
run: |
for f in \
purchasely/skills/integrate/SKILL.md \
purchasely/references/android/initialization.md \
purchasely/commands/integrate.md \
purchasely/agents/sdk-expert.md \
purchasely/hooks/hooks.json
do
test -f "$f" || { echo "::error file=$f::Missing plugin folder link"; exit 1; }
done
- name: Validate SKILL.md front-matter
run: |
python3 <<'PY'
import os, re, sys, glob
required = ("name", "description")
errors = []
for path in sorted(glob.glob("skills/*/SKILL.md")):
with open(path) as f:
content = f.read()
m = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL)
if not m:
errors.append(f"{path}: missing YAML front-matter")
continue
fm = m.group(1)
for key in required:
if not re.search(rf"^{key}\s*:", fm, re.MULTILINE):
errors.append(f"{path}: front-matter missing '{key}:'")
if errors:
for e in errors:
print(f"::error::{e}")
sys.exit(1)
print("All SKILL.md front-matter OK")
PY
- name: Validate skill reference links
run: |
python3 <<'PY'
import glob
import pathlib
import re
import sys
errors = []
pattern = re.compile(r"(?<![\w./-])(?:\.\./\.\./references/|\.\./references/|references/)[A-Za-z0-9_./-]+")
for path in sorted(glob.glob("skills/*/SKILL.md")):
skill_path = pathlib.Path(path)
content = skill_path.read_text()
for match in pattern.finditer(content):
ref = match.group(0).rstrip(".,;:)")
if not ref.startswith("../../references/"):
errors.append(f"{path}: use ../../{ref.removeprefix('../')} so installed plugins resolve references from the skill directory")
continue
target = (skill_path.parent / ref).resolve()
if not target.exists():
errors.append(f"{path}: missing reference target {ref}")
if errors:
for error in errors:
print(f"::error::{error}")
sys.exit(1)
print("All skill reference links OK")
PY
- name: Reject committed real API keys
run: |
if grep -rE 'pk_(live|test)_[A-Za-z0-9]{20,}|sk_(live|test)_[A-Za-z0-9]{20,}' \
--include='*.md' --include='*.json' --include='*.sh' --include='*.yml' \
--exclude-dir=.git . ; then
echo "::error::Real-looking API key detected — refusing to merge."
exit 1
fi
markdownlint:
name: Lint Markdown
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DavidAnson/markdownlint-cli2-action@v17
with:
globs: |
**/*.md
!node_modules
!.github/PULL_REQUEST_TEMPLATE.md
!.github/ISSUE_TEMPLATE/**