commit 0e5eee146f2657cb6c5bc5b2636146fcc6016465 Author: Alex Palaistras Date: Sun Oct 30 23:17:54 2016 +0000 First version of Grawkit, with simple single-branch test This version has support for adding commits on a single branch using `git commit`, and enough scaffolding for adding additional commands and accompanying tests easily. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2d3f13c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# Code styles for Fawkss. + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = tab +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1777578 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Alex Palaistras + +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. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..777dbbe --- /dev/null +++ b/Makefile @@ -0,0 +1,98 @@ +# -------------------- +# Makefile for Grawkit +# -------------------- + +# Run `make help` for information on available actions. + +# -------------------- +# Variable definitions +# -------------------- + +# Default name for Grawkit executable. +CMD = $(CURDIR)/grawkit + +# Default executables to use. +SHELL = /bin/bash +DIFF = $(shell which colordiff || which diff) + +# Test files to execute. +TESTS ?= $(shell find tests/*/*) + +# Color & style definitions. +BOLD = \033[1m +UNDERLINE = \033[4m +RED = \033[31m +GREEN = \033[32m +BLUE = \033[36m +RESET = \033[0m + +# ---------------- +# Other directives +# ---------------- + +# Make `help` be the default action when no arguments are passed to `make`. +.DEFAULT_GOAL = help +.PHONY: $(TESTS) test help + +# Awk script for extracting Grawkit documentation as Markdown. +define EXTRACT_MARKDOWN + /^(#|# .*)$$/ { + if (f==1) {f=0; printf "```\n\n"} + print substr($$0, 3) + } + /^[^#]/ { + if (f==0) {f=1; printf "\n```awk\n"} + print + } + !NF { + print + } + END { + if (f==1) {printf "```\n"} + } +endef +export EXTRACT_MARKDOWN + +# ---------------- +# Rule definitions +# ---------------- + +## Build documentation from source file in Markdown format. +doc: + @awk "$$EXTRACT_MARKDOWN" "$(CMD)" + +## Execute test suite, accepts list of specific files to run. +test: test-before $(TESTS) test-after + +test-before: + @printf ">> $(BOLD)Executing tests...$(RESET)\n" + +test-after: + @printf ">> $(BOLD)Finished executing tests.$(RESET)\n" + +$(TESTS): + $(eval TEST_$@ := awk '// {exit} f' $@) + $(eval EXPECTED_$@ := awk '/-->/ {f=1;getline;next} f' $@) + $(eval ACTUAL_$@ := $(CMD) <($(TEST_$@))) + + @printf ">> $(BOLD)Testing file '$@'...$(RESET) " + + # Generate diff between expected and actual results and print back to user. + @result=$$($(DIFF) -ud <($(EXPECTED_$@)) <($(ACTUAL_$@)) | tail -n +3); \ + if [ -z "$$result" ]; then \ + printf "$(GREEN)OK$(RESET)\n"; \ + else \ + printf "$(RED)FAIL$(RESET)\n"; \ + echo "$$result"; \ + fi \ + +## Show usage information for this Makefile. +help: + @printf "$(BOLD)Grawkit — The Awksome Git Graph Generator.$(RESET)\n\n" + @printf "This Makefile contains tasks for processing auxiliary actions, such as\n" + @printf "generating documentation or running test cases against the test suite.\n\n" + @printf "$(UNDERLINE)Available Tasks$(RESET)\n\n" + @awk -F \ + ':|##' '/^##/ {c=$$2; getline; printf "$(BLUE)%6s$(RESET) %s\n", $$1, c}' \ + $(MAKEFILE_LIST) + @printf "\n" diff --git a/README.md b/README.md new file mode 100644 index 0000000..1120e5a --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Grawkit - The AWKsome Git Graph Toolkit + +Grawkit is a tool that helps build SVG graphs from git command-line descriptions, and is built in Awk. + +## Testing & Documentation + +A `Makefile` is provided for running tests and producing documentation for Grawkit. Run `make help` in the project root for more information. + +A full test-suite is provided (depending only on `make` and `awk`), which should serve as a good example of the existing feature-set. + +## License + +All code in this repository is covered by the terms of the MIT License, the full text of which can be found in the LICENSE file. + +[license-url]: https://github.com/deuill/grawkit/blob/master/LICENSE +[license-svg]: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/grawkit b/grawkit new file mode 100755 index 0000000..3ad06a8 --- /dev/null +++ b/grawkit @@ -0,0 +1,157 @@ +#!/usr/bin/awk -f +# +# Grawkit — The Awksome Git Graph Generator. +# ========================================== +# +# Grawkit is a tool that helps build SVG graphs from Git command-line descriptions. +# +# This documentation is built using Markdown syntax, and can be parsed out by +# running `make doc` in the project root. Please check the project's README file +# for additional information. +# +# Built-in Functions +# ------------------ +# +# This section contains global helper functions, used across different rules, as +# defined in the next section below. +# +# > Function `t` processes the string passed as a template. It's mainly used for +# > cleaning up strings with single quotes etc. +function t(str) { + # Replace single quotes with double quotes. + gsub("'", "\"", str) + + return str +} + +# > Function `branch` renders pre-defined branch under a specific name to its +# > SVG representation. +function branch(name, _, buf, tmp, refs, i, bs, cs) { + # Find index of branch. + for (n in branches) { + if (n == name) break + i += 1 + } + + # Get commit refs. + split(branches[name], refs, ",") + bs = i * style["branch/spacing"] + + # Add path for branch. + tmp = "M" refs[1] * style["commit/spacing"] "," bs + tmp = tmp " L" (length(refs) - 1) * style["commit/spacing"] "," bs + + # Print path. + buf = sprintf(svg["g"], name) + buf = buf "\n" sprintf(svg["path"], tmp) + + # Add commits on path. + for (c in refs) { + cs = refs[c] * style["commit/spacing"] + + tmp = sprintf(svg["circle"], cs, bs, style["commit/radius"]) + buf = buf "\n" tmp + } + + buf = buf "\n" svg["/g"] + return buf +} + +# Global Declarations +# ------------------- +# +# This block contains logic for initializing global variables used across Grawkit. + +BEGIN { + # Rule matching. + rule["commit"] = "^git commit" + + # Style definitions. + style["branch/spacing"] = "50" + style["branch/fill"] = "none" + style["branch/stroke"] = "#333" + style["branch/stroke-width"] = "10" + + style["commit/spacing"] = "100" + style["commit/fill"] = "#fff" + style["commit/stroke"] = style["branch/stroke"] + style["commit/stroke-width"] = style["branch/stroke-width"] / 2 + style["commit/radius"] = style["commit/stroke-width"] * 1.5 + + # Static SVG templates. + svg["svg"] = t("") + svg["/svg"] = "" + svg["g"] = t("") + svg["/g"] = "" + svg["path"] = t("") + svg["circle"] = t("") + + # Branch definitions. + branches["master"] = "0" + len["branches"] = 1; + + # Commit definitions. + commits[0] = "b:master" + len["commits"] = 1; + + # Tracks the state across calls. + state["branch"] = "master" + state["HEAD"] = 0 +} + +# Rule Definitions +# ---------------- +# +# This block contains definitions for line manipulation rules used across Fawkss. +# Rules may or may not be exclusive, i.e. the effects of one rule may cascade to +# subsequent rules for the same line. +# + +# > Match `git commit` declarations. +$0 ~ rule["commit"] { + # Add new commit with specific message. + commits[len["commits"]] = "b:" state["branch"] + + # Update commit references. + branches[state["branch"]] = branches[state["branch"]] "," len["commits"] + state["HEAD"] = len["commits"] + + len["commits"] += 1 +} + +# SVG Graph Generation +# +# This block contains logic for building the final SVG output from Grawkit's +# internal state, as defined in the command-line provided. +# +END { + xy = style["branch/stroke-width"] * -1 + w = (style["commit/spacing"] * (len["commits"] - 1)) + (style["commit/stroke-width"] * 4) + h = (style["branch/spacing"] * (len["branches"] - 1)) + (style["branch/stroke-width"] * 2) + + # Print SVG header. + printf svg["svg"], xy, xy, w, h + printf "\n" + + # Print inline style definitions. + print t("" + + # Print each branch and corresponding commits in turn. + for (name in branches) { + print branch(name) + } + + # Print SVG footer. + print svg["/svg"] +} diff --git a/tests/simple/master.svg b/tests/simple/master.svg new file mode 100644 index 0000000..2910191 --- /dev/null +++ b/tests/simple/master.svg @@ -0,0 +1,27 @@ + + + + + + + + + +