Changeset 80 in code


Ignore:
Timestamp:
Dec 12, 2023, 2:09:17 PM (18 months ago)
Author:
Izuru Yakumo
Message:

A good time to finally release a stable version

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

Location:
trunk
Files:
7 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Makefile

    r75 r80  
    11DESTDIR ?=
    2 GOFLAGS ?= -v -buildvcs=false -mod=vendor -buildmode=exe
     2GOFLAGS ?= -v -buildvcs=false -mod=vendor -buildmode=exe -ldflags "-w -X `go list`.Date=${DATE} -X `go list`.Vendor=${GOOS} -X `go list`.Version=${VERSION}"
    33PREFIX ?= /usr/local
     4DATE ?= `date -u +%F`
     5GOOS ?= `go env GOOS`
    46VERSION ?= `git describe --tags`
    57
  • trunk/README.md

    r75 r80  
    1818Build it manually assuming you have Go (>=1.17) installed:
    1919
    20         $ go install marisa.chaotic.ninja/aya/cmd/aya@latest
     20        $ go install marisa.chaotic.ninja/aya/cmd/aya@latest (1)
    2121        --- or ---
    2222        $ git clone https://git.chaotic.ninja/yakumo.izuru/aya
     
    2424        $ make
    2525        # make install
    26        
     26
     27(1) If you use this method, the `aya version` subcommand may print the wrong string,
     28but it should not be a problem unless you use it on a page.
     29
    2730## Ideology
    2831
    29 Keep your texts in markdown, or HTML format right in the main directory
     32Keep your texts in markdown, [amber](https://github.com/eknkc/amber), or html format right in the main directory
    3033of your blog/site.
    3134
     
    3336in the `.aya` subdirectory.
    3437
    35 Define variables in the header of the content files using [YAML]:
     38Define variables in the header of the content files using [YAML](https://www.yaml.io) :
    3639
    37         title: My web site
    38         keywords: best website, hello, world
    39         ---
     40```markdown
     41title: My web site
     42keywords: best website, hello, world
     43---
    4044
    41         Markdown text goes after a header *separator*
     45Markdown text goes after a header *separator*
     46```
    4247
    4348Use placeholders for variables and plugins in your markdown or html
     
    4752subdiretory.
    4853
    49 Everything the extensions prints to stdout becomes the value of the
     54Everything the extensions prints to [stdout](https://man.freebsd.org/cgi/man.cgi?fd) becomes the value of the
    5055placeholder.
    5156
    52 Every variable from the content header will be passed via environment variables like `title` becomes `$AYA_TITLE` and so on. There are some special variables:
     57Every variable from the content header will be passed via environment variables like `title` becomes `$AYA\_TITLE` and so on.
     58There are some special variables:
    5359
    5460* `$AYA` - a path to the `aya` executable
     
    94100        rm -f $AYA_OUTDIR/styles.css
    95101
    96 ## Extras
    97 
    98 `aya` also supports generating `.html` and `.css` by means of using `.amber`
    99 and `.gcss` files. See more at [eknkc/amber](https://github.com/eknkc/amber) [yosssi/gcss](https://github.com/yosssi/gcss)
     102Note, you can also place `.gcss` files for [gcss](https://github.com/yosssi/gcss) to process instead
    100103
    101104## Command line usage
  • trunk/cmd/aya/main.go

    r79 r80  
    1 // $TheSupernovaDuo: marisa.chaotic.ninja/aya/cmd/aya, v0.7.0 2023-12-11 17:22:51+0000, yakumo_izuru Exp $
     1// $TheSupernovaDuo: marisa.chaotic.ninja/aya/cmd/aya, v1.0.0 2023-12-12 13:44:23+0000, yakumo_izuru Exp $
    22package main
    33
    44import (
    5         "bytes"
    65        "fmt"
    7         "io"
    86        "os"
    9         "os/exec"
    10         "path/filepath"
    117        "strings"
    12         "time"
    138
    14         "gopkg.in/yaml.v3"
    159        "marisa.chaotic.ninja/aya"
    1610)
     
    2317
    2418type Vars map[string]string
    25 
    26 // renameExt renames extension (if any) from oldext to newext
    27 // If oldext is an empty string - extension is extracted automatically.
    28 // If path has no extension - new extension is appended
    29 func renameExt(path, oldext, newext string) string {
    30         if oldext == "" {
    31                 oldext = filepath.Ext(path)
    32         }
    33         if oldext == "" || strings.HasSuffix(path, oldext) {
    34                 return strings.TrimSuffix(path, oldext) + newext
    35         }
    36         return path
    37 }
    38 
    39 // globals returns list of global OS environment variables that start
    40 // with AYA_ prefix as Vars, so the values can be used inside templates
    41 func globals() Vars {
    42         vars := Vars{}
    43         for _, e := range os.Environ() {
    44                 pair := strings.Split(e, "=")
    45                 if strings.HasPrefix(pair[0], "AYA_") {
    46                         vars[strings.ToLower(pair[0][3:])] = pair[1]
    47                 }
    48         }
    49         return vars
    50 }
    51 
    52 // run executes a command or a script. Vars define the command environment,
    53 // each aya var is converted into OS environemnt variable with AYA_ prefix
    54 // prepended.  Additional variable $AYA contains path to the aya binary. Command
    55 // stderr is printed to aya stderr, command output is returned as a string.
    56 func run(vars Vars, cmd string, args ...string) (string, error) {
    57         // First check if partial exists (.html)
    58         if b, err := os.ReadFile(filepath.Join(AYADIR, cmd+".html")); err == nil {
    59                 return string(b), nil
    60         }
    61 
    62         var errbuf, outbuf bytes.Buffer
    63         c := exec.Command(cmd, args...)
    64         env := []string{"AYA=" + os.Args[0], "AYA_OUTDIR=" + PUBDIR}
    65         env = append(env, os.Environ()...)
    66         for k, v := range vars {
    67                 env = append(env, "AYA_"+strings.ToUpper(k)+"="+v)
    68         }
    69         c.Env = env
    70         c.Stdout = &outbuf
    71         c.Stderr = &errbuf
    72 
    73         err := c.Run()
    74 
    75         if errbuf.Len() > 0 {
    76                 fmt.Println("ERROR:", errbuf.String())
    77         }
    78         if err != nil {
    79                 return "", err
    80         }
    81         return string(outbuf.Bytes()), nil
    82 }
    83 
    84 // getVars returns list of variables defined in a text file and actual file
    85 // content following the variables declaration. Header is separated from
    86 // content by an empty line. Header can be either YAML or JSON.
    87 // If no empty newline is found - file is treated as content-only.
    88 func getVars(path string, globals Vars) (Vars, string, error) {
    89         b, err := os.ReadFile(path)
    90         if err != nil {
    91                 return nil, "", err
    92         }
    93         s := string(b)
    94 
    95         // Pick some default values for content-dependent variables
    96         v := Vars{}
    97         title := strings.Replace(strings.Replace(path, "_", " ", -1), "-", " ", -1)
    98         v["title"] = strings.ToTitle(title)
    99         v["description"] = ""
    100         v["file"] = path
    101         v["url"] = path[:len(path)-len(filepath.Ext(path))] + ".html"
    102         v["output"] = filepath.Join(PUBDIR, v["url"])
    103 
    104         // Override default values with globals
    105         for name, value := range globals {
    106                 v[name] = value
    107         }
    108 
    109         // Add layout if none is specified
    110         if _, ok := v["layout"]; !ok {
    111                 v["layout"] = "layout.html"
    112         }
    113 
    114         delim := "\n---\n"
    115         if sep := strings.Index(s, delim); sep == -1 {
    116                 return v, s, nil
    117         } else {
    118                 header := s[:sep]
    119                 body := s[sep+len(delim):]
    120 
    121                 vars := Vars{}
    122                 if err := yaml.Unmarshal([]byte(header), &vars); err != nil {
    123                         fmt.Println("ERROR: failed to parse header", err)
    124                         return nil, "", err
    125                 } else {
    126                         // Override default values + globals with the ones defines in the file
    127                         for key, value := range vars {
    128                                 v[key] = value
    129                         }
    130                 }
    131                 if strings.HasPrefix(v["url"], "./") {
    132                         v["url"] = v["url"][2:]
    133                 }
    134                 return v, body, nil
    135         }
    136 }
    137 
    138 // Render expanding aya plugins and variables
    139 func render(s string, vars Vars) (string, error) {
    140         delimOpen := "{{"
    141         delimClose := "}}"
    142 
    143         out := &bytes.Buffer{}
    144         for {
    145                 if from := strings.Index(s, delimOpen); from == -1 {
    146                         out.WriteString(s)
    147                         return out.String(), nil
    148                 } else {
    149                         if to := strings.Index(s, delimClose); to == -1 {
    150                                 return "", fmt.Errorf("Closing delimiter not found")
    151                         } else {
    152                                 out.WriteString(s[:from])
    153                                 cmd := s[from+len(delimOpen) : to]
    154                                 s = s[to+len(delimClose):]
    155                                 m := strings.Fields(cmd)
    156                                 if len(m) == 1 {
    157                                         if v, ok := vars[m[0]]; ok {
    158                                                 out.WriteString(v)
    159                                                 continue
    160                                         }
    161                                 }
    162                                 if res, err := run(vars, m[0], m[1:]...); err == nil {
    163                                         out.WriteString(res)
    164                                 } else {
    165                                         fmt.Println(err)
    166                                 }
    167                         }
    168                 }
    169         }
    170 
    171 }
    172 
    173 // This function passes the files to build to their corresponding functions
    174 // As far as I'm aware, Markdown has three possible filename extensions,
    175 // but .md is the most common one known.
    176 func build(path string, w io.Writer, vars Vars) error {
    177         ext := filepath.Ext(path)
    178         if ext == ".md" || ext == ".mkd" || ext == ".markdown" {
    179                 return buildMarkdown(path, w, vars)
    180         } else if ext == ".htm" || ext == ".html" || ext == ".xht" || ext == ".xhtml" {
    181                 return buildHTML(path, w, vars)
    182         } else if ext == ".amber" {
    183                 return buildAmber(path, w, vars)
    184         } else if ext == ".gcss" {
    185                 return buildGCSS(path, w)
    186         } else {
    187                 return buildRaw(path, w)
    188         }
    189 }
    190 
    191 // Build everything and store it on PUBDIR
    192 // If boolean watch is true, it keeps on going
    193 // every time you modify something.
    194 func buildAll(watch bool) {
    195         lastModified := time.Unix(0, 0)
    196         modified := false
    197 
    198         vars := globals()
    199         for {
    200                 os.Mkdir(PUBDIR, 0755)
    201                 filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
    202                         // ignore hidden files and directories
    203                         if filepath.Base(path)[0] == '.' || strings.HasPrefix(path, ".") {
    204                                 return nil
    205                         }
    206                         // inform user about fs walk errors, but continue iteration
    207                         if err != nil {
    208                                 fmt.Println("error:", err)
    209                                 return nil
    210                         }
    211 
    212                         if info.IsDir() {
    213                                 os.Mkdir(filepath.Join(PUBDIR, path), 0755)
    214                                 return nil
    215                         } else if info.ModTime().After(lastModified) {
    216                                 if !modified {
    217                                         // First file in this build cycle is about to be modified
    218                                         run(vars, "prehook")
    219                                         modified = true
    220                                 }
    221                                 fmt.Println("build:", path)
    222                                 return build(path, nil, vars)
    223                         }
    224                         return nil
    225                 })
    226                 if modified {
    227                         // At least one file in this build cycle has been modified
    228                         run(vars, "posthook")
    229                         modified = false
    230                 }
    231                 if !watch {
    232                         break
    233                 }
    234                 lastModified = time.Now()
    235                 time.Sleep(1 * time.Second)
    236         }
    237 }
    23819
    23920// Initialize the environment
     
    29576                }
    29677        case "version":
    297                 fmt.Printf("%v\n", aya.FullVersion())
     78                fmt.Printf("%v\n", aya.PrintVersion())
    29879                os.Exit(0)
    29980        case "watch":
  • trunk/usage.go

    r78 r80  
    77// This function is called by the `aya help` subcommand
    88func PrintUsage() {
    9         fmt.Printf("aya/%v\n", FullVersion())
     9        fmt.Printf("aya/%v\n", PrintFullVersion())
    1010        fmt.Println("Homepage: https://aya.chaotic.ninja")
    1111        fmt.Println("Repository: https://git.chaotic.ninja/yakumo.izuru/aya")
  • trunk/version.go

    r79 r80  
    44import (
    55        "fmt"
    6         "time"
    76)
    87
    98var (
    10         // Set to current tag
    11         Version = "v0.7.0"
    12         Time = time.Now()
     9        // Variables set at build-time
     10        Date string
     11        Vendor string
     12        Version string
    1313)
    1414
    15 // FullVersion display the full version and build
    16 func FullVersion() string {
    17         d := Time.Day()
    18         m := Time.Month()
    19         y := Time.Year()
    20         return fmt.Sprintf("%v || %d.%d.%d", Version, y, m, d)
     15// PrintVersion only displays the obvious
     16func PrintVersion() string {
     17        return fmt.Sprintf("%s", Version)
    2118}
     19
     20// PrintFullVersion display the full version and build
     21func PrintFullVersion() string {
     22        return fmt.Sprintf("%s, built at %s, on %s", Version, Date, Vendor)
     23}
Note: See TracChangeset for help on using the changeset viewer.