mirror of
https://git.mills.io/prologic/zs
synced 2024-11-24 06:16:32 +03:00
Refactor a bunch of stuff adding zs serve, CI/CD workflows, fixing docs and license (#10)
Fixes #5 Fixes #8 Fixes #9 Co-authored-by: James Mills <1290234+prologic@users.noreply.github.com> Reviewed-on: https://git.mills.io/prologic/zs/pulls/10
This commit is contained in:
parent
0f4623afae
commit
339de0457c
9
.dockerfiles/entrypoint.sh
Executable file
9
.dockerfiles/entrypoint.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
[ -n "${PUID}" ] && usermod -u "${PUID}" zs
|
||||
[ -n "${PGID}" ] && groupmod -g "${PGID}" zs
|
||||
|
||||
printf "Configuring zs...\n"
|
||||
|
||||
printf "Switching UID=%s and GID=%s\n" "${PUID}" "${PGID}"
|
||||
exec su-exec zs:zs "$@"
|
81
.drone.yml
Normal file
81
.drone.yml
Normal file
@ -0,0 +1,81 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: exec
|
||||
name: 🚀 CI
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- name: 🛠️ Build
|
||||
commands:
|
||||
- make build
|
||||
|
||||
- name: 🧪 Test
|
||||
commands:
|
||||
- make test
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
- tag
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: 🐳 Docker
|
||||
|
||||
steps:
|
||||
- name: 📦 Image
|
||||
image: plugins/kaniko
|
||||
settings:
|
||||
repo: prologic/saltyim
|
||||
tags: latest
|
||||
build_args:
|
||||
- VERSION=latest
|
||||
- COMMIT=${DRONE_COMMIT_SHA:0:8}
|
||||
username:
|
||||
from_secret: dockerhub_username
|
||||
password:
|
||||
from_secret: dockerhub_password
|
||||
when:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
- push
|
||||
|
||||
depends_on:
|
||||
- 🚀 CI
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
- push
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: 🥳 Done
|
||||
|
||||
steps:
|
||||
- name: 🔔 Notify
|
||||
image: plugins/webhook
|
||||
settings:
|
||||
urls:
|
||||
- https://msgbus.mills.io/ci.mills.io
|
||||
|
||||
depends_on:
|
||||
- 🚀 CI
|
||||
- 🐳 Docker
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
- tag
|
||||
- push
|
||||
- pull_request
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -4,4 +4,4 @@
|
||||
|
||||
/zs
|
||||
/dist
|
||||
/test.md
|
||||
**/.DS_Store
|
||||
|
60
Dockerfile
Normal file
60
Dockerfile
Normal file
@ -0,0 +1,60 @@
|
||||
# Build
|
||||
FROM golang:alpine AS build
|
||||
|
||||
RUN apk add --no-cache -U build-base git
|
||||
|
||||
RUN mkdir -p /src
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
# Copy Makefile
|
||||
COPY Makefile ./
|
||||
|
||||
# Install deps
|
||||
RUN make deps
|
||||
|
||||
# Copy go.mod and go.sum and install and cache dependencies
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
|
||||
# Download dependencies
|
||||
RUN go mod download
|
||||
|
||||
# Copy sources
|
||||
COPY *.go ./
|
||||
|
||||
# Version/Commit (there there is no .git in Docker build context)
|
||||
# NOTE: This is fairly low down in the Dockerfile instructions so
|
||||
# we don't break the Docker build cache just be changing
|
||||
# unrelated files that actually haven't changed but caused the
|
||||
# COMMIT value to change.
|
||||
ARG VERSION="0.0.0"
|
||||
ARG COMMIT="HEAD"
|
||||
ARG BUILD=""
|
||||
|
||||
# Build cli binary
|
||||
RUN make cli VERSION=$VERSION COMMIT=$COMMIT BUILD=$BUILD
|
||||
|
||||
# Runtime
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk --no-cache -U add su-exec shadow
|
||||
|
||||
ENV PUID=1000
|
||||
ENV PGID=1000
|
||||
|
||||
RUN addgroup -g "${PGID}" zs && \
|
||||
adduser -D -H -G zs -h /var/empty -u "${PUID}" zs && \
|
||||
mkdir -p /data && chown -R zs:zs /data
|
||||
|
||||
VOLUME /data
|
||||
|
||||
WORKDIR /
|
||||
|
||||
COPY --from=build /src/zs /usr/local/bin/zs
|
||||
|
||||
COPY .dockerfiles/entrypoint.sh /init
|
||||
|
||||
ENTRYPOINT ["/init"]
|
||||
|
||||
CMD ["zs""]
|
36
LICENSE
36
LICENSE
@ -1,22 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (C) 2021-present James Mills
|
||||
|
||||
Copyright (c) 2014 zserge
|
||||
zs is covered by the MIT license::
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
22
LICENSE.old
Normal file
22
LICENSE.old
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 zserge
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
103
Makefile
103
Makefile
@ -1,13 +1,92 @@
|
||||
destdir ?=
|
||||
prefix ?= /usr/local
|
||||
-include environ.inc
|
||||
.PHONY: help deps dev build install image release test clean
|
||||
|
||||
build:
|
||||
go build -v
|
||||
clean:
|
||||
rm -f zs
|
||||
install:
|
||||
install -m0755 zs ${destdir}${prefix}/bin/zs
|
||||
install -m0644 zs.1 ${destdir}${prefix}/share/man/man1/zs.1
|
||||
uninstall:
|
||||
rm -f ${prefix}/bin/zs
|
||||
rm -f ${prefix}/share/man/man1/zs.1
|
||||
export CGO_ENABLED=0
|
||||
VERSION=$(shell git describe --abbrev=0 --tags 2>/dev/null || echo "$VERSION")
|
||||
COMMIT=$(shell git rev-parse --short HEAD || echo "$COMMIT")
|
||||
BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
|
||||
BUILD=$(shell git show -s --pretty=format:%cI)
|
||||
GOCMD=go
|
||||
|
||||
DESTDIR=/usr/local/bin
|
||||
|
||||
ifeq ($(LOCAL), 1)
|
||||
IMAGE := r.mills.io/prologic/zs
|
||||
TAG := dev
|
||||
else
|
||||
IMAGE := prologic/zs
|
||||
TAG := latest
|
||||
endif
|
||||
|
||||
all: help
|
||||
|
||||
help: ## Show this help message
|
||||
@echo "zs - Zen Static site generator"
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[$$()% a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||
|
||||
preflight: ## Run preflight checks to ensure you have the right build tools
|
||||
@./preflight.sh
|
||||
|
||||
deps: ## Install any required dependencies
|
||||
|
||||
dev : DEBUG=1
|
||||
dev : build ## Build debug version of zs (cli)
|
||||
@./zs -v
|
||||
|
||||
cli: ## Build the zs command-line tool
|
||||
ifeq ($(DEBUG), 1)
|
||||
@echo "Building in debug mode..."
|
||||
@$(GOCMD) build -tags "netgo static_build" -installsuffix netgo \
|
||||
-ldflags "\
|
||||
-X $(shell go list).Version=$(VERSION) \
|
||||
-X $(shell go list).Commit=$(COMMIT) \
|
||||
-X $(shell go list).Build=$(BUILD)" \
|
||||
.
|
||||
else
|
||||
@$(GOCMD) build -tags "netgo static_build" -installsuffix netgo \
|
||||
-ldflags "-w \
|
||||
-X $(shell go list).Version=$(VERSION) \
|
||||
-X $(shell go list).Commit=$(COMMIT) \
|
||||
-X $(shell go list).Build=$(BUILD)" \
|
||||
.
|
||||
endif
|
||||
|
||||
build: cli ## Build the cli
|
||||
|
||||
install: build ## Install zs (cli) to $DESTDIR
|
||||
@install -D -m 755 zs $(DESTDIR)/zs
|
||||
|
||||
ifeq ($(PUBLISH), 1)
|
||||
image: generate ## Build the Docker image
|
||||
@docker buildx build \
|
||||
--build-arg VERSION="$(VERSION)" \
|
||||
--build-arg COMMIT="$(COMMIT)" \
|
||||
--build-arg BUILD="$(BUILD)" \
|
||||
--platform linux/amd64,linux/arm64 --push -t $(IMAGE):$(TAG) .
|
||||
else
|
||||
image: generate
|
||||
@docker build \
|
||||
--build-arg VERSION="$(VERSION)" \
|
||||
--build-arg COMMIT="$(COMMIT)" \
|
||||
--build-arg BUILD="$(BUILD)" \
|
||||
-t $(IMAGE):$(TAG) .
|
||||
endif
|
||||
|
||||
release: generate ## Release a new version to Gitea
|
||||
@./tools/release.sh
|
||||
|
||||
fmt: ## Format sources files
|
||||
@$(GOCMD) fmt ./...
|
||||
|
||||
test: ## Run test suite
|
||||
@CGO_ENABLED=1 $(GOCMD) test -v -cover -race ./...
|
||||
|
||||
coverage: ## Get test coverage report
|
||||
@CGO_ENABLED=1 $(GOCMD) test -v -cover -race -cover -coverprofile=coverage.out ./...
|
||||
@$(GOCMD) tool cover -html=coverage.out
|
||||
|
||||
clean: ## Remove untracked files
|
||||
@git clean -f -d -x
|
||||
|
||||
clean-all: ## Remove untracked and Git ignored files
|
||||
@git clean -f -d -X
|
||||
|
62
README.md
62
README.md
@ -1,10 +1,8 @@
|
||||
# zs
|
||||
# zs - Zen Static site generator
|
||||
|
||||
zs is an extremely minimal static site generator written in Go.
|
||||
|
||||
It's inspired by `zas` generator, but is even more minimal.
|
||||
|
||||
The name stands for 'zen static' as well as it's my initials.
|
||||
[![Build Status](https://ci.mills.io/api/badges/prologic/zs/status.svg)](https://ci.mills.io/prologic/zs)
|
||||
|
||||
## Features
|
||||
|
||||
@ -17,9 +15,19 @@ The name stands for 'zen static' as well as it's my initials.
|
||||
|
||||
## Installation
|
||||
|
||||
Download the binaries from Github or build it manually:
|
||||
Download the binaries from [go.mills.io/prologic/zs](https://git.mills.io/prologic/zs):
|
||||
|
||||
$ go get git.mills.io/prologic/zs
|
||||
```console
|
||||
go get go.mills.io/zs@latest
|
||||
```
|
||||
|
||||
Or build from source manaully:
|
||||
|
||||
```console
|
||||
git clone https://git.mills.io/prologic/zs
|
||||
cd zs
|
||||
make install
|
||||
```
|
||||
|
||||
## Ideology
|
||||
|
||||
@ -29,13 +37,16 @@ of your blog/site.
|
||||
Keep all service files (extensions, layout pages, deployment scripts etc)
|
||||
in the `.zs` subdirectory.
|
||||
|
||||
Define variables in the header of the content files using [YAML]:
|
||||
Define variables in the header of the content files using [YAML front matter](https://assemble.io/docs/YAML-front-matter.html):
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: My web site
|
||||
keywords: best website, hello, world
|
||||
---
|
||||
|
||||
Markdown text goes after a header *separator*
|
||||
```
|
||||
|
||||
Use placeholders for variables and plugins in your markdown or html
|
||||
files, e.g. `{{ title }}` or `{{ command arg1 arg2 }}.
|
||||
@ -48,10 +59,10 @@ placeholder.
|
||||
|
||||
Every variable from the content header will be passed via environment variables like `title` becomes `$ZS_TITLE` and so on. There are some special variables:
|
||||
|
||||
* `$ZS` - a path to the `zs` executable
|
||||
* `$ZS_OUTDIR` - a path to the directory with generated files
|
||||
* `$ZS_FILE` - a path to the currently processed markdown file
|
||||
* `$ZS_URL` - a URL for the currently generated page
|
||||
- `$ZS` - a path to the `zs` executable
|
||||
- `$ZS_OUTDIR` - a path to the directory with generated files
|
||||
- `$ZS_FILE` - a path to the currently processed markdown file
|
||||
- `$ZS_URL` - a URL for the currently generated page
|
||||
|
||||
## Example of RSS generation
|
||||
|
||||
@ -81,26 +92,29 @@ done | sort -r -n | cut -d' ' -f2-
|
||||
|
||||
There are two special plugin names that are executed every time the build
|
||||
happens - `prehook` and `posthook`. You can define some global actions here like
|
||||
content generation, or additional commands, like LESS to CSS conversion:
|
||||
|
||||
# .zs/post
|
||||
content generation, or additional commands, like to minify CSS or Javascript files.
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
lessc < $ZS_OUTDIR/styles.less > $ZS_OUTDIR/styles.css
|
||||
rm -f $ZS_OUTDIR/styles.css
|
||||
|
||||
set -e
|
||||
|
||||
minify -o "$ZS_OUTDIR/css/fa.min.css" "$ZS_OUTDIR/css/fa.css"
|
||||
minify -o "$ZS_OUTDIR/css/site.min.css" "$ZS_OUTDIR/css/site.css"
|
||||
|
||||
rm -rf "$ZS_OUTDIR/css/fa.css"
|
||||
rm -rf "$ZS_OUTDIR/css/screen.css"
|
||||
```
|
||||
|
||||
## Command line usage
|
||||
|
||||
`zs build` re-builds your site.
|
||||
|
||||
`zs build <file>` re-builds one file and prints resulting content to stdout.
|
||||
|
||||
`zs watch` rebuilds your site every time you modify any file.
|
||||
|
||||
`zs var <filename> [var1 var2...]` prints a list of variables defined in the
|
||||
- `zs build` re-builds your site.
|
||||
- `zs build <file>` re-builds one file and prints resulting content to stdout.
|
||||
- `zs watch` rebuilds your site every time you modify any file.
|
||||
- `zs var <filename> [var1 var2...]` prints a list of variables defined in the
|
||||
header of a given markdown file, or the values of certain variables (even if
|
||||
it's an empty string).
|
||||
|
||||
## License
|
||||
|
||||
The software is distributed under the MIT license.
|
||||
`zs` is licensed under the terms of the [MIT License](/LICENSE) and was orignally forked from [zserge/zs](https://github.com/zserge/zs) also licensed under the terms of the [MIT License](/LICENSE.old).
|
||||
|
12
go.mod
12
go.mod
@ -1,8 +1,18 @@
|
||||
module git.mills.io/prologic/zs
|
||||
module go.mills.io/zs
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0
|
||||
go.mills.io/static v0.0.0-20230312034046-6dff09caed3b
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
|
||||
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/unrolled/logger v0.0.0-20201216141554-31a3694fe979 // indirect
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect
|
||||
)
|
||||
|
27
go.sum
27
go.sum
@ -1,6 +1,33 @@
|
||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/unrolled/logger v0.0.0-20201216141554-31a3694fe979 h1:47+K4wN0S8L3fUwgZtPEBIfNqtAE3tUvBfvHVZJAXfg=
|
||||
github.com/unrolled/logger v0.0.0-20201216141554-31a3694fe979/go.mod h1:X5DBNY1yIVkuLwJP3BXlCoQCa5mGg7hPJPIA0Blwc44=
|
||||
go.mills.io/static v0.0.0-20230312034046-6dff09caed3b h1:9mSSHQJztO83b4939B31Z8bCOlvQUei6bRhnJq8eRC0=
|
||||
go.mills.io/static v0.0.0-20230312034046-6dff09caed3b/go.mod h1:TmaEDwM+IgrCRyMxtVWtmSdoxLP3N6ehBa7AiOZj2Mk=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
121
main.go
121
main.go
@ -1,29 +1,53 @@
|
||||
// Package main is a command-lint tool `zs` called Zen Static for generating static websites
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/russross/blackfriday/v2"
|
||||
"go.mills.io/static"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
// ZSDIR is the default directory for storing layouts and extensions
|
||||
ZSDIR = ".zs"
|
||||
|
||||
// PUBDIR is the default directory for publishing final built content
|
||||
PUBDIR = ".pub"
|
||||
)
|
||||
|
||||
// Vars holds a map of global variables
|
||||
type Vars map[string]string
|
||||
|
||||
// NewTicker is a function that wraps a time.Ticker and ticks immediately instead of waiting for the first interval
|
||||
func NewTicker(d time.Duration) *time.Ticker {
|
||||
ticker := time.NewTicker(d)
|
||||
oc := ticker.C
|
||||
nc := make(chan time.Time, 1)
|
||||
go func() {
|
||||
nc <- time.Now()
|
||||
for tm := range oc {
|
||||
nc <- tm
|
||||
}
|
||||
}()
|
||||
ticker.C = nc
|
||||
return ticker
|
||||
}
|
||||
|
||||
// renameExt renames extension (if any) from oldext to newext
|
||||
// If oldext is an empty string - extension is extracted automatically.
|
||||
// If path has no extension - new extension is appended
|
||||
@ -113,46 +137,50 @@ func getVars(path string, globals Vars) (Vars, string, error) {
|
||||
}
|
||||
|
||||
delim := "\n---\n"
|
||||
if sep := strings.Index(s, delim); sep == -1 {
|
||||
sep := strings.Index(s, delim)
|
||||
if sep == -1 {
|
||||
return v, s, nil
|
||||
} else {
|
||||
}
|
||||
|
||||
header := s[:sep]
|
||||
body := s[sep+len(delim):]
|
||||
|
||||
vars := Vars{}
|
||||
if err := yaml.Unmarshal([]byte(header), &vars); err != nil {
|
||||
fmt.Println("ERROR: failed to parse header", err)
|
||||
return nil, "", err
|
||||
} else {
|
||||
fmt.Println("WARN: failed to parse header", err)
|
||||
return v, s, nil
|
||||
}
|
||||
// Override default values + globals with the ones defines in the file
|
||||
for key, value := range vars {
|
||||
v[key] = value
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(v["url"], "./") {
|
||||
v["url"] = v["url"][2:]
|
||||
}
|
||||
return v, body, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Render expanding zs plugins and variables
|
||||
func render(s string, vars Vars) (string, error) {
|
||||
delim_open := "{{"
|
||||
delim_close := "}}"
|
||||
openingDelimiter := "{{"
|
||||
closingDelimiter := "}}"
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
for {
|
||||
if from := strings.Index(s, delim_open); from == -1 {
|
||||
from := strings.Index(s, openingDelimiter)
|
||||
if from == -1 {
|
||||
out.WriteString(s)
|
||||
return out.String(), nil
|
||||
} else {
|
||||
if to := strings.Index(s, delim_close); to == -1 {
|
||||
}
|
||||
|
||||
to := strings.Index(s, closingDelimiter)
|
||||
if to == -1 {
|
||||
return "", fmt.Errorf("Close delim not found")
|
||||
} else {
|
||||
}
|
||||
|
||||
out.WriteString(s[:from])
|
||||
cmd := s[from+len(delim_open) : to]
|
||||
s = s[to+len(delim_close):]
|
||||
cmd := s[from+len(openingDelimiter) : to]
|
||||
s = s[to+len(closingDelimiter):]
|
||||
m := strings.Fields(cmd)
|
||||
if len(m) == 1 {
|
||||
if v, ok := vars[m[0]]; ok {
|
||||
@ -166,8 +194,6 @@ func render(s string, vars Vars) (string, error) {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -228,13 +254,13 @@ func buildRaw(path string, w io.Writer) error {
|
||||
}
|
||||
defer in.Close()
|
||||
if w == nil {
|
||||
if out, err := os.Create(filepath.Join(PUBDIR, path)); err != nil {
|
||||
out, err := os.Create(filepath.Join(PUBDIR, path))
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
}
|
||||
defer out.Close()
|
||||
w = out
|
||||
}
|
||||
}
|
||||
_, err = io.Copy(w, in)
|
||||
return err
|
||||
}
|
||||
@ -250,12 +276,19 @@ func build(path string, w io.Writer, vars Vars) error {
|
||||
}
|
||||
}
|
||||
|
||||
func buildAll(watch bool) {
|
||||
func buildAll(ctx context.Context, watch bool) {
|
||||
ticker := NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
lastModified := time.Unix(0, 0)
|
||||
modified := false
|
||||
|
||||
vars := globals()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
os.Mkdir(PUBDIR, 0755)
|
||||
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
|
||||
// ignore hidden files and directories
|
||||
@ -288,12 +321,34 @@ func buildAll(watch bool) {
|
||||
modified = false
|
||||
}
|
||||
if !watch {
|
||||
break
|
||||
return
|
||||
}
|
||||
lastModified = time.Now()
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// serve runs a static web server and builds and continuously watches for changes to rebuild
|
||||
func serve(ctx context.Context, bind string) error {
|
||||
os.Mkdir(PUBDIR, 0755)
|
||||
|
||||
svr, err := static.NewServer(
|
||||
static.WithBind(bind),
|
||||
static.WithDir(true),
|
||||
static.WithRoot(PUBDIR),
|
||||
static.WithSPA(true),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go svr.Run(ctx)
|
||||
go buildAll(ctx, true)
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
// prepend .zs to $PATH, so plugins will be found before OS commands
|
||||
@ -305,15 +360,21 @@ func init() {
|
||||
|
||||
func main() {
|
||||
if len(os.Args) == 1 {
|
||||
fmt.Println(os.Args[0], "<command> [args]")
|
||||
fmt.Printf("%s <command> [args]\n", filepath.Base(os.Args[0]))
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
cmd := os.Args[1]
|
||||
args := os.Args[2:]
|
||||
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
switch cmd {
|
||||
case "build":
|
||||
if len(args) == 0 {
|
||||
buildAll(false)
|
||||
buildAll(ctx, false)
|
||||
} else if len(args) == 1 {
|
||||
if err := build(args[0], os.Stdout, globals()); err != nil {
|
||||
fmt.Println("ERROR: " + err.Error())
|
||||
@ -322,7 +383,15 @@ func main() {
|
||||
fmt.Println("ERROR: too many arguments")
|
||||
}
|
||||
case "watch":
|
||||
buildAll(true)
|
||||
buildAll(ctx, true)
|
||||
case "serve":
|
||||
bind := ":8000"
|
||||
if len(args) > 1 {
|
||||
bind = args[0]
|
||||
}
|
||||
if err := serve(ctx, bind); err != nil {
|
||||
fmt.Println("ERROR: " + err.Error())
|
||||
}
|
||||
case "var":
|
||||
if len(args) == 0 {
|
||||
fmt.Println("var: filename expected")
|
||||
|
145
preflight.sh
Executable file
145
preflight.sh
Executable file
@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
set -e
|
||||
|
||||
color() {
|
||||
fg="$1"
|
||||
bg="${2}"
|
||||
ft="${3:-0}"
|
||||
|
||||
printf "\33[%s;%s;%s" "$ft" "$fg" "$bg"
|
||||
}
|
||||
|
||||
color_reset() {
|
||||
printf "\033[0m"
|
||||
}
|
||||
|
||||
ok() {
|
||||
if [ -t 1 ]; then
|
||||
printf "%s[ OK ]%s\n" "$(color 37 42m 1)" "$(color_reset)"
|
||||
else
|
||||
printf "%s\n" "[ OK ]"
|
||||
fi
|
||||
}
|
||||
|
||||
err() {
|
||||
if [ -t 1 ]; then
|
||||
printf "%s[ ERR ]%s\n" "$(color 37 41m 1)" "$(color_reset)"
|
||||
else
|
||||
printf "%s\n" "[ ERR ]"
|
||||
fi
|
||||
}
|
||||
|
||||
run() {
|
||||
retval=0
|
||||
logfile="$(mktemp -t "run-XXXXXX")"
|
||||
if "$@" 2> "$logfile"; then
|
||||
ok
|
||||
else
|
||||
retval=$?
|
||||
err
|
||||
cat "$logfile" || true
|
||||
fi
|
||||
rm -rf "$logfile"
|
||||
return $retval
|
||||
}
|
||||
|
||||
progress() {
|
||||
printf "%-40s" "$(printf "%s ... " "$1")"
|
||||
}
|
||||
|
||||
log() {
|
||||
printf "%s\n" "$1"
|
||||
}
|
||||
|
||||
log2() {
|
||||
printf "%s\n" "$1" 1>&2
|
||||
}
|
||||
|
||||
error() {
|
||||
log "ERROR: ${1}"
|
||||
}
|
||||
|
||||
fail() {
|
||||
log "FATAL: ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
check_goversion() {
|
||||
progress "Checking Go version"
|
||||
|
||||
if ! command -v go > /dev/null 2>&1; then
|
||||
log2 "Cannot find the Go compiler"
|
||||
return 1
|
||||
fi
|
||||
|
||||
gover="$(go version | grep -o -E 'go[0-9]+\.[0-9]+(\.[0-9]+)?')"
|
||||
|
||||
if ! go version | grep -E 'go1\.1[6789](\.[0-9]+)?' > /dev/null; then
|
||||
log2 "Go 1.16+ is required, found ${gover}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
check_path() {
|
||||
progress "Checking \$PATH"
|
||||
|
||||
gobin="$(eval "$(go env | grep GOBIN)")"
|
||||
gopath="$(eval "$(go env | grep GOPATH)")"
|
||||
|
||||
if [ -n "$gobin" ] && ! echo "$PATH" | grep "$gobin" > /dev/null; then
|
||||
log2 "\$GOBIN '$gobin' is not in your \$PATH"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ -n "$gopath" ] && ! echo "$PATH" | grep "$gopath/bin" > /dev/null; then
|
||||
log2 "\$GOPATH/bin '$gopath/bin' is not in your \$PATH"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! echo "$PATH" | grep "$HOME/go/bin" > /dev/null; then
|
||||
log2 "\$HOME/go/bin is not in your \$PATH"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
check_deps() {
|
||||
progress "Checking deps"
|
||||
|
||||
if ! command -v minify > /dev/null 2>&1; then
|
||||
log2 "minify not found, Try running: make deps"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! minify --help 2>&1 | grep '\-b, \-\-bundle' > /dev/null; then
|
||||
log2 "wrong version of minify found, Try running: make deps"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! command -v goi18n > /dev/null 2>&1; then
|
||||
log2 "goi18n not found, Try running: make deps"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
steps="check_goversion check_path check_deps"
|
||||
|
||||
_main() {
|
||||
for step in $steps; do
|
||||
if ! run "$step"; then
|
||||
fail "🙁 preflight failed"
|
||||
fi
|
||||
done
|
||||
|
||||
log "🥳 All Done! Ready to build, run: make build"
|
||||
}
|
||||
|
||||
if [ -n "$0" ] && [ x"$0" != x"-bash" ]; then
|
||||
_main "$@"
|
||||
fi
|
Loading…
Reference in New Issue
Block a user