diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 227ece3..2ce848b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,6 +15,7 @@ jobs: go-version: '1.22' - uses: gacts/install-hurl@v1 - uses: cue-lang/setup-cue@v1.0.0 + - run: go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest - name: Login to Docker Hub uses: docker/login-action@v3 @@ -25,6 +26,13 @@ jobs: # CUE_DEBUG_TOOLS_FLOW=true cue cmd ci - run: cue cmd ci + - uses: actions/upload-artifact@v4 + with: + name: logs + path: | + test/*.log + test/report/ + - uses: actions/upload-artifact@v4 with: name: xtemplate-amd64-linux diff --git a/config.go b/config.go index 1a909f4..6871270 100644 --- a/config.go +++ b/config.go @@ -20,7 +20,7 @@ type Config struct { // The path to the templates directory. Default `templates`. TemplatesDir string `json:"templates_dir,omitempty" arg:"-t,--template-dir" default:"templates"` - // The FS to load templates from. Overrides Path if not nil. + // The FS to load templates from. Overrides TemplatesDir if not nil. TemplatesFS fs.FS `json:"-" arg:"-"` // File extension to search for to find template files. Default `.html`. diff --git a/handlers.go b/handlers.go index b1786ce..32f2a2e 100644 --- a/handlers.go +++ b/handlers.go @@ -29,7 +29,7 @@ func bufferingTemplateHandler(server *Instance, tmpl *template.Template) http.Ha dot, err := server.bufferDot.value(server.config.Ctx, w, r) if err != nil { - log.Error("failed to initialize dot value: %w", err) + log.Error("failed to initialize dot value", slog.Any("error", err)) http.Error(w, "internal server error", http.StatusInternalServerError) return } @@ -65,7 +65,7 @@ func flushingTemplateHandler(server *Instance, tmpl *template.Template) http.Han dot, err := server.flusherDot.value(server.config.Ctx, w, r) if err != nil { - log.Error("failed to initialize dot value: %w", err) + log.Error("failed to initialize dot value", slog.Any("error", err)) http.Error(w, "internal server error", http.StatusInternalServerError) return } diff --git a/make_tool.cue b/make_tool.cue index 33981b1..1109576 100644 --- a/make_tool.cue +++ b/make_tool.cue @@ -7,9 +7,6 @@ import ( "tool/exec" "tool/file" "tool/os" - - // "encoding/json" - // "tool/cli" ) #vars: { @@ -19,6 +16,7 @@ import ( version: string ldflags: string latest: string + env: {[string]: string} } meta: { @@ -29,6 +27,7 @@ meta: { version: strings.TrimSpace(_commands.version.stdout) ldflags: "-X 'github.com/infogulch/xtemplate/app.version=\(version)'" latest: strings.TrimSpace(_commands.latest.stdout) + env: {for k, v in _commands.env if k != "$id" {(k): v}} } _commands: { reporoot: exec.Run & { @@ -50,6 +49,7 @@ meta: { dir: vars.rootdir stdout: string } + env: os.Environ } } @@ -69,20 +69,13 @@ task: build: { task: run: { vars: #vars - gobuild: task.build & {"vars": vars, outfile: "\(vars.testdir)/xtemplate"} rmdataw: file.RemoveAll & {path: "\(vars.testdir)/dataw"} mkdataw: file.Mkdir & {path: "\(vars.testdir)/dataw", $after: rmdataw.$done} - mklog: file.Create & {filename: "\(vars.testdir)/xtemplate.log", contents: ""} start: exec.Run & { - $after: mkdataw.$done && mklog.$done && gobuild.gobuild.$done - cmd: ["bash", "-c", "./xtemplate --loglevel -4 -d DB:sql:sqlite3:file:./dataw/test.sqlite -d FS:fs:./data --config-file config.json >\(mklog.filename) 2>&1"] - dir: vars.testdir - } - - ready: exec.Run & { - $after: mklog.$done - cmd: ["bash", "-c", "grep -q 'starting server' <(tail -f \(mklog.filename))"] + cmd: ["bash", "-c", "./xtemplate --loglevel -4 -d DB:sql:sqlite3:file:./dataw/test.sqlite -d FS:fs:./data --config-file config.json >xtemplate.log 2>&1 &"] + dir: vars.testdir + $after: mkdataw.$done } } @@ -92,9 +85,11 @@ task: test: { port: int | *8080 list: file.Glob & {glob: "\(vars.testdir)/tests/*.hurl"} + ready: exec.Run & {cmd: "curl -X GET --retry-all-errors --retry 5 --retry-connrefused --retry-delay 1 http://localhost:\(port)/ready --silent", stdout: "OK"} hurl: exec.Run & { - cmd: ["hurl", "--continue-on-error", "--test", "--report-html", "report", "--connect-to", "localhost:8080:localhost:\(port)"] + list.files - dir: vars.testdir + cmd: ["hurl", "--continue-on-error", "--no-output", "--test", "--report-html", "report", "--connect-to", "localhost:8080:localhost:\(port)"] + list.files + dir: vars.testdir + after: ready.$done } } @@ -102,11 +97,20 @@ task: gotest: { vars: #vars gotest: exec.Run & { - cmd: "go test -v ./..." + cmd: ["bash", "-c", "go test -v ./... >\(vars.testdir)/gotest.log"] dir: vars.rootdir } } +task: build_test: { + vars: #vars + + build: task.build & {"vars": vars, outfile: "\(vars.testdir)/xtemplate"} + run: task.run & {"vars": vars, start: $after: build.gobuild.$done} + test: task.test & {"vars": vars, ready: $after: run.start.$done} + kill: exec.Run & {cmd: "pkill xtemplate", $after: test.hurl.$done} +} + task: dist: { vars: #vars @@ -115,7 +119,6 @@ task: dist: { oses: ["linux", "darwin", "windows"] arches: ["amd64", "arm64"] matrix: [for os in oses for arch in arches {GOOS: os, GOARCH: arch}] - osenv: os.Environ for env in matrix { (env.GOOS + "_" + env.GOARCH): { @@ -126,13 +129,27 @@ task: dist: { } mkdir: file.MkdirAll & {path: dir, $after: rmdist.$done} - build: task.build & {"vars": vars, outfile: "\(dir)/\(exe)", gobuild: {$after: mkdir.$done, "env": env & osenv}} + build: task.build & {"vars": vars, outfile: "\(dir)/\(exe)", gobuild: {$after: mkdir.$done, "env": env & vars.env}} cp: exec.Run & {cmd: ["cp", "README.md", "LICENSE", "\(dir)"], $after: mkdir.$done} zip: exec.Run & {cmd: ["zip", "-jqr6", "\(dir)_\(vars.version).zip", dir], $after: cp.$done && build.$done} } } } +task: build_docker: { + vars: #vars + + tags: [ + "infogulch/xtemplate:\(vars.version)", + if vars.version == vars.latest {"infogulch/xtemplate:latest"}, + ] + + build: exec.Run & { + cmd: ["docker", "build"] + list.FlattenN([for t in tags {["-t", t]}], 1) + ["--build-arg", "LDFLAGS=\(vars.ldflags)", "."] + dir: vars.rootdir + } +} + task: test_docker: { vars: #vars @@ -141,43 +158,58 @@ task: test_docker: { dir: vars.rootdir } run: exec.Run & { - cmd: "docker run -d --rm --name xtemplate-test -p 8081:80 xtemplate-test" + cmd: ["docker", "run", "-d", "--rm", "--name", "xtemplate-test", "-p", "8081:80", "xtemplate-test"] $after: build.$done } - ready: exec.Run & { - cmd: ["bash", "-c", "grep -q 'starting server' <(docker logs -f xtemplate-test)"] - $after: run.$done - } - test: task.test & {"vars": vars, port: 8081, hurl: $after: ready.$done} + test: task.test & {"vars": vars, port: 8081, ready: $after: run.$done} stop: exec.Run & {cmd: "docker stop xtemplate-test", $after: test.hurl.$done} // be nice if we can always run this even if previous steps fail } -task: build_docker: { +task: build_caddy: { vars: #vars - tags: [ - "infogulch/xtemplate:\(vars.version)", - if vars.version == vars.latest {"infogulch/xtemplate:latest"}, - ] - - build: exec.Run & { - cmd: ["docker", "build"] + list.FlattenN([for t in tags {["-t", t]}], 1) + ["--build-arg", "LDFLAGS=\(vars.ldflags)", "."] + xbuild: exec.Run & { + cmd: ["bash", "-c", + "xcaddy build " + + "--with github.com/infogulch/xtemplate-caddy " + + "--with github.com/infogulch/xtemplate=. " + + "--with github.com/infogulch/xtemplate/providers=./providers " + + "--with github.com/infogulch/xtemplate/providers/nats=./providers/nats " + + "--with github.com/mattn/go-sqlite3 " + + "--output \(vars.testdir)/caddy " + + ">\(vars.testdir)/xcaddy.log 2>&1", + ] dir: vars.rootdir + env: vars.env & {CGO_ENABLED: "1"} } } -command: { - for k, t in task { - (k): {cfg: meta, vars: cfg.vars, t} +task: run_caddy: { + vars: #vars + + rmdataw: file.RemoveAll & {path: "\(vars.testdir)/dataw"} + mkdataw: file.Mkdir & {path: "\(vars.testdir)/dataw", $after: rmdataw.$done} + + start: exec.Run & { + cmd: ["bash", "-c", "./caddy start --config caddy.json >xtemplate.caddy.log 2>&1"] + dir: vars.testdir + $after: mkdataw.$done } } -command: run_test: { - cfg: meta +task: build_test_caddy: { + vars: #vars - run: task.run & {"vars": cfg.vars, start: mustSucceed: false} - test: task.test & {"vars": cfg.vars, hurl: $after: run.ready.$done} - kill: exec.Run & {cmd: "pkill xtemplate", $after: test.hurl.$done} + build: task.build_caddy & {"vars": vars} + run: task.run_caddy & {"vars": vars, start: $after: build.xbuild.$done} + test: task.test & {"vars": vars, port: 8082, ready: $after: run.start.$done} + kill: exec.Run & {cmd: "pkill caddy", $after: test.hurl.$done} // is there a better way? +} + +command: { + for k, t in task { + (k): {cfg: meta, vars: cfg.vars, t} + } } command: ci: { @@ -185,16 +217,16 @@ command: ci: { gotest: task.gotest & {"vars": cfg.vars} - run: task.run & {"vars": cfg.vars, start: mustSucceed: false} // it's killed so it will never succeed - test: task.test & {"vars": cfg.vars, hurl: $after: run.ready.$done} - kill: exec.Run & {cmd: "pkill xtemplate", $after: test.hurl.$done} // is there a better way? + build_test: task.build_test & {"vars": cfg.vars} + build_test_caddy: task.build_test_caddy & {"vars": cfg.vars, run: rmdataw: $after: build_test.kill.$done} - dist: task.dist & {"vars": cfg.vars, rmdist: $after: kill.$done} + dist: task.dist & {"vars": cfg.vars, rmdist: $after: build_test.kill.$done} test_docker: task.test_docker & {"vars": cfg.vars} build_docker: task.build_docker & {"vars": cfg.vars, build: $after: test_docker.stop.$done} + push: exec.Run & { cmd: ["docker", "push"] + build_docker.tags - $after: build_docker.build.$done + $after: build_docker.build.$done && build_test.kill.$done && build_test_caddy.kill.$done } } diff --git a/test/caddy.json b/test/caddy.json new file mode 100644 index 0000000..79363bd --- /dev/null +++ b/test/caddy.json @@ -0,0 +1,59 @@ +{ + "apps": { + "http": { + "servers": { + "": { + "listen": [ + ":8082" + ], + "routes": [ + { + "handle": [ + { + "handler": "xtemplate", + "minify": true, + "dot": [ + { + "type": "sql", + "name": "DB", + "driver": "sqlite3", + "connstr": "file:./dataw/test.sqlite" + }, + { + "type": "fs", + "name": "FS", + "path": "./data" + }, + { + "type": "fs", + "name": "FSW", + "path": "./dataw" + }, + { + "type": "fs", + "name": "Migrations", + "path": "./migrations" + }, + { + "type": "kv", + "name": "KV", + "values": { + "a": "1", + "b": "2", + "hello": "world" + } + }, + { + "type": "nats", + "name": "Nats" + } + ] + } + ] + } + ] + } + } + } + } +} \ No newline at end of file diff --git a/test/templates/ready.html b/test/templates/ready.html new file mode 100644 index 0000000..a0aba93 --- /dev/null +++ b/test/templates/ready.html @@ -0,0 +1 @@ +OK \ No newline at end of file