Skip to content

Latest commit

 

History

History
156 lines (110 loc) · 4.46 KB

File metadata and controls

156 lines (110 loc) · 4.46 KB

Local Development

How to run the full OVOS Localize pipeline locally without deploying to GitHub.

Prerequisites

uv pip install -e ".[dev]"

Zero external dependencies — everything uses Python stdlib.

1. Generate Data

python scripts/generate_data.py

This clones every repo in skills.txt into repos/ (shallow clone), scans locale directories, runs AST analysis and validation, then writes JSON to data/.

First run takes several minutes (cloning ~60 repos). Subsequent runs are faster (git pull only).

Subset testing

To test with fewer skills, create a temporary skills.txt:

# Back up the full list
cp skills.txt skills.txt.bak

# Test with just 2 skills
cat > skills.txt << 'EOF'
OpenVoiceOS/ovos-skill-hello-world
OpenVoiceOS/ovos-skill-weather
EOF

python scripts/generate_data.py

# Restore
mv skills.txt.bak skills.txt

Output structure

After running, data/ contains:

data/
├── repos.json              # Skill index (all skills)
├── coverage.json           # Language × skill coverage matrix
├── validation.json         # Aggregated validation results
└── skills/
    ├── ovos-skill-hello-world.json
    ├── ovos-skill-weather.json
    └── ...

2. Serve the SPA

python -m http.server 8000

Open http://localhost:8000 in a browser.

Views

URL What you see
http://localhost:8000/#/ Dashboard — coverage heatmap, 53 skills × 40 languages
http://localhost:8000/#/skill/ovos-skill-weather Skill detail — file list with per-language validation badges
http://localhost:8000/#/skill/ovos-skill-weather/weather.intent/de-de Translation editor — source panel, editable textarea, context card

Submitting translations

The editor view has a "Submit as PR" button. The workflow:

  1. Click Sign in in the header
  2. Paste a GitHub Personal Access Token with public_repo scope
  3. Navigate to a file and language (e.g. #/skill/ovos-skill-weather/weather.intent/de-de)
  4. Edit the translation in the center textarea
  5. Click Submit as PR

What happens behind the scenes:

  • Forks the skill repo to your GitHub account (idempotent)
  • Creates a translate/{lang}/{file} branch on your fork
  • Commits the edited file
  • Opens a PR from your fork to the upstream dev branch
  • Opens the PR in a new tab

The token is stored in localStorage only — never sent anywhere except the GitHub API.

Troubleshooting

Dashboard shows nothing / fetch errors in console:

  • Verify data/repos.json exists and is valid JSON: python -m json.tool data/repos.json > /dev/null
  • The server must run from the repo root (where index.html and data/ both live)

"Submit as PR" fails with 404:

  • Your token may lack public_repo scope — regenerate it
  • The fork may not be ready yet — wait a few seconds and retry

"Edit on GitHub" links point to wrong branch:

  • generate_data.py defaults to dev branch. The RepoScanner.clone_or_pull() also defaults to dev.

3. Validate a Single Skill

No data generation needed — the CLI works standalone:

# Text output
ovos-localize-cli --repo /path/to/ovos-skill-weather

# GitHub Actions annotation format (for CI)
ovos-localize-cli --repo /path/to/ovos-skill-weather --report-format github

# JSON output
ovos-localize-cli --repo /path/to/ovos-skill-weather --report-format json

Exit code: 0 = no errors, 1 = validation errors found.

4. Run Tests

uv run pytest test/ -v

With coverage:

uv run pytest test/ -v --cov=ovos_localize --cov-report=term-missing

5. Scan a Single Repo Programmatically

from ovos_localize.sync.github import RepoScanner
from ovos_localize.analyzers.context_builder import build_context_card

scanner = RepoScanner("./repos")
scan = scanner.full_sync("OpenVoiceOS", "ovos-skill-weather")

print(f"Skill: {scan.skill_class_name}")
print(f"Languages: {scan.languages}")
print(f"Files: {len(scan.locale_files)}")

for f in scan.locale_files:
    if f.lang == "en-us":
        card = build_context_card(f, scan.skill_analysis, scan.locale_files)
        print(f"\n{f.base_name}.{f.file_type.value}: {card.file_type_label}")
        if card.handler_method:
            print(f"  Handler: {card.handler_method}() at :{card.handler_line}")
        if card.tips:
            print(f"  Tips: {card.tips[0]}")