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.
This commit is contained in:
Alex Palaistras 2016-10-30 23:17:54 +00:00
commit 0e5eee146f
6 changed files with 331 additions and 0 deletions

14
.editorconfig Normal file
View File

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

19
LICENSE Normal file
View File

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

98
Makefile Normal file
View File

@ -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 '/<!--/ {f=1;next} /-->/ {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"

16
README.md Normal file
View File

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

157
grawkit Executable file
View File

@ -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 xmlns='http://www.w3.org/2000/svg' viewBox='%d %d %d %d'>")
svg["/svg"] = "</svg>"
svg["g"] = t("<g id='%s'>")
svg["/g"] = "</g>"
svg["path"] = t("<path class='branch' d='%s' />")
svg["circle"] = t("<circle class='commit' cx='%d' cy='%d' r='%s' />")
# 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("<style type='text/css'><![CDATA[")
print ".branch {"
print " fill: " style["branch/fill"] ";"
print " stroke: " style["branch/stroke"] ";"
print " stroke-width: " style["branch/stroke-width"] ";"
print "}"
print ".commit {"
print " fill: " style["commit/fill"] ";"
print " stroke: " style["commit/stroke"] ";"
print " stroke-width: " style["commit/stroke-width"] ";"
print "}"
print "]]></style>"
# Print each branch and corresponding commits in turn.
for (name in branches) {
print branch(name)
}
# Print SVG footer.
print svg["/svg"]
}

27
tests/simple/master.svg Normal file
View File

@ -0,0 +1,27 @@
<!--
# Test a simple scenario of adding a few commits to `master`.
git commit -m "Adding a new commit"
-->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -10 120 20">
<style type="text/css"><![CDATA[
.branch {
fill: none;
stroke: #333;
stroke-width: 10;
}
.commit {
fill: #fff;
stroke: #333;
stroke-width: 5;
}
]]></style>
<g id="master">
<path class="branch" d="M0,0 L100,0" />
<circle class="commit" cx="0" cy="0" r="7.5" />
<circle class="commit" cx="100" cy="0" r="7.5" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 523 B