feat: externalize apps/analysis to Gitea repos, add analysis table
- Migration 007: repo_url on apps table + analysis table with FTS5 - Analysis struct, parser, CRUD, validation, hash computation - Selective purge: remote-only apps/analysis preserved across fn index - CLI: fn app list/clone/pull, fn analysis list/clone/pull - search/show/list now include analysis results - Apps removed from git tracking (content lives in Gitea repos) - .gitkeep for apps/ and analysis/ dirs - Bash functions: jupyter analysis pipeline, shell utilities - Browser domain: CDP functions moved from infra to browser Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -74,6 +74,22 @@ type rawApp struct {
|
||||
Framework string `yaml:"framework"`
|
||||
EntryPoint string `yaml:"entry_point"`
|
||||
DirPath string `yaml:"dir_path"`
|
||||
RepoURL string `yaml:"repo_url"`
|
||||
}
|
||||
|
||||
// rawAnalysis mirrors the YAML frontmatter of an analysis .md file.
|
||||
type rawAnalysis struct {
|
||||
Name string `yaml:"name"`
|
||||
Lang string `yaml:"lang"`
|
||||
Domain string `yaml:"domain"`
|
||||
Description string `yaml:"description"`
|
||||
Tags []string `yaml:"tags"`
|
||||
UsesFunctions []string `yaml:"uses_functions"`
|
||||
UsesTypes []string `yaml:"uses_types"`
|
||||
Framework string `yaml:"framework"`
|
||||
EntryPoint string `yaml:"entry_point"`
|
||||
DirPath string `yaml:"dir_path"`
|
||||
RepoURL string `yaml:"repo_url"`
|
||||
}
|
||||
|
||||
// extractFrontmatter splits a .md file into YAML frontmatter and body.
|
||||
@@ -266,11 +282,58 @@ func ParseAppMD(path string, root string) (*App, error) {
|
||||
Documentation: sections.documentation,
|
||||
Notes: sections.notes,
|
||||
DirPath: raw.DirPath,
|
||||
RepoURL: raw.RepoURL,
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// ParseAnalysisMD parses an analysis .md file into an Analysis.
|
||||
func ParseAnalysisMD(path string, root string) (*Analysis, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %s: %w", path, err)
|
||||
}
|
||||
|
||||
fm, body, err := extractFrontmatter(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s: %w", path, err)
|
||||
}
|
||||
|
||||
var raw rawAnalysis
|
||||
if err := yaml.Unmarshal(fm, &raw); err != nil {
|
||||
return nil, fmt.Errorf("parsing YAML in %s: %w", path, err)
|
||||
}
|
||||
|
||||
if raw.Name == "" {
|
||||
return nil, fmt.Errorf("%s: name is required", path)
|
||||
}
|
||||
if raw.Description == "" {
|
||||
return nil, fmt.Errorf("%s: description is required", path)
|
||||
}
|
||||
|
||||
sections := extractSections(body)
|
||||
|
||||
an := &Analysis{
|
||||
ID: GenerateID(raw.Name, raw.Lang, raw.Domain),
|
||||
Name: raw.Name,
|
||||
Lang: raw.Lang,
|
||||
Domain: raw.Domain,
|
||||
Description: raw.Description,
|
||||
Tags: raw.Tags,
|
||||
UsesFunctions: raw.UsesFunctions,
|
||||
UsesTypes: raw.UsesTypes,
|
||||
Framework: raw.Framework,
|
||||
EntryPoint: raw.EntryPoint,
|
||||
Documentation: sections.documentation,
|
||||
Notes: sections.notes,
|
||||
DirPath: raw.DirPath,
|
||||
RepoURL: raw.RepoURL,
|
||||
}
|
||||
|
||||
return an, nil
|
||||
}
|
||||
|
||||
// bodySections holds the extracted sections from a .md body.
|
||||
type bodySections struct {
|
||||
example string // content under ## Ejemplo
|
||||
|
||||
Reference in New Issue
Block a user