Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tooling): add releases #1241

Merged
merged 2 commits into from
Nov 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ static/_gen
data/toc.json
themes
layouts
api/dist
api/worker
125 changes: 44 additions & 81 deletions api/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
const Router = require('./router')
const Router = require('./src/router')
const dlv = require('dlv')
const merge = require('merge-options')
const nanoid = require('nanoid/non-secure')
const get = require('./src/get')
const map = require('p-map')

const cacheBust = nanoid.nanoid()
/**
* Example of how router can be used in an application
* */
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event))
})

async function handleRequest(event) {
const r = new Router()
// Replace with the appropriate paths and handlers
r.get('.*/cov', () => cov(event))
r.get('.*/github', () => github(event))
r.get('.*/releases', () => releases(event))
r.get('/', () => new Response('Hello worker!')) // return a default message for the root route

try {
Expand Down Expand Up @@ -61,7 +57,7 @@ async function github(event) {
const ref = file[6]
const headers = {
'User-Agent': 'hugomrdias',
// Authorization: `token ${GITHUB_TOKEN}`
Authorization: `token ${GITHUB_TOKEN}`,
}

const treeUrlRsp = await get(event, {
Expand All @@ -84,79 +80,46 @@ async function github(event) {
return data
}

async function get(event, options) {
const { url, transform, force, headers } = merge(
{
url: '',
transform: (d) => d,
force: false,
headers: {},
},
options
)

const cache = caches.default
const cacheKey = url + cacheBust
const cacheTTL = 86400 * 2 // 2 days
const cacheRevalidateTTL = 3600 * 2 // 2 hours
const cachedResponse = await cache.match(cacheKey)

if (force || !cachedResponse) {
console.log('Cache miss for ', cacheKey)
// if not in cache get from the origin
const response = await fetch(url, {
headers: {
...headers,
'If-None-Match': cachedResponse
? cachedResponse.headers.get('ETag')
: null,
},
})

if (response.ok) {
const { headers } = response
const contentType = headers.get('content-type') || ''

if (contentType.includes('application/json')) {
// transform the data
const data = transform(await response.json())

// build new response with the transformed body
const transformedResponse = new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Cache-Control': `max-age=${cacheTTL}`,
'X-RateLimit-Limit': headers.get('X-RateLimit-Limit'),
'X-RateLimit-Remaining': headers.get('X-RateLimit-Remaining'),
'X-RateLimit-Reset': headers.get('X-RateLimit-Reset'),
ETag: headers.get('ETag'),
async function releases(event) {
const headers = {
'User-Agent': 'hugomrdias',
Authorization: `token ${GITHUB_TOKEN}`,
}
const rsp = await get(event, {
url: `https://api.github.com/repos/filecoin-project/specs/releases?per_page=100&page=1`,
headers,
transform: async (releases) => {
return (
await map(
releases,
async (r) => {
const status = await get(event, {
url: `https://api.github.com/repos/filecoin-project/specs/commits/${r.tag_name}/status`,
headers,
})
const statusData = await status.json()
const preview = dlv(statusData, 'statuses').find(
(d) => d.description === 'Preview ready'
)

if (preview) {
return {
state: dlv(statusData, 'state'),
preview: preview.target_url,
tag_name: r.tag_name,
name: r.name,
author: r.author,
created_at: r.created_at,
published_at: r.published_at,
body: r.body,
}
}
},
})

// save response to cache
event.waitUntil(cache.put(cacheKey, transformedResponse.clone()))

return transformedResponse
} else {
throw new Error(
`Request error content type not supported. ${contentType}`
{ concurrency: 3 }
)
}
} else if (response.status === 304) {
// renew cache response
event.waitUntil(cache.put(cacheKey, cachedResponse.clone()))
return cachedResponse.clone()
} else {
return response
}
} else {
console.log('Cache hit for ', cacheKey, cachedResponse.headers.get('age'))
const cacheAge = cachedResponse.headers.get('age')
).filter(Boolean)
},
})

if (cacheAge > cacheRevalidateTTL) {
console.log('Cache is too old, revalidating...')
event.waitUntil(get(event, { url, transform, force: true }))
}
return cachedResponse
}
return rsp
}
3 changes: 2 additions & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dependencies": {
"dlv": "^1.1.3",
"merge-options": "^3.0.3",
"nanoid": "^3.1.16"
"nanoid": "^3.1.16",
"p-map": "^4.0.0"
}
}
85 changes: 85 additions & 0 deletions api/src/get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict'

const merge = require('merge-options')
const nanoid = require('nanoid/non-secure')

const cacheBust = nanoid.nanoid()

async function get(event, options) {
const { url, transform, force, headers } = merge(
{
url: '',
transform: (d) => d,
force: false,
headers: {},
},
options
)

const cache = caches.default
const cacheKey = url + cacheBust
const cacheTTL = 86400 * 2 // 2 days
const cacheRevalidateTTL = 3600 * 2 // 2 hours
const cachedResponse = await cache.match(cacheKey)

if (force || !cachedResponse) {
console.log('Cache miss for ', cacheKey)
// if not in cache get from the origin
const response = await fetch(url, {
headers: {
...headers,
'If-None-Match': cachedResponse
? cachedResponse.headers.get('ETag')
: null,
},
})

if (response.ok) {
const { headers } = response
const contentType = headers.get('content-type') || ''

if (contentType.includes('application/json')) {
// transform the data
const data = await transform(await response.json())

// build new response with the transformed body
const transformedResponse = new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'Cache-Control': `max-age=${cacheTTL}`,
'X-RateLimit-Limit': headers.get('X-RateLimit-Limit'),
'X-RateLimit-Remaining': headers.get('X-RateLimit-Remaining'),
'X-RateLimit-Reset': headers.get('X-RateLimit-Reset'),
ETag: headers.get('ETag'),
},
})

// save response to cache
event.waitUntil(cache.put(cacheKey, transformedResponse.clone()))

return transformedResponse
} else {
throw new Error(
`Request error content type not supported. ${contentType}`
)
}
} else if (response.status === 304) {
// renew cache response
event.waitUntil(cache.put(cacheKey, cachedResponse.clone()))
return cachedResponse.clone()
} else {
return response
}
} else {
console.log('Cache hit for ', cacheKey, cachedResponse.headers.get('age'))
const cacheAge = cachedResponse.headers.get('age')

if (cacheAge > cacheRevalidateTTL) {
console.log('Cache is too old, revalidating...')
event.waitUntil(get(event, { url, transform, force: true }))
}
return cachedResponse
}
}

