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
116 changes: 116 additions & 0 deletions .github/scripts/validate_templates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#!/usr/bin/env python3
"""
Validate templates.json and template file structure.

This script checks:
1. templates.json is valid JSON
2. All referenced files exist
3. Complex templates have required files
4. Schema is correct
"""

import json
import sys
from pathlib import Path


def validate_json_file(templates_json_path: Path) -> dict:
"""Validate templates.json is valid JSON and return parsed data."""
try:
with open(templates_json_path, 'r', encoding='utf-8') as f:
data = json.load(f)
print(f"✓ {templates_json_path} is valid JSON")
return data
except json.JSONDecodeError as e:
print(f"✗ {templates_json_path} is not valid JSON: {e}")
sys.exit(1)
except FileNotFoundError:
print(f"✗ {templates_json_path} not found")
sys.exit(1)


def validate_template_entry(name: str, config: dict, repo_root: Path) -> list[str]:
"""Validate a single template entry. Returns list of errors."""
errors = []

# Check required fields
if 'file' not in config:
errors.append(f"Template '{name}' missing required field 'file'")
if 'description' not in config:
errors.append(f"Template '{name}' missing required field 'description'")

if 'file' not in config:
return errors # Can't continue without file field

# Check main file exists
main_file = repo_root / config['file']
if not main_file.exists():
errors.append(f"Template '{name}': main file '{config['file']}' does not exist")

# Check files array if present
if 'files' in config:
for file_spec in config['files']:
if 'source' not in file_spec:
errors.append(f"Template '{name}': file entry missing 'source' field")
continue
if 'dest' not in file_spec:
errors.append(f"Template '{name}': file entry missing 'dest' field")
continue

source_path = repo_root / file_spec['source']
if not source_path.exists():
errors.append(f"Template '{name}': source file '{file_spec['source']}' does not exist")

# For complex templates, check required files exist
is_complex = len(config['files']) > 1
if is_complex:
template_dir = Path(config['file']).parent
required_files = ['README.md', 'pyproject.toml.template', '.env.example.template']

for required in required_files:
# Check if it's in the files array
found = any(
Path(f['source']).name == required
for f in config['files']
)
if not found:
errors.append(
f"Template '{name}': complex template missing '{required}' in files array"
)

return errors


def main():
repo_root = Path(__file__).parent.parent.parent
templates_json = repo_root / 'templates.json'

print("Validating template registry...\n")

# Validate JSON
templates = validate_json_file(templates_json)

# Validate each template entry
all_errors = []
for name, config in templates.items():
errors = validate_template_entry(name, config, repo_root)
all_errors.extend(errors)

if not errors:
print(f"✓ Template '{name}' is valid")
else:
for error in errors:
print(f"✗ {error}")

# Summary
print(f"\n{'='*60}")
if all_errors:
print(f"Validation failed with {len(all_errors)} error(s)")
sys.exit(1)
else:
print(f"✓ All {len(templates)} templates are valid!")
sys.exit(0)


if __name__ == '__main__':
main()
72 changes: 72 additions & 0 deletions .github/scripts/verify_template_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""
Verify that template initialization created the expected files.

Usage: python verify_template_output.py <template-name>
"""

import json
import sys
from pathlib import Path


def main():
if len(sys.argv) != 2:
print("Usage: python verify_template_output.py <template-name>")
sys.exit(1)

template_name = sys.argv[1]
repo_root = Path(__file__).parent.parent.parent
templates_json = repo_root / 'templates.json'

# Load templates.json
with open(templates_json) as f:
templates = json.load(f)

if template_name not in templates:
print(f"✗ Template '{template_name}' not found in templates.json")
sys.exit(1)

config = templates[template_name]
template_dir = repo_root / 'test-env' / template_name

# Check if template directory was created
if not template_dir.exists():
print(f"✗ Template directory '{template_dir}' was not created")
sys.exit(1)

print(f"✓ Template directory created: {template_dir}")

# For simple templates, just check the output file exists
if 'files' not in config:
# Simple template - should have created test_output.py
output_file = template_dir / 'test_output.py'
if output_file.exists():
print(f"✓ Output file created: {output_file}")
sys.exit(0)
else:
print(f"✗ Output file not found: {output_file}")
sys.exit(1)

# For complex templates, check all expected files
errors = []
for file_spec in config['files']:
dest = file_spec['dest']
expected_file = template_dir / dest

if expected_file.exists():
print(f"✓ File created: {dest}")
else:
errors.append(f"✗ Missing file: {dest}")
print(errors[-1])

if errors:
print(f"\n✗ {len(errors)} file(s) missing")
sys.exit(1)
else:
print(f"\n✓ All {len(config['files'])} expected files created")
sys.exit(0)


if __name__ == '__main__':
main()
45 changes: 45 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: lint

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

jobs:
lint-syntax:
name: syntax-errors
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- name: Check syntax errors in template files
run: |
uvx ruff check --no-fix --select PLE \
*_template.py \
*/main.py \
*/email_tools.py \
*/launch_chrome_debug.py \
*/app/*.py \
--exclude test-env

lint-format:
name: code-format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- name: Check code formatting in template files
run: |
uvx ruff format --check \
*_template.py \
*/main.py \
*/email_tools.py \
*/launch_chrome_debug.py \
*/app/*.py \
--exclude test-env
67 changes: 67 additions & 0 deletions .github/workflows/test-templates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: test-templates

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

jobs:
find-templates:
name: find-templates
runs-on: ubuntu-latest
outputs:
templates: ${{ steps.get-templates.outputs.templates }}
steps:
- uses: actions/checkout@v4
- name: Get template names from templates.json
id: get-templates
run: |
templates=$(python3 -c "
import json
with open('templates.json') as f:
data = json.load(f)
print(json.dumps(list(data.keys())))
")
echo "templates=$templates" >> $GITHUB_OUTPUT

test-init:
name: test-${{ matrix.template }}
needs: find-templates
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
template: ${{ fromJson(needs.find-templates.outputs.templates) }}
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5

- name: Setup test environment
working-directory: test-env
run: uv sync

- name: Test template initialization
working-directory: test-env
run: |
uv run test_templates.py --template ${{ matrix.template }} --output test_output.py

- name: Debug generated files
run: ls -la test-env/${{ matrix.template }}/ || echo "Directory not found"

- name: Verify files were created
run: |
python3 .github/scripts/verify_template_output.py ${{ matrix.template }}

- name: Compile generated Python files
run: |
# Find all .py files generated (exclude __pycache__)
find test-env/${{ matrix.template }} -name "*.py" -not -path "*/.*" 2>/dev/null | while read file; do
echo "Compiling $file"
python3 -m py_compile "$file"
done || echo "No Python files to compile (simple template)"
24 changes: 24 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: validate

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

jobs:
validate-templates:
name: validate-template-registry
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Validate templates.json and template files
run: python .github/scripts/validate_templates.py
64 changes: 32 additions & 32 deletions advanced_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,35 @@


async def main():
browser = Browser(
use_cloud=False,
# headless=False,
# disable_security=False,
# extra_chromium_args=[],
# allowed_domains=None,
# prohibited_domains=None,
# cdp_url=None,
)

llm = ChatBrowserUse()

agent = Agent(
task='Find the number of stars of the browser-use repository on GitHub',
llm=llm,
browser=browser,
# use_vision='auto',
# save_conversation_path=None,
# max_failures=3,
# generate_gif=False,
# max_actions_per_step=4,
# use_thinking=True,
# flash_mode=False,
# calculate_cost=False,
# step_timeout=180,
)

await agent.run()


if __name__ == '__main__':
asyncio.run(main())
browser = Browser(
use_cloud=False,
# headless=False,
# disable_security=False,
# extra_chromium_args=[],
# allowed_domains=None,
# prohibited_domains=None,
# cdp_url=None,
)

llm = ChatBrowserUse()

agent = Agent(
task="Find the number of stars of the browser-use repository on GitHub",
llm=llm,
browser=browser,
# use_vision='auto',
# save_conversation_path=None,
# max_failures=3,
# generate_gif=False,
# max_actions_per_step=4,
# use_thinking=True,
# flash_mode=False,
# calculate_cost=False,
# step_timeout=180,
)

await agent.run()


if __name__ == "__main__":
asyncio.run(main())
Loading
Loading