feat(kotlin-compose): design system + 33 components + gallery_kt + e2e android emulator + scaffolder fixes
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// CppAppAudit holds the standard-conformance report for a single C++ app.
|
||||
type CppAppAudit struct {
|
||||
AppID string `json:"app_id"`
|
||||
DirPath string `json:"dir_path"`
|
||||
OK bool `json:"ok"`
|
||||
Issues []string `json:"issues"`
|
||||
}
|
||||
|
||||
// AuditCppApps walks every app registered with lang='cpp' in registry.db and
|
||||
// checks conformance with cpp/PATTERNS.md and .claude/rules/cpp_apps.md:
|
||||
// - main.cpp exists at the root of the app dir
|
||||
// - main.cpp uses fn::run_app(...) (not glfwInit directly)
|
||||
// - cfg.about is declared (or .about = passed in run_app literal)
|
||||
// - cfg.log is declared
|
||||
// - main.cpp does NOT call fn_ui::app_menubar(nullptr,...) manually
|
||||
// - main.cpp does NOT call ImGui::DockSpaceOverViewport(...) unless
|
||||
// cfg.auto_dockspace = false is also set
|
||||
// - app.md frontmatter declares framework: imgui and entry_point
|
||||
//
|
||||
// Apps whose dir does not exist on disk are skipped with a placeholder issue.
|
||||
func AuditCppApps(registryRoot string) ([]CppAppAudit, error) {
|
||||
dbPath := filepath.Join(registryRoot, "registry.db")
|
||||
dsn := fmt.Sprintf("file:%s?mode=ro&_foreign_keys=on", dbPath)
|
||||
db, err := sql.Open("sqlite3", dsn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("audit_cpp_apps: open db: %w", err)
|
||||
}
|
||||
defer db.Close()
|
||||
if err := db.Ping(); err != nil {
|
||||
return nil, fmt.Errorf("audit_cpp_apps: ping db: %w", err)
|
||||
}
|
||||
|
||||
rows, err := db.Query(`SELECT id, dir_path, COALESCE(framework, '') FROM apps WHERE lang = 'cpp' ORDER BY id`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("audit_cpp_apps: query apps: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var results []CppAppAudit
|
||||
for rows.Next() {
|
||||
var id, dir, framework string
|
||||
if err := rows.Scan(&id, &dir, &framework); err != nil {
|
||||
continue
|
||||
}
|
||||
// Solo auditar apps imgui — apps CLI/test no necesitan cfg.about/log/menubar.
|
||||
if framework != "imgui" {
|
||||
continue
|
||||
}
|
||||
audit := CppAppAudit{AppID: id, DirPath: dir}
|
||||
|
||||
absDir := dir
|
||||
if !filepath.IsAbs(absDir) {
|
||||
absDir = filepath.Join(registryRoot, dir)
|
||||
}
|
||||
if _, err := os.Stat(absDir); os.IsNotExist(err) {
|
||||
audit.Issues = append(audit.Issues, "directory_missing")
|
||||
results = append(results, audit)
|
||||
continue
|
||||
}
|
||||
|
||||
// main.cpp checks
|
||||
mainPath := filepath.Join(absDir, "main.cpp")
|
||||
mainBytes, err := os.ReadFile(mainPath)
|
||||
if err != nil {
|
||||
audit.Issues = append(audit.Issues, "main.cpp_missing_or_unreadable")
|
||||
} else {
|
||||
src := string(mainBytes)
|
||||
if !strings.Contains(src, "fn::run_app") {
|
||||
audit.Issues = append(audit.Issues, "no_fn_run_app_call")
|
||||
}
|
||||
if strings.Contains(src, "glfwInit(") {
|
||||
audit.Issues = append(audit.Issues, "manual_glfwInit_call")
|
||||
}
|
||||
if !cppHasAbout(src) {
|
||||
audit.Issues = append(audit.Issues, "missing_cfg_about")
|
||||
}
|
||||
if !cppHasLog(src) {
|
||||
audit.Issues = append(audit.Issues, "missing_cfg_log")
|
||||
}
|
||||
if cppHasManualMenubar(src) {
|
||||
audit.Issues = append(audit.Issues, "manual_app_menubar_call")
|
||||
}
|
||||
if cppHasManualDockSpace(src) && !cppHasAutoDockspaceFalse(src) {
|
||||
audit.Issues = append(audit.Issues, "manual_DockSpaceOverViewport_without_auto_dockspace_false")
|
||||
}
|
||||
}
|
||||
|
||||
// app.md checks
|
||||
appMdPath := filepath.Join(absDir, "app.md")
|
||||
mdBytes, err := os.ReadFile(appMdPath)
|
||||
if err != nil {
|
||||
audit.Issues = append(audit.Issues, "app.md_missing")
|
||||
} else {
|
||||
md := string(mdBytes)
|
||||
if !strings.Contains(md, "framework:") || !strings.Contains(md, "imgui") {
|
||||
audit.Issues = append(audit.Issues, "app.md_missing_framework_imgui")
|
||||
}
|
||||
if !strings.Contains(md, "entry_point:") {
|
||||
audit.Issues = append(audit.Issues, "app.md_missing_entry_point")
|
||||
}
|
||||
}
|
||||
|
||||
audit.OK = len(audit.Issues) == 0
|
||||
results = append(results, audit)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// cppHasAbout returns true if main.cpp sets cfg.about or passes .about = in a
|
||||
// run_app literal initializer.
|
||||
func cppHasAbout(src string) bool {
|
||||
return strings.Contains(src, "cfg.about") ||
|
||||
strings.Contains(src, ".about ") ||
|
||||
strings.Contains(src, ".about=") ||
|
||||
strings.Contains(src, "about_window_set_info")
|
||||
}
|
||||
|
||||
// cppHasLog returns true if main.cpp sets cfg.log or .log = in a run_app
|
||||
// literal initializer.
|
||||
func cppHasLog(src string) bool {
|
||||
return strings.Contains(src, "cfg.log") ||
|
||||
strings.Contains(src, ".log ") ||
|
||||
strings.Contains(src, ".log=")
|
||||
}
|
||||
|
||||
// cppHasManualMenubar returns true if main.cpp invokes
|
||||
// fn_ui::app_menubar(...) directly. The framework already calls it once per
|
||||
// frame, so any manual call is a duplicate.
|
||||
func cppHasManualMenubar(src string) bool {
|
||||
return strings.Contains(src, "fn_ui::app_menubar(") ||
|
||||
strings.Contains(src, "app_menubar(nullptr")
|
||||
}
|
||||
|
||||
// cppHasManualDockSpace returns true if main.cpp invokes
|
||||
// ImGui::DockSpaceOverViewport(...) inside its render(). Coexists with the
|
||||
// framework's auto dockspace only if cfg.auto_dockspace = false is set.
|
||||
func cppHasManualDockSpace(src string) bool {
|
||||
return strings.Contains(src, "DockSpaceOverViewport(")
|
||||
}
|
||||
|
||||
// cppHasAutoDockspaceFalse returns true if main.cpp opts out of the
|
||||
// framework auto-dockspace via cfg.auto_dockspace = false or
|
||||
// .auto_dockspace = false.
|
||||
func cppHasAutoDockspaceFalse(src string) bool {
|
||||
return strings.Contains(src, "auto_dockspace = false") ||
|
||||
strings.Contains(src, "auto_dockspace=false")
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
name: audit_cpp_apps
|
||||
kind: function
|
||||
lang: go
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "func AuditCppApps(registryRoot string) ([]CppAppAudit, error)"
|
||||
description: "Audita conformidad de apps C++ del registry con cpp/PATTERNS.md y .claude/rules/cpp_apps.md. Verifica que cada main.cpp use fn::run_app, declare cfg.about y cfg.log, no llame fn_ui::app_menubar manualmente, no llame ImGui::DockSpaceOverViewport sin opt-out (cfg.auto_dockspace = false), y que app.md tenga framework: imgui y entry_point. Read-only."
|
||||
tags: [doctor, registry-first, audit, cpp, imgui, standard]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: ["database/sql", "fmt", "os", "path/filepath", "strings", "github.com/mattn/go-sqlite3"]
|
||||
params:
|
||||
- name: registryRoot
|
||||
desc: "ruta absoluta al directorio raiz del fn_registry (donde vive registry.db y cpp/apps/)"
|
||||
output: "slice de CppAppAudit, uno por app C++ registrada. Cada entrada incluye AppID, DirPath, OK (true si no hay issues), y lista Issues con codigos: directory_missing, main.cpp_missing_or_unreadable, no_fn_run_app_call, manual_glfwInit_call, missing_cfg_about, missing_cfg_log, manual_app_menubar_call, manual_DockSpaceOverViewport_without_auto_dockspace_false, app.md_missing, app.md_missing_framework_imgui, app.md_missing_entry_point. Error solo si registry.db no puede abrirse."
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "functions/infra/audit_cpp_apps.go"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```go
|
||||
audits, err := infra.AuditCppApps("/home/lucas/fn_registry")
|
||||
if err != nil { log.Fatal(err) }
|
||||
for _, a := range audits {
|
||||
if !a.OK {
|
||||
fmt.Printf("%s: %v\n", a.AppID, a.Issues)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Contexto
|
||||
|
||||
Wrapper consumido por `fn doctor cpp-apps`. Sirve como gate para detectar apps que se desvian del estandar (ej. `app_menubar` manual residual, falta `cfg.about`, `app.md` sin frontmatter completo) tras refactors o creacion manual sin usar el scaffolder `init_cpp_app_bash_pipelines`.
|
||||
|
||||
Issues detectados son codigos cortos (snake_case) para que sean estables y filtrables desde JSON. La interpretacion humana vive en `cpp/PATTERNS.md`.
|
||||
Reference in New Issue
Block a user