From 06bac57ab0b6d83b5855132c92c762153dbde8bb Mon Sep 17 00:00:00 2001 From: Marshall Cottrell Date: Fri, 18 Feb 2022 14:28:02 -0600 Subject: Add support for CODEOWNERS Fixes #9474 --- hugolib/codeowners.go | 69 +++++++++++++++++++++++++++++++++++++++++++++ hugolib/content_map_page.go | 6 ++++ hugolib/hugo_sites.go | 24 ++++++++++++++-- hugolib/page.go | 4 +++ hugolib/page__common.go | 3 +- hugolib/page_test.go | 2 ++ hugolib/testsite/CODEOWNERS | 1 + 7 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 hugolib/codeowners.go create mode 100644 hugolib/testsite/CODEOWNERS (limited to 'hugolib') diff --git a/hugolib/codeowners.go b/hugolib/codeowners.go new file mode 100644 index 000000000..77068f1eb --- /dev/null +++ b/hugolib/codeowners.go @@ -0,0 +1,69 @@ +// Copyright 2019 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 hugolib + +import ( + "io" + "os" + "path" + + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/resources/page" + "github.com/hairyhenderson/go-codeowners" + "github.com/spf13/afero" +) + +var afs = afero.NewOsFs() + +func findCodeownersFile(dir string) (io.Reader, error) { + for _, p := range []string{".", "docs", ".github", ".gitlab"} { + f := path.Join(dir, p, "CODEOWNERS") + + _, err := afs.Stat(f) + if err != nil { + if os.IsNotExist(err) { + continue + } + return nil, err + } + + return afs.Open(f) + } + + return nil, nil +} + +type codeownerInfo struct { + owners *codeowners.Codeowners +} + +func (c *codeownerInfo) forPage(p page.Page) []string { + return c.owners.Owners(p.File().Filename()) +} + +func newCodeowners(cfg config.Provider) (*codeownerInfo, error) { + workingDir := cfg.GetString("workingDir") + + r, err := findCodeownersFile(workingDir) + if err != nil || r == nil { + return nil, err + } + + owners, err := codeowners.FromReader(r, workingDir) + if err != nil { + return nil, err + } + + return &codeownerInfo{owners: owners}, nil +} diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index 228564351..e7661bb81 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -135,6 +135,12 @@ func (m *pageMap) newPageFromContentNode(n *contentNode, parentBucket *pagesMapB } ps.gitInfo = gi + owners, err := s.h.codeownersForPage(ps) + if err != nil { + return nil, errors.Wrap(err, "failed to load CODEOWNERS") + } + ps.codeowners = owners + r, err := content() if err != nil { return nil, err diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index d2f4426d5..662ba19d3 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -75,7 +75,8 @@ type HugoSites struct { *deps.Deps - gitInfo *gitInfo + gitInfo *gitInfo + codeownerInfo *codeownerInfo // As loaded from the /data dirs data map[string]interface{} @@ -174,7 +175,7 @@ type hugoSitesInit struct { // Performs late initialization (before render) of the templates. layouts *lazy.Init - // Loads the Git info for all the pages if enabled. + // Loads the Git info and CODEOWNERS for all the pages if enabled. gitInfo *lazy.Init // Maps page translations. @@ -208,6 +209,18 @@ func (h *HugoSites) gitInfoForPage(p page.Page) (*gitmap.GitInfo, error) { return h.gitInfo.forPage(p), nil } +func (h *HugoSites) codeownersForPage(p page.Page) ([]string, error) { + if _, err := h.init.gitInfo.Do(); err != nil { + return nil, err + } + + if h.codeownerInfo == nil { + return nil, nil + } + + return h.codeownerInfo.forPage(p), nil +} + func (h *HugoSites) siteInfos() page.Sites { infos := make(page.Sites, len(h.Sites)) for i, site := range h.Sites { @@ -417,6 +430,13 @@ func (h *HugoSites) loadGitInfo() error { } else { h.gitInfo = gi } + + co, err := newCodeowners(h.Cfg) + if err != nil { + h.Log.Errorln("Failed to read CODEOWNERS:", err) + } else { + h.codeownerInfo = co + } } return nil } diff --git a/hugolib/page.go b/hugolib/page.go index 11b41e169..83b654cc0 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -148,6 +148,10 @@ func (p *pageState) GitInfo() *gitmap.GitInfo { return p.gitInfo } +func (p *pageState) Codeowners() []string { + return p.codeowners +} + // GetTerms gets the terms defined on this page in the given taxonomy. // The pages returned will be ordered according to the front matter. func (p *pageState) GetTerms(taxonomy string) page.Pages { diff --git a/hugolib/page__common.go b/hugolib/page__common.go index 0294393e4..e55bb7e25 100644 --- a/hugolib/page__common.go +++ b/hugolib/page__common.go @@ -103,7 +103,8 @@ type pageCommon struct { pageContent // Set if feature enabled and this is in a Git repo. - gitInfo *gitmap.GitInfo + gitInfo *gitmap.GitInfo + codeowners []string // Positional navigation posNextPrev *nextPrev diff --git a/hugolib/page_test.go b/hugolib/page_test.go index 1edef622b..c281ad36c 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -1075,12 +1075,14 @@ func TestPageWithLastmodFromGitInfo(t *testing.T) { // 2018-03-11 is the Git author date for testsite/content/first-post.md c.Assert(enSite.RegularPages()[0].Lastmod().Format("2006-01-02"), qt.Equals, "2018-03-11") + c.Assert(enSite.RegularPages()[0].Codeowners()[0], qt.Equals, "@bep") nnSite := h.Sites[1] c.Assert(len(nnSite.RegularPages()), qt.Equals, 1) // 2018-08-11 is the Git author date for testsite/content_nn/first-post.md c.Assert(nnSite.RegularPages()[0].Lastmod().Format("2006-01-02"), qt.Equals, "2018-08-11") + c.Assert(enSite.RegularPages()[0].Codeowners()[0], qt.Equals, "@bep") } func TestPageWithFrontMatterConfig(t *testing.T) { diff --git a/hugolib/testsite/CODEOWNERS b/hugolib/testsite/CODEOWNERS new file mode 100644 index 000000000..41f196327 --- /dev/null +++ b/hugolib/testsite/CODEOWNERS @@ -0,0 +1 @@ +* @bep \ No newline at end of file -- cgit v1.2.3