Skip to content

Commit

Permalink
Merge pull request #1 from pnmcosta/blog
Browse files Browse the repository at this point in the history
Static blog posts from markdown and templates
  • Loading branch information
pnmcosta authored Apr 4, 2024
2 parents ca7b9fb + 7463531 commit 6893e29
Show file tree
Hide file tree
Showing 22 changed files with 563 additions and 121 deletions.
31 changes: 29 additions & 2 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ jobs:
steps:
- uses: actions/checkout@v4

- id: vars
run: |
echo "sha_short=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_OUTPUT"
- uses: actions/setup-node@v4
with:
check-latest: true
Expand All @@ -31,17 +35,40 @@ jobs:
cache-dependency-path: |
package-lock.json
- run: npx tailwindcss -i ./src/input.css -o ./src/output.css
- uses: actions/setup-go@v5
with:
check-latest: true
cache-dependency-path: go.sum
go-version-file: go.mod
cache: true

- run: make build-deps

- run: go mod download

- run: go mod verify

- run: make build

- uses: actions/upload-artifact@v4
if: github.ref != 'refs/heads/main'
with:
if-no-files-found: error
name: 'public-${{ steps.vars.outputs.sha_short }}'
path: ./public
retention-days: 1

- name: Upload artifact
if: github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v3
with:
path: ./src
path: ./public

# Deploy job
deploy:
# Add a dependency to the build job
needs: build
if: github.ref == 'refs/heads/main'

# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules
output.css
/public

*_templ.go
42 changes: 42 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
GOOS=linux
GOARCH=amd64
CGO_ENABLED=0

install: test-deps build-deps ## install from the current working tree
@npm i
@go install -v .

clean: ## remove build artifacts from the working tree
@rm -rf public

test-deps: ## install test dependencies
@go install github.com/bokwoon95/wgo@latest
@go get -v ./...
@go mod tidy

build-deps: ## install build dependencies
@go install github.com/a-h/templ/cmd/templ@latest

deps: build-deps test-deps ## install build and test dependencies

assets: clean
@cp -a assets/. ./public/

generate: ## Go and Templ generate
@npx tailwindcss -i style.css -o ./public/style.css
@templ generate

run: ## run and watch
@wgo -file=.go -file=.templ -file=.css -file=.md -xfile=_templ.go make generate :: go run main.go --dev

build: assets generate ## build public static
@go run main.go

update: ## update dependencies
@go get -u
@go mod tidy
@make install
@go mod tidy

help: ## Show this help.
@grep -F -h "##" $(MAKEFILE_LIST) | grep -F -v grep | sed -e 's/\\$$//' | sed -e 's/:.\+##/\n\t/'
Binary file added assets/clouds_1.webp
Binary file not shown.
Binary file added assets/clouds_2.webp
Binary file not shown.
Binary file added assets/clouds_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/favicon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
15 changes: 15 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module github.com/pnmcosta/csta.dev

go 1.22

require (
github.com/a-h/templ v0.2.648
github.com/gosimple/slug v1.14.0
github.com/yuin/goldmark v1.7.1
github.com/yuin/goldmark-meta v1.1.0
)

