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
112 changes: 71 additions & 41 deletions .github/scripts/verify_template_output.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
"""
Verify that template initialization created the expected files.
Validate templates.json and verify that all referenced template files exist.

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

import json
Expand All @@ -11,60 +11,90 @@


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")
# Validate templates.json is valid JSON
try:
with open(templates_json) as f:
templates = json.load(f)
print(f"✓ templates.json is valid JSON")
except json.JSONDecodeError as e:
print(f"✗ templates.json is invalid JSON: {e}")
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")
except FileNotFoundError:
print(f"✗ templates.json not found")
sys.exit(1)

print(f"✓ Template directory created: {template_dir}")
errors = []

# 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)
# Validate each template
for template_name, config in templates.items():
print(f"\nValidating template: {template_name}")

# For complex templates, check all expected files
errors = []
for file_spec in config['files']:
dest = file_spec['dest']
expected_file = template_dir / dest
# Check required fields
if 'file' not in config:
errors.append(f"✗ {template_name}: missing 'file' field")
print(errors[-1])
continue

if 'description' not in config:
errors.append(f"✗ {template_name}: missing 'description' field")
print(errors[-1])

if expected_file.exists():
print(f"✓ File created: {dest}")
# Check main file exists
main_file = repo_root / config['file']
if main_file.exists():
print(f" ✓ Main file exists: {config['file']}")

# Try to compile Python files
if config['file'].endswith('.py'):
try:
import py_compile
py_compile.compile(main_file, doraise=True)
print(f" ✓ Python file compiles: {config['file']}")
except py_compile.PyCompileError as e:
errors.append(f"✗ {template_name}: Python compilation error in {config['file']}: {e}")
print(errors[-1])
else:
errors.append(f"✗ Missing file: {dest}")
errors.append(f"✗ {template_name}: main file not found: {config['file']}")
print(errors[-1])

# Check all files in complex templates
if 'files' in config:
for file_spec in config['files']:
source = file_spec.get('source')
if not source:
errors.append(f"✗ {template_name}: file spec missing 'source' field")
print(errors[-1])
continue

source_file = repo_root / source
if source_file.exists():
print(f" ✓ File exists: {source}")

# Try to compile Python files
if source.endswith('.py'):
try:
import py_compile
py_compile.compile(source_file, doraise=True)
print(f" ✓ Python file compiles: {source}")
except py_compile.PyCompileError as e:
errors.append(f"✗ {template_name}: Python compilation error in {source}: {e}")
print(errors[-1])
else:
errors.append(f"✗ {template_name}: source file not found: {source}")
print(errors[-1])

# Print summary
print("\n" + "="*50)
if errors:
print(f"\n✗ {len(errors)} file(s) missing")
print(f"✗ Validation failed with {len(errors)} error(s)")
for error in errors:
print(f" {error}")
sys.exit(1)
else:
print(f"\n✓ All {len(config['files'])} expected files created")
print(f"✓ All {len(templates)} template(s) validated successfully")
sys.exit(0)


Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ jobs:
*/main.py \
*/email_tools.py \
*/launch_chrome_debug.py \
*/app/*.py \
--exclude test-env
*/app/*.py

lint-format:
name: code-format
Expand All @@ -41,5 +40,4 @@ jobs:
*/main.py \
*/email_tools.py \
*/launch_chrome_debug.py \
*/app/*.py \
--exclude test-env
*/app/*.py
55 changes: 7 additions & 48 deletions .github/workflows/test-templates.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: test-templates

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
Expand All @@ -12,56 +15,12 @@ on:
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
validate-templates:
name: validate-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
- name: Validate templates.json
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)"
python3 .github/scripts/verify_template_output.py
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@
# Test environment - committed once, then ignored
test-env/
41 changes: 14 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,22 @@ uvx browser-use init --template agentmail

## Testing Templates Locally

Before submitting a PR, you can test your templates locally using the included test environment:
To test your templates before submitting a PR, you can modify the browser-use CLI to use your fork/branch:

```bash
# First time setup
cd test-env
uv sync

# Test a simple template
uv run test_templates.py --template default --output my_test.py

# Test a complex template
uv run test_templates.py --template shopping --output my_bot
```
1. Fork this repository and create a branch with your changes
2. Clone the browser-use repository and locate `browser_use/init_cmd.py`
3. Find the `TEMPLATE_REPO_URL` variable (line 27, typically set to `https://raw.githubusercontent.com/browser-use/template-library/main`)
4. Replace it with your fork and branch: `https://raw.githubusercontent.com/YOUR_USERNAME/template-library/YOUR_BRANCH`
5. From the browser-use directory, test your template:
```bash
# Interactive mode (select from list)
python -m browser_use.init_cmd

The test script monkey-patches browser-use CLI to use your local `templates.json` and template files instead of fetching from GitHub, allowing you to verify:
# Direct template selection
python -m browser_use.init_cmd --template your-template --output test.py
```

This allows you to verify:
- ✓ Template files are copied correctly
- ✓ `next_steps` display properly
- ✓ File permissions are set (executable files)
Expand All @@ -79,12 +79,6 @@ The test script monkey-patches browser-use CLI to use your local `templates.json
}
```

3. Test it locally:
```bash
cd test-env
uv run test_templates.py --template my-template --output test.py
```

### Complex Template (Multiple Files)

1. Create a new directory with your template files:
Expand Down Expand Up @@ -148,12 +142,6 @@ The test script monkey-patches browser-use CLI to use your local `templates.json
}
```

3. Test it locally:
```bash
cd test-env
uv run test_templates.py --template my-template --output my_bot
```

## Template Structure Reference

### templates.json Schema
Expand Down Expand Up @@ -325,8 +313,7 @@ The optional `featured` boolean flag marks templates for prominent display in th
1. Fork this repository
2. Create a new branch for your template
3. Add your template files and update `templates.json`
4. Test locally using `test-env/test_templates.py`
5. Submit a PR with:
4. Submit a PR with:
- Clear description of what the template does
- Use case or problem it solves
- Any special requirements or dependencies
Expand Down
13 changes: 13 additions & 0 deletions sandbox/.env.example.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Browser-Use API Key
# Get your key at: https://cloud.browser-use.com/dashboard/settings?tab=api-keys&new
BROWSER_USE_API_KEY=your-key-here

# Cloud Profile ID (optional - for persistent authentication)
# Get your profile ID from: https://cloud.browser-use.com/#settings/profiles
CLOUD_PROFILE_ID=your-profile-id-here

# Cloud Proxy Country Code (optional - default: us)
CLOUD_PROXY_COUNTRY_CODE=us

# Cloud Session Timeout in minutes (optional - default: 60)
CLOUD_TIMEOUT=60
Loading
Loading