Skip to content

Commit d437bea

Browse files
committed
Simplify prepare-envs by reducing prepare_runner_envs and prepare_handler_envs to single install_envs action. Improve docs how does it work
1 parent 9d1989d commit d437bea

File tree

18 files changed

+179
-663
lines changed

18 files changed

+179
-663
lines changed

docs/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ See [Preparing Environments](guides/preparing-environments.md) for a full explan
133133
| `--dev-env=<env>` | Override the detected dev environment. One of: `ai`, `ci`, `cli`, `ide`, `precommit` (default: auto-detected) |
134134

135135

136-
!!! note `--env-names` restricts only the final `prepare_handler_envs` step. The `create_envs` and `prepare_runner_envs` steps still run for **all** envs regardless of this flag — envs and runners must exist for every env even when you only need to update dependencies in one of them.
136+
!!! note `--env-names` restricts only the `install_envs` step. The `create_envs` step still runs for **all** envs regardless of this flag — virtualenvs must exist for every env even when you only need to update dependencies in one of them.
137137

138138
---
139139

docs/guides/preparing-environments.md

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
FineCode runs handlers in purpose-specific virtual environments. Handlers that share the same `env` name (e.g. `dev_no_runtime`) run in the same virtualenv. Before handlers can execute, their environments must exist and contain the right dependencies. This guide explains how that process works and how to control it.
44

5-
## The three-step sequence
5+
## The two-step sequence
66

7-
Environment preparation is split into three distinct actions that must run in order:
7+
Environment preparation is split into two distinct actions that must run in order:
88

99
```
10-
create_envs → prepare_runner_envs → prepare_handler_envs
10+
create_envs → install_envs
1111
```
1212

1313
### Step 1 — `create_envs`
@@ -25,33 +25,40 @@ runtime = ["fastapi>=0.100", ...]
2525

2626
→ Creates `.venvs/dev_workspace/`, `.venvs/dev_no_runtime/`, `.venvs/runtime/`.
2727

28-
### Step 2 — `prepare_runner_envs`
28+
### Step 2 — `install_envs`
2929

30-
Installs the **Extension Runner** (`finecode_extension_runner`) into each virtualenv. This is what lets FineCode start runners that can actually load handler code.
30+
Installs the full dependency set into each virtualenv. This reads the `dependency-groups` entries and calls `install_deps_in_env` for each env, including `finecode_extension_runner` and all handler tool dependencies (e.g. ruff, mypy).
3131

32-
Preset packages are only installed in `dev_workspace` (handled during the bootstrap phase — see below). Only the runner is installed into other envs here — not the full handler dependency trees.
32+
After this step every handler has all its dependencies available and can execute.
3333

34-
!!! note
35-
`prepare_runner_envs` must run **after** `create_envs` and **before** runners are started. Runners are started automatically between steps 2 and 3 by the WM during `prepare-envs`.
34+
---
3635

37-
### Step 3 — `prepare_handler_envs`
36+
## The `dev_workspace` bootstrap
3837

39-
Installs the full dependency set for each handler into its declared `env` virtualenv. This reads the `dependency-groups` entries and calls `install_deps_in_env` for each env.
38+
The `dev_workspace` env is special: it contains FineCode itself and the preset packages. The handlers that implement `create_envs` and `install_envs` live inside `dev_workspace` — which creates a bootstrapping constraint.
4039

41-
After this step every handler has all its dependencies available and can execute.
40+
### Workspace root bootstrap (manual, one-time)
4241

43-
---
42+
The workspace root's `dev_workspace` is the **seed** for everything. `prepare-envs` cannot run unless FineCode is already installed somewhere, so the workspace root's `dev_workspace` must be created manually on a fresh checkout:
43+
44+
```bash
45+
python -m venv .venvs/dev_workspace
46+
source .venvs/dev_workspace/bin/activate # Windows: .venvs\dev_workspace\Scripts\activate
47+
python -m pip install --group="dev_workspace"
48+
```
49+
50+
This is the only step that cannot be automated by FineCode itself. See [Getting Started](../getting-started.md) for the full first-time setup sequence.
4451

