Add support for CODEOWNERS
authorMarshall Cottrell <mcottrell@gitlab.com>
Fri, 18 Feb 2022 20:28:02 +0000 (14:28 -0600)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 23 Feb 2022 21:43:19 +0000 (22:43 +0100)
Fixes #9474

13 files changed:
commands/commands.go
go.mod
go.sum
hugolib/codeowners.go [new file with mode: 0644]
hugolib/content_map_page.go
hugolib/hugo_sites.go
hugolib/page.go
hugolib/page__common.go
hugolib/page_test.go
hugolib/testsite/CODEOWNERS [new file with mode: 0644]
resources/page/page.go
resources/page/page_nop.go
resources/page/testhelpers_test.go

index 10b7a6649c58afdd2e0f5674d8b7fcdff671af3a..399f59af989c4656b436716f78f6a02cb2040a1b 100644 (file)
@@ -295,7 +295,7 @@ func (cc *hugoBuilderCommon) handleFlags(cmd *cobra.Command) {
        cmd.Flags().StringP("destination", "d", "", "filesystem path to write files to")
        cmd.Flags().StringSliceP("theme", "t", []string{}, "themes to use (located in /themes/THEMENAME/)")
        cmd.Flags().StringVarP(&cc.baseURL, "baseURL", "b", "", "hostname (and path) to the root, e.g. http://spf13.com/")
-       cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date and author info to the pages")
+       cmd.Flags().Bool("enableGitInfo", false, "add Git revision, date, author, and CODEOWNERS info to the pages")
        cmd.Flags().BoolVar(&cc.gc, "gc", false, "enable to run some cleanup tasks (remove unused cache files) after the build")
        cmd.Flags().StringVar(&cc.poll, "poll", "", "set this to a poll interval, e.g --poll 700ms, to use a poll based approach to watch for file system changes")
        cmd.Flags().BoolVar(&loggers.PanicOnWarning, "panicOnWarning", false, "panic on first WARNING log")
diff --git a/go.mod b/go.mod
index 93bdb0b551c0d027464c6f7ebcb72b3bdcd5dece..c2870abb83997506c0db22d551f5d294daf02dbd 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -31,6 +31,7 @@ require (
        github.com/gohugoio/testmodBuilder/mods v0.0.0-20190520184928-c56af20f2e95
        github.com/google/go-cmp v0.5.6
        github.com/gorilla/websocket v1.4.2
+       github.com/hairyhenderson/go-codeowners v0.2.3-0.20201026200250-cdc7c0759690 // indirect
        github.com/jdkato/prose v1.2.1
        github.com/kylelemons/godebug v1.1.0
        github.com/kyokomi/emoji/v2 v2.2.8
@@ -48,7 +49,7 @@ require (
        github.com/russross/blackfriday v1.6.0
        github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd
        github.com/sanity-io/litter v1.5.1
-       github.com/spf13/afero v1.6.0
+       github.com/spf13/afero v1.8.1
        github.com/spf13/cast v1.4.1
        github.com/spf13/cobra v1.3.0
        github.com/spf13/fsync v0.9.0
diff --git a/go.sum b/go.sum
index 08247e49783702f455e652fbbd59690dd34dc9ed..bbe41ee08e6f2a27295d8743ba2df752faadc9c5 100644 (file)
--- a/go.sum
+++ b/go.sum
@@ -20,6 +20,7 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY
 cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
 cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
 cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
+cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
 cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
 cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
 cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
@@ -54,6 +55,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
 cloud.google.com/go/storage v1.9.0/go.mod h1:m+/etGaqZbylxaNT876QGXqEHp4PR2Rq5GMqICWb9bU=
 cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+cloud.google.com/go/storage v1.14.0 h1:6RRlFMv1omScs6iq2hfE3IvgE+l6RfJPampq8UZc5TU=
+cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
 contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw=
 contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
@@ -344,6 +347,7 @@ github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hf
 github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
 github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
+github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
@@ -363,6 +367,7 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
 github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
 github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU=
 github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
+github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
 github.com/gorilla/csrf v1.6.0/go.mod h1:7tSf8kmjNYr7IWDCYhd3U8Ck34iQ/Yw5CJu7bAkHEGI=
 github.com/gorilla/handlers v1.4.1/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@@ -371,6 +376,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hairyhenderson/go-codeowners v0.2.3-0.20201026200250-cdc7c0759690 h1:XWjCrg/HJRLZCbvsUxS5R/9JhwiiwNctEsRvZ1Vjz5k=
+github.com/hairyhenderson/go-codeowners v0.2.3-0.20201026200250-cdc7c0759690/go.mod h1:8Qu9UmnhCRunfRv365Z3w+mT/WfLGKJiK+vugY9qNCU=
 github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
 github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -513,6 +520,7 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
+github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -552,9 +560,12 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
 github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
 github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
+github.com/spf13/afero v1.8.1 h1:izYHOT71f9iZ7iq37Uqjael60/vYC6vMtzedudZ0zEk=
+github.com/spf13/afero v1.8.1/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
 github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
 github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
@@ -627,8 +638,10 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -798,6 +811,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -805,6 +819,7 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -890,6 +905,7 @@ golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
@@ -988,7 +1004,9 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
diff --git a/hugolib/codeowners.go b/hugolib/codeowners.go
new file mode 100644 (file)
index 0000000..77068f1
--- /dev/null
@@ -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
+}
index 228564351e345f53233ee4c030c681d238c8f47d..e7661bb81cda711bf5c7970f98aa2670b3312987 100644 (file)
@@ -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
index d2f4426d58a5ef1c101b9448c00a925fd8d3782f..662ba19d3a3bd2e26c0e7289fd7b711857b92932 100644 (file)
@@ -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
 }
index 11b41e169206389e1c3e894be9d826587ddb05a9..83b654cc05f12d49e9eb517775702b1096c52642 100644 (file)
@@ -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 {
index 0294393e42b51b2827249576e4be0b6e08cc933b..e55bb7e253c3624c21d309780fa1b31f8bd28554 100644 (file)
@@ -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
index 1edef622bbe68a66112e2412854e228104d13734..c281ad36c174b20482bcc0d46fd6f156eaffbad4 100644 (file)
@@ -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 (file)
index 0000000..41f1963
--- /dev/null
@@ -0,0 +1 @@
+* @bep
\ No newline at end of file
index 1ad536e943691248bd72ead42d3a85ffd681a897..797322717caea5d8024d384b3ce01487860f86bb 100644 (file)
@@ -107,6 +107,7 @@ type GetPageProvider interface {
 // GitInfoProvider provides Git info.
 type GitInfoProvider interface {
        GitInfo() *gitmap.GitInfo
+       Codeowners() []string
 }
 
 // InSectionPositioner provides section navigation.
index fd706f99405b0a69daff3d8576c7012fe427de8c..641c34d41bf6be5326a1c13cfc32fd41cc42b8d4 100644 (file)
@@ -194,6 +194,10 @@ func (p *nopPage) GitInfo() *gitmap.GitInfo {
        return nil
 }
 
+func (p *nopPage) Codeowners() []string {
+       return nil
+}
+
 func (p *nopPage) HasMenuCurrent(menuID string, me *navigation.MenuEntry) bool {
        return false
 }
index df4e79db484003fcbccca503af64399ea52a629d..1aa80e2bb2ca121104f24fc5cd4ce74781b154d6 100644 (file)
@@ -252,6 +252,10 @@ func (p *testPage) GitInfo() *gitmap.GitInfo {
        return nil
 }
 
+func (p *testPage) Codeowners() []string {
+       return nil
+}
+
 func (p *testPage) HasMenuCurrent(menuID string, me *navigation.MenuEntry) bool {
        panic("not implemented")
 }