Skip to content

fix: PyPI package name MertCapkin_GraphStack (graphstack taken) #15

fix: PyPI package name MertCapkin_GraphStack (graphstack taken)

fix: PyPI package name MertCapkin_GraphStack (graphstack taken) #15

Workflow file for this run

name: GraphStack CI
on:
push:
branches: [main, dev, master]
pull_request:
branches: [main, master]
jobs:
# ──────────────────────────────────────────────────────────────────
# Job 1: Bash syntax check (Linux only — bash isn't on Windows native)
# ──────────────────────────────────────────────────────────────────
bash-syntax:
name: Bash syntax check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: bash -n install.sh
run: bash -n install.sh
- name: bash -n scripts/board.sh
run: bash -n scripts/board.sh
- name: bash -n scripts/post-commit
run: bash -n scripts/post-commit
# ──────────────────────────────────────────────────────────────────
# Job 2: Cross-platform validation matrix (Win + Mac + Linux)
# ──────────────────────────────────────────────────────────────────
validate:
name: Validate (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
# depth 2+ required: validate --fail-stale-graph matches graph commit to HEAD~1
# when graph artifacts land in a follow-up commit (v4.1 release pattern).
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install package, pytest, and Graphify
run: |
python -m pip install --quiet pytest
python -m pip install --quiet -e .
python -m pip install --quiet -r requirements.txt
- name: Verify required files exist (Python)
shell: python
run: |
import sys
from pathlib import Path
required = [
".cursor/skills/architect/ARCHITECT.md",
".cursor/skills/builder/BUILDER.md",
".cursor/skills/reviewer/REVIEWER.md",
".cursor/skills/qa/QA.md",
".cursor/skills/ship/SHIP.md",
".cursor/skills/bootstrapper/BOOTSTRAPPER.md",
"orchestrator/ORCHESTRATOR.md",
"orchestrator/TOKEN_OPTIMIZER.md",
".cursor/rules/graphstack.mdc",
".cursor/commands/graphstack.md",
"handoff/BRIEF.md",
"handoff/REVIEW.md",
"handoff/BOOTSTRAP.md",
"handoff/STATE.md",
"handoff/board/README.md",
"handoff/board/todo/.gitkeep",
"handoff/board/doing/.gitkeep",
"handoff/board/done/.gitkeep",
"docs/CURSOR_PROMPTS.md",
"install.sh",
"install.ps1",
"requirements.txt",
"pyproject.toml",
"scripts/board.sh",
"scripts/board.ps1",
"scripts/post-commit",
"scripts/post-commit.ps1",
"scripts/graphstack/__init__.py",
"scripts/graphstack/__main__.py",
"scripts/graphstack/cli.py",
"scripts/graphstack/board.py",
"scripts/graphstack/installer.py",
"scripts/graphstack/hook.py",
"scripts/graphstack/platform_utils.py",
"scripts/graphstack/constants.py",
"scripts/graphstack/validate.py",
"scripts/graphstack/gate.py",
"scripts/graphstack/state.py",
"scripts/graphstack/graph.py",
"scripts/graphstack/init_cmd.py",
"scripts/graphstack/bootstrap.py",
"scripts/graphstack/assets/orchestrator/ORCHESTRATOR.md",
"scripts/bootstrap.ps1",
"scripts/bootstrap.sh",
"scripts/sync_assets.py",
"scripts/gate-hook.sh",
"scripts/gate-hook.ps1",
".cursor/hooks.json",
".claude/settings.json",
".graphstack-framework",
".graphifyignore",
"docs/case-studies/graphstack-self.md",
]
missing = [p for p in required if not Path(p).is_file()]
for p in required:
mark = "OK" if Path(p).is_file() else "MISSING"
print(f"[{mark}] {p}")
if missing:
print(f"\n{len(missing)} file(s) missing.", file=sys.stderr)
sys.exit(1)
- name: Validate GNAP task schema (board new)
shell: python
run: |
import json, subprocess, sys
from pathlib import Path
subprocess.run(
[sys.executable, "-m", "graphstack", "board", "new", "schema-check", "CI schema probe"],
check=True,
)
path = Path("handoff/board/todo/schema-check.json")
data = json.loads(path.read_text(encoding="utf-8"))
required = ["id", "title", "status", "created_at"]
missing = [k for k in required if k not in data]
path.unlink(missing_ok=True)
if missing:
print(f"board new() missing keys: {missing}", file=sys.stderr)
sys.exit(1)
print("GNAP task schema OK")
- name: Run graphstack pytest suite
run: python -m pytest scripts/graphstack/tests -v
- name: Validate project layout (graphstack validate)
run: python -m graphstack validate
- name: Process gate check (graphstack gate)
run: python -m graphstack gate check
- name: Refresh knowledge graph (CI)
run: graphify update .
- name: Fail if knowledge graph is stale vs HEAD
run: python -m graphstack validate --fail-stale-graph
- name: Board smoke test (Python module — cross-platform)
shell: python
run: |
import os, subprocess, sys
from pathlib import Path
def run(*args):
proc = subprocess.run(
[sys.executable, "-m", "graphstack", "board", *args],
check=True,
)
return proc
run("status")
run("new", "py-ci-task", "Pythonic", "CI", "task")
run("claim", "py-ci-task", "reviewer")
run("complete", "py-ci-task")
done = Path("handoff/board/done/py-ci-task.json")
if done.exists():
done.unlink()
print("Python board lifecycle OK")
- name: Board smoke test (bash shim — Unix only)
if: runner.os != 'Windows'
run: |
bash scripts/board.sh status
bash scripts/board.sh new sh-ci-task Bash smoke test task
bash scripts/board.sh claim sh-ci-task builder
bash scripts/board.sh complete sh-ci-task
rm -f handoff/board/done/sh-ci-task.json
echo "bash shim board lifecycle OK"
- name: Board smoke test (PowerShell shim — Windows only)
if: runner.os == 'Windows'
shell: pwsh
run: |
.\scripts\board.ps1 status
.\scripts\board.ps1 new ps-ci-task PowerShell smoke test task
.\scripts\board.ps1 claim ps-ci-task builder
.\scripts\board.ps1 complete ps-ci-task
if (Test-Path handoff/board/done/ps-ci-task.json) {
Remove-Item handoff/board/done/ps-ci-task.json
}
Write-Host "PowerShell shim board lifecycle OK"
# ──────────────────────────────────────────────────────────────────
# Job 3: Markdown size + link sanity check (Linux only — runs once)
# ──────────────────────────────────────────────────────────────────
markdown-checks:
name: Markdown lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Detect short markdown files
shell: python
run: |
import sys
from pathlib import Path
warnings = 0
for path in sorted(Path(".").glob(".cursor/skills/**/*.md")) + sorted(Path("orchestrator").glob("*.md")):
lines = sum(1 for _ in path.open(encoding="utf-8"))
if lines < 10:
print(f"WARN: {path} seems too short ({lines} lines)")
warnings += 1
if warnings:
print(f"\n{warnings} short markdown file(s) detected (warning only).")
else:
print("All markdown files pass size threshold.")
- name: Check internal links resolve
shell: python
run: |
import re, sys
from pathlib import Path
link_re = re.compile(r"\]\(([^)#]+?)(?:#[^)]*)?\)")
broken = []
for md in Path(".").rglob("*.md"):
if any(part in md.parts for part in (".git", "node_modules", "graphify-out")):
continue
text = md.read_text(encoding="utf-8", errors="replace")
for match in link_re.finditer(text):
target = match.group(1).strip()
if target.startswith(("http://", "https://", "mailto:")):
continue
if target.startswith("#") or "://" in target:
continue
resolved = (md.parent / target).resolve()
if not resolved.exists():
broken.append((md, target))
for md, target in broken:
print(f"BROKEN: {md} -> {target}")
if broken:
print(f"\n{len(broken)} broken link(s) found.", file=sys.stderr)
sys.exit(1)
print("All relative markdown links resolve.")