45-
## The `dev_workspace` bootstrap env
52+
### Subproject bootstrap (automated by `prepare-envs`)
4653

47-
The `dev_workspace` env is special: it contains FineCode itself and the preset packages. This means the handlers that implement `prepare_runner_envs` and `prepare_handler_envs` *live inside* `dev_workspace`.
54+
For subprojects in the workspace, `prepare-envs` creates their `dev_workspace` envs automatically — **before** starting any runners — using the workspace root's handler configuration:
4855

49-
Because of this, `prepare-envs` handles `dev_workspace` separately, **before** starting runners:
56+
1. `create_envs` (subproject `dev_workspace` envs) — create the venvs
57+
2. `install_envs` (subproject `dev_workspace` envs) — install FineCode + presets
5058

51-
1. `create_envs` (dev_workspace only) — create the venv if it doesn't exist
52-
2. `prepare_handler_envs` (dev_workspace only) — install FineCode + presets
59+
**Requirement:** the workspace root's `create_envs` and `install_envs` configuration must produce a valid `dev_workspace` for every subproject. In practice this is rarely a constraint: `dev_workspace` envs exist only to run FineCode and preset packages, so their setup is uniform across projects. If a subproject genuinely requires different handler configuration for either action, its `dev_workspace` must be bootstrapped manually the same way as the workspace root's.
5360

54-
Only after this bootstrap are runners started, and only then can the remaining steps run across all envs.
61+
Only after all `dev_workspace` envs exist are runners started, and only then can the remaining steps run across all envs.
5562

5663
---
5764

@@ -66,11 +73,10 @@ python -m finecode prepare-envs
6673
This is the only command most users need. It:
6774

6875
1. Discovers all projects in the workspace
69-
2. Bootstraps `dev_workspace` (steps 1–2 above) for each project
76+
2. Bootstraps `dev_workspace` for each subproject (`create_envs` + `install_envs`, using workspace root config)
7077
3. Starts Extension Runners
7178
4. Runs `create_envs` across all projects
72-
5. Runs `prepare_runner_envs` across all projects
73-
6. Runs `prepare_handler_envs` across all projects
79+
5. Runs `install_envs` across all projects
7480

