File size: 4,400 Bytes
27cdb3e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"""
Level 2 Scenarios — Two-step fixes.
"""

from __future__ import annotations
from typing import List
from scenarios.registry import Scenario


def _success_port_freed(output: str) -> bool:
    """Check if the port conflict was resolved."""
    o = output.lower()
    return "server running" in o or "running on" in o or "killed" in o or "no process" in o


def _success_env_var_set(output: str) -> bool:
    """Check if the DATABASE_URL env var is set and app runs."""
    o = output.lower()
    return "connected to database" in o or "success" in o or "postgresql://" in o


def _success_requirements_fixed(output: str) -> bool:
    """Check if requirements.txt was fixed and packages installed."""
    o = output.lower()
    return "successfully installed" in o or "requirement already satisfied" in o


def get_level2_scenarios() -> List[Scenario]:
    """Return all Level 2 (two-step fix) scenarios."""
    return [
        Scenario(
            id="port_conflict",
            level=2,
            description="Flask app fails because port 5000 is already in use.",
            initial_state={"files": {}, "env_vars": {}, "processes": []},
            success_condition=_success_port_freed,
            hint_commands=["lsof -t -i:5000 | xargs kill -9", "python /app/server.py &"],
            error_fingerprint=r"Address already in use|EADDRINUSE",
            setup_commands=[
                "pip install flask -q",
                "mkdir -p /app",
                "cat > /app/server.py << 'PYEOF'\nfrom flask import Flask\napp = Flask(__name__)\n@app.route('/')\ndef hello():\n    return 'Hello World'\nif __name__ == '__main__':\n    app.run(host='0.0.0.0', port=5000)\nPYEOF",
                "python -c \"import socket,time; s=socket.socket(); s.bind(('',5000)); s.listen(); time.sleep(3600)\" &",
            ],
            initial_error_log=(
                "$ python /app/server.py\n"
                "Traceback (most recent call last):\n"
                "  File \"/app/server.py\", line 7, in <module>\n"
                "    app.run(host='0.0.0.0', port=5000)\n"
                "OSError: [Errno 98] Address already in use\n"
            ),
            verification_command="curl -s http://localhost:5000 || echo 'port freed'",
        ),
        Scenario(
            id="missing_env_var",
            level=2,
            description="App crashes because DATABASE_URL environment variable is not set.",
            initial_state={"files": {}, "env_vars": {}, "processes": []},
            success_condition=_success_env_var_set,
            hint_commands=["export DATABASE_URL=postgresql://localhost:5432/mydb", "python /app/db_app.py"],
            error_fingerprint=r"KeyError.*DATABASE_URL",
            setup_commands=[
                "mkdir -p /app",
                "cat > /app/db_app.py << 'PYEOF'\nimport os\ndb_url = os.environ['DATABASE_URL']\nprint(f'Connected to database at: {db_url}')\nprint('Success')\nPYEOF",
            ],
            initial_error_log=(
                "$ python /app/db_app.py\n"
                "Traceback (most recent call last):\n"
                "  File \"/app/db_app.py\", line 2, in <module>\n"
                "    db_url = os.environ['DATABASE_URL']\n"
                "KeyError: 'DATABASE_URL'\n"
            ),
            verification_command="DATABASE_URL=postgresql://localhost:5432/mydb python /app/db_app.py",
        ),
        Scenario(
            id="broken_requirements",
            level=2,
            description="requirements.txt has a conflicting version pin.",
            initial_state={"files": {}, "env_vars": {}, "processes": []},
            success_condition=_success_requirements_fixed,
            hint_commands=["sed -i 's/werkzeug==1.0.0/werkzeug>=2.3.0/' /app/requirements.txt", "pip install -r /app/requirements.txt"],
            error_fingerprint=r"version.*conflict|ResolutionImpossible",
            setup_commands=[
                "mkdir -p /app",
                "cat > /app/requirements.txt << 'PYEOF'\nflask==2.3.0\nwerkzeug==1.0.0\njinja2>=3.0\nPYEOF",
            ],
            initial_error_log=(
                "$ pip install -r /app/requirements.txt\n"
                "ERROR: Cannot install flask==2.3.0 and werkzeug==1.0.0\n"
                "ERROR: ResolutionImpossible\n"
            ),
            verification_command="pip install -r /app/requirements.txt",
        ),
    ]