Let a shell parameter operator after a known word pass, so ${PORT:-8080} is not refused (issue 231)
The case-insensitive rule for the mesh's namespace words took ${PORT:-8080},
${SHELL:-/bin/sh}, ${SECRET:?unset} and ${dir:-/tmp} for misspellings, though they
are among the commonest lines of a script or env file. A :-, :=, :+ or :? after the
colon is the shell's, whatever the word's case.
This commit is contained in:
@@ -24,15 +24,18 @@ import (
|
|||||||
// What is refused:
|
// What is refused:
|
||||||
// - a placeholder of a namespace the mesh knows, in a field that namespace's pass does not read: it
|
// - a placeholder of a namespace the mesh knows, in a field that namespace's pass does not read: it
|
||||||
// would reach the machine as the same text;
|
// would reach the machine as the same text;
|
||||||
// - any other `${<known namespace>:`, case-insensitively and whatever follows — `${Machine:address}`,
|
// - any other `${<known namespace>:`, case-insensitively, unless a shell's parameter operator follows
|
||||||
// `${machine:.address}`, `${machine: address}`, an unclosed `${machine:address` — because no
|
// its colon — `${Machine:address}`, `${machine:.address}`, `${machine: address}`, an unclosed
|
||||||
// pass's pattern takes it;
|
// `${machine:address` — because no pass's pattern takes it;
|
||||||
// - a namespace-shaped placeholder of a word the mesh does not know: a lower-case word, a colon, and
|
// - a namespace-shaped placeholder of a word the mesh does not know: a lower-case word, a colon, and
|
||||||
// a key that begins with a letter or a digit and holds no space, brace or `$`.
|
// a key that begins with a letter or a digit and holds no space, brace or `$`.
|
||||||
//
|
//
|
||||||
// **The shell's own syntax is not that shape, and passes.** `${NAME:-…}` has an upper-case word,
|
// **The shell's own syntax is not that shape, and passes.** `${NAME:-…}` has an upper-case word,
|
||||||
// `${(%):-…}` and `${1:-.}` begin with no letter, and a lower-case variable with an operator after its
|
// `${(%):-…}` and `${1:-.}` begin with no letter, and a lower-case variable with an operator after its
|
||||||
// colon — `${count:-}`, `${trial:+…}`, `${state:=…}` — has no key that begins with a letter or a digit.
|
// colon — `${count:-}`, `${trial:+…}`, `${state:=…}` — has no key that begins with a letter or a digit.
|
||||||
|
// And an operator — `:-`, `:=`, `:+`, `:?` — after a word the mesh also uses is the shell's too:
|
||||||
|
// `${PORT:-8080}`, `${SHELL:-/bin/sh}`, `${SECRET:?unset}` and `${dir:-/tmp}` are among the commonest
|
||||||
|
// lines of a script or an env file, and pass whatever the word's case.
|
||||||
// What the shape does catch is a zsh modifier (`${path:t}`) or a substring (`${where:0:12}`) in a
|
// What the shape does catch is a zsh modifier (`${path:t}`) or a substring (`${where:0:12}`) in a
|
||||||
// resource's own text: shell code of that kind belongs in the module's contributed shell code, which
|
// resource's own text: shell code of that kind belongs in the module's contributed shell code, which
|
||||||
// is not a resource and is never swept (novox/hq ADR 0204).
|
// is not a resource and is never swept (novox/hq ADR 0204).
|
||||||
@@ -78,9 +81,10 @@ func (f filler) reads(kind, field string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ofKnownNamespace is any `${<known namespace>:` and what follows it up to its brace or the end of
|
// ofKnownNamespace is any `${<known namespace>:` and what follows it up to its brace or the end of
|
||||||
// its line, whatever its case: what remains of one once every pass's pattern is set aside is a
|
// its line, whatever its case, unless what follows the colon is a shell's parameter operator (`-`,
|
||||||
// misspelling.
|
// `=`, `+`, `?`): what remains of one once every pass's pattern is set aside is a misspelling.
|
||||||
var ofKnownNamespace = regexp.MustCompile(`(?i)\$\{(?:` + strings.Join(namespacesOf(fillers), "|") + `):[^}\n]*\}?`)
|
var ofKnownNamespace = regexp.MustCompile(`(?im)\$\{(?:` + strings.Join(namespacesOf(fillers), "|") +
|
||||||
|
`):(?:[^-=+?}\n][^}\n]*\}?|\}|$)`)
|
||||||
|
|
||||||
// namespaceShaped is a placeholder in the mesh's shape: a lower-case word, a colon, and a key that
|
// namespaceShaped is a placeholder in the mesh's shape: a lower-case word, a colon, and a key that
|
||||||
// begins with a letter or a digit and holds no space, brace or `$`.
|
// begins with a letter or a digit and holds no space, brace or `$`.
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ const (
|
|||||||
undeclaredSetting = "${setting:Undeclared}"
|
undeclaredSetting = "${setting:Undeclared}"
|
||||||
misspelledMachine = "${machnie:address}"
|
misspelledMachine = "${machnie:address}"
|
||||||
shellsOwn = "${XDG_CACHE_HOME:-x}"
|
shellsOwn = "${XDG_CACHE_HOME:-x}"
|
||||||
|
// The shell's operators after a word the mesh also uses, in any case: the shell's, and passed.
|
||||||
|
shellsOperators = "${PORT:-8080} ${SHELL:-/bin/sh} ${SECRET:?unset} ${dir:-/tmp}"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The catalogue check — the strict parse registration runs too — refuses each by name, with the
|
// The catalogue check — the strict parse registration runs too — refuses each by name, with the
|
||||||
@@ -42,6 +44,7 @@ func TestAMisspelledPlaceholderIsRefusedAtTheCheck(t *testing.T) {
|
|||||||
{`{"id":"rc","type":"file","path":"/etc/rc","content":"${Machine:address}"}`, "${Machine:address}", "content"},
|
{`{"id":"rc","type":"file","path":"/etc/rc","content":"${Machine:address}"}`, "${Machine:address}", "content"},
|
||||||
{`{"id":"rc","type":"file","path":"/etc/rc","content":"${machine:.address}"}`, "${machine:.address}", "content"},
|
{`{"id":"rc","type":"file","path":"/etc/rc","content":"${machine:.address}"}`, "${machine:.address}", "content"},
|
||||||
{`{"id":"rc","type":"file","path":"/etc/rc","content":"${machine: address}"}`, "${machine: address}", "content"},
|
{`{"id":"rc","type":"file","path":"/etc/rc","content":"${machine: address}"}`, "${machine: address}", "content"},
|
||||||
|
{`{"id":"rc","type":"file","path":"/etc/rc","content":"${Dir:x}"}`, "${Dir:x}", "content"},
|
||||||
{`{"id":"rc","type":"file","path":"/etc/rc","content":"a=${machine:address\nb=1"}`, "${machine:address", "content"},
|
{`{"id":"rc","type":"file","path":"/etc/rc","content":"a=${machine:address\nb=1"}`, "${machine:address", "content"},
|
||||||
{`{"id":"rc","type":"process","name":"speller","env":{"NAME":"${machine:name}"}}`, "${machine:name}", "env.NAME"},
|
{`{"id":"rc","type":"process","name":"speller","env":{"NAME":"${machine:name}"}}`, "${machine:name}", "env.NAME"},
|
||||||
} {
|
} {
|
||||||
@@ -55,7 +58,9 @@ func TestAMisspelledPlaceholderIsRefusedAtTheCheck(t *testing.T) {
|
|||||||
// And the shell's syntax alone, beside placeholders every pass knows, is accepted — as is the
|
// And the shell's syntax alone, beside placeholders every pass knows, is accepted — as is the
|
||||||
// shape a lower-case shell variable takes with an operator after its colon.
|
// shape a lower-case shell variable takes with an operator after its colon.
|
||||||
raw = `{"module":"speller","version":"1","resources":[{"id":"rc","type":"file","path":"${machine:account-home}/.rc",` +
|
raw = `{"module":"speller","version":"1","resources":[{"id":"rc","type":"file","path":"${machine:account-home}/.rc",` +
|
||||||
`"content":"` + shellsOwn + ` ${(%):-%n} ${1:-.} ${count:-} ${trial:+ on trial} ${machine:address}\n"}]}`
|
`"content":"` + shellsOwn + ` ${(%):-%n} ${1:-.} ${count:-} ${trial:+ on trial} ${machine:address} ` +
|
||||||
|
shellsOperators + `\n"},` +
|
||||||
|
`{"id":"server","type":"container","name":"server","env":{"PORT":"${PORT:-80}"}}]}`
|
||||||
if _, err := ParseManifest([]byte(raw)); err != nil {
|
if _, err := ParseManifest([]byte(raw)); err != nil {
|
||||||
t.Fatalf("the shell's own syntax was refused: %v", err)
|
t.Fatalf("the shell's own syntax was refused: %v", err)
|
||||||
}
|
}
|
||||||
@@ -77,6 +82,7 @@ func TestAMisspelledPlaceholderIsRefusedAtComposition(t *testing.T) {
|
|||||||
{"content", map[string]any{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "${Machine:address}"}, "${Machine:address}"},
|
{"content", map[string]any{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "${Machine:address}"}, "${Machine:address}"},
|
||||||
{"content", map[string]any{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "${machine:.address}"}, "${machine:.address}"},
|
{"content", map[string]any{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "${machine:.address}"}, "${machine:.address}"},
|
||||||
{"content", map[string]any{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "${machine: address}"}, "${machine: address}"},
|
{"content", map[string]any{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "${machine: address}"}, "${machine: address}"},
|
||||||
|
{"content", map[string]any{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "${Dir:x}"}, "${Dir:x}"},
|
||||||
{"content", map[string]any{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "a=${machine:address\nb=1"}, "${machine:address"},
|
{"content", map[string]any{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "a=${machine:address\nb=1"}, "${machine:address"},
|
||||||
} {
|
} {
|
||||||
m := Manifest{Module: "speller", Version: "1", Resources: []map[string]any{c.r}}
|
m := Manifest{Module: "speller", Version: "1", Resources: []map[string]any{c.r}}
|
||||||
@@ -87,14 +93,15 @@ func TestAMisspelledPlaceholderIsRefusedAtComposition(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m := Manifest{Module: "speller", Version: "1", Resources: []map[string]any{{
|
m := Manifest{Module: "speller", Version: "1", Resources: []map[string]any{
|
||||||
"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "d=" + shellsOwn + "\n",
|
{"id": "rc", "type": "file", "path": "/etc/speller.rc", "content": "d=" + shellsOwn + " " + shellsOperators + "\n"},
|
||||||
}}}
|
{"id": "server", "type": "process", "name": "server", "env": map[string]any{"PORT": "${PORT:-80}"}},
|
||||||
|
}}
|
||||||
out, err := Resolution{Node: "workstation", Account: "op", Modules: []Manifest{m}}.Declaration(Rendering{})
|
out, err := Resolution{Node: "workstation", Account: "op", Modules: []Manifest{m}}.Declaration(Rendering{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("the shell's own syntax was refused: %v", err)
|
t.Fatalf("the shell's own syntax was refused: %v", err)
|
||||||
}
|
}
|
||||||
if got := contentOf(t, out, "speller.rc"); got != "d="+shellsOwn+"\n" {
|
if got := contentOf(t, out, "speller.rc"); got != "d="+shellsOwn+" "+shellsOperators+"\n" {
|
||||||
t.Fatalf("the shell's own syntax did not pass through as written: %q", got)
|
t.Fatalf("the shell's own syntax did not pass through as written: %q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user