module.exports = get
File renamed without changes.
6 changes: 6 additions & 0 deletions api/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
target: 'webworker',
// devtool: 'cheap-module-source-map',
entry: './index.js',
mode: 'production',
}
1 change: 1 addition & 0 deletions api/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ account_id = "fffa4b4363a7e5250af8357087263b3a"
workers_dev = true
route = ""
zone_id = ""
webpack_config = "webpack.config.js"
24 changes: 12 additions & 12 deletions assets/plugins/_numbered.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,52 @@
counter-reset: h1 h2 h3 h4 h5 h6;
}

.book-page .markdown h1 {
.book-page .markdown section > h1 {
counter-reset: h2 0;
counter-increment: h1;
}
.book-page .markdown h1::before {
.book-page .markdown section > h1::before {
content: counter(h1) '. ';
margin-right: 4px;
}
.book-page .markdown h2 {
.book-page .markdown section > h2 {
counter-reset: h3 0;
counter-increment: h2;
}
.book-page .markdown h2::before {
.book-page .markdown section > h2::before {
content: counter(h1) '.' counter(h2) '.';
margin-right: 4px;
}
.book-page .markdown h3 {
.book-page .markdown section > h3 {
counter-reset: h4 0;
counter-increment: h3;
}
.book-page .markdown h3::before {
.book-page .markdown section > h3::before {
content: counter(h1) '.' counter(h2) '.' counter(h3) '.' ' ';
margin-right: 4px;
}
.book-page .markdown h4 {
.book-page .markdown section > h4 {
counter-reset: h5 0;
counter-increment: h4;
}
.book-page .markdown h4::before {
.book-page .markdown section > h4::before {
content: counter(h1) '.' counter(h2) '.' counter(h3) '.' counter(h4) '.' ' ';
margin-right: 4px;
}
.book-page .markdown h5 {
.book-page .markdown section > h5 {
counter-reset: h6 0;
counter-increment: h5;
}
.book-page .markdown h5::before {
.book-page .markdown section > h5::before {
content: counter(h1) '.' counter(h2) '.' counter(h3) '.' counter(h4) '.'
counter(h5) '.' ' ';
margin-right: 4px;
}
.book-page .markdown h6 {
.book-page .markdown section > h6 {
counter-reset: h7 0;
counter-increment: h6;
}
.book-page .markdown h6::before {
.book-page .markdown section > h6::before {
content: counter(h1) '.' counter(h2) '.' counter(h3) '.' counter(h4) '.'
counter(h5) '.' counter(h6) '.' ' ';
margin-right: 4px;
Expand Down
8 changes: 8 additions & 0 deletions content/releases/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: Releases
weight: 9
---

# Releases

{{<changelog>}}
21 changes: 21 additions & 0 deletions layouts/shortcodes/changelog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{{ $url := printf "%s/releases" $.Site.Params.API }}
{{- range $cls := getJSON $url -}}
{{- printf "## %s" $cls.name | $.Page.RenderString -}}
<img style="border-radius: 50%; vertical-align: sub;" src="{{$cls.author.avatar_url}}" width="20" height="20" alt="@hugomrdias"> {{ $cls.author.login }} released this in {{ $cls.published_at }} · <a href="{{ $cls.preview}}"> Go to this release <svg class="icon">
<use xlink:href="/symbol-defs.svg#icon-external-link"></use>
</svg></a>

<div class="book-expand">
<label>
<div class="book-expand-head flex justify-between">
<span>Changelog</span>
<span>⇵</span>
</div>
<input type="checkbox" class="hidden">
<div class="book-expand-content markdown-inner">
{{- $cls.body | markdownify -}}
</div>
</label>
</div>

{{- end -}}