Improve shell quoting
This commit is contained in:
parent
fe81d713e1
commit
b594ff78b2
2
Makefile
2
Makefile
|
@ -9,5 +9,7 @@ acid: acid.go
|
|||
go build -ldflags "-X 'main.projectVersion=$(version)'" -o $@
|
||||
acid.1: acid.adoc
|
||||
asciidoctor -b manpage -a release-version=$(version) -o $@ acid.adoc
|
||||
test: all
|
||||
go test
|
||||
clean:
|
||||
rm -f $(outputs)
|
||||
|
|
|
@ -71,6 +71,9 @@ which has the following fields:
|
|||
*CloneURL*::
|
||||
Gitea link for cloning the repository over HTTP.
|
||||
|
||||
The special *quote* template function quotes fields for safe usage
|
||||
in *sh*(1) command arguments.
|
||||
|
||||
Runners
|
||||
-------
|
||||
Runners receive the following additional environment variables:
|
||||
|
|
27
acid.go
27
acid.go
|
@ -93,10 +93,30 @@ func parseConfig(path string) error {
|
|||
}
|
||||
|
||||
var err error
|
||||
gNotifyScript, err = ttemplate.New("notify").Parse(gConfig.Notify)
|
||||
gNotifyScript, err =
|
||||
ttemplate.New("notify").Funcs(shellFuncs).Parse(gConfig.Notify)
|
||||
return err
|
||||
}
|
||||
|
||||
var shellFuncs = ttemplate.FuncMap{
|
||||
"quote": func(word string) string {
|
||||
// History expansion is annoying, don't let it cut us.
|
||||
if strings.IndexRune(word, '!') >= 0 {
|
||||
return "'" + strings.ReplaceAll(word, "'", `'"'"'`) + "'"
|
||||
}
|
||||
|
||||
const special = "$`\"\\"
|
||||
quoted := []rune{'"'}
|
||||
for _, r := range word {
|
||||
if strings.IndexRune(special, r) >= 0 {
|
||||
quoted = append(quoted, '\\')
|
||||
}
|
||||
quoted = append(quoted, r)
|
||||
}
|
||||
return string(append(quoted, '"'))
|
||||
},
|
||||
}
|
||||
|
||||
// --- Utilities ---------------------------------------------------------------
|
||||
|
||||
func giteaSign(b []byte) string {
|
||||
|
@ -910,8 +930,9 @@ func executorRunTask(ctx context.Context, task Task) error {
|
|||
// - we might have to clone submodules as well.
|
||||
// Otherwise, we could download a source archive from Gitea,
|
||||
// and use SFTP to upload it to the runner.
|
||||
tmplScript, err := ttemplate.New("script").Parse(rt.Runner.Setup + "\n" +
|
||||
rt.ProjectRunner.Setup + "\n" + rt.ProjectRunner.Build)
|
||||
tmplScript, err := ttemplate.New("script").Funcs(shellFuncs).
|
||||
Parse(rt.Runner.Setup + "\n" +
|
||||
rt.ProjectRunner.Setup + "\n" + rt.ProjectRunner.Build)
|
||||
if err != nil {
|
||||
return fmt.Errorf("script: %w", err)
|
||||
}
|
||||
|
|
|
@ -44,9 +44,9 @@ runners:
|
|||
setup: |
|
||||
set -ex
|
||||
sudo pacman -Syu --noconfirm git
|
||||
git clone --recursive '{{.CloneURL}}' '{{.Repo}}'
|
||||
cd '{{.Repo}}'
|
||||
git -c advice.detachedHead=false checkout '{{.Hash}}'
|
||||
git clone --recursive {{quote .CloneURL}} {{quote .Repo}}
|
||||
cd {{quote .Repo}}
|
||||
git -c advice.detachedHead=false checkout {{quote .Hash}}
|
||||
|
||||
# Configuration for individual Gitea repositories.
|
||||
projects:
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
ttemplate "text/template"
|
||||
)
|
||||
|
||||
func TestTemplateQuote(t *testing.T) {
|
||||
// Ideally, we should back-parse it using sh syntax.
|
||||
// This is an unnecessarily fragile test.
|
||||
for _, test := range []struct {
|
||||
input, output string
|
||||
}{
|
||||
{`!!`, `'!!'`},
|
||||
{``, `""`},
|
||||
{`${var}`, `"\${var}"`},
|
||||
{"`cat`", "\"\\`cat\\`\""},
|
||||
{`"魚\"`, `"\"魚\\\""`},
|
||||
} {
|
||||
var b bytes.Buffer
|
||||
err := ttemplate.Must(ttemplate.New("test").
|
||||
Funcs(shellFuncs).Parse("{{quote .}}")).Execute(&b, test.input)
|
||||
if err != nil {
|
||||
t.Errorf("template execution error: %s\n", err)
|
||||
}
|
||||
if b.String() != test.output {
|
||||
t.Errorf("%q should be quoted os %q, not %q\n",
|
||||
test.input, test.output, b.String())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue