Skip to content

Commit 8890327

Browse files
committed
style: Formatting to match ruff
1 parent 4363590 commit 8890327

File tree

3 files changed

+111
-49
lines changed

3 files changed

+111
-49
lines changed

src/linear_cli/api/client/client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,8 +1284,10 @@ async def resolve_milestone_id(
12841284
if project_id:
12851285
# First pass: look for exact match in the specified project
12861286
for milestone in nodes:
1287-
if (milestone.get("name", "").lower() == milestone_identifier.lower() and
1288-
milestone.get("project", {}).get("id") == project_id):
1287+
if (
1288+
milestone.get("name", "").lower() == milestone_identifier.lower()
1289+
and milestone.get("project", {}).get("id") == project_id
1290+
):
12891291
return milestone.get("id")
12901292

12911293
# Fallback: search all milestones without project constraint

src/linear_cli/cli/commands/project.py

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -318,13 +318,11 @@ async def fetch_updates() -> dict[str, Any]:
318318

319319
@project.command("milestones")
320320
@click.argument("project_id")
321-
@click.option("--limit", "-l", type=int, default=50, help="Maximum number of milestones to show")
321+
@click.option(
322+
"--limit", "-l", type=int, default=50, help="Maximum number of milestones to show"
323+
)
322324
@click.pass_context
323-
def list_milestones(
324-
ctx: click.Context,
325-
project_id: str,
326-
limit: int
327-
) -> None:
325+
def list_milestones(ctx: click.Context, project_id: str, limit: int) -> None:
328326
"""
329327
List milestones for a project.
330328
@@ -360,10 +358,7 @@ async def fetch_milestones() -> dict[str, Any]:
360358
if milestone.get("project", {}).get("id") == project_data["id"]:
361359
project_milestones.append(milestone)
362360

363-
return {
364-
"nodes": project_milestones,
365-
"pageInfo": result.get("pageInfo", {})
366-
}
361+
return {"nodes": project_milestones, "pageInfo": result.get("pageInfo", {})}
367362

368363
return {}
369364

@@ -429,7 +424,11 @@ async def fetch_milestone() -> dict[str, Any] | None:
429424
@click.option("--target-date", help="Target completion date (YYYY-MM-DD)")
430425
@click.pass_context
431426
def create_milestone(
432-
ctx: click.Context, project_id: str, name: str, description: str | None, target_date: str | None
427+
ctx: click.Context,
428+
project_id: str,
429+
name: str,
430+
description: str | None,
431+
target_date: str | None,
433432
) -> None:
434433
"""
435434
Create a new milestone for a project.
@@ -465,7 +464,9 @@ async def create_milestone_async() -> dict[str, Any]:
465464
parsed_date = datetime.strptime(target_date, "%Y-%m-%d")
466465
formatted_date = f"{parsed_date.strftime('%Y-%m-%d')}T00:00:00Z"
467466
except ValueError:
468-
print_error(f"Invalid date format: {target_date}. Use YYYY-MM-DD format.")
467+
print_error(
468+
f"Invalid date format: {target_date}. Use YYYY-MM-DD format."
469+
)
469470
return {}
470471

471472
result = await client.create_milestone(
@@ -541,7 +542,9 @@ async def update_milestone_async() -> dict[str, Any]:
541542
parsed_date = datetime.strptime(target_date, "%Y-%m-%d")
542543
formatted_date = f"{parsed_date.strftime('%Y-%m-%d')}T00:00:00Z"
543544
except ValueError:
544-
print_error(f"Invalid date format: {target_date}. Use YYYY-MM-DD format.")
545+
print_error(
546+
f"Invalid date format: {target_date}. Use YYYY-MM-DD format."
547+
)
545548
return {}
546549

547550
result = await client.update_milestone(
@@ -575,7 +578,9 @@ async def update_milestone_async() -> dict[str, Any]:
575578
@click.argument("milestone_id")
576579
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
577580
@click.pass_context
578-
def delete_milestone(ctx: click.Context, project_id: str, milestone_id: str, yes: bool) -> None:
581+
def delete_milestone(
582+
ctx: click.Context, project_id: str, milestone_id: str, yes: bool
583+
) -> None:
579584
"""
580585
Delete a milestone from a project.
581586
@@ -600,7 +605,10 @@ async def delete_milestone_async() -> bool:
600605

601606
try:
602607
if not yes:
603-
click.confirm(f"Are you sure you want to delete milestone '{milestone_id}'?", abort=True)
608+
click.confirm(
609+
f"Are you sure you want to delete milestone '{milestone_id}'?",
610+
abort=True,
611+
)
604612

605613
success = asyncio.run(delete_milestone_async())
606614
if success:
@@ -616,9 +624,13 @@ async def delete_milestone_async() -> bool:
616624
@project.command("milestone-issues")
617625
@click.argument("project_id")
618626
@click.argument("milestone_id")
619-
@click.option("--limit", "-l", type=int, default=50, help="Maximum number of issues to show")
627+
@click.option(
628+
"--limit", "-l", type=int, default=50, help="Maximum number of issues to show"
629+
)
620630
@click.pass_context
621-
def list_milestone_issues(ctx: click.Context, project_id: str, milestone_id: str, limit: int) -> None:
631+
def list_milestone_issues(
632+
ctx: click.Context, project_id: str, milestone_id: str, limit: int
633+
) -> None:
622634
"""
623635
List issues in a project milestone.
624636
@@ -652,7 +664,9 @@ async def fetch_milestone_issues() -> dict[str, Any] | None:
652664
if not milestone_data:
653665
raise click.Abort()
654666

655-
console.print(f"[bold]Issues in milestone:[/bold] {milestone_data.get('name', milestone_id)}")
667+
console.print(
668+
f"[bold]Issues in milestone:[/bold] {milestone_data.get('name', milestone_id)}"
669+
)
656670

657671
issues = milestone_data.get("issues", {}).get("nodes", [])
658672
if not issues:
@@ -670,9 +684,18 @@ async def fetch_milestone_issues() -> dict[str, Any] | None:
670684

671685
@project.command("create-test-data")
672686
@click.option("--team", "-t", required=True, help="Team key or ID to use for test data")
673-
@click.option("--projects", type=int, default=1, help="Number of test projects to create")
674-
@click.option("--milestones-per-project", type=int, default=3, help="Number of milestones per project")
675-
@click.option("--issues-per-milestone", type=int, default=5, help="Number of issues per milestone")
687+
@click.option(
688+
"--projects", type=int, default=1, help="Number of test projects to create"
689+
)
690+
@click.option(
691+
"--milestones-per-project",
692+
type=int,
693+
default=3,
694+
help="Number of milestones per project",
695+
)
696+
@click.option(
697+
"--issues-per-milestone", type=int, default=5, help="Number of issues per milestone"
698+
)
676699
@click.pass_context
677700
def create_test_data(
678701
ctx: click.Context,
@@ -730,7 +753,9 @@ async def create_test_data_async() -> dict[str, int]:
730753

731754
# Create milestones for this project
732755
for milestone_num in range(1, milestones_per_project + 1):
733-
status.update(f"Creating milestone {milestone_num} for project {project_num}...")
756+
status.update(
757+
f"Creating milestone {milestone_num} for project {project_num}..."
758+
)
734759

735760
milestone_name = f"Milestone {milestone_num}"
736761
milestone_result = await client.create_milestone(
@@ -740,15 +765,19 @@ async def create_test_data_async() -> dict[str, int]:
740765
)
741766

742767
if not milestone_result.get("success"):
743-
console.print(f"[red]Failed to create milestone {milestone_name}[/red]")
768+
console.print(
769+
f"[red]Failed to create milestone {milestone_name}[/red]"
770+
)
744771
continue
745772

746773
milestone_id = milestone_result["projectMilestone"]["id"]
747774
counts["milestones"] += 1
748775

749776
# Create issues for this milestone
750777
for issue_num in range(1, issues_per_milestone + 1):
751-
status.update(f"Creating issue {issue_num} for milestone {milestone_num}...")
778+
status.update(
779+
f"Creating issue {issue_num} for milestone {milestone_num}..."
780+
)
752781

753782
# WHY: Use secure random choice for realistic test data variety
754783
# Different priorities and states make testing more realistic

tests/unit/test_project_milestone_commands.py

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ def test_list_project_milestones(self, runner, mock_cli_context):
9797
)
9898

9999
assert result.exit_code == 0
100-
mock_cli_context.get_client().get_project.assert_called_once_with("Test Project")
100+
mock_cli_context.get_client().get_project.assert_called_once_with(
101+
"Test Project"
102+
)
101103
mock_cli_context.get_client().get_milestones.assert_called_once_with(limit=50)
102104

103105
def test_list_milestones_project_not_found(self, runner, mock_cli_context):
@@ -128,7 +130,9 @@ def test_show_project_milestone(self, runner, mock_cli_context):
128130
}
129131

130132
# Mock milestone resolution first
131-
mock_cli_context.get_client().resolve_milestone_id.return_value = "milestone_123"
133+
mock_cli_context.get_client().resolve_milestone_id.return_value = (
134+
"milestone_123"
135+
)
132136
mock_cli_context.get_client().get_milestone.return_value = milestone_data
133137

134138
result = runner.invoke(
@@ -138,18 +142,21 @@ def test_show_project_milestone(self, runner, mock_cli_context):
138142
)
139143

140144
assert result.exit_code == 0
141-
mock_cli_context.get_client().resolve_milestone_id.assert_called_once_with("milestone_123", "Test Project")
142-
mock_cli_context.get_client().get_milestone.assert_called_once_with("milestone_123")
145+
mock_cli_context.get_client().resolve_milestone_id.assert_called_once_with(
146+
"milestone_123", "Test Project"
147+
)
148+
mock_cli_context.get_client().get_milestone.assert_called_once_with(
149+
"milestone_123"
150+
)
143151

144152
def test_show_milestone_by_name_with_resolution(self, runner, mock_cli_context):
145153
"""Test showing milestone by name with resolution."""
146-
milestone_data = {
147-
"id": "milestone_123",
148-
"name": "Sprint 1"
149-
}
154+
milestone_data = {"id": "milestone_123", "name": "Sprint 1"}
150155

151156
# Mock name resolution
152-
mock_cli_context.get_client().resolve_milestone_id.return_value = "milestone_123"
157+
mock_cli_context.get_client().resolve_milestone_id.return_value = (
158+
"milestone_123"
159+
)
153160
mock_cli_context.get_client().get_milestone.return_value = milestone_data
154161

155162
result = runner.invoke(
@@ -159,8 +166,12 @@ def test_show_milestone_by_name_with_resolution(self, runner, mock_cli_context):
159166
)
160167

161168
assert result.exit_code == 0
162-
mock_cli_context.get_client().resolve_milestone_id.assert_called_once_with("Sprint 1", "Test Project")
163-
mock_cli_context.get_client().get_milestone.assert_called_once_with("milestone_123")
169+
mock_cli_context.get_client().resolve_milestone_id.assert_called_once_with(
170+
"Sprint 1", "Test Project"
171+
)
172+
mock_cli_context.get_client().get_milestone.assert_called_once_with(
173+
"milestone_123"
174+
)
164175

165176

166177
class TestCreateMilestone:
@@ -259,7 +270,9 @@ class TestUpdateMilestone:
259270
def test_update_project_milestone(self, runner, mock_cli_context):
260271
"""Test updating milestone in a project."""
261272
# Mock milestone resolution
262-
mock_cli_context.get_client().resolve_milestone_id.return_value = "milestone_123"
273+
mock_cli_context.get_client().resolve_milestone_id.return_value = (
274+
"milestone_123"
275+
)
263276

264277
# Mock update result
265278
update_result = {
@@ -273,7 +286,13 @@ def test_update_project_milestone(self, runner, mock_cli_context):
273286

274287
result = runner.invoke(
275288
project,
276-
["update-milestone", "Test Project", "Sprint 1", "--name", "Sprint 1 Updated"],
289+
[
290+
"update-milestone",
291+
"Test Project",
292+
"Sprint 1",
293+
"--name",
294+
"Sprint 1 Updated",
295+
],
277296
obj={"cli_context": mock_cli_context},
278297
)
279298

@@ -294,7 +313,9 @@ class TestDeleteMilestone:
294313
def test_delete_project_milestone(self, runner, mock_cli_context):
295314
"""Test deleting milestone from a project."""
296315
# Mock milestone resolution
297-
mock_cli_context.get_client().resolve_milestone_id.return_value = "milestone_123"
316+
mock_cli_context.get_client().resolve_milestone_id.return_value = (
317+
"milestone_123"
318+
)
298319
mock_cli_context.get_client().delete_milestone.return_value = True
299320

300321
result = runner.invoke(
@@ -307,11 +328,15 @@ def test_delete_project_milestone(self, runner, mock_cli_context):
307328
assert result.exit_code == 0
308329
assert "Deleted milestone: Sprint 1" in result.output
309330

310-
mock_cli_context.get_client().delete_milestone.assert_called_once_with("milestone_123")
331+
mock_cli_context.get_client().delete_milestone.assert_called_once_with(
332+
"milestone_123"
333+
)
311334

312335
def test_delete_milestone_with_yes_flag(self, runner, mock_cli_context):
313336
"""Test deleting milestone with --yes flag."""
314-
mock_cli_context.get_client().resolve_milestone_id.return_value = "milestone_123"
337+
mock_cli_context.get_client().resolve_milestone_id.return_value = (
338+
"milestone_123"
339+
)
315340
mock_cli_context.get_client().delete_milestone.return_value = True
316341

317342
result = runner.invoke(
@@ -330,7 +355,9 @@ class TestMilestoneIssues:
330355
def test_list_milestone_issues(self, runner, mock_cli_context):
331356
"""Test listing issues in a project milestone."""
332357
# Mock milestone resolution
333-
mock_cli_context.get_client().resolve_milestone_id.return_value = "milestone_123"
358+
mock_cli_context.get_client().resolve_milestone_id.return_value = (
359+
"milestone_123"
360+
)
334361

335362
# Mock milestone data with issues
336363
milestone_data = {
@@ -431,11 +458,13 @@ def test_project_milestone_command_hierarchy(self):
431458
"update-milestone",
432459
"delete-milestone",
433460
"milestone-issues",
434-
"create-test-data"
461+
"create-test-data",
435462
]
436463

437464
for expected in expected_milestone_commands:
438-
assert expected in command_names, f"Command {expected} not found in project commands"
465+
assert expected in command_names, (
466+
f"Command {expected} not found in project commands"
467+
)
439468

440469
def test_milestone_commands_require_project_context(self):
441470
"""Test that milestone commands properly require project context."""
@@ -455,14 +484,16 @@ def test_milestone_commands_require_project_context(self):
455484
create_milestone,
456485
update_milestone,
457486
delete_milestone,
458-
list_milestone_issues
487+
list_milestone_issues,
459488
]
460489

461490
for cmd in commands_with_project_arg:
462491
# Check that first parameter is project_id
463-
params = [p for p in cmd.params if hasattr(p, 'name')]
464-
project_params = [p for p in params if 'project' in p.name.lower()]
465-
assert len(project_params) > 0, f"Command {cmd.name} missing project parameter"
492+
params = [p for p in cmd.params if hasattr(p, "name")]
493+
project_params = [p for p in params if "project" in p.name.lower()]
494+
assert len(project_params) > 0, (
495+
f"Command {cmd.name} missing project parameter"
496+
)
466497

467498

468499
if __name__ == "__main__":

0 commit comments

Comments
 (0)