mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
parent
4b780ca778
commit
56a1308044
7 changed files with 415 additions and 271 deletions
|
@ -20,16 +20,29 @@ import (
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
)
|
)
|
||||||
|
|
||||||
var envCmd = &cobra.Command{
|
var _ cmder = (*envCmd)(nil)
|
||||||
Use: "env",
|
|
||||||
Short: "Print Hugo version and environment info",
|
|
||||||
Long: `Print Hugo version and environment info. This is useful in Hugo bug reports.`,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
printHugoVersion()
|
|
||||||
jww.FEEDBACK.Printf("GOOS=%q\n", runtime.GOOS)
|
|
||||||
jww.FEEDBACK.Printf("GOARCH=%q\n", runtime.GOARCH)
|
|
||||||
jww.FEEDBACK.Printf("GOVERSION=%q\n", runtime.Version())
|
|
||||||
|
|
||||||
return nil
|
type envCmd struct {
|
||||||
},
|
cmd *cobra.Command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *envCmd) getCommand() *cobra.Command {
|
||||||
|
return c.cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEnvCmd() *envCmd {
|
||||||
|
return &envCmd{cmd: &cobra.Command{
|
||||||
|
Use: "env",
|
||||||
|
Short: "Print Hugo version and environment info",
|
||||||
|
Long: `Print Hugo version and environment info. This is useful in Hugo bug reports.`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
printHugoVersion()
|
||||||
|
jww.FEEDBACK.Printf("GOOS=%q\n", runtime.GOOS)
|
||||||
|
jww.FEEDBACK.Printf("GOARCH=%q\n", runtime.GOARCH)
|
||||||
|
jww.FEEDBACK.Printf("GOVERSION=%q\n", runtime.Version())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,13 +194,13 @@ func Execute() {
|
||||||
// AddCommands adds child commands to the root command HugoCmd.
|
// AddCommands adds child commands to the root command HugoCmd.
|
||||||
func AddCommands() {
|
func AddCommands() {
|
||||||
HugoCmd.AddCommand(serverCmd)
|
HugoCmd.AddCommand(serverCmd)
|
||||||
HugoCmd.AddCommand(versionCmd)
|
HugoCmd.AddCommand(newVersionCmd().getCommand())
|
||||||
HugoCmd.AddCommand(envCmd)
|
HugoCmd.AddCommand(newEnvCmd().getCommand())
|
||||||
HugoCmd.AddCommand(configCmd)
|
HugoCmd.AddCommand(configCmd)
|
||||||
HugoCmd.AddCommand(newCheckCmd().getCommand())
|
HugoCmd.AddCommand(newCheckCmd().getCommand())
|
||||||
HugoCmd.AddCommand(newBenchmarkCmd().getCommand())
|
HugoCmd.AddCommand(newBenchmarkCmd().getCommand())
|
||||||
HugoCmd.AddCommand(newConvertCmd().getCommand())
|
HugoCmd.AddCommand(newConvertCmd().getCommand())
|
||||||
HugoCmd.AddCommand(newCmd)
|
HugoCmd.AddCommand(newNewCmd().getCommand())
|
||||||
HugoCmd.AddCommand(listCmd)
|
HugoCmd.AddCommand(listCmd)
|
||||||
HugoCmd.AddCommand(importCmd)
|
HugoCmd.AddCommand(importCmd)
|
||||||
|
|
||||||
|
|
277
commands/new.go
277
commands/new.go
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2016 The Hugo Authors. All rights reserved.
|
// Copyright 2018 The Hugo Authors. All rights reserved.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -15,80 +15,64 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/create"
|
"github.com/gohugoio/hugo/create"
|
||||||
"github.com/gohugoio/hugo/helpers"
|
"github.com/gohugoio/hugo/helpers"
|
||||||
"github.com/gohugoio/hugo/hugofs"
|
|
||||||
"github.com/gohugoio/hugo/hugolib"
|
"github.com/gohugoio/hugo/hugolib"
|
||||||
"github.com/gohugoio/hugo/parser"
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var _ cmder = (*newCmd)(nil)
|
||||||
configFormat string
|
|
||||||
|
type newCmd struct {
|
||||||
contentEditor string
|
contentEditor string
|
||||||
contentType string
|
contentType string
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
newSiteCmd.Flags().StringVarP(&configFormat, "format", "f", "toml", "config & frontmatter format")
|
|
||||||
newSiteCmd.Flags().Bool("force", false, "init inside non-empty directory")
|
|
||||||
newCmd.Flags().StringVarP(&contentType, "kind", "k", "", "content type to create")
|
|
||||||
newCmd.PersistentFlags().StringVarP(&source, "source", "s", "", "filesystem path to read files relative from")
|
|
||||||
newCmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
|
||||||
newCmd.Flags().StringVar(&contentEditor, "editor", "", "edit new content with this editor, if provided")
|
|
||||||
|
|
||||||
newCmd.AddCommand(newSiteCmd)
|
|
||||||
newCmd.AddCommand(newThemeCmd)
|
|
||||||
|
|
||||||
|
cmd *cobra.Command
|
||||||
}
|
}
|
||||||
|
|
||||||
var newCmd = &cobra.Command{
|
func (c *newCmd) getCommand() *cobra.Command {
|
||||||
Use: "new [path]",
|
return c.cmd
|
||||||
Short: "Create new content for your site",
|
}
|
||||||
Long: `Create a new content file and automatically set the date and title.
|
|
||||||
|
func newNewCmd() *newCmd {
|
||||||
|
ccmd := &newCmd{}
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "new [path]",
|
||||||
|
Short: "Create new content for your site",
|
||||||
|
Long: `Create a new content file and automatically set the date and title.
|
||||||
It will guess which kind of file to create based on the path provided.
|
It will guess which kind of file to create based on the path provided.
|
||||||
|
|
||||||
You can also specify the kind with ` + "`-k KIND`" + `.
|
You can also specify the kind with ` + "`-k KIND`" + `.
|
||||||
|
|
||||||
If archetypes are provided in your theme or site, they will be used.`,
|
If archetypes are provided in your theme or site, they will be used.`,
|
||||||
|
|
||||||
RunE: NewContent,
|
RunE: ccmd.newContent,
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringVarP(&ccmd.contentType, "kind", "k", "", "content type to create")
|
||||||
|
// TODO(bep) cli refactor
|
||||||
|
cmd.PersistentFlags().StringVarP(&source, "source", "s", "", "filesystem path to read files relative from")
|
||||||
|
cmd.PersistentFlags().SetAnnotation("source", cobra.BashCompSubdirsInDir, []string{})
|
||||||
|
cmd.Flags().StringVar(&ccmd.contentEditor, "editor", "", "edit new content with this editor, if provided")
|
||||||
|
|
||||||
|
cmd.AddCommand(newNewSiteCmd().getCommand())
|
||||||
|
cmd.AddCommand(newNewThemeCmd().getCommand())
|
||||||
|
|
||||||
|
ccmd.cmd = cmd
|
||||||
|
|
||||||
|
return ccmd
|
||||||
}
|
}
|
||||||
|
|
||||||
var newSiteCmd = &cobra.Command{
|
func (n *newCmd) newContent(cmd *cobra.Command, args []string) error {
|
||||||
Use: "site [path]",
|
|
||||||
Short: "Create a new site (skeleton)",
|
|
||||||
Long: `Create a new site in the provided directory.
|
|
||||||
The new site will have the correct structure, but no content or theme yet.
|
|
||||||
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
|
||||||
RunE: NewSite,
|
|
||||||
}
|
|
||||||
|
|
||||||
var newThemeCmd = &cobra.Command{
|
|
||||||
Use: "theme [name]",
|
|
||||||
Short: "Create a new theme",
|
|
||||||
Long: `Create a new theme (skeleton) called [name] in the current directory.
|
|
||||||
New theme is a skeleton. Please add content to the touched files. Add your
|
|
||||||
name to the copyright line in the license and adjust the theme.toml file
|
|
||||||
as you see fit.`,
|
|
||||||
RunE: NewTheme,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContent adds new content to a Hugo site.
|
|
||||||
func NewContent(cmd *cobra.Command, args []string) error {
|
|
||||||
cfgInit := func(c *commandeer) error {
|
cfgInit := func(c *commandeer) error {
|
||||||
if cmd.Flags().Changed("editor") {
|
if cmd.Flags().Changed("editor") {
|
||||||
c.Set("newContentEditor", contentEditor)
|
c.Set("newContentEditor", n.contentEditor)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -109,8 +93,8 @@ func NewContent(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
createPath, kind = newContentPathSection(createPath)
|
createPath, kind = newContentPathSection(createPath)
|
||||||
|
|
||||||
if contentType != "" {
|
if n.contentType != "" {
|
||||||
kind = contentType
|
kind = n.contentType
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := c.DepsCfg
|
cfg := c.DepsCfg
|
||||||
|
@ -151,56 +135,6 @@ func NewContent(cmd *cobra.Command, args []string) error {
|
||||||
return create.NewContent(ps, siteFactory, kind, createPath)
|
return create.NewContent(ps, siteFactory, kind, createPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func doNewSite(fs *hugofs.Fs, basepath string, force bool) error {
|
|
||||||
archeTypePath := filepath.Join(basepath, "archetypes")
|
|
||||||
dirs := []string{
|
|
||||||
filepath.Join(basepath, "layouts"),
|
|
||||||
filepath.Join(basepath, "content"),
|
|
||||||
archeTypePath,
|
|
||||||
filepath.Join(basepath, "static"),
|
|
||||||
filepath.Join(basepath, "data"),
|
|
||||||
filepath.Join(basepath, "themes"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if exists, _ := helpers.Exists(basepath, fs.Source); exists {
|
|
||||||
if isDir, _ := helpers.IsDir(basepath, fs.Source); !isDir {
|
|
||||||
return errors.New(basepath + " already exists but not a directory")
|
|
||||||
}
|
|
||||||
|
|
||||||
isEmpty, _ := helpers.IsEmpty(basepath, fs.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, fs.Source); exists {
|
|
||||||
return errors.New(path + " already exists")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dir := range dirs {
|
|
||||||
if err := fs.Source.MkdirAll(dir, 0777); err != nil {
|
|
||||||
return fmt.Errorf("Failed to create dir: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
createConfig(fs, basepath, configFormat)
|
|
||||||
|
|
||||||
// Create a defaul archetype file.
|
|
||||||
helpers.SafeWriteToDisk(filepath.Join(archeTypePath, "default.md"),
|
|
||||||
strings.NewReader(create.ArchetypeTemplateTemplate), fs.Source)
|
|
||||||
|
|
||||||
jww.FEEDBACK.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", basepath)
|
|
||||||
jww.FEEDBACK.Println(nextStepsText())
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextStepsText() string {
|
func nextStepsText() string {
|
||||||
var nextStepsText bytes.Buffer
|
var nextStepsText bytes.Buffer
|
||||||
|
|
||||||
|
@ -222,98 +156,6 @@ Visit https://gohugo.io/ for quickstart guide and full documentation.`)
|
||||||
return nextStepsText.String()
|
return nextStepsText.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSite creates a new Hugo site and initializes a structured Hugo directory.
|
|
||||||
func NewSite(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return newUserError("path needs to be provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
createpath, err := filepath.Abs(filepath.Clean(args[0]))
|
|
||||||
if err != nil {
|
|
||||||
return newUserError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
forceNew, _ := cmd.Flags().GetBool("force")
|
|
||||||
|
|
||||||
return doNewSite(hugofs.NewDefault(viper.New()), createpath, forceNew)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewTheme creates a new Hugo theme.
|
|
||||||
func NewTheme(cmd *cobra.Command, args []string) error {
|
|
||||||
c, err := InitializeConfig(false, nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) < 1 {
|
|
||||||
return newUserError("theme name needs to be provided")
|
|
||||||
}
|
|
||||||
|
|
||||||
createpath := c.PathSpec().AbsPathify(filepath.Join(c.Cfg.GetString("themesDir"), args[0]))
|
|
||||||
jww.INFO.Println("creating theme at", createpath)
|
|
||||||
|
|
||||||
cfg := c.DepsCfg
|
|
||||||
|
|
||||||
if x, _ := helpers.Exists(createpath, cfg.Fs.Source); x {
|
|
||||||
return newUserError(createpath, "already exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
mkdir(createpath, "layouts", "_default")
|
|
||||||
mkdir(createpath, "layouts", "partials")
|
|
||||||
|
|
||||||
touchFile(cfg.Fs.Source, createpath, "layouts", "index.html")
|
|
||||||
touchFile(cfg.Fs.Source, createpath, "layouts", "404.html")
|
|
||||||
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "list.html")
|
|
||||||
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "single.html")
|
|
||||||
|
|
||||||
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "header.html")
|
|
||||||
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "footer.html")
|
|
||||||
|
|
||||||
mkdir(createpath, "archetypes")
|
|
||||||
|
|
||||||
archDefault := []byte("+++\n+++\n")
|
|
||||||
|
|
||||||
err = helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), cfg.Fs.Source)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
mkdir(createpath, "static", "js")
|
|
||||||
mkdir(createpath, "static", "css")
|
|
||||||
|
|
||||||
by := []byte(`The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) ` + time.Now().Format("2006") + ` YOUR_NAME_HERE
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
`)
|
|
||||||
|
|
||||||
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), cfg.Fs.Source)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
createThemeMD(cfg.Fs, createpath)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mkdir(x ...string) {
|
func mkdir(x ...string) {
|
||||||
p := filepath.Join(x...)
|
p := filepath.Join(x...)
|
||||||
|
|
||||||
|
@ -332,39 +174,7 @@ func touchFile(fs afero.Fs, x ...string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createThemeMD(fs *hugofs.Fs, inpath string) (err error) {
|
// TODO(bep) cli refactor => method
|
||||||
|
|
||||||
by := []byte(`# theme.toml template for a Hugo theme
|
|
||||||
# See https://github.com/gohugoio/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://example.com/"
|
|
||||||
tags = []
|
|
||||||
features = []
|
|
||||||
min_version = "0.38"
|
|
||||||
|
|
||||||
[author]
|
|
||||||
name = ""
|
|
||||||
homepage = ""
|
|
||||||
|
|
||||||
# If porting an existing theme
|
|
||||||
[original]
|
|
||||||
name = ""
|
|
||||||
homepage = ""
|
|
||||||
repo = ""
|
|
||||||
`)
|
|
||||||
|
|
||||||
err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), fs.Source)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func newContentPathSection(path string) (string, string) {
|
func newContentPathSection(path string) (string, string) {
|
||||||
// Forward slashes is used in all examples. Convert if needed.
|
// Forward slashes is used in all examples. Convert if needed.
|
||||||
// Issue #1133
|
// Issue #1133
|
||||||
|
@ -381,20 +191,3 @@ func newContentPathSection(path string) (string, string) {
|
||||||
|
|
||||||
return createpath, section
|
return createpath, section
|
||||||
}
|
}
|
||||||
|
|
||||||
func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
|
|
||||||
in := map[string]string{
|
|
||||||
"baseURL": "http://example.org/",
|
|
||||||
"title": "My New Hugo Site",
|
|
||||||
"languageCode": "en-us",
|
|
||||||
}
|
|
||||||
kind = parser.FormatSanitize(kind)
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err = parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind), &buf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), &buf, fs.Source)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2016 The Hugo Authors. All rights reserved.
|
// Copyright 2018 The Hugo Authors. All rights reserved.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -48,10 +48,12 @@ func checkNewSiteInited(fs *hugofs.Fs, basepath string, t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoNewSite(t *testing.T) {
|
func TestDoNewSite(t *testing.T) {
|
||||||
|
// TODO(bep) cli refactor
|
||||||
|
n := newNewSiteCmd()
|
||||||
basepath := filepath.Join("base", "blog")
|
basepath := filepath.Join("base", "blog")
|
||||||
_, fs := newTestCfg()
|
_, fs := newTestCfg()
|
||||||
|
|
||||||
require.NoError(t, doNewSite(fs, basepath, false))
|
require.NoError(t, n.doNewSite(fs, basepath, false))
|
||||||
|
|
||||||
checkNewSiteInited(fs, basepath, t)
|
checkNewSiteInited(fs, basepath, t)
|
||||||
}
|
}
|
||||||
|
@ -59,31 +61,34 @@ func TestDoNewSite(t *testing.T) {
|
||||||
func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
|
func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
|
||||||
basepath := filepath.Join("base", "blog")
|
basepath := filepath.Join("base", "blog")
|
||||||
_, fs := newTestCfg()
|
_, fs := newTestCfg()
|
||||||
|
n := newNewSiteCmd()
|
||||||
|
|
||||||
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
||||||
|
|
||||||
require.NoError(t, doNewSite(fs, basepath, false))
|
require.NoError(t, n.doNewSite(fs, basepath, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoNewSite_error_base_exists(t *testing.T) {
|
func TestDoNewSite_error_base_exists(t *testing.T) {
|
||||||
basepath := filepath.Join("base", "blog")
|
basepath := filepath.Join("base", "blog")
|
||||||
_, fs := newTestCfg()
|
_, fs := newTestCfg()
|
||||||
|
n := newNewSiteCmd()
|
||||||
|
|
||||||
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
||||||
_, err := fs.Source.Create(filepath.Join(basepath, "foo"))
|
_, err := fs.Source.Create(filepath.Join(basepath, "foo"))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// Since the directory already exists and isn't empty, expect an error
|
// Since the directory already exists and isn't empty, expect an error
|
||||||
require.Error(t, doNewSite(fs, basepath, false))
|
require.Error(t, n.doNewSite(fs, basepath, false))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoNewSite_force_empty_dir(t *testing.T) {
|
func TestDoNewSite_force_empty_dir(t *testing.T) {
|
||||||
basepath := filepath.Join("base", "blog")
|
basepath := filepath.Join("base", "blog")
|
||||||
_, fs := newTestCfg()
|
_, fs := newTestCfg()
|
||||||
|
n := newNewSiteCmd()
|
||||||
|
|
||||||
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
||||||
|
|
||||||
require.NoError(t, doNewSite(fs, basepath, true))
|
require.NoError(t, n.doNewSite(fs, basepath, true))
|
||||||
|
|
||||||
checkNewSiteInited(fs, basepath, t)
|
checkNewSiteInited(fs, basepath, t)
|
||||||
}
|
}
|
||||||
|
@ -91,23 +96,25 @@ func TestDoNewSite_force_empty_dir(t *testing.T) {
|
||||||
func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) {
|
func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) {
|
||||||
basepath := filepath.Join("base", "blog")
|
basepath := filepath.Join("base", "blog")
|
||||||
_, fs := newTestCfg()
|
_, fs := newTestCfg()
|
||||||
|
n := newNewSiteCmd()
|
||||||
|
|
||||||
contentPath := filepath.Join(basepath, "content")
|
contentPath := filepath.Join(basepath, "content")
|
||||||
|
|
||||||
require.NoError(t, fs.Source.MkdirAll(contentPath, 777))
|
require.NoError(t, fs.Source.MkdirAll(contentPath, 777))
|
||||||
require.Error(t, doNewSite(fs, basepath, true))
|
require.Error(t, n.doNewSite(fs, basepath, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
|
func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
|
||||||
basepath := filepath.Join("base", "blog")
|
basepath := filepath.Join("base", "blog")
|
||||||
_, fs := newTestCfg()
|
_, fs := newTestCfg()
|
||||||
|
n := newNewSiteCmd()
|
||||||
|
|
||||||
configPath := filepath.Join(basepath, "config.toml")
|
configPath := filepath.Join(basepath, "config.toml")
|
||||||
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
require.NoError(t, fs.Source.MkdirAll(basepath, 777))
|
||||||
_, err := fs.Source.Create(configPath)
|
_, err := fs.Source.Create(configPath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Error(t, doNewSite(fs, basepath, true))
|
require.Error(t, n.doNewSite(fs, basepath, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestCfg() (*viper.Viper, *hugofs.Fs) {
|
func newTestCfg() (*viper.Viper, *hugofs.Fs) {
|
152
commands/new_site.go
Normal file
152
commands/new_site.go
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
// Copyright 2018 The Hugo Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/create"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/parser"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/helpers"
|
||||||
|
"github.com/gohugoio/hugo/hugofs"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ cmder = (*newSiteCmd)(nil)
|
||||||
|
|
||||||
|
type newSiteCmd struct {
|
||||||
|
configFormat string
|
||||||
|
|
||||||
|
cmd *cobra.Command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *newSiteCmd) getCommand() *cobra.Command {
|
||||||
|
return c.cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNewSiteCmd() *newSiteCmd {
|
||||||
|
ccmd := &newSiteCmd{}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "site [path]",
|
||||||
|
Short: "Create a new site (skeleton)",
|
||||||
|
Long: `Create a new site in the provided directory.
|
||||||
|
The new site will have the correct structure, but no content or theme yet.
|
||||||
|
Use ` + "`hugo new [contentPath]`" + ` to create new content.`,
|
||||||
|
RunE: ccmd.newSite,
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Flags().StringVarP(&ccmd.configFormat, "format", "f", "toml", "config & frontmatter format")
|
||||||
|
cmd.Flags().Bool("force", false, "init inside non-empty directory")
|
||||||
|
|
||||||
|
ccmd.cmd = cmd
|
||||||
|
|
||||||
|
return ccmd
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *newSiteCmd) doNewSite(fs *hugofs.Fs, basepath string, force bool) error {
|
||||||
|
archeTypePath := filepath.Join(basepath, "archetypes")
|
||||||
|
dirs := []string{
|
||||||
|
filepath.Join(basepath, "layouts"),
|
||||||
|
filepath.Join(basepath, "content"),
|
||||||
|
archeTypePath,
|
||||||
|
filepath.Join(basepath, "static"),
|
||||||
|
filepath.Join(basepath, "data"),
|
||||||
|
filepath.Join(basepath, "themes"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if exists, _ := helpers.Exists(basepath, fs.Source); exists {
|
||||||
|
if isDir, _ := helpers.IsDir(basepath, fs.Source); !isDir {
|
||||||
|
return errors.New(basepath + " already exists but not a directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmpty, _ := helpers.IsEmpty(basepath, fs.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."+n.configFormat))
|
||||||
|
for _, path := range all {
|
||||||
|
if exists, _ := helpers.Exists(path, fs.Source); exists {
|
||||||
|
return errors.New(path + " already exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dir := range dirs {
|
||||||
|
if err := fs.Source.MkdirAll(dir, 0777); err != nil {
|
||||||
|
return fmt.Errorf("Failed to create dir: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createConfig(fs, basepath, n.configFormat)
|
||||||
|
|
||||||
|
// Create a defaul archetype file.
|
||||||
|
helpers.SafeWriteToDisk(filepath.Join(archeTypePath, "default.md"),
|
||||||
|
strings.NewReader(create.ArchetypeTemplateTemplate), fs.Source)
|
||||||
|
|
||||||
|
jww.FEEDBACK.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", basepath)
|
||||||
|
jww.FEEDBACK.Println(nextStepsText())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSite creates a new Hugo site and initializes a structured Hugo directory.
|
||||||
|
func (n *newSiteCmd) newSite(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return newUserError("path needs to be provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
createpath, err := filepath.Abs(filepath.Clean(args[0]))
|
||||||
|
if err != nil {
|
||||||
|
return newUserError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
forceNew, _ := cmd.Flags().GetBool("force")
|
||||||
|
|
||||||
|
return n.doNewSite(hugofs.NewDefault(viper.New()), createpath, forceNew)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
|
||||||
|
in := map[string]string{
|
||||||
|
"baseURL": "http://example.org/",
|
||||||
|
"title": "My New Hugo Site",
|
||||||
|
"languageCode": "en-us",
|
||||||
|
}
|
||||||
|
kind = parser.FormatSanitize(kind)
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err = parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind), &buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), &buf, fs.Source)
|
||||||
|
}
|
165
commands/new_theme.go
Normal file
165
commands/new_theme.go
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
// Copyright 2018 The Hugo Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/hugofs"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/helpers"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ cmder = (*newThemeCmd)(nil)
|
||||||
|
|
||||||
|
type newThemeCmd struct {
|
||||||
|
cmd *cobra.Command
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *newThemeCmd) getCommand() *cobra.Command {
|
||||||
|
return c.cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNewThemeCmd() *newThemeCmd {
|
||||||
|
ccmd := &newThemeCmd{}
|
||||||
|
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "theme [name]",
|
||||||
|
Short: "Create a new theme",
|
||||||
|
Long: `Create a new theme (skeleton) called [name] in the current directory.
|
||||||
|
New theme is a skeleton. Please add content to the touched files. Add your
|
||||||
|
name to the copyright line in the license and adjust the theme.toml file
|
||||||
|
as you see fit.`,
|
||||||
|
RunE: ccmd.newTheme,
|
||||||
|
}
|
||||||
|
|
||||||
|
ccmd.cmd = cmd
|
||||||
|
|
||||||
|
return ccmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *newThemeCmd) newTheme(cmd *cobra.Command, args []string) error {
|
||||||
|
c, err := InitializeConfig(false, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) < 1 {
|
||||||
|
return newUserError("theme name needs to be provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
createpath := c.PathSpec().AbsPathify(filepath.Join(c.Cfg.GetString("themesDir"), args[0]))
|
||||||
|
jww.INFO.Println("creating theme at", createpath)
|
||||||
|
|
||||||
|
cfg := c.DepsCfg
|
||||||
|
|
||||||
|
if x, _ := helpers.Exists(createpath, cfg.Fs.Source); x {
|
||||||
|
return newUserError(createpath, "already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir(createpath, "layouts", "_default")
|
||||||
|
mkdir(createpath, "layouts", "partials")
|
||||||
|
|
||||||
|
touchFile(cfg.Fs.Source, createpath, "layouts", "index.html")
|
||||||
|
touchFile(cfg.Fs.Source, createpath, "layouts", "404.html")
|
||||||
|
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "list.html")
|
||||||
|
touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "single.html")
|
||||||
|
|
||||||
|
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "header.html")
|
||||||
|
touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "footer.html")
|
||||||
|
|
||||||
|
mkdir(createpath, "archetypes")
|
||||||
|
|
||||||
|
archDefault := []byte("+++\n+++\n")
|
||||||
|
|
||||||
|
err = helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), cfg.Fs.Source)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir(createpath, "static", "js")
|
||||||
|
mkdir(createpath, "static", "css")
|
||||||
|
|
||||||
|
by := []byte(`The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) ` + time.Now().Format("2006") + ` YOUR_NAME_HERE
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
`)
|
||||||
|
|
||||||
|
err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), cfg.Fs.Source)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n.createThemeMD(cfg.Fs, createpath)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *newThemeCmd) createThemeMD(fs *hugofs.Fs, inpath string) (err error) {
|
||||||
|
|
||||||
|
by := []byte(`# theme.toml template for a Hugo theme
|
||||||
|
# See https://github.com/gohugoio/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://example.com/"
|
||||||
|
tags = []
|
||||||
|
features = []
|
||||||
|
min_version = "0.38"
|
||||||
|
|
||||||
|
[author]
|
||||||
|
name = ""
|
||||||
|
homepage = ""
|
||||||
|
|
||||||
|
# If porting an existing theme
|
||||||
|
[original]
|
||||||
|
name = ""
|
||||||
|
homepage = ""
|
||||||
|
repo = ""
|
||||||
|
`)
|
||||||
|
|
||||||
|
err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), fs.Source)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -23,14 +23,28 @@ import (
|
||||||
jww "github.com/spf13/jwalterweatherman"
|
jww "github.com/spf13/jwalterweatherman"
|
||||||
)
|
)
|
||||||
|
|
||||||
var versionCmd = &cobra.Command{
|
var _ cmder = (*versionCmd)(nil)
|
||||||
Use: "version",
|
|
||||||
Short: "Print the version number of Hugo",
|
type versionCmd struct {
|
||||||
Long: `All software has versions. This is Hugo's.`,
|
cmd *cobra.Command
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
}
|
||||||
printHugoVersion()
|
|
||||||
return nil
|
func newVersionCmd() *versionCmd {
|
||||||
},
|
return &versionCmd{
|
||||||
|
&cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Print the version number of Hugo",
|
||||||
|
Long: `All software has versions. This is Hugo's.`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
printHugoVersion()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *versionCmd) getCommand() *cobra.Command {
|
||||||
|
return c.cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func printHugoVersion() {
|
func printHugoVersion() {
|
||||||
|
|
Loading…
Reference in a new issue