-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvalidate-obsidian-plugin-manifest.go
166 lines (141 loc) · 4.68 KB
/
validate-obsidian-plugin-manifest.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
)
type Manifest struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Author string `json:"author"`
Version string `json:"version"`
MinAppVersion string `json:"minAppVersion"`
IsDesktopOnly bool `json:"isDesktopOnly"`
AuthorURL string `json:"authorUrl,omitempty"`
FundingURL string `json:"fundingUrl,omitempty"`
}
type ValidationResult struct {
Errors []string
Warnings []string
}
func (vr *ValidationResult) addError(format string, args ...interface{}) {
vr.Errors = append(vr.Errors, fmt.Sprintf(format, args...))
}
func (vr *ValidationResult) addWarning(format string, args ...interface{}) {
vr.Warnings = append(vr.Warnings, fmt.Sprintf(format, args...))
}
func (vr *ValidationResult) isValid() bool {
return len(vr.Errors) == 0
}
func validateManifest(manifest *Manifest) ValidationResult {
result := ValidationResult{}
// Validate ID
if strings.Contains(strings.ToLower(manifest.ID), "obsidian") {
result.addError("Plugin ID should not contain the word 'obsidian'")
}
if strings.HasSuffix(strings.ToLower(manifest.ID), "plugin") {
result.addError("Plugin ID should not end with 'plugin'")
}
if !regexp.MustCompile(`^[a-z0-9-_]+$`).MatchString(manifest.ID) {
result.addError("Plugin ID must contain only lowercase alphanumeric characters and dashes")
}
// Validate Name
if strings.Contains(strings.ToLower(manifest.Name), "obsidian") {
result.addError("Plugin name should not contain the word 'Obsidian'")
}
if strings.HasSuffix(strings.ToLower(manifest.Name), "plugin") {
result.addError("Plugin name should not end with 'Plugin'")
}
// Validate Description
if strings.Contains(strings.ToLower(manifest.Description), "obsidian") {
result.addError("Description should not contain the word 'Obsidian'")
}
if strings.Contains(strings.ToLower(manifest.Description), "this plugin") {
result.addWarning("Avoid phrases like 'this plugin' in the description")
}
if len(manifest.Description) > 250 {
result.addError("Description should be under 250 characters (currently %d)", len(manifest.Description))
}
// Validate Author
emailRegex := regexp.MustCompile(`^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$`)
if emailRegex.MatchString(manifest.Author) {
result.addWarning("Email addresses are discouraged in the author field")
}
// Validate Version
if !regexp.MustCompile(`^[0-9.]+$`).MatchString(manifest.Version) {
result.addError("Invalid version number format (should only contain numbers and dots)")
}
// Validate URLs
if manifest.AuthorURL == "https://obsidian.md" {
result.addError("Author URL should not point to Obsidian website")
}
if manifest.FundingURL != "" {
if manifest.FundingURL == "https://obsidian.md/pricing" {
result.addError("Funding URL should not point to Obsidian pricing")
}
}
// Validate MinAppVersion
if manifest.MinAppVersion == "" {
result.addError("MinAppVersion is required")
} else if !regexp.MustCompile(`^[0-9.]+$`).MatchString(manifest.MinAppVersion) {
result.addError("Invalid minAppVersion format (should only contain numbers and dots)")
}
return result
}
func printResults(result ValidationResult) {
if len(result.Errors) > 0 {
fmt.Println("\n❌ Errors:")
for _, err := range result.Errors {
fmt.Printf(" • %s\n", err)
}
}
if len(result.Warnings) > 0 {
fmt.Println("\n⚠️ Warnings:")
for _, warning := range result.Warnings {
fmt.Printf(" • %s\n", warning)
}
}
if result.isValid() {
fmt.Println("\n✅ Manifest validation passed!")
if len(result.Warnings) > 0 {
fmt.Printf(" (but has %d warning(s) to consider)\n", len(result.Warnings))
}
} else {
fmt.Printf("\n❌ Validation failed with %d error(s) and %d warning(s)\n",
len(result.Errors), len(result.Warnings))
}
}
func main() {
var manifestPath string
flag.StringVar(&manifestPath, "manifest", "manifest.json", "Path to manifest.json file")
flag.Parse()
if !filepath.IsAbs(manifestPath) {
cwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting current directory: %v\n", err)
os.Exit(1)
}
manifestPath = filepath.Join(cwd, manifestPath)
}
data, err := os.ReadFile(manifestPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading manifest.json: %v\n", err)
os.Exit(1)
}
var manifest Manifest
if err := json.Unmarshal(data, &manifest); err != nil {
fmt.Fprintf(os.Stderr, "Error parsing manifest.json: %v\n", err)
os.Exit(1)
}
fmt.Printf("📝 Validating manifest for plugin: %s\n", manifest.Name)
result := validateManifest(&manifest)
printResults(result)
if !result.isValid() {
os.Exit(1)
}
}