diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 78130baf..9c0a85f2 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -39,9 +39,11 @@ jobs: echo "$env:GITHUB_WORKSPACE\dart-sass" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append shell: pwsh + - name: Set timezone + run: tzutil /s "Eastern Standard Time" + - name: Build run: go build main.go - name: Test run: go test ./... - continue-on-error: true # Allow failures until Windows-specific bugs are fixed diff --git a/collection/collection_test.go b/collection/collection_test.go index 4259c2c8..eec020b2 100644 --- a/collection/collection_test.go +++ b/collection/collection_test.go @@ -1,6 +1,7 @@ package collection import ( + "path/filepath" "testing" "github.com/osteele/gojekyll/config" @@ -20,7 +21,7 @@ func TestNewCollection(t *testing.T) { c1 := New(site, "c", map[string]interface{}{"output": true}) require.Equal(t, true, c1.Output()) - require.Equal(t, "_c/", c1.PathPrefix()) + require.Equal(t, "_c/", filepath.ToSlash(c1.PathPrefix())) c2 := New(site, "c", map[string]interface{}{}) require.Equal(t, false, c2.Output()) diff --git a/config/config_test.go b/config/config_test.go index 8f057225..5a2b2add 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1,6 +1,7 @@ package config import ( + "path/filepath" "strings" "testing" @@ -9,7 +10,7 @@ import ( func TestConfig_SourceDir(t *testing.T) { c := Default() - require.True(t, strings.HasPrefix(c.SourceDir(), "/")) + require.True(t, filepath.IsAbs(c.SourceDir()) || strings.HasPrefix(c.SourceDir(), "/")) } func TestDefaultConfig(t *testing.T) { c := Default() diff --git a/pages/page_test.go b/pages/page_test.go index 55bb8f06..e2a0e64f 100644 --- a/pages/page_test.go +++ b/pages/page_test.go @@ -51,7 +51,7 @@ func TestPage_Write(t *testing.T) { require.Contains(t, err.Error(), "render error") pe, ok := err.(utils.PathError) require.True(t, ok) - require.Equal(t, "testdata/liquid_error.md", pe.Path()) + require.Equal(t, "testdata/liquid_error.md", filepath.ToSlash(pe.Path())) }) t.Run("layout: none", func(t *testing.T) { diff --git a/site/site.go b/site/site.go index 9470dfd5..9603012e 100644 --- a/site/site.go +++ b/site/site.go @@ -137,6 +137,10 @@ func (s *Site) KeepFile(filename string) bool { // FilePathPage returns a Page, give a file path relative to site source directory. func (s *Site) FilePathPage(rel string) (Document, bool) { + // Normalize the input path to use OS-specific separators + // This handles cases where templates use forward slashes (e.g., {% link _c1/c1p1.md %}) + rel = filepath.FromSlash(rel) + // This looks wasteful. If it shows up as a hotspot, you know what to do. for _, p := range s.Routes { if p.Source() != "" { diff --git a/utils/filepath_test.go b/utils/filepath_test.go index 23a38129..c3927638 100644 --- a/utils/filepath_test.go +++ b/utils/filepath_test.go @@ -2,6 +2,7 @@ package utils import ( "os" + "path/filepath" "strings" "testing" "time" @@ -9,16 +10,9 @@ import ( "github.com/stretchr/testify/require" ) -func timeMustParse(s string) time.Time { - t, err := time.Parse(time.RFC3339, s) - if err != nil { - panic(err) - } - return t -} - func TestMustAbs(t *testing.T) { - require.True(t, strings.HasPrefix(MustAbs("."), "/")) + abs := MustAbs(".") + require.True(t, filepath.IsAbs(abs) || strings.HasPrefix(abs, "/")) } func TestParseFilenameDate(t *testing.T) { @@ -26,7 +20,9 @@ func TestParseFilenameDate(t *testing.T) { d, title, found := ParseFilenameDateTitle("2017-07-02-post.html") require.True(t, found) require.Equal(t, "Post", title) - require.Equal(t, timeMustParse("2017-07-02T00:00:00-04:00"), d) + // The date should be 2017-07-02 at midnight in the local timezone + expected := time.Date(2017, 7, 2, 0, 0, 0, 0, time.Local) + require.Equal(t, expected, d) _, _, found = ParseFilenameDateTitle("not-post.html") require.False(t, found) diff --git a/utils/ioutil.go b/utils/ioutil.go index 09c8f2ee..8c056d29 100644 --- a/utils/ioutil.go +++ b/utils/ioutil.go @@ -68,8 +68,13 @@ func PostfixWalk(root string, walkFn filepath.WalkFunc) error { // IsNotEmpty returns a boolean indicating whether the error is known to report that a directory is not empty. func IsNotEmpty(err error) bool { - if err, ok := err.(*os.PathError); ok { - return err.Err.(syscall.Errno) == syscall.ENOTEMPTY + if pathErr, ok := err.(*os.PathError); ok { + if errno, ok := pathErr.Err.(syscall.Errno); ok { + // Check for platform-specific "directory not empty" errors + // Unix/Linux/macOS: ENOTEMPTY + // Windows: ERROR_DIR_NOT_EMPTY (checked via platform-specific helper) + return errno == syscall.ENOTEMPTY || isWindowsDirNotEmpty(errno) + } } return false } diff --git a/utils/ioutil_test.go b/utils/ioutil_test.go index bd9567df..e3163599 100644 --- a/utils/ioutil_test.go +++ b/utils/ioutil_test.go @@ -29,7 +29,10 @@ func TestCopyFileContents(t *testing.T) { if err != nil { t.Fatal(err) } - require.Equal(t, "content\n", string(b)) + // Normalize line endings for cross-platform compatibility + content := string(b) + // Accept either Unix or Windows line endings + require.True(t, content == "content\n" || content == "content\r\n", "expected 'content\\n' or 'content\\r\\n', got %q", content) err = CopyFileContents(f.Name(), testFile("missing.txt"), 0x644) require.Error(t, err) diff --git a/utils/ioutil_unix.go b/utils/ioutil_unix.go new file mode 100644 index 00000000..3d4eeb2b --- /dev/null +++ b/utils/ioutil_unix.go @@ -0,0 +1,10 @@ +//go:build !windows + +package utils + +import "syscall" + +// isWindowsDirNotEmpty always returns false on non-Windows platforms +func isWindowsDirNotEmpty(errno syscall.Errno) bool { + return false +} diff --git a/utils/ioutil_windows.go b/utils/ioutil_windows.go new file mode 100644 index 00000000..7ccb7908 --- /dev/null +++ b/utils/ioutil_windows.go @@ -0,0 +1,10 @@ +//go:build windows + +package utils + +import "syscall" + +// isWindowsDirNotEmpty checks if the error is Windows ERROR_DIR_NOT_EMPTY +func isWindowsDirNotEmpty(errno syscall.Errno) bool { + return errno == syscall.ERROR_DIR_NOT_EMPTY +}