diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..a6be48b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,21 @@ +name: Bug report +description: Something is broken or looks wrong +labels: ["bug"] +body: + - type: textarea + id: what + attributes: + label: What happened? + description: What did you expect, and what did you see instead? + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to reproduce + placeholder: "1. Open...\n2. Click...\n3. See..." + - type: input + id: device + attributes: + label: Browser and device + placeholder: "Firefox 140 on Windows 11, Safari on iPhone..." diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..709f1bc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Security problem + url: https://github.com/Cop1ouss/secplus-tracker/security/advisories/new + about: Report vulnerabilities privately, not as a public issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..7a277a3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,14 @@ +name: Idea or improvement +description: Suggest a feature, a topic to cover, or a better way to do something +labels: ["enhancement"] +body: + - type: textarea + id: idea + attributes: + label: The idea + validations: + required: true + - type: textarea + id: why + attributes: + label: Why it would help diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2835d60 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +# Weekly dependency and Actions updates, grouped into one PR per ecosystem. +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "08:00" + timezone: "America/New_York" + labels: ["dependencies"] + commit-message: + prefix: "chore(deps)" + groups: + github_actions: + patterns: ["*"] diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..6874cd3 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,8 @@ +## What changed + +## Why + +## Checklist +- [ ] CI is green +- [ ] Tested in the browser (or ran the script) myself +- [ ] README / PRD updated if behavior changed diff --git a/.github/scripts/check_site.py b/.github/scripts/check_site.py new file mode 100644 index 0000000..8680061 --- /dev/null +++ b/.github/scripts/check_site.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +"""Repo health check run by CI: every JSON file parses, and every local link in +HTML and Markdown files points at a file that exists. Standard library only. +Usage: python3 .github/scripts/check_site.py [ROOT]""" +import json, re, shutil, subprocess, sys, tempfile +from pathlib import Path +from urllib.parse import unquote, urlsplit + +ROOT = Path(sys.argv[1] if len(sys.argv) > 1 else ".").resolve() +SKIP_DIRS = {".git", "node_modules", "dist", ".venv", "__pycache__", ".claude"} +HTML_REF = re.compile(r'''(?:href|src)\s*=\s*["']([^"'<>]+)["']''', re.I) +MD_REF = re.compile(r'\]\(([^)\s]+)(?:\s+"[^"]*")?\)') +EXTERNAL = ("http:", "https:", "mailto:", "tel:", "data:", "javascript:", "#", "//", "blob:") + +def files(*exts): + for p in ROOT.rglob("*"): + if p.is_file() and p.suffix.lower() in exts and not SKIP_DIRS & set(p.relative_to(ROOT).parts): + yield p + +def is_local(ref): + return ref and not ref.startswith(EXTERNAL) and not any(c in ref for c in "${}`+") and not ref.startswith("{") + +errors = [] +count = {"json": 0, "links": 0} +for f in files(".json"): + count["json"] += 1 + try: + json.loads(f.read_text(encoding="utf-8")) + except Exception as e: # noqa: BLE001 + errors.append(f"{f.relative_to(ROOT)}: invalid JSON ({e})") + +for f in files(".html", ".htm", ".md"): + text = f.read_text(encoding="utf-8", errors="replace") + if f.suffix == ".md": + text = re.sub(r"```.*?```", "", text, flags=re.S) # code blocks are examples, not links + refs = MD_REF.findall(text) + HTML_REF.findall(text) + else: + text = re.sub(r"]*>.*?]*>", "", text, flags=re.S | re.I) # JS builds its own URLs + refs = HTML_REF.findall(text) + for ref in refs: + ref = ref.strip() + if not is_local(ref): + continue + path = unquote(urlsplit(ref).path) + if not path: + continue + target = (ROOT / path.lstrip("/")) if path.startswith("/") else (f.parent / path) + count["links"] += 1 + if not (target.exists() or (target / "index.html").exists()): + errors.append(f"{f.relative_to(ROOT)}: broken link -> {ref}") + +# JavaScript syntax: standalone .js files and inline - - - - diff --git a/sprint-2026.html b/sprint-2026.html new file mode 100644 index 0000000..a8cb7c0 --- /dev/null +++ b/sprint-2026.html @@ -0,0 +1,736 @@ + + + + + +SY0-701 · Original 42-Day Sprint (Jun–Jul 2026, archived) + + + + + + +
+ + + + + + + +