7581
See [CLI reference — prepare-envs](../cli.md#prepare-envs) for available options.
7682

@@ -96,22 +102,21 @@ Only prepares environments for the listed projects. Useful in a large workspaces
96102
python -m finecode prepare-envs --env-names=dev_no_runtime
97103
```
98104

99-
Restricts the `prepare_handler_envs` step (step 3) to the named environments. The `create_envs` and `prepare_runner_envs` steps still run for **all** envs regardless of this flag.
105+
Restricts the `install_envs` step (step 2) to the named environments. The `create_envs` step still runs for **all** envs regardless of this flag.
100106

101-
**Why?** Virtualenvs and runners must exist for every env — they are cheap to create and skip if already valid. Filtering at those steps would leave envs in a broken state if they don't exist yet.
107+
**Why?** Virtualenvs must exist for every env — they are cheap to create and skip if already valid. Filtering at that step would leave envs in a broken state if they don't exist yet.
102108

103109
Useful when you've added a new handler in one env and want to update only that env without reinstalling everything.
104110

105111
---
106112

107113
## Calling actions directly
108114

109-
The three actions (`create_envs`, `prepare_runner_envs`, `prepare_handler_envs`) are standard FineCode actions and can be invoked individually via the WM API or `python -m finecode run`. This is useful when writing custom orchestration.
115+
The two actions (`create_envs`, `install_envs`) are standard FineCode actions and can be invoked individually via the WM API or `python -m finecode run`. This is useful when writing custom orchestration.
110116

111117
| Action | Source |
112118
|---|---|
113119
| `create_envs` | `finecode_extension_api.actions.create_envs.CreateEnvsAction` |
114-
| `prepare_runner_envs` | `finecode_extension_api.actions.prepare_runner_envs.PrepareRunnerEnvsAction` |
115-
| `prepare_handler_envs` | `finecode_extension_api.actions.prepare_handler_envs.PrepareHandlerEnvsAction` |
120+
| `install_envs` | `finecode_extension_api.actions.install_envs.InstallEnvsAction` |
116121

117122
See [Built-in Actions reference](../reference/actions.md) for payload fields and result types.

finecode_builtin_handlers/src/finecode_builtin_handlers/prepare_handler_env_install_deps.py renamed to finecode_builtin_handlers/src/finecode_builtin_handlers/install_env_install_deps.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@
33
from finecode_extension_api import code_action
44
from finecode_extension_api.actions import (
55
install_deps_in_env as install_deps_in_env_action,
6-
prepare_handler_env as prepare_handler_env_action,
6+
install_env as install_env_action,
77
)
8-
from finecode_extension_api.actions.prepare_handler_envs import (
9-
PrepareHandlerEnvsRunResult,
8+
from finecode_extension_api.actions.install_envs import (
9+
InstallEnvsRunResult,
1010
)
1111
from finecode_extension_api.interfaces import iactionrunner, ilogger
1212
from finecode_builtin_handlers.dependency_config_utils import process_raw_deps
1313

1414

1515
@dataclasses.dataclass
16-
class PrepareHandlerEnvInstallDepsHandlerConfig(code_action.ActionHandlerConfig): ...
16+
class InstallEnvInstallDepsHandlerConfig(code_action.ActionHandlerConfig): ...
1717

1818

19-
class PrepareHandlerEnvInstallDepsHandler(
19+
class InstallEnvInstallDepsHandler(
2020
code_action.ActionHandler[
21-
prepare_handler_env_action.PrepareHandlerEnvAction,
22-
PrepareHandlerEnvInstallDepsHandlerConfig,
21+
install_env_action.InstallEnvAction,
22+
InstallEnvInstallDepsHandlerConfig,
2323
]
2424
):
2525
def __init__(
@@ -30,14 +30,14 @@ def __init__(
3030

3131
async def run(
3232
self,
33-
payload: prepare_handler_env_action.PrepareHandlerEnvRunPayload,
34-
run_context: prepare_handler_env_action.PrepareHandlerEnvRunContext,
35-
) -> PrepareHandlerEnvsRunResult:
33+
payload: install_env_action.InstallEnvRunPayload,
34+
run_context: install_env_action.InstallEnvRunContext,
35+
) -> InstallEnvsRunResult:
3636
env = payload.env
3737
project_def = run_context.project_def
3838
if project_def is None:
3939
raise code_action.ActionFailedException(
40-
"project_def must be set by PrepareHandlerEnvReadConfigHandler"
40+
"project_def must be set by InstallEnvReadConfigHandler"
4141
)
4242

4343
install_deps_in_env_action_instance = self.action_runner.get_action_by_name(
@@ -82,4 +82,4 @@ async def run(
8282
payload=install_deps_payload,
8383
meta=run_context.meta,
8484
)
85-
return PrepareHandlerEnvsRunResult(errors=result.errors)
85+
return InstallEnvsRunResult(errors=result.errors)

finecode_builtin_handlers/src/finecode_builtin_handlers/prepare_handler_env_read_config.py renamed to finecode_builtin_handlers/src/finecode_builtin_handlers/install_env_read_config.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
from finecode_extension_api import code_action
44
from finecode_extension_api.actions import (
5-
prepare_handler_env as prepare_handler_env_action,
5+
install_env as install_env_action,
66
)
7-
from finecode_extension_api.actions.prepare_handler_envs import (
8-
PrepareHandlerEnvsRunResult,
7+
from finecode_extension_api.actions.install_envs import (
8+
InstallEnvsRunResult,
99
)
1010
from finecode_extension_api.interfaces import ilogger, iprojectinfoprovider
1111
from finecode_builtin_handlers import dependency_config_utils
1212

1313

1414
@dataclasses.dataclass
15-
class PrepareHandlerEnvReadConfigHandlerConfig(code_action.ActionHandlerConfig): ...
15+
class InstallEnvReadConfigHandlerConfig(code_action.ActionHandlerConfig): ...
1616

1717

18-
class PrepareHandlerEnvReadConfigHandler(
18+
class InstallEnvReadConfigHandler(
1919
code_action.ActionHandler[
20-
prepare_handler_env_action.PrepareHandlerEnvAction,
21-
PrepareHandlerEnvReadConfigHandlerConfig,
20+
install_env_action.InstallEnvAction,
21+
InstallEnvReadConfigHandlerConfig,
2222
]
2323
):
2424
def __init__(
@@ -31,14 +31,14 @@ def __init__(
3131

3232
async def run(
3333
self,
34-
payload: prepare_handler_env_action.PrepareHandlerEnvRunPayload,
35-
run_context: prepare_handler_env_action.PrepareHandlerEnvRunContext,
36-
) -> PrepareHandlerEnvsRunResult:
34+
payload: install_env_action.InstallEnvRunPayload,
35+
run_context: install_env_action.InstallEnvRunContext,
36+
) -> InstallEnvsRunResult:
3737
project_raw_config = await self.project_info_provider.get_project_raw_config(
3838
payload.env.project_def_path
3939
)
4040
dependency_config_utils.make_project_config_pip_compatible(
4141
project_raw_config, payload.env.project_def_path
4242
)
4343
run_context.project_def = project_raw_config
44-
return PrepareHandlerEnvsRunResult(errors=[])
44+
return InstallEnvsRunResult(errors=[])

finecode_builtin_handlers/src/finecode_builtin_handlers/prepare_handler_envs_discover_envs.py renamed to finecode_builtin_handlers/src/finecode_builtin_handlers/install_envs_discover_envs.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from finecode_extension_api import code_action
44
from finecode_extension_api.actions import (
5-
prepare_handler_envs as prepare_handler_envs_action,
5+
install_envs as install_envs_action,
66
)
77
from finecode_extension_api.actions.create_envs import EnvInfo
88
from finecode_extension_api.interfaces import (
@@ -13,13 +13,13 @@
1313

1414

1515
@dataclasses.dataclass
16-
class PrepareHandlerEnvsDiscoverEnvsHandlerConfig(code_action.ActionHandlerConfig): ...
16+
class InstallEnvsDiscoverEnvsHandlerConfig(code_action.ActionHandlerConfig): ...
1717

1818

19-
class PrepareHandlerEnvsDiscoverEnvsHandler(
19+
class InstallEnvsDiscoverEnvsHandler(
2020
code_action.ActionHandler[
21-
prepare_handler_envs_action.PrepareHandlerEnvsAction,
22-
PrepareHandlerEnvsDiscoverEnvsHandlerConfig,
21+
install_envs_action.InstallEnvsAction,
22+
InstallEnvsDiscoverEnvsHandlerConfig,
2323
]
2424
):
2525
"""Discover and populate run_context.envs from the current project's config.
@@ -42,9 +42,9 @@ def __init__(
4242

4343
async def run(
4444
self,
45-
payload: prepare_handler_envs_action.PrepareHandlerEnvsRunPayload,
46-
run_context: prepare_handler_envs_action.PrepareHandlerEnvsRunContext,
47-
) -> prepare_handler_envs_action.PrepareHandlerEnvsRunResult:
45+
payload: install_envs_action.InstallEnvsRunPayload,
46+
run_context: install_envs_action.InstallEnvsRunContext,
47+
) -> install_envs_action.InstallEnvsRunResult:
4848
if payload.envs:
4949
envs = list(payload.envs)
5050
else:
@@ -64,10 +64,10 @@ async def run(
6464
)
6565
for env_name in deps_groups
6666
]
67-
67+
6868
if payload.env_names is not None:
6969
envs = [e for e in envs if e.name in payload.env_names]
7070

7171
self.logger.debug(f"Discovered handler envs: {[e.name for e in envs]}")
7272
run_context.envs = envs
73-
return prepare_handler_envs_action.PrepareHandlerEnvsRunResult(errors=[])
73+
return install_envs_action.InstallEnvsRunResult(errors=[])

finecode_builtin_handlers/src/finecode_builtin_handlers/prepare_handler_envs_dispatch.py renamed to finecode_builtin_handlers/src/finecode_builtin_handlers/install_envs_dispatch.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@
33

44
from finecode_extension_api import code_action
55
from finecode_extension_api.actions import (
6-
prepare_handler_env as prepare_handler_env_action,
7-
prepare_handler_envs as prepare_handler_envs_action,
6+
install_env as install_env_action,
7+
install_envs as install_envs_action,
88
)
99
from finecode_extension_api.interfaces import iactionrunner, ilogger
1010

1111

1212
@dataclasses.dataclass
13-
class PrepareHandlerEnvsDispatchHandlerConfig(code_action.ActionHandlerConfig): ...
13+
class InstallEnvsDispatchHandlerConfig(code_action.ActionHandlerConfig): ...
1414

1515

16-
class PrepareHandlerEnvsDispatchHandler(
16+
class InstallEnvsDispatchHandler(
1717
code_action.ActionHandler[
18-
prepare_handler_envs_action.PrepareHandlerEnvsAction,
19-
PrepareHandlerEnvsDispatchHandlerConfig,
18+
install_envs_action.InstallEnvsAction,
19+
InstallEnvsDispatchHandlerConfig,
2020
]
2121
):
22-
"""Dispatch a prepare_handler_env call per environment concurrently."""
22+
"""Dispatch an install_env call per environment concurrently."""
2323

2424
def __init__(
2525
self, action_runner: iactionrunner.IActionRunner, logger: ilogger.ILogger
@@ -29,28 +29,28 @@ def __init__(
2929

3030
async def run(
3131
self,
32-
payload: prepare_handler_envs_action.PrepareHandlerEnvsRunPayload,
33-
run_context: prepare_handler_envs_action.PrepareHandlerEnvsRunContext,
34-
) -> prepare_handler_envs_action.PrepareHandlerEnvsRunResult:
35-
prepare_handler_env_action_instance = self.action_runner.get_action_by_name(
36-
name="prepare_handler_env",
37-
expected_type=prepare_handler_env_action.PrepareHandlerEnvAction,
32+
payload: install_envs_action.InstallEnvsRunPayload,
33+
run_context: install_envs_action.InstallEnvsRunContext,
34+
) -> install_envs_action.InstallEnvsRunResult:
35+
install_env_action_instance = self.action_runner.get_action_by_name(
36+
name="install_env",
37+
expected_type=install_env_action.InstallEnvAction,
3838
)
3939

4040
if run_context.envs is None:
4141
raise code_action.ActionFailedException(
4242
"envs must be populated must be provided in payload or populated by previous handlers"
4343
)
4444
tasks: list[
45-
asyncio.Task[prepare_handler_envs_action.PrepareHandlerEnvsRunResult]
45+
asyncio.Task[install_envs_action.InstallEnvsRunResult]
4646
] = []
4747
try:
4848
async with asyncio.TaskGroup() as tg:
4949
for env in run_context.envs:
5050
task = tg.create_task(
5151
self.action_runner.run_action(
52-
action=prepare_handler_env_action_instance,
53-
payload=prepare_handler_env_action.PrepareHandlerEnvRunPayload(
52+
action=install_env_action_instance,
53+
payload=install_env_action.InstallEnvRunPayload(
5454
env=env,
5555
),
5656
meta=run_context.meta,
@@ -64,4 +64,4 @@ async def run(
6464
errors: list[str] = []
6565
for task in tasks:
6666
errors += task.result().errors
67-
return prepare_handler_envs_action.PrepareHandlerEnvsRunResult(errors=errors)
67+
return install_envs_action.InstallEnvsRunResult(errors=errors)

0 commit comments

Comments
 (0)