require (
github.com/gosimple/unidecode v1.0.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
github.com/a-h/templ v0.2.648 h1:A1ggHGIE7AONOHrFaDTM8SrqgqHL6fWgWCijQ21Zy9I=
github.com/a-h/templ v0.2.648/go.mod h1:SA7mtYwVEajbIXFRh3vKdYm/4FYyLQAtPH1+KxzGPA8=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/gosimple/slug v1.14.0 h1:RtTL/71mJNDfpUbCOmnf/XFkzKRtD6wL6Uy+3akm4Es=
github.com/gosimple/slug v1.14.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
121 changes: 121 additions & 0 deletions internal/posts/posts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package posts

import (
"bytes"
"io/fs"
"log"
"os"
"path/filepath"
"sort"
"strings"
"time"

"github.com/yuin/goldmark"
meta "github.com/yuin/goldmark-meta"
"github.com/yuin/goldmark/parser"
)

type Post struct {
Title string
Summary string
Tags []string
Date time.Time
Content []byte
}

type walker struct {
posts []Post
}

func ParsePosts() []Post {
w := walker{}
filepath.WalkDir("./posts", w.walk)
// stort by latest
sort.Slice(w.posts, func(i, j int) bool { return w.posts[i].Date.After(w.posts[j].Date) })
return w.posts
}

func (w *walker) walk(s string, d fs.DirEntry, err error) error {
if err != nil {
return err
}

if !d.IsDir() {
if !strings.HasSuffix(s, ".md") {
return nil
}

content, err := os.ReadFile(s)
if err != nil {
log.Printf("%s: could not read\n", s)
return err
}

markdown := goldmark.New(
goldmark.WithExtensions(
meta.Meta,
),
)

var buf bytes.Buffer
context := parser.NewContext()
if err := markdown.Convert(content, &buf, parser.WithContext(context)); err != nil {
log.Printf("%s: invalid markdown\n", s)
return err
}

parsed := buf.Bytes()
if len(parsed) == 0 {
log.Printf("%s: empty\n", s)
return nil
}

post := Post{
Content: parsed,
}

metaData := meta.Get(context)
if value, ok := metaData["Title"].(string); ok && len(value) > 0 {
post.Title = value
} else {
log.Printf("%s: title required\n", s)
return nil
}

if value, ok := metaData["Summary"].(string); ok && len(value) > 0 {
post.Summary = value
}

if value, ok := metaData["Tags"].([]interface{}); ok && len(value) > 0 {
var strValue []string = make([]string, len(value))
for i, d := range value {
if v, ok := d.(string); ok {
strValue[i] = v
}
}
post.Tags = strValue
}

if value, ok := metaData["Date"].(string); ok && len(value) > 0 {
if dt, err := time.Parse("2006/01/02", value); err == nil {
post.Date = dt
}
}

if post.Date.IsZero() {
fi, err := os.Stat(s)
if err != nil {
log.Printf("%s: err stating: %s\n", s, err)
}
post.Date = fi.ModTime().UTC()
}

if post.Date.IsZero() {
post.Date = time.Now().UTC()
}

log.Printf("%s: post parsed\n", s)
w.posts = append(w.posts, post)
}
return nil
}
50 changes: 50 additions & 0 deletions internal/templates/index/view.templ
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package index

import "github.com/pnmcosta/csta.dev/internal/posts"
import "github.com/pnmcosta/csta.dev/internal/templates/layout"
import "path"
import "github.com/gosimple/slug"

templ View(posts []posts.Post) {
@layout.Base() {
<p>
Olá, I'm <a href="https://www.linkedin.com/in/pnmcosta/" target="_blank">Pedro Maia Costa</a> a product
software
engineer based in Braga, Portugal.
</p>
<p>
Currently working on/with <a href="https://www.socialclique.app/" target="_blank">SocialClique</a> developing
<a href="https://shopify.dev/building-for-the-app-store" target="_blank">Shopify Apps</a>.
</p>
<p>
Having previously worked at/with <a href="https://www.juni.co/" target="_blank">Juni</a>, <a
href="https://www.thesimgrid.com/"
target="_blank"
>The SimGrid</a>,
<a
href="http://www.jpmorganchase.com"
target="_blank"
>
JPMorgan
Chase & Co.
</a> and <a href="https://allhuman.com/" target="_blank">All human</a>.
</p>
if len(posts) > 0 {
<p>I've also recently started sharing my experience and know-how on these blog articles:</p>
<p>
for i, post := range posts {
if i >0 {
;
}
<a href={ templ.SafeURL(path.Join(post.Date.Format("2006/01/02"), slug.Make(post.Title), "/")) }>{ post.Title } ({ post.Date.Format("2006/01/02") })</a>
}
</p>
}
<p>
Drop me a comment to <a
href="mailto:[email protected]"
>&#112;&#101;&#100;&#114;&#111;&#064;&#099;&#115;&#116;&#097;&#046;&#100;&#101;&#118;</a>
</p>
<p>All the best</p>
}
}
66 changes: 66 additions & 0 deletions internal/templates/layout/base.templ
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package layout

templ Base() {
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link href="/style.css" rel="stylesheet"/>
<title>csta.dev</title>
<link rel="icon" type="image/png" href="/favicon.png"/>
<meta name="description" content="I'm Pedro Maia Costa, a product software engineer based in Braga, Portugal"/>
<!-- Twitter Card data -->
<meta name="twitter:card" value="I'm Pedro Maia Costa, a product software engineer based in Braga, Portugal"/>
<!-- Open Graph data -->
<meta property="og:title" content="I'm Pedro Maia Costa, a product software engineer"/>
<meta property="og:type" content="article"/>
<meta property="og:url" content="https://csta.dev/"/>
<meta property="og:image" content="/ogimg.png"/>
<meta
property="og:description"
content="I'm Pedro Maia Costa, product software engineer based in Braga, Portugal"
/>
</head>
<body class="bg-black font-mono">
<div class="container md:w-2/3 lg:w-3/5 xl:w-1/2 space-y-3 px-4 sm:px-6 lg:px-8 mt-4 sm:mt-6 lg:mt-8 text-lg sm:text-xl lg:text-2xl text-stone-300">
<h1 class="text-5xl sm:text-6xl lg:text-8xl pb-4 sm:pb-6 lg:pb-8 font-bold text-nowrap" id="title">
<a href="/">
<span class="visible">csta.dev</span>
<span>&lt;csta&#x2F;&gt;</span>
<span>&amp;csta{ }</span>
<span>#csta</span>
<span>()=>csta</span>
</a>
</h1>
{ children... }
</div>
<div class="clouds">
<div class="clouds-1"></div>
<div class="clouds-2"></div>
<div class="clouds-3"></div>
</div>
<script>
const titleEls = document.querySelectorAll("#title > a > span")
const visibleEl = document.querySelector("#title > a > span.visible")
let visible = Array.prototype.indexOf.call(visibleEl.parentNode.children, visibleEl);
setInterval(() => {
if (visible < titleEls.length - 1) {
visible++;
} else {
visible = 0
}

for (let i = 0; i < titleEls.length; i++) {
if (visible === i) {
titleEls[i].classList.add("visible")
document.title = titleEls[i].textContent
} else {
titleEls[i].classList.remove("visible")
}
}
}, 10000) // every 10s
</script>
</body>
</html>
}
11 changes: 11 additions & 0 deletions internal/templates/post/unsafe.templ
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package post

import "context"
import "io"

func Unsafe(html string) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
_, err = io.WriteString(w, html)
return
})
}
Loading

0 comments on commit 6893e29

Please sign in to comment.