From 1c5f0dbb2e33ce105c815133d4b5e50e1625b841 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Wed, 17 Feb 2021 12:15:39 -0500 Subject: [PATCH 01/13] bump to booklit v0.12.0 no reason in particular, just getting onto a proper tag Signed-off-by: Alex Suraci --- go.mod | 2 +- go.sum | 110 +++++++++++++++++++++++++++++++++++---------------------- 2 files changed, 68 insertions(+), 44 deletions(-) diff --git a/go.mod b/go.mod index efd7c61e2..ef07aa055 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/google/go-querystring v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 github.com/prometheus/common v0.9.1 - github.com/vito/booklit v0.11.1-0.20200528143027-05f9902b5ed9 + github.com/vito/booklit v0.12.0 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 ) diff --git a/go.sum b/go.sum index 459cdae5b..bdb9da923 100644 --- a/go.sum +++ b/go.sum @@ -1,16 +1,11 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= -github.com/alecthomas/chroma v0.5.0 h1:PI0RlRSWL+8GSMuIMMA5KIND4CeJ5KXUQA60LLp/SjA= -github.com/alecthomas/chroma v0.5.0/go.mod h1:MmozekIi2rfQSzDcdEZ2BoJ9Pxs/7uc2Y4Boh+hIeZo= -github.com/alecthomas/chroma v0.7.3/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM= -github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo= +github.com/alecthomas/chroma v0.8.2/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM= github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0= github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= -github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1 h1:GDQdwm/gAcJcLAKQQZGOJ4knlw+7rfEQQcmwTbt4p5E= github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -20,15 +15,14 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg= -github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE= github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -39,8 +33,18 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= @@ -51,12 +55,8 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= -github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -65,8 +65,14 @@ github.com/mna/pigeon v1.0.1-0.20200224192238-18953b277063/go.mod h1:rkFeDZ0gc+Y github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.5/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -85,75 +91,93 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/segmentio/textio v1.2.0 h1:Ug4IkV3kh72juJbG8azoSBlgebIbUUxVNrfFcKHfTSQ= github.com/segmentio/textio v1.2.0/go.mod h1:+Rb7v0YVODP+tK5F7FD9TCkV7gOYx9IgLHWiqtvY8ag= -github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/vito/booklit v0.10.1-0.20200506172243-0753b4a78766 h1:EWieWlPC7oNNd5uCXOTHsLnQs2cLCBG9rc2+YR67edI= -github.com/vito/booklit v0.10.1-0.20200506172243-0753b4a78766/go.mod h1:PABFI6CUtYORQeJFqkbiQaew3a7+0R4IUAwRt5kwpL4= -github.com/vito/booklit v0.10.1-0.20200508035331-f676d9d08216 h1:YHHjOlLVkEA+F5msuf2UU0YjeEme6CkNbez4hZHwPOo= -github.com/vito/booklit v0.10.1-0.20200508035331-f676d9d08216/go.mod h1:EJD//6POAlujndCtf1FeZrutEN7jNnDsnwmxEGnODOQ= -github.com/vito/booklit v0.11.0 h1:pAmjde1ZpNmvYXZ9Gt4Oy0w4Cyxu3bBVmYMbN5tLWo4= -github.com/vito/booklit v0.11.0/go.mod h1:EJD//6POAlujndCtf1FeZrutEN7jNnDsnwmxEGnODOQ= -github.com/vito/booklit v0.11.1-0.20200528143027-05f9902b5ed9 h1:HnrkPIum3X2s4khsBjuiMUtDuFdd+QiDM8EKhfN92XU= -github.com/vito/booklit v0.11.1-0.20200528143027-05f9902b5ed9/go.mod h1:upy7W/yBvBvElAhiBv7yK3D7eYQ+35BbVIJGH4Grkvk= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/vito/booklit v0.12.0 h1:gyFawwScwwCLrUPgPk04nNTN8EwVo8yWxTiXJZQfvl0= +github.com/vito/booklit v0.12.0/go.mod h1:x5ufTXYRTw0yGSaGr0hjyclQwcUPYWbrHpDoRLTp4LQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY= +golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190415145633-3fd5a3612ccd h1:MNN7PRW7zYXd8upVO5qfKeOnQG74ivRNv7sz4k4cQMs= -golang.org/x/sys v0.0.0-20190415145633-3fd5a3612ccd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201218084310-7d0127a74742 h1:+CBz4km/0KPU3RGTwARGh/noP3bEwtHcq+0YcBQM2JQ= +golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190830223141-573d9926052a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8 h1:BMFHd4OFnFtWX46Xj4DN6vvT1btiBxyq+s0orYBqcQY= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201218024724-ae774e9781d2 h1:lHDhNNs7asPT3p01mm8EP3B+bNyyVfg0bcYjhJUYgxw= +golang.org/x/tools v0.0.0-20201218024724-ae774e9781d2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= From f2e2529be2e77620ea106dbd0f0b39ce0c315b83 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Wed, 17 Feb 2021 14:07:46 -0500 Subject: [PATCH 02/13] docs plugin: split up plugin.go, remove stale code Signed-off-by: Alex Suraci --- go/docs/db.go | 58 +++ go/docs/examples.go | 44 +++ go/docs/github.go | 167 ++++++++ go/docs/metrics.go | 130 +++++++ go/docs/plugin.go | 776 +------------------------------------ go/docs/schema.go | 229 +++++++++++ go/docs/splash.go | 41 ++ html/example-pipeline.tmpl | 1 - html/literate-segment.tmpl | 9 - 9 files changed, 674 insertions(+), 781 deletions(-) create mode 100644 go/docs/db.go create mode 100644 go/docs/examples.go create mode 100644 go/docs/github.go create mode 100644 go/docs/metrics.go create mode 100644 go/docs/schema.go create mode 100644 go/docs/splash.go delete mode 100644 html/example-pipeline.tmpl delete mode 100644 html/literate-segment.tmpl diff --git a/go/docs/db.go b/go/docs/db.go new file mode 100644 index 000000000..49aed5de3 --- /dev/null +++ b/go/docs/db.go @@ -0,0 +1,58 @@ +package docs + +import "github.com/vito/booklit" + +func (p Plugin) DefineTable(table string, content booklit.Content) booklit.Content { + tagName := table + "-table" + + return OmitFromSearchExcerpt{ + booklit.Styled{ + Style: "definition", + Content: content, + Partials: booklit.Partials{ + "Targets": booklit.Target{ + TagName: tagName, + Location: p.section.InvokeLocation, + Title: booklit.Styled{ + Style: booklit.StyleVerbatim, + Content: booklit.Styled{ + Style: booklit.StyleBold, + Content: booklit.String(table), + }, + }, + Content: content, + }, + "Thumb": booklit.Styled{ + Style: booklit.StyleVerbatim, + Content: booklit.Preformatted{ + &booklit.Reference{ + TagName: tagName, + Location: p.section.InvokeLocation, + Content: booklit.Styled{ + Style: booklit.StyleBold, + Content: booklit.String(table), + }, + }, + }, + }, + }, + }, + } +} + +func (p Plugin) ReferenceColumn(table string, column string) booklit.Content { + return &booklit.Reference{ + TagName: table + "-table", + Location: p.section.InvokeLocation, + Content: booklit.Styled{ + Style: booklit.StyleVerbatim, + Content: booklit.Sequence{ + booklit.Styled{ + Style: booklit.StyleBold, + Content: booklit.String(table), + }, + booklit.String(" (" + column + ")"), + }, + }, + } +} diff --git a/go/docs/examples.go b/go/docs/examples.go new file mode 100644 index 000000000..3efa380a4 --- /dev/null +++ b/go/docs/examples.go @@ -0,0 +1,44 @@ +package docs + +import ( + "fmt" + "io/ioutil" + "net/http" + + "github.com/vito/booklit" +) + +func (p Plugin) Frame(src booklit.Content, optionalHeight ...string) booklit.Content { + height := "300px" + if len(optionalHeight) > 0 { + height = optionalHeight[0] + } + + return booklit.Styled{ + Style: "frame", + Content: booklit.Empty, + Partials: booklit.Partials{ + "URL": src, + "Height": booklit.String(height), + }, + } +} + +func (p Plugin) RemoteCodeblock(language string, url string) (booklit.Content, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return nil, fmt.Errorf("remote codeblock for %s failed: %s", url, resp.Status) + } + + content, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return p.Codeblock(language, booklit.String(content)), nil +} diff --git a/go/docs/github.go b/go/docs/github.go new file mode 100644 index 000000000..f5734c887 --- /dev/null +++ b/go/docs/github.go @@ -0,0 +1,167 @@ +package docs + +import ( + "context" + "errors" + "fmt" + "net/http" + "os" + "strings" + + "github.com/blang/semver" + "github.com/google/go-github/github" + "github.com/vito/booklit" + "golang.org/x/oauth2" +) + +func (p Plugin) Ghuser(user string) booklit.Content { + return booklit.Styled{ + Style: "github-user-link", + Content: booklit.String(user), + } +} + +func (p Plugin) Ghrelease(tag string, optionalRepo ...string) booklit.Content { + repo := "concourse" + if len(optionalRepo) > 0 { + repo = optionalRepo[0] + } + + return booklit.Styled{ + Style: "github-release-link", + Content: booklit.String(tag), + Partials: booklit.Partials{ + "Owner": booklit.String("concourse"), + "Repo": booklit.String(repo), + }, + } +} + +func (p Plugin) Ghpr(number string, optionalRepo ...string) booklit.Content { + repo := "concourse" + if len(optionalRepo) > 0 { + repo = optionalRepo[0] + } + + return booklit.Styled{ + Style: "github-pr-link", + Content: booklit.String(number), + Partials: booklit.Partials{ + "Owner": booklit.String("concourse"), + "Repo": booklit.String(repo), + }, + } +} + +func (p Plugin) Ghissue(number string, optionalRepo ...string) booklit.Content { + repo := "concourse" + if len(optionalRepo) > 0 { + repo = optionalRepo[0] + } + + return booklit.Styled{ + Style: "github-issue-link", + Content: booklit.String(number), + Partials: booklit.Partials{ + "Owner": booklit.String("concourse"), + "Repo": booklit.String(repo), + }, + } +} + +func (p *Plugin) DownloadLinks() (booklit.Content, error) { + ctx := context.Background() + + client := p.githubClient(ctx) + + var latestRelease *github.RepositoryRelease + var latestVersion semver.Version + releases, _, err := client.Repositories.ListReleases(ctx, "concourse", "concourse", nil) + if err != nil { + return nil, err + } + if len(releases) == 0 { + return nil, errors.New("no releases found!") + } + for _, release := range releases { + if release.GetPrerelease() { + continue + } + + if release.TagName == nil { + return nil, fmt.Errorf("no tag name for release %v", release) + } + version, err := semver.ParseTolerant(*release.TagName) + if err != nil { + return nil, err + } + if latestRelease == nil || version.GT(latestVersion) { + latestRelease = release + latestVersion = version + } + } + + var linuxURL, darwinURL, windowsURL string + var flyLinuxURL, flyDarwinURL, flyWindowsURL string + for _, asset := range latestRelease.Assets { + name := asset.GetName() + + if strings.Contains(name, "concourse") && strings.Contains(name, "linux") && strings.HasSuffix(name, ".tgz") { + linuxURL = asset.GetBrowserDownloadURL() + } + + if strings.Contains(name, "concourse") && strings.Contains(name, "darwin") && strings.HasSuffix(name, ".tgz") { + darwinURL = asset.GetBrowserDownloadURL() + } + + if strings.Contains(name, "concourse") && strings.Contains(name, "windows") && strings.HasSuffix(name, ".zip") { + windowsURL = asset.GetBrowserDownloadURL() + } + + if strings.Contains(name, "fly") && strings.Contains(name, "linux") && strings.HasSuffix(name, ".tgz") { + flyLinuxURL = asset.GetBrowserDownloadURL() + } + + if strings.Contains(name, "fly") && strings.Contains(name, "darwin") && strings.HasSuffix(name, ".tgz") { + flyDarwinURL = asset.GetBrowserDownloadURL() + } + + if strings.Contains(name, "fly") && strings.Contains(name, "windows") && strings.HasSuffix(name, ".zip") { + flyWindowsURL = asset.GetBrowserDownloadURL() + } + } + + return booklit.Styled{ + Style: "download-links", + Block: true, + Content: booklit.Link{ + Target: latestRelease.GetHTMLURL(), + Content: booklit.String(latestRelease.GetName()), + }, + Partials: booklit.Partials{ + "Version": booklit.String(latestRelease.GetName()), + "ReleaseNotesURL": booklit.String(latestRelease.GetHTMLURL()), + "LinuxURL": booklit.String(linuxURL), + "DarwinURL": booklit.String(darwinURL), + "WindowsURL": booklit.String(windowsURL), + "FlyLinuxURL": booklit.String(flyLinuxURL), + "FlyDarwinURL": booklit.String(flyDarwinURL), + "FlyWindowsURL": booklit.String(flyWindowsURL), + }, + }, nil +} + +func (p *Plugin) githubClient(ctx context.Context) *github.Client { + var tc *http.Client + var githubToken = os.Getenv("GITHUB_TOKEN") + + if githubToken != "" { + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: githubToken}, + ) + + tc = oauth2.NewClient(ctx, ts) + } + + return github.NewClient(tc) +} diff --git a/go/docs/metrics.go b/go/docs/metrics.go new file mode 100644 index 000000000..b6c15a262 --- /dev/null +++ b/go/docs/metrics.go @@ -0,0 +1,130 @@ +package docs + +import ( + "bytes" + "fmt" + "sort" + "strings" + + prom "github.com/prometheus/client_model/go" + "github.com/prometheus/common/expfmt" + "github.com/vito/booklit" +) + +func (p Plugin) PromethusDocs(sample string) (booklit.Content, error) { + parser := new(expfmt.TextParser) + + mfs, err := parser.TextToMetricFamilies(bytes.NewBufferString(sample)) + if err != nil { + return nil, fmt.Errorf("failed to parse prometheus sample: %w", err) + } + + type metric struct { + name string + family *prom.MetricFamily + } + + metrics := booklit.Sequence{} + + sorted := []metric{} + for metricName, family := range mfs { + sorted = append(sorted, metric{ + name: metricName, + family: family, + }) + } + + sort.Slice(sorted, func(i, j int) bool { + return sorted[i].name < sorted[j].name + }) + + for _, metric := range sorted { + metricName := metric.name + family := metric.family + + distinctLabels := map[string]bool{} + + labels := booklit.Sequence{} + for _, metric := range family.GetMetric() { + for _, label := range metric.GetLabel() { + labelName := label.GetName() + + if distinctLabels[labelName] { + continue + } + + distinctLabels[labelName] = true + + labels = append(labels, booklit.String(labelName)) + } + } + + metricType := strings.ToLower(family.GetType().String()) + + metrics = append(metrics, booklit.Styled{ + Style: "prometheus-metric", + Content: booklit.String(family.GetHelp()), + Partials: booklit.Partials{ + "Name": booklit.String(metricName), + "Type": booklit.String(metricType), + "Labels": labels, + }, + }) + } + + return metrics, nil +} + +func (p Plugin) autoReferenceType(type_ string) booklit.Content { + if strings.HasPrefix(type_, "[") && strings.HasSuffix(type_, "]") { + subType := strings.TrimPrefix(strings.TrimSuffix(type_, "]"), "[") + return booklit.Sequence{ + booklit.String("["), + p.autoReferenceType(subType), + booklit.String("]"), + } + } + + if strings.HasPrefix(type_, "{") && strings.HasSuffix(type_, "}") { + subType := strings.TrimPrefix(strings.TrimSuffix(type_, "}"), "{") + return booklit.Sequence{ + booklit.String("{"), + p.autoReferenceType(subType), + booklit.String("}"), + } + } + + for _, punc := range []string{" | ", ": ", ", "} { + if strings.Contains(type_, punc) { + ors := strings.Split(type_, punc) + + seq := booklit.Sequence{} + for i, t := range ors { + seq = append(seq, p.autoReferenceType(t)) + + if i+1 < len(ors) { + seq = append(seq, booklit.String(punc)) + } + } + + return seq + } + } + + if strings.HasPrefix(type_, "`") && strings.HasSuffix(type_, "`") { + scalar := strings.TrimPrefix(strings.TrimSuffix(type_, "`"), "`") + return booklit.Styled{ + Style: "schema-scalar", + Content: booklit.String(scalar), + } + } + + return &booklit.Reference{ + TagName: "schema." + type_, + Location: p.section.InvokeLocation, + Content: booklit.Styled{ + Style: booklit.StyleBold, + Content: booklit.String(type_), + }, + } +} diff --git a/go/docs/plugin.go b/go/docs/plugin.go index d9dab181f..a34e6396f 100644 --- a/go/docs/plugin.go +++ b/go/docs/plugin.go @@ -1,23 +1,8 @@ package docs import ( - "bytes" - "context" - "crypto/sha1" - "errors" "fmt" - "io/ioutil" - "net/http" - "os" - "sort" "strings" - "time" - - "github.com/blang/semver" - "github.com/google/go-github/github" - dto "github.com/prometheus/client_model/go" - "github.com/prometheus/common/expfmt" - "golang.org/x/oauth2" "github.com/vito/booklit" "github.com/vito/booklit/ast" @@ -75,25 +60,6 @@ func (p Plugin) Codeblock(language string, code booklit.Content) booklit.Content } } -func (p Plugin) RemoteCodeblock(language string, url string) (booklit.Content, error) { - resp, err := http.Get(url) - if err != nil { - return nil, err - } - defer resp.Body.Close() - - if resp.StatusCode < 200 || resp.StatusCode >= 300 { - return nil, fmt.Errorf("remote codeblock for %s failed: %s", url, resp.Status) - } - - content, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - return p.Codeblock(language, booklit.String(content)), nil -} - func (p Plugin) InlineHeader(content booklit.Content) booklit.Content { return booklit.Styled{ Style: "inline-header", @@ -102,363 +68,6 @@ func (p Plugin) InlineHeader(content booklit.Content) booklit.Content { } } -func (p Plugin) SplashIntro(intro, downloads booklit.Content) { - p.section.SetPartial( - "Splash", - booklit.Styled{ - Style: "splash-intro", - Block: true, - - Content: intro, - - Partials: booklit.Partials{ - "Downloads": downloads, - }, - }, - ) -} - -func (p Plugin) QuickStart(content booklit.Content) booklit.Content { - return booklit.Styled{ - Style: "quick-start", - Block: true, - Content: content, - } -} - -func (p *Plugin) Schema(name string, contentNode ast.Node) (booklit.Content, error) { - old := p.schemaContext - p.schemaContext = []string{name} - defer func() { - p.schemaContext = old - }() - - tagName := "schema." + name - - p.section.SetTagAnchored( - tagName, - booklit.Sequence{ - booklit.Styled{ - Style: booklit.StyleBold, - Content: booklit.Styled{ - Style: booklit.StyleVerbatim, - Content: booklit.String(name), - }, - }, - booklit.String(" schema"), - }, - p.section.InvokeLocation, - booklit.Empty, - tagName, - ) - - stage := &stages.Evaluate{ - Section: p.section, - } - - err := contentNode.Visit(stage) - if err != nil { - return nil, err - } - - content := stage.Result - - return booklit.Styled{ - Style: "schema", - Block: true, - Content: content, - Partials: booklit.Partials{ - "Name": booklit.String(name), - "Anchor": booklit.String(tagName), - }, - }, nil -} - -func (p *Plugin) SchemaGroup(title booklit.Content, tagName string, contentNode ast.Node) (booklit.Content, error) { - p.pushSchema(tagName) - defer p.popSchema() - - p.schemaGroupTitle = title - defer func() { - p.schemaGroupTitle = nil - }() - - stage := &stages.Evaluate{ - Section: p.section, - } - - err := contentNode.Visit(stage) - if err != nil { - return nil, err - } - - content := stage.Result - - return booklit.Styled{ - Style: "schema-group", - Block: true, - Content: content, - Partials: booklit.Partials{ - "Title": title, - "TagName": booklit.String(tagName), - "Target": booklit.Target{ - TagName: tagName, - Location: p.section.InvokeLocation, - Title: title, - Content: content, - }, - }, - }, nil -} - -func (p *Plugin) RequiredAttribute(attribute string, type_ string, contentNode ast.Node) (booklit.Content, error) { - return p.schemaAttribute( - attribute, - type_, - contentNode, - booklit.Partials{ - "Required": booklit.String("true"), - }, - ) -} - -func (p *Plugin) OptionalAttribute(attribute string, type_ string, contentNode ast.Node) (booklit.Content, error) { - return p.schemaAttribute( - attribute, - type_, - contentNode, - booklit.Partials{ - "Optional": booklit.String("true"), - }, - ) -} - -func (p *Plugin) ExampleToggle(title, content booklit.Content) booklit.Content { - uniq := strings.Join(p.schemaContext, ".") + title.String() - hash := sha1.Sum([]byte(uniq)) - - return booklit.Styled{ - Style: "example-toggle", - Block: true, - Content: content, - Partials: booklit.Partials{ - "Title": title, - "For": booklit.String(fmt.Sprintf("%x", hash)), - }, - } -} - -func (p *Plugin) OneOf(options ...booklit.Content) booklit.Content { - return booklit.Styled{ - Style: "schema-one-of", - Block: true, - Content: booklit.Sequence(options), - } -} - -func (p *Plugin) SchemaAttribute(attribute string, type_ string, contentNode ast.Node) (booklit.Content, error) { - return p.schemaAttribute( - attribute, - type_, - contentNode, - booklit.Partials{}, - ) -} - -func (p *Plugin) schemaAttribute(attribute string, type_ string, contentNode ast.Node, partials booklit.Partials) (booklit.Content, error) { - p.pushSchema(attribute) - defer p.popSchema() - - tagName := "schema." + strings.Join(p.schemaContext, ".") - - stage := &stages.Evaluate{ - Section: p.section, - } - - err := contentNode.Visit(stage) - if err != nil { - return nil, err - } - - content := stage.Result - - var display booklit.Content - if p.schemaGroupTitle != nil { - display = booklit.Sequence{ - p.schemaGroupTitle, - booklit.String(" "), - booklit.Styled{ - Style: booklit.StyleBold, - Content: booklit.Styled{ - Style: booklit.StyleVerbatim, - Content: booklit.String(attribute), - }, - }, - } - } else { - attr := booklit.Sequence{} - for _, con := range p.schemaContext { - field := con - if len(attr) > 0 { - field = "." + field - } - - attr = append(attr, booklit.String(field)) - } - - display = booklit.Styled{ - Style: "schema-attribute-name", - Content: attr, - } - } - - targets := booklit.Sequence{ - booklit.Target{ - TagName: tagName, - Location: p.section.InvokeLocation, - Title: display, - Content: content, - }, - } - - partials["TagName"] = booklit.String(tagName) - partials["Targets"] = targets - partials["Attribute"] = booklit.String(attribute) - partials["Type"] = p.autoReferenceType(type_) - - return NoIndex{ - booklit.Styled{ - Style: "schema-attribute", - Block: true, - Content: content, - Partials: partials, - }, - }, nil -} - -func (p *Plugin) pushSchema(attribute string) { - p.schemaContext = append(p.schemaContext, attribute) -} - -func (p *Plugin) popSchema() { - p.schemaContext = p.schemaContext[0 : len(p.schemaContext)-1] -} - -func (p *Plugin) DownloadLinks() (booklit.Content, error) { - ctx := context.Background() - - var tc *http.Client - var githubToken = os.Getenv("GITHUB_TOKEN") - - if githubToken != "" { - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: githubToken}, - ) - - tc = oauth2.NewClient(ctx, ts) - } - - client := github.NewClient(tc) - - var latestRelease *github.RepositoryRelease - var latestVersion semver.Version - releases, _, err := client.Repositories.ListReleases(ctx, "concourse", "concourse", nil) - if err != nil { - return nil, err - } - if len(releases) == 0 { - return nil, errors.New("no releases found!") - } - for _, release := range releases { - if release.GetPrerelease() { - continue - } - - if release.TagName == nil { - return nil, fmt.Errorf("no tag name for release %v", release) - } - version, err := semver.ParseTolerant(*release.TagName) - if err != nil { - return nil, err - } - if latestRelease == nil || version.GT(latestVersion) { - latestRelease = release - latestVersion = version - } - } - - var linuxURL, darwinURL, windowsURL string - var flyLinuxURL, flyDarwinURL, flyWindowsURL string - for _, asset := range latestRelease.Assets { - name := asset.GetName() - - if strings.Contains(name, "concourse") && strings.Contains(name, "linux") && strings.HasSuffix(name, ".tgz") { - linuxURL = asset.GetBrowserDownloadURL() - } - - if strings.Contains(name, "concourse") && strings.Contains(name, "darwin") && strings.HasSuffix(name, ".tgz") { - darwinURL = asset.GetBrowserDownloadURL() - } - - if strings.Contains(name, "concourse") && strings.Contains(name, "windows") && strings.HasSuffix(name, ".zip") { - windowsURL = asset.GetBrowserDownloadURL() - } - - if strings.Contains(name, "fly") && strings.Contains(name, "linux") && strings.HasSuffix(name, ".tgz") { - flyLinuxURL = asset.GetBrowserDownloadURL() - } - - if strings.Contains(name, "fly") && strings.Contains(name, "darwin") && strings.HasSuffix(name, ".tgz") { - flyDarwinURL = asset.GetBrowserDownloadURL() - } - - if strings.Contains(name, "fly") && strings.Contains(name, "windows") && strings.HasSuffix(name, ".zip") { - flyWindowsURL = asset.GetBrowserDownloadURL() - } - } - - return booklit.Styled{ - Style: "download-links", - Block: true, - Content: booklit.Link{ - Target: latestRelease.GetHTMLURL(), - Content: booklit.String(latestRelease.GetName()), - }, - Partials: booklit.Partials{ - "Version": booklit.String(latestRelease.GetName()), - "ReleaseNotesURL": booklit.String(latestRelease.GetHTMLURL()), - "LinuxURL": booklit.String(linuxURL), - "DarwinURL": booklit.String(darwinURL), - "WindowsURL": booklit.String(windowsURL), - "FlyLinuxURL": booklit.String(flyLinuxURL), - "FlyDarwinURL": booklit.String(flyDarwinURL), - "FlyWindowsURL": booklit.String(flyWindowsURL), - }, - }, nil -} - -func (p Plugin) SplashExample(title booklit.Content, content booklit.Content, example booklit.Content) booklit.Content { - return booklit.Styled{ - Style: "splash-example", - Block: true, - - Content: content, - - Partials: booklit.Partials{ - "Title": title, - "Example": example, - }, - } -} - -func (p Plugin) ExamplePipeline() booklit.Content { - return booklit.Styled{ - Style: "example-pipeline", - Block: true, - Content: booklit.Empty, - } -} - func (p Plugin) TitledCodeblock(title booklit.Content, language string, code booklit.Content) (booklit.Content, error) { return booklit.Styled{ Style: "titled-codeblock", @@ -502,22 +111,6 @@ func (p Plugin) BetterTable(content booklit.Content) (booklit.Content, error) { } } -func (p Plugin) Frame(src booklit.Content, optionalHeight ...string) booklit.Content { - height := "300px" - if len(optionalHeight) > 0 { - height = optionalHeight[0] - } - - return booklit.Styled{ - Style: "frame", - Content: booklit.Empty, - Partials: booklit.Partials{ - "URL": src, - "Height": booklit.String(height), - }, - } -} - func (p *Plugin) DefineAttribute(attribute string, contentNode ast.Node, tags ...string) (booklit.Content, error) { attrSplit := strings.SplitN(attribute, ":", 2) @@ -560,7 +153,7 @@ func (p *Plugin) DefineAttribute(attribute string, contentNode ast.Node, tags .. }) } - return NoIndex{ + return OmitFromSearchExcerpt{ booklit.Styled{ Style: "definition", Content: content, @@ -587,82 +180,6 @@ func (p *Plugin) DefineAttribute(attribute string, contentNode ast.Node, tags .. }, nil } -func (p Plugin) DefineMetric(metric string, content booklit.Content) booklit.Content { - return NoIndex{ - booklit.Styled{ - Style: "definition", - Content: content, - Partials: booklit.Partials{ - "Targets": booklit.Target{ - TagName: metric, - Location: p.section.InvokeLocation, - Title: booklit.String(metric), - Content: content, - }, - "Thumb": booklit.Styled{ - Style: booklit.StyleVerbatim, - Content: booklit.Preformatted{booklit.String(metric)}, - }, - }, - }, - } -} - -func (p Plugin) DefineTable(table string, content booklit.Content) booklit.Content { - tagName := table + "-table" - - return NoIndex{ - booklit.Styled{ - Style: "definition", - Content: content, - Partials: booklit.Partials{ - "Targets": booklit.Target{ - TagName: tagName, - Location: p.section.InvokeLocation, - Title: booklit.Styled{ - Style: booklit.StyleVerbatim, - Content: booklit.Styled{ - Style: booklit.StyleBold, - Content: booklit.String(table), - }, - }, - Content: content, - }, - "Thumb": booklit.Styled{ - Style: booklit.StyleVerbatim, - Content: booklit.Preformatted{ - &booklit.Reference{ - TagName: tagName, - Location: p.section.InvokeLocation, - Content: booklit.Styled{ - Style: booklit.StyleBold, - Content: booklit.String(table), - }, - }, - }, - }, - }, - }, - } -} - -func (p Plugin) ReferenceColumn(table string, column string) booklit.Content { - return &booklit.Reference{ - TagName: table + "-table", - Location: p.section.InvokeLocation, - Content: booklit.Styled{ - Style: booklit.StyleVerbatim, - Content: booklit.Sequence{ - booklit.Styled{ - Style: booklit.StyleBold, - Content: booklit.String(table), - }, - booklit.String(" (" + column + ")"), - }, - }, - } -} - func (p Plugin) Boshprop(job string, target string) booklit.Content { return booklit.Link{ Target: fmt.Sprintf("https://bosh.io/jobs/%s?source=github.com/concourse/concourse-bosh-release#p=%s", job, target), @@ -673,61 +190,6 @@ func (p Plugin) Boshprop(job string, target string) booklit.Content { } } -func (p Plugin) Ghuser(user string) booklit.Content { - return booklit.Styled{ - Style: "github-user-link", - Content: booklit.String(user), - } -} - -func (p Plugin) Ghrelease(tag string, optionalRepo ...string) booklit.Content { - repo := "concourse" - if len(optionalRepo) > 0 { - repo = optionalRepo[0] - } - - return booklit.Styled{ - Style: "github-release-link", - Content: booklit.String(tag), - Partials: booklit.Partials{ - "Owner": booklit.String("concourse"), - "Repo": booklit.String(repo), - }, - } -} - -func (p Plugin) Ghpr(number string, optionalRepo ...string) booklit.Content { - repo := "concourse" - if len(optionalRepo) > 0 { - repo = optionalRepo[0] - } - - return booklit.Styled{ - Style: "github-pr-link", - Content: booklit.String(number), - Partials: booklit.Partials{ - "Owner": booklit.String("concourse"), - "Repo": booklit.String(repo), - }, - } -} - -func (p Plugin) Ghissue(number string, optionalRepo ...string) booklit.Content { - repo := "concourse" - if len(optionalRepo) > 0 { - repo = optionalRepo[0] - } - - return booklit.Styled{ - Style: "github-issue-link", - Content: booklit.String(number), - Partials: booklit.Partials{ - "Owner": booklit.String("concourse"), - "Repo": booklit.String(repo), - }, - } -} - func (p Plugin) Resource(name string) booklit.Content { return booklit.Link{ Target: fmt.Sprintf("https://github.com/concourse/%s-resource", name), @@ -752,28 +214,6 @@ func (p Plugin) Diagram(path string, width string) booklit.Content { } } -func (p Plugin) LiterateSegment(parasAndFinalCode ...booklit.Content) (booklit.Content, error) { - if len(parasAndFinalCode) == 0 { - return nil, fmt.Errorf("no paragraphs or code given") - } - - paras := parasAndFinalCode[0 : len(parasAndFinalCode)-1] - code := parasAndFinalCode[len(parasAndFinalCode)-1] - - if len(paras) == 0 { - paras = []booklit.Content{code} - code = booklit.Empty - } - - return booklit.Styled{ - Style: "literate-segment", - Content: booklit.Sequence(paras), - Partials: booklit.Partials{ - "Code": code, - }, - }, nil -} - func (p Plugin) SideBySide(left, right booklit.Content) booklit.Content { return booklit.Styled{ Style: "side-by-side", @@ -790,32 +230,6 @@ func (p Plugin) IncludeTemplate(name string) booklit.Content { } } -func (p *Plugin) Note(commaSeparatedTags string, content booklit.Content) booklit.Content { - tags := strings.Split(commaSeparatedTags, ",") - - p.noteIdx++ - - idx := p.noteIdx - targetTag := fmt.Sprintf("%s-note-%d", p.section.PrimaryTag.Name, idx) - - tagNotes := []booklit.Content{} - for _, t := range tags { - tagNotes = append(tagNotes, booklit.Styled{ - Style: "release-note-tag", - Content: booklit.String(t), - }) - } - - return booklit.Styled{ - Style: "release-note", - Content: content, - Partials: booklit.Partials{ - "Tags": booklit.Sequence(tagNotes), - "Target": booklit.String(targetTag), - }, - } -} - func (p Plugin) RightSide(title, content booklit.Content) { wrappedContent := booklit.Styled{ Style: "sidebar-right", @@ -849,192 +263,12 @@ func (p Plugin) TrademarkGuidelines(content ...booklit.Content) booklit.Content } } -var archivedBinariesVersion = semver.MustParse("5.0.0") -var flyBinariesVersion = semver.MustParse("2.2.0") - -func (p Plugin) ReleaseVersion(version string) error { - p.section.Style = "release" - - p.section.SetTitle(booklit.String("v"+version), p.section.InvokeLocation) - - p.section.SetPartial("Version", booklit.String(version)) - - v, err := semver.Parse(version) - if err != nil { - return err - } - - downloadURL := "https://github.com/concourse/concourse/releases/download/v" + version - - if v.GTE(archivedBinariesVersion) { - p.section.SetPartial("ConcourseLinuxURL", booklit.String(downloadURL+"/concourse-"+version+"-linux-amd64.tgz")) - p.section.SetPartial("ConcourseWindowsURL", booklit.String(downloadURL+"/concourse-"+version+"-windows-amd64.zip")) - p.section.SetPartial("ConcourseDarwinURL", booklit.String(downloadURL+"/concourse-"+version+"-darwin-amd64.tgz")) - - p.section.SetPartial("HasFlyBinaries", booklit.Empty) - p.section.SetPartial("FlyLinuxURL", booklit.String(downloadURL+"/fly-"+version+"-linux-amd64.tgz")) - p.section.SetPartial("FlyWindowsURL", booklit.String(downloadURL+"/fly-"+version+"-windows-amd64.zip")) - p.section.SetPartial("FlyDarwinURL", booklit.String(downloadURL+"/fly-"+version+"-darwin-amd64.tgz")) - } else { - p.section.SetPartial("ConcourseLinuxURL", booklit.String(downloadURL+"/concourse_linux_amd64")) - p.section.SetPartial("ConcourseWindowsURL", booklit.String(downloadURL+"/concourse_windows_amd64.exe")) - p.section.SetPartial("ConcourseDarwinURL", booklit.String(downloadURL+"/concourse_darwin_amd64")) - - if v.GTE(flyBinariesVersion) { - p.section.SetPartial("HasFlyBinaries", booklit.Empty) - p.section.SetPartial("FlyLinuxURL", booklit.String(downloadURL+"/fly_linux_amd64")) - p.section.SetPartial("FlyWindowsURL", booklit.String(downloadURL+"/fly_windows_amd64.exe")) - p.section.SetPartial("FlyDarwinURL", booklit.String(downloadURL+"/fly_darwin_amd64")) - } - } - - return nil -} - -func (p Plugin) ReleaseGardenLinuxVersion(version string) { - p.section.SetPartial("GardenLinuxVersion", booklit.String(version)) -} - -func (p Plugin) ReleaseGardenRuncVersion(version string) { - p.section.SetPartial("GardenRuncVersion", booklit.String(version)) -} - -func (p Plugin) ReleaseDate(date string) error { - t, err := time.Parse("2006-1-2", date) - if err != nil { - return err - } - - p.section.SetPartial("ReleaseDate", booklit.Styled{ - Style: "release-date", - Content: booklit.String(t.Format("January 2, 2006")), - }) - - return nil -} - -func (p Plugin) PromethusDocs(sample string) (booklit.Content, error) { - parser := new(expfmt.TextParser) - - mfs, err := parser.TextToMetricFamilies(bytes.NewBufferString(sample)) - if err != nil { - return nil, fmt.Errorf("failed to parse prometheus sample: %w", err) - } - - type metric struct { - name string - family *dto.MetricFamily - } - - metrics := booklit.Sequence{} - - sorted := []metric{} - for metricName, family := range mfs { - sorted = append(sorted, metric{ - name: metricName, - family: family, - }) - } - - sort.Slice(sorted, func(i, j int) bool { - return sorted[i].name < sorted[j].name - }) - - for _, metric := range sorted { - metricName := metric.name - family := metric.family - - distinctLabels := map[string]bool{} - - labels := booklit.Sequence{} - for _, metric := range family.GetMetric() { - for _, label := range metric.GetLabel() { - labelName := label.GetName() - - if distinctLabels[labelName] { - continue - } - - distinctLabels[labelName] = true - - labels = append(labels, booklit.String(labelName)) - } - } - - metricType := strings.ToLower(family.GetType().String()) - - metrics = append(metrics, booklit.Styled{ - Style: "prometheus-metric", - Content: booklit.String(family.GetHelp()), - Partials: booklit.Partials{ - "Name": booklit.String(metricName), - "Type": booklit.String(metricType), - "Labels": labels, - }, - }) - } - - return metrics, nil -} - -func (p Plugin) autoReferenceType(type_ string) booklit.Content { - if strings.HasPrefix(type_, "[") && strings.HasSuffix(type_, "]") { - subType := strings.TrimPrefix(strings.TrimSuffix(type_, "]"), "[") - return booklit.Sequence{ - booklit.String("["), - p.autoReferenceType(subType), - booklit.String("]"), - } - } - - if strings.HasPrefix(type_, "{") && strings.HasSuffix(type_, "}") { - subType := strings.TrimPrefix(strings.TrimSuffix(type_, "}"), "{") - return booklit.Sequence{ - booklit.String("{"), - p.autoReferenceType(subType), - booklit.String("}"), - } - } - - for _, punc := range []string{" | ", ": ", ", "} { - if strings.Contains(type_, punc) { - ors := strings.Split(type_, punc) - - seq := booklit.Sequence{} - for i, t := range ors { - seq = append(seq, p.autoReferenceType(t)) - - if i+1 < len(ors) { - seq = append(seq, booklit.String(punc)) - } - } - - return seq - } - } - - if strings.HasPrefix(type_, "`") && strings.HasSuffix(type_, "`") { - scalar := strings.TrimPrefix(strings.TrimSuffix(type_, "`"), "`") - return booklit.Styled{ - Style: "schema-scalar", - Content: booklit.String(scalar), - } - } - - return &booklit.Reference{ - TagName: "schema." + type_, - Location: p.section.InvokeLocation, - Content: booklit.Styled{ - Style: booklit.StyleBold, - Content: booklit.String(type_), - }, - } -} - -type NoIndex struct { +type OmitFromSearchExcerpt struct { booklit.Content } -func (NoIndex) String() string { +// Strings returns an empty string, preventing the content from appearing in +// search index excerpts. +func (OmitFromSearchExcerpt) String() string { return "" } diff --git a/go/docs/schema.go b/go/docs/schema.go new file mode 100644 index 000000000..a4e0818f7 --- /dev/null +++ b/go/docs/schema.go @@ -0,0 +1,229 @@ +package docs + +import ( + "crypto/sha1" + "fmt" + "strings" + + "github.com/vito/booklit" + "github.com/vito/booklit/ast" + "github.com/vito/booklit/stages" +) + +func (p *Plugin) Schema(name string, contentNode ast.Node) (booklit.Content, error) { + old := p.schemaContext + p.schemaContext = []string{name} + defer func() { + p.schemaContext = old + }() + + tagName := "schema." + name + + p.section.SetTagAnchored( + tagName, + booklit.Sequence{ + booklit.Styled{ + Style: booklit.StyleBold, + Content: booklit.Styled{ + Style: booklit.StyleVerbatim, + Content: booklit.String(name), + }, + }, + booklit.String(" schema"), + }, + p.section.InvokeLocation, + booklit.Empty, + tagName, + ) + + stage := &stages.Evaluate{ + Section: p.section, + } + + err := contentNode.Visit(stage) + if err != nil { + return nil, err + } + + content := stage.Result + + return booklit.Styled{ + Style: "schema", + Block: true, + Content: content, + Partials: booklit.Partials{ + "Name": booklit.String(name), + "Anchor": booklit.String(tagName), + }, + }, nil +} + +func (p *Plugin) SchemaGroup(title booklit.Content, tagName string, contentNode ast.Node) (booklit.Content, error) { + p.pushSchema(tagName) + defer p.popSchema() + + p.schemaGroupTitle = title + defer func() { + p.schemaGroupTitle = nil + }() + + stage := &stages.Evaluate{ + Section: p.section, + } + + err := contentNode.Visit(stage) + if err != nil { + return nil, err + } + + content := stage.Result + + return booklit.Styled{ + Style: "schema-group", + Block: true, + Content: content, + Partials: booklit.Partials{ + "Title": title, + "TagName": booklit.String(tagName), + "Target": booklit.Target{ + TagName: tagName, + Location: p.section.InvokeLocation, + Title: title, + Content: content, + }, + }, + }, nil +} + +func (p *Plugin) RequiredAttribute(attribute string, type_ string, contentNode ast.Node) (booklit.Content, error) { + return p.schemaAttribute( + attribute, + type_, + contentNode, + booklit.Partials{ + "Required": booklit.String("true"), + }, + ) +} + +func (p *Plugin) OptionalAttribute(attribute string, type_ string, contentNode ast.Node) (booklit.Content, error) { + return p.schemaAttribute( + attribute, + type_, + contentNode, + booklit.Partials{ + "Optional": booklit.String("true"), + }, + ) +} + +func (p *Plugin) ExampleToggle(title, content booklit.Content) booklit.Content { + uniq := strings.Join(p.schemaContext, ".") + title.String() + hash := sha1.Sum([]byte(uniq)) + + return booklit.Styled{ + Style: "example-toggle", + Block: true, + Content: content, + Partials: booklit.Partials{ + "Title": title, + "For": booklit.String(fmt.Sprintf("%x", hash)), + }, + } +} + +func (p *Plugin) OneOf(options ...booklit.Content) booklit.Content { + return booklit.Styled{ + Style: "schema-one-of", + Block: true, + Content: booklit.Sequence(options), + } +} + +func (p *Plugin) SchemaAttribute(attribute string, type_ string, contentNode ast.Node) (booklit.Content, error) { + return p.schemaAttribute( + attribute, + type_, + contentNode, + booklit.Partials{}, + ) +} + +func (p *Plugin) schemaAttribute(attribute string, type_ string, contentNode ast.Node, partials booklit.Partials) (booklit.Content, error) { + p.pushSchema(attribute) + defer p.popSchema() + + tagName := "schema." + strings.Join(p.schemaContext, ".") + + stage := &stages.Evaluate{ + Section: p.section, + } + + err := contentNode.Visit(stage) + if err != nil { + return nil, err + } + + content := stage.Result + + var display booklit.Content + if p.schemaGroupTitle != nil { + display = booklit.Sequence{ + p.schemaGroupTitle, + booklit.String(" "), + booklit.Styled{ + Style: booklit.StyleBold, + Content: booklit.Styled{ + Style: booklit.StyleVerbatim, + Content: booklit.String(attribute), + }, + }, + } + } else { + attr := booklit.Sequence{} + for _, con := range p.schemaContext { + field := con + if len(attr) > 0 { + field = "." + field + } + + attr = append(attr, booklit.String(field)) + } + + display = booklit.Styled{ + Style: "schema-attribute-name", + Content: attr, + } + } + + targets := booklit.Sequence{ + booklit.Target{ + TagName: tagName, + Location: p.section.InvokeLocation, + Title: display, + Content: content, + }, + } + + partials["TagName"] = booklit.String(tagName) + partials["Targets"] = targets + partials["Attribute"] = booklit.String(attribute) + partials["Type"] = p.autoReferenceType(type_) + + return OmitFromSearchExcerpt{ + booklit.Styled{ + Style: "schema-attribute", + Block: true, + Content: content, + Partials: partials, + }, + }, nil +} + +func (p *Plugin) pushSchema(attribute string) { + p.schemaContext = append(p.schemaContext, attribute) +} + +func (p *Plugin) popSchema() { + p.schemaContext = p.schemaContext[0 : len(p.schemaContext)-1] +} diff --git a/go/docs/splash.go b/go/docs/splash.go new file mode 100644 index 000000000..c0aaeccb0 --- /dev/null +++ b/go/docs/splash.go @@ -0,0 +1,41 @@ +package docs + +import "github.com/vito/booklit" + +func (p Plugin) SplashIntro(intro, downloads booklit.Content) { + p.section.SetPartial( + "Splash", + booklit.Styled{ + Style: "splash-intro", + Block: true, + + Content: intro, + + Partials: booklit.Partials{ + "Downloads": downloads, + }, + }, + ) +} + +func (p Plugin) QuickStart(content booklit.Content) booklit.Content { + return booklit.Styled{ + Style: "quick-start", + Block: true, + Content: content, + } +} + +func (p Plugin) SplashExample(title booklit.Content, content booklit.Content, example booklit.Content) booklit.Content { + return booklit.Styled{ + Style: "splash-example", + Block: true, + + Content: content, + + Partials: booklit.Partials{ + "Title": title, + "Example": example, + }, + } +} diff --git a/html/example-pipeline.tmpl b/html/example-pipeline.tmpl deleted file mode 100644 index ec6ebc29f..000000000 --- a/html/example-pipeline.tmpl +++ /dev/null @@ -1 +0,0 @@ - diff --git a/html/literate-segment.tmpl b/html/literate-segment.tmpl deleted file mode 100644 index f44a1f7a6..000000000 --- a/html/literate-segment.tmpl +++ /dev/null @@ -1,9 +0,0 @@ -
-
-
- {{.Content | render}} -
- - {{.Partial "Code" | render}} -
-
From 78a2bc4f6c3b2c39ffd56b7d20487c3abb17e5b4 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Thu, 18 Mar 2021 18:20:59 -0400 Subject: [PATCH 03/13] fix inconsistent palette use for tr border color Signed-off-by: Alex Suraci --- css/blog.css | 2 +- css/booklit.css | 2 +- less/base.less | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/css/blog.css b/css/blog.css index 877e253f5..2ef8303ed 100644 --- a/css/blog.css +++ b/css/blog.css @@ -266,7 +266,7 @@ h3 { .page-content tr:first-child { font-family: 'Roboto Slab', serif; font-weight: 500; - border-bottom: 2px solid black; + border-bottom: 2px solid #2a2929; } .page-content td { padding: 0.3em 1em 0.3em 0; diff --git a/css/booklit.css b/css/booklit.css index 6cb5b0dc0..3cc87e20b 100644 --- a/css/booklit.css +++ b/css/booklit.css @@ -266,7 +266,7 @@ h3 { .page-content tr:first-child { font-family: 'Roboto Slab', serif; font-weight: 500; - border-bottom: 2px solid black; + border-bottom: 2px solid #2a2929; } .page-content td { padding: 0.3em 1em 0.3em 0; diff --git a/less/base.less b/less/base.less index 95261c2aa..9f903f816 100644 --- a/less/base.less +++ b/less/base.less @@ -305,7 +305,7 @@ h2, h3 { .page-content tr:first-child { font-family: 'Roboto Slab', serif; font-weight: 500; - border-bottom: 2px solid black; + border-bottom: 2px solid @darker-grey; } .page-content td { From e449c42af9473db37ef4963ed8498506faf63bad Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Thu, 18 Mar 2021 18:24:00 -0400 Subject: [PATCH 04/13] remove unused template Signed-off-by: Alex Suraci --- html/download-page.tmpl | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 html/download-page.tmpl diff --git a/html/download-page.tmpl b/html/download-page.tmpl deleted file mode 100644 index 0609db000..000000000 --- a/html/download-page.tmpl +++ /dev/null @@ -1,30 +0,0 @@ - - - - {{template "head.tmpl" .}} - - - {{template "top.tmpl" .}} - - - -
- {{- if .Partial "RightSide" }} -
- {{.Partial "RightSide" | render}} -
- {{ end -}} -
- -
- {{template "section.tmpl" .}} -
- - - - From 959e47efbf8c0d93619e010a8ab6e18ec0a7c967 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Thu, 18 Mar 2021 18:24:51 -0400 Subject: [PATCH 05/13] add \ghlabel function ended up not using this, but it may be useful in the future Signed-off-by: Alex Suraci --- go/docs/colors.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++ go/docs/github.go | 25 +++++++++++++++++++ html/label.tmpl | 1 + 3 files changed, 89 insertions(+) create mode 100644 go/docs/colors.go create mode 100644 html/label.tmpl diff --git a/go/docs/colors.go b/go/docs/colors.go new file mode 100644 index 000000000..df8e64858 --- /dev/null +++ b/go/docs/colors.go @@ -0,0 +1,63 @@ +package docs + +import ( + "math" + + "github.com/lucasb-eyer/go-colorful" +) + +var white = colorful.Color{ + R: 1, + B: 1, + G: 1, +} + +var black = colorful.Color{ + R: 0, + G: 0, + B: 0, +} + +// red, green, and blue coefficients +const rc = 0.2126 +const gc = 0.7152 +const bc = 0.0722 + +// low-gamma adjust coefficient +const lowc = 1 / 12.92 + +func adjustGamma(v float64) float64 { + return math.Pow((v+0.055)/1.055, 2.4) +} + +func contrast(a, b colorful.Color) float64 { + al := relativeLuminance(a) + bl := relativeLuminance(b) + lighter := math.Max(al, bl) + darker := math.Min(al, bl) + return (lighter + 0.05) / (darker + 0.05) +} + +func relativeLuminance(color colorful.Color) float64 { + var r, g, b float64 + + if color.R <= 0.03928 { + r = color.R * lowc + } else { + r = adjustGamma(color.R) + } + + if color.G <= 0.03928 { + g = color.G * lowc + } else { + g = adjustGamma(color.G) + } + + if color.B <= 0.03928 { + b = color.B * lowc + } else { + b = adjustGamma(color.B) + } + + return r*rc + g*gc + b*bc +} diff --git a/go/docs/github.go b/go/docs/github.go index f5734c887..e8a165c08 100644 --- a/go/docs/github.go +++ b/go/docs/github.go @@ -10,6 +10,7 @@ import ( "github.com/blang/semver" "github.com/google/go-github/github" + "github.com/lucasb-eyer/go-colorful" "github.com/vito/booklit" "golang.org/x/oauth2" ) @@ -69,6 +70,30 @@ func (p Plugin) Ghissue(number string, optionalRepo ...string) booklit.Content { } } +func (p Plugin) Ghlabel(name booklit.Content, colorHex string) (booklit.Content, error) { + color, err := colorful.Hex(colorHex) + if err != nil { + return nil, err + } + + lightContrastRating := contrast(color, white) + darkContrastRating := contrast(color, color.BlendRgb(black, 0.8)) + + class := "dark-label" + if lightContrastRating > darkContrastRating { + class = "light-label" + } + + return booklit.Styled{ + Style: "label", + Content: name, + Partials: booklit.Partials{ + "Color": booklit.String(colorHex), + "Class": booklit.String(class), + }, + }, nil +} + func (p *Plugin) DownloadLinks() (booklit.Content, error) { ctx := context.Background() diff --git a/html/label.tmpl b/html/label.tmpl new file mode 100644 index 000000000..affb0d4cb --- /dev/null +++ b/html/label.tmpl @@ -0,0 +1 @@ +{{.Content | render}} From 6b47479e731e99ca501bcc558cc04574f8c22a92 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Thu, 18 Mar 2021 18:25:13 -0400 Subject: [PATCH 06/13] install booklit with 'go get' pretty sure this fixed weirdness with go 1.16 Signed-off-by: Alex Suraci --- scripts/build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build b/scripts/build index 2208893b1..e9fdc3f23 100755 --- a/scripts/build +++ b/scripts/build @@ -4,7 +4,7 @@ set -e -u cd $(dirname $0)/.. -go install github.com/vito/booklit/cmd/booklit +go get github.com/vito/booklit/cmd/booklit booklit -i lit/index.lit -o . \ --save-search-index \ From 44b551100e4656c1fb6d36df0e075a2591b45880 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Thu, 18 Mar 2021 18:26:32 -0400 Subject: [PATCH 07/13] new front page with RFCs table * switch to GitHub GraphQL API * $GITHUB_TOKEN is now required * fetch top RFCs at build time * cached so it doesn't happen all the time in server mode * show number of open questions for each RFC * sort RFC table by reactions to focus-fire feedback on the most popular RFCs so we can resolve them more quickly Signed-off-by: Alex Suraci --- css/booklit.css | 140 ++++++++++++- go.mod | 8 +- go.sum | 19 +- go/docs/github.go | 68 +++++-- go/docs/rfcs.go | 427 +++++++++++++++++++++++++++++++++++++++ go/docs/splash.go | 37 ++-- html/download-links.tmpl | 7 - html/index-page.tmpl | 13 +- html/page-region.tmpl | 7 + html/rfc-questions.tmpl | 1 + html/rfc-reaction.tmpl | 1 + html/rfc-status.tmpl | 1 + html/rfc.tmpl | 6 + html/rfcs-table.tmpl | 21 ++ html/splash-intro.tmpl | 16 +- less/index.less | 173 +++++++++++++++- lit/index.lit | 323 +++++++++++++++-------------- 17 files changed, 1032 insertions(+), 236 deletions(-) create mode 100644 go/docs/rfcs.go create mode 100644 html/page-region.tmpl create mode 100644 html/rfc-questions.tmpl create mode 100644 html/rfc-reaction.tmpl create mode 100644 html/rfc-status.tmpl create mode 100644 html/rfc.tmpl create mode 100644 html/rfcs-table.tmpl diff --git a/css/booklit.css b/css/booklit.css index 3cc87e20b..1a63695e9 100644 --- a/css/booklit.css +++ b/css/booklit.css @@ -680,10 +680,12 @@ a.github-user:hover { text-decoration: underline; } body.index { - background: #f6f5f5; + background: url('../images/concourse-pattern.svg') center center no-repeat; + background-size: cover; + background-attachment: fixed; } body.index .page-content p { - font-size: 16px; + font-size: 18px; } body.index .page-content a { color: #1e1d1d; @@ -699,11 +701,11 @@ body.index .page-content a { background: #3d3c3c; } .splash { - background: url('../images/concourse-pattern.svg') center center / cover no-repeat; color: white; } .splash .splash-content { - padding: 70px; + padding: 140px 70px; + background: rgba(255, 255, 255, 0.1); max-width: 1100px; margin: 0 auto; } @@ -725,10 +727,10 @@ body.index .page-content h2 { margin-bottom: 14px; font-size: 19px; border: 0; - margin-top: 64px; + margin-top: 100px; } body.index .page-content h2:first-child { - margin-top: 0; + margin-top: 50px; } .splash-intro, .splash-downloads { @@ -786,7 +788,6 @@ body.index .page-content h2:first-child { } .splash-downloads { background: #2a2929; - border: 2px solid #3d3c3c; justify-self: stretch; position: relative; } @@ -885,13 +886,16 @@ body.index .page-content h2:first-child { .side-by-side { display: grid; margin-bottom: 1em; - grid-template-columns: 50% 50%; - grid-gap: 0 30px; + grid-template-columns: 48% 48%; + grid-gap: 0 4%; } .side-by-side .segment { display: flex; flex-direction: column; } +.side-by-side .segment.example { + margin: 0; +} .side-by-side .segment.example pre, .side-by-side .segment.example p { margin: 0; @@ -905,6 +909,124 @@ body.index .page-content h2:first-child { grid-gap: 30px 0; } } +.rfcs { + width: 100%; + font-size: 18px; +} +.rfcs a { + text-decoration: none; +} +.rfcs a:hover { + text-decoration: underline; +} +.rfcs tr:nth-child(odd) { + background: #f6f5f5; +} +.rfcs tr:nth-child(odd).header { + background: transparent; +} +.rfcs .rfc-number, +.rfcs .rfc-status, +.rfcs .rfc-questions { + padding: 3px 5px; + color: white; +} +.rfcs .rfc-number, +.rfcs .rfc-questions { + font-family: 'Iosevka', monospace; + background: #3d3c3c; + color: white !important; +} +.rfcs .rfc-status a { + color: white !important; +} +.rfcs .rfc-status.no-reviews { + background: #5a5555; +} +.rfcs .rfc-status.open { + background: #4a90e2; +} +.rfcs .rfc-status.pending-merge { + background: #11c560; +} +.rfcs .rfc-status.pending-close { + background: #ed4b35; +} +.rfcs .rfc-status.pending-postpone { + background: #f5a623; +} +.rfcs .rfc-questions { + background: #f5a623; + color: white !important; +} +.rfcs .reaction { + padding: 3px 5px; + line-height: 18px; + border-radius: 5px; + width: 50px; + display: inline-block; +} +.rfcs .reaction .emoji { + font-size: 16px; +} +.rfcs .reaction .count { + margin-left: 5px; + font-size: 16px; +} +.rfcs .rfc-col { + width: 150px; +} +.rfcs .status-col { + width: 150px; +} +.rfcs .reactions-col { + width: 150px; +} +.rfcs tr.footer { + background: transparent; +} +.rfcs .view-all-col a { + text-align: center; + line-height: 40px; + font-family: 'Roboto Slab', serif; + font-size: 18px; + color: white !important; + background: #3d3c3c; + display: block; +} +.rfcs .view-all-col a:hover { + text-decoration: none; + background: #2a2929; +} +.page-region p { + font-size: 18px; +} +.page-region .page-content { + padding: 70px; + max-width: 1100px; + margin: 0 auto; +} +.page-region .page-content h1 { + border: 0; + padding-top: 0; +} +.page-region.dark { + background: #2a2929; + color: white; +} +.page-region.dark .page-content { + background: #3d3c3c; +} +.page-region.dark .page-content a { + color: white; +} +.page-region.dark .page-content a code { + color: white; + background: #2a2929; +} +.page-region + .page-region .page-content { + border-top: 2px dashed #e6e5e5; +} .schema { margin: 1.5em 0; margin-bottom: 1.5em; diff --git a/go.mod b/go.mod index ef07aa055..4350343d5 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,16 @@ module github.com/concourse/docs require ( + github.com/PuerkitoBio/goquery v1.6.1 github.com/blang/semver v3.5.1+incompatible - github.com/google/go-github v17.0.0+incompatible - github.com/google/go-querystring v1.0.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 github.com/prometheus/client_model v0.2.0 github.com/prometheus/common v0.9.1 + github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa + github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a // indirect + github.com/sirupsen/logrus v1.7.0 github.com/vito/booklit v0.12.0 + golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 ) diff --git a/go.sum b/go.sum index bdb9da923..6bddc785f 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/PuerkitoBio/goquery v1.6.1 h1:FgjbQZKl5HTmcn4sKBgvx8vv63nhyhIpv7lJpFGCWpk= +github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= @@ -11,6 +13,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= @@ -45,10 +49,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -56,6 +56,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -92,6 +94,10 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/segmentio/textio v1.2.0 h1:Ug4IkV3kh72juJbG8azoSBlgebIbUUxVNrfFcKHfTSQ= github.com/segmentio/textio v1.2.0/go.mod h1:+Rb7v0YVODP+tK5F7FD9TCkV7gOYx9IgLHWiqtvY8ag= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa h1:jozR3igKlnYCj9IVHOVump59bp07oIRoLQ/CcjMYIUA= +github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa/go.mod h1:hAF0iLZy4td2EX+/8Tw+4nodhlMrwN3HupfaXj3zkGo= +github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a h1:KikTa6HtAK8cS1qjvUvvq4QO21QnwC+EfvB+OAuZ/ZU= +github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= 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/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= @@ -111,6 +117,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -118,11 +125,13 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201216054612-986b41b23924 h1:QsnDpLLOKwHBBDa8nDws4DYNc/ryVW2vCpxCs09d4PY= golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7 h1:OgUuv8lsRpBibGNbSizVwKWlysjaNzmC9gYMhPVfqFM= +golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/go/docs/github.go b/go/docs/github.go index e8a165c08..5cdc9f44c 100644 --- a/go/docs/github.go +++ b/go/docs/github.go @@ -9,8 +9,8 @@ import ( "strings" "github.com/blang/semver" - "github.com/google/go-github/github" "github.com/lucasb-eyer/go-colorful" + "github.com/shurcooL/githubv4" "github.com/vito/booklit" "golang.org/x/oauth2" ) @@ -99,17 +99,42 @@ func (p *Plugin) DownloadLinks() (booklit.Content, error) { client := p.githubClient(ctx) - var latestRelease *github.RepositoryRelease - var latestVersion semver.Version - releases, _, err := client.Repositories.ListReleases(ctx, "concourse", "concourse", nil) + type GitHubRelease struct { + Name string + TagName *string + IsDraft bool + IsPrerelease bool + Url string + Assets struct { + Nodes []struct { + Name string + DownloadUrl string + } + } `graphql:"releaseAssets(first: 20)"` + } + + var releasesQuery struct { + Repository struct { + Releases struct { + Nodes []GitHubRelease + } `graphql:"releases(first: 10, orderBy: {field: CREATED_AT, direction: DESC})"` + } `graphql:"repository(owner: \"concourse\", name: \"concourse\")"` + } + + err := client.Query(ctx, &releasesQuery, nil) if err != nil { - return nil, err + return nil, fmt.Errorf("fetch releases: %w", err) } + + releases := releasesQuery.Repository.Releases.Nodes + + var latestRelease *GitHubRelease + var latestVersion semver.Version if len(releases) == 0 { return nil, errors.New("no releases found!") } for _, release := range releases { - if release.GetPrerelease() { + if release.IsPrerelease { continue } @@ -120,39 +145,40 @@ func (p *Plugin) DownloadLinks() (booklit.Content, error) { if err != nil { return nil, err } + if latestRelease == nil || version.GT(latestVersion) { - latestRelease = release + latestRelease = &release latestVersion = version } } var linuxURL, darwinURL, windowsURL string var flyLinuxURL, flyDarwinURL, flyWindowsURL string - for _, asset := range latestRelease.Assets { - name := asset.GetName() + for _, asset := range latestRelease.Assets.Nodes { + name := asset.Name if strings.Contains(name, "concourse") && strings.Contains(name, "linux") && strings.HasSuffix(name, ".tgz") { - linuxURL = asset.GetBrowserDownloadURL() + linuxURL = asset.DownloadUrl } if strings.Contains(name, "concourse") && strings.Contains(name, "darwin") && strings.HasSuffix(name, ".tgz") { - darwinURL = asset.GetBrowserDownloadURL() + darwinURL = asset.DownloadUrl } if strings.Contains(name, "concourse") && strings.Contains(name, "windows") && strings.HasSuffix(name, ".zip") { - windowsURL = asset.GetBrowserDownloadURL() + windowsURL = asset.DownloadUrl } if strings.Contains(name, "fly") && strings.Contains(name, "linux") && strings.HasSuffix(name, ".tgz") { - flyLinuxURL = asset.GetBrowserDownloadURL() + flyLinuxURL = asset.DownloadUrl } if strings.Contains(name, "fly") && strings.Contains(name, "darwin") && strings.HasSuffix(name, ".tgz") { - flyDarwinURL = asset.GetBrowserDownloadURL() + flyDarwinURL = asset.DownloadUrl } if strings.Contains(name, "fly") && strings.Contains(name, "windows") && strings.HasSuffix(name, ".zip") { - flyWindowsURL = asset.GetBrowserDownloadURL() + flyWindowsURL = asset.DownloadUrl } } @@ -160,12 +186,12 @@ func (p *Plugin) DownloadLinks() (booklit.Content, error) { Style: "download-links", Block: true, Content: booklit.Link{ - Target: latestRelease.GetHTMLURL(), - Content: booklit.String(latestRelease.GetName()), + Target: latestRelease.Url, + Content: booklit.String(latestRelease.Name), }, Partials: booklit.Partials{ - "Version": booklit.String(latestRelease.GetName()), - "ReleaseNotesURL": booklit.String(latestRelease.GetHTMLURL()), + "Version": booklit.String(latestRelease.Name), + "ReleaseNotesURL": booklit.String(latestRelease.Url), "LinuxURL": booklit.String(linuxURL), "DarwinURL": booklit.String(darwinURL), "WindowsURL": booklit.String(windowsURL), @@ -176,7 +202,7 @@ func (p *Plugin) DownloadLinks() (booklit.Content, error) { }, nil } -func (p *Plugin) githubClient(ctx context.Context) *github.Client { +func (p *Plugin) githubClient(ctx context.Context) *githubv4.Client { var tc *http.Client var githubToken = os.Getenv("GITHUB_TOKEN") @@ -188,5 +214,5 @@ func (p *Plugin) githubClient(ctx context.Context) *github.Client { tc = oauth2.NewClient(ctx, ts) } - return github.NewClient(tc) + return githubv4.NewClient(tc) } diff --git a/go/docs/rfcs.go b/go/docs/rfcs.go new file mode 100644 index 000000000..7a2359d89 --- /dev/null +++ b/go/docs/rfcs.go @@ -0,0 +1,427 @@ +package docs + +import ( + "context" + "fmt" + "net/http" + "reflect" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/PuerkitoBio/goquery" + "github.com/sirupsen/logrus" + "github.com/vito/booklit" +) + +const resolutionMerge = "resolution/merge" +const resolutionPostpone = "resolution/postpone" +const resolutionClose = "resolution/close" + +var cachedRFCs []PullRequest +var cacheOnce = new(sync.Once) + +type PullRequest struct { + URL string + Number int + Title string + IsDraft bool + + CreatedAt time.Time + + ProposalURL string + QuestionsURL string + OpenQuestionCount int + + Labels []GitHubLabel + Reactions []GitHubReaction + + CommentCount int + ReviewCount int +} + +func (pr PullRequest) ByTotalReactions() int { + var s int + for _, reaction := range pr.Reactions { + s += reaction.Count + } + + return s +} + +func (pr PullRequest) ByOpenQuestions() int { + return pr.OpenQuestionCount +} + +func (pr PullRequest) ByCreatedAt() int { + return int(pr.CreatedAt.Unix()) +} + +func (pr PullRequest) ByReviews() int { + return pr.ReviewCount +} + +func (pr PullRequest) Resolving() bool { + return pr.HasLabel(resolutionMerge) || + pr.HasLabel(resolutionPostpone) || + pr.HasLabel(resolutionClose) +} + +func (pr PullRequest) HasLabel(name string) bool { + for _, label := range pr.Labels { + if label.Name == name { + return true + } + } + + return false +} + +type GitHubLabel struct { + Name string + Color string +} + +type GitHubReaction struct { + Emoji string + Count int +} + +func (p *Plugin) RfcsTable(countStr string, sortBy string) (booklit.Content, error) { + count, err := strconv.Atoi(countStr) + if err != nil { + return nil, fmt.Errorf("invalid rfc count: %w", err) + } + + ctx := context.Background() + + cacheOnce.Do(func() { + cachedRFCs, err = p.fetchRFCs(ctx) + }) + if err != nil { + return nil, err + } + + rfcPRs := make([]PullRequest, len(cachedRFCs)) + copy(rfcPRs, cachedRFCs) + + sorter := prsBy{sortBy, rfcPRs} + sort.Sort(sort.Reverse(sorter)) + + rfcs := booklit.Sequence{} + for i, rfc := range sorter.PRs { + if i > count { + break + } + + rfcs = append(rfcs, rfcRow(rfc)) + } + + return booklit.Styled{ + Style: "rfcs-table", + Content: rfcs, + Block: true, + }, nil +} + +func rfcRow(rfc PullRequest) booklit.Content { + var status booklit.Content + switch { + case rfc.HasLabel(resolutionMerge): + status = booklit.Styled{ + Style: "rfc-status", + Content: booklit.String("merging"), + Partials: booklit.Partials{ + "Class": booklit.String("pending-merge"), + }, + } + case rfc.HasLabel(resolutionClose): + status = booklit.Styled{ + Style: "rfc-status", + Content: booklit.String("closing"), + Partials: booklit.Partials{ + "Class": booklit.String("pending-close"), + }, + } + case rfc.HasLabel(resolutionPostpone): + status = booklit.Styled{ + Style: "rfc-status", + Content: booklit.String("postponing"), + Partials: booklit.Partials{ + "Class": booklit.String("pending-postpone"), + }, + } + default: + status = booklit.Styled{ + Style: "rfc-status", + Content: booklit.Link{ + Target: rfc.URL + "/files", + Content: booklit.String("open"), + }, + Partials: booklit.Partials{ + "Class": booklit.String("open"), + }, + } + } + + reactions := booklit.Sequence{} + for _, reaction := range rfc.Reactions { + reactions = append(reactions, booklit.Styled{ + Style: "rfc-reaction", + Content: booklit.String(reaction.Emoji), + Partials: booklit.Partials{ + "Count": booklit.String(strconv.Itoa(reaction.Count)), + }, + }) + } + + var questions booklit.Content = booklit.Empty + if rfc.OpenQuestionCount > 0 { + questions = booklit.Styled{ + Style: "rfc-questions", + Content: booklit.String(strconv.Itoa(rfc.OpenQuestionCount)), + Partials: booklit.Partials{ + "QuestionsURL": booklit.String(rfc.QuestionsURL), + }, + } + } + + return booklit.Styled{ + Style: "rfc", + Content: booklit.Link{ + Target: rfc.URL, + Content: booklit.String(rfc.Title), + }, + Partials: booklit.Partials{ + "Number": booklit.String(strconv.Itoa(rfc.Number)), + "Status": status, + "Reactions": reactions, + "Questions": questions, + "ProposalURL": booklit.String(rfc.ProposalURL), + }, + } +} + +var reactionEmoji = map[string]string{ + "THUMBS_UP": "👍", + "THUMBS_DOWN": "👎", + "LAUGH": "😆", + "HOORAY": "🙌", + "CONFUSED": "😕", + "HEART": "❤️", + "ROCKET": "🚀", + "EYES": "👀", +} + +func (p *Plugin) fetchRFCs(ctx context.Context) ([]PullRequest, error) { + client := p.githubClient(ctx) + + type repoId struct { + Name string + Owner struct { + Login string + } + } + + var prsQuery struct { + Repository struct { + PullRequests struct { + Nodes []struct { + Number int + Title string + IsDraft bool + Url string + + CreatedAt time.Time + + Labels struct { + Nodes []GitHubLabel + } `graphql:"labels(first:10)"` + + HeadRefName string + HeadRefOid string + + BaseRepository *repoId + HeadRepository *repoId + + Files struct { + Nodes []struct { + Path string + } + } `graphql:"files(first:50)"` + + ReactionGroups []struct { + Content string + Users struct { + TotalCount int + } + } + + Comments struct { + TotalCount int + } + + Reviews struct { + TotalCount int + } + } + } `graphql:"pullRequests(first: 100, states: [OPEN])"` + } `graphql:"repository(owner: \"concourse\", name: \"rfcs\")"` + } + + err := client.Query(ctx, &prsQuery, nil) + if err != nil { + return nil, fmt.Errorf("fetch rfcs: %w", err) + } + + pulls := []PullRequest{} + for _, node := range prsQuery.Repository.PullRequests.Nodes { + if node.IsDraft { + // don't put drafts on the website; they're not ready for public + // consumption. drafts are for more targeted feedback from individuals. + continue + } + + reactions := []GitHubReaction{} + for _, rg := range node.ReactionGroups { + if rg.Users.TotalCount == 0 { + continue + } + + emoji, found := reactionEmoji[rg.Content] + if !found { + return nil, fmt.Errorf("unknown reaction group: %s", rg.Content) + } + + reactions = append(reactions, GitHubReaction{ + Emoji: emoji, + Count: rg.Users.TotalCount, + }) + } + + var proposalPath string + for _, file := range node.Files.Nodes { + if strings.HasSuffix(file.Path, "proposal.md") { + proposalPath = file.Path + } + } + + var repo repoId + if node.HeadRepository != nil { + repo = *node.HeadRepository + } else { + repo = *node.BaseRepository + } + + proposalURL := fmt.Sprintf( + "https://github.com/%s/%s/blob/%s/%s", + repo.Owner.Login, + repo.Name, + node.HeadRefOid, + proposalPath, + ) + + questionsURL, totalQuestions, err := countQuestions(proposalURL) + if err != nil { + return nil, fmt.Errorf("count open questions: %w", err) + } + + pulls = append(pulls, PullRequest{ + URL: node.Url, + Number: node.Number, + CreatedAt: node.CreatedAt, + Title: node.Title, + IsDraft: node.IsDraft, + Labels: node.Labels.Nodes, + + ProposalURL: proposalURL, + QuestionsURL: questionsURL, + OpenQuestionCount: totalQuestions, + + Reactions: reactions, + CommentCount: node.Comments.TotalCount, + ReviewCount: node.Reviews.TotalCount, + }) + } + + return pulls, nil +} + +func countQuestions(proposalURL string) (string, int, error) { + resp, err := http.Get(proposalURL) + if err != nil { + return "", 0, fmt.Errorf("get %s: %w", proposalURL, err) + } + + if resp.StatusCode != http.StatusOK { + return "", 0, fmt.Errorf("bad response for %s: %s", proposalURL, resp.Status) + } + + doc, err := goquery.NewDocumentFromReader(resp.Body) + if err != nil { + return "", 0, fmt.Errorf("query HTML: %w", err) + } + + var questionsURL string + var totalQuestions int + doc.Find("#readme").Find("h1").Each(func(i int, sel *goquery.Selection) { + if sel.Text() == "Open Questions" { + anchor, found := sel.Find("a.anchor").Attr("href") + if found { + questionsURL = proposalURL + anchor + logrus.Debugf("open questions: %s", questionsURL) + } + + questions := sel.NextUntil("h1") + + countQuestion := func(i int, sel *goquery.Selection) { + if strings.Contains(sel.Text(), "?") { + logrus.WithFields(logrus.Fields{ + "question": sel.Text(), + }).Debug("found question") + + totalQuestions++ + } + } + + questions.Filter("h2").Each(countQuestion) + questions.Find("li").Each(countQuestion) + questions.Find("p").Each(countQuestion) + + logrus.Infof("total questions: %d", totalQuestions) + } + }) + + return questionsURL, totalQuestions, nil +} + +type prsBy struct { + Method string + PRs []PullRequest +} + +func (by prsBy) Len() int { return len(by.PRs) } +func (by prsBy) Swap(i, j int) { by.PRs[i], by.PRs[j] = by.PRs[j], by.PRs[i] } + +func (by prsBy) Less(i, j int) bool { + pri := by.PRs[i] + prj := by.PRs[j] + + // regardless of order, show resolving PRs first + switch { + case pri.Resolving() && !prj.Resolving(): + return false + case !pri.Resolving() && prj.Resolving(): + return true + } + + ret := reflect.ValueOf(pri).MethodByName(by.Method).Call(nil) + a := ret[0].Interface().(int) + + ret = reflect.ValueOf(prj).MethodByName(by.Method).Call(nil) + b := ret[0].Interface().(int) + + return a < b +} diff --git a/go/docs/splash.go b/go/docs/splash.go index c0aaeccb0..847a63f20 100644 --- a/go/docs/splash.go +++ b/go/docs/splash.go @@ -2,20 +2,31 @@ package docs import "github.com/vito/booklit" -func (p Plugin) SplashIntro(intro, downloads booklit.Content) { - p.section.SetPartial( - "Splash", - booklit.Styled{ - Style: "splash-intro", - Block: true, - - Content: intro, - - Partials: booklit.Partials{ - "Downloads": downloads, - }, +func (p Plugin) SplashIntro(intro, downloads booklit.Content) booklit.Content { + return booklit.Styled{ + Style: "splash-intro", + Block: true, + + Content: intro, + + Partials: booklit.Partials{ + "Downloads": downloads, + }, + } +} + +func (p Plugin) PageRegion(style, title, content booklit.Content) booklit.Content { + return booklit.Styled{ + Style: "page-region", + Block: true, + + Content: content, + + Partials: booklit.Partials{ + "Style": style, + "Title": title, }, - ) + } } func (p Plugin) QuickStart(content booklit.Content) booklit.Content { diff --git a/html/download-links.tmpl b/html/download-links.tmpl index 845cf908a..f1ccdeda0 100644 --- a/html/download-links.tmpl +++ b/html/download-links.tmpl @@ -30,10 +30,3 @@ - -
-
-

Help build the next one:

- Roadmap -
-
diff --git a/html/index-page.tmpl b/html/index-page.tmpl index 8ece45dae..f25317653 100644 --- a/html/index-page.tmpl +++ b/html/index-page.tmpl @@ -5,17 +5,6 @@ {{template "top.tmpl" .}} - -
-
- {{.Partial "Splash" | render}} -
-
- -
-
- {{template "section.tmpl" .}} -
-
+ {{template "section.tmpl" .}} diff --git a/html/page-region.tmpl b/html/page-region.tmpl new file mode 100644 index 000000000..232a50a2d --- /dev/null +++ b/html/page-region.tmpl @@ -0,0 +1,7 @@ +
+
+

{{.Partial "Title" | render}}

+ + {{.Content | render}} +
+
diff --git a/html/rfc-questions.tmpl b/html/rfc-questions.tmpl new file mode 100644 index 000000000..decd3c167 --- /dev/null +++ b/html/rfc-questions.tmpl @@ -0,0 +1 @@ +{{.Content | render}} ?s diff --git a/html/rfc-reaction.tmpl b/html/rfc-reaction.tmpl new file mode 100644 index 000000000..a44b2e98f --- /dev/null +++ b/html/rfc-reaction.tmpl @@ -0,0 +1 @@ +{{.Content | render}}{{.Partial "Count" | render}} diff --git a/html/rfc-status.tmpl b/html/rfc-status.tmpl new file mode 100644 index 000000000..17848226d --- /dev/null +++ b/html/rfc-status.tmpl @@ -0,0 +1 @@ +{{.Content | render}} diff --git a/html/rfc.tmpl b/html/rfc.tmpl new file mode 100644 index 000000000..0c3dc9e81 --- /dev/null +++ b/html/rfc.tmpl @@ -0,0 +1,6 @@ + + RFC {{.Partial "Number" | render}}{{.Partial "Questions" | render}} + {{.Content | render}} + {{.Partial "Status" | render}}{{.Partial "Reviews" | render}} + {{.Partial "Reactions" | render}} + diff --git a/html/rfcs-table.tmpl b/html/rfcs-table.tmpl new file mode 100644 index 000000000..3f24da792 --- /dev/null +++ b/html/rfcs-table.tmpl @@ -0,0 +1,21 @@ + + + + + + + + + {{range .Content}} + + + + + + + {{end}} + + + + +
ProposalPull RequestStatusReactions
RFC {{.Partial "Number" | render}}{{.Partial "Questions" | render}}{{.Content | render}}{{.Partial "Status" | render}}{{.Partial "Reactions" | render}}
diff --git a/html/splash-intro.tmpl b/html/splash-intro.tmpl index 581631a2b..b2d9daa05 100644 --- a/html/splash-intro.tmpl +++ b/html/splash-intro.tmpl @@ -1,9 +1,13 @@ -
-
- {{.Content | render}} -
+
+
+
+
+ {{.Content | render}} +
-
- {{.Partial "Downloads" | render}} +
+ {{.Partial "Downloads" | render}} +
+
diff --git a/less/index.less b/less/index.less index 573bbd085..d98352b1d 100644 --- a/less/index.less +++ b/less/index.less @@ -1,9 +1,11 @@ body.index { - background: @off-white; + background: url('../images/concourse-pattern.svg') center center no-repeat; + background-size: cover; + background-attachment: fixed; .page-content { p { - font-size: 16px; + font-size: 18px; } a { @@ -25,11 +27,11 @@ body.index { } .splash { - background: url('../images/concourse-pattern.svg') center center / cover no-repeat; color: white; .splash-content { - padding: 70px; + padding: 140px 70px; + background: fade(white, 10%); max-width: @page-width; margin: 0 auto; } @@ -59,10 +61,10 @@ body.index .page-content h2 { font-size: 19px; border: 0; - margin-top: 64px; + margin-top: 100px; &:first-child { - margin-top: 0; + margin-top: 50px; } } @@ -142,7 +144,6 @@ body.index .page-content h2 { .splash-downloads { background: @darker-grey; - border: 2px solid @dark-grey; justify-self: stretch; position: relative; @@ -263,8 +264,8 @@ body.index .page-content h2 { .side-by-side { display: grid; margin-bottom: 1em; - grid-template-columns: 50% 50%; - grid-gap: 0 30px; + grid-template-columns: 48% 48%; + grid-gap: 0 4%; .segment { display: flex; @@ -272,6 +273,8 @@ body.index .page-content h2 { } .segment.example { + margin: 0; + pre, p { margin: 0; } @@ -288,3 +291,155 @@ body.index .page-content h2 { grid-gap: 30px 0; } } + +.rfcs { + width: 100%; + font-size: 18px; + + a { + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + + tr:nth-child(odd) { + background: @off-white; + + &.header { + background: transparent; + } + } + + .rfc-number, .rfc-status, .rfc-questions { + padding: 3px 5px; + color: white; + } + + .rfc-number, .rfc-questions { + font-family: 'Iosevka', monospace; + background: @dark-grey; + color: white !important; + } + + .rfc-status { + a { + color: white !important; + } + + &.no-reviews { + background: @light-grey; + } + + &.open { + background: @blue-primary; + } + + &.pending-merge { + background: @green-primary; + } + + &.pending-close { + background: @red-primary; + } + + &.pending-postpone { + background: @amber-primary; + } + } + + .rfc-questions { + background: @amber-primary; + color: white !important; + } + + .reaction { + padding: 3px 5px; + line-height: 18px; + border-radius: 5px; + width: 50px; + display: inline-block; + + .emoji { + font-size: 16px; + } + + .count { + margin-left: 5px; + font-size: 16px; + } + } + + .rfc-col { + width: 150px; + } + + .status-col { + width: 150px; + } + + .reactions-col { + width: 150px; + } + + tr.footer { + background: transparent; + } + + .view-all-col a { + text-align: center; + line-height: 40px; + font-family: 'Roboto Slab', serif; + font-size: 18px; + color: white !important; + background: @dark-grey; + display: block; + + &:hover { + text-decoration: none; + background: @darker-grey; + } + } +} + +.page-region { + p { + font-size: 18px; + } + + .page-content { + padding: 70px; + max-width: @page-width; + margin: 0 auto; + + h1 { + border: 0; + padding-top: 0; + } + } + + &.dark { + background: @darker-grey; + color: white; + + .page-content { + background: @dark-grey; + + a { + color: white; + + code { + color: white; + background: @darker-grey; + } + } + } + } +} + +.page-region + .page-region { + .page-content { + border-top: 2px dashed @offest-white; + } +} diff --git a/lit/index.lit b/lit/index.lit index cefca8d1a..942b094ce 100644 --- a/lit/index.lit +++ b/lit/index.lit @@ -16,162 +16,181 @@ \download-links } -\side-by-side{ - \inline-header{Configure as code} - - \codeblock{yaml}{{{ - resources: - - name: booklit - type: git - source: {uri: "https://github.com/vito/booklit"} - - jobs: - - name: unit - plan: - - get: booklit - trigger: true - - task: test - file: booklit/ci/test.yml - }}} -}{ - \inline-header{Visualize to verify} - - \include-template{basic-pipeline-svg} -} - -\side-by-side{ - A Concourse \reference{pipelines}{pipeline} is like a distributed, continuous - \code{Makefile}. - - Each \reference{jobs}{job} has a \reference{schema.job.plan}{build plan} - declaring the job's input \reference{resources}{resources} and what to run - with them when they change. -}{ - Your pipeline is then visualized in the web UI, taking only one click to get - from a failed job to seeing why it failed. - - The visualization provides a "gut check" feedback loop: if it \italic{looks} - wrong, it probably \italic{is} wrong. -} - -\inline-header{A more complicated example...} - -Jobs can depend on other jobs by configuring -\reference{schema.step.get-step.passed}{\code{passed}} constraints. The -resulting chain of jobs and resources is a dependency graph that continuously -pushes your project forward, from source code to production. - -\include-template{pipeline-image} - -\italic{This particular pipeline can be found in the \link{Booklit -repository}{https://github.com/vito/booklit/blob/8741a4ca3116dcf24c30fedfa78e4aadcaff178a/ci/pipeline.yml}.} +\page-region{light}{Built in the open}{ + Concourse's \link{RFC process}{https://github.com/concourse/rfcs} welcomes + anyone to submit ideas and feedback to influence the project roadmap. -\splash-example{CI under source control}{ - All configuration and administration is done using \reference{fly-cli}{the - \code{fly} CLI}. + \rfcs-table{10}{ByTotalReactions} - The \reference{fly-set-pipeline} command pushes the config up to Concourse. - Once it looks good, you can then check the file in to source control. This - makes it easy to recover your project if the Concourse server burns down. -}{ - \codeblock{sh}{{{ - $ fly -t ci set-pipeline -p booklit -c pipeline.yml - $ vim pipeline.yml - $ fly -t ci set-pipeline -p booklit -c pipeline.yml - $ git add pipeline.yml - $ git commit -m "initial pipeline"█ - }}} -} - -\splash-example{Reproducible, debuggable builds}{ - Everything runs in containers, ensuring a clean environment on every run. - - Each \reference{tasks}{task} specifies its own image, giving it full control - over its dependencies, rather than managing packages and state on your - workers. - - The \reference{fly-intercept} command will pop you right into one of your - build's containers, making it easy to troubleshoot flaky builds. -}{ - \codeblock{sh}{{{ - $ fly -t ci intercept -j booklit/unit -s unit - root@2c15ff11:/tmp/build/0df9eea0# ps - PID TTY TIME CMD - 171 pts/1 00:00:00 bash - 1876 pts/1 00:00:00 ps - root@2c15ff11:/tmp/build/0df9eea0# ls - depspath gopath - root@2c15ff11:/tmp/build/0df9eea0# █ - }}} + Help shape Concourse into a tool that meets your needs by \link{submitting + feedback}{https://github.com/concourse/rfcs/#providing-feedback-on-an-rfc}! + The above RFCs are ranked by their total GitHub reactions, with the aim being + to focus feedback on popular RFCs so that they reach resolution, making room + for more on the table. } -\splash-example{Rapid local iteration}{ - The \reference{fly-execute} command lets you run a build with local changes. - - This build runs in exactly the same way as it would run in your pipeline, - without having to push broken commits until it works. - - When a build in the pipeline fails, you can run \reference{fly-execute} with - the \code{-j} flag to run a one-off build with the same inputs as the failed - build. You can then replace an input with your local changes with \code{-i} - to see if your fix is valid. -}{ - \codeblock{sh}{{{ - ~/booklit $ fly -t ci execute -c ci/test.yml - executing build 1 at http://localhost:8080/builds/1 - initializing - booklit: 4.74 MiB/s 0s - running gopath/src/github.com/vito/booklit/ci/test - fetching dependencies... - installing ginkgo... - running tests... - █ - }}} -} - -\inline-header{Bring your own integrations} - -\side-by-side{ - \codeblock{yaml}{{{ - resource_types: - - name: rubygem - type: registry-image - source: - repository: troykinsella/concourse-rubygems-resource - - resources: - - name: rspec-gem - type: rubygem - source: {gem: rspec} - - jobs: - - name: bundle - plan: - - get: rspec-gem - trigger: true - - # ... - }}} -}{ - Concourse does not have a complex plugin system. Instead, it focuses on a - single strong abstraction: \reference{resources}{resource}, which are - implemented by \reference{resource-types}{resource types}. - - The \reference{schema.pipeline.resources} field configures external artifacts - that your pipeline will monitor for changes, fetch from, and push to. - - For example, a resource with type \code{git} refers to a git repository, - which will be \code{clone}d in a \reference{get-step} and \code{push}ed to - using a \reference{put-step}. Behind the scenes, Concourse will continuously - run \code{git fetch} to look for new commits that jobs may want to trigger - on. - - At its core, Concourse knows nothing about \code{git}. It comes with a - \link{\code{git} resource type}{https://github.com/concourse/git-resource} - out of the box, but you could just as easily bring your own into your - pipeline by setting the \reference{schema.pipeline.resource_types} field. - - To see what resource types are available, check out the \link{Resource Types - catalog}{https://resource-types.concourse-ci.org}! +\page-region{light}{Key features}{ + Concourse is designed to be \link{expressive, versatile, and + safe}{https://github.com/concourse/rfcs/blob/master/DESIGN_PRINCIPLES.md}, + remaining intuitive as the complexity of your project grows. + + \side-by-side{ + \inline-header{Configure as code} + + \codeblock{yaml}{{{ + resources: + - name: booklit + type: git + source: {uri: "https://github.com/vito/booklit"} + + jobs: + - name: unit + plan: + - get: booklit + trigger: true + - task: test + file: booklit/ci/test.yml + }}} + }{ + \inline-header{Visualize to verify} + + \include-template{basic-pipeline-svg} + } + + \side-by-side{ + A Concourse \reference{pipelines}{pipeline} is like a distributed, continuous + \code{Makefile}. + + Each \reference{jobs}{job} has a \reference{schema.job.plan}{build plan} + declaring the job's input \reference{resources}{resources} and what to run + with them when they change. + }{ + Your pipeline is then visualized in the web UI, taking only one click to get + from a failed job to seeing why it failed. + + The visualization provides a "gut check" feedback loop: if it \italic{looks} + wrong, it probably \italic{is} wrong. + } + + \inline-header{A more complicated example...} + + Jobs can depend on other jobs by configuring + \reference{schema.step.get-step.passed}{\code{passed}} constraints. The + resulting chain of jobs and resources is a dependency graph that continuously + pushes your project forward, from source code to production. + + \include-template{pipeline-image} + + \italic{This particular pipeline can be found in the \link{Booklit + repository}{https://github.com/vito/booklit/blob/8741a4ca3116dcf24c30fedfa78e4aadcaff178a/ci/pipeline.yml}.} + + \splash-example{CI under source control}{ + All configuration and administration is done using \reference{fly-cli}{the + \code{fly} CLI}. + + The \reference{fly-set-pipeline} command pushes the config up to Concourse. + Once it looks good, you can then check the file in to source control. This + makes it easy to recover your project if the Concourse server burns down. + }{ + \codeblock{sh}{{{ + $ fly -t ci set-pipeline -p booklit -c pipeline.yml + $ vim pipeline.yml + $ fly -t ci set-pipeline -p booklit -c pipeline.yml + $ git add pipeline.yml + $ git commit -m "initial pipeline"█ + }}} + } + + \splash-example{Reproducible, debuggable builds}{ + Everything runs in containers, ensuring a clean environment on every run. + + Each \reference{tasks}{task} specifies its own image, giving it full control + over its dependencies, rather than managing packages and state on your + workers. + + The \reference{fly-intercept} command will pop you right into one of your + build's containers, making it easy to troubleshoot flaky builds. + }{ + \codeblock{sh}{{{ + $ fly -t ci intercept -j booklit/unit -s unit + root@2c15ff11:/tmp/build/0df9eea0# ps + PID TTY TIME CMD + 171 pts/1 00:00:00 bash + 1876 pts/1 00:00:00 ps + root@2c15ff11:/tmp/build/0df9eea0# ls + depspath gopath + root@2c15ff11:/tmp/build/0df9eea0# █ + }}} + } + + \splash-example{Rapid local iteration}{ + The \reference{fly-execute} command lets you run a build with local changes. + + This build runs in exactly the same way as it would run in your pipeline, + without having to push broken commits until it works. + + When a build in the pipeline fails, you can run \reference{fly-execute} with + the \code{-j} flag to run a one-off build with the same inputs as the failed + build. You can then replace an input with your local changes with \code{-i} + to see if your fix is valid. + }{ + \codeblock{sh}{{{ + ~/booklit $ fly -t ci execute -c ci/test.yml + executing build 1 at http://localhost:8080/builds/1 + initializing + booklit: 4.74 MiB/s 0s + running gopath/src/github.com/vito/booklit/ci/test + fetching dependencies... + installing ginkgo... + running tests... + █ + }}} + } + + \inline-header{Bring your own integrations} + + \side-by-side{ + \codeblock{yaml}{{{ + resource_types: + - name: rubygem + type: registry-image + source: + repository: troykinsella/concourse-rubygems-resource + + resources: + - name: rspec-gem + type: rubygem + source: {gem: rspec} + + jobs: + - name: bundle + plan: + - get: rspec-gem + trigger: true + - # ... + }}} + }{ + Concourse does not have a complex plugin system. Instead, it focuses on a + single strong abstraction: \reference{resources}{resource}, which are + implemented by \reference{resource-types}{resource types}. + + The \reference{schema.pipeline.resources} field configures external artifacts + that your pipeline will monitor for changes, fetch from, and push to. + + For example, a resource with type \code{git} refers to a git repository, + which will be \code{clone}d in a \reference{get-step} and \code{push}ed to + using a \reference{put-step}. Behind the scenes, Concourse will continuously + run \code{git fetch} to look for new commits that jobs may want to trigger + on. + + At its core, Concourse knows nothing about \code{git}. It comes with a + \link{\code{git} resource type}{https://github.com/concourse/git-resource} + out of the box, but you could just as easily bring your own into your + pipeline by setting the \reference{schema.pipeline.resource_types} field. + + To see what resource types are available, check out the \link{Resource Types + catalog}{https://resource-types.concourse-ci.org}! + } } \split-sections From b63addf7387dd71d8289e2c3a86721d72c3b1dc7 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Fri, 19 Mar 2021 11:16:20 -0400 Subject: [PATCH 08/13] slightly smaller emojis in rfcs table prevents word-wrapping, and matches github emoji font size (...i think) Signed-off-by: Alex Suraci --- css/booklit.css | 4 ++-- less/index.less | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/css/booklit.css b/css/booklit.css index 1a63695e9..54967983d 100644 --- a/css/booklit.css +++ b/css/booklit.css @@ -967,11 +967,11 @@ body.index .page-content h2:first-child { display: inline-block; } .rfcs .reaction .emoji { - font-size: 16px; + font-size: 14px; } .rfcs .reaction .count { margin-left: 5px; - font-size: 16px; + font-size: 14px; } .rfcs .rfc-col { width: 150px; diff --git a/less/index.less b/less/index.less index d98352b1d..ea2f63214 100644 --- a/less/index.less +++ b/less/index.less @@ -362,12 +362,12 @@ body.index .page-content h2 { display: inline-block; .emoji { - font-size: 16px; + font-size: 14px; } .count { margin-left: 5px; - font-size: 16px; + font-size: 14px; } } From 8ee39ec226c5455ad330d1ac52e2259026042c44 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Fri, 19 Mar 2021 11:16:42 -0400 Subject: [PATCH 09/13] inject filler content when $GITHUB_TOKEN isn't set Signed-off-by: Alex Suraci --- go/docs/github.go | 43 +++++++++++++++++++++++++++++++------------ go/docs/rfcs.go | 24 +++++++++++++++++++++++- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/go/docs/github.go b/go/docs/github.go index 5cdc9f44c..9e90ad51b 100644 --- a/go/docs/github.go +++ b/go/docs/github.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "net/http" "os" "strings" @@ -97,7 +96,10 @@ func (p Plugin) Ghlabel(name booklit.Content, colorHex string) (booklit.Content, func (p *Plugin) DownloadLinks() (booklit.Content, error) { ctx := context.Background() - client := p.githubClient(ctx) + client, ok := p.githubClient(ctx) + if !ok { + return fillerDownloads, nil + } type GitHubRelease struct { Name string @@ -202,17 +204,34 @@ func (p *Plugin) DownloadLinks() (booklit.Content, error) { }, nil } -func (p *Plugin) githubClient(ctx context.Context) *githubv4.Client { - var tc *http.Client - var githubToken = os.Getenv("GITHUB_TOKEN") +func (p *Plugin) githubClient(ctx context.Context) (*githubv4.Client, bool) { + githubToken := os.Getenv("GITHUB_TOKEN") + if githubToken == "" { + return nil, false + } - if githubToken != "" { - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: githubToken}, - ) + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: githubToken}, + ) - tc = oauth2.NewClient(ctx, ts) - } + return githubv4.NewClient(oauth2.NewClient(ctx, ts)), true +} - return githubv4.NewClient(tc) +var fillerDownloads = booklit.Styled{ + Style: "download-links", + Block: true, + Content: booklit.Link{ + Target: "https://github.com/concourse/concourse/releases/latest", + Content: booklit.String("vX.X.X"), + }, + Partials: booklit.Partials{ + "Version": booklit.String("vX.X.X"), + "ReleaseNotesURL": booklit.String("https://github.com/concourse/concourse/releases/latest"), + "LinuxURL": booklit.String("https://example.com/#linux"), + "DarwinURL": booklit.String("https://example.com/#darwin"), + "WindowsURL": booklit.String("https://example.com/#windows"), + "FlyLinuxURL": booklit.String("https://example.com/#fly-linux"), + "FlyDarwinURL": booklit.String("https://example.com/#fly-darwin"), + "FlyWindowsURL": booklit.String("https://example.com/#fly-windows"), + }, } diff --git a/go/docs/rfcs.go b/go/docs/rfcs.go index 7a2359d89..6907e25a8 100644 --- a/go/docs/rfcs.go +++ b/go/docs/rfcs.go @@ -216,7 +216,10 @@ var reactionEmoji = map[string]string{ } func (p *Plugin) fetchRFCs(ctx context.Context) ([]PullRequest, error) { - client := p.githubClient(ctx) + client, ok := p.githubClient(ctx) + if !ok { + return fillerRFCs, nil + } type repoId struct { Name string @@ -425,3 +428,22 @@ func (by prsBy) Less(i, j int) bool { return a < b } + +var fillerRFCs = []PullRequest{ + { + URL: "https://example.com", + Number: 42, + Title: "Fake RFC", + + ProposalURL: "https://example.com/#proposal", + QuestionsURL: "https://example.com/#questions", + OpenQuestionCount: 3, + + Reactions: []GitHubReaction{ + { + Emoji: reactionEmoji["ROCKET"], + Count: 12, + }, + }, + }, +} From 53b98b2e4144e887ec3710ef401351017026daea Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Mon, 22 Mar 2021 21:26:16 -0400 Subject: [PATCH 10/13] skip pull requests that do not contain proposals Signed-off-by: Alex Suraci --- go/docs/rfcs.go | 110 +++++++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 47 deletions(-) diff --git a/go/docs/rfcs.go b/go/docs/rfcs.go index 6907e25a8..e45cfbec0 100644 --- a/go/docs/rfcs.go +++ b/go/docs/rfcs.go @@ -20,29 +20,33 @@ const resolutionMerge = "resolution/merge" const resolutionPostpone = "resolution/postpone" const resolutionClose = "resolution/close" -var cachedRFCs []PullRequest +var cachedRFCs []RFC var cacheOnce = new(sync.Once) -type PullRequest struct { - URL string - Number int - Title string - IsDraft bool - +type RFC struct { + URL string + Number int + Title string + IsDraft bool CreatedAt time.Time - ProposalURL string - QuestionsURL string - OpenQuestionCount int - Labels []GitHubLabel Reactions []GitHubReaction CommentCount int ReviewCount int + + Proposal Proposal } -func (pr PullRequest) ByTotalReactions() int { +type Proposal struct { + URL string + + QuestionsURL string + QuestionCount int +} + +func (pr RFC) ByTotalReactions() int { var s int for _, reaction := range pr.Reactions { s += reaction.Count @@ -51,25 +55,25 @@ func (pr PullRequest) ByTotalReactions() int { return s } -func (pr PullRequest) ByOpenQuestions() int { - return pr.OpenQuestionCount +func (pr RFC) ByOpenQuestions() int { + return pr.Proposal.QuestionCount } -func (pr PullRequest) ByCreatedAt() int { +func (pr RFC) ByCreatedAt() int { return int(pr.CreatedAt.Unix()) } -func (pr PullRequest) ByReviews() int { +func (pr RFC) ByReviews() int { return pr.ReviewCount } -func (pr PullRequest) Resolving() bool { +func (pr RFC) Resolving() bool { return pr.HasLabel(resolutionMerge) || pr.HasLabel(resolutionPostpone) || pr.HasLabel(resolutionClose) } -func (pr PullRequest) HasLabel(name string) bool { +func (pr RFC) HasLabel(name string) bool { for _, label := range pr.Labels { if label.Name == name { return true @@ -104,7 +108,7 @@ func (p *Plugin) RfcsTable(countStr string, sortBy string) (booklit.Content, err return nil, err } - rfcPRs := make([]PullRequest, len(cachedRFCs)) + rfcPRs := make([]RFC, len(cachedRFCs)) copy(rfcPRs, cachedRFCs) sorter := prsBy{sortBy, rfcPRs} @@ -126,7 +130,7 @@ func (p *Plugin) RfcsTable(countStr string, sortBy string) (booklit.Content, err }, nil } -func rfcRow(rfc PullRequest) booklit.Content { +func rfcRow(rfc RFC) booklit.Content { var status booklit.Content switch { case rfc.HasLabel(resolutionMerge): @@ -178,12 +182,12 @@ func rfcRow(rfc PullRequest) booklit.Content { } var questions booklit.Content = booklit.Empty - if rfc.OpenQuestionCount > 0 { + if rfc.Proposal.QuestionCount > 0 { questions = booklit.Styled{ Style: "rfc-questions", - Content: booklit.String(strconv.Itoa(rfc.OpenQuestionCount)), + Content: booklit.String(strconv.Itoa(rfc.Proposal.QuestionCount)), Partials: booklit.Partials{ - "QuestionsURL": booklit.String(rfc.QuestionsURL), + "QuestionsURL": booklit.String(rfc.Proposal.QuestionsURL), }, } } @@ -199,7 +203,7 @@ func rfcRow(rfc PullRequest) booklit.Content { "Status": status, "Reactions": reactions, "Questions": questions, - "ProposalURL": booklit.String(rfc.ProposalURL), + "ProposalURL": booklit.String(rfc.Proposal.URL), }, } } @@ -215,7 +219,7 @@ var reactionEmoji = map[string]string{ "EYES": "👀", } -func (p *Plugin) fetchRFCs(ctx context.Context) ([]PullRequest, error) { +func (p *Plugin) fetchRFCs(ctx context.Context) ([]RFC, error) { client, ok := p.githubClient(ctx) if !ok { return fillerRFCs, nil @@ -279,7 +283,7 @@ func (p *Plugin) fetchRFCs(ctx context.Context) ([]PullRequest, error) { return nil, fmt.Errorf("fetch rfcs: %w", err) } - pulls := []PullRequest{} + pulls := []RFC{} for _, node := range prsQuery.Repository.PullRequests.Nodes { if node.IsDraft { // don't put drafts on the website; they're not ready for public @@ -326,12 +330,16 @@ func (p *Plugin) fetchRFCs(ctx context.Context) ([]PullRequest, error) { proposalPath, ) - questionsURL, totalQuestions, err := countQuestions(proposalURL) + proposal, found, err := fetchProposal(proposalURL) if err != nil { return nil, fmt.Errorf("count open questions: %w", err) } - pulls = append(pulls, PullRequest{ + if !found { + continue + } + + pulls = append(pulls, RFC{ URL: node.Url, Number: node.Number, CreatedAt: node.CreatedAt, @@ -339,9 +347,7 @@ func (p *Plugin) fetchRFCs(ctx context.Context) ([]PullRequest, error) { IsDraft: node.IsDraft, Labels: node.Labels.Nodes, - ProposalURL: proposalURL, - QuestionsURL: questionsURL, - OpenQuestionCount: totalQuestions, + Proposal: proposal, Reactions: reactions, CommentCount: node.Comments.TotalCount, @@ -352,29 +358,37 @@ func (p *Plugin) fetchRFCs(ctx context.Context) ([]PullRequest, error) { return pulls, nil } -func countQuestions(proposalURL string) (string, int, error) { +func fetchProposal(proposalURL string) (Proposal, bool, error) { resp, err := http.Get(proposalURL) if err != nil { - return "", 0, fmt.Errorf("get %s: %w", proposalURL, err) + return Proposal{}, false, fmt.Errorf("get %s: %w", proposalURL, err) + } + + if resp.StatusCode == http.StatusNotFound { + resp.Body.Close() + return Proposal{}, false, nil } if resp.StatusCode != http.StatusOK { - return "", 0, fmt.Errorf("bad response for %s: %s", proposalURL, resp.Status) + resp.Body.Close() + return Proposal{}, false, fmt.Errorf("bad response for %s: %s", proposalURL, resp.Status) } doc, err := goquery.NewDocumentFromReader(resp.Body) if err != nil { - return "", 0, fmt.Errorf("query HTML: %w", err) + return Proposal{}, false, fmt.Errorf("query HTML: %w", err) + } + + proposal := Proposal{ + URL: proposalURL, } - var questionsURL string - var totalQuestions int doc.Find("#readme").Find("h1").Each(func(i int, sel *goquery.Selection) { if sel.Text() == "Open Questions" { anchor, found := sel.Find("a.anchor").Attr("href") if found { - questionsURL = proposalURL + anchor - logrus.Debugf("open questions: %s", questionsURL) + proposal.QuestionsURL = proposalURL + anchor + logrus.Debugf("open questions: %s", proposal.QuestionsURL) } questions := sel.NextUntil("h1") @@ -385,7 +399,7 @@ func countQuestions(proposalURL string) (string, int, error) { "question": sel.Text(), }).Debug("found question") - totalQuestions++ + proposal.QuestionCount++ } } @@ -393,16 +407,16 @@ func countQuestions(proposalURL string) (string, int, error) { questions.Find("li").Each(countQuestion) questions.Find("p").Each(countQuestion) - logrus.Infof("total questions: %d", totalQuestions) + logrus.Infof("total questions: %d", proposal.QuestionCount) } }) - return questionsURL, totalQuestions, nil + return proposal, true, nil } type prsBy struct { Method string - PRs []PullRequest + PRs []RFC } func (by prsBy) Len() int { return len(by.PRs) } @@ -429,15 +443,17 @@ func (by prsBy) Less(i, j int) bool { return a < b } -var fillerRFCs = []PullRequest{ +var fillerRFCs = []RFC{ { URL: "https://example.com", Number: 42, Title: "Fake RFC", - ProposalURL: "https://example.com/#proposal", - QuestionsURL: "https://example.com/#questions", - OpenQuestionCount: 3, + Proposal: Proposal{ + URL: "https://example.com/#proposal", + QuestionsURL: "https://example.com/#questions", + QuestionCount: 3, + }, Reactions: []GitHubReaction{ { From 7bbe95f52f9d0318ac5cd0c3d1143f248b453122 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Mon, 22 Mar 2021 21:33:37 -0400 Subject: [PATCH 11/13] fix latest release detection * rename local GitHubRelease type so it doesn't look exported * just don't use a pointer * skip draft releases, too * move 'no releases' check to the end (since pre and draft releases should count as 'no releases') * ...not that any of this really matters, since we've, like, released already Signed-off-by: Alex Suraci --- go/docs/github.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/go/docs/github.go b/go/docs/github.go index 9e90ad51b..bf2919643 100644 --- a/go/docs/github.go +++ b/go/docs/github.go @@ -101,7 +101,7 @@ func (p *Plugin) DownloadLinks() (booklit.Content, error) { return fillerDownloads, nil } - type GitHubRelease struct { + type release struct { Name string TagName *string IsDraft bool @@ -118,7 +118,7 @@ func (p *Plugin) DownloadLinks() (booklit.Content, error) { var releasesQuery struct { Repository struct { Releases struct { - Nodes []GitHubRelease + Nodes []release } `graphql:"releases(first: 10, orderBy: {field: CREATED_AT, direction: DESC})"` } `graphql:"repository(owner: \"concourse\", name: \"concourse\")"` } @@ -130,30 +130,32 @@ func (p *Plugin) DownloadLinks() (booklit.Content, error) { releases := releasesQuery.Repository.Releases.Nodes - var latestRelease *GitHubRelease + var latestRelease release var latestVersion semver.Version - if len(releases) == 0 { - return nil, errors.New("no releases found!") - } for _, release := range releases { - if release.IsPrerelease { + if release.IsPrerelease || release.IsDraft { continue } if release.TagName == nil { return nil, fmt.Errorf("no tag name for release %v", release) } + version, err := semver.ParseTolerant(*release.TagName) if err != nil { return nil, err } - if latestRelease == nil || version.GT(latestVersion) { - latestRelease = &release + if version.GT(latestVersion) { + latestRelease = release latestVersion = version } } + if latestRelease.Url == "" { + return nil, errors.New("no releases found!") + } + var linuxURL, darwinURL, windowsURL string var flyLinuxURL, flyDarwinURL, flyWindowsURL string for _, asset := range latestRelease.Assets.Nodes { From c3d41f93319ca5e8905722e0a273f34305977409 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Thu, 25 Mar 2021 17:09:49 -0400 Subject: [PATCH 12/13] add logs for missing $GITHUB_TOKEN, tweak filler Signed-off-by: Alex Suraci --- go/docs/github.go | 2 + go/docs/rfcs.go | 100 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/go/docs/github.go b/go/docs/github.go index bf2919643..901460218 100644 --- a/go/docs/github.go +++ b/go/docs/github.go @@ -10,6 +10,7 @@ import ( "github.com/blang/semver" "github.com/lucasb-eyer/go-colorful" "github.com/shurcooL/githubv4" + "github.com/sirupsen/logrus" "github.com/vito/booklit" "golang.org/x/oauth2" ) @@ -98,6 +99,7 @@ func (p *Plugin) DownloadLinks() (booklit.Content, error) { client, ok := p.githubClient(ctx) if !ok { + logrus.Warn("no $GITHUB_TOKEN set; using filler download links") return fillerDownloads, nil } diff --git a/go/docs/rfcs.go b/go/docs/rfcs.go index e45cfbec0..5e8232520 100644 --- a/go/docs/rfcs.go +++ b/go/docs/rfcs.go @@ -222,9 +222,12 @@ var reactionEmoji = map[string]string{ func (p *Plugin) fetchRFCs(ctx context.Context) ([]RFC, error) { client, ok := p.githubClient(ctx) if !ok { + logrus.Warn("no $GITHUB_TOKEN set; using filler RFCs") return fillerRFCs, nil } + logrus.Info("fetching RFCs") + type repoId struct { Name string Owner struct { @@ -407,7 +410,7 @@ func fetchProposal(proposalURL string) (Proposal, bool, error) { questions.Find("li").Each(countQuestion) questions.Find("p").Each(countQuestion) - logrus.Infof("total questions: %d", proposal.QuestionCount) + logrus.Debugf("total questions: %d", proposal.QuestionCount) } }) @@ -447,7 +450,7 @@ var fillerRFCs = []RFC{ { URL: "https://example.com", Number: 42, - Title: "Fake RFC", + Title: "Open RFC", Proposal: Proposal{ URL: "https://example.com/#proposal", @@ -458,8 +461,101 @@ var fillerRFCs = []RFC{ Reactions: []GitHubReaction{ { Emoji: reactionEmoji["ROCKET"], + Count: 5, + }, + }, + }, + { + URL: "https://example.com", + Number: 42, + Title: "Another Open RFC", + + Proposal: Proposal{ + URL: "https://example.com/#proposal", + QuestionsURL: "https://example.com/#questions", + QuestionCount: 3, + }, + + Reactions: []GitHubReaction{ + { + Emoji: reactionEmoji["THUMBS_UP"], Count: 12, }, }, }, + { + URL: "https://example.com", + Number: 43, + Title: "Merging RFC", + + Proposal: Proposal{ + URL: "https://example.com/#proposal", + QuestionsURL: "https://example.com/#questions", + QuestionCount: 0, + }, + + Labels: []GitHubLabel{ + { + Name: resolutionMerge, + Color: "#ff00ff", + }, + }, + + Reactions: []GitHubReaction{ + { + Emoji: reactionEmoji["ROCKET"], + Count: 10, + }, + }, + }, + { + URL: "https://example.com", + Number: 12, + Title: "Closing RFC", + + Proposal: Proposal{ + URL: "https://example.com/#proposal", + QuestionsURL: "https://example.com/#questions", + QuestionCount: 1, + }, + + Labels: []GitHubLabel{ + { + Name: resolutionClose, + Color: "#ff00ff", + }, + }, + + Reactions: []GitHubReaction{ + { + Emoji: reactionEmoji["THUMBS_DOWN"], + Count: 1, + }, + }, + }, + { + URL: "https://example.com", + Number: 2, + Title: "Postponing RFC", + + Proposal: Proposal{ + URL: "https://example.com/#proposal", + QuestionsURL: "https://example.com/#questions", + QuestionCount: 3, + }, + + Labels: []GitHubLabel{ + { + Name: resolutionPostpone, + Color: "#ff00ff", + }, + }, + + Reactions: []GitHubReaction{ + { + Emoji: reactionEmoji["CONFUSED"], + Count: 2, + }, + }, + }, } From 3c116ff4776d3714d81e813684b876bdfd5fc766 Mon Sep 17 00:00:00 2001 From: Alex Suraci Date: Thu, 25 Mar 2021 17:10:30 -0400 Subject: [PATCH 13/13] reword 'built in the open' section also update link for submitting feedback (section was renamed) Signed-off-by: Alex Suraci --- lit/index.lit | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lit/index.lit b/lit/index.lit index 942b094ce..194192d97 100644 --- a/lit/index.lit +++ b/lit/index.lit @@ -17,16 +17,16 @@ } \page-region{light}{Built in the open}{ - Concourse's \link{RFC process}{https://github.com/concourse/rfcs} welcomes - anyone to submit ideas and feedback to influence the project roadmap. + Concourse's \link{RFC process}{https://github.com/concourse/rfcs} and + \link{governance model}{https://github.com/concourse/governance} invite + anyone to become a contributor, developing the project roadmap by + collaborating in the open. \rfcs-table{10}{ByTotalReactions} - Help shape Concourse into a tool that meets your needs by \link{submitting - feedback}{https://github.com/concourse/rfcs/#providing-feedback-on-an-rfc}! - The above RFCs are ranked by their total GitHub reactions, with the aim being - to focus feedback on popular RFCs so that they reach resolution, making room - for more on the table. + Help shape Concourse into a tool that fits your needs by \link{submitting + feedback}{https://github.com/concourse/rfcs/#reviewing-rfcs} on the RFCs + listed above! } \page-region{light}{Key features}{