Skip to content

Schema Submission

Schema Submission #11

name: Schema Submission
on:
issues:
types: [opened]
jobs:
ingest:
if: startsWith(github.event.issue.title, 'schema-submission:')
runs-on: ubuntu-latest
permissions:
contents: write
issues: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install dependencies
run: pip install -r requirements.txt
- name: Parse issue metadata
id: meta
run: |
TITLE="${{ github.event.issue.title }}"
SCHEMA_NAME="${TITLE#schema-submission:}"
SCHEMA_NAME=$(echo "$SCHEMA_NAME" | xargs | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
echo "schema_name=$SCHEMA_NAME" >> $GITHUB_OUTPUT
# Determine bump type:
# label "breaking" → major, default → minor
LABELS='${{ toJson(github.event.issue.labels.*.name) }}'
if echo "$LABELS" | grep -q "breaking"; then
echo "bump=major" >> $GITHUB_OUTPUT
else
echo "bump=minor" >> $GITHUB_OUTPUT
fi
- name: Extract and convert schema from issue body
env:
ISSUE_BODY: ${{ github.event.issue.body }}
SCHEMA_NAME: ${{ steps.meta.outputs.schema_name }}
run: |
python3 - << 'PYEOF'
import os, re, sys, json
import yaml
body = os.environ["ISSUE_BODY"]
name = os.environ["SCHEMA_NAME"]
# Detect format from the code-fence language tag:
# ```linkml / ```yaml / ```yml → already LinkML
# ```json / ```json-schema → JSON Schema, needs conversion
fenced = re.search(r'```([\w-]*)\s*\n(.*?)```', body, re.DOTALL)
if fenced:
lang = (fenced.group(1) or "yaml").lower()
content = fenced.group(2).strip()
else:
lang = "yaml"
content = body.strip()
json_schema_langs = {"json", "json-schema", "jsonschema"}
linkml_langs = {"yaml", "yml", "linkml", ""}
os.makedirs("schemas", exist_ok=True)
out_path = f"schemas/{name}.yml"
if lang in json_schema_langs:
print(f"Detected format: JSON Schema (lang={lang!r}) — converting to LinkML…")
sys.path.insert(0, ".")
from neuro_ghost.converters.from_jsonschema import convert as js2lm
try:
data = json.loads(content)
except json.JSONDecodeError:
data = yaml.safe_load(content)
linkml_dict = js2lm(data, name)
with open(out_path, "w") as f:
yaml.dump(linkml_dict, f, default_flow_style=False,
allow_unicode=True, sort_keys=False)
print(f"Converted → {out_path} "
f"({len(linkml_dict['classes'])} classes, "
f"{len(linkml_dict['slots'])} slots)")
elif lang in linkml_langs or lang not in json_schema_langs:
print(f"Detected format: LinkML (lang={lang!r})")
with open(out_path, "w") as f:
f.write(content)
print(f"Wrote {out_path}")
else:
print(f"ERROR: Unsupported schema format '{lang}'. "
"Use ```yaml (LinkML) or ```json (JSON Schema).")
sys.exit(1)
PYEOF
- name: Validate LinkML schema
run: |
python neuro_ghost/validate_schema.py schemas/${{ steps.meta.outputs.schema_name }}.yml
- name: Seed schema.org (if DB not cached)
run: |
python neuro_ghost/seed.py \
--db registry.lbug \
--registry-version 1.0.0
- name: Ingest schema
run: |
python neuro_ghost/ingest_linkml.py \
--file schemas/${{ steps.meta.outputs.schema_name }}.yml \
--db registry.lbug \
--issue ${{ github.event.issue.number }} \
--agent "${{ github.event.issue.user.login }}"
- name: Run alignment
run: |
python neuro_ghost/align.py \
--source ${{ steps.meta.outputs.schema_name }} \
--db registry.lbug
- name: Export + version registry
run: |
python neuro_ghost/export_json.py \
--db registry.lbug \
--bump ${{ steps.meta.outputs.bump }} \
--issue ${{ github.event.issue.number }} \
--agent "${{ github.event.issue.user.login }}" \
--schema ${{ steps.meta.outputs.schema_name }}
- name: Commit all artifacts
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add \
schemas/${{ steps.meta.outputs.schema_name }}.yml \
data/registry.json \
data/versions/ \
data/provenance.json
git diff --cached --quiet || git commit -m \
"feat(registry): ingest ${{ steps.meta.outputs.schema_name }} from issue #${{ github.event.issue.number }}"
git push
- name: Comment success
if: success()
uses: actions/github-script@v7
with:
script: |
const name = "${{ steps.meta.outputs.schema_name }}";
const pagesUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}`;
await github.rest.issues.createComment({
owner: context.repo.owner, repo: context.repo.repo,
issue_number: context.issue.number,
body: [
`✅ **\`${name}\` ingested successfully.**`,
``,
`- Classes, properties, and alignments committed.`,
`- Provenance logged to \`data/provenance.json\`.`,
`- Archived snapshot in \`data/versions/\`.`,
``,
`Browse: ${pagesUrl}`,
].join("\n"),
});
await github.rest.issues.update({
owner: context.repo.owner, repo: context.repo.repo,
issue_number: context.issue.number, state: "closed",
});
- name: Comment failure
if: failure()
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner, repo: context.repo.repo,
issue_number: context.issue.number,
body: [
`❌ **Ingestion failed.**`,
``,
`Check the run log for the specific LinkML validation error (e.g. an undefined slot, ` +
`\`is_a\` parent, or range reference).`,
``,
`[View run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`,
].join("\n"),
});