diff --git a/Dockerfile b/Dockerfile index 6cab4c7..df6c2b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,11 @@ +FROM golang:1.14-alpine as go-builder + +ENV GO111MODULE=on +WORKDIR /app + +ADD social-image-gen . +RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-w -s" -o social-image-gen main.go + FROM debian:buster AS builder RUN apt-get -qq update \ @@ -11,8 +19,13 @@ RUN curl -sL -o /tmp/hugo.deb \ dpkg -i /tmp/hugo.deb && \ rm /tmp/hugo.deb +COPY --from=go-builder /app/social-image-gen /social-image-gen + WORKDIR /app ADD . . + +ADD ./social-image-gen/fonts/ /fonts +RUN /social-image-gen --mode posts RUN hugo -d /usr/share/nginx/html/ FROM nginx:latest diff --git a/config.toml b/config.toml index 30ea62b..91b0786 100644 --- a/config.toml +++ b/config.toml @@ -7,6 +7,8 @@ enableGitInfo = true [permalinks] posts = "/:title/" - [params] gitUrl = "https://git.cluster.fun/AverageMarcus/til/commit/" + +[taxonomies] + series = "series" diff --git a/content/posts/tekton-multi-arch-builds.md b/content/posts/tekton-multi-arch-builds.md index ed42ec4..f3f2192 100644 --- a/content/posts/tekton-multi-arch-builds.md +++ b/content/posts/tekton-multi-arch-builds.md @@ -5,6 +5,8 @@ draft: false tags: - tekton - docker +images: +- /images/tekton-multi-arch-builds.gif --- Using Buildkit to build multi-arch compatible images without Docker daemon: diff --git a/social-image-gen/fonts/Inter-SemiBold.ttf b/social-image-gen/fonts/Inter-SemiBold.ttf new file mode 100644 index 0000000..556e972 Binary files /dev/null and b/social-image-gen/fonts/Inter-SemiBold.ttf differ diff --git a/social-image-gen/fonts/Noto.ttf b/social-image-gen/fonts/Noto.ttf new file mode 100644 index 0000000..0d61749 Binary files /dev/null and b/social-image-gen/fonts/Noto.ttf differ diff --git a/social-image-gen/go.mod b/social-image-gen/go.mod new file mode 100644 index 0000000..fd3dae1 --- /dev/null +++ b/social-image-gen/go.mod @@ -0,0 +1,13 @@ +module social-image-gen + +go 1.13 + +require ( + github.com/ericaro/frontmatter v0.0.0-20200210094738-46863cd917e2 + github.com/fogleman/gg v1.3.0 + github.com/gofiber/fiber v1.14.6 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/pkg/errors v0.9.1 + golang.org/x/image v0.0.0-20200801110659-972c09e46d76 // indirect + gopkg.in/yaml.v2 v2.3.0 // indirect +) diff --git a/social-image-gen/go.sum b/social-image-gen/go.sum new file mode 100644 index 0000000..f287a5d --- /dev/null +++ b/social-image-gen/go.sum @@ -0,0 +1,42 @@ +github.com/andybalholm/brotli v1.0.0 h1:7UCwP93aiSfvWpapti8g88vVVGp2qqtGyePsSuDafo4= +github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= +github.com/ericaro/frontmatter v0.0.0-20200210094738-46863cd917e2 h1:+dzAHE3uQKGtPxobmh43c0n8V8A7X70zqUUDzL7ePVA= +github.com/ericaro/frontmatter v0.0.0-20200210094738-46863cd917e2/go.mod h1:Xy0DdToffIGb/ZMCwM5zFgIQOrVh0cKvNABpKRJYo1w= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/gofiber/fiber v1.14.6 h1:QRUPvPmr8ijQuGo1MgupHBn8E+wW0IKqiOvIZPtV70o= +github.com/gofiber/fiber v1.14.6/go.mod h1:Yw2ekF1YDPreO9V6TMYjynu94xRxZBdaa8X5HhHsjCM= +github.com/gofiber/utils v0.0.10 h1:3Mr7X7JdCUo7CWf/i5sajSaDmArEDtti8bM1JUVso2U= +github.com/gofiber/utils v0.0.10/go.mod h1:9J5aHFUIjq0XfknT4+hdSMG6/jzfaAgCu4HEbWDeBlo= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= +github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg= +github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.16.0 h1:9zAqOYLl8Tuy3E5R6ckzGDJ1g8+pw15oQp2iL9Jl6gQ= +github.com/valyala/fasthttp v1.16.0/go.mod h1:YOKImeEosDdBPnxc0gy7INqi3m1zK6A+xl6TwOBhHCA= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/image v0.0.0-20200801110659-972c09e46d76 h1:U7GPaoQyQmX+CBRWXKrvRzWTbd+slqeSh8uARsIyhAw= +golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/social-image-gen/main.go b/social-image-gen/main.go new file mode 100644 index 0000000..a53c49e --- /dev/null +++ b/social-image-gen/main.go @@ -0,0 +1,137 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "image" + "image/color" + "image/color/palette" + "image/draw" + "image/gif" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/ericaro/frontmatter" + "github.com/fogleman/gg" +) + +type Post struct { + Title string `yaml:"title"` + Tags []string `yaml:"tags"` +} + +func main() { + if err := filepath.Walk("./content/posts/", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + body, err := ioutil.ReadFile(path) + if err != nil { + return err + } + + var out Post + frontmatter.Unmarshal(body, &out) + + img, err := run(out.Title) + if err != nil { + return err + } + + filename := "./static/images/" + strings.ReplaceAll(info.Name(), ".md", ".gif") + fmt.Printf("Saving %s", filename) + ioutil.WriteFile(filename, img, 0755) + + return nil + }); err != nil { + panic(err) + } +} + +const ( + width = 1200 + height = 628 +) + +var ( + monoFont = filepath.Join("/fonts", "Noto.ttf") + headerFont = filepath.Join("/fonts", "Inter-SemiBold.ttf") + fontColor = color.RGBA{169, 169, 179, 0xff} +) + +func run(title string) ([]byte, error) { + dc := gg.NewContext(width, height) + + // Background + dc.SetHexColor("#292a2d") + dc.DrawRectangle(0, 0, width, height) + dc.Fill() + + // Header + dc.SetHexColor("#252627") + dc.DrawRectangle(0, 0, width, 100) + dc.Fill() + + if err := dc.LoadFontFace(monoFont, 45); err != nil { + return nil, err + } + dc.SetColor(fontColor) + dc.DrawString("#til", 45, 60) + + dc.LoadFontFace(monoFont, 20) + dc.SetColor(fontColor) + dc.DrawString("https://til.marcusnoble.co.uk", 895, 623) + + dc.LoadFontFace(headerFont, 80) + textRightMargin := 50.0 + textTopMargin := 130.0 + x := textRightMargin + y := textTopMargin + maxWidth := float64(dc.Width()) - textRightMargin - textRightMargin + dc.SetColor(color.White) + dc.DrawStringWrapped(title, x+1, y+1, 0, 0, maxWidth, 1.5, gg.AlignLeft) + dc.SetColor(fontColor) + dc.DrawStringWrapped(title, x, y, 0, 0, maxWidth, 1.5, gg.AlignLeft) + + frame1 := dc.Image() + + dc = gg.NewContextForImage(frame1) + + dc.LoadFontFace(headerFont, 80) + title += " |" + textRightMargin = 50.0 + textTopMargin = 130.0 + x = textRightMargin + y = textTopMargin + maxWidth = float64(dc.Width()) - textRightMargin - textRightMargin + dc.SetColor(color.White) + dc.DrawStringWrapped(title, x+1, y+1, 0, 0, maxWidth, 1.5, gg.AlignLeft) + dc.SetColor(fontColor) + dc.DrawStringWrapped(title, x, y, 0, 0, maxWidth, 1.5, gg.AlignLeft) + + frame2 := dc.Image() + + palettedImage1 := image.NewPaletted(frame1.Bounds(), palette.Plan9) + draw.Over.Draw(palettedImage1, frame1.Bounds(), frame1, image.ZP) + palettedImage2 := image.NewPaletted(frame2.Bounds(), palette.Plan9) + draw.Over.Draw(palettedImage2, frame2.Bounds(), frame2, image.ZP) + + var output bytes.Buffer + gif.EncodeAll(bufio.NewWriter(&output), &gif.GIF{ + Image: []*image.Paletted{ + palettedImage1, + palettedImage2, + }, + Delay: []int{50, 50}, + }) + + return output.Bytes(), nil +}