DEVX-40: feat: remove .taskid file fallback, use branch name only
Post-merge / detect-type (push) Successful in 13s
Post-merge / validate-commit-msg (push) Successful in 13s
Post-merge / configure-repo (push) Successful in 13s
Post-merge / release (push) Successful in 39s
Post-merge / vikunja (push) Successful in 7s
Post-merge / sync-wiki (push) Successful in 56s
Post-merge / badges (push) Successful in 1m25s

This commit was merged in pull request #64.
This commit is contained in:
2026-06-24 20:02:19 +00:00
parent fbb1fc3134
commit cf85964877
7 changed files with 51 additions and 92 deletions
+15 -27
View File
@@ -2,9 +2,9 @@
"""Auto-merge PR when all CI checks pass.
Runs as the final job in ci.yml. Reads the task ID from the branch name
(falling back to ``.taskid`` file for branches without a task ID prefix),
validates the PR title, and squash-merges with a conventional commit
message prefixed by the task ID.
(e.g., ``DEVX-31-fix-foo`` → ``DEVX-31``), validates the PR title against
the Vikunja task, and squash-merges with a conventional commit message
prefixed by the task ID.
PR title format: ``{PREFIX}-N: <vikunja task title>``
Merge commit format: ``{PREFIX}-N <conventional commit message>``
@@ -42,7 +42,7 @@ from devx.config import (
from devx.exceptions import APIError
from devx.i18n import _
TASKID_FILE = ".taskid"
TASKID_FILE = ".taskid" # Deprecated, kept for backward-compat warnings
PR_TITLE_RE = re.compile(rf"^{TASK_PREFIX}-\d+:\s+.+")
load_dotenv()
@@ -63,44 +63,31 @@ def run_cmd(args: list[str], check: bool = True) -> subprocess.CompletedProcess[
def read_taskid(branch: str) -> str:
"""Read task ID from branch name, falling back to .taskid file.
"""Read task ID from branch name.
The branch name is the primary source of truth for the task ID
(e.g., ``DEVX-31-fix-foo`` → ``DEVX-31``). The ``.taskid`` file
is a legacy fallback for branches without a task ID prefix.
The branch name is the sole source of truth for the task ID
(e.g., ``DEVX-31-fix-foo`` → ``DEVX-31``). Branches must include
the task ID prefix — there is no ``.taskid`` file fallback.
If both sources exist and disagree, a warning is printed and the
branch task ID is preferred (it is the current source of truth).
If a stale ``.taskid`` file exists and disagrees with the branch
name, a deprecation warning is printed advising its removal.
"""
branch_task_id = extract_task_id(branch)
if branch_task_id:
# Check for stale .taskid file that disagrees with branch name
# Warn about stale .taskid file if it exists and disagrees
path = Path(TASKID_FILE)
if path.exists():
file_task_id = path.read_text(encoding="utf-8").strip()
if file_task_id and file_task_id != branch_task_id:
click.echo(
_(
"WARNING: .taskid file ({file_id}) disagrees with branch name ({branch_id}). "
"Using branch task ID. Update or delete .taskid to silence this warning.",
"WARNING: .taskid file ({file_id}) is deprecated and disagrees with branch name ({branch_id}). "
"Delete .taskid from the repo — branch name is the sole source of truth.",
file_id=file_task_id,
branch_id=branch_task_id,
)
)
return branch_task_id
# Fallback: read from .taskid file
path = Path(TASKID_FILE)
if path.exists():
task_id = path.read_text(encoding="utf-8").strip()
if task_id:
click.echo(
_(
"Task ID from .taskid file: {task_id} (not found in branch name '{branch}')",
task_id=task_id,
branch=branch,
)
)
return task_id
return ""
@@ -229,7 +216,8 @@ def main(branch: str, pr_title: str, repo: str, pr_number: str) -> None:
if not task_id:
raise click.ClickException(
_(
"Oops! No task ID found in .taskid file or branch name '{branch}'.",
"Oops! No task ID found in branch name '{branch}'. "
"Branch names must include the task ID prefix (e.g., DEVX-31-fix-bug).",
branch=branch,
)
)
-2
View File
@@ -298,8 +298,6 @@ DEFAULT_INFRASTRUCTURE: list[str] = [
"activate.sh",
"activate.fish",
"activate.zsh",
# CI task tracking file (written by CI, not by developers)
".taskid",
]
+14 -21
View File
@@ -776,13 +776,6 @@
"ru": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}",
"zh": "Oops! Master branch commits must start with a task ID.\n Expected: {prefix}-N: <conventional commit message>\n Got: {subject}"
},
"Oops! No task ID found in .taskid file or branch name '{branch}'.": {
"bg": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"de": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"en": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"ru": "Oops! No task ID found in .taskid file or branch name '{branch}'.",
"zh": "Oops! No task ID found in .taskid file or branch name '{branch}'."
},
"Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}": {
"bg": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
"de": "Oops! PR title must follow format '{prefix}-N: <task title>'.\n Expected: {task_id}: <task title>\n Got: {pr_title}",
@@ -993,13 +986,6 @@
"ru": "Task ID: {task_id}",
"zh": "Task ID: {task_id}"
},
"Task ID from .taskid file: {task_id} (not found in branch name '{branch}')": {
"bg": "Task ID от .taskid файл: {task_id} (не е намерен в името на клона '{branch}')",
"de": "Task ID aus .taskid-Datei: {task_id} (nicht im Branch-Namen '{branch}' gefunden)",
"en": "Task ID from .taskid file: {task_id} (not found in branch name '{branch}')",
"ru": "Task ID из файла .taskid: {task_id} (не найден в имени ветки '{branch}')",
"zh": "来自 .taskid 文件的 Task ID: {task_id}(在分支名 '{branch}' 中未找到)"
},
"Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.": {
"bg": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
"de": "Test '{name}' took {elapsed:.2f}s (limit: {limit}s). Optimise: use lighter fixtures, reduce I/O, or mock external calls.",
@@ -1084,13 +1070,6 @@
"ru": "WARNING: --skip-tests passed — skipping test verification.",
"zh": "WARNING: --skip-tests passed — skipping test verification."
},
"WARNING: .taskid file ({file_id}) disagrees with branch name ({branch_id}). Using branch task ID. Update or delete .taskid to silence this warning.": {
"bg": "ВНИМАНИЕ: .taskid файл ({file_id}) не съвпада с името на клона ({branch_id}). Използва се task ID от клона. Актуализирайте или изтрийте .taskid за да премахнете това предупреждение.",
"de": "WARNUNG: .taskid-Datei ({file_id}) stimmt nicht mit Branch-Namen ({branch_id}) überein. Branch-Task-ID wird verwendet. Aktualisieren oder löschen Sie .taskid, um diese Warnung zu unterdrücken.",
"en": "WARNING: .taskid file ({file_id}) disagrees with branch name ({branch_id}). Using branch task ID. Update or delete .taskid to silence this warning.",
"ru": "ВНИМАНИЕ: файл .taskid ({file_id}) не совпадает с именем ветки ({branch_id}). Используется Task ID из ветки. Обновите или удалите .taskid, чтобы скрыть это предупреждение.",
"zh": "警告:.taskid 文件 ({file_id}) 与分支名 ({branch_id}) 不一致。使用分支 Task ID。更新或删除 .taskid 以消除此警告。"
},
"Warning: could not fetch tags from origin.": {
"bg": "Warning: could not fetch tags from origin.",
"de": "Warning: could not fetch tags from origin.",
@@ -1258,5 +1237,19 @@
"en": "{file} already exists. Use --force to overwrite.",
"ru": "{file} already exists. Use --force to overwrite.",
"zh": "{file} already exists. Use --force to overwrite."
},
"Oops! No task ID found in branch name '{branch}'. Branch names must include the task ID prefix (e.g., DEVX-31-fix-bug).": {
"en": "Oops! No task ID found in branch name '{branch}'. Branch names must include the task ID prefix (e.g., DEVX-31-fix-bug).",
"bg": "Ой! Не е намерен ID на задача в името на клона '{branch}'. Имената на клонове трябва да включват префикса за ID на задача (напр. DEVX-31-fix-bug).",
"de": "Hoppla! Keine Task-ID im Branch-Namen '{branch}' gefunden. Branch-Namen müssen das Task-ID-Präfix enthalten (z.B. DEVX-31-fix-bug).",
"ru": "Ой! ID задачи не найден в имени ветки '{branch}'. Имена веток должны включать префикс ID задачи (например, DEVX-31-fix-bug).",
"zh": "哎呀!在分支名称 '{branch}' 中未找到任务 ID。分支名称必须包含任务 ID 前缀(例如 DEVX-31-fix-bug)。"
},
"WARNING: .taskid file ({file_id}) is deprecated and disagrees with branch name ({branch_id}). Delete .taskid from the repo — branch name is the sole source of truth.": {
"en": "WARNING: .taskid file ({file_id}) is deprecated and disagrees with branch name ({branch_id}). Delete .taskid from the repo — branch name is the sole source of truth.",
"bg": "ВНИМАНИЕ: Файлът .taskid ({file_id}) е остарял и не съвпада с името на клона ({branch_id}). Изтрийте .taskid от хранилището — името на клона е единственият източник на истината.",
"de": "WARNUNG: Die Datei .taskid ({file_id}) ist veraltet und stimmt nicht mit dem Branch-Namen ({branch_id}) überein. Löschen Sie .taskid aus dem Repo — der Branch-Name ist die einzige Wahrheitsquelle.",
"ru": "ВНИМАНИЕ: Файл .taskid ({file_id}) устарел и не совпадает с именем ветки ({branch_id}). Удалите .taskid из репозитория — имя ветки — единственный источник истины.",
"zh": "警告:.taskid 文件 ({file_id}) 已弃用,与分支名称 ({branch_id}) 不一致。请从仓库中删除 .taskid — 分支名称是唯一的真实来源。"
}
}