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 cli/localci/cli/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

from localci.cli.list import _MATRIX_TABLE_COLUMNS, _PLATFORM_COLOR
from localci.core.serialization import workflow_summary, workflow_to_json
from localci.core.workflow import Workflow, WorkflowAnalyzer, WorkflowError
from localci.core.workflow import Workflow, WorkflowAnalyzer
from localci.errors import WorkflowError
from localci.utils.output import (
console,
make_table,
Expand All @@ -39,7 +40,7 @@ def _print_workflow_header(wf: Workflow) -> None:
console.print()


def _print_jobs_table(wf) -> None:
def _print_jobs_table(wf: Workflow) -> None:
"""Print summary of all jobs."""
table = make_table(
"#", "Job ID", "Matrix", "Steps", "Depends On", "Timeout", title="Jobs"
Expand Down Expand Up @@ -84,7 +85,7 @@ def _print_matrix_table(wf: Workflow) -> None:
console.print()


def _print_platform_summary(wf) -> None:
def _print_platform_summary(wf: Workflow) -> None:
"""Print platform breakdown."""
summary = wf.platform_summary()
if not summary:
Expand Down
3 changes: 2 additions & 1 deletion cli/localci/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from __future__ import annotations

from pathlib import Path
from typing import Any

import click
import yaml
Expand Down Expand Up @@ -103,7 +104,7 @@ def config_set(ctx: click.Context, key: str, value: str) -> None:
ctx.exit(1)

with open(config_path, encoding="utf-8") as fh:
data: dict = yaml.safe_load(fh) or {}
data: dict[str, Any] = yaml.safe_load(fh) or {}

# Navigate dot-notation key.
keys = key.split(".")
Expand Down
2 changes: 1 addition & 1 deletion cli/localci/cli/list.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
CompilerFamily,
Platform,
WorkflowAnalyzer,
WorkflowError,
)
from localci.errors import WorkflowError
from localci.utils.output import (
console,
make_table,
Expand Down
3 changes: 2 additions & 1 deletion cli/localci/cli/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import click

from localci.core.executor import JobResult
from localci.core.results import ExecutionSummary
from localci.utils.output import (
console,
Expand Down Expand Up @@ -151,7 +152,7 @@ def logs(
click.echo(content)


def _find_job(summary: ExecutionSummary, query: str):
def _find_job(summary: ExecutionSummary, query: str) -> JobResult | None:
"""Find a job by index or name.
Matching priority:
Expand Down
5 changes: 2 additions & 3 deletions cli/localci/cli/run/click_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,8 @@ def run_options(command: F) -> F:
"Run in offline mode (no action downloads, requires pre-cached actions)."
),
),
click.pass_context,
]
wrapped: F = command
wrapped: F = click.pass_context(command) # type: ignore[assignment]
for opt in reversed(opts):
wrapped = opt(wrapped) # type: ignore[assignment]
wrapped = opt(wrapped)
return wrapped
4 changes: 2 additions & 2 deletions cli/localci/cli/run/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from localci.core.boost_cache import ensure_boost_cache
from localci.core.ccache_stats import get_ccache_stats
from localci.core.config import LocalCIConfig, resolve_cache_paths
from localci.core.config import LocalCIConfig, ResolvedCachePaths, resolve_cache_paths
from localci.core.executor import JobExecutor
from localci.core.orchestrator import OrchestratorConfig, ParallelExecutionManager
from localci.core.progress import ProgressTracker
Expand All @@ -30,7 +30,7 @@ class RunDependencies:
priority_config_factory: Callable[[LocalCIConfig], PriorityConfig]
ensure_boost_cache_fn: Callable[..., bool]
get_ccache_stats_fn: Callable[..., str | None]
resolve_cache_paths_fn: Callable[..., object]
resolve_cache_paths_fn: Callable[..., ResolvedCachePaths | None]
workflow_patcher: Callable[..., Path]


Expand Down
5 changes: 4 additions & 1 deletion cli/localci/cli/run/patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@

from localci.core.config import LocalCIConfig
from localci.core.patch_pipeline import PatchContext, PatchPipeline
from localci.core.queue import PriorityJobQueue
from localci.core.workflow import MatrixEntry
from localci.utils.output import console, print_info, print_key_value


def _print_execution_plan(queue, workflow_path: Path, timeout: int) -> None:
def _print_execution_plan(
queue: PriorityJobQueue, workflow_path: Path, timeout: int
) -> None:
"""Print dry-run execution plan from the queue."""
from rich.table import Table

Expand Down
22 changes: 12 additions & 10 deletions cli/localci/cli/run/run_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@
from __future__ import annotations

from pathlib import Path
from typing import Any

from localci.cli.run.container import RunDependencies, build_run_container
from localci.cli.run.params import RunOptions
from localci.cli.run.patcher import _print_execution_plan, make_workflow_patcher
from localci.core.config import LocalCIConfig
from localci.core.executor import ActNotFoundError, DockerNotAvailableError
from localci.core.executor import JobExecutor
from localci.core.github_token import resolve_github_token, warn_sentinel_github_token
from localci.core.models import JobEvent, JobEventType
from localci.core.results import ExecutionSummary
from localci.core.workflow import MatrixEntry, Platform
from localci.errors import WorkflowError
from localci.core.workflow import MatrixEntry, Platform, Workflow
from localci.errors import ActNotFoundError, DockerNotAvailableError, WorkflowError
from localci.utils.output import print_error, print_info, print_warning

_PLATFORM_MAP = {
Expand Down Expand Up @@ -72,9 +73,10 @@ def execute_run(
return 0

priority_config = container.priority_config_factory(cfg)
registry_path = project_dir / "image-registry.yml"
if not registry_path.exists():
registry_path = None
registry_candidate = project_dir / "image-registry.yml"
registry_path: Path | None = (
registry_candidate if registry_candidate.exists() else None
)

matrix_include, matrix_exclude = _resolve_matrix_filters(options, cfg)
plat_filter = _PLATFORM_MAP.get(options.platform) if options.platform else None
Expand Down Expand Up @@ -176,7 +178,7 @@ def execute_run(
return 0 if summary.all_passed else 1


def _collect_matrix_pairs(wf) -> list[tuple[str, MatrixEntry]]:
def _collect_matrix_pairs(wf: Workflow) -> list[tuple[str, MatrixEntry]]:
pairs: list[tuple[str, MatrixEntry]] = []
for job_id, job in wf.jobs.items():
for entry in job.matrix:
Expand Down Expand Up @@ -229,8 +231,8 @@ def _filter_matrix_pairs(

def _resolve_matrix_filters(
options: RunOptions, cfg: LocalCIConfig
) -> tuple[list[dict] | None, list[dict] | None]:
cli_matrix_include: list[dict] | None = None
) -> tuple[list[dict[str, Any]] | None, list[dict[str, Any]] | None]:
cli_matrix_include: list[dict[str, Any]] | None = None
if options.matrix_filters:
cli_matrix_include = [{}]
for s in options.matrix_filters:
Expand All @@ -257,7 +259,7 @@ def _resolve_matrix_filters(
return matrix_include, matrix_exclude


def _run_preflight(executor) -> int:
def _run_preflight(executor: JobExecutor) -> int:
try:
act_version = executor.check_act()
print_info(f"Using {act_version}")
Expand Down
17 changes: 12 additions & 5 deletions cli/localci/cli/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import json
import time
from pathlib import Path
from typing import Any

import click

Expand Down Expand Up @@ -178,13 +179,19 @@ def _safe_float(value: object, default: float = 0.0) -> float:
"""Coerce *value* to float, returning *default* on None or parse failure."""
if value is None:
return default
try:
return float(value)
except (TypeError, ValueError):
if isinstance(value, bool):
return default
if isinstance(value, (int, float)):
return float(value)
if isinstance(value, str):
try:
return float(value)
except ValueError:
return default
return default


def _job_list(data: dict, key: str) -> list[dict]:
def _job_list(data: dict[str, Any], key: str) -> list[dict[str, Any]]:
"""Return a list of dict items from *data[key]*, skipping non-dict entries."""
raw = data.get(key) if isinstance(data, dict) else None
if not isinstance(raw, list):
Expand Down Expand Up @@ -247,7 +254,7 @@ def _follow_status(status_file: Path, output_format: str) -> None:
"""Poll status file and refresh display until Ctrl+C."""
console.print("\nFollowing... (Ctrl+C to stop)")
try:
last_data: dict | None = None
last_data: dict[str, Any] | None = None
while True:
time.sleep(1)
if not status_file.exists():
Expand Down
18 changes: 1 addition & 17 deletions cli/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,7 @@ markers = [
]
norecursedirs = ["integration"]

# Mypy baseline: legacy CLI modules suppressed via per-module ignore_errors
# overrides below. localci.core.* and localci.utils.* pass strict mypy with
# zero errors. New modules must not use ignore_errors; remove overrides as
# legacy CLI modules are typed.
# Mypy: strict mode for all localci packages (core, utils, cli).
[tool.mypy]
python_version = "3.10"
strict = true
Expand All @@ -54,19 +51,6 @@ warn_redundant_casts = true
warn_unused_ignores = true
packages = ["localci"]

[[tool.mypy.overrides]]
module = [
"localci.cli.analyze",
"localci.cli.config",
"localci.cli.list",
"localci.cli.logs",
"localci.cli.run.click_options",
"localci.cli.run.patcher",
"localci.cli.run.run_flow",
"localci.cli.status",
]
ignore_errors = true

[tool.ruff]
target-version = "py310"
line-length = 88
Expand Down
Loading