Compare commits
21 Commits
f407b1c4af
...
v1.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 19f96553d9 | |||
| 1a7525a857 | |||
| e7d90d18e8 | |||
| 4019d2721d | |||
| b58604a4cf | |||
| 99e805b180 | |||
| 62075dea4a | |||
| 7afd041e53 | |||
| 64d08a0de3 | |||
| dd18bc3367 | |||
| f89661c1a5 | |||
| e0a3b66fa9 | |||
| 4e6c9dbeb5 | |||
| af453ca2ec | |||
| cea84de242 | |||
| ad1ec9c2e3 | |||
| fa3b5592da | |||
| 722d687afe | |||
| 77c0b29b4c | |||
| 9ae965662c | |||
| 9ced2af562 |
16
.gitea/workflows/lint.yml
Normal file
16
.gitea/workflows/lint.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
shellcheck:
|
||||||
|
runs-on: local
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Shellcheck
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y shellcheck
|
||||||
|
- name: Run Shellcheck
|
||||||
|
run: shellcheck -s sh kewt.sh markdown.sh tools/build-standalone.sh || true
|
||||||
109
.gitea/workflows/release.yml
Normal file
109
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
name: Release Standalone Builder
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: local
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build standalone executable
|
||||||
|
run: |
|
||||||
|
chmod +x tools/build-standalone.sh
|
||||||
|
./tools/build-standalone.sh
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '1.21'
|
||||||
|
|
||||||
|
- name: Upload Release Asset
|
||||||
|
uses: https://gitea.com/actions/release-action@main
|
||||||
|
with:
|
||||||
|
files: |-
|
||||||
|
kewt
|
||||||
|
api_key: '${{secrets.GITEA_TOKEN}}'
|
||||||
|
|
||||||
|
- name: Push to GitHub Release
|
||||||
|
run: |
|
||||||
|
TAG="${GITHUB_REF#refs/tags/}"
|
||||||
|
|
||||||
|
# Create the release on GitHub
|
||||||
|
curl -sL -X POST \
|
||||||
|
-H "Authorization: token ${{ secrets.GH_RELEASE_TOKEN }}" \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
"https://api.github.com/repos/n0va-bot/kewt/releases" \
|
||||||
|
-d "{\"tag_name\":\"${TAG}\",\"name\":\"${TAG}\",\"draft\":false,\"prerelease\":false}" || true
|
||||||
|
|
||||||
|
# Get the release ID
|
||||||
|
RELEASE_ID=$(curl -sL \
|
||||||
|
-H "Authorization: token ${{ secrets.GH_RELEASE_TOKEN }}" \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
"https://api.github.com/repos/n0va-bot/kewt/releases/tags/${TAG}" | jq -r '.id')
|
||||||
|
|
||||||
|
# Upload the asset
|
||||||
|
curl -sL -X POST \
|
||||||
|
-H "Authorization: token ${{ secrets.GH_RELEASE_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
"https://uploads.github.com/repos/n0va-bot/kewt/releases/${RELEASE_ID}/assets?name=kewt" \
|
||||||
|
--data-binary @kewt
|
||||||
|
|
||||||
|
publish-aur:
|
||||||
|
runs-on: local
|
||||||
|
needs: build
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Arch Linux environment
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y pacman-package-manager curl jq || true
|
||||||
|
|
||||||
|
- name: Render PKGBUILD and SRCINFO
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/v}
|
||||||
|
VERSION=${VERSION#refs/tags/}
|
||||||
|
|
||||||
|
curl -sL -o kewt https://git.krzak.org/N0VA/kewt/releases/download/v${VERSION}/kewt
|
||||||
|
|
||||||
|
CHECKSUM=$(sha256sum kewt | awk '{print $1}')
|
||||||
|
|
||||||
|
mkdir -p aur-work
|
||||||
|
sed -e "s/VERSION_PLACEHOLDER/${VERSION}/g" \
|
||||||
|
-e "s/SHA256SUM_PLACEHOLDER/${CHECKSUM}/g" \
|
||||||
|
packaging/AUR/PKGBUILD.template > aur-work/PKGBUILD
|
||||||
|
|
||||||
|
cat > aur-work/.SRCINFO << SRCEOF
|
||||||
|
pkgbase = kewt-bin
|
||||||
|
pkgdesc = A minimalist, 100% POSIX, static site generator inspired by werc and kew
|
||||||
|
pkgver = ${VERSION}
|
||||||
|
pkgrel = 1
|
||||||
|
url = https://git.krzak.org/N0VA/kewt
|
||||||
|
arch = any
|
||||||
|
license = MIT
|
||||||
|
depends = sh
|
||||||
|
provides = kewt
|
||||||
|
conflicts = kewt
|
||||||
|
conflicts = kewt-git
|
||||||
|
source = kewt-bin-${VERSION}.sh::https://git.krzak.org/N0VA/kewt/releases/download/v${VERSION}/kewt
|
||||||
|
sha256sums = ${CHECKSUM}
|
||||||
|
|
||||||
|
pkgname = kewt-bin
|
||||||
|
SRCEOF
|
||||||
|
# Remove leading whitespace from heredoc
|
||||||
|
sed -i 's/^ //' aur-work/.SRCINFO
|
||||||
|
|
||||||
|
- name: Publish to AUR
|
||||||
|
uses: KSXGitHub/github-actions-deploy-aur@v3.0.1
|
||||||
|
with:
|
||||||
|
pkgname: kewt-bin
|
||||||
|
pkgbuild: ./aur-work/PKGBUILD
|
||||||
|
commit_username: ${{ github.actor }}
|
||||||
|
commit_email: ${{ github.actor }}@users.noreply.github.com
|
||||||
|
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
|
||||||
|
commit_message: "Update kewt-bin to ${{ github.ref_name }}"
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
out/
|
out/
|
||||||
|
kewt
|
||||||
35
LICENSE
Normal file
35
LICENSE
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright 2026 N0\A
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software
|
||||||
|
for any purpose with or without fee is hereby granted, provided
|
||||||
|
that the above copyright notice and this permission notice appear
|
||||||
|
in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||||
|
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||||
|
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
|
||||||
|
OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This project incorporates code (CSS style) from the 'kew' project, which is also licensed under the ISC License:
|
||||||
|
|
||||||
|
Copyright (c) 2023 uint23
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
21
README.md
21
README.md
@@ -19,6 +19,20 @@ It's meant to be a static site generator, like _[kew](https://github.com/uint23/
|
|||||||
|
|
||||||
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
You can clone the repository to use `kewt.sh` directly, or you can download the standalone executable, which bundles all dependencies into a single file:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -L -o kewt https://git.krzak.org/N0VA/kewt/releases/latest/download/kewt
|
||||||
|
chmod +x kewt
|
||||||
|
```
|
||||||
|
|
||||||
|
On Arch Linux, _kewt_ is available on the AUR:
|
||||||
|
|
||||||
|
- [kewt-bin](https://aur.archlinux.org/packages/kewt-bin) — prebuilt standalone binary from the latest release
|
||||||
|
- [kewt-git](https://aur.archlinux.org/packages/kewt-git) — built from the latest git source
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -49,6 +63,9 @@ display_logo = false
|
|||||||
display_title = true
|
display_title = true
|
||||||
logo_as_favicon = true
|
logo_as_favicon = true
|
||||||
favicon = ""
|
favicon = ""
|
||||||
|
generate_page_title = true
|
||||||
|
error_page = "not_found.html"
|
||||||
|
versioning = false
|
||||||
```
|
```
|
||||||
|
|
||||||
- `title` site title
|
- `title` site title
|
||||||
@@ -67,6 +84,9 @@ favicon = ""
|
|||||||
- `display_title` show title text in header
|
- `display_title` show title text in header
|
||||||
- `logo_as_favicon` use `logo` as favicon
|
- `logo_as_favicon` use `logo` as favicon
|
||||||
- `favicon` explicit favicon path (used when `logo_as_favicon` is false or no logo is set)
|
- `favicon` explicit favicon path (used when `logo_as_favicon` is false or no logo is set)
|
||||||
|
- `generate_page_title` automatically generate title text from the first markdown heading or filename (default: true)
|
||||||
|
- `error_page` filename for the generated 404 error page (default: "not_found.html", empty to disable)
|
||||||
|
- `versioning` append a version query parameter (`?v=timestamp`) to css asset urls to bypass cache (default: false)
|
||||||
|
|
||||||
## Ignores
|
## Ignores
|
||||||
|
|
||||||
@@ -86,7 +106,6 @@ favicon = ""
|
|||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
- Markdown to html conversion based on [markdown.bash](https://github.com/chadbraunduin/markdown.bash) by [chadbraunduin](https://github.com/chadbraunduin)
|
|
||||||
- Default css style and html template based on _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
- Default css style and html template based on _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
||||||
|
|
||||||
>[!WARNING]
|
>[!WARNING]
|
||||||
|
|||||||
@@ -8,18 +8,21 @@ function strip_markdown(s) {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
function print_header(line) {
|
function print_header(line) {
|
||||||
if (line ~ /^# /) {
|
tag = ""
|
||||||
sub(/^# /, "", line); print "<h1 id=\"" strip_markdown(line) "\">" line "</h1>"
|
if (line ~ /^# /) { tag = "h1"; sub(/^# /, "", line) }
|
||||||
} else if (line ~ /^## /) {
|
else if (line ~ /^## /) { tag = "h2"; sub(/^## /, "", line) }
|
||||||
sub(/^## /, "", line); print "<h2 id=\"" strip_markdown(line) "\">" line "</h2>"
|
else if (line ~ /^### /) { tag = "h3"; sub(/^### /, "", line) }
|
||||||
} else if (line ~ /^### /) {
|
else if (line ~ /^#### /) { tag = "h4"; sub(/^#### /, "", line) }
|
||||||
sub(/^### /, "", line); print "<h3 id=\"" strip_markdown(line) "\">" line "</h3>"
|
else if (line ~ /^##### /) { tag = "h5"; sub(/^##### /, "", line) }
|
||||||
} else if (line ~ /^#### /) {
|
else if (line ~ /^###### /) { tag = "h6"; sub(/^###### /, "", line) }
|
||||||
sub(/^#### /, "", line); print "<h4 id=\"" strip_markdown(line) "\">" line "</h4>"
|
|
||||||
} else if (line ~ /^##### /) {
|
if (tag != "") {
|
||||||
sub(/^##### /, "", line); print "<h5 id=\"" strip_markdown(line) "\">" line "</h5>"
|
id = strip_markdown(line)
|
||||||
} else if (line ~ /^###### /) {
|
if (enable_header_links == "true") {
|
||||||
sub(/^###### /, "", line); print "<h6 id=\"" strip_markdown(line) "\">" line "</h6>"
|
print "<" tag " id=\"" id "\"><a href=\"#" id "\" class=\"header-anchor\">" line "</a></" tag ">"
|
||||||
|
} else {
|
||||||
|
print "<" tag " id=\"" id "\">" line "</" tag ">"
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
print line
|
print line
|
||||||
}
|
}
|
||||||
|
|||||||
428
kewt.sh
428
kewt.sh
@@ -6,23 +6,31 @@ die() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
|
invoked_as=$(basename "${KEWT_INVOKED_AS:-$0}")
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $0 [--from <src>] [--to <out>]
|
Usage: $invoked_as [--from <src>] [--to <out>]
|
||||||
$0 [src] [out]
|
$invoked_as [src] [out]
|
||||||
$0 --new [title]
|
$invoked_as --new [title]
|
||||||
$0 --help
|
$invoked_as --update [dir]
|
||||||
|
$invoked_as --post
|
||||||
|
$invoked_as --help
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--help Show this help message.
|
--help Show this help message.
|
||||||
--new [title] Create a new site directory (default: site)
|
--new [title] Create a new site directory (default: site)
|
||||||
--from <src> Source directory (default: site)
|
--update [dir] Update site.conf and template.html with latest defaults (defaults to current directory)
|
||||||
--to <out> Output directory (default: out)
|
--post Create a new empty post file in the configured posts_dir with current date and time as name
|
||||||
|
--from <src> Source directory (default: site)
|
||||||
|
--to <out> Output directory (default: out)
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
script_dir=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd)
|
||||||
awk_dir="$script_dir/awk"
|
awk_dir="$script_dir/awk"
|
||||||
|
|
||||||
|
KEWT_TMPDIR=$(mktemp -d "/tmp/kewt_run.XXXXXX")
|
||||||
|
trap 'rm -rf "$KEWT_TMPDIR"' EXIT HUP INT TERM
|
||||||
|
|
||||||
ensure_root_defaults() {
|
ensure_root_defaults() {
|
||||||
if [ ! -f "./site.conf" ]; then
|
if [ ! -f "./site.conf" ]; then
|
||||||
cat > "./site.conf" <<'EOF'
|
cat > "./site.conf" <<'EOF'
|
||||||
@@ -42,6 +50,14 @@ display_logo = false
|
|||||||
display_title = true
|
display_title = true
|
||||||
logo_as_favicon = true
|
logo_as_favicon = true
|
||||||
favicon = ""
|
favicon = ""
|
||||||
|
generate_page_title = true
|
||||||
|
error_page = "not_found.html"
|
||||||
|
versioning = false
|
||||||
|
enable_header_links = true
|
||||||
|
base_url = ""
|
||||||
|
generate_feed = false
|
||||||
|
feed_file = "rss.xml"
|
||||||
|
posts_dir = ""
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -93,15 +109,141 @@ create_new_site() {
|
|||||||
exit 0
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_nav() {
|
create_new_post() {
|
||||||
dinfo=$(find "$1" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -print | sort | awk -v src="$1" -f "$awk_dir/collect_dir_info.awk")
|
post_src_dir="$1"
|
||||||
find "$1" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -name "*.md" -print | sort | awk -v src="$1" -v single_file_index="$single_file_index" -v flatten="$flatten" -v order="$order" -v dinfo="$dinfo" -f "$awk_dir/generate_sidebar.awk"
|
|
||||||
|
target_dir="$post_src_dir"
|
||||||
|
if [ -n "$posts_dir" ]; then
|
||||||
|
target_dir="$post_src_dir/$posts_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$target_dir"
|
||||||
|
|
||||||
|
base_filename="$(date +%Y-%m-%d-%H:%M)"
|
||||||
|
filename="${base_filename}.md"
|
||||||
|
file_path="$target_dir/$filename"
|
||||||
|
|
||||||
|
counter=1
|
||||||
|
while [ -e "$file_path" ]; do
|
||||||
|
filename="${base_filename}_${counter}.md"
|
||||||
|
file_path="$target_dir/$filename"
|
||||||
|
counter=$((counter + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
touch "$file_path"
|
||||||
|
|
||||||
|
echo "Created new post at '$file_path'."
|
||||||
|
exit 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_site() {
|
||||||
|
update_dir="${1:-.}"
|
||||||
|
[ -d "$update_dir" ] || die "Directory '$update_dir' does not exist."
|
||||||
|
|
||||||
|
target_conf="$update_dir/site.conf"
|
||||||
|
target_tmpl="$update_dir/template.html"
|
||||||
|
|
||||||
|
# Generate default site.conf
|
||||||
|
default_conf="$KEWT_TMPDIR/default_site.conf"
|
||||||
|
cat > "$default_conf" <<'CONFEOF'
|
||||||
|
title = "kewt"
|
||||||
|
style = "kewt"
|
||||||
|
dir_indexes = true
|
||||||
|
single_file_index = true
|
||||||
|
flatten = false
|
||||||
|
order = ""
|
||||||
|
home_name = "Home"
|
||||||
|
show_home_in_nav = true
|
||||||
|
nav_links = ""
|
||||||
|
nav_extra = ""
|
||||||
|
footer = "made with <a href="https://kewt.krzak.org">kewt</a>"
|
||||||
|
logo = ""
|
||||||
|
display_logo = false
|
||||||
|
display_title = true
|
||||||
|
logo_as_favicon = true
|
||||||
|
favicon = ""
|
||||||
|
generate_page_title = true
|
||||||
|
error_page = "not_found.html"
|
||||||
|
versioning = false
|
||||||
|
enable_header_links = true
|
||||||
|
base_url = ""
|
||||||
|
generate_feed = false
|
||||||
|
feed_file = "rss.xml"
|
||||||
|
posts_dir = ""
|
||||||
|
CONFEOF
|
||||||
|
|
||||||
|
# Update site.conf
|
||||||
|
if [ ! -f "$target_conf" ]; then
|
||||||
|
echo "No site.conf found in '$update_dir'; nothing to update."
|
||||||
|
else
|
||||||
|
added=0
|
||||||
|
while IFS= read -r line; do
|
||||||
|
case "$line" in
|
||||||
|
''|'#'*) continue ;;
|
||||||
|
*=*) ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
key=$(printf '%s' "${line%%=*}" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
|
||||||
|
if ! grep -q "^[[:space:]]*${key}[[:space:]]*=" "$target_conf"; then
|
||||||
|
printf '%s\n' "$line" >> "$target_conf"
|
||||||
|
echo " Added: $key"
|
||||||
|
added=$((added + 1))
|
||||||
|
fi
|
||||||
|
done < "$default_conf"
|
||||||
|
if [ "$added" -eq 0 ]; then
|
||||||
|
echo "site.conf is already up to date."
|
||||||
|
else
|
||||||
|
echo "Added $added new key(s) to '$target_conf'."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update template.html
|
||||||
|
if [ -f "$target_tmpl" ]; then
|
||||||
|
default_tmpl="$KEWT_TMPDIR/default_template.html"
|
||||||
|
cat > "$default_tmpl" <<'TMPLEOF'
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>{{TITLE}}</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{CSS}}" type="text/css" />
|
||||||
|
{{HEAD_EXTRA}}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>{{HEADER_BRAND}}</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<nav id="side-bar">{{NAV}}</nav>
|
||||||
|
|
||||||
|
<article>{{CONTENT}}</article>
|
||||||
|
<footer>{{FOOTER}}</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
TMPLEOF
|
||||||
|
if cmp -s "$default_tmpl" "$target_tmpl" 2>/dev/null; then
|
||||||
|
echo "template.html is already up to date."
|
||||||
|
else
|
||||||
|
cp "$default_tmpl" "${target_tmpl}.default"
|
||||||
|
echo "template.html has local changes; saved latest default as '${target_tmpl}.default'."
|
||||||
|
echo ""
|
||||||
|
diff "$target_tmpl" "${target_tmpl}.default" || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
src=""
|
src=""
|
||||||
out=""
|
out=""
|
||||||
new_mode="false"
|
new_mode="false"
|
||||||
new_title=""
|
new_title=""
|
||||||
|
post_mode="false"
|
||||||
|
post_title=""
|
||||||
positional_count=0
|
positional_count=0
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
@@ -117,6 +259,17 @@ while [ $# -gt 0 ]; do
|
|||||||
shift
|
shift
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
--post)
|
||||||
|
post_mode="true"
|
||||||
|
;;
|
||||||
|
--update)
|
||||||
|
update_dir="."
|
||||||
|
if [ $# -gt 1 ] && [ "${2#-}" = "$2" ]; then
|
||||||
|
update_dir="$2"
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
update_site "$update_dir"
|
||||||
|
;;
|
||||||
--from)
|
--from)
|
||||||
[ $# -lt 2 ] && die "--from requires a value."
|
[ $# -lt 2 ] && die "--from requires a value."
|
||||||
src="$2"
|
src="$2"
|
||||||
@@ -133,9 +286,9 @@ while [ $# -gt 0 ]; do
|
|||||||
*)
|
*)
|
||||||
positional_count=$((positional_count + 1))
|
positional_count=$((positional_count + 1))
|
||||||
if [ "$positional_count" -eq 1 ]; then
|
if [ "$positional_count" -eq 1 ]; then
|
||||||
[ -z "$src" ] && src="$1" || die "Source already set (use either positional or --from)."
|
if [ -z "$src" ]; then src="$1"; else die "Source already set (use either positional or --from)."; fi
|
||||||
elif [ "$positional_count" -eq 2 ]; then
|
elif [ "$positional_count" -eq 2 ]; then
|
||||||
[ -z "$out" ] && out="$1" || die "Output already set (use either positional or --to)."
|
if [ -z "$out" ]; then out="$1"; else die "Output already set (use either positional or --to)."; fi
|
||||||
else
|
else
|
||||||
die "Too many positional arguments."
|
die "Too many positional arguments."
|
||||||
fi
|
fi
|
||||||
@@ -154,7 +307,14 @@ ensure_root_defaults
|
|||||||
src="${src%/}"
|
src="${src%/}"
|
||||||
out="${out%/}"
|
out="${out%/}"
|
||||||
|
|
||||||
[ -d "$src" ] || die "Source directory '$src' does not exist."
|
if [ ! -d "$src" ]; then
|
||||||
|
if [ "$src" = "site" ]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
die "Source directory '$src' does not exist."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
IGNORE_ARGS="-name '.kewtignore' -o -path '$src/.*'"
|
IGNORE_ARGS="-name '.kewtignore' -o -path '$src/.*'"
|
||||||
|
|
||||||
@@ -177,14 +337,14 @@ if [ -f "$src/.kewtignore" ]; then
|
|||||||
done < "$src/.kewtignore"
|
done < "$src/.kewtignore"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
find "$src" -name .kewtignore > "/tmp/kewt_ignore_$$"
|
find "$src" -name .kewtignore > "$KEWT_TMPDIR/kewt_ignore"
|
||||||
while read -r ki; do
|
while read -r ki; do
|
||||||
d="${ki%/.kewtignore}"
|
d="${ki%/.kewtignore}"
|
||||||
if [ "$d" != "$src" ] && [ "$d" != "." ]; then
|
if [ "$d" != "$src" ] && [ "$d" != "." ]; then
|
||||||
IGNORE_ARGS="$IGNORE_ARGS -o -path '$d' -o -path '$d/*'"
|
IGNORE_ARGS="$IGNORE_ARGS -o -path '$d' -o -path '$d/*'"
|
||||||
fi
|
fi
|
||||||
done < "/tmp/kewt_ignore_$$"
|
done < "$KEWT_TMPDIR/kewt_ignore"
|
||||||
rm -f "/tmp/kewt_ignore_$$"
|
rm -f "$KEWT_TMPDIR/kewt_ignore"
|
||||||
|
|
||||||
HIDE_ARGS="-name '.kewtignore' -o -name '.kewthide' -o -name '.kewtpreserve' -o -path '$src/.*'"
|
HIDE_ARGS="-name '.kewtignore' -o -name '.kewthide' -o -name '.kewtpreserve' -o -path '$src/.*'"
|
||||||
|
|
||||||
@@ -207,14 +367,14 @@ if [ -f "$src/.kewthide" ]; then
|
|||||||
done < "$src/.kewthide"
|
done < "$src/.kewthide"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
find "$src" -name .kewthide > "/tmp/kewt_hide_$$"
|
find "$src" -name .kewthide > "$KEWT_TMPDIR/kewt_hide"
|
||||||
while read -r kh; do
|
while read -r kh; do
|
||||||
d="${kh%/.kewthide}"
|
d="${kh%/.kewthide}"
|
||||||
if [ "$d" != "$src" ] && [ "$d" != "." ]; then
|
if [ "$d" != "$src" ] && [ "$d" != "." ]; then
|
||||||
HIDE_ARGS="$HIDE_ARGS -o -path '$d' -o -path '$d/*'"
|
HIDE_ARGS="$HIDE_ARGS -o -path '$d' -o -path '$d/*'"
|
||||||
fi
|
fi
|
||||||
done < "/tmp/kewt_hide_$$"
|
done < "$KEWT_TMPDIR/kewt_hide"
|
||||||
rm -f "/tmp/kewt_hide_$$"
|
rm -f "$KEWT_TMPDIR/kewt_hide"
|
||||||
|
|
||||||
PRESERVE_ARGS="-false"
|
PRESERVE_ARGS="-false"
|
||||||
|
|
||||||
@@ -237,18 +397,22 @@ if [ -f "$src/.kewtpreserve" ]; then
|
|||||||
done < "$src/.kewtpreserve"
|
done < "$src/.kewtpreserve"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
find "$src" -name .kewtpreserve > "/tmp/kewt_preserve_$$"
|
find "$src" -name .kewtpreserve > "$KEWT_TMPDIR/kewt_preserve"
|
||||||
while read -r kp; do
|
while read -r kp; do
|
||||||
d="${kp%/.kewtpreserve}"
|
d="${kp%/.kewtpreserve}"
|
||||||
if [ "$d" != "$src" ] && [ "$d" != "." ]; then
|
if [ "$d" != "$src" ] && [ "$d" != "." ]; then
|
||||||
PRESERVE_ARGS="$PRESERVE_ARGS -o -path '$d' -o -path '$d/*'"
|
PRESERVE_ARGS="$PRESERVE_ARGS -o -path '$d' -o -path '$d/*'"
|
||||||
fi
|
fi
|
||||||
done < "/tmp/kewt_preserve_$$"
|
done < "$KEWT_TMPDIR/kewt_preserve"
|
||||||
rm -f "/tmp/kewt_preserve_$$"
|
rm -f "$KEWT_TMPDIR/kewt_preserve"
|
||||||
|
|
||||||
generate_nav() {
|
generate_nav() {
|
||||||
dinfo=$(eval "find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -print" | sort | awk -v src="$1" -f "$awk_dir/collect_dir_info.awk")
|
dinfo=$(eval "find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -print" | sort | awk -v src="$1" -f "$awk_dir/collect_dir_info.awk")
|
||||||
eval "find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -name \"*.md\" -print" | sort | awk -v src="$1" -v single_file_index="$single_file_index" -v flatten="$flatten" -v order="$order" -v home_name="$home_name" -v show_home_in_nav="$show_home_in_nav" -v dinfo="$dinfo" -f "$awk_dir/generate_sidebar.awk"
|
find_cmd="find \"$1\" \( $IGNORE_ARGS -o $HIDE_ARGS -o $PRESERVE_ARGS \) -prune -o -name \"*.md\" -print"
|
||||||
|
if [ -n "$posts_dir" ] && [ -d "$1/$posts_dir" ]; then
|
||||||
|
find_cmd="$find_cmd && echo \"$1/$posts_dir/index.md\""
|
||||||
|
fi
|
||||||
|
eval "$find_cmd" | sort -u | awk -v src="$1" -v single_file_index="$single_file_index" -v flatten="$flatten" -v order="$order" -v home_name="$home_name" -v show_home_in_nav="$show_home_in_nav" -v dinfo="$dinfo" -f "$awk_dir/generate_sidebar.awk"
|
||||||
}
|
}
|
||||||
|
|
||||||
title="kewt"
|
title="kewt"
|
||||||
@@ -268,6 +432,14 @@ display_logo="false"
|
|||||||
display_title="true"
|
display_title="true"
|
||||||
logo_as_favicon="true"
|
logo_as_favicon="true"
|
||||||
favicon=""
|
favicon=""
|
||||||
|
generate_page_title="true"
|
||||||
|
error_page="not_found.html"
|
||||||
|
versioning="false"
|
||||||
|
enable_header_links="true"
|
||||||
|
base_url=""
|
||||||
|
generate_feed="false"
|
||||||
|
feed_file="rss.xml"
|
||||||
|
posts_dir=""
|
||||||
|
|
||||||
load_config() {
|
load_config() {
|
||||||
[ -f "$1" ] || return
|
[ -f "$1" ] || return
|
||||||
@@ -305,6 +477,14 @@ load_config() {
|
|||||||
display_title) display_title="$val" ;;
|
display_title) display_title="$val" ;;
|
||||||
logo_as_favicon) logo_as_favicon="$val" ;;
|
logo_as_favicon) logo_as_favicon="$val" ;;
|
||||||
favicon) favicon="$val" ;;
|
favicon) favicon="$val" ;;
|
||||||
|
generate_page_title) generate_page_title="$val" ;;
|
||||||
|
error_page) error_page="$val" ;;
|
||||||
|
versioning) versioning="$val" ;;
|
||||||
|
enable_header_links) enable_header_links="$val" ;;
|
||||||
|
base_url) base_url="$val" ;;
|
||||||
|
generate_feed) generate_feed="$val" ;;
|
||||||
|
feed_file) feed_file="$val" ;;
|
||||||
|
posts_dir) posts_dir="$val" ;;
|
||||||
esac
|
esac
|
||||||
done < "$1"
|
done < "$1"
|
||||||
}
|
}
|
||||||
@@ -312,6 +492,17 @@ load_config() {
|
|||||||
load_config "./site.conf"
|
load_config "./site.conf"
|
||||||
load_config "$src/site.conf"
|
load_config "$src/site.conf"
|
||||||
|
|
||||||
|
if [ -n "$posts_dir" ]; then
|
||||||
|
HIDE_ARGS="$HIDE_ARGS -o -path '$src/$posts_dir/*'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "$post_mode" = "true" ] && create_new_post "$src"
|
||||||
|
|
||||||
|
asset_version=""
|
||||||
|
if [ "$versioning" = "true" ]; then
|
||||||
|
asset_version="?v=$(date +%s)"
|
||||||
|
fi
|
||||||
|
|
||||||
escape_html_text() {
|
escape_html_text() {
|
||||||
printf '%s' "$1" | sed \
|
printf '%s' "$1" | sed \
|
||||||
-e 's/&/\&/g' \
|
-e 's/&/\&/g' \
|
||||||
@@ -333,6 +524,7 @@ nav_links_html() {
|
|||||||
old_ifs=$IFS
|
old_ifs=$IFS
|
||||||
set -f
|
set -f
|
||||||
IFS=','
|
IFS=','
|
||||||
|
# shellcheck disable=SC2086
|
||||||
set -- $nav_links
|
set -- $nav_links
|
||||||
IFS=$old_ifs
|
IFS=$old_ifs
|
||||||
set +f
|
set +f
|
||||||
@@ -412,13 +604,28 @@ copy_style_with_resolved_vars() {
|
|||||||
|
|
||||||
render_markdown() {
|
render_markdown() {
|
||||||
file="$1"
|
file="$1"
|
||||||
|
is_home="$2"
|
||||||
|
|
||||||
|
content_file="$file"
|
||||||
|
if [ -n "$posts_dir" ] && [ "$file" != "$src/$posts_dir/index.md" ]; then
|
||||||
|
dir_of_file=$(dirname "$file")
|
||||||
|
rel_dir_of_file="${dir_of_file#"$src"}"
|
||||||
|
rel_dir_of_file="${rel_dir_of_file#/}"
|
||||||
|
if [ "$rel_dir_of_file" = "$posts_dir" ]; then
|
||||||
|
temp_post_with_backlink="$KEWT_TMPDIR/post_with_backlink.md"
|
||||||
|
printf "[< Back](index.html)\n\n" > "$temp_post_with_backlink"
|
||||||
|
cat "$file" >> "$temp_post_with_backlink"
|
||||||
|
content_file="$temp_post_with_backlink"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
local_template=$(find_closest "template.html" "$(dirname "$file")")
|
local_template=$(find_closest "template.html" "$(dirname "$file")")
|
||||||
[ -z "$local_template" ] && local_template="$template"
|
[ -z "$local_template" ] && local_template="$template"
|
||||||
|
|
||||||
closest_style_src=$(find_closest "styles.css" "$(dirname "$file")")
|
closest_style_src=$(find_closest "styles.css" "$(dirname "$file")")
|
||||||
[ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$file")")
|
[ -z "$closest_style_src" ] && closest_style_src=$(find_closest "style.css" "$(dirname "$file")")
|
||||||
if [ -n "$closest_style_src" ]; then
|
if [ -n "$closest_style_src" ]; then
|
||||||
style_rel_to_src="${closest_style_src#$src/}"
|
style_rel_to_src="${closest_style_src#"$src"/}"
|
||||||
case "$closest_style_src" in
|
case "$closest_style_src" in
|
||||||
"$src/styles.css") style_rel_to_src="styles.css" ;;
|
"$src/styles.css") style_rel_to_src="styles.css" ;;
|
||||||
"$src/style.css") style_rel_to_src="style.css" ;;
|
"$src/style.css") style_rel_to_src="style.css" ;;
|
||||||
@@ -460,13 +667,32 @@ render_markdown() {
|
|||||||
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
head_extra="<link rel=\"icon\" href=\"$favicon_src\" />"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$file" | awk -v title="$title" -v nav="$nav" -v footer="$footer" -v style_path="$style_path" -v header_brand="$header_brand" -v head_extra="$head_extra" -f "$awk_dir/render_template.awk" "$local_template"
|
page_title="$title"
|
||||||
|
if [ "$generate_page_title" = "true" ] && [ -n "$file" ] && [ -f "$file" ]; then
|
||||||
|
if [ "$is_home" = "true" ] && [ -n "$home_name" ]; then
|
||||||
|
page_title="$home_name - $title"
|
||||||
|
else
|
||||||
|
first_heading=$(grep -m 1 '^# ' "$file" | sed 's/^# *//; s/ *$//')
|
||||||
|
if [ -n "$first_heading" ]; then
|
||||||
|
first_heading=$(echo "$first_heading" | sed -e 's/\[//g' -e 's/\]//g' -e 's/!//g' -e 's/\*//g' -e 's/_//g' -e 's/`//g' -e 's/([^)]*)//g' | sed 's/\\//g')
|
||||||
|
page_title="$first_heading - $title"
|
||||||
|
else
|
||||||
|
basename_no_ext=$(basename "$file" .md)
|
||||||
|
if [ "$basename_no_ext" != "index" ] && [ "$basename_no_ext" != "404_gen" ]; then
|
||||||
|
cap_basename=$(echo "$basename_no_ext" | awk '{print toupper(substr($0,1,1)) substr($0,2)}')
|
||||||
|
page_title="$cap_basename - $title"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
ENABLE_HEADER_LINKS="$enable_header_links" MARKDOWN_SITE_ROOT="$src" MARKDOWN_FALLBACK_FILE="$script_dir/styles/$style.css" sh "$script_dir/markdown.sh" "$content_file" | awk -v title="$page_title" -v nav="$nav" -v footer="$footer" -v style_path="${style_path}${asset_version}" -v header_brand="$header_brand" -v head_extra="$head_extra" -f "$awk_dir/render_template.awk" "$local_template"
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "Building site from '$src' to '$out'..."
|
echo "Building site from '$src' to '$out'..."
|
||||||
|
|
||||||
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while read -r dir; do
|
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while read -r dir; do
|
||||||
rel_dir="${dir#$src}"
|
rel_dir="${dir#"$src"}"
|
||||||
rel_dir="${rel_dir#/}"
|
rel_dir="${rel_dir#/}"
|
||||||
[ -z "$rel_dir" ] && rel_dir="."
|
[ -z "$rel_dir" ] && rel_dir="."
|
||||||
out_dir="$out/$rel_dir"
|
out_dir="$out/$rel_dir"
|
||||||
@@ -485,17 +711,25 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
|||||||
md_count=$(find "$dir" ! -name "$(basename "$dir")" -prune -name "*.md" | wc -l)
|
md_count=$(find "$dir" ! -name "$(basename "$dir")" -prune -name "*.md" | wc -l)
|
||||||
if [ "$md_count" -eq 1 ]; then
|
if [ "$md_count" -eq 1 ]; then
|
||||||
md_file=$(find "$dir" ! -name "$(basename "$dir")" -prune -name "*.md")
|
md_file=$(find "$dir" ! -name "$(basename "$dir")" -prune -name "*.md")
|
||||||
render_markdown "$md_file" > "$out_dir/index.html"
|
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||||
|
render_markdown "$md_file" "$is_home" > "$out_dir/index.html"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
temp_index="/tmp/kewt_index_$$.md"
|
temp_index="$KEWT_TMPDIR/index.md"
|
||||||
display_dir="${rel_dir#.}"
|
display_dir="${rel_dir#.}"
|
||||||
[ -z "$display_dir" ] && display_dir="/"
|
[ -z "$display_dir" ] && display_dir="/"
|
||||||
echo "# Index of $display_dir" > "$temp_index"
|
echo "# Index of $display_dir" > "$temp_index"
|
||||||
echo "" >> "$temp_index"
|
echo "" >> "$temp_index"
|
||||||
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | sort | while read -r entry; do
|
|
||||||
|
sort_args=""
|
||||||
|
# If this is the posts dir reverse
|
||||||
|
if [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; then
|
||||||
|
sort_args="-r"
|
||||||
|
fi
|
||||||
|
|
||||||
|
find "$dir" ! -name "$(basename "$dir")" -prune ! -name ".*" -print | LC_ALL=C sort $sort_args | while read -r entry; do
|
||||||
name="${entry##*/}"
|
name="${entry##*/}"
|
||||||
case "$name" in
|
case "$name" in
|
||||||
template.html|site.conf|style.css|index.md) continue ;;
|
template.html|site.conf|style.css|index.md) continue ;;
|
||||||
@@ -503,12 +737,40 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
|
|||||||
if [ -d "$entry" ]; then
|
if [ -d "$entry" ]; then
|
||||||
echo "- [${name}/](${name}/index.html)" >> "$temp_index"
|
echo "- [${name}/](${name}/index.html)" >> "$temp_index"
|
||||||
elif [ "${entry%.md}" != "$entry" ]; then
|
elif [ "${entry%.md}" != "$entry" ]; then
|
||||||
echo "- [${name%.md}](${name%.md}.html)" >> "$temp_index"
|
label="${name%.md}"
|
||||||
|
|
||||||
|
# Try to get first heading
|
||||||
|
post_h=$(grep -m 1 '^# ' "$entry" | sed 's/^# *//')
|
||||||
|
if [ -n "$post_h" ]; then
|
||||||
|
post_h=$(echo "$post_h" | sed -e 's/\[//g' -e 's/\]//g' -e 's/!//g' -e 's/\*//g' -e 's/_//g' -e 's/`//g' -e 's/([^)]*)//g' | sed 's/\\//g')
|
||||||
|
|
||||||
|
if [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; then
|
||||||
|
# For posts add date and time
|
||||||
|
p_date=$(echo "${name%.md}" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
p_time="00:00"
|
||||||
|
if echo "${name%.md}" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
p_time=$(echo "${name%.md}" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
label="$post_h - $p_date $p_time"
|
||||||
|
else
|
||||||
|
label="$post_h"
|
||||||
|
fi
|
||||||
|
elif [ "$rel_dir" = "$posts_dir" ] || [ "./$rel_dir" = "$posts_dir" ]; then
|
||||||
|
# No heading and date and time for posts
|
||||||
|
p_date=$(echo "${name%.md}" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
p_time="00:00"
|
||||||
|
if echo "${name%.md}" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
p_time=$(echo "${name%.md}" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
label="$p_date $p_time"
|
||||||
|
fi
|
||||||
|
echo "- [$label](${name%.md}.html)" >> "$temp_index"
|
||||||
else
|
else
|
||||||
echo "- [$name]($name)" >> "$temp_index"
|
echo "- [$name]($name)" >> "$temp_index"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
render_markdown "$temp_index" > "$out_dir/index.html"
|
is_home="false"; [ "$dir" = "$src" ] && is_home="true"
|
||||||
|
render_markdown "$temp_index" "$is_home" > "$out_dir/index.html"
|
||||||
rm "$temp_index"
|
rm "$temp_index"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -518,7 +780,7 @@ if [ ! -f "$out/styles.css" ] && [ -f "$script_dir/styles/$style.css" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do
|
eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while IFS= read -r file; do
|
||||||
rel_path="${file#$src}"
|
rel_path="${file#"$src"}"
|
||||||
rel_path="${rel_path#/}"
|
rel_path="${rel_path#/}"
|
||||||
dir_rel=$(dirname "$rel_path")
|
dir_rel=$(dirname "$rel_path")
|
||||||
out_dir="$out/$dir_rel"
|
out_dir="$out/$dir_rel"
|
||||||
@@ -538,12 +800,108 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type f -print" | sort | while
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ]; then
|
if [ "${file%.md}" != "$file" ] && [ "$is_preserved" -eq 0 ]; then
|
||||||
|
is_home="false"; [ "$file" = "$src/index.md" ] && is_home="true"
|
||||||
out_file="$out/${rel_path%.md}.html"
|
out_file="$out/${rel_path%.md}.html"
|
||||||
render_markdown "$file" > "$out_file"
|
render_markdown "$file" "$is_home" > "$out_file"
|
||||||
else
|
else
|
||||||
cp "$file" "$out/$rel_path"
|
cp "$file" "$out/$rel_path"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [ -n "$error_page" ] && [ ! -f "$out/$error_page" ]; then
|
||||||
|
temp_404="$KEWT_TMPDIR/404_gen.md"
|
||||||
|
echo "# 404 - Not Found" > "$temp_404"
|
||||||
|
echo "" >> "$temp_404"
|
||||||
|
echo "The requested page could not be found." >> "$temp_404"
|
||||||
|
render_markdown "$temp_404" > "$out/$error_page"
|
||||||
|
rm -f "$temp_404"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$base_url" ]; then
|
||||||
|
sitemap_file="$out/sitemap.xml"
|
||||||
|
base_url="${base_url%/}"
|
||||||
|
today=$(date +%Y-%m-%d)
|
||||||
|
|
||||||
|
printf '<?xml version="1.0" encoding="UTF-8"?>\n' > "$sitemap_file"
|
||||||
|
printf '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n' >> "$sitemap_file"
|
||||||
|
|
||||||
|
find "$out" -type f -name "*.html" -print | sort | while IFS= read -r html_file; do
|
||||||
|
rel_url="${html_file#"$out"}"
|
||||||
|
|
||||||
|
# Don't include 404 in the sitemap (duh)
|
||||||
|
[ "${rel_url#/}" = "$error_page" ] && continue
|
||||||
|
|
||||||
|
printf ' <url>\n' >> "$sitemap_file"
|
||||||
|
printf ' <loc>%s%s</loc>\n' "$base_url" "$rel_url" >> "$sitemap_file"
|
||||||
|
printf ' <lastmod>%s</lastmod>\n' "$today" >> "$sitemap_file"
|
||||||
|
printf ' </url>\n' >> "$sitemap_file"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '</urlset>\n' >> "$sitemap_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$generate_feed" = "true" ] && [ -n "$base_url" ]; then
|
||||||
|
feed_path="$out/$feed_file"
|
||||||
|
base_url_feed="${base_url%/}"
|
||||||
|
build_date=$(date -u '+%a, %d %b %Y %H:%M:%S +0000')
|
||||||
|
|
||||||
|
printf '<?xml version="1.0" encoding="UTF-8"?>\n' > "$feed_path"
|
||||||
|
printf '<rss version="2.0">\n' >> "$feed_path"
|
||||||
|
printf ' <channel>\n' >> "$feed_path"
|
||||||
|
printf ' <title>%s</title>\n' "$title" >> "$feed_path"
|
||||||
|
printf ' <link>%s</link>\n' "$base_url_feed" >> "$feed_path"
|
||||||
|
printf ' <description>%s</description>\n' "$title" >> "$feed_path"
|
||||||
|
printf ' <lastBuildDate>%s</lastBuildDate>\n' "$build_date" >> "$feed_path"
|
||||||
|
|
||||||
|
find "$src" -type f -name '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]*.md' -print | LC_ALL=C sort -r | while IFS= read -r post_file; do
|
||||||
|
post_basename=$(basename "$post_file" .md)
|
||||||
|
# Extract YYYY-MM-DD
|
||||||
|
post_date=$(echo "$post_basename" | sed 's/^\([0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}\).*/\1/')
|
||||||
|
|
||||||
|
# Extract HH:MM if present (e.g., 2026-03-17-10:30 or 2026-03-17-10:30_1)
|
||||||
|
post_time="00:00"
|
||||||
|
if echo "$post_basename" | grep -q '^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}'; then
|
||||||
|
post_time=$(echo "$post_basename" | sed 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-\([0-9]\{2\}[:\-][0-9]\{2\}\).*/\1/' | tr '-' ':')
|
||||||
|
fi
|
||||||
|
|
||||||
|
post_slug=$(echo "$post_basename" | sed -e 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{2\}[:\-][0-9]\{2\}//' -e 's/^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}//' -e 's/^[_\-]//')
|
||||||
|
|
||||||
|
post_heading=$(grep -m 1 '^# ' "$post_file" | sed 's/^# *//')
|
||||||
|
if [ -z "$post_heading" ]; then
|
||||||
|
if [ -n "$post_slug" ] && ! echo "$post_slug" | grep -q '^[0-9]\+$'; then
|
||||||
|
post_heading=$(echo "$post_slug" | sed 's/-/ /g' | awk '{for(i=1;i<=NF;i++) $i=toupper(substr($i,1,1)) substr($i,2)}1')
|
||||||
|
else
|
||||||
|
post_heading="Post"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
post_heading=$(echo "$post_heading" | sed -e 's/\[//g' -e 's/\]//g' -e 's/!//g' -e 's/\*//g' -e 's/_//g' -e 's/`//g' -e 's/([^)]*)//g' | sed 's/\\//g')
|
||||||
|
post_title="$post_heading - $post_date $post_time"
|
||||||
|
|
||||||
|
rel_path="${post_file#"$src"}"
|
||||||
|
rel_path="${rel_path#/}"
|
||||||
|
post_url="$base_url_feed/${rel_path%.md}.html"
|
||||||
|
|
||||||
|
pub_year=$(echo "$post_date" | cut -d- -f1)
|
||||||
|
pub_month=$(echo "$post_date" | cut -d- -f2)
|
||||||
|
pub_day=$(echo "$post_date" | cut -d- -f3)
|
||||||
|
case "$pub_month" in
|
||||||
|
01) pub_mon="Jan" ;; 02) pub_mon="Feb" ;; 03) pub_mon="Mar" ;;
|
||||||
|
04) pub_mon="Apr" ;; 05) pub_mon="May" ;; 06) pub_mon="Jun" ;;
|
||||||
|
07) pub_mon="Jul" ;; 08) pub_mon="Aug" ;; 09) pub_mon="Sep" ;;
|
||||||
|
10) pub_mon="Oct" ;; 11) pub_mon="Nov" ;; 12) pub_mon="Dec" ;;
|
||||||
|
esac
|
||||||
|
pub_date="${pub_day} ${pub_mon} ${pub_year} ${post_time}:00 +0000"
|
||||||
|
|
||||||
|
printf ' <item>\n' >> "$feed_path"
|
||||||
|
printf ' <title>%s</title>\n' "$post_title" >> "$feed_path"
|
||||||
|
printf ' <link>%s</link>\n' "$post_url" >> "$feed_path"
|
||||||
|
printf ' <guid>%s</guid>\n' "$post_url" >> "$feed_path"
|
||||||
|
printf ' <pubDate>%s</pubDate>\n' "$pub_date" >> "$feed_path"
|
||||||
|
printf ' </item>\n' >> "$feed_path"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf ' </channel>\n' >> "$feed_path"
|
||||||
|
printf '</rss>\n' >> "$feed_path"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Build complete."
|
echo "Build complete."
|
||||||
|
|||||||
10
markdown.sh
10
markdown.sh
@@ -1,16 +1,18 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
script_dir=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd)
|
||||||
awk_dir="$script_dir/awk"
|
awk_dir="$script_dir/awk"
|
||||||
|
|
||||||
sed_inplace() {
|
sed_inplace() {
|
||||||
script="$1"
|
script="$1"
|
||||||
file="$2"
|
file="$2"
|
||||||
tmp="${file}.tmp.$$"
|
tmp="${file}.tmp.$$"
|
||||||
sed "$script" "$file" > "$tmp" && mv "$tmp" "$file" || {
|
if sed "$script" "$file" > "$tmp" && mv "$tmp" "$file"; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
return 1
|
return 1
|
||||||
}
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
temp_file="/tmp/markdown.$$.md"
|
temp_file="/tmp/markdown.$$.md"
|
||||||
@@ -49,7 +51,7 @@ awk -f "$awk_dir/blockquote_to_admonition.awk" "$temp_file" > "$temp_file.tmp" &
|
|||||||
awk -f "$awk_dir/fenced_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/fenced_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -f "$awk_dir/indented_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/indented_code.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -f "$awk_dir/pipe_tables.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/pipe_tables.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -f "$awk_dir/headers.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -v enable_header_links="$ENABLE_HEADER_LINKS" -f "$awk_dir/headers.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
awk -f "$awk_dir/lists.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
awk -f "$awk_dir/lists.awk" "$temp_file" > "$temp_file.tmp" && mv "$temp_file.tmp" "$temp_file"
|
||||||
|
|
||||||
# Spacing
|
# Spacing
|
||||||
|
|||||||
29
packaging/AUR/PKGBUILD.git
Normal file
29
packaging/AUR/PKGBUILD.git
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Maintainer: n0va <n0va@krzak.org>
|
||||||
|
pkgname=kewt-git
|
||||||
|
pkgver=r0.0000000
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="A minimalist, 100% POSIX, static site generator inspired by werc and kew"
|
||||||
|
arch=('any')
|
||||||
|
url="https://git.krzak.org/N0VA/kewt"
|
||||||
|
license=('MIT')
|
||||||
|
makedepends=('git')
|
||||||
|
depends=('sh')
|
||||||
|
provides=('kewt')
|
||||||
|
conflicts=('kewt' 'kewt-bin')
|
||||||
|
source=("${pkgname}::git+${url}.git")
|
||||||
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
|
pkgver() {
|
||||||
|
cd "${pkgname}"
|
||||||
|
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "${pkgname}"
|
||||||
|
sh tools/build-standalone.sh
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "${pkgname}"
|
||||||
|
install -Dm755 kewt "${pkgdir}/usr/bin/kewt"
|
||||||
|
}
|
||||||
21
packaging/AUR/PKGBUILD.template
Normal file
21
packaging/AUR/PKGBUILD.template
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Maintainer: n0va <n0va@krzak.org>
|
||||||
|
pkgname=kewt-bin
|
||||||
|
pkgver=VERSION_PLACEHOLDER
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="A minimalist, 100% POSIX, static site generator inspired by werc and kew"
|
||||||
|
arch=('any')
|
||||||
|
url="https://git.krzak.org/N0VA/kewt"
|
||||||
|
license=('MIT')
|
||||||
|
depends=('sh')
|
||||||
|
provides=('kewt')
|
||||||
|
conflicts=('kewt' 'kewt-git')
|
||||||
|
source=("${pkgname}-${pkgver}.sh::${url}/releases/download/v${pkgver}/kewt")
|
||||||
|
sha256sums=('SHA256SUM_PLACEHOLDER')
|
||||||
|
|
||||||
|
build() {
|
||||||
|
chmod +x "${srcdir}/${pkgname}-${pkgver}.sh"
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
install -Dm755 "${srcdir}/${pkgname}-${pkgver}.sh" "${pkgdir}/usr/bin/kewt"
|
||||||
|
}
|
||||||
16
site.conf
16
site.conf
@@ -1,16 +0,0 @@
|
|||||||
title = "kewt"
|
|
||||||
style = "kewt"
|
|
||||||
dir_indexes = true
|
|
||||||
single_file_index = true
|
|
||||||
flatten = false
|
|
||||||
footer = "made with <a href="https://kewt.krzak.org">kewt</a>"
|
|
||||||
logo = ""
|
|
||||||
display_logo = false
|
|
||||||
display_title = true
|
|
||||||
logo_as_favicon = true
|
|
||||||
favicon = ""
|
|
||||||
order = ""
|
|
||||||
home_name = "Home"
|
|
||||||
show_home_in_nav = true
|
|
||||||
nav_links = ""
|
|
||||||
nav_extra = ""
|
|
||||||
@@ -19,6 +19,20 @@ It's meant to be a static site generator, like _[kew](https://github.com/uint23/
|
|||||||
|
|
||||||
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
If you want to **force** a file to be inlined, use `\!![]` instead of `\![]`
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
You can clone the repository to use `kewt.sh` directly, or you can download the standalone executable, which bundles all dependencies into a single file:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -L -o kewt https://git.krzak.org/N0VA/kewt/releases/latest/download/kewt
|
||||||
|
chmod +x kewt
|
||||||
|
```
|
||||||
|
|
||||||
|
On Arch Linux, _kewt_ is available on the AUR:
|
||||||
|
|
||||||
|
- [kewt-bin](https://aur.archlinux.org/packages/kewt-bin) — prebuilt standalone binary from the latest release
|
||||||
|
- [kewt-git](https://aur.archlinux.org/packages/kewt-git) — built from the latest git source
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -49,6 +63,9 @@ display_logo = false
|
|||||||
display_title = true
|
display_title = true
|
||||||
logo_as_favicon = true
|
logo_as_favicon = true
|
||||||
favicon = ""
|
favicon = ""
|
||||||
|
generate_page_title = true
|
||||||
|
error_page = "not_found.html"
|
||||||
|
versioning = false
|
||||||
```
|
```
|
||||||
|
|
||||||
- `title` site title
|
- `title` site title
|
||||||
@@ -67,6 +84,9 @@ favicon = ""
|
|||||||
- `display_title` show title text in header
|
- `display_title` show title text in header
|
||||||
- `logo_as_favicon` use `logo` as favicon
|
- `logo_as_favicon` use `logo` as favicon
|
||||||
- `favicon` explicit favicon path (used when `logo_as_favicon` is false or no logo is set)
|
- `favicon` explicit favicon path (used when `logo_as_favicon` is false or no logo is set)
|
||||||
|
- `generate_page_title` automatically generate title text from the first markdown heading or filename (default: true)
|
||||||
|
- `error_page` filename for the generated 404 error page (default: "not_found.html", empty to disable)
|
||||||
|
- `versioning` append a version query parameter (`?v=timestamp`) to css asset urls to bypass cache (default: false)
|
||||||
|
|
||||||
## Ignores
|
## Ignores
|
||||||
|
|
||||||
@@ -86,7 +106,6 @@ favicon = ""
|
|||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
- Markdown to html conversion based on [markdown.bash](https://github.com/chadbraunduin/markdown.bash) by [chadbraunduin](https://github.com/chadbraunduin)
|
|
||||||
- Default css style and html template based on _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
- Default css style and html template based on _[kew](https://github.com/uint23/kew)_ by [uint23](https://github.com/uint23)
|
||||||
|
|
||||||
>![WARNING]
|
>![WARNING]
|
||||||
|
|||||||
@@ -10,3 +10,12 @@ display_title = true
|
|||||||
logo_as_favicon = true
|
logo_as_favicon = true
|
||||||
favicon = ""
|
favicon = ""
|
||||||
order = ""
|
order = ""
|
||||||
|
home_name = "Home"
|
||||||
|
show_home_in_nav = true
|
||||||
|
nav_links = ""
|
||||||
|
nav_extra = ""
|
||||||
|
generate_page_title = true
|
||||||
|
error_page = "not_found.html"
|
||||||
|
versioning = false
|
||||||
|
enable_header_links = true
|
||||||
|
base_url = "https://kewt.krzak.org"
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<title>{{TITLE}}</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="{{CSS}}" type="text/css" />
|
|
||||||
{{HEAD_EXTRA}}
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<header>
|
|
||||||
<h1>{{HEADER_BRAND}}</h1>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<nav id="side-bar">{{NAV}}</nav>
|
|
||||||
|
|
||||||
<article>{{CONTENT}}</article>
|
|
||||||
<footer>{{FOOTER}}</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
33
tools/build-standalone.sh
Executable file
33
tools/build-standalone.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
|
||||||
|
if [ ! -f "$REPO_ROOT/kewt.sh" ]; then
|
||||||
|
echo "kewt.sh not found. Run from the repository root or tools/."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
OUT_FILE="$REPO_ROOT/kewt"
|
||||||
|
|
||||||
|
cat << 'EOF' > "$OUT_FILE"
|
||||||
|
#!/bin/sh
|
||||||
|
tmpdir=$(mktemp -d "/tmp/kewt.XXXXXX")
|
||||||
|
trap 'rm -rf "$tmpdir"' EXIT HUP INT TERM
|
||||||
|
|
||||||
|
# Extract payload
|
||||||
|
sed '1,/^#==PAYLOAD==$/d' "$0" | tar -xz -C "$tmpdir"
|
||||||
|
|
||||||
|
# Pass control to the extracted script
|
||||||
|
KEWT_INVOKED_AS="$0" "$tmpdir/kewt.sh" "$@"
|
||||||
|
exit $?
|
||||||
|
|
||||||
|
#==PAYLOAD==
|
||||||
|
EOF
|
||||||
|
|
||||||
|
tar -cz -C "$REPO_ROOT" kewt.sh markdown.sh awk styles >> "$OUT_FILE"
|
||||||
|
|
||||||
|
chmod +x "$OUT_FILE"
|
||||||
|
|
||||||
|
echo "Generated standalone executable at $OUT_FILE"
|
||||||
Reference in New Issue
Block a user