Enable plain TTY logging mode + loose-data processing
This commit is contained in:
+17
-3
@@ -139,6 +139,20 @@ func isInteractiveTTY(w io.Writer) bool {
|
|||||||
return (info.Mode() & os.ModeCharDevice) != 0
|
return (info.Mode() & os.ModeCharDevice) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func spinnerEnabledFor(w io.Writer, level logLevel) bool {
|
||||||
|
mode := strings.TrimSpace(strings.ToLower(os.Getenv("SOW_TOOLS_TTY_MODE")))
|
||||||
|
switch mode {
|
||||||
|
case "plain", "off", "0", "false", "no":
|
||||||
|
return false
|
||||||
|
case "spinner", "on", "1", "true", "yes":
|
||||||
|
return level == logLevelNormal && isInteractiveTTY(w)
|
||||||
|
case "", "auto":
|
||||||
|
return isInteractiveTTY(w) && strings.TrimSpace(os.Getenv("CI")) == "" && level == logLevelNormal
|
||||||
|
default:
|
||||||
|
return isInteractiveTTY(w) && strings.TrimSpace(os.Getenv("CI")) == "" && level == logLevelNormal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var commands = []command{
|
var commands = []command{
|
||||||
{
|
{
|
||||||
name: "build",
|
name: "build",
|
||||||
@@ -605,7 +619,7 @@ func newProjectConsole(ctx context, p *project.Project, commandName string) *pro
|
|||||||
commandName: commandName,
|
commandName: commandName,
|
||||||
commandLabel: projectCommandLabel(commandName),
|
commandLabel: projectCommandLabel(commandName),
|
||||||
level: ctx.logLevel,
|
level: ctx.logLevel,
|
||||||
spinnerEnabled: isInteractiveTTY(ctx.stderr) && strings.TrimSpace(os.Getenv("CI")) == "" && ctx.logLevel == logLevelNormal,
|
spinnerEnabled: spinnerEnabledFor(ctx.stderr, ctx.logLevel),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -787,7 +801,7 @@ func newBuildHAKConsole(ctx context, p *project.Project, opts buildHAKOptions) *
|
|||||||
projectName: p.Config.Module.Name,
|
projectName: p.Config.Module.Name,
|
||||||
planOnly: opts.planOnly,
|
planOnly: opts.planOnly,
|
||||||
level: opts.logLevel,
|
level: opts.logLevel,
|
||||||
spinnerEnabled: isInteractiveTTY(ctx.stderr) && strings.TrimSpace(os.Getenv("CI")) == "" && opts.logLevel == logLevelNormal,
|
spinnerEnabled: spinnerEnabledFor(ctx.stderr, opts.logLevel),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -962,7 +976,7 @@ func newTopdataConsole(ctx context, p *project.Project, commandName string) *top
|
|||||||
commandName: commandName,
|
commandName: commandName,
|
||||||
commandLabel: topdataCommandLabel(commandName),
|
commandLabel: topdataCommandLabel(commandName),
|
||||||
level: ctx.logLevel,
|
level: ctx.logLevel,
|
||||||
spinnerEnabled: isInteractiveTTY(ctx.stderr) && strings.TrimSpace(os.Getenv("CI")) == "" && ctx.logLevel == logLevelNormal,
|
spinnerEnabled: spinnerEnabledFor(ctx.stderr, ctx.logLevel),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ topdata:
|
|||||||
|
|
||||||
sourceTime := time.Now().Add(-2 * time.Hour)
|
sourceTime := time.Now().Add(-2 * time.Hour)
|
||||||
outputTime := time.Now().Add(-1 * time.Hour)
|
outputTime := time.Now().Add(-1 * time.Hour)
|
||||||
setFileTime(t, filepath.Join(root, "topdata", "assets", "gui", "testicon.png"), sourceTime)
|
setTreeTime(t, filepath.Join(root, "topdata"), sourceTime)
|
||||||
setFileTime(t, filepath.Join(root, "topdata", "data", "repadjust", "base.json"), sourceTime)
|
|
||||||
setFileTime(t, filepath.Join(root, ".cache", "2da", "repadjust.2da"), outputTime)
|
setFileTime(t, filepath.Join(root, ".cache", "2da", "repadjust.2da"), outputTime)
|
||||||
setFileTime(t, filepath.Join(root, "build", "sow_tlk.tlk"), outputTime)
|
setFileTime(t, filepath.Join(root, "build", "sow_tlk.tlk"), outputTime)
|
||||||
|
|
||||||
@@ -130,6 +129,19 @@ haks:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setTreeTime(t *testing.T, root string, modTime time.Time) {
|
||||||
|
t.Helper()
|
||||||
|
err := filepath.WalkDir(root, func(path string, d os.DirEntry, walkErr error) error {
|
||||||
|
if walkErr != nil {
|
||||||
|
return walkErr
|
||||||
|
}
|
||||||
|
return os.Chtimes(path, modTime, modTime)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("set tree time under %s: %v", root, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunBuildHAKsVerboseListsMappings(t *testing.T) {
|
func TestRunBuildHAKsVerboseListsMappings(t *testing.T) {
|
||||||
root := t.TempDir()
|
root := t.TempDir()
|
||||||
mkdirAll(t, filepath.Join(root, "assets", "envi", "music", "westgate"))
|
mkdirAll(t, filepath.Join(root, "assets", "envi", "music", "westgate"))
|
||||||
@@ -212,6 +224,13 @@ func TestTopdataConsoleSuppressesProgressInNormalMode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSpinnerEnabledForHonorsPlainTTYMode(t *testing.T) {
|
||||||
|
t.Setenv("SOW_TOOLS_TTY_MODE", "plain")
|
||||||
|
if spinnerEnabledFor(&bytes.Buffer{}, logLevelNormal) {
|
||||||
|
t.Fatal("expected plain tty mode to disable spinner output")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTopdataConsoleDebugProgressAndRelativePaths(t *testing.T) {
|
func TestTopdataConsoleDebugProgressAndRelativePaths(t *testing.T) {
|
||||||
var stdout bytes.Buffer
|
var stdout bytes.Buffer
|
||||||
console := &topdataConsole{
|
console := &topdataConsole{
|
||||||
|
|||||||
+39
-16
@@ -503,6 +503,27 @@ func countModuleFiles(dir string) int {
|
|||||||
|
|
||||||
func discoverNativeDatasets(dataDir string) ([]nativeDataset, error) {
|
func discoverNativeDatasets(dataDir string) ([]nativeDataset, error) {
|
||||||
var datasets []nativeDataset
|
var datasets []nativeDataset
|
||||||
|
appendPlainDataset := func(rootPath, filePath, relName string) error {
|
||||||
|
tableData, err := loadJSONObject(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
outputName, _ := tableData["output"].(string)
|
||||||
|
if strings.TrimSpace(outputName) == "" {
|
||||||
|
outputName = nativeDatasetDefaultOutputName(filePath, nativeDatasetPlain)
|
||||||
|
}
|
||||||
|
datasets = append(datasets, nativeDataset{
|
||||||
|
Kind: nativeDatasetPlain,
|
||||||
|
Name: filepath.ToSlash(relName),
|
||||||
|
RootPath: rootPath,
|
||||||
|
BasePath: filePath,
|
||||||
|
LockPath: filepath.Join(rootPath, "lock.json"),
|
||||||
|
OutputName: outputName,
|
||||||
|
Spec: specForDataset(filepath.ToSlash(relName)),
|
||||||
|
CompareReference: optionalBool(tableData["compare_reference"], true),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
err := filepath.WalkDir(dataDir, func(path string, d fs.DirEntry, err error) error {
|
err := filepath.WalkDir(dataDir, func(path string, d fs.DirEntry, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -519,6 +540,23 @@ func discoverNativeDatasets(dataDir string) ([]nativeDataset, error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if rel == "." {
|
if rel == "." {
|
||||||
|
entries, err := os.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := entry.Name()
|
||||||
|
if !strings.HasSuffix(strings.ToLower(name), ".json") || name == "lock.json" || name == "base.json" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filePath := filepath.Join(path, name)
|
||||||
|
if err := appendPlainDataset(path, filePath, strings.TrimSuffix(name, filepath.Ext(name))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if filepath.ToSlash(rel) == "parts/overrides" {
|
if filepath.ToSlash(rel) == "parts/overrides" {
|
||||||
@@ -564,24 +602,9 @@ func discoverNativeDatasets(dataDir string) ([]nativeDataset, error) {
|
|||||||
name := entry.Name()
|
name := entry.Name()
|
||||||
if strings.HasSuffix(strings.ToLower(name), ".json") && name != "lock.json" {
|
if strings.HasSuffix(strings.ToLower(name), ".json") && name != "lock.json" {
|
||||||
filePath := filepath.Join(path, name)
|
filePath := filepath.Join(path, name)
|
||||||
tableData, err := loadJSONObject(filePath)
|
if err := appendPlainDataset(path, filePath, filepath.Join(rel, strings.TrimSuffix(name, filepath.Ext(name)))); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
outputName, _ := tableData["output"].(string)
|
|
||||||
if strings.TrimSpace(outputName) == "" {
|
|
||||||
outputName = nativeDatasetDefaultOutputName(filePath, nativeDatasetPlain)
|
|
||||||
}
|
|
||||||
datasets = append(datasets, nativeDataset{
|
|
||||||
Kind: nativeDatasetPlain,
|
|
||||||
Name: filepath.ToSlash(filepath.Join(rel, strings.TrimSuffix(name, filepath.Ext(name)))),
|
|
||||||
RootPath: path,
|
|
||||||
BasePath: filePath,
|
|
||||||
LockPath: filepath.Join(path, "lock.json"),
|
|
||||||
OutputName: outputName,
|
|
||||||
Spec: specForDataset(filepath.ToSlash(filepath.Join(rel, strings.TrimSuffix(name, filepath.Ext(name))))),
|
|
||||||
CompareReference: optionalBool(tableData["compare_reference"], true),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -267,6 +267,9 @@ func newestTopDataSource(p *project.Project) (time.Time, string, error) {
|
|||||||
if d.IsDir() && shouldSkipTopDataSourceDir(path, skipDirs) {
|
if d.IsDir() && shouldSkipTopDataSourceDir(path, skipDirs) {
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
|
if path == sourceDir {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
info, err := d.Info()
|
info, err := d.Info()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -6892,7 +6892,7 @@ func TestBuildGeneratedApplyAfterPrunesLowLockedID(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildOverrideNullValueKeyNullifiesEntireRow(t *testing.T) {
|
func TestBuildOverrideAsteriskKeyRemovesIdentityWithoutNullingRow(t *testing.T) {
|
||||||
root := testProjectRoot(t)
|
root := testProjectRoot(t)
|
||||||
mkdirAll(t, filepath.Join(root, "topdata", "data", "dense", "modules"))
|
mkdirAll(t, filepath.Join(root, "topdata", "data", "dense", "modules"))
|
||||||
writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
|
writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
|
||||||
@@ -6925,8 +6925,8 @@ func TestBuildOverrideNullValueKeyNullifiesEntireRow(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("read dense.2da: %v", err)
|
t.Fatalf("read dense.2da: %v", err)
|
||||||
}
|
}
|
||||||
if !strings.Contains(string(got), "7\t****\t****\n") {
|
if !strings.Contains(string(got), "7\tShouldNotLeak\tstill-not-kept\n") {
|
||||||
t.Fatalf("expected override key \"****\" to nullify row 7, got:\n%s", string(got))
|
t.Fatalf("expected override key \"****\" to remove identity without blanking row 7, got:\n%s", string(got))
|
||||||
}
|
}
|
||||||
lockRaw, err := os.ReadFile(filepath.Join(root, "topdata", "data", "dense", "lock.json"))
|
lockRaw, err := os.ReadFile(filepath.Join(root, "topdata", "data", "dense", "lock.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -11084,13 +11084,13 @@ func setTreeFileTimes(t *testing.T, root string, modTime time.Time) {
|
|||||||
if walkErr != nil {
|
if walkErr != nil {
|
||||||
return walkErr
|
return walkErr
|
||||||
}
|
}
|
||||||
if d.IsDir() {
|
if err := os.Chtimes(path, modTime, modTime); err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
return os.Chtimes(path, modTime, modTime)
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("set file times under %s: %v", root, err)
|
t.Fatalf("set tree times under %s: %v", root, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11302,6 +11302,34 @@ func TestValidateProjectWarnsWhenLockKeepsNormalizedEquivalentKeysAlive(t *testi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBuildNativeSupportsLoosePlainTableAtTopdataDataRoot(t *testing.T) {
|
||||||
|
root := testProjectRoot(t)
|
||||||
|
mkdirAll(t, filepath.Join(root, "topdata", "data"))
|
||||||
|
writeFile(t, filepath.Join(root, "topdata", "base_dialog.json"), "{}\n")
|
||||||
|
writeFile(t, filepath.Join(root, "topdata", "data", "ruleset.json"), `{
|
||||||
|
"columns": ["Name", "Value"],
|
||||||
|
"rows": [
|
||||||
|
{"id": 0, "Name": "TEST_RULE", "Value": "1"}
|
||||||
|
]
|
||||||
|
}`+"\n")
|
||||||
|
|
||||||
|
proj := testProject(root)
|
||||||
|
proj.Config.TopData.ReferenceBuilder = ""
|
||||||
|
|
||||||
|
report := ValidateProject(proj)
|
||||||
|
if report.HasErrors() {
|
||||||
|
t.Fatalf("expected loose root table to validate cleanly, got:\n%s", diagnosticsText(report.Diagnostics))
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := BuildNativeWithOptions(proj, NativeBuildOptions{BuildWiki: false}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expected loose root table to build: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(result.Output2DADir, "ruleset.2da")); err != nil {
|
||||||
|
t.Fatalf("expected ruleset.2da output for loose root table: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateProjectErrorsOnTopPackageAssetCollision(t *testing.T) {
|
func TestValidateProjectErrorsOnTopPackageAssetCollision(t *testing.T) {
|
||||||
root := testProjectRoot(t)
|
root := testProjectRoot(t)
|
||||||
mkdirAll(t, filepath.Join(root, "topdata", "data", "repadjust"))
|
mkdirAll(t, filepath.Join(root, "topdata", "data", "repadjust"))
|
||||||
|
|||||||
Reference in New Issue
Block a user