diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 151971c..2c4ef98 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,28 +1,28 @@ name: Go -on: [push] +on: [push, pull_request] jobs: build: name: Build runs-on: ubuntu-latest + strategy: + matrix: + go-version: ['1.21', '1.22', '1.23'] + steps: + - name: Check out code + uses: actions/checkout@v4 - - name: Set up Go 1.12 - uses: actions/setup-go@v1 + - name: Set up Go ${{ matrix.go-version }} + uses: actions/setup-go@v5 with: - go-version: 1.12 - id: go + go-version: ${{ matrix.go-version }} - - name: Check out code into the Go module directory - uses: actions/checkout@v1 + - name: Build + run: go build -v ./... - - name: Get dependencies - run: | - go get -v -t -d ./... - if [ -f Gopkg.toml ]; then - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure - fi + - name: Test + run: go test -v ./... - - name: Build - run: go build -v . + - name: Vet + run: go vet ./... diff --git a/compile.go b/compile.go index ce5a21c..f170698 100644 --- a/compile.go +++ b/compile.go @@ -7,7 +7,6 @@ import ( "github.com/wendal/errors" "github.com/wendal/mustache" "io" - "io/ioutil" "log" "os" "path/filepath" @@ -433,7 +432,9 @@ func WriteTo(url string, content string) { if HTML_EXT != "" { content += HTML_EXT } - ioutil.WriteFile(dstPath, []byte(content), os.ModePerm) + if err := os.WriteFile(dstPath, []byte(content), os.ModePerm); err != nil { + log.Println("ERR WriteTo", dstPath, err) + } } func PrapareAssets(theme string, layoutName string, topCtx mustache.Context) string { @@ -526,7 +527,7 @@ func copyDir(src string, target string) error { if !fst.IsDir() { return nil } - finfos, err := ioutil.ReadDir(src) + finfos, err := os.ReadDir(src) if err != nil { log.Println(err) return err diff --git a/config.go b/config.go index 770f2af..a47f6a1 100644 --- a/config.go +++ b/config.go @@ -5,7 +5,6 @@ import ( "encoding/json" "github.com/wendal/goyaml2" "io" - "io/ioutil" "log" "os" ) @@ -37,7 +36,7 @@ func ReadYml(path string) (cnf map[string]interface{}, err error) { func ReadYmlReader(r io.Reader) (cnf map[string]interface{}, err error) { err = nil - buf, err := ioutil.ReadAll(r) + buf, err := io.ReadAll(r) if err != nil || len(buf) < 3 { return } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cea821f --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/wendal/gor + +go 1.24.13 + +require ( + github.com/howeyc/fsnotify v0.9.0 + github.com/russross/blackfriday v1.6.0 + github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec + github.com/wendal/goyaml2 v0.0.0-20190215120833-ee753311eeae + github.com/wendal/mustache v0.0.0-20130201095610-61b948aeb760 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..83e9ffa --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/howeyc/fsnotify v0.9.0 h1:0gtV5JmOKH4A8SsFxG2BczSeXWWPvcMT0euZt5gDAxY= +github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= +github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= +github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec h1:bua919NvciYmjqfeZMsVkXTny1QvXMrri0X6NlqILRs= +github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= +github.com/wendal/goyaml2 v0.0.0-20190215120833-ee753311eeae h1:ke3nvVbPZDAsGLqBukiPAPdzgllyrQAZ4OtdzTf3z5U= +github.com/wendal/goyaml2 v0.0.0-20190215120833-ee753311eeae/go.mod h1:CoOAlRIb5zPgRa7NEsqTVNu/26+FJ1jrIj3/NWU3hM8= +github.com/wendal/mustache v0.0.0-20130201095610-61b948aeb760 h1:yBVQh3XiGSrCTosbppoAmxpCV2fnqE0bUjxVF9iqVdc= +github.com/wendal/mustache v0.0.0-20130201095610-61b948aeb760/go.mod h1:e+ZfVXOi5GtWL3bAoE3ErzEnNyguYqHVegUqgbFtMZM= diff --git a/gor/gor.go b/gor/gor.go index 29cbb14..b13ffe0 100644 --- a/gor/gor.go +++ b/gor/gor.go @@ -7,7 +7,6 @@ import ( "fmt" "github.com/howeyc/fsnotify" "github.com/wendal/gor" - "io/ioutil" "log" "net/http" "os" @@ -125,8 +124,14 @@ func main() { _compile() } case ".update.zip.go": - d, _ := ioutil.ReadFile("gor-content.zip") - _zip, _ := os.OpenFile("zip.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm) + d, err := os.ReadFile("gor-content.zip") + if err != nil { + log.Fatal("failed to read gor-content.zip:", err) + } + _zip, err := os.OpenFile("zip.go", os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm) + if err != nil { + log.Fatal("failed to open zip.go for writing:", err) + } header := `package main const INIT_ZIP="` _zip.Write([]byte(header)) diff --git a/gor/new.go b/gor/new.go index 601b65a..113f556 100644 --- a/gor/new.go +++ b/gor/new.go @@ -5,7 +5,6 @@ import ( "bytes" "encoding/base64" "io" - "io/ioutil" "log" "os" "path/filepath" @@ -23,7 +22,10 @@ func CmdInit(path string) { } decoder := base64.NewDecoder(base64.StdEncoding, bytes.NewBufferString(INIT_ZIP)) - b, _ := ioutil.ReadAll(decoder) + b, err := io.ReadAll(decoder) + if err != nil { + log.Fatalf("failed to read embedded init zip data: %v", err) + } z, err := zip.NewReader(bytes.NewReader(b), int64(len(b))) if err != nil { diff --git a/gor_test.go b/gor_test.go new file mode 100644 index 0000000..68da20b --- /dev/null +++ b/gor_test.go @@ -0,0 +1,207 @@ +package gor + +import ( + "testing" + "time" +) + +func TestEncodePathInfo(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"hello world", "hello-world"}, + {"go:lang", "go-lang"}, + {"simple", "simple"}, + {"a b:c", "a-b-c"}, + } + for _, tt := range tests { + got := EncodePathInfo(tt.input) + if got != tt.expected { + t.Errorf("EncodePathInfo(%q) = %q, want %q", tt.input, got, tt.expected) + } + } +} + +func TestDecodePathInfo(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {"hello%20world", "hello world"}, + {"go-lang", "go-lang"}, + {"simple", "simple"}, + } + for _, tt := range tests { + got := DecodePathInfo(tt.input) + if got != tt.expected { + t.Errorf("DecodePathInfo(%q) = %q, want %q", tt.input, got, tt.expected) + } + } +} + +func TestEncodeDecodePathInfo(t *testing.T) { + original := "hello world" + encoded := EncodePathInfo(original) + // EncodePathInfo replaces spaces with dashes, not url-encode, so round-trip differs + if encoded == original { + t.Errorf("EncodePathInfo should transform %q", original) + } +} + +func TestAsStrings(t *testing.T) { + tests := []struct { + name string + input interface{} + expected []string + }{ + {"nil", nil, []string{}}, + {"string", "hello", []string{"hello"}}, + {"string slice", []string{"a", "b"}, []string{"a", "b"}}, + {"interface slice", []interface{}{"x", "y"}, []string{"x", "y"}}, + } + for _, tt := range tests { + got := AsStrings(tt.input) + if len(got) != len(tt.expected) { + t.Errorf("[%s] AsStrings(%v) len=%d, want %d", tt.name, tt.input, len(got), len(tt.expected)) + continue + } + for i := range got { + if got[i] != tt.expected[i] { + t.Errorf("[%s] AsStrings(%v)[%d] = %q, want %q", tt.name, tt.input, i, got[i], tt.expected[i]) + } + } + } +} + +func TestCreatePostURL(t *testing.T) { + db := make(map[string]interface{}) + date := time.Date(2023, 5, 15, 0, 0, 0, 0, time.Local) + post := map[string]interface{}{ + "permalink": "/:year/:month/:title/", + "_date": date, + "title": "Hello World", + "id": "posts/2023-05-15-hello-world.md", + "categories": []string{"go"}, + } + CreatePostURL(db, "/", post) + url, ok := post["url"].(string) + if !ok { + t.Fatal("post[url] should be a string") + } + if url != "/2023/05/Hello-World/" { + t.Errorf("unexpected url: %q", url) + } +} + +func TestCreatePostURL_StaticPermalink(t *testing.T) { + db := make(map[string]interface{}) + date := time.Date(2023, 1, 1, 0, 0, 0, 0, time.Local) + post := map[string]interface{}{ + "permalink": "/about/", + "_date": date, + "title": "About", + "id": "pages/about.md", + "categories": []string{}, + } + CreatePostURL(db, "/", post) + url, ok := post["url"].(string) + if !ok { + t.Fatal("post[url] should be a string") + } + if url != "/about/" { + t.Errorf("unexpected url: %q", url) + } +} + +func TestMarkdownToHtml(t *testing.T) { + tests := []struct { + name string + input string + contains string + }{ + {"bold", "**hello**", "hello"}, + {"header", "# Title", "