]> Sergey Matveev's repositories - schwabrak.git/commitdiff
Initial commit
authorSergey Matveev <stargrave@stargrave.org>
Wed, 10 Jan 2024 13:52:56 +0000 (16:52 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 11 Jan 2024 11:45:11 +0000 (14:45 +0300)
12 files changed:
README [new file with mode: 0644]
add [new file with mode: 0755]
cd [new file with mode: 0755]
comment-list [new file with mode: 0755]
dep-add [new file with mode: 0755]
lib.zsh.rc [new file with mode: 0644]
list [new file with mode: 0755]
recfile-export [new file with mode: 0755]
recfile-export-all [new file with mode: 0755]
show [new file with mode: 0755]
tag-add [new file with mode: 0755]
tag-list [new file with mode: 0755]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..102fcbd
--- /dev/null
+++ b/README
@@ -0,0 +1,101 @@
+schwabrak -- simple flat file based issue tracker
+
+I want relatively simple issue/ticket tracker. Something like Trac,
+Redmine or Fossil. But they require heavy dependencies (Python, Ruby),
+database and they are centralised. You can not work with them offline
+and it is relatively complicated to replicate data to locally running
+copy of any of those trackers (well, except for Fossil I believe). And
+does web-interface necessary at all?
+
+What is a ticket/issue? Just some plain text descriptions, that have
+attached enumerations (statuses, assignments, priorities, projects,
+subsystems, severities, resolutions, etc) and a pile of append-only
+comments, possibly with another file attachments. Can all of that
+live in a directory with several plain text files? Can it be linked
+with other issues just by making a symbolic links in deps/ subdirectory?
+Are not Git commits provide supplementary metainformation about when and
+by whom any of the change is made with that directory? Definitely yes!
+And because of DVCS you get ability to keep the whole distributed copy
+of the tracker on each developer's machine. You can send changes to it
+asynchronously as a patch or bundle.
+
+Basically schwabrak is mainly about a convention how to keep issues in
+files, loosely similar to https://github.com/driusan/PoormanIssueTracker,
+where I borrowed idea of replacing spaces with dashes in issue names.
+
+Issues are kept in issues/ directory. Each issue can be a part of
+projects hierarchy: main-project/sub-proj/issue-name. Directory's name
+is the issue's brief name. Dashes should be interpreted as spaces and n
+> 1 dashes should be interpreted as n-1 dashes when converting directory
+name to a human readable issue title, as PoormanIssueTracker suggests.
+"about" file contains the description of the issue. "result" (initially
+empty) contains the closed issue resolution information.
+
+"created" issue's file contains the datetime it was created. It is used
+to help sort issues by date. Unfortunately Git does not keep and restore
+mtimes, that probably can eliminate the need of separate files with the
+timestamps.
+
+Each issue can have attached tags. For keeping their set in consistent
+well-defined state, tags/ directory above the issues/ contains available
+tags for you projects.
+    $ for tag in status:open status:done assigned:alice assigned:bob
+            doc db test:integration severity:high severity:low ... ; do
+        touch tags/$tag
+    done
+It is your choice how to name and deal with them. Want to find all
+issues in done state? for i (issues/**/tags/status:done) print $i:h:h.
+All of that kind of information are just enumerations.
+
+deps/ subdirectory in each issue can contain symbolic link to another
+issue, referencing it. Create another kind of links between them as you
+wish.
+
+Want to search among the issues? Just use git grep, or ordinary grep!
+Want to search through attached PDFs or other kind of documents? You are
+free to index issues/ directory with something like recoll. Want to see
+the whole history of changes related to specific issues? Just run
+git log issues/issue-name! You can add a tag by simply touching
+issues/issues-name/tags/tag, but "tag-add" included in schwabrak creates
+symbolic links to tags/tag and checks if the tags is known beforehand,
+to keep tags set consistent. Want to remove tag? (git) rm
+issues/issues-name/tags/tag!
+
+"comment" issue's file is intended to keep the last comment related to
+the issue. By committing it you automatically accompany it with your
+(commit's author) name and the time it was added.
+    $ cat > issues/issues-name/comment <<EOF
+    Here are my thoughts:
+    * bla bla bla
+    EOF
+    $ git commit issues/issues-name/comment
+And you can view all comments/authors history later just with:
+    $ git log -p issues/issues-name/comment
+Or by using "comment-list" command included in that distribution.
+And of course you can add additional files with each commit.
+
+schwabrak includes a bunch of utilities to slightly ease dealing with
+all of that data. Personally I "hash -d" schwabrak's directory to be
+able to quickly call commands from it:
+    $ hash -d s=~/work/schwabrak
+    $ cd my/issues
+    $ ~s/add proj/issue name to create
+To ease new issue's directory structure creation you can use "add"
+command as seen above. And yes, that is intended that "proj/issue" and
+"name", "to", "create" can be passed as separate arguments, but
+proj/issue-name-to-create will be created as expected.
+
+All utilities strip off possible "issues/" prefix from issues name, so
+you can easily complete issue names with something like fzf. "cd"
+utility exactly runs fzf to show you available issues, outputing path to
+selected one.
+
+"list" lists all issues in tab separated format, sorting by descending
+creation date and showing their tags. "tag-list issue" prints issue's
+tags if any. "tag-add (issues/)issue (tags/)tag" adds a tag, as was
+noticed before. "dep-add issue-dst issue-src" will link issue-src in
+issues-dst's deps/. "show issue" shows most of issue's information in
+human friendly way.
+
+comment-list, recfile-export and recfile-export-all produces
+recutils'es compatible recfile output, that can is machine friendlier.
diff --git a/add b/add
new file mode 100755 (executable)
index 0000000..04818bd
--- /dev/null
+++ b/add
@@ -0,0 +1,17 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+name="$@"
+name=${name#issues/}
+name=$name:h/`print -- $name:t | endash`
+name=${name#./}
+print $name
+mkdir issues/$name
+mkdir issues/$name/tags
+zmodload -F zsh/datetime b:strftime
+TZ=UTC strftime "%Y-%m-%d %H:%M:%S" > issues/$name/created
+touch issues/$name/about issues/$name/result issues/$name/comment
+$EDITOR issues/$name/about
+git add issues/$name
diff --git a/cd b/cd
new file mode 100755 (executable)
index 0000000..b419c6f
--- /dev/null
+++ b/cd
@@ -0,0 +1,7 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+$root/list | fzf -d "\t" --tac --preview="$root/show {2}" | cut -f2 | read d
+print issues/$d
diff --git a/comment-list b/comment-list
new file mode 100755 (executable)
index 0000000..284cd72
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+issue=${1#issues/}
+hashes=(`git log --format=format:%H issues/$issue/comment`)
+for i ({${#hashes}..1}) {
+    header=`git show --no-patch --format=format:"When: %ai%nAuthor: %an <%ae>" ${hashes[$i]}`
+    comment=`git cat-file blob ${hashes[$i]}:issues/$issue/comment | sed "s/^/+ /"`
+    [[ -n $comment ]] || continue
+    print $header
+    print Comment:
+    print $comment
+    [[ $i -eq 1 ]] || print
+}
diff --git a/dep-add b/dep-add
new file mode 100755 (executable)
index 0000000..8079ef9
--- /dev/null
+++ b/dep-add
@@ -0,0 +1,20 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+usage() {
+    die Usage: $0 ISSUE-DST ISSUE-SRC
+}
+
+[[ -n $1 ]] || usage
+[[ -n $2 ]] || usage
+dst=issues/${1#issues/}
+src=issues/${2#issues/}
+[[ -d $dst ]] || die Unexistent dst
+[[ -d $src ]] || die Unexistent src
+dst=$dst:a
+src=$src:a
+mkdir -p $dst/deps
+cd $dst/deps
+ln -f -s `relpath $src .`
diff --git a/lib.zsh.rc b/lib.zsh.rc
new file mode 100644 (file)
index 0000000..159dbb2
--- /dev/null
@@ -0,0 +1,30 @@
+set -e
+setopt EXTENDED_GLOB GLOB_STAR_SHORT
+
+PERL=${PERL:-perl}
+EDITOR=${EDITOR:-vi}
+
+die() {
+    print $@ >&2
+    exit 1
+}
+
+[[ -d issues ]] || die You must run that command in directory with issues/
+
+relpath() {
+    $PERL -mFile::Spec -le "print File::Spec->abs2rel(@ARGV)" $1:a $2:a
+}
+
+endash() {
+    $PERL -npe 's/(-+)/$1-/g ; s/ /-/g'
+}
+
+dedash() {
+    $PERL -npe 's/([^-])-([^-])/$1 $2/g ; s/-(-+)/$1/g'
+}
+
+delim() {
+    local i
+    for i ({1..40}) print -n -- -
+    print
+}
diff --git a/list b/list
new file mode 100755 (executable)
index 0000000..3495d6b
--- /dev/null
+++ b/list
@@ -0,0 +1,11 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+for issue (issues/**/created) {
+    issue=$issue:h
+    issue=${issue#issues/}
+    print -n `cat issues/$issue/created`\\t$issue\\t
+    $root/tag-list $issue
+} | sort -r
diff --git a/recfile-export b/recfile-export
new file mode 100755 (executable)
index 0000000..95efcf5
--- /dev/null
@@ -0,0 +1,18 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+issue=${1#issues/}
+print Created: `cat issues/$issue/created`
+print Issue: $issue
+print Project: $issue:h
+print Name: `print $issue:t | dedash`
+for tag (`$root/tag-list $issue`) print Tag: $tag
+for dep (issues/$issue/deps/*(N)) print Depends: $(relpath $(realpath $dep) issues)
+print About:
+sed "s/^/+ /" < issues/$issue/about
+print Result:
+sed "s/^/+ /" < issues/$issue/result
+print Comments:
+$root/comment-list $issue | sed "s/^/+ /"
diff --git a/recfile-export-all b/recfile-export-all
new file mode 100755 (executable)
index 0000000..fd068b1
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+$root/list | while read line ; do
+    line=(${=line})
+    issue=${line[3]}
+    $root/recfile-export $issue
+    print
+done
diff --git a/show b/show
new file mode 100755 (executable)
index 0000000..0792bc8
--- /dev/null
+++ b/show
@@ -0,0 +1,22 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+issue=${1#issues/}
+print `cat issues/$issue/created` \| $issue:h \| `print $issue:t | dedash`
+$root/tag-list $issue
+deps=(issues/$issue/deps/*(N))
+[[ ${#deps} -eq 0 ]] || {
+    delim
+    print Depends on:
+    for dep ($deps) print "\t"$dep:t
+}
+delim
+cat issues/$issue/about
+[[ -s issues/$issue/result ]] && {
+    delim
+    cat issues/$issue/result
+}
+delim
+$root/comment-list $issue
diff --git a/tag-add b/tag-add
new file mode 100755 (executable)
index 0000000..a7e43cc
--- /dev/null
+++ b/tag-add
@@ -0,0 +1,17 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+usage() {
+    die Usage: $0 ISSUE TAG
+}
+
+[[ -n $1 ]] || usage
+[[ -n $2 ]] || usage
+issue=${1#issues/}
+tag=tags/$2:t
+tag=$tag:a
+[[ -r $tag ]] || die Unknown tag
+mkdir -p issues/$issue/tags
+ln -f -s `relpath $tag issues/$issue/tags` issues/$issue/tags/$tag:t
diff --git a/tag-list b/tag-list
new file mode 100755 (executable)
index 0000000..011225d
--- /dev/null
+++ b/tag-list
@@ -0,0 +1,9 @@
+#!/usr/bin/env zsh
+
+root=$0:h:a
+. $root/lib.zsh.rc
+
+issue=${1#issues/}
+tags=()
+for t (issues/$issue/tags/*(NOn)) tags=($t:t $tags)
+[[ ${#tags} -eq 0 ]] || print $tags