Skip to content

Commit a366637

Browse files
authored
Merge pull request #4 from SentienceAPI/week3.5
sync extensions
2 parents 5206520 + bc370ce commit a366637

File tree

5 files changed

+216
-8
lines changed

5 files changed

+216
-8
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: Sync Extension from sentience-chrome
2+
3+
on:
4+
repository_dispatch:
5+
types: [extension-updated]
6+
workflow_dispatch:
7+
inputs:
8+
release_tag:
9+
description: 'Release tag from sentience-chrome (e.g., v1.0.0)'
10+
required: true
11+
type: string
12+
schedule:
13+
# Check for new releases daily at 2 AM UTC
14+
- cron: '0 2 * * *'
15+
16+
jobs:
17+
sync-extension:
18+
runs-on: ubuntu-latest
19+
permissions:
20+
contents: write
21+
pull-requests: write
22+
23+
steps:
24+
- name: Checkout sdk-python
25+
uses: actions/checkout@v4
26+
with:
27+
token: ${{ secrets.GITHUB_TOKEN }}
28+
29+
- name: Set up Python
30+
uses: actions/setup-python@v5
31+
with:
32+
python-version: '3.11'
33+
34+
- name: Determine release tag
35+
id: release
36+
run: |
37+
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
38+
TAG="${{ github.event.inputs.release_tag }}"
39+
elif [ "${{ github.event_name }}" == "repository_dispatch" ]; then
40+
TAG="${{ github.event.client_payload.release_tag }}"
41+
else
42+
# Scheduled check - get latest release
43+
TAG=$(curl -s https://api.github.com/repos/${{ secrets.SENTIENCE_CHROME_REPO }}/releases/latest | jq -r '.tag_name // empty')
44+
fi
45+
46+
if [ -z "$TAG" ] || [ "$TAG" == "null" ]; then
47+
echo "No release found, skipping"
48+
echo "skip=true" >> $GITHUB_OUTPUT
49+
exit 0
50+
fi
51+
52+
echo "tag=$TAG" >> $GITHUB_OUTPUT
53+
echo "Release tag: $TAG"
54+
55+
- name: Download extension files
56+
if: steps.release.outputs.skip != 'true'
57+
run: |
58+
TAG="${{ steps.release.outputs.tag }}"
59+
REPO="${{ secrets.SENTIENCE_CHROME_REPO }}"
60+
61+
# Download release assets
62+
mkdir -p extension-temp
63+
cd extension-temp
64+
65+
# Download each file from release
66+
curl -L -H "Authorization: token ${{ secrets.SENTIENCE_CHROME_TOKEN }}" \
67+
"https://api.github.com/repos/$REPO/releases/tags/$TAG" | \
68+
jq -r '.assets[] | select(.name | endswith(".js") or endswith(".wasm") or endswith(".json") or endswith(".d.ts")) | .browser_download_url' | \
69+
while read url; do
70+
filename=$(basename "$url")
71+
curl -L -H "Authorization: token ${{ secrets.SENTIENCE_CHROME_TOKEN }}" "$url" -o "$filename"
72+
done
73+
74+
# Alternative: Download from release archive if available
75+
# Or use the extension-package artifact
76+
77+
- name: Copy extension files
78+
if: steps.release.outputs.skip != 'true'
79+
run: |
80+
# Create extension directory structure
81+
mkdir -p sentience/extension/pkg
82+
83+
# Copy extension files
84+
cp extension-temp/manifest.json sentience/extension/ 2>/dev/null || echo "manifest.json not found in release"
85+
cp extension-temp/content.js sentience/extension/ 2>/dev/null || echo "content.js not found in release"
86+
cp extension-temp/background.js sentience/extension/ 2>/dev/null || echo "background.js not found in release"
87+
cp extension-temp/injected_api.js sentience/extension/ 2>/dev/null || echo "injected_api.js not found in release"
88+
89+
# Copy WASM files
90+
cp extension-temp/pkg/sentience_core.js sentience/extension/pkg/ 2>/dev/null || echo "sentience_core.js not found"
91+
cp extension-temp/pkg/sentience_core_bg.wasm sentience/extension/pkg/ 2>/dev/null || echo "sentience_core_bg.wasm not found"
92+
cp extension-temp/pkg/*.d.ts sentience/extension/pkg/ 2>/dev/null || echo "Type definitions not found"
93+
94+
- name: Check for changes
95+
if: steps.release.outputs.skip != 'true'
96+
id: changes
97+
run: |
98+
git config --local user.email "action@github.com"
99+
git config --local user.name "GitHub Action"
100+
git add sentience/extension/ || true
101+
if git diff --staged --quiet; then
102+
echo "changed=false" >> $GITHUB_OUTPUT
103+
echo "No changes detected"
104+
else
105+
echo "changed=true" >> $GITHUB_OUTPUT
106+
echo "Changes detected"
107+
fi
108+
109+
- name: Create Pull Request
110+
if: steps.release.outputs.skip != 'true' && steps.changes.outputs.changed == 'true'
111+
uses: peter-evans/create-pull-request@v5
112+
with:
113+
token: ${{ secrets.GITHUB_TOKEN }}
114+
commit-message: "chore: sync extension files from sentience-chrome ${{ steps.release.outputs.tag }}"
115+
title: "Sync Extension: ${{ steps.release.outputs.tag }}"
116+
body: |
117+
This PR syncs extension files from sentience-chrome release ${{ steps.release.outputs.tag }}.
118+
119+
**Files updated:**
120+
- Extension manifest and scripts
121+
- WASM binary and bindings
122+
123+
**Source:** [sentience-chrome release ${{ steps.release.outputs.tag }}](${{ secrets.SENTIENCE_CHROME_REPO }}/releases/tag/${{ steps.release.outputs.tag }})
124+
branch: sync-extension-${{ steps.release.outputs.tag }}
125+
delete-branch: true
126+

sentience/browser.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,34 @@ def __init__(
5353

5454
def start(self) -> None:
5555
"""Launch browser with extension loaded"""
56-
# Get extension path (sentience-chrome directory)
56+
# Try to find extension in multiple locations:
57+
# 1. Embedded extension (sentience/extension/) - for production/CI
58+
# 2. Development mode (../sentience-chrome/) - for local development
59+
5760
# __file__ is sdk-python/sentience/browser.py, so:
5861
# parent = sdk-python/sentience/
5962
# parent.parent = sdk-python/
60-
# parent.parent.parent = Sentience/ (project root)
61-
repo_root = Path(__file__).parent.parent.parent
62-
extension_source = repo_root / "sentience-chrome"
63+
sdk_root = Path(__file__).parent.parent
64+
65+
# Check for embedded extension first (production/CI)
66+
embedded_extension = sdk_root / "sentience" / "extension"
6367

64-
if not extension_source.exists():
68+
# Check for development extension (local development)
69+
repo_root = sdk_root.parent
70+
dev_extension = repo_root / "sentience-chrome"
71+
72+
# Prefer embedded extension, fall back to dev extension
73+
if embedded_extension.exists() and (embedded_extension / "manifest.json").exists():
74+
extension_source = embedded_extension
75+
elif dev_extension.exists() and (dev_extension / "manifest.json").exists():
76+
extension_source = dev_extension
77+
else:
6578
raise FileNotFoundError(
66-
f"Extension not found at {extension_source}. "
67-
"Make sure sentience-chrome directory exists."
79+
f"Extension not found. Checked:\n"
80+
f" 1. {embedded_extension}\n"
81+
f" 2. {dev_extension}\n"
82+
"Make sure extension files are available. "
83+
"For development: cd ../sentience-chrome && ./build.sh"
6884
)
6985

7086
# Create temporary extension bundle

tests/conftest.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""
2+
Pytest configuration and fixtures for Sentience SDK tests
3+
"""
4+
import os
5+
import pytest
6+
from pathlib import Path
7+
8+
9+
def pytest_configure(config):
10+
"""Register custom markers"""
11+
config.addinivalue_line(
12+
"markers", "requires_extension: mark test as requiring the sentience-chrome extension"
13+
)
14+
15+
16+
@pytest.fixture(scope="session")
17+
def extension_available():
18+
"""Check if the sentience-chrome extension is available"""
19+
# Check if extension exists
20+
# __file__ is sdk-python/tests/conftest.py
21+
# parent = sdk-python/tests/
22+
# parent.parent = sdk-python/
23+
# parent.parent.parent = Sentience/ (project root)
24+
repo_root = Path(__file__).parent.parent.parent
25+
extension_source = repo_root / "sentience-chrome"
26+
27+
# Also check for required extension files
28+
if extension_source.exists():
29+
required_files = ["manifest.json", "content.js", "injected_api.js"]
30+
pkg_dir = extension_source / "pkg"
31+
if pkg_dir.exists():
32+
# Check if WASM files exist
33+
wasm_files = ["sentience_core.js", "sentience_core_bg.wasm"]
34+
all_exist = all(
35+
(extension_source / f).exists() for f in required_files
36+
) and all(
37+
(pkg_dir / f).exists() for f in wasm_files
38+
)
39+
return all_exist
40+
41+
return False
42+
43+
44+
@pytest.fixture(autouse=True)
45+
def skip_if_no_extension(request, extension_available):
46+
"""Automatically skip tests that require extension if it's not available"""
47+
# Check if test is marked as requiring extension
48+
marker = request.node.get_closest_marker("requires_extension")
49+
50+
if marker and not extension_available:
51+
# In CI, skip silently
52+
# Otherwise, show a helpful message
53+
if os.getenv("CI"):
54+
pytest.skip("Extension not available in CI environment")
55+
else:
56+
pytest.skip(
57+
"Extension not found. Build it first: cd ../sentience-chrome && ./build.sh"
58+
)
59+

tests/test_snapshot.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from sentience.models import Snapshot
88

99

10+
@pytest.mark.requires_extension
1011
def test_snapshot_basic():
1112
"""Test basic snapshot on example.com"""
1213
with SentienceBrowser(headless=False) as browser:
@@ -23,6 +24,7 @@ def test_snapshot_basic():
2324
for el in snap.elements)
2425

2526

27+
@pytest.mark.requires_extension
2628
def test_snapshot_roundtrip():
2729
"""Test snapshot round-trip on multiple sites"""
2830
# Use sites that reliably have elements
@@ -57,6 +59,7 @@ def test_snapshot_roundtrip():
5759
# (min size 5x5, visibility, etc.) - this is acceptable
5860

5961

62+
@pytest.mark.requires_extension
6063
def test_snapshot_save():
6164
"""Test snapshot save functionality"""
6265
import tempfile

tests/test_spec_validation.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111

1212
def load_schema():
1313
"""Load JSON schema from spec directory"""
14-
repo_root = Path(__file__).parent.parent.parent
14+
# __file__ is sdk-python/tests/test_spec_validation.py
15+
# parent = sdk-python/tests/
16+
# parent.parent = sdk-python/
17+
repo_root = Path(__file__).parent.parent
1518
schema_path = repo_root / "spec" / "snapshot.schema.json"
1619

1720
with open(schema_path) as f:
@@ -57,6 +60,7 @@ def validate_against_schema(data: dict, schema: dict) -> list:
5760
return errors
5861

5962

63+
@pytest.mark.requires_extension
6064
def test_snapshot_matches_spec():
6165
"""Test that snapshot response matches spec schema"""
6266
schema = load_schema()

0 commit comments

Comments
 (0)