scaffold unprompted site: eleventy, templates, gitea CI
Some checks failed
Deploy to S3 / deploy (push) Failing after 1m26s

This commit is contained in:
2026-03-28 04:10:09 +00:00
commit 0ed5909289
14 changed files with 471 additions and 0 deletions

35
.eleventy.js Normal file
View File

@@ -0,0 +1,35 @@
module.exports = function (eleventyConfig) {
eleventyConfig.addPassthroughCopy("css");
eleventyConfig.addCollection("columns", function (collectionApi) {
return collectionApi
.getFilteredByGlob("columns/**/*.md")
.sort((a, b) => b.date - a.date);
});
eleventyConfig.addFilter("year", function (date) {
return new Date(date).getFullYear();
});
eleventyConfig.addFilter("isoDate", function (date) {
return new Date(date).toISOString().split("T")[0];
});
eleventyConfig.addFilter("readableDate", function (date) {
return new Date(date).toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
});
return {
dir: {
input: ".",
includes: "_includes",
output: "_site",
},
markdownTemplateEngine: "njk",
htmlTemplateEngine: "njk",
};
};

View File

@@ -0,0 +1,41 @@
name: Deploy to S3
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build
run: yarn build
- name: Deploy to S3
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
run: |
pip install awscli
aws s3 sync _site/ s3://${{ vars.S3_BUCKET }} --delete
- name: Invalidate CloudFront
if: vars.CF_DISTRIBUTION_ID != ''
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }}
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ vars.CF_DISTRIBUTION_ID }} \
--paths "/*"

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules/
_site/

10
Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json yarn.lock* ./
RUN yarn install --frozen-lockfile 2>/dev/null || yarn install
COPY . .
RUN yarn build
FROM alpine:3.20
RUN apk add --no-cache aws-cli
COPY --from=build /app/_site /site

27
Makefile Normal file
View File

@@ -0,0 +1,27 @@
S3_BUCKET ?= s3://donny.nyc
CF_DISTRIBUTION_ID ?=
.PHONY: build dev deploy invalidate
build:
docker build -t unprompted .
dev:
docker run --rm -v $$(pwd):/app -w /app -p 8080:8080 node:22-alpine sh -c "yarn install && yarn dev --port 8080"
deploy: build
docker run --rm \
-e AWS_ACCESS_KEY_ID \
-e AWS_SECRET_ACCESS_KEY \
-e AWS_DEFAULT_REGION \
unprompted \
aws s3 sync /site $(S3_BUCKET) --delete
invalidate:
ifdef CF_DISTRIBUTION_ID
aws cloudfront create-invalidation \
--distribution-id $(CF_DISTRIBUTION_ID) \
--paths "/*"
else
@echo "CF_DISTRIBUTION_ID not set, skipping invalidation"
endif

29
_includes/base.njk Normal file
View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% if title %}{{ title }} — un:prompted{% else %}un:prompted{% endif %}</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<header class="site-header">
<a href="/" class="site-title">un:prompted</a>
</header>
<div class="site-layout">
<main class="content">
{{ content | safe }}
</main>
{% include "sidebar.njk" %}
</div>
<footer class="site-footer">
<nav class="footer-nav">
<a href="/about/">about</a>
<a href="/code/">code</a>
</nav>
</footer>
</body>
</html>

37
_includes/column.njk Normal file
View File

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }} — un:prompted</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<header class="site-header">
<a href="/" class="site-title">un:prompted</a>
</header>
<div class="site-layout">
<main class="content">
<article class="column-entry">
<header class="column-header">
<h1>{{ title }}</h1>
<time datetime="{{ date | isoDate }}">{{ date | readableDate }}</time>
</header>
<div class="column-body">
{{ content | safe }}
</div>
</article>
</main>
{% include "sidebar.njk" %}
</div>
<footer class="site-footer">
<nav class="footer-nav">
<a href="/about/">about</a>
<a href="/code/">code</a>
</nav>
</footer>
</body>
</html>

21
_includes/sidebar.njk Normal file
View File

@@ -0,0 +1,21 @@
<aside class="sidebar">
<nav class="sidebar-nav">
<h2>archive</h2>
{%- set currentYear = null -%}
{%- for column in collections.columns %}
{%- set colYear = column.date | year -%}
{%- if colYear != currentYear %}
{%- if currentYear != null %}
</ul>
{%- endif %}
{%- set currentYear = colYear %}
<h3 class="year-label">{{ colYear }}</h3>
<ul>
{%- endif %}
<li><a href="{{ column.url }}">{{ column.data.title }}</a></li>
{%- endfor %}
{%- if currentYear != null %}
</ul>
{%- endif %}
</nav>
</aside>

View File

@@ -0,0 +1,9 @@
---
title: Hello, world
date: 2026-03-28
layout: column.njk
tags: column
permalink: /columns/2026/hello-world/
---
This is the first entry.

216
css/style.css Normal file
View File

