Add GSP project manifest and design layout
This commit is contained in:
@@ -28,26 +28,45 @@ func LoadProject(root string) (*Project, error) {
|
||||
ByID: map[string]*Unit{},
|
||||
Duplicates: map[string][]*Unit{},
|
||||
}
|
||||
manifest, err := loadManifest(absRoot)
|
||||
if err != nil {
|
||||
project.LoadIssues = append(project.LoadIssues, Issue{Level: "error", Code: "manifest_error", File: "gsp.manifest", Message: err.Error()})
|
||||
}
|
||||
project.Manifest = manifest
|
||||
|
||||
var files []string
|
||||
err = filepath.WalkDir(absRoot, func(path string, entry os.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
project.LoadIssues = append(project.LoadIssues, Issue{Level: "error", Code: "walk_error", File: path, Message: walkErr.Error()})
|
||||
return nil
|
||||
for _, scanEntry := range project.Manifest.scanEntries() {
|
||||
scanPath := filepath.Join(absRoot, filepath.FromSlash(scanEntry))
|
||||
info, statErr := os.Stat(scanPath)
|
||||
if statErr != nil {
|
||||
project.LoadIssues = append(project.LoadIssues, Issue{Level: "warning", Code: "scan_missing", File: scanEntry, Message: "scan path does not exist"})
|
||||
continue
|
||||
}
|
||||
if entry.IsDir() {
|
||||
if ignoredDirs[entry.Name()] {
|
||||
return filepath.SkipDir
|
||||
if !info.IsDir() {
|
||||
if strings.EqualFold(filepath.Ext(scanPath), ".gsp") {
|
||||
files = append(files, scanPath)
|
||||
}
|
||||
continue
|
||||
}
|
||||
walkErr := filepath.WalkDir(scanPath, func(path string, entry os.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
project.LoadIssues = append(project.LoadIssues, Issue{Level: "error", Code: "walk_error", File: path, Message: walkErr.Error()})
|
||||
return nil
|
||||
}
|
||||
if entry.IsDir() {
|
||||
if ignoredDirs[entry.Name()] {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if strings.EqualFold(filepath.Ext(entry.Name()), ".gsp") {
|
||||
files = append(files, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if walkErr != nil {
|
||||
return nil, walkErr
|
||||
}
|
||||
if strings.EqualFold(filepath.Ext(entry.Name()), ".gsp") {
|
||||
files = append(files, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Strings(files)
|
||||
|
||||
|
||||
58
toolkit/internal/gsp/manifest.go
Normal file
58
toolkit/internal/gsp/manifest.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package gsp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Manifest struct {
|
||||
GSPVersion string `json:"gspVersion,omitempty" yaml:"gspVersion"`
|
||||
ToolkitVersion string `json:"toolkitVersion,omitempty" yaml:"toolkitVersion"`
|
||||
Project string `json:"project,omitempty" yaml:"project"`
|
||||
Entry []string `json:"entry,omitempty" yaml:"entry"`
|
||||
Scan []string `json:"scan,omitempty" yaml:"scan"`
|
||||
StageRules map[string]StageRule `json:"stageRules,omitempty" yaml:"stageRules"`
|
||||
Types []string `json:"types,omitempty" yaml:"types"`
|
||||
Output string `json:"output,omitempty" yaml:"output"`
|
||||
File string `json:"file,omitempty" yaml:"-"`
|
||||
}
|
||||
|
||||
type StageRule struct {
|
||||
MinResolution string `json:"minResolution,omitempty" yaml:"minResolution"`
|
||||
}
|
||||
|
||||
func loadManifest(root string) (*Manifest, error) {
|
||||
path := filepath.Join(root, "gsp.manifest")
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var manifest Manifest
|
||||
if err := yaml.Unmarshal(data, &manifest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
manifest.File = relPath(root, path)
|
||||
return &manifest, nil
|
||||
}
|
||||
|
||||
func (m *Manifest) scanEntries() []string {
|
||||
if m == nil || len(m.Scan) == 0 {
|
||||
return []string{"design"}
|
||||
}
|
||||
return m.Scan
|
||||
}
|
||||
|
||||
func (m *Manifest) minResolution(stage string) (string, bool) {
|
||||
if m != nil && m.StageRules != nil {
|
||||
if rule, ok := m.StageRules[stage]; ok && rule.MinResolution != "" {
|
||||
return rule.MinResolution, true
|
||||
}
|
||||
}
|
||||
value, ok := defaultStageRules[stage]
|
||||
return value, ok
|
||||
}
|
||||
@@ -96,6 +96,7 @@ func (r *Report) addNotice(code, id, file, message string) {
|
||||
|
||||
type Project struct {
|
||||
Root string
|
||||
Manifest *Manifest
|
||||
Units []*Unit
|
||||
ByID map[string]*Unit
|
||||
Duplicates map[string][]*Unit
|
||||
|
||||
@@ -9,8 +9,15 @@ import (
|
||||
func (p *Project) Validate(stage string) Report {
|
||||
report := Report{OK: true}
|
||||
for _, issue := range p.LoadIssues {
|
||||
report.Errors = append(report.Errors, issue)
|
||||
report.OK = false
|
||||
switch issue.Level {
|
||||
case "error":
|
||||
report.Errors = append(report.Errors, issue)
|
||||
report.OK = false
|
||||
case "warning":
|
||||
report.Warnings = append(report.Warnings, issue)
|
||||
default:
|
||||
report.Notices = append(report.Notices, issue)
|
||||
}
|
||||
}
|
||||
for _, unit := range p.Units {
|
||||
if unit.ID == "" {
|
||||
@@ -81,15 +88,17 @@ func (p *Project) Index() []IndexEntry {
|
||||
return entries
|
||||
}
|
||||
|
||||
var defaultStageRules = map[string]string{
|
||||
"design": "L0",
|
||||
"integrate": "L2",
|
||||
"implement": "L3",
|
||||
"bind": "L4",
|
||||
"release": "L5",
|
||||
}
|
||||
|
||||
func (p *Project) StageCheck(stage string) Report {
|
||||
report := Report{OK: true}
|
||||
required, ok := map[string]string{
|
||||
"design": "L0",
|
||||
"integrate": "L2",
|
||||
"implement": "L3",
|
||||
"bind": "L4",
|
||||
"release": "L5",
|
||||
}[stage]
|
||||
required, ok := p.Manifest.minResolution(stage)
|
||||
if !ok {
|
||||
report.addError("unknown_stage", "", "", fmt.Sprintf("unknown stage %q", stage))
|
||||
return report
|
||||
|
||||
@@ -8,7 +8,11 @@ import (
|
||||
|
||||
func TestLoadValidateAndFlatten(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeTestFile(t, root, "page.gsp", `id: page.lottery.main
|
||||
design := filepath.Join(root, "design")
|
||||
if err := os.MkdirAll(design, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeTestFile(t, design, "page.gsp", `id: page.lottery.main
|
||||
type: page
|
||||
resolution: L3
|
||||
context: Lottery page.
|
||||
@@ -18,17 +22,17 @@ with:
|
||||
- feedback.positive
|
||||
refines: page.lottery.base
|
||||
`)
|
||||
writeTestFile(t, root, "button.gsp", `id: ui.button.primary
|
||||
writeTestFile(t, design, "button.gsp", `id: ui.button.primary
|
||||
type: ui
|
||||
resolution: L3
|
||||
context: Primary button.
|
||||
`)
|
||||
writeTestFile(t, root, "feedback.gsp", `id: feedback.positive
|
||||
writeTestFile(t, design, "feedback.gsp", `id: feedback.positive
|
||||
type: feedback
|
||||
resolution: L2
|
||||
context: Positive feedback.
|
||||
`)
|
||||
writeTestFile(t, root, "base.gsp", `id: page.lottery.base
|
||||
writeTestFile(t, design, "base.gsp", `id: page.lottery.base
|
||||
resolution: L2
|
||||
context: Base lottery page.
|
||||
`)
|
||||
@@ -52,7 +56,11 @@ context: Base lottery page.
|
||||
|
||||
func TestValidateMissingReference(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeTestFile(t, root, "page.gsp", `id: page.missing
|
||||
design := filepath.Join(root, "design")
|
||||
if err := os.MkdirAll(design, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeTestFile(t, design, "page.gsp", `id: page.missing
|
||||
with:
|
||||
- ui.missing
|
||||
`)
|
||||
@@ -77,7 +85,11 @@ with:
|
||||
|
||||
func TestStageCheck(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeTestFile(t, root, "page.gsp", `id: page.low
|
||||
design := filepath.Join(root, "design")
|
||||
if err := os.MkdirAll(design, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeTestFile(t, design, "page.gsp", `id: page.low
|
||||
resolution: L2
|
||||
`)
|
||||
project, err := LoadProject(root)
|
||||
|
||||
Reference in New Issue
Block a user