Fix lockfile generation, Key authoring hardening, and generic table injection
This commit is contained in:
@@ -264,6 +264,7 @@ func ValidateProject(p *project.Project) ValidationReport {
|
||||
})
|
||||
validateTopPackageAssets(sourceDir, dataDir, &report)
|
||||
validateNativeOutputCatalog(dataDir, &report)
|
||||
validateNativeLockAllocation(dataDir, &report)
|
||||
validateItempropsRegistryGraph(dataDir, &report)
|
||||
if _, err := os.Stat(statePath); err == nil {
|
||||
report.Files++
|
||||
@@ -892,6 +893,118 @@ func validateNativeOutputCatalog(dataDir string, report *ValidationReport) {
|
||||
}
|
||||
}
|
||||
|
||||
func validateNativeLockAllocation(dataDir string, report *ValidationReport) {
|
||||
datasets, err := discoverNativeDatasets(dataDir)
|
||||
if err != nil {
|
||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||
Severity: SeverityError,
|
||||
Path: dataDir,
|
||||
Message: fmt.Sprintf("discover native datasets for lock allocation validation: %v", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
for _, dataset := range datasets {
|
||||
if dataset.Kind != nativeDatasetBase {
|
||||
continue
|
||||
}
|
||||
baseObj, err := loadJSONObject(dataset.BasePath)
|
||||
if err != nil {
|
||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||
Severity: SeverityError,
|
||||
Path: dataset.BasePath,
|
||||
Message: fmt.Sprintf("load base rows for lock allocation validation: %v", err),
|
||||
})
|
||||
continue
|
||||
}
|
||||
rawRows, ok := baseObj["rows"].([]any)
|
||||
if !ok || len(rawRows) == 0 {
|
||||
continue
|
||||
}
|
||||
baseBoundaryID := len(rawRows) - 1
|
||||
baseKeys := map[string]struct{}{}
|
||||
for index, raw := range rawRows {
|
||||
row, ok := raw.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rowID := index
|
||||
if rawID, ok := row["id"]; ok {
|
||||
if parsed, err := asInt(rawID); err == nil {
|
||||
rowID = parsed
|
||||
}
|
||||
}
|
||||
baseBoundaryID = rowID
|
||||
if key, ok := row["key"].(string); ok && key != "" {
|
||||
baseKeys[key] = struct{}{}
|
||||
}
|
||||
}
|
||||
explicitModuleIDs := map[string]int{}
|
||||
for _, dir := range []string{dataset.ModulesDir, dataset.GeneratedDir} {
|
||||
paths, err := collectModulePaths(dir)
|
||||
if err != nil {
|
||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||
Severity: SeverityError,
|
||||
Path: dir,
|
||||
Message: fmt.Sprintf("collect module paths for lock allocation validation: %v", err),
|
||||
})
|
||||
continue
|
||||
}
|
||||
for _, path := range paths {
|
||||
obj, err := loadJSONObject(path)
|
||||
if err != nil {
|
||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||
Severity: SeverityError,
|
||||
Path: path,
|
||||
Message: fmt.Sprintf("load module for lock allocation validation: %v", err),
|
||||
})
|
||||
continue
|
||||
}
|
||||
ids, err := collectExplicitModuleIDs(dataset.Name, path, obj)
|
||||
if err != nil {
|
||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||
Severity: SeverityError,
|
||||
Path: path,
|
||||
Message: err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
for key, rowID := range ids {
|
||||
explicitModuleIDs[key] = rowID
|
||||
}
|
||||
}
|
||||
}
|
||||
lockData, err := loadLockfile(dataset.LockPath)
|
||||
if err != nil {
|
||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||
Severity: SeverityError,
|
||||
Path: dataset.LockPath,
|
||||
Message: fmt.Sprintf("load lockfile for allocation validation: %v", err),
|
||||
})
|
||||
continue
|
||||
}
|
||||
invalid := 0
|
||||
for key, rowID := range lockData {
|
||||
if rowID > baseBoundaryID {
|
||||
continue
|
||||
}
|
||||
if _, ok := baseKeys[key]; ok {
|
||||
continue
|
||||
}
|
||||
if explicitID, ok := explicitModuleIDs[key]; ok && explicitID == rowID {
|
||||
continue
|
||||
}
|
||||
invalid++
|
||||
}
|
||||
if invalid > 0 {
|
||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||
Severity: SeverityWarning,
|
||||
Path: dataset.LockPath,
|
||||
Message: fmt.Sprintf("%d lock entrie(s) are inside base id space and will be regenerated above row %d", invalid, baseBoundaryID),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateTopPackageAssets(sourceDir, dataDir string, report *ValidationReport) {
|
||||
assetsDir := filepath.Join(sourceDir, "assets")
|
||||
generated2DADir := filepath.Join(assetsDir, "2da")
|
||||
@@ -1031,6 +1144,16 @@ func validateNativeBuildability(p *project.Project, report *ValidationReport) {
|
||||
|
||||
clone := *p
|
||||
clone.Config.TopData.Build = tempBuild
|
||||
lockSnapshot, err := snapshotLockfiles(dataDir)
|
||||
if err != nil {
|
||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||
Severity: SeverityError,
|
||||
Path: dataDir,
|
||||
Message: fmt.Sprintf("snapshot lockfiles for validation build: %v", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer restoreLockfiles(lockSnapshot)
|
||||
if _, err := buildNativeUnchecked(&clone, nil, nil); err != nil {
|
||||
report.Diagnostics = append(report.Diagnostics, Diagnostic{
|
||||
Severity: SeverityError,
|
||||
@@ -1040,6 +1163,53 @@ func validateNativeBuildability(p *project.Project, report *ValidationReport) {
|
||||
}
|
||||
}
|
||||
|
||||
type lockfileSnapshot struct {
|
||||
Root string
|
||||
Files map[string][]byte
|
||||
}
|
||||
|
||||
func snapshotLockfiles(dataDir string) (lockfileSnapshot, error) {
|
||||
snapshot := lockfileSnapshot{
|
||||
Root: dataDir,
|
||||
Files: map[string][]byte{},
|
||||
}
|
||||
err := filepath.WalkDir(dataDir, func(path string, d fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
if d.IsDir() || filepath.Base(path) != "lock.json" {
|
||||
return nil
|
||||
}
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snapshot.Files[path] = raw
|
||||
return nil
|
||||
})
|
||||
return snapshot, err
|
||||
}
|
||||
|
||||
func restoreLockfiles(snapshot lockfileSnapshot) {
|
||||
seen := map[string]struct{}{}
|
||||
for path, raw := range snapshot.Files {
|
||||
seen[path] = struct{}{}
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
continue
|
||||
}
|
||||
_ = os.WriteFile(path, raw, 0o644)
|
||||
}
|
||||
_ = filepath.WalkDir(snapshot.Root, func(path string, d fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil || d.IsDir() || filepath.Base(path) != "lock.json" {
|
||||
return nil
|
||||
}
|
||||
if _, ok := seen[path]; !ok {
|
||||
_ = os.Remove(path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func validateRowCollection(path string, obj map[string]any, rows []any, report *ValidationReport) {
|
||||
columns := extractValidationColumns(obj)
|
||||
rowHasIdentity := func(row map[string]any, fallbackID int) (string, bool) {
|
||||
|
||||
Reference in New Issue
Block a user