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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ The current workspace flow:

For remote environments, the stable lane model is `testing` plus `prod`.
Launchplane-managed PR previews are a separate control-plane concern rather
than a third durable runtime lane exposed through `platform runtime`. Remote release
or promotion flow is artifact-backed and belongs in `launchplane`, not
in branch-oriented `odoo-devkit` commands.
than a third durable runtime lane exposed through `platform runtime`.
`odoo-devkit` can publish artifact images for handoff, but remote ship,
promote, gate, and preview lifecycle flow belongs in `launchplane`, not in
branch-oriented `odoo-devkit` commands.

## Command surface

Expand Down
4 changes: 1 addition & 3 deletions odoo_devkit/artifact_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,7 @@ def _read_source_definitions(
exact_ref = _read_optional_string(entry, "exact_ref", scope=entry_scope)
selector = _read_optional_string(entry, "selector", scope=entry_scope)
if bool(exact_ref) == bool(selector):
raise ArtifactInputsError(
f"{entry_scope} must set exactly one of exact_ref or selector."
)
raise ArtifactInputsError(f"{entry_scope} must set exactly one of exact_ref or selector.")
definitions.append(
ArtifactInputSourceDefinition(
repository=repository,
Expand Down
15 changes: 4 additions & 11 deletions odoo_devkit/dokploy_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,10 @@ def load_dokploy_source_of_truth(repo_root: Path) -> DokploySourceOfTruth | None
for target_index, raw_target in enumerate(targets_payload, start=1)
)
if control_plane_root is not None:
missing_target_id_routes = [
f"{target.context}/{target.instance}" for target in targets if not target.target_id.strip()
]
missing_target_id_routes = [f"{target.context}/{target.instance}" for target in targets if not target.target_id.strip()]
if missing_target_id_routes:
missing_joined = ", ".join(missing_target_id_routes)
target_ids_display = (
str(target_ids_file_path) if target_ids_file_path is not None else "config/dokploy-targets.toml"
)
target_ids_display = str(target_ids_file_path) if target_ids_file_path is not None else "config/dokploy-targets.toml"
raise RuntimeCommandError(
"Control-plane Dokploy route catalog resolved through ODOO_CONTROL_PLANE_ROOT is missing pinned target ids for "
f"{missing_joined}. Define them in {target_ids_display} or inline target_id values in {source_file_path}."
Expand Down Expand Up @@ -154,12 +150,9 @@ def _apply_dokploy_target_id_catalog(
merged_targets.append(merged_target)

if remaining_routes:
unknown_routes = ", ".join(
f"{context_name}/{instance_name}" for context_name, instance_name in sorted(remaining_routes)
)
unknown_routes = ", ".join(f"{context_name}/{instance_name}" for context_name, instance_name in sorted(remaining_routes))
raise RuntimeCommandError(
"Dokploy target-id catalog contains route(s) that are not present in the control-plane route catalog: "
f"{unknown_routes}"
f"Dokploy target-id catalog contains route(s) that are not present in the control-plane route catalog: {unknown_routes}"
)

merged_payload["targets"] = merged_targets
Expand Down
8 changes: 2 additions & 6 deletions odoo_devkit/workspace_cockpit.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,7 @@ def load_workspace_cockpit_manifest(manifest_path: Path) -> WorkspaceCockpitMani
_read_string_tuple(docs_table, "external_reference_boundary") or _default_docs_external_reference_lines()
),
docs_working_split_lines=_read_string_tuple(docs_table, "working_split") or _default_docs_working_split_lines(),
docs_operational_note_lines=(
_read_string_tuple(docs_table, "operational_notes") or _default_docs_operational_note_lines()
),
docs_operational_note_lines=(_read_string_tuple(docs_table, "operational_notes") or _default_docs_operational_note_lines()),
session_prompt_rule_lines=(
_read_string_tuple(session_prompt_table, "working_rules") or _default_session_prompt_rule_lines()
),
Expand Down Expand Up @@ -367,9 +365,7 @@ def _default_docs_working_split_lines() -> tuple[str, ...]:


def _default_docs_operational_note_lines() -> tuple[str, ...]:
return (
"Historical plans remain available under `/Users/cbusillo/.codex/plans/` when you need rationale or prior sequencing.",
)
return ("Historical plans remain available under `/Users/cbusillo/.codex/plans/` when you need rationale or prior sequencing.",)


def _default_session_prompt_rule_lines() -> tuple[str, ...]:
Expand Down
6 changes: 1 addition & 5 deletions odoo_devkit/workspace_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,7 @@ def _render_workspace_session_prompt(
shared_addons_repo_path: Path | None,
) -> str:
shared_addons_source_path = shared_addons_repo_path.resolve() if shared_addons_repo_path is not None else None
shared_addons_line = (
f"- sources/shared-addons -> {shared_addons_source_path}\n"
if shared_addons_source_path is not None
else ""
)
shared_addons_line = f"- sources/shared-addons -> {shared_addons_source_path}\n" if shared_addons_source_path is not None else ""
return (
"# Session Prompt Template\n\n"
"Use this as a starting prompt for a new Every Code session from the generated workspace root.\n\n"
Expand Down
10 changes: 7 additions & 3 deletions tests/test_scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def test_real_template_renders_current_shared_addons_contract(self) -> None:
workspace_status_text = (output_directory / "scripts" / "workspace-status").read_text(encoding="utf-8")

self.assertIn('name = "odoo-devkit"', manifest_text)
self.assertIn('[repos.runtime]', manifest_text)
self.assertIn("[repos.runtime]", manifest_text)
self.assertIn('path = "../odoo-devkit"', manifest_text)
self.assertIn('name = "odoo-shared-addons"', manifest_text)
self.assertIn('path = "../odoo-shared-addons"', manifest_text)
Expand Down Expand Up @@ -272,7 +272,9 @@ def test_sync_workspace_cockpit_rerenders_existing_root(self) -> None:
self.assertIn("sources/shared-addons", agents_text)
self.assertIn("AGENTS.override.md", agents_text)
self.assertIn("Public base image", (output_directory / "docs" / "README.md").read_text(encoding="utf-8"))
self.assertIn("source repos as the source of truth", (output_directory / "docs" / "session-prompt.md").read_text(encoding="utf-8"))
self.assertIn(
"source repos as the source of truth", (output_directory / "docs" / "session-prompt.md").read_text(encoding="utf-8")
)

def test_workspace_cockpit_status_reports_current_missing_and_stale_files(self) -> None:
with tempfile.TemporaryDirectory() as temporary_directory:
Expand Down Expand Up @@ -317,7 +319,9 @@ def test_workspace_cockpit_status_reports_current_missing_and_stale_files(self)
(output_directory / "AGENTS.md").write_text("stale\n", encoding="utf-8")
stale_result = workspace_cockpit_status(manifest=manifest, output_directory=output_directory)
self.assertFalse(stale_result.is_current)
stale_agents_status = next(file_status for file_status in stale_result.file_statuses if file_status.path.name == "AGENTS.md")
stale_agents_status = next(
file_status for file_status in stale_result.file_statuses if file_status.path.name == "AGENTS.md"
)
self.assertTrue(stale_agents_status.exists)
self.assertFalse(stale_agents_status.matches_expected)

Expand Down
Loading