Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows: mount path with leading slash is found in the project directory #12470

Open
jmooring opened this issue May 7, 2024 · 6 comments
Open
Labels
Milestone

Comments

@jmooring
Copy link
Member

jmooring commented May 7, 2024

Reference: https://discourse.gohugo.io/t/linux-hugo-for-ubuntu-renders-one-page-less-than-windows-version-from-mounted-folder/49664/7?u=jmooring

On Windows, if a mount path has a leading slash, and that path exists relative to the root of the project directory, Hugo finds the path in the project directory and mounts the directory when it shouldn't. Paths with leading slashes are supposed to be relative to the system root.

func TestFoo(t *testing.T) {
	t.Parallel()

	files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
[[module.mounts]]
source = "content"
target = "content"
[[module.mounts]]
source = "/content-other" # leading slash
target = "content"
-- content/p1.md --
---
title: p1
---
-- content-other/p2.md --
---
title: p2
---
-- layouts/_default/single.html --
{{ .Title }}
`
	b := hugolib.NewIntegrationTestBuilder(
		hugolib.IntegrationTestConfig{
			T:           t,
			TxtarString: files,
			NeedsOsFS:   true,
		}).Build()

	b.AssertFileExists("public/p1/index.html", true)
	b.AssertFileExists("public/p2/index.html", false) // fails on Windows
}
@irkode
Copy link

irkode commented May 7, 2024

one point regarding handling from (my) users perspective.
mounting a non-existing folder should cause at least a "no such file" warning or even an error. not just a normal rendering stuff.

@jmooring
Copy link
Member Author

jmooring commented May 7, 2024

mounting a non-existing folder...

See #6967

@irkode
Copy link

irkode commented May 8, 2024

consider the strange windows behavior with having drive letters and cwd for each drive (multiple roots)

No idea about

  • go handles that

  • or Hugo already considered these

  • no idea how the virtual fs works

  • if the file X:\content-other\p2.md exists

  • and your current working directory is on drive X:

  • /content-other is a valid path that points to X:\content-other

so your second test case should fail on windows

willing to assist , if there's something (not GO) that would help

and more special stuff

  • drive letters -> so more than one root
    cygwin solves that by mapping these to /c/, /d/, /x/ respective. did not find how go handles these

  • drive relative paths
    with cwd = c:\bla
    cd c:hi means c:\bla\hi
    and \hi means c:\ (in fact current drive letter)

  • each drive has a cwd that can be changed from outside (guess for current shell)

  • ...

@irkode
Copy link

irkode commented May 8, 2024

source="c:" or source ="d:" may be problematic cause they refer to the current folder of that drive. So if you are somewhere in c:... and address d: it may point to some unexpected unwanted folder.

btw. docs state it must me forward slashes, but "c:\content" works fine

@bep bep removed the NeedsTriage label May 8, 2024
@bep bep added this to the v0.126.0 milestone May 8, 2024
@bep
Copy link
Member

bep commented May 8, 2024

@irkode you have some valid points. My take on this would be that if

os.Stat("/foo/bar.txt")  // Using the correct OS path separator

... throws an error (typically os.ErrNotExist), then that file does not exist.

I'm not sure exactly happens in this particular case, but I'm pretty sure it can be explained re. the above.

@irkode
Copy link

irkode commented May 10, 2024

I played around a little with Windows style pathes and have a finding

short result

Go's Stat and Abs method seem not to handle Drive Relative Paths like
x:some-path-without-starting-slash

I'm new to Go, so there might be methods that handle that

Dunno if this will affect Hugo code

I could not identify issues if absolute paths are used X:\

(p.s. backslash and slash are handled properly)

TL;TR;

wrote a small Go program that

  • calls os.Stat
  • if that succeeds path/os.Abs
  • if that succeeds outputs the determined path

run that for several filenames and compared with the output of Windows Powershell tests

  • Test-Path
  • Resolve-Path

The Good answer for all tested combinations where

  • Tested filename
  • Current Working Directory

have an absolute path with the same drive the results are identical Even for Drive relative
paths.

if the drive letter differs

  • tested file is on drive C:
  • current Working Directory is on drive E:

Go's method seem not to handle Drive Relative paths respect the current working of the tested Drive
and uses absolute paths

drive C:

C:\
├───bar.md
└───stat
      ├───bar.md
      └───baz.md

failing testcases

this reads as:

  • my current working directory is = cwd
  • the current working directory of the tested drive is = driveCwd
  • I test the path
  • and print out results for Powershell and Go
PS E:\> C:\mydir\winosstat\mystat.ps1| ?{-Not $_.status }| ft -AutoSize

status cwd driveCwd  path          winStat goStat winAbs         goAbs
------ --- --------  ----          ------- ------ ------         -----
 False E:\ c:\stat   c:bar.md         True   True C:\stat\bar.md C:\bar.md
 False E:\ c:\stat   c:baz.md         True  False C:\stat\baz.md
 False E:\ c:\stat   c:stat/bar.md   False   True                C:\stat\bar.md
 False E:\ c:\stat   c:stat/baz.md   False   True                C:\stat\baz.md
 False E:\ c:\mydir  c:bar.md        False   True                C:\bar.md
 False E:\ c:\mydir  c:stat/bar.md   False   True                C:\stat\bar.md
 False E:\ c:\mydir  c:stat/baz.md   False   True                C:\stat\baz.md
 False E:\ c:\_repos c:bar.md        False   True                C:\bar.md
 False E:\ c:\_repos c:stat/bar.md   False   True                C:\stat\bar.md
 False E:\ c:\_repos c:stat/baz.md   False   True                C:\stat\baz.md

CODE

quite hacky powershell and my first Go program - please be generous.

Go (mystat.go)

package main

import (
   "fmt"
   "os"
   "path/filepath"
)

func main() {

   if len(os.Args) != 2 {
      var arg0 = os.Args[0]
      prg, _ := os.Stat(arg0)
      fmt.Printf("Usage: %s <PATH>", prg.Name())
      os.Exit(1)
   }
   var filename = os.Args[1]
   _, err := os.Stat(filename)
   if err != nil {
      os.Exit(2)
   } else {
      absPath, err := filepath.Abs(filename)
      if err != nil {
         os.Exit(3)
      } else {
         fmt.Print(absPath)
         os.Exit(0)
      }
   }
   os.Exit(1)
}

Powershell

$mystat = "C:\mydir\winosstat\mystat.exe"

# This will be the current workin directories for finding files from
$driveCwds = @(
   "c:\"       # bar.txt
   "c:\stat"    # bar.txt & baz.txt
   "c:\mydir"  # neither
   "c:\_repos"  # neither
)

# Matrix test prefixes + pathes
$prefixes = @(
   "",
   "/",
   "c:",
   "c:/"
)
$pathes = @(
   "bar.md"
   "baz.md"

   "stat/bar.md"
   "stat/baz.md"
)
# Save Current Working directory to be able to go back after changing work director on other drive
$cwd = Get-Location

$driveCwds | % {
   $driveCwd = $_
   # Switch the tested drives working folder and come back
   $newLoc = Set-location $driveCwd -PassThru
   if ($cwd.Drive -ne $newLoc.Drive) {
      Set-Location $cwd
   }
   $prefixes | % {
      $pre = $_; $pathes | % {
         $checkPath = $pre + $_
         $winAbs = $Null
         $goAbs = $Null

         # check Windows style
         $winStat = Test-Path $checkPath -ErrorAction SilentlyContinue
         If ($winStat) {
            $winAbs = (Resolve-Path $checkPath).Path
         }
         # Use Go program
         $goAbs = & $mystat $checkPath
         $goStat = $LastExitCode -eq 0
         [PSCustomObject]@{
            # compare results
            status   = (($winStat -eq $goStat) -and ($winAbs -eq $goAbs))
            cwd      = $cwd
            driveCwd = $driveCwd
            path     = $checkPath
            winStat  = $winStat
            goStat   = $goStat
            winAbs   = $winAbs
            goAbs    = $goAbs
         }
      }
   }
}

@bep bep modified the milestones: v0.126.0, v0.127.0 May 15, 2024
@bep bep modified the milestones: v0.127.0, v0.128.0 Jun 8, 2024
@bep bep modified the milestones: v0.128.0, v0.129.0 Jun 21, 2024
@bep bep modified the milestones: v0.129.0, v0.131.0 Jul 22, 2024
@bep bep modified the milestones: v0.131.0, v0.133.0 Aug 9, 2024
@bep bep modified the milestones: v0.133.0, Unscheduled Aug 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants