13 Commits

Author SHA1 Message Date
64d08a0de3 functionality: BetterHelp
All checks were successful
Lint / shellcheck (push) Successful in 19s
Release Standalone Builder / build (release) Successful in 32s
Release Standalone Builder / publish-aur (release) Successful in 35s
2026-03-16 10:33:21 +01:00
dd18bc3367 docs: update readme
All checks were successful
Lint / shellcheck (push) Successful in 39s
2026-03-16 09:33:45 +01:00
f89661c1a5 standalone version tmp spam fix
All checks were successful
Lint / shellcheck (push) Successful in 19s
Release Standalone Builder / build (release) Successful in 31s
Release Standalone Builder / publish-aur (release) Successful in 33s
2026-03-11 13:57:10 +01:00
e0a3b66fa9 docs: AUR
All checks were successful
Lint / shellcheck (push) Successful in 16s
2026-03-11 08:44:37 +01:00
4e6c9dbeb5 kewt-git AUR 2026-03-11 08:43:19 +01:00
af453ca2ec aur now workie pretty please
All checks were successful
Lint / shellcheck (push) Successful in 16s
Release Standalone Builder / build (release) Successful in 33s
Release Standalone Builder / publish-aur (release) Successful in 1m7s
2026-03-11 08:36:22 +01:00
cea84de242 Hopefully AUR fixed
Some checks failed
Lint / shellcheck (push) Successful in 17s
Release Standalone Builder / build (release) Successful in 31s
Release Standalone Builder / publish-aur (release) Failing after 23s
2026-03-11 08:31:00 +01:00
ad1ec9c2e3 Auto GitHub
Some checks failed
Lint / shellcheck (push) Successful in 18s
Release Standalone Builder / build (release) Successful in 31s
Release Standalone Builder / publish-aur (release) Failing after 43s
2026-03-11 08:26:36 +01:00
fa3b5592da AUR
All checks were successful
Lint / shellcheck (push) Successful in 17s
2026-03-11 08:21:12 +01:00
722d687afe docs: add standalone download instructions
All checks were successful
Lint / shellcheck (push) Successful in 15s
2026-03-11 08:07:17 +01:00
77c0b29b4c fix gitea actions missing go
All checks were successful
Lint / shellcheck (push) Successful in 16s
Release Standalone Builder / build (release) Successful in 1m8s
2026-03-11 08:01:26 +01:00
9ae965662c Actions! Work!
Some checks failed
Lint / shellcheck (push) Successful in 48s
Release Standalone Builder / build (release) Failing after 6s
2026-03-11 07:51:31 +01:00
9ced2af562 Actions
Some checks failed
Lint / shellcheck (push) Has been cancelled
2026-03-11 07:42:45 +01:00
10 changed files with 276 additions and 30 deletions

16
.gitea/workflows/lint.yml Normal file
View 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

View 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
View File

@@ -1 +1,2 @@
out/ out/
kewt

View File

@@ -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
@@ -86,7 +100,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]

59
kewt.sh
View File

@@ -6,11 +6,12 @@ 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 --help
Options: Options:
--help Show this help message. --help Show this help message.
@@ -20,9 +21,12 @@ Options:
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'
@@ -93,10 +97,7 @@ create_new_site() {
exit 0 exit 0
} }
generate_nav() {
dinfo=$(find "$1" \( -name ".*" ! -name "." ! -name ".." -prune \) -o -print | sort | awk -v src="$1" -f "$awk_dir/collect_dir_info.awk")
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"
}
src="" src=""
out="" out=""
@@ -133,9 +134,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 +155,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 +185,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 +215,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,14 +245,14 @@ 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")
@@ -333,6 +341,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
@@ -418,7 +427,7 @@ render_markdown() {
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" ;;
@@ -466,7 +475,7 @@ render_markdown() {
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"
@@ -490,7 +499,7 @@ eval "find \"$src\" \( $IGNORE_ARGS \) -prune -o -type d -print" | sort | while
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"
@@ -518,7 +527,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"

View File

@@ -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"

View 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"
}

View 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"
}

View File

@@ -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
@@ -86,7 +100,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]

33
tools/build-standalone.sh Executable file
View 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"