v0.35.2 lwconf, doc and deploy update
Package / arch (push) Canceled after 0s
Package / user (push) Canceled after 0s
Package / deb (push) Canceled after 0s
Package / freebsd (push) Canceled after 0s
Package / image (push) Canceled after 0s
Test / test (push) Canceled after 0s
Test / e2e (push) Canceled after 0s

This commit is contained in:
lixen committed 2026-10-13 14:23:37 -04:00
1 parent 0b8701ec13
commit de87b9a008
21 files changed
+255 -185

No files matched your search

+25 -10
View File
@@ -61,13 +61,21 @@ func FromPreset(name string, values map[string]string, isDir config.IsDir) (*Com
// FromCommandLine starts from a pasted lw command line, quoted as a POSIX
// shell reads it; it takes pipeline flags only, at least one, as a composition
// holds no settings
// holds no settings. Of a line piped into lw it reads lw's part, whose
// pipelines read standard input.
func FromCommandLine(line string, isDir config.IsDir) (*Composition, error) {
words, err := split(strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(line)) // pasted line ends
if err != nil {
return nil, err
}
if len(words) > 0 && (words[0] == "lw" || strings.HasSuffix(words[0], "/lw")) {
lw := func(word string) bool { return word == "lw" || strings.HasSuffix(word, "/lw") }
for i := len(words) - 2; i >= 0; i-- {
if words[i] == "|" && lw(words[i+1]) {
words = words[i+1:]
break
}
}
if len(words) > 0 && lw(words[0]) {
words = words[1:]
}
var specs []config.Spec
@@ -77,8 +85,12 @@ func FromCommandLine(line string, isDir config.IsDir) (*Composition, error) {
name = "--preset"
}
flag, long := strings.CutPrefix(name, "--")
if !long || !slices.ContainsFunc(config.SpecFlags(), func(f config.SpecFlag) bool { return f.Flag == flag }) {
return nil, fmt.Errorf("%s: not a pipeline flag", words[i])
if flags := config.SpecFlags(); !long || !slices.ContainsFunc(flags, func(f config.SpecFlag) bool { return f.Flag == flag }) {
var names []string
for _, f := range flags {
names = append(names, "--"+f.Flag)
}
return nil, fmt.Errorf("%s: not a pipeline flag; paste lw's %s only", words[i], strings.Join(names, ", "))
}
if !inline {
if i++; i == len(words) || strings.HasPrefix(words[i], "-") {
@@ -205,8 +217,11 @@ func (c *Composition) RenamePipeline(i int, name string) error {
// free refuses a name that is empty or held by a pipeline other than i
func (c *Composition) free(name string, i int) error {
if j := slices.IndexFunc(c.Pipelines, func(p config.PipelineConfig) bool { return p.Name == name }); name == "" || j >= 0 && j != i {
return fmt.Errorf("pipeline %q: a name no other pipeline has", name)
switch j := slices.IndexFunc(c.Pipelines, func(p config.PipelineConfig) bool { return p.Name == name }); {
case name == "":
return errors.New("a pipeline needs a name; give it one")
case j >= 0 && j != i:
return fmt.Errorf("another pipeline is named %q; choose another name", name)
}
return nil
}
@@ -574,7 +589,7 @@ func (c *Composition) CommandLine() (string, error) {
// line as an environment file holds them; the environment holds one pipeline
func (c *Composition) Environment() (string, error) {
if len(c.Pipelines) != 1 {
return "", fmt.Errorf("the environment holds one pipeline, not %d", len(c.Pipelines))
return "", fmt.Errorf("the environment holds one pipeline, not %d; choose the command or file form, or remove pipelines down to one", len(c.Pipelines))
}
specs, err := c.shellSpecs()
if err != nil {
@@ -632,9 +647,9 @@ type Form struct {
}
var forms = []Form{
{"command", "lw's flags", (*Composition).CommandLine, FromCommandLine},
{"environment", "LOGWISP_ variables: one pipeline", (*Composition).Environment, FromEnvironment},
{"file", "a configuration file's pipelines", func(c *Composition) (string, error) {
{"command", "an lw command line", (*Composition).CommandLine, FromCommandLine},
{"environment", "LOGWISP_ variables for one pipeline", (*Composition).Environment, FromEnvironment},
{"file", "the pipelines of a configuration file", func(c *Composition) (string, error) {
data, err := c.File()
return string(data), err
}, func(text string, _ config.IsDir) (*Composition, error) { return FromFile([]byte(text)) }},
+20 -4
View File
@@ -115,6 +115,18 @@ func TestPastedEnvironmentReadsAsAShellRunsIt(t *testing.T) {
}
}
// Of a line piped into lw, lw's part is read, its pipeline reading standard
// input; a word that is no pipeline flag is refused, naming those to paste
func TestPastedCommandLineIsLwsPart(t *testing.T) {
c, err := FromCommandLine("tail -F app.log | lw --filter include:patterns=ERROR", nil)
if err != nil || c.Pipelines[0].PluginSources[0].Type != "console" || len(c.Pipelines[0].Flow.Filters) != 1 {
t.Fatalf("%v %v", c, err)
}
if _, err := FromCommandLine("lw -c lw.toml --sink null", nil); err == nil || !strings.HasPrefix(err.Error(), "-c: not a pipeline flag; paste lw's --pipeline, --preset, ") {
t.Fatalf("-c: %v", err)
}
}
// Locate places a problem by its path: a plugin's key, a key of its table, a
// rule between its table's keys, a list entry, the plugin itself under an id
// holding ']', a filter's list entry, a stage and the pipeline; and nothing
@@ -192,8 +204,9 @@ func TestShellFormsRefuseControlCharacters(t *testing.T) {
}
}
// Set refuses a value of the wrong kind and leaves the key as it was; Unset
// returns a key to its default, dropping a table it leaves empty
// Set refuses a value of the wrong kind, a plugin's and a stage's alike, and
// leaves the key as it was; Unset returns a key to its default, dropping a
// table it leaves empty
func TestSetKeepsAKeyItCannotTake(t *testing.T) {
c := &Composition{}
if err := c.AddPipeline("p"); err != nil {
@@ -207,8 +220,11 @@ func TestSetKeepsAKeyItCannotTake(t *testing.T) {
t.Fatal(err)
}
}
if err := c.Set(0, n, "port", "http"); err == nil || !strings.HasPrefix(err.Error(), "port: ") {
t.Fatalf("port http: %v", err)
for want, err := range map[string]error{"port: not an integer": c.Set(0, n, "port", "http"),
"interval_ms: not an integer": c.Set(0, beat, "interval_ms", "soon")} {
if err == nil || err.Error() != want {
t.Errorf("%v, want %s", err, want)
}
}
if m, beat := c.Pipelines[0].PluginSinks[0].Config, c.Pipelines[0].Flow.Heartbeat; !reflect.DeepEqual(m, map[string]any{"port": int64(8080)}) ||
beat.IntervalMS != 1000 {