Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6066890bae | ||
|
|
96c08ed15e | ||
|
|
52e87608ad | ||
|
|
90be279a2b | ||
|
|
eb39c1ed5d | ||
|
|
121c59760e |
@@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## 0.22.1 — 2026-10-20
|
||||
|
||||
### Correctif — colonne Terminé vide
|
||||
|
||||
- Les issues **fermées dans Gitea** mais suivies par le board
|
||||
n'apparaissaient pas dans la colonne **Terminé** : la
|
||||
réconciliation plaçait bien leur ligne locale en `done`, mais
|
||||
l'affichage du board ne fusionnait que les issues **ouvertes**.
|
||||
- Le board (page HTML et API JSON) inclut désormais les issues
|
||||
fermées dont la ligne locale est en `done`, y compris dans la
|
||||
vue par milestone.
|
||||
- Test de régression ajouté ; suite complète : 201 tests verts.
|
||||
|
||||
## 0.22.0 — 2026-10-14
|
||||
|
||||
### Écran de connexion (demande de Bruno)
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ from pathlib import Path
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
APP_VERSION = "0.22.0"
|
||||
APP_VERSION = "0.22.1"
|
||||
|
||||
|
||||
def _env_bool(name: str, default: bool) -> bool:
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ async def _mcp_session(client: httpx.AsyncClient, url: str, token: str) -> None:
|
||||
{
|
||||
"protocolVersion": MCP_PROTOCOL_VERSION,
|
||||
"capabilities": {},
|
||||
"clientInfo": {"name": "kanbante", "version": "0.22.0"},
|
||||
"clientInfo": {"name": "kanbante", "version": "0.22.1"},
|
||||
},
|
||||
req_id=1,
|
||||
)
|
||||
|
||||
+25
-5
@@ -832,13 +832,25 @@ async def board(request: Request, owner: str, repo: str, milestone: str = ""):
|
||||
# A kanban:* label changed outside Kanbante wins over local state.
|
||||
_adopt_label_columns(full_name, open_issues, get_board_state(full_name))
|
||||
|
||||
# Closed issues stay visible while the board tracks them as done
|
||||
# (reconcile_board_state moved their row). Merging open issues only
|
||||
# made the "Terminé" column permanently empty.
|
||||
done_numbers = {
|
||||
c["issue_number"] for c in get_board_state(full_name).get("done", [])
|
||||
}
|
||||
closed_display = [
|
||||
i
|
||||
for i in issues
|
||||
if i.get("state") != "open" and i["number"] in done_numbers
|
||||
]
|
||||
|
||||
# Milestone view: filter the board to one milestone (sprint-style),
|
||||
# with its deadline and completion progress.
|
||||
milestones: list[dict] = []
|
||||
selected_milestone = None
|
||||
milestone_progress = None
|
||||
milestone_forecast = None
|
||||
display_issues = open_issues
|
||||
display_issues = open_issues + closed_display
|
||||
try:
|
||||
milestones = await gitea.get_milestones(owner, repo)
|
||||
except Exception as exc: # milestones are decoration, never fatal
|
||||
@@ -851,7 +863,7 @@ async def board(request: Request, owner: str, repo: str, milestone: str = ""):
|
||||
str(ms.get("id")) == milestone or ms.get("title") == milestone
|
||||
)
|
||||
|
||||
display_issues = [i for i in open_issues if _in_milestone(i)]
|
||||
display_issues = [i for i in display_issues if _in_milestone(i)]
|
||||
selected_milestone = next(
|
||||
(
|
||||
m for m in milestones
|
||||
@@ -941,7 +953,16 @@ def _board_columns_for(
|
||||
for number in changes["closed_numbers"]:
|
||||
log_event(full_name, number, "closed", to_column="done", source="gitea")
|
||||
_adopt_label_columns(full_name, open_issues, get_board_state(full_name))
|
||||
display_issues = open_issues
|
||||
board_state = get_board_state(full_name)
|
||||
# Closed issues stay visible while the board tracks them as done
|
||||
# (reconcile_board_state moved their row). Merging open issues only
|
||||
# made the "Terminé" column permanently empty.
|
||||
done_numbers = {c["issue_number"] for c in board_state.get("done", [])}
|
||||
display_issues = open_issues + [
|
||||
i
|
||||
for i in issues
|
||||
if i.get("state") != "open" and i["number"] in done_numbers
|
||||
]
|
||||
if milestone:
|
||||
def _in_ms(issue: dict) -> bool:
|
||||
ms = issue.get("milestone") or {}
|
||||
@@ -949,8 +970,7 @@ def _board_columns_for(
|
||||
str(ms.get("id")) == milestone or ms.get("title") == milestone
|
||||
)
|
||||
|
||||
display_issues = [i for i in open_issues if _in_ms(i)]
|
||||
board_state = get_board_state(full_name)
|
||||
display_issues = [i for i in display_issues if _in_ms(i)]
|
||||
columns = _merge_issues_with_board(display_issues, board_state)
|
||||
return columns, display_issues, board_state
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "kanbante"
|
||||
version = "0.22.0"
|
||||
version = "0.22.1"
|
||||
description = "Kanban project manager integrated with Gitea"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
|
||||
@@ -39,6 +39,19 @@ def test_board_hides_pull_requests_and_reconciles(client):
|
||||
assert [c["issue_number"] for c in state["done"]] == [3] # reconciled
|
||||
|
||||
|
||||
def test_board_renders_closed_issue_in_done_column(client):
|
||||
# Regression: reconcile moved the closed issue's row to "done", but
|
||||
# the board merged open issues only, so the card never rendered and
|
||||
# the "Terminé" column stayed empty.
|
||||
models.move_issue("Projets/demo", 3, "todo") # closed in Gitea
|
||||
r = client.get("/board/Projets/demo")
|
||||
assert r.status_code == 200
|
||||
assert "Ancienne tâche" in r.text
|
||||
payload = client.get("/api/board/Projets/demo").json()
|
||||
assert payload["columns"]["done"]["count"] == 1
|
||||
assert "Ancienne tâche" in payload["columns"]["done"]["html"]
|
||||
|
||||
|
||||
def test_focus_lists_in_progress_across_projects(client):
|
||||
models.move_issue("Projets/demo", 5, "in_progress")
|
||||
r = client.get("/focus")
|
||||
|
||||
Reference in New Issue
Block a user