Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/BRANCH_PROTECTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Branch Protection Baseline

Use this as the admin checklist for `main` after the EverOS 1.0 history reset.

## Required Repository Rule

- Require pull requests before merging.
- Require two approving reviews for normal work.
- Require conversation resolution before merge.
- Block force pushes.
- Block branch deletion.
- Do not grant routine admin bypasses.

## Required Status Checks

Mark these checks as required before merge:

- `CI / lint`
- `CI / unit tests`
- `CI / integration tests`
- `CI / package build`
- `Docs / links`
- `Commit lint / commit messages`

## Optional Repository Checks

Do not require checks that are not emitted for every pull request. Treat these
as advisory unless GitHub shows they run on all normal PRs:

- `.github/dependabot.yml`

## Merge Policy

- Work on feature branches.
- Push branches normally; do not force-push shared branches.
- Merge through PRs after checks are green.
- Delete merged branches.

Temporary admin bypass should be reserved for repository recovery work only.
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
## Checklist

- [ ] I kept the change scoped to the relevant area.
- [ ] I am opening this from a separate branch, not pushing directly to `main`.
- [ ] I updated docs, examples, or setup notes when behavior changed.
- [ ] I added or updated tests when the change affects behavior.
- [ ] I did not commit secrets, `.env` files, dependency folders, or generated output.
Expand Down
64 changes: 59 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
push:
branches: [main, dev, master]
branches: [main]
pull_request:

# Cancel superseded runs on the same ref to save CI minutes.
Expand All @@ -14,14 +14,14 @@ permissions:
contents: read

jobs:
ci:
name: lint + test + integration
lint:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v4
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
cache-dependency-glob: uv.lock
Expand All @@ -35,8 +35,62 @@ jobs:
- name: Lint (ruff + import-linter + datetime + openapi drift)
run: make lint

unit:
name: unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Set up Python
run: uv python install 3.12

- name: Install dependencies (frozen)
run: make install-deps

- name: Unit tests
run: make test

integration:
name: integration tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Set up Python
run: uv python install 3.12

- name: Install dependencies (frozen)
run: make install-deps

- name: Integration tests
run: make integration

package:
name: package build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Set up Python
run: uv python install 3.12

- name: Build and smoke-test package
run: make package
28 changes: 28 additions & 0 deletions .github/workflows/commits.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Commit lint

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: commit-lint-${{ github.ref }}
cancel-in-progress: true

jobs:
messages:
name: commit messages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Validate Conventional Commit subjects
env:
GITHUB_EVENT_BEFORE: ${{ github.event.before }}
GITHUB_PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: make check-commits
111 changes: 4 additions & 107 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ on:
- ".github/ISSUE_TEMPLATE/**"
- ".github/PULL_REQUEST_TEMPLATE.md"
- ".github/workflows/docs.yml"
- "scripts/check_docs.py"
push:
branches: [main]
paths:
- "**/*.md"
- ".github/ISSUE_TEMPLATE/**"
- ".github/PULL_REQUEST_TEMPLATE.md"
- ".github/workflows/docs.yml"
- "scripts/check_docs.py"

permissions:
contents: read
Expand All @@ -28,110 +30,5 @@ jobs:
steps:
- uses: actions/checkout@v6

- name: Validate active relative Markdown links
run: |
python3 - <<'PY'
from pathlib import Path
import re
import sys

# Validate every Markdown file in the documentation surface. Globbing
# (rather than a hand-maintained list) means new docs are covered
# automatically and phantom links cannot slip in. Skip vendored trees.
skip_dirs = {".git", "node_modules", ".venv", ".uv-cache"}
files = sorted(
p
for p in Path(".").rglob("*.md")
if not any(part in skip_dirs for part in p.parts)
)

missing = []
for path in files:
if not path.exists():
continue
text = path.read_text()
active = re.sub(r"<!--.*?-->", "", text, flags=re.S)
for raw in re.findall(r"\[[^\]]*\]\(([^)]+)\)", active):
link = raw.split("#", 1)[0]
if not link or link.startswith(("http://", "https://", "mailto:")):
continue
target = (path.parent / link).resolve()
try:
target.relative_to(Path.cwd().resolve())
except ValueError:
missing.append((path, raw, "outside repository"))
continue
if not target.exists():
missing.append((path, raw, "missing"))

if missing:
for path, raw, reason in missing:
print(f"{path}: {raw} -> {reason}")
sys.exit(1)

print("Active relative Markdown links resolve.")
PY

- name: Validate use-case banner links
run: |
python3 - <<'PY'
from pathlib import Path
import re
import sys

files = {
Path("README.md"): "## Use Cases",
Path("use-cases/README.md"): "## Use Cases",
}

failures = []
warnings = []
primary_link_pattern = re.compile(
r"^\[(?:Code|Plugin|Live Demo|Learn more)\]\(([^)]+)\)",
flags=re.M,
)

for path, heading in files.items():
text = path.read_text()
start = text.find(heading)
if start == -1:
failures.append(f"{path}: missing {heading}")
continue

table_start = text.find("<table>", start)
table_end = text.find("</table>", table_start)
if table_start == -1 or table_end == -1:
failures.append(f"{path}: missing use-case table")
continue

table = text[table_start:table_end]
cells = re.findall(r"<td[^>]*>(.*?)</td>", table, flags=re.S)
for index, cell in enumerate(cells, start=1):
title_match = re.search(r"####\s+(.+)", cell)
title = title_match.group(1).strip() if title_match else f"use case {index}"
banner_match = re.search(r"\[!\[[^\]]*\]\([^)]+\)\]\(([^)]+)\)", cell)
primary_match = primary_link_pattern.search(cell)

if not banner_match and primary_match:
warnings.append(f"{path}: {title}: primary link has no linked banner")
elif banner_match and not primary_match:
failures.append(f"{path}: {title}: missing primary link")
elif banner_match and primary_match and banner_match.group(1) != primary_match.group(1):
failures.append(
f"{path}: {title}: banner link {banner_match.group(1)} "
f"does not match primary link {primary_match.group(1)}"
)

if warnings:
print("\n".join(f"warning: {warning}" for warning in warnings))

if failures:
print("\n".join(failures))
sys.exit(1)

print("Use-case banner links match primary links.")
PY

- name: Validate issue template YAML
run: |
ruby -e 'require "yaml"; Dir[".github/ISSUE_TEMPLATE/*.yml"].sort.each { |p| YAML.load_file(p); puts "YAML ok: #{p}" }'
- name: Validate Markdown docs
run: make docs-check
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ coverage.xml
*.py,cover
.hypothesis/
.pytest_cache/
.ruff_cache/
.import_linter_cache/
.uv-cache/
.package-smoke/

# Translations
*.mo
Expand Down Expand Up @@ -146,6 +150,8 @@ dmypy.json
!.claude/settings.json
.claude/settings.local.json
.claude/worktrees/
.worktrees/
worktrees/

# Runtime data (the default memory root + local databases)
.everos/
Expand Down
Loading
Loading