diff --git a/.build.yml b/.build.yml new file mode 100644 index 0000000..15d2cd9 --- /dev/null +++ b/.build.yml @@ -0,0 +1,16 @@ +image: alpine/latest +packages: + - git + - openssh +secrets: + - 0639564d-6995-4e2e-844b-2f8feb0b7fb1 +environment: + repo: zona + github: git@github.com:ficcdaf/zona.git +tasks: + - mirror: | + ssh-keyscan github.com >> ~/.ssh/known_hosts + cd "$repo" + git remote add github "$github" + git push --mirror github + diff --git a/.gitignore b/.gitignore index 7f6e79c..dc6a675 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ bin/ go.sum # test/ +foobar/ diff --git a/README.md b/README.md index f4f5af4..5de03c3 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,65 @@ # Zona -Zona is a small tool for building a static blog website. It allows users to write pages and blog posts in Markdown and automatically build them into a static, lightweight blog. +**IMPORTANT:** Zona is currently migrating to a Python implementation. The new +repository is called [zona-py](https://git.sr.ht/~ficd/zona-py). **It will +replace this repository once the migration is complete.** -> **Warning:** Implementing v1 functionality is still WIP. There will be binaries available to download from the Releases tab when Zona is ready to be used. Until then, you are welcome to download the source code, but I can't promise anything will work! +[Zona](https://sr.ht/~ficd/zona/) is a tool for building a static website, +optimized for lightweight blogs following minimalist design principles. The +project is hosted on [sourcehut](https://sr.ht/~ficd/zona/) and mirrored on +[GitHub](https://github.com/ficcdaf/zona). I am not accepting GitHub issues, +please make your submission to the +[issue tracker](https://todo.sr.ht/~ficd/zona) or send an email to the public +mailing list at +[~ficd/zona-devel@lists.sr.ht](mailto:~ficd/zona-devel@lists.sr.ht) -## Table of Contents + -- [Features](#v1-features) -- [Installation](#installation) -- [Roadmap](#roadmap) +> Zona is currently in development. The `main` branch of this repository does +> not yet contain the software. The `dev-stable` branch contains the code used +> to generate [ficd.ca](https://ficd.ca) -- although the program is undocumented +> and missing features, so please proceed at your own risk. The `dev` branch +> contains the latest development updates and is not guaranteed to be functional +> (or even compile) at any given commit. Kindly note that the commit history +> will be cleaned up before the program is merged into the `main` branch. -## v1 Features + -- Write your pages in Markdown. -- Build a lightweight website with zero JavaScript. -- Simple CLI build interface. -- HTML layout optimized for screen readers and Neocities. +## Design Goals -## Getting Started +Zona is intended to be easy-to-use. A user should be able to build a reasonably +complex website or blog with only a directory of Markdown content and a single +command, without needing to write any HTML or configuration. However, users +should optionally have access to sensible and flexible configuration options, +including writing HTML. The output of Zona should also be lightweight, retaining +the smallest file sizes possible. These characteristics make Zona well-suited +for both beginners and power users that wish to host a website on a service like +Neocities or GitHub Pages. -### Dependencies +## Features Implemented -- `go 1.23.2` +- Write pages purely in Markdown. +- Single-command build process. +- Lightweight output. +- Sensible default template and stylesheet. +- Configuration file. +- Internal links preserved. +- Custom image element parsing & formatting. +- Site header and footer defined in Markdown. +- YAML frontmatter support. -```Bash -# On Arch Linux -sudo pacman -S go +## Planned Features -# On Ubuntu/Debian -sudo apt install go -``` - -### Installation - -First, download the repository and open it: - -```Bash -git clone https://github.com/ficcdaf/zona.git && cd zona -``` - -On Linux: - -```Bash -# run the provided build script -./build.sh -``` - -On other platforms: - -```Bash -go build -o bin/zona cmd/zona -``` - -The resulting binary can be found at `bin/zona`. - -## Roadmap - -- [ ] Zona configuration file to define build options. -- [ ] Image optimization & dithering options. -- [ ] AUR package after first release -- [ ] Automatic RSS/Atom feed generation. +- Automatically treat contents of `posts/` directory as blog posts. +- Automatically generated `Archive`, `Recent Posts`, and `Image Gallery` + elements. +- Support for custom stylesheets, favicons, and page templates. +- Image optimization and dithering. +- Custom markdown tags that expand to user-defined templates. +- Live preview server. +- Robust tests. ## Inspirations - [Zoner](https://git.sr.ht/~ryantrawick/zoner) -- Zonelets +- [Zonelets](https://zonelets.net/) diff --git a/build.sh b/build.sh deleted file mode 100755 index fc1bdf6..0000000 --- a/build.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -go build -o bin/zona ./cmd/zona -ln -sf bin/zona ./zona diff --git a/cmd/zona/main.go b/cmd/zona/main.go deleted file mode 100644 index 2b0fe15..0000000 --- a/cmd/zona/main.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "errors" - "flag" - "fmt" - "os" - - "github.com/ficcdaf/zona/internal/util" -) - -// validateFile checks whether a given path -// is a valid file && matches an expected extension -func validateFile(path, ext string) bool { - return (util.CheckExtension(path, ext) == nil) && (util.PathIsValid(path, true)) -} - -func main() { - mdPath := flag.String("file", "", "Path to the markdown file.") - flag.Parse() - if *mdPath == "" { - // no flag provided, check for positional argument instead - n := flag.NArg() - var e error - switch n { - case 1: - // we read the positional arg - arg := flag.Arg(0) - // mdPath wants a pointer so we get arg's address - mdPath = &arg - case 0: - // in case of no flag and no arg, we fail - e = errors.New("Required argument missing!") - default: - // more args than expected is also fail - e = errors.New("Unexpected arguments!") - } - if e != nil { - fmt.Printf("Error: %s\n", e.Error()) - os.Exit(1) - } - - } - // if !validateFile(*mdPath, ".md") { - // fmt.Println("File validation failed!") - // os.Exit(1) - // } - // convert.ConvertFile(*mdPath, "test/test.html") - err := util.Traverse(*mdPath, "foobar") - if err != nil { - fmt.Printf("Error: %s\n", err.Error()) - } -} diff --git a/go.mod b/go.mod deleted file mode 100644 index 182992a..0000000 --- a/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/ficcdaf/zona - -go 1.23.2 - -require github.com/gomarkdown/markdown v0.0.0-20240930133441-72d49d9543d8 // indirect diff --git a/internal/convert/convert.go b/internal/convert/convert.go deleted file mode 100644 index f6222ff..0000000 --- a/internal/convert/convert.go +++ /dev/null @@ -1,76 +0,0 @@ -package convert - -import ( - "io" - "os" - - "github.com/gomarkdown/markdown" - "github.com/gomarkdown/markdown/html" - "github.com/gomarkdown/markdown/parser" -) - -// This function takes a Markdown document and returns an HTML document. -func MdToHTML(md []byte) ([]byte, error) { - // create parser with extensions - extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock - p := parser.NewWithExtensions(extensions) - doc := p.Parse(md) - - // build HTML renderer - htmlFlags := html.CommonFlags | html.HrefTargetBlank - opts := html.RendererOptions{Flags: htmlFlags} - renderer := html.NewRenderer(opts) - - return markdown.Render(doc, renderer), nil -} - -// WriteFile writes a given byte array to the given path. -func WriteFile(b []byte, p string) error { - f, err := os.Create(p) - if err != nil { - return err - } - _, err = f.Write(b) - defer f.Close() - if err != nil { - return err - } - return nil -} - -// ReadFile reads a byte array from a given path. -func ReadFile(p string) ([]byte, error) { - f, err := os.Open(p) - if err != nil { - return nil, err - } - var result []byte - buf := make([]byte, 1024) - for { - n, err := f.Read(buf) - // check for a non EOF error - if err != nil && err != io.EOF { - return nil, err - } - // n==0 when there are no chunks left to read - if n == 0 { - defer f.Close() - break - } - result = append(result, buf[:n]...) - } - return result, nil -} - -func ConvertFile(in string, out string) error { - md, err := ReadFile(in) - if err != nil { - return err - } - html, err := MdToHTML(md) - if err != nil { - return err - } - err = WriteFile(html, out) - return err -} diff --git a/internal/tree/node.go b/internal/tree/node.go deleted file mode 100644 index 9025b3d..0000000 --- a/internal/tree/node.go +++ /dev/null @@ -1,50 +0,0 @@ -package tree - -// Node is a struct containing nodes of a file tree. -type Node struct { - // can be nil - Parent *Node - Name string - // Empty value mean directory - Ext string - // cannot be nil; may have len 0 - Children []*Node -} - -// NewNode constructs and returns a Node instance. -// parent and children are optional and can be nil, -// in which case Parent will be stored as nil, -// but Children will be initialized as []*Node of len 0. -// If ext == "", the Node is a directory. -func NewNode(name string, ext string, parent *Node, children []*Node) *Node { - var c []*Node - if children == nil { - c = make([]*Node, 0) - } else { - c = children - } - n := &Node{ - Name: name, - Ext: ext, - Parent: parent, - Children: c, - } - return n -} - -func (n *Node) IsRoot() bool { - return n.Parent == nil -} - -func (n *Node) IsTail() bool { - return len(n.Children) == 0 -} - -func (n *Node) IsDir() bool { - return n.Ext == "" -} - -// TODO: Implement recursive depth-first traversal to process a tree - -func Traverse(root *Node) { -} diff --git a/internal/util/path.go b/internal/util/path.go deleted file mode 100644 index 7013734..0000000 --- a/internal/util/path.go +++ /dev/null @@ -1,94 +0,0 @@ -// Package util provides general utilities. -package util - -import ( - "errors" - "fmt" - "io/fs" - "os" - "path/filepath" - "strings" -) - -// CheckExtension checks if the file located at path (string) -// matches the provided extension type -func CheckExtension(path, ext string) error { - if filepath.Ext(path) == ext { - return nil - } else { - return errors.New("Invalid extension.") - } -} - -// PathIsValid checks if a path is valid. -// If requireFile is set, directories are not considered valid. -func PathIsValid(path string, requireFile bool) bool { - s, err := os.Stat(path) - if os.IsNotExist(err) { - return false - } else if requireFile { - // fmt.Printf("Directory status: %s\n", strconv.FormatBool(s.IsDir())) - return !s.IsDir() - } - return err == nil -} - -func getRoot(path string) string { - for { - parent := filepath.Dir(path) - if parent == "." { - break - } - path = parent - } - fmt.Println("getRoot: ", path) - return path -} - -func replaceRoot(inPath, outRoot string) string { - relPath := strings.TrimPrefix(inPath, getRoot(inPath)) - outPath := filepath.Join(outRoot, relPath) - return outPath -} - -func createFileWithParents(path string) error { - dir := filepath.Dir(path) - // Check if the parent directory already exists - // before trying to create it - if _, dirErr := os.Stat(dir); os.IsNotExist(dirErr) { - // create directories - err := os.MkdirAll(dir, os.ModePerm) - if err != nil { - return err - } - // TODO: write the file here - } - return nil -} - -func processFile(inPath string, entry fs.DirEntry, err error, outRoot string) error { - if err != nil { - return err - } - if !entry.IsDir() { - ext := filepath.Ext(inPath) - fmt.Println("Root: ", replaceRoot(inPath, outRoot)) - switch ext { - case ".md": - fmt.Println("Processing markdown...") - default: - // All other file types, we copy! - } - } - fmt.Printf("Visited: %s\n", inPath) - return nil -} - -func Traverse(root string, outRoot string) error { - // err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, err error) error { - walkFunc := func(path string, entry fs.DirEntry, err error) error { - return processFile(path, entry, err, outRoot) - } - err := filepath.WalkDir(root, walkFunc) - return err -} diff --git a/test/d1/file1 b/test/d1/file1 deleted file mode 100644 index e69de29..0000000 diff --git a/test/d2/tt b/test/d2/tt deleted file mode 100644 index e69de29..0000000 diff --git a/test/d5/d4/anod/arst b/test/d5/d4/anod/arst deleted file mode 100644 index e69de29..0000000 diff --git a/test/d5/d4/tta b/test/d5/d4/tta deleted file mode 100644 index e69de29..0000000 diff --git a/test/d5/t b/test/d5/t deleted file mode 100644 index e69de29..0000000 diff --git a/test/in.md b/test/in.md deleted file mode 100644 index a933e82..0000000 --- a/test/in.md +++ /dev/null @@ -1,3 +0,0 @@ -# My amazing markdown file! - -I can _even_ do **this**! diff --git a/test/test.html b/test/test.html deleted file mode 100644 index ff2795a..0000000 --- a/test/test.html +++ /dev/null @@ -1,3 +0,0 @@ -
I can even do this!
diff --git a/zona b/zona deleted file mode 120000 index 0ee2e17..0000000 --- a/zona +++ /dev/null @@ -1 +0,0 @@ -bin/zona \ No newline at end of file