@@ -0,0 +1,216 @@
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--font-body: Georgia, "Times New Roman", serif;
--font-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
--color-bg: #fdfdfd;
--color-text: #1a1a1a;
--color-muted: #6b6b6b;
--color-border: #e0e0e0;
--color-link: #1a1a1a;
--color-link-hover: #555;
--max-width: 960px;
--sidebar-width: 200px;
}
html {
font-size: 18px;
line-height: 1.6;
}
body {
font-family: var(--font-body);
color: var(--color-text);
background: var(--color-bg);
max-width: var(--max-width);
margin: 0 auto;
padding: 2rem 1.5rem;
}
a {
color: var(--color-link);
text-decoration: none;
border-bottom: 1px solid var(--color-border);
}
a:hover {
color: var(--color-link-hover);
border-bottom-color: var(--color-muted);
}
/* header */
.site-header {
margin-bottom: 2rem;
}
.site-title {
font-family: var(--font-ui);
font-size: 1rem;
font-weight: 600;
letter-spacing: 0.02em;
border-bottom: none;
}
/* layout */
.site-layout {
display: grid;
grid-template-columns: 1fr var(--sidebar-width);
gap: 3rem;
}
/* main content */
.content h1 {
font-size: 1.6rem;
line-height: 1.25;
margin-bottom: 0.25rem;
}
.column-header time {
font-family: var(--font-ui);
font-size: 0.8rem;
color: var(--color-muted);
}
.column-body {
margin-top: 1.5rem;
}
.column-body h2 {
font-size: 1.2rem;
margin-top: 2rem;
margin-bottom: 0.5rem;
}
.column-body h3 {
font-size: 1rem;
margin-top: 1.5rem;
margin-bottom: 0.5rem;
}
.column-body p {
margin-bottom: 1rem;
}
.column-body blockquote {
border-left: 3px solid var(--color-border);
padding-left: 1rem;
color: var(--color-muted);
margin-bottom: 1rem;
}
.column-body ul,
.column-body ol {
margin-bottom: 1rem;
padding-left: 1.5rem;
}
.column-body pre {
background: #f5f5f5;
padding: 1rem;
overflow-x: auto;
margin-bottom: 1rem;
font-size: 0.85rem;
line-height: 1.5;
}
.column-body code {
font-size: 0.85em;
}
.column-body hr {
border: none;
border-top: 1px solid var(--color-border);
margin: 2rem 0;
}
/* sidebar */
.sidebar {
font-family: var(--font-ui);
font-size: 0.8rem;
}
.sidebar h2 {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-muted);
margin-bottom: 0.75rem;
}
.sidebar .year-label {
font-size: 0.75rem;
font-weight: 600;
color: var(--color-muted);
margin-top: 0.75rem;
margin-bottom: 0.25rem;
}
.sidebar ul {
list-style: none;
}
.sidebar li {
margin-bottom: 0.3rem;
}
.sidebar a {
border-bottom: none;
color: var(--color-muted);
}
.sidebar a:hover {
color: var(--color-text);
}
/* footer */
.site-footer {
margin-top: 3rem;
padding-top: 1.5rem;
border-top: 1px solid var(--color-border);
}
.footer-nav {
font-family: var(--font-ui);
font-size: 0.8rem;
display: flex;
gap: 1.5rem;
}
.footer-nav a {
color: var(--color-muted);
border-bottom: none;
}
.footer-nav a:hover {
color: var(--color-text);
}
/* responsive */
@media (max-width: 640px) {
html {
font-size: 16px;
}
.site-layout {
grid-template-columns: 1fr;
gap: 2rem;
}
.sidebar {
order: 1;
border-top: 1px solid var(--color-border);
padding-top: 1.5rem;
}
}

18
index.njk Normal file
View File

@@ -0,0 +1,18 @@
---
layout: base.njk
---
{%- set latest = collections.columns | first -%}
{%- if latest %}
<article class="column-entry">
<header class="column-header">
<h1><a href="{{ latest.url }}">{{ latest.data.title }}</a></h1>
<time datetime="{{ latest.date | isoDate }}">{{ latest.date | readableDate }}</time>
</header>
<div class="column-body">
{{ latest.content | safe }}
</div>
</article>
{%- else %}
<p>Nothing here yet.</p>
{%- endif %}

12
package.json Normal file
View File

@@ -0,0 +1,12 @@
{
"name": "unprompted",
"version": "1.0.0",
"private": true,
"scripts": {
"build": "eleventy",
"dev": "eleventy --serve --watch"
},
"dependencies": {
"@11ty/eleventy": "^3.0.0"
}
}

7
pages/about.md Normal file
View File

@@ -0,0 +1,7 @@
---
title: About
layout: base.njk
permalink: /about/
---
About page.

7
pages/code.md Normal file
View File

@@ -0,0 +1,7 @@
---
title: Code
layout: base.njk
permalink: /code/
---
Code and projects.