202 lines
4.5 KiB
Go
202 lines
4.5 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
_ "image/png"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/omixlab/mosis-portal/pkg/mospkg"
|
|
)
|
|
|
|
// ValidateCmd returns the validate command
|
|
func ValidateCmd() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "validate [directory]",
|
|
Short: "Validate project manifest and assets",
|
|
Long: "Validate a Mosis project without building. Checks manifest.json, assets, and icons.",
|
|
Args: cobra.MaximumNArgs(1),
|
|
RunE: runValidate,
|
|
}
|
|
}
|
|
|
|
func runValidate(cmd *cobra.Command, args []string) error {
|
|
dir := "."
|
|
if len(args) > 0 {
|
|
dir = args[0]
|
|
}
|
|
|
|
hasErrors := false
|
|
|
|
// Validate manifest
|
|
fmt.Println("Validating manifest.json...")
|
|
manifest, errs := validateManifest(dir)
|
|
if len(errs) > 0 {
|
|
hasErrors = true
|
|
for _, e := range errs {
|
|
fmt.Printf("✗ %s\n", e)
|
|
}
|
|
} else {
|
|
fmt.Println("✓ Required fields present")
|
|
fmt.Println("✓ Package ID format valid")
|
|
fmt.Println("✓ Version format valid")
|
|
}
|
|
|
|
if manifest == nil {
|
|
return fmt.Errorf("\nValidation failed.")
|
|
}
|
|
|
|
// Validate assets
|
|
fmt.Println("\nValidating assets...")
|
|
assetErrs := validateAssets(dir, manifest)
|
|
if len(assetErrs) > 0 {
|
|
hasErrors = true
|
|
for _, e := range assetErrs {
|
|
fmt.Printf("✗ %s\n", e)
|
|
}
|
|
} else {
|
|
fmt.Printf("✓ Entry point exists: %s\n", manifest.Entry)
|
|
|
|
// Count files by type
|
|
rmlCount, rcssCount, luaCount := countAssetFiles(dir)
|
|
if rmlCount > 0 {
|
|
fmt.Printf("✓ Found %d RML files\n", rmlCount)
|
|
}
|
|
if rcssCount > 0 {
|
|
fmt.Printf("✓ Found %d RCSS files\n", rcssCount)
|
|
}
|
|
if luaCount > 0 {
|
|
fmt.Printf("✓ Found %d Lua files\n", luaCount)
|
|
}
|
|
}
|
|
|
|
// Validate icons
|
|
fmt.Println("\nValidating icons...")
|
|
iconErrs := validateIcons(dir, manifest)
|
|
if len(iconErrs) > 0 {
|
|
hasErrors = true
|
|
for _, e := range iconErrs {
|
|
fmt.Printf("✗ %s\n", e)
|
|
}
|
|
} else {
|
|
if manifest.Icons.Size32 != "" {
|
|
fmt.Printf("✓ icon-32.png\n")
|
|
}
|
|
if manifest.Icons.Size64 != "" {
|
|
fmt.Printf("✓ icon-64.png\n")
|
|
}
|
|
if manifest.Icons.Size128 != "" {
|
|
fmt.Printf("✓ icon-128.png\n")
|
|
}
|
|
}
|
|
|
|
// Check permissions
|
|
fmt.Println("\nChecking permissions...")
|
|
if len(manifest.Permissions) == 0 {
|
|
fmt.Println("✓ No permissions declared")
|
|
} else {
|
|
fmt.Printf("✓ Permissions declared: %s\n", strings.Join(manifest.Permissions, ", "))
|
|
}
|
|
|
|
if hasErrors {
|
|
return fmt.Errorf("\nValidation failed with errors.")
|
|
}
|
|
|
|
fmt.Println("\nAll validations passed!")
|
|
return nil
|
|
}
|
|
|
|
func validateManifest(dir string) (*mospkg.Manifest, []string) {
|
|
manifestPath := filepath.Join(dir, "manifest.json")
|
|
data, err := os.ReadFile(manifestPath)
|
|
if err != nil {
|
|
return nil, []string{"manifest.json not found"}
|
|
}
|
|
|
|
manifest, err := mospkg.ParseManifest(data)
|
|
if err != nil {
|
|
return nil, []string{fmt.Sprintf("Invalid manifest.json: %v", err)}
|
|
}
|
|
|
|
validationErrs := manifest.Validate()
|
|
var errs []string
|
|
for _, e := range validationErrs {
|
|
errs = append(errs, e.Message)
|
|
}
|
|
|
|
if len(errs) > 0 {
|
|
return manifest, errs
|
|
}
|
|
|
|
return manifest, nil
|
|
}
|
|
|
|
func validateAssets(dir string, manifest *mospkg.Manifest) []string {
|
|
var errs []string
|
|
|
|
// Check entry point exists
|
|
entryPath := filepath.Join(dir, manifest.Entry)
|
|
if _, err := os.Stat(entryPath); os.IsNotExist(err) {
|
|
errs = append(errs, fmt.Sprintf("Entry point not found: %s", manifest.Entry))
|
|
}
|
|
|
|
return errs
|
|
}
|
|
|
|
func countAssetFiles(dir string) (rml, rcss, lua int) {
|
|
assetsDir := filepath.Join(dir, "assets")
|
|
filepath.Walk(assetsDir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil || info.IsDir() {
|
|
return nil
|
|
}
|
|
switch strings.ToLower(filepath.Ext(path)) {
|
|
case ".rml":
|
|
rml++
|
|
case ".rcss":
|
|
rcss++
|
|
case ".lua":
|
|
lua++
|
|
}
|
|
return nil
|
|
})
|
|
return
|
|
}
|
|
|
|
func validateIcons(dir string, manifest *mospkg.Manifest) []string {
|
|
var errs []string
|
|
|
|
checkIcon := func(path string, expectedSize int) {
|
|
if path == "" {
|
|
return
|
|
}
|
|
fullPath := filepath.Join(dir, path)
|
|
f, err := os.Open(fullPath)
|
|
if err != nil {
|
|
errs = append(errs, fmt.Sprintf("Icon not found: %s", path))
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
img, _, err := image.DecodeConfig(f)
|
|
if err != nil {
|
|
errs = append(errs, fmt.Sprintf("Invalid icon format: %s", path))
|
|
return
|
|
}
|
|
|
|
if img.Width != expectedSize || img.Height != expectedSize {
|
|
errs = append(errs, fmt.Sprintf("Icon %s should be %dx%d, got %dx%d",
|
|
path, expectedSize, expectedSize, img.Width, img.Height))
|
|
}
|
|
}
|
|
|
|
checkIcon(manifest.Icons.Size32, 32)
|
|
checkIcon(manifest.Icons.Size64, 64)
|
|
checkIcon(manifest.Icons.Size128, 128)
|
|
|
|
return errs
|
|
}
|