Skip to content

Commit

Permalink
all: fix for Deno Deploy
Browse files Browse the repository at this point in the history
Remove redirects and headers, which is a little unfortunate.

#111
  • Loading branch information
croaky authored May 3, 2022
1 parent a9c0ba0 commit 98544ff
Show file tree
Hide file tree
Showing 14 changed files with 44 additions and 127 deletions.
29 changes: 5 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# blog

Short articles about software at <https://dancroak.com>.
Static site generator designed to be deployed to [Netlify](https://netlify.com).
Static site generator deployed to [Deno Deploy](https://deno.com/deploy).

## Setup

Expand Down Expand Up @@ -32,7 +32,7 @@ It expects a file layout like this:
│ └── example.png
├── theme
│ ├── public
│ │ └── _headers
│ │ └── favicon.ico
│ ├── article.html
│ └── index.html
└── config.json
Expand Down Expand Up @@ -93,7 +93,7 @@ Add images to the `images` directory.
Refer to them in articles:

```md
![alt text](images/example.png)
![alt text](/images/example.png)
```

Configure articles in `config.json`:
Expand All @@ -110,20 +110,6 @@ Configure articles in `config.json`:
"react"
]
},
{
"description": "Redirect old URL slugs.",
"id": "article-with-redirects",
"last_updated": "2018-02-01",
"published": "2018-02-01",
"tags": [
"go"
]
"redirects": [
"/article-original-name",
"/article-renamed-again",
"/this-feature-works-only-on-netlify",
]
},
{
"canonical": "https://seo.example.com/avoid-duplicate-content-penalty",
"description": "Canonical article is on a separate site.",
Expand All @@ -146,14 +132,9 @@ The `last_updated` date can be in the future.
A [GitHub Action is scheduled daily](https://dancroak.com/schedule-netlify-builds-with-github-actions)
to auto-publish to Netlify.

The `redirects` are converted into a
[Netlify _redirects file](https://docs.netlify.com/routing/redirects/).

## Modify theme

All `theme/public` files are copied to `public`.
`theme/public/_headers` are
[Netlify Headers](https://www.netlify.com/docs/headers-and-basic-auth/).

The `theme/*.html` files
are parsed as [Go templates](https://gowebexamples.com/templates/).
Expand Down Expand Up @@ -193,7 +174,7 @@ The `theme/index.html` template accepts a data structure like this:

## Publish

Configure [Netlify](https://netlify.com):
Configure [Deno Deploy](https://deno.com/deploy):

* Repository: `https://github.com/croaky/blog`
* Production branch: `main`
Expand All @@ -202,4 +183,4 @@ Configure [Netlify](https://netlify.com):

To publish articles, commit and push to the GitHub repo.

View deploy logs in the Netlify web interface.
View deploy logs in the Deno Deploy web interface.
2 changes: 1 addition & 1 deletion articles/align-markdown-tables-in-vim.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ The `<Bslash>` key is the `|` key,
a mnemonic for the table's `|`s.
Here's how it looks:

![Visual select, leader, backslash](images/align-markdown-table-in-vim.gif)
![Visual select, leader, backslash](/images/align-markdown-table-in-vim.gif)
2 changes: 1 addition & 1 deletion articles/heroku-slack-aws-lambda.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
When my production app processes change state on Heroku,
I want to be notified in Slack:

![Screenshot of Slack notification](images/heroku-to-slack.png)
![Screenshot of Slack notification](/images/heroku-to-slack.png)

Other examples:

Expand Down
2 changes: 1 addition & 1 deletion articles/north-star-metric.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ people are getting blocked.

Here's Hound's activation funnel in Mixpanel:

![Hound's Mixpanel funnel](images/hound-funnel.png)
![Hound's Mixpanel funnel](/images/hound-funnel.png)

While both steps could be improved,
it seems more likely that
Expand Down
2 changes: 1 addition & 1 deletion articles/postgres-visualize-slow-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ The output is an interactive visualization that makes it
easy to identify which parts of the query are
slowest, largest, and costliest.

![EXPLAIN visualizer](images/postgres-explain-visualizer.png)
![EXPLAIN visualizer](/images/postgres-explain-visualizer.png)
2 changes: 1 addition & 1 deletion articles/pull-requests-and-kanban.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ I've mostly used [Trello](https://trello.com)
but lately have been falling in love with [Notion](https://notion.so).
Here's an example board:

![Kanban board](images/kanban-board.png)
![Kanban board](/images/kanban-board.png)

We add new work to the board as a card in the "Next Up" column.
It might represent a feature, bug, or chore.
Expand Down
16 changes: 8 additions & 8 deletions articles/retention-curves-mixpanel-google-sheets.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Choose "Day", "Week", or "Month" based on the expected frequency of that event.

[ns]: north-star-metric

![](images/retention-report.png)
![](/images/retention-report.png)

Click the "CSV" download button in the bottom right.
Go to Google Sheets.
Expand All @@ -45,7 +45,7 @@ Click "File > Import > Upload".
Select the file.
Select "Replace spreadsheet".

![](images/retention-curve-table.png)
![](/images/retention-curve-table.png)

Select all the data.
Click "Insert > Chart".
Expand All @@ -54,19 +54,19 @@ Click "Chart types",
and the curvy "Line" chart type (for style).
Click "Customization" to edit the title.

![](images/retention-curve-ready.png)
![](/images/retention-curve-ready.png)

Select each time series,
and pick a shade of color in the same column,
making the oldest time series the lightest shade.

![](images/retention-curve-series.png)
![](/images/retention-curve-series.png)

The goal of this shading to create a relationship between the series.
Darker is more recent ("fresh paint")
and lighter is older ("faded").

![](images/retention-curve-final.png)
![](/images/retention-curve-final.png)

## Analyzing FormKeep's curves

Expand Down Expand Up @@ -99,7 +99,7 @@ Let's compare the retention curves of another real product,
[Upcase](https://thoughtbot.com/upcase/join),
an online training community for programmers.

![](images/upcase-retention-curve.png)
![](/images/upcase-retention-curve.png)

The initial drop-off in usage is shallower than FormKeep's.
Where FormKeep's cohorts are closer to 10% active in weeks one and two,
Expand Down Expand Up @@ -130,7 +130,7 @@ Click "+ Segment" in Mixpanel
and choose an important property to segment by,
such as user acquisition channel:

![](images/mixpanel-segment.png)
![](images/mixpanel-utm-source-segment.png)
![](/images/mixpanel-segment.png)
![](/images/mixpanel-utm-source-segment.png)

Each segment can have its own retention curve.
4 changes: 2 additions & 2 deletions articles/search-projects-in-vim.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ nnoremap <Leader>k :grep! "\b<C-R><C-W>\b"<CR>:cw<CR>
It looks like this when `<Leader>k`
is typed with the cursor over `SubscriptionMailer`:

![''](images/quickfix-under-cursor.png)
![''](/images/quickfix-under-cursor.png)

Cursor over each search result, hit `Enter`, and the file will be opened.

Expand Down Expand Up @@ -78,4 +78,4 @@ Standard `ag` arguments may be passed in at this point:

Hitting `Enter` results in:

![''](images/quickfix-custom-command.png)
![''](/images/quickfix-custom-command.png)
6 changes: 3 additions & 3 deletions articles/spell-check-in-vim.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Vim will highlight misspelled words.
Move to the next misspelled word with `]s`
or move backwards with `[s`:

![](images/project-specific-spelling.gif)
![](/images/project-specific-spelling.gif)

## Add words to the dictionary

Expand All @@ -49,10 +49,10 @@ the spell file is located at
When the cursor is over a misspelled word,
get suggestions for the word with `z=`:

![](images/word-suggestions.gif)
![](/images/word-suggestions.gif)

## Tab completion

The words in the spell file will show up in tab completion:

![](images/tab-completion.gif)
![](/images/tab-completion.gif)
5 changes: 1 addition & 4 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,7 @@
"description": "Heroku routing errors don't reach error-tracking code in app processes, have to track with a logging service",
"id": "log-alert-heroku-routing-errors",
"last_updated": "2015-04-21",
"redirects": [
"/post/send-your-apps-heroku-routing-errors-to-slack-with-papertrail-alerts/",
"/heroku-routing-errors-slack-papertrail"
],
"redirects": ["/heroku-routing-errors-slack-papertrail"],
"tags": ["Infra"]
},
{
Expand Down
92 changes: 18 additions & 74 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ type Article struct {
Description string `json:"description"`
ID string `json:"id"`
LastUpdated string `json:"last_updated"`
Redirects []string `json:"redirects,omitempty"`
Tags []string `json:"tags"`

Body template.HTML `json:"-"`
Expand All @@ -110,7 +109,7 @@ type Article struct {
}

func add(id string) {
articles, _, _ := load()
articles, _ := load()

noDashes := strings.Replace(id, "-", " ", -1)
noUnderscores := strings.Replace(noDashes, "_", " ", -1)
Expand All @@ -131,65 +130,25 @@ func add(id string) {
}

func serve(addr string) {
http.HandleFunc("/", handler)
check(http.ListenAndServe(addr, nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
// log every request except favicon.ico noise
if r.URL.Path != "/favicon.ico" {
fmt.Println(r.Method + " " + r.URL.Path)
}

// don't rebuild for images or favicon
if !strings.HasPrefix(r.URL.Path, "/images/") && !strings.HasPrefix(r.URL.Path, "/favicon.ico") {
for k, v := range build() {
if r.URL.Path == k {
http.Redirect(w, r, v, 302)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// log every request except favicon.ico noise
if r.URL.Path != "/favicon.ico" {
fmt.Println(r.Method + " " + r.URL.Path)
}
}

// convert URLs like /intro to /intro/index.html for http.FileServer
if r.URL.Path != "/" && path.Ext(r.URL.Path) == "" {
r.URL.Path = r.URL.Path + "/index.html"
}

// use same headers as production, if present
for k, v := range headers() {
w.Header().Set(k, v)
}

fs := http.FileServer(http.Dir(wd + "/public"))
fs.ServeHTTP(w, r)
}

func headers() map[string]string {
result := make(map[string]string)

dat, err := ioutil.ReadFile(wd + "/theme/public/_headers")
if err != nil {
return result
}

for i, line := range strings.Split(string(dat), "\n") {
if i == 0 { // ignore first line ("/*\n")
continue
// don't rebuild for images or favicon
if !strings.HasPrefix(r.URL.Path, "/images/") && !strings.HasPrefix(r.URL.Path, "/favicon.ico") {
build()
}
parts := strings.SplitN(line, ": ", 2)
if len(parts) != 2 {
continue
}
k := strings.TrimSpace(parts[0])
v := strings.TrimSpace(parts[1])
result[k] = v
}

return result
fs := http.FileServer(http.Dir(wd + "/public"))
fs.ServeHTTP(w, r)
})
check(http.ListenAndServe(addr, nil))
}

func build() map[string]string {
articles, tags, redirectMap := load()
func build() {
articles, tags := load()

// public directories
dir, err := ioutil.ReadDir(wd + "/public")
Expand All @@ -215,7 +174,7 @@ func build() map[string]string {
articlePage := template.Must(template.ParseFiles(wd + "/theme/article.html"))
for _, a := range articles {
check(os.Mkdir(wd+"/public/"+a.ID, os.ModePerm))
f, err := os.Create("public/" + a.ID + "/index.html")
f, err := os.Create(wd + "/public/" + a.ID + "/index.html")
check(err)
articleData := struct {
Article Article
Expand All @@ -229,28 +188,18 @@ func build() map[string]string {
cmd := exec.Command("cp", "-a", wd+"/images/.", wd+"/public/images")
cmd.Run()

// headers, favicon.ico, and additional files from theme
// favicon.ico, and additional files from theme
cmd = exec.Command("cp", "-a", wd+"/theme/public/.", wd+"/public")
cmd.Run()

// redirects
redirects := "/archive/* /\n"
for k, v := range redirectMap {
redirects = redirects + k + " " + v + "\n"
}
check(ioutil.WriteFile(wd+"/public/_redirects", []byte(redirects), 0644))

return redirectMap
}

func load() ([]Article, []string, map[string]string) {
func load() ([]Article, []string) {
config, err := ioutil.ReadFile(wd + "/config.json")
check(err)
var articles []Article
check(json.Unmarshal(config, &articles))

tags := make([]string, 0)
redirectMap := make(map[string]string)

for i, a := range articles {
t, err := time.Parse("2006-01-02", a.LastUpdated)
Expand All @@ -277,7 +226,6 @@ func load() ([]Article, []string, map[string]string) {
LastUpdated: a.LastUpdated,
LastUpdatedIn: t.Format("2006"),
LastUpdatedOn: t.Format("January 2, 2006"),
Redirects: a.Redirects,
Tags: a.Tags,
Title: title,
}
Expand All @@ -286,10 +234,6 @@ func load() ([]Article, []string, map[string]string) {
for _, t := range a.Tags {
tags = append(tags, t)
}

for _, r := range a.Redirects {
redirectMap[r] = "/" + a.ID
}
}

sort.Strings(tags)
Expand All @@ -302,7 +246,7 @@ func load() ([]Article, []string, map[string]string) {
prev = t
}

return articles, uniqTags, redirectMap
return articles, uniqTags
}

/*
Expand Down
2 changes: 1 addition & 1 deletion theme/article.html
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@
<div class="container">
<nav>
<a class="logo" href="/">
<img alt="Dan's profile photo" src="logo.png" width="64px" height="64px" />
<img alt="Dan's profile photo" src="/logo.png" width="64px" height="64px" />
</a>
</nav>

Expand Down
Loading

0 comments on commit 98544ff

Please sign in to comment.