hugolib: Add gitpurism module
authorNikita Shubin <nikita.shubin@maquefel.me>
Fri, 29 Mar 2024 10:02:04 +0000 (13:02 +0300)
committerNikita Shubin <nikita.shubin@maquefel.me>
Fri, 29 Mar 2024 10:02:04 +0000 (13:02 +0300)
Add gitpurism module to extract tags and categories directly from git
notes.

Curently it scans notes/categories and notes/tags branches.

Example:
Notes (categories):
    categories: [git]

Notes (tags):
    tags: [git,git-notes]

Data is expected in YAML format.

TODO: specify note branches to scan

Signed-off-by: Nikita Shubin <nikita.shubin@maquefel.me>
hugolib/gitpurism.go [new file with mode: 0644]

diff --git a/hugolib/gitpurism.go b/hugolib/gitpurism.go
new file mode 100644 (file)
index 0000000..1ef0684
--- /dev/null
@@ -0,0 +1,122 @@
+// Copyright 2024 Nikita Shubin <nikita.shubin@maquefel.me>. 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 hugolib
+
+import (
+       "path/filepath"
+       "os/exec"
+       "strings"
+       "errors"
+       "bytes"
+       "fmt"
+
+       "github.com/gohugoio/hugo/config"
+       "github.com/gohugoio/hugo/resources/page"
+
+       "gopkg.in/yaml.v2"
+)
+
+func git(args ...string) ([]byte, error) {
+       out, err := exec.Command(gitExec, args...).CombinedOutput()
+
+       if err != nil {
+               if ee, ok := err.(*exec.Error); ok {
+                       if ee.Err == exec.ErrNotFound {
+                               return nil, GitNotFound
+                       }
+               }
+
+               return nil, errors.New(string(bytes.TrimSpace(out)))
+       }
+
+       return out, nil
+}
+
+var (
+       // will be modified during tests
+       gitExec string = "git"
+       GitNotFound = errors.New("Git executable not found in $PATH")
+)
+
+type gitPurismInfo struct {
+       contentDir string
+}
+
+type GitPurismInfo struct {
+       Tags []string
+       Categories []string
+}
+
+type Tags struct {
+       Tags []string `yaml:"tags"`
+}
+
+type Cats struct {
+       Categories []string `yaml:"categories"`
+}
+
+func (g *gitPurismInfo) forPage(p page.Page) *GitPurismInfo {
+       var t_res []string
+       var c_res []string
+
+       name := strings.TrimPrefix(filepath.ToSlash(p.File().Filename()), g.contentDir)
+       name = strings.TrimPrefix(name, "/")
+
+       gitLogArgs := strings.Fields(fmt.Sprintf(
+               `--no-pager -C %s log --notes=%s --notes=%s --format=%%N -- %s`,
+               g.contentDir,
+               "tags",
+               "categories",
+               name,
+       ))
+
+       out, err := git(gitLogArgs...)
+       entriesStr := string(out)
+       entriesStr = strings.Trim(entriesStr, "\n\x1e'")
+       entries := strings.Split(string(entriesStr), "\n")
+
+       for _, e := range entries {
+               var t Tags
+               var c Cats
+               err = yaml.Unmarshal([]byte(e), &t)
+               if err != nil {
+                       continue
+               }
+
+               err = yaml.Unmarshal([]byte(e), &c)
+               if err != nil {
+                       continue
+               }
+
+               t_res = append(t_res, t.Tags...)
+               c_res = append(c_res, c.Categories...)
+       }
+
+       return &GitPurismInfo{Tags: t_res, Categories: c_res}
+}
+
+func newGitPurismInfo(cfg config.Provider) (*gitPurismInfo, error) {
+       contentDir := cfg.GetString("contentDir")
+       absRepoPath, err := filepath.Abs(contentDir)
+
+       if err != nil {
+               return nil, err
+       }
+
+       _, err = git("-C", absRepoPath, "rev-parse", "--show-cdup")
+       if err != nil {
+               return nil, err
+       }
+
+       return &gitPurismInfo{contentDir: absRepoPath}, nil
+}