Example #1
0
func (f *Filesystem) captureFiles() {
	walker := func(filePath string, fi os.FileInfo, err error) error {
		if err != nil {
			return nil
		}

		b, err := f.shouldRead(filePath, fi)
		if err != nil {
			return err
		}
		if b {
			rd, err := NewLazyFileReader(hugofs.Source(), filePath)
			if err != nil {
				return err
			}
			f.add(filePath, rd)
		}
		return err
	}

	err := helpers.SymbolicWalk(hugofs.Source(), f.Base, walker)

	if err != nil {
		jww.ERROR.Println(err)
	}

}
Example #2
0
func (f *Filesystem) captureFiles() {
	walker := func(filePath string, fi os.FileInfo, err error) error {
		if err != nil {
			return nil
		}

		b, err := f.shouldRead(filePath, fi)
		if err != nil {
			return err
		}
		if b {
			rd, err := NewLazyFileReader(hugofs.Source(), filePath)
			if err != nil {
				return err
			}
			f.add(filePath, rd)
		}
		return err
	}

	err := helpers.SymbolicWalk(hugofs.Source(), f.Base, walker)

	if err != nil {
		jww.ERROR.Println(err)
		if err == helpers.WalkRootTooShortError {
			panic("The root path is too short. If this is a test, make sure to init the content paths.")
		}
	}

}
Example #3
0
func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) error {
	t.checkState()
	// get the suffix and switch on that
	ext := filepath.Ext(path)
	switch ext {
	case ".amber":
		templateName := strings.TrimSuffix(name, filepath.Ext(name)) + ".html"
		compiler := amber.New()
		b, err := afero.ReadFile(hugofs.Source(), path)

		if err != nil {
			return err
		}

		// Parse the input data
		if err := compiler.ParseData(b, path); err != nil {
			return err
		}

		if _, err := compiler.CompileWithTemplate(t.New(templateName)); err != nil {
			return err
		}
	case ".ace":
		var innerContent, baseContent []byte
		innerContent, err := afero.ReadFile(hugofs.Source(), path)

		if err != nil {
			return err
		}

		if baseTemplatePath != "" {
			baseContent, err = afero.ReadFile(hugofs.Source(), baseTemplatePath)
			if err != nil {
				return err
			}
		}

		return t.AddAceTemplate(name, baseTemplatePath, path, baseContent, innerContent)
	default:

		if baseTemplatePath != "" {
			return t.AddTemplateFileWithMaster(name, path, baseTemplatePath)
		}

		b, err := afero.ReadFile(hugofs.Source(), path)

		if err != nil {
			return err
		}

		jww.DEBUG.Printf("Add template file from path %s", path)

		return t.AddTemplate(name, string(b))
	}

	return nil

}
Example #4
0
func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
	basepath := filepath.Join(os.TempDir(), "blog")
	configPath := filepath.Join(basepath, "config.toml")
	hugofs.InitMemFs()
	hugofs.Source().MkdirAll(basepath, 777)
	hugofs.Source().Create(configPath)
	err := doNewSite(basepath, true)
	assert.NotNil(t, err)
}
// resGetResource loads the content of a local or remote file
func resGetResource(url string) ([]byte, error) {
	if url == "" {
		return nil, nil
	}
	if strings.Contains(url, "://") {
		return resGetRemote(url, hugofs.Source(), http.DefaultClient)
	}
	return resGetLocal(url, hugofs.Source())
}
Example #6
0
func TestDoNewSite_error_base_exists(t *testing.T) {
	basepath := filepath.Join(os.TempDir(), "blog")
	hugofs.InitMemFs()
	hugofs.Source().MkdirAll(basepath, 777)
	hugofs.Source().Create(filepath.Join(basepath, "foo"))
	// Since the directory already exists and isn't empty, expect an error
	err := doNewSite(basepath, false)
	assert.NotNil(t, err)
}
Example #7
0
File: hugo.go Project: tischda/hugo
// getDirList provides NewWatcher() with a list of directories to watch for changes.
func getDirList() []string {
	var a []string
	dataDir := helpers.AbsPathify(viper.GetString("DataDir"))
	layoutDir := helpers.AbsPathify(viper.GetString("LayoutDir"))
	walker := func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			if path == dataDir && os.IsNotExist(err) {
				jww.WARN.Println("Skip DataDir:", err)
				return nil

			}
			if path == layoutDir && os.IsNotExist(err) {
				jww.WARN.Println("Skip LayoutDir:", err)
				return nil

			}
			jww.ERROR.Println("Walker: ", err)
			return nil
		}

		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
			link, err := filepath.EvalSymlinks(path)
			if err != nil {
				jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", path, err)
				return nil
			}
			linkfi, err := os.Stat(link)
			if err != nil {
				jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
				return nil
			}
			if !linkfi.Mode().IsRegular() {
				jww.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", path)
			}
			return nil
		}

		if fi.IsDir() {
			if fi.Name() == ".git" ||
				fi.Name() == "node_modules" || fi.Name() == "bower_components" {
				return filepath.SkipDir
			}
			a = append(a, path)
		}
		return nil
	}

	helpers.SymbolicWalk(hugofs.Source(), dataDir, walker)
	helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("ContentDir")), walker)
	helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("LayoutDir")), walker)
	helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("StaticDir")), walker)
	if helpers.ThemeSet() {
		helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("themesDir")+"/"+viper.GetString("theme")), walker)
	}

	return a
}
Example #8
0
func testCommonResetState() {
	hugofs.InitMemFs()
	viper.Reset()
	viper.SetFs(hugofs.Source())
	loadDefaultSettings()

	// Default is false, but true is easier to use as default in tests
	viper.Set("DefaultContentLanguageInSubdir", true)

	if err := hugofs.Source().Mkdir("content", 0755); err != nil {
		panic("Content folder creation failed.")
	}

}
Example #9
0
func doNewSite(basepath string, force bool) error {
	dirs := []string{
		filepath.Join(basepath, "layouts"),
		filepath.Join(basepath, "content"),
		filepath.Join(basepath, "archetypes"),
		filepath.Join(basepath, "static"),
		filepath.Join(basepath, "data"),
		filepath.Join(basepath, "themes"),
	}

	if exists, _ := helpers.Exists(basepath, hugofs.Source()); exists {
		if isDir, _ := helpers.IsDir(basepath, hugofs.Source()); !isDir {
			return errors.New(basepath + " already exists but not a directory")
		}

		isEmpty, _ := helpers.IsEmpty(basepath, hugofs.Source())

		switch {
		case !isEmpty && !force:
			return errors.New(basepath + " already exists and is not empty")

		case !isEmpty && force:
			all := append(dirs, filepath.Join(basepath, "config."+configFormat))
			for _, path := range all {
				if exists, _ := helpers.Exists(path, hugofs.Source()); exists {
					return errors.New(path + " already exists")
				}
			}
		}
	}

	for _, dir := range dirs {
		hugofs.Source().MkdirAll(dir, 0777)
	}

	createConfig(basepath, configFormat)

	jww.FEEDBACK.Printf("Congratulations! Your new Hugo site is created in %q.\n\n", basepath)
	jww.FEEDBACK.Println(`Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder. Choose a theme from https://themes.gohugo.io or
   create your own with the "hugo new theme <THEMENAME>" command
2. Perhaps you want to add some content. You can add single files with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>"
3. Start the built-in live server via "hugo server"

For more information read the documentation at https://gohugo.io.`)

	return nil
}
Example #10
0
func (t *GoHTMLTemplate) AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error {

	// There is currently no known way to associate a cloned template with an existing one.
	// This funky master/overlay design will hopefully improve in a future version of Go.
	//
	// Simplicity is hard.
	//
	// Until then we'll have to live with this hackery.
	//
	// See https://github.com/golang/go/issues/14285
	//
	// So, to do minimum amount of changes to get this to work:
	//
	// 1. Lookup or Parse the master
	// 2. Parse and store the overlay in a separate map

	masterTpl := t.Lookup(masterFilename)

	if masterTpl == nil {
		b, err := afero.ReadFile(hugofs.Source(), masterFilename)
		if err != nil {
			return err
		}
		masterTpl, err = t.New(masterFilename).Parse(string(b))

		if err != nil {
			// TODO(bep) Add a method that does this
			t.errors = append(t.errors, &templateErr{name: name, err: err})
			return err
		}
	}

	b, err := afero.ReadFile(hugofs.Source(), overlayFilename)
	if err != nil {
		return err
	}

	overlayTpl, err := template.Must(masterTpl.Clone()).Parse(string(b))
	if err != nil {
		t.errors = append(t.errors, &templateErr{name: name, err: err})
	} else {
		// The extra lookup is a workaround, see
		// * https://github.com/golang/go/issues/16101
		// * https://github.com/spf13/hugo/issues/2549
		t.overlays[name] = overlayTpl.Lookup(overlayTpl.Name())
	}

	return err
}
Example #11
0
// NewContent adds new content to a Hugo site.
func NewContent(cmd *cobra.Command, args []string) error {
	if err := InitializeConfig(); err != nil {
		return err
	}

	if flagChanged(cmd.Flags(), "format") {
		viper.Set("MetaDataFormat", configFormat)
	}

	if flagChanged(cmd.Flags(), "editor") {
		viper.Set("NewContentEditor", contentEditor)
	}

	if len(args) < 1 {
		return newUserError("path needs to be provided")
	}

	createpath := args[0]

	var kind string

	createpath, kind = newContentPathSection(createpath)

	if contentType != "" {
		kind = contentType
	}

	return create.NewContent(hugofs.Source(), kind, createpath)
}
Example #12
0
func createThemeMD(inpath string) (err error) {

	by := []byte(`# theme.toml template for a Hugo theme
# See https://github.com/spf13/hugoThemes#themetoml for an example

name = "` + strings.Title(helpers.MakeTitle(filepath.Base(inpath))) + `"
license = "MIT"
licenselink = "https://github.com/yourname/yourtheme/blob/master/LICENSE.md"
description = ""
homepage = "http://siteforthistheme.com/"
tags = ["", ""]
features = ["", ""]
min_version = 0.15

[author]
  name = ""
  homepage = ""

# If porting an existing theme
[original]
  name = ""
  homepage = ""
  repo = ""
`)

	err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), hugofs.Source())
	if err != nil {
		return
	}

	return nil
}
// getCSV expects a data separator and one or n-parts of a URL to a resource which
// can either be a local or a remote one.
// The data separator can be a comma, semi-colon, pipe, etc, but only one character.
// If you provide multiple parts for the URL they will be joined together to the final URL.
// GetCSV returns nil or a slice slice to use in a short code.
func getCSV(sep string, urlParts ...string) [][]string {
	var d [][]string
	url := strings.Join(urlParts, "")

	var clearCacheSleep = func(i int, u string) {
		jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
		time.Sleep(resSleep)
		resDeleteCache(url, hugofs.Source())
	}

	for i := 0; i <= resRetries; i++ {
		c, err := resGetResource(url)

		if err == nil && false == bytes.Contains(c, []byte(sep)) {
			err = errors.New("Cannot find separator " + sep + " in CSV.")
		}

		if err != nil {
			jww.ERROR.Printf("Failed to read csv resource %s with error message %s", url, err)
			clearCacheSleep(i, url)
			continue
		}

		if d, err = parseCSV(c, sep); err != nil {
			jww.ERROR.Printf("Failed to parse csv file %s with error message %s", url, err)
			clearCacheSleep(i, url)
			continue
		}
		break
	}
	return d
}
Example #14
0
func (p *Page) saveSource(by []byte, inpath string, safe bool) (err error) {
	if !filepath.IsAbs(inpath) {
		inpath = helpers.AbsPathify(inpath)
	}
	jww.INFO.Println("creating", inpath)

	if safe {
		err = helpers.SafeWriteToDisk(inpath, bytes.NewReader(by), hugofs.Source())
	} else {
		err = helpers.WriteToDisk(inpath, bytes.NewReader(by), hugofs.Source())
	}
	if err != nil {
		return
	}
	return nil
}
Example #15
0
func loadJekyllConfig(jekyllRoot string) map[string]interface{} {
	fs := hugofs.Source()
	path := filepath.Join(jekyllRoot, "_config.yml")

	exists, err := helpers.Exists(path, fs)

	if err != nil || !exists {
		jww.WARN.Println("_config.yaml not found: Is the specified Jekyll root correct?")
		return nil
	}

	f, err := fs.Open(path)
	if err != nil {
		return nil
	}

	defer f.Close()

	b, err := ioutil.ReadAll(f)

	if err != nil {
		return nil
	}

	c, err := parser.HandleYAMLMetaData(b)

	if err != nil {
		return nil
	}

	return c.(map[string]interface{})
}
Example #16
0
func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
	basepath := filepath.Join(os.TempDir(), "blog")
	hugofs.InitMemFs()
	hugofs.Source().MkdirAll(basepath, 777)
	err := doNewSite(basepath, false)
	assert.Nil(t, err)
}
Example #17
0
func (f *Filesystem) shouldRead(filePath string, fi os.FileInfo) (bool, error) {
	if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
		link, err := filepath.EvalSymlinks(filePath)
		if err != nil {
			jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", filePath, err)
			return false, nil
		}
		linkfi, err := hugofs.Source().Stat(link)
		if err != nil {
			jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
			return false, nil
		}
		if !linkfi.Mode().IsRegular() {
			jww.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", filePath)
		}
		return false, nil
	}

	if fi.IsDir() {
		if f.avoid(filePath) || isNonProcessablePath(filePath) {
			return false, filepath.SkipDir
		}
		return false, nil
	}

	if isNonProcessablePath(filePath) {
		return false, nil
	}
	return true, nil
}
Example #18
0
func initFs() error {
	hugofs.SetSource(new(afero.MemMapFs))
	perm := os.FileMode(0755)
	var err error

	// create directories
	dirs := []string{
		"archetypes",
		"content",
		filepath.Join("themes", "sample", "archetypes"),
	}
	for _, dir := range dirs {
		dir = filepath.Join(os.TempDir(), dir)
		err = hugofs.Source().Mkdir(dir, perm)
		if err != nil {
			return err
		}
	}

	// create files
	for _, v := range []struct {
		path    string
		content string
	}{
		{
			path:    filepath.Join(os.TempDir(), "archetypes", "post.md"),
			content: "+++\ndate = \"2015-01-12T19:20:04-07:00\"\ntitle = \"post arch\"\ntest = \"test1\"\n+++\n",
		},
		{
			path:    filepath.Join(os.TempDir(), "archetypes", "product.md"),
			content: "+++\n+++\n",
		},
	} {
		f, err := hugofs.Source().Create(v.path)
		if err != nil {
			return err
		}
		defer f.Close()

		_, err = f.Write([]byte(v.content))
		if err != nil {
			return err
		}
	}

	return nil
}
Example #19
0
func touchFile(x ...string) {
	inpath := filepath.Join(x...)
	mkdir(filepath.Dir(inpath))
	err := helpers.WriteToDisk(inpath, bytes.NewReader([]byte{}), hugofs.Source())
	if err != nil {
		jww.FATAL.Fatalln(err)
	}
}
Example #20
0
func TestDoNewSite_force_empty_dir(t *testing.T) {
	basepath := filepath.Join(os.TempDir(), "blog")
	hugofs.InitMemFs()
	hugofs.Source().MkdirAll(basepath, 777)
	err := doNewSite(basepath, true)
	assert.Nil(t, err)

	checkNewSiteInited(basepath, t)
}
Example #21
0
func getStaticSourceFs() afero.Fs {
	source := hugofs.Source()
	themeDir, err := helpers.GetThemeStaticDirPath()
	staticDir := helpers.GetStaticDirPath() + helpers.FilePathSeparator

	useTheme := true
	useStatic := true

	if err != nil {
		jww.WARN.Println(err)
		useTheme = false
	} else {
		if _, err := source.Stat(themeDir); os.IsNotExist(err) {
			jww.WARN.Println("Unable to find Theme Static Directory:", themeDir)
			useTheme = false
		}
	}

	if _, err := source.Stat(staticDir); os.IsNotExist(err) {
		jww.WARN.Println("Unable to find Static Directory:", staticDir)
		useStatic = false
	}

	if !useStatic && !useTheme {
		return nil
	}

	if !useStatic {
		jww.INFO.Println(themeDir, "is the only static directory available to sync from")
		return afero.NewReadOnlyFs(afero.NewBasePathFs(source, themeDir))
	}

	if !useTheme {
		jww.INFO.Println(staticDir, "is the only static directory available to sync from")
		return afero.NewReadOnlyFs(afero.NewBasePathFs(source, staticDir))
	}

	jww.INFO.Println("using a UnionFS for static directory comprised of:")
	jww.INFO.Println("Base:", themeDir)
	jww.INFO.Println("Overlay:", staticDir)
	base := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.Source(), themeDir))
	overlay := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.Source(), staticDir))
	return afero.NewCopyOnWriteFs(base, overlay)
}
func testRetryWhenDone() wd {
	cd := viper.GetString("cacheDir")
	viper.Set("cacheDir", helpers.GetTempDir("", hugofs.Source()))
	var tmpSleep time.Duration
	tmpSleep, resSleep = resSleep, time.Millisecond
	return wd{func() {
		viper.Set("cacheDir", cd)
		resSleep = tmpSleep
	}}
}
Example #23
0
func doNewSite(basepath string, force bool) error {
	dirs := []string{
		filepath.Join(basepath, "layouts"),
		filepath.Join(basepath, "content"),
		filepath.Join(basepath, "archetypes"),
		filepath.Join(basepath, "static"),
		filepath.Join(basepath, "data"),
		filepath.Join(basepath, "themes"),
	}

	if exists, _ := helpers.Exists(basepath, hugofs.Source()); exists {
		if isDir, _ := helpers.IsDir(basepath, hugofs.Source()); !isDir {
			return errors.New(basepath + " already exists but not a directory")
		}

		isEmpty, _ := helpers.IsEmpty(basepath, hugofs.Source())

		switch {
		case !isEmpty && !force:
			return errors.New(basepath + " already exists and is not empty")

		case !isEmpty && force:
			all := append(dirs, filepath.Join(basepath, "config."+configFormat))
			for _, path := range all {
				if exists, _ := helpers.Exists(path, hugofs.Source()); exists {
					return errors.New(path + " already exists")
				}
			}
		}
	}

	for _, dir := range dirs {
		hugofs.Source().MkdirAll(dir, 0777)
	}

	createConfig(basepath, configFormat)

	jww.FEEDBACK.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", basepath)
	jww.FEEDBACK.Println(nextStepsText())

	return nil
}
Example #24
0
func TestNewContent(t *testing.T) {
	initViper()

	err := initFs()
	if err != nil {
		t.Fatalf("initialization error: %s", err)
	}

	cases := []struct {
		kind          string
		path          string
		resultStrings []string
	}{
		{"post", "post/sample-1.md", []string{`title = "sample 1"`, `test = "test1"`}},
		{"stump", "stump/sample-2.md", []string{`title = "sample 2"`}},     // no archetype file
		{"", "sample-3.md", []string{`title = "sample 3"`}},                // no archetype
		{"product", "product/sample-4.md", []string{`title = "sample 4"`}}, // empty archetype front matter
	}

	for i, c := range cases {
		err = create.NewContent(hugofs.Source(), c.kind, c.path)
		if err != nil {
			t.Errorf("[%d] NewContent: %s", i, err)
		}

		fname := filepath.Join(os.TempDir(), "content", filepath.FromSlash(c.path))
		_, err = hugofs.Source().Stat(fname)
		if err != nil {
			t.Errorf("[%d] Stat: %s", i, err)
		}

		for _, v := range c.resultStrings {
			found, err := afero.FileContainsBytes(hugofs.Source(), fname, []byte(v))
			if err != nil {
				t.Errorf("[%d] FileContainsBytes: %s", i, err)
			}
			if !found {
				t.Errorf("content missing from output: %q", v)
			}
		}
	}
}
Example #25
0
func getThemeDirPath(path string) (string, error) {
	if !ThemeSet() {
		return "", errors.New("No theme set")
	}

	themeDir := filepath.Join(GetThemeDir(), path)
	if _, err := hugofs.Source().Stat(themeDir); os.IsNotExist(err) {
		return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, viper.GetString("theme"), themeDir)
	}

	return themeDir, nil
}
Example #26
0
File: hugo.go Project: tischda/hugo
// isThemeVsHugoVersionMismatch returns whether the current Hugo version is
// less than the theme's min_version.
func isThemeVsHugoVersionMismatch() (mismatch bool, requiredMinVersion string) {
	if !helpers.ThemeSet() {
		return
	}

	themeDir := helpers.GetThemeDir()

	fs := hugofs.Source()
	path := filepath.Join(themeDir, "theme.toml")

	exists, err := helpers.Exists(path, fs)

	if err != nil || !exists {
		return
	}

	f, err := fs.Open(path)

	if err != nil {
		return
	}

	defer f.Close()

	b, err := ioutil.ReadAll(f)

	if err != nil {
		return
	}

	c, err := parser.HandleTOMLMetaData(b)

	if err != nil {
		return
	}

	config := c.(map[string]interface{})

	if minVersion, ok := config["min_version"]; ok {
		switch minVersion.(type) {
		case float32:
			return helpers.HugoVersionNumber < minVersion.(float32), fmt.Sprint(minVersion)
		case float64:
			return helpers.HugoVersionNumber < minVersion.(float64), fmt.Sprint(minVersion)
		default:
			return
		}

	}

	return
}
Example #27
0
// Undraft publishes the specified content by setting its draft status
// to false and setting its publish date to now. If the specified content is
// not a draft, it will log an error.
func Undraft(cmd *cobra.Command, args []string) error {
	if err := InitializeConfig(); err != nil {
		return err
	}

	if len(args) < 1 {
		return newUserError("a piece of content needs to be specified")
	}

	location := args[0]
	// open the file
	f, err := hugofs.Source().Open(location)
	if err != nil {
		return err
	}

	// get the page from file
	p, err := parser.ReadFrom(f)
	f.Close()
	if err != nil {
		return err
	}

	w, err := undraftContent(p)
	if err != nil {
		return newSystemErrorF("an error occurred while undrafting %q: %s", location, err)
	}

	f, err = hugofs.Source().OpenFile(location, os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		return newSystemErrorF("%q not be undrafted due to error opening file to save changes: %q\n", location, err)
	}
	defer f.Close()
	_, err = w.WriteTo(f)
	if err != nil {
		return newSystemErrorF("%q not be undrafted due to save error: %q\n", location, err)
	}
	return nil
}
Example #28
0
func checkNewSiteInited(basepath string, t *testing.T) {
	paths := []string{
		filepath.Join(basepath, "layouts"),
		filepath.Join(basepath, "content"),
		filepath.Join(basepath, "archetypes"),
		filepath.Join(basepath, "static"),
		filepath.Join(basepath, "data"),
		filepath.Join(basepath, "config.toml"),
	}

	for _, path := range paths {
		_, err := hugofs.Source().Stat(path)
		assert.Nil(t, err)
	}
}
Example #29
0
// TODO: Consider calling doNewSite() instead?
func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) error {
	fs := hugofs.Source()
	if exists, _ := helpers.Exists(targetDir, fs); exists {
		if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
			return errors.New("Target path \"" + targetDir + "\" already exists but not a directory")
		}

		isEmpty, _ := helpers.IsEmpty(targetDir, fs)

		if !isEmpty && !force {
			return errors.New("Target path \"" + targetDir + "\" already exists and is not empty")
		}
	}

	jekyllConfig := loadJekyllConfig(jekyllRoot)

	// Crude test to make sure at least one of _drafts/ and _posts/ exists
	// and is not empty.
	hasPostsOrDrafts := false
	postsDir := filepath.Join(jekyllRoot, "_posts")
	draftsDir := filepath.Join(jekyllRoot, "_drafts")
	for _, d := range []string{postsDir, draftsDir} {
		if exists, _ := helpers.Exists(d, fs); exists {
			if isDir, _ := helpers.IsDir(d, fs); isDir {
				if isEmpty, _ := helpers.IsEmpty(d, fs); !isEmpty {
					hasPostsOrDrafts = true
				}
			}
		}
	}
	if !hasPostsOrDrafts {
		return errors.New("Your Jekyll root contains neither posts nor drafts, aborting.")
	}

	mkdir(targetDir, "layouts")
	mkdir(targetDir, "content")
	mkdir(targetDir, "archetypes")
	mkdir(targetDir, "static")
	mkdir(targetDir, "data")
	mkdir(targetDir, "themes")

	createConfigFromJekyll(targetDir, "yaml", jekyllConfig)

	copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"))

	return nil
}
// getJSON expects one or n-parts of a URL to a resource which can either be a local or a remote one.
// If you provide multiple parts they will be joined together to the final URL.
// GetJSON returns nil or parsed JSON to use in a short code.
func getJSON(urlParts ...string) interface{} {
	var v interface{}
	url := strings.Join(urlParts, "")

	for i := 0; i <= resRetries; i++ {
		c, err := resGetResource(url)
		if err != nil {
			jww.ERROR.Printf("Failed to get json resource %s with error message %s", url, err)
			return nil
		}

		err = json.Unmarshal(c, &v)
		if err != nil {
			jww.ERROR.Printf("Cannot read json from resource %s with error message %s", url, err)
			jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
			time.Sleep(resSleep)
			resDeleteCache(url, hugofs.Source())
			continue
		}
		break
	}
	return v
}