From c30f31e202e58430a758d1d05bf6f83904c16287 Mon Sep 17 00:00:00 2001 From: Stuart Hannon Date: Wed, 8 Jul 2026 10:57:00 -0400 Subject: [PATCH 1/2] fix(open-tasks): gather issues from all Flipper One repos (#371) Extend SECTIONS to cover all Flipper One repositories listed on the flipperdevices GitHub org page: - Linux (CPU Software): add flipper-linux-kernel, u-boot, rkbin, rockchip-linux, and bsb-protobuf alongside the existing flipperone-linux-build-scripts. These repos are part of the Linux build ecosystem (kernel, bootloader, firmware blobs, protobuf definitions). - FlipCTL: add a new section covering flipctl and flipctl-fonts, as requested in the issue. To support multiple repos per section, change the slug field from str to a list of strings and add a _match_section_slug helper. Also bump --limit from 200 to 1000 to future-proof against growth in the number of help-wanted issues across the org. Tested: script runs successfully with --input-json against a live snapshot of 18 open help-wanted issues. The flipper-linux-kernel issue #6 now correctly appears under the Linux section where it was previously silently dropped. --- tools/generate_open_tasks.py | 58 ++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/tools/generate_open_tasks.py b/tools/generate_open_tasks.py index 584268d..c9c2ffb 100755 --- a/tools/generate_open_tasks.py +++ b/tools/generate_open_tasks.py @@ -29,6 +29,7 @@ from pathlib import Path from textwrap import shorten from typing import TypedDict +import html from jinja2 import Environment, FileSystemLoader @@ -37,7 +38,10 @@ # anchor on the corresponding "About โ€ฆ" page on docs.flipper.net/one. SECTIONS: list[_Section] = [ { - "slug": "flipperdevices/flipperone-hardware", + "slug": [ + "flipperdevices/flipperone-hardware", + "flipperdevices/flipperone-debug-probe", + ], "title": "Hardware", "emoji": "๐Ÿ”Œ", "description": "Electrical hardware development: printed circuit boards (PCBs), antennas, and everything related to the electrical design.", @@ -46,7 +50,7 @@ "github_repo": "https://github.com/flipperdevices/flipperone-hardware", }, { - "slug": "flipperdevices/flipperone-mechanics", + "slug": ["flipperdevices/flipperone-mechanics"], "title": "Mechanics", "emoji": "โš™๏ธ", "description": "Mechanical design: enclosures, buttons, connectors, and everything related to the physical construction.", @@ -55,7 +59,14 @@ "github_repo": "https://github.com/flipperdevices/flipperone-mechanics", }, { - "slug": "flipperdevices/flipperone-linux-build-scripts", + "slug": [ + "flipperdevices/flipperone-linux-build-scripts", + "flipperdevices/flipper-linux-kernel", + "flipperdevices/u-boot", + "flipperdevices/rkbin", + "flipperdevices/rockchip-linux", + "flipperdevices/bsb-protobuf", + ], "title": "Linux (CPU Software)", "emoji": "๐Ÿง", "description": "Build system for the Linux firmware: U-Boot, kernel, root filesystem, and bootable images for the RK3576.", @@ -64,7 +75,7 @@ "github_repo": "https://github.com/flipperdevices/flipperone-linux-build-scripts", }, { - "slug": "flipperdevices/flipperone-mcu-firmware", + "slug": ["flipperdevices/flipperone-mcu-firmware"], "title": "MCU Firmware", "emoji": "๐Ÿ•น๏ธ", "description": "Firmware for the RP2350 co-processor: FreeRTOS, display, buttons, touchpad, LEDs, and power management.", @@ -73,7 +84,7 @@ "github_repo": "https://github.com/flipperdevices/flipperone-mcu-firmware", }, { - "slug": "flipperdevices/flipperone-ui", + "slug": ["flipperdevices/flipperone-ui"], "title": "User Interface", "emoji": "๐ŸŽจ", "description": "User interface design: operating modes, on-screen graphics, device prints, and FlipCTL.", @@ -82,7 +93,7 @@ "github_repo": "https://github.com/flipperdevices/flipperone-ui", }, { - "slug": "flipperdevices/flipperone-testing", + "slug": ["flipperdevices/flipperone-testing"], "title": "Testing", "emoji": "๐Ÿงช", "description": "Hardware validation and test scripts: temperature monitoring, power, CPU, GPU, network, and disk tests.", @@ -91,7 +102,7 @@ "github_repo": "https://github.com/flipperdevices/flipperone-testing", }, { - "slug": "flipperdevices/flipperone-docs", + "slug": ["flipperdevices/flipperone-docs"], "title": "Docs", "emoji": "๐Ÿ“š", "description": "Developer documentation published at docs.flipper.net/one.", @@ -99,6 +110,15 @@ "contribution_guide": "https://docs.flipper.net/one/resources/about-docs#how-to-contribute", "github_repo": "https://github.com/flipperdevices/flipperone-docs", }, + { + "slug": ["flipperdevices/flipctl", "flipperdevices/flipctl-fonts"], + "title": "FlipCTL", + "emoji": "๐ŸŽ›๏ธ", + "description": "Command-line tool and companion fonts for managing and configuring Flipper One.", + "task_tracker": "https://github.com/orgs/flipperdevices/projects/12", + "contribution_guide": "https://docs.flipper.net/one/user-interface/about#how-to-contribute", + "github_repo": "https://github.com/flipperdevices/flipctl", + }, ] LABEL = "help wanted" @@ -112,7 +132,7 @@ class _Repository(TypedDict): nameWithOwner: str class _Section(TypedDict): - slug: str + slug: str | list[str] title: str emoji: str description: str @@ -147,7 +167,7 @@ def fetch_issues() -> list[_Issue]: "--owner", ORG, "--label", LABEL, "--state", "open", - "--limit", "200", + "--limit", "1000", "--json", "repository,title,number,url,body,commentsCount", ], capture_output=True, text=True, check=True, @@ -191,12 +211,7 @@ def make_summary(body: str, max_len: int = 120) -> str: def html_escape(text: str) -> str: - return ( - text.replace("&", "&") - .replace("<", "<") - .replace(">", ">") - .replace('"', """) - ) + return html.escape(text, quote=True) def archbee_timestamp(dt: datetime) -> str: @@ -223,6 +238,13 @@ def _content_equal_ignoring_updated_at(a: str, b: str) -> bool: return pattern.sub("updatedAt: -", a) == pattern.sub("updatedAt: -", b) +def _match_section_slug(slug: str | list[str], repo_full: str) -> bool: + """Check if a repo name matches a section slug (single name or list).""" + if isinstance(slug, list): + return repo_full in slug + return repo_full == slug + + def _build_template_sections(issues: list[_Issue]) -> list[_DisplaySection]: """Prepare section and issue data for the Open Tasks Jinja template.""" by_repo: dict[str, list[_Issue]] = {} @@ -232,6 +254,10 @@ def _build_template_sections(issues: list[_Issue]) -> list[_DisplaySection]: sections: list[_DisplaySection] = [] for section in SECTIONS: + matched_issues: list[_Issue] = [] + for repo_full, repo_issues in by_repo.items(): + if _match_section_slug(section["slug"], repo_full): + matched_issues.extend(repo_issues) rendered_issues: list[_DisplayIssue] = [ { "number": issue["number"], @@ -240,7 +266,7 @@ def _build_template_sections(issues: list[_Issue]) -> list[_DisplaySection]: "summary": html_escape(make_summary(issue.get("body", ""))), "commentsCount": issue.get("commentsCount", 0), } - for issue in by_repo.get(section["slug"], []) + for issue in matched_issues ] rendered_issues.sort(key=lambda i: i["number"], reverse=True) sections.append({**section, "issues": rendered_issues}) From 9a701c76f4a07bffba6c539c04d9f64f6d15a6bb Mon Sep 17 00:00:00 2001 From: Stuart Hannon Date: Wed, 8 Jul 2026 11:54:26 -0400 Subject: [PATCH 2/2] fix: add graceful error handling when gh CLI is missing or not authenticated Add two layers of protection in fetch_issues(): 1. Early shutil.which('gh') check before attempting the subprocess call 2. try/except around subprocess.run(check=True) catching CalledProcessError Both paths print a clear, actionable message telling the user to install gh via brew and authenticate with gh auth login, then exit. Fixes the opaque CalledProcessError crash identified in task 158. --- tools/generate_open_tasks.py | 40 ++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/tools/generate_open_tasks.py b/tools/generate_open_tasks.py index c9c2ffb..54a1f1a 100755 --- a/tools/generate_open_tasks.py +++ b/tools/generate_open_tasks.py @@ -23,6 +23,7 @@ import argparse import json import re +import shutil import subprocess import sys from datetime import datetime, timezone @@ -161,17 +162,34 @@ class _DisplaySection(_Section): def fetch_issues() -> list[_Issue]: """Fetch all open `help wanted` issues across the org via the `gh` CLI.""" - result = subprocess.run( - [ - "gh", "search", "issues", - "--owner", ORG, - "--label", LABEL, - "--state", "open", - "--limit", "1000", - "--json", "repository,title,number,url,body,commentsCount", - ], - capture_output=True, text=True, check=True, - ) + if not shutil.which("gh"): + print( + "Error: gh CLI command failed. Ensure gh is installed (`brew install gh`) " + "and authenticated (`gh auth login`).", + file=sys.stderr, + ) + sys.exit(1) + + try: + result = subprocess.run( + [ + "gh", "search", "issues", + "--owner", ORG, + "--label", LABEL, + "--state", "open", + "--limit", "1000", + "--json", "repository,title,number,url,body,commentsCount", + ], + capture_output=True, text=True, check=True, + ) + except subprocess.CalledProcessError: + print( + "Error: gh CLI command failed. Ensure gh is installed (`brew install gh`) " + "and authenticated (`gh auth login`).", + file=sys.stderr, + ) + sys.exit(1) + return json.loads(result.stdout) # type: ignore[no-any-return]