beastie 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.textile +79 -0
- data/Rakefile +1 -0
- data/beastie.gemspec +23 -0
- data/bin/beastie +6 -0
- data/lib/beastie/issue.rb +151 -0
- data/lib/beastie/version.rb +3 -0
- data/lib/beastie.rb +110 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c52cc68533324dfc1d94ca88426850744b211c0a
|
4
|
+
data.tar.gz: aec85bee46e23870970efe04ff84f5db48f1cedc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 17dbb387afdd0873ae92597dc069be60b549efb6ad744da231cf60c248f8160284769bd0a1161b02200de9962886a2b4220249fa13b636e3c1753ed827e1f991
|
7
|
+
data.tar.gz: 385fef52bde1882313043b40208db029a081c07a5a2f296c31cbc9f7ddd37e66d861fd00f2273149c4612b24b1e34769fd9e5223b6f4d258fcdc97d6142653eb
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Adolfo Villafiorita
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
h1. Beastie
|
2
|
+
|
3
|
+
A bare-bones command-line bug tracking system for personal or small projects which require little formality.
|
4
|
+
|
5
|
+
h2. Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
<pre>
|
10
|
+
gem 'beastie'
|
11
|
+
</pre>
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
<pre>
|
16
|
+
$ bundle
|
17
|
+
</pre>
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
<pre>
|
22
|
+
$ gem install beastie
|
23
|
+
</pre>
|
24
|
+
|
25
|
+
h2. Usage
|
26
|
+
|
27
|
+
Beastie is a bug tracking system at its bare essentials.
|
28
|
+
|
29
|
+
Main commands:
|
30
|
+
|
31
|
+
- @beastie new@ := asks information about an issue and generate a YAML file in the current directory.
|
32
|
+
|
33
|
+
- @beastie list@ := reads all YAML files in the current directory and generate a list of all the issues.
|
34
|
+
|
35
|
+
- @beastie show N@ := shows issue @N@, where @N@ is the identifier shown by the @beastie list@ command.
|
36
|
+
|
37
|
+
- @beastie edit N@ := edits issue @N@. The commands invokes the editor set in the shell @EDITOR@ variable or use @emacsclient@ if @EDITOR@ is unset.
|
38
|
+
|
39
|
+
- @beastie help@ := unleashes the real power of @beastie@.
|
40
|
+
|
41
|
+
h2. Limitations and Warnings
|
42
|
+
|
43
|
+
Beastie uses the *current directory* for all operations. While this gives a lot of flexibility and freedom in choosing one's standards, it also requires discipline: so, make sure you select one directory where issues are stored and always invoke @beastie@ from that directory.
|
44
|
+
|
45
|
+
Beastie tries to generate human-readable filenames, following the convention "Jekyll":http://jekyllrb.com has for blog posts. Selecting issues using filenames, however, is a bit clumsy and to simplify operations @beastie@ assigns a number to each issue, which can be used to reference them. The number can be seen with the @list@ command and depends upon the order in which issues are parsed. Thus *the same issue might be assigned a different id over time*, if the order in which files are read changes (e.g., if a new back-dated issue is added by hand).
|
46
|
+
|
47
|
+
Beastie does not have a specific workflow for bug statutes.
|
48
|
+
|
49
|
+
Beastie does not perform syntax checks when reading data. This can be changed by specifying appropriate input functions in the @ISSUE_FIELDS@ variable (@lib/beastie/issue.rb@).
|
50
|
+
|
51
|
+
Beastie asks various information about a bug. Change the @ISSUE_FIELDS@ variable, if you are not happy with the default fieldset.
|
52
|
+
|
53
|
+
|
54
|
+
h2. Similar solutions
|
55
|
+
|
56
|
+
There are different command-line bug tracking systems. The ones I came across with before re-inventing the wheel include: ditz, "bugs everywhere":http://bugseverywhere.org, "ticgit-Ng":https://github.com/jeffWelling/ticgit, "bugzyrb":https://github.com/rkumar/bugzyrb, "git-issues":https://github.com/duplys/git-issues, "later":https://github.com/qznc/later.
|
57
|
+
|
58
|
+
Why did I re-invent the wheel?
|
59
|
+
|
60
|
+
# for fun
|
61
|
+
# human-readable filenames, so that I can manage issues without using beastie, if I want
|
62
|
+
# a "programmable" fieldset (see @lib/beastie/issue.rb@)
|
63
|
+
# keeping the solution simple
|
64
|
+
|
65
|
+
h2. Author
|
66
|
+
|
67
|
+
Adolfo Villafiorita
|
68
|
+
|
69
|
+
h2. License
|
70
|
+
|
71
|
+
"MIT":https://github.com/avillafiorita/beastie/blob/master/LICENSE.txt
|
72
|
+
|
73
|
+
h2. Contributing
|
74
|
+
|
75
|
+
1. Fork it
|
76
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
77
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
78
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
79
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/beastie.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'beastie/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "beastie"
|
8
|
+
spec.version = Beastie::VERSION
|
9
|
+
spec.authors = ["Adolfo Villafiorita"]
|
10
|
+
spec.email = ["adolfo.villafiorita@me.com"]
|
11
|
+
spec.description = %q{A command-line issue and bug tracking system}
|
12
|
+
spec.summary = %q{A command-line issue and bug tracking system which uses a file per bug, yaml for data storage and it is not opinionated about versioning system, workflows, fieldsets, etc. Useful for small and personal projects when high formality is not required.}
|
13
|
+
spec.homepage = "https://github.com/avillafiorita/beastie"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
data/bin/beastie
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Beastie
|
6
|
+
class Issue
|
7
|
+
# A default issue has:
|
8
|
+
#
|
9
|
+
# - title
|
10
|
+
# - status
|
11
|
+
# - created
|
12
|
+
# - component
|
13
|
+
# - priority
|
14
|
+
# - severity
|
15
|
+
# - points
|
16
|
+
# - type
|
17
|
+
# - description
|
18
|
+
#
|
19
|
+
# Customizing the fields:
|
20
|
+
#
|
21
|
+
# - the fields aske by the new command are specified by the
|
22
|
+
# ISSUE_FIELDS variable
|
23
|
+
#
|
24
|
+
# - for each field, the structure if the following:
|
25
|
+
#
|
26
|
+
# field name => init value, prompt
|
27
|
+
#
|
28
|
+
# where
|
29
|
+
# * "init value" is evaluated (use "gets.chomp" to ask input)
|
30
|
+
# * prompt is what is asked to the user. Use an empty string for
|
31
|
+
# a value which is filled automatically.
|
32
|
+
#
|
33
|
+
# - title, created, and status are compulsory: do not delete them
|
34
|
+
# (gen_filename depends upon title and created; the close
|
35
|
+
# command depends upon status)
|
36
|
+
#
|
37
|
+
ISSUE_FIELDS = {
|
38
|
+
"title" => ["gets.chomp", "Short description of the issue"],
|
39
|
+
"status" => ["'open'", ""],
|
40
|
+
"created" => ["Date.today", ""],
|
41
|
+
"component" => ["gets.chomp", "Component affected by the issue"],
|
42
|
+
"priority" => ["get_int", "Priority (an integer number, e.g., from 1 to 5)"],
|
43
|
+
"severity" => ["get_int", "Severity (an integer number, e.g., from 1 to 5)"],
|
44
|
+
"points" => ["get_int", "Points (an integer estimating the difficulty of the issue)"],
|
45
|
+
"type" => ["gets.chomp", "Type (e.g., story, task, bug, refactoring)"],
|
46
|
+
"description" => ["get_lines", "Longer description (terminate with '.')"]
|
47
|
+
}
|
48
|
+
|
49
|
+
# which fields go to the report and formatting options
|
50
|
+
REPORT_FIELDS = {
|
51
|
+
"status" => "%-10s",
|
52
|
+
"type" => "%-10s",
|
53
|
+
"priority" => "%8s",
|
54
|
+
"severity" => "%8s",
|
55
|
+
"created" => "%-10s",
|
56
|
+
"title" => "%-32s"
|
57
|
+
}
|
58
|
+
|
59
|
+
# these keep the actual values
|
60
|
+
attr_reader :issue
|
61
|
+
# the filename, after the issue has been saved or if it has been loaded
|
62
|
+
attr_reader :filename
|
63
|
+
|
64
|
+
# interactively ask from command line all fields specified in ISSUE_FIELDS
|
65
|
+
def ask
|
66
|
+
@issue = Hash.new
|
67
|
+
|
68
|
+
ISSUE_FIELDS.keys.each do |key|
|
69
|
+
puts "#{ISSUE_FIELDS[key][1]}: " if ISSUE_FIELDS[key][1] != ""
|
70
|
+
@issue[key] = (eval ISSUE_FIELDS[key][0]) || ""
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# load from command line
|
75
|
+
def load filename
|
76
|
+
@filename = filename
|
77
|
+
@issue = YAML.load_file(filename)
|
78
|
+
end
|
79
|
+
|
80
|
+
def change field, value
|
81
|
+
@issue[field] = value
|
82
|
+
end
|
83
|
+
|
84
|
+
# save object to file
|
85
|
+
def save
|
86
|
+
@filename = @filename || gen_filename
|
87
|
+
|
88
|
+
file = File.open(@filename, 'w') { |f|
|
89
|
+
f.puts @issue.to_yaml
|
90
|
+
}
|
91
|
+
|
92
|
+
puts "Issue saved to #{filename}"
|
93
|
+
end
|
94
|
+
|
95
|
+
# return the n-th filename
|
96
|
+
def self.filename n
|
97
|
+
Dir.glob('*.{yml,yaml}')[n - 1]
|
98
|
+
end
|
99
|
+
|
100
|
+
# list all issues in current directory
|
101
|
+
def self.list
|
102
|
+
# print header
|
103
|
+
printf "ID "
|
104
|
+
REPORT_FIELDS.keys.each do |key|
|
105
|
+
printf REPORT_FIELDS[key] + " ", key
|
106
|
+
end
|
107
|
+
printf "\n"
|
108
|
+
|
109
|
+
# print issues
|
110
|
+
file_no = 0
|
111
|
+
Dir.glob('*.{yml,yaml}') do |file|
|
112
|
+
data = YAML.load_file(file)
|
113
|
+
file_no += 1
|
114
|
+
|
115
|
+
printf "%3d ", file_no
|
116
|
+
REPORT_FIELDS.keys.each do |key|
|
117
|
+
printf REPORT_FIELDS[key] + " ", data[key]
|
118
|
+
end
|
119
|
+
printf "\n"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# read n-lines (terminated by a ".")
|
126
|
+
def get_lines
|
127
|
+
$/ = "."
|
128
|
+
STDIN.gets.chomp(".")
|
129
|
+
end
|
130
|
+
|
131
|
+
# read an integer
|
132
|
+
def get_int
|
133
|
+
gets.chomp.to_i
|
134
|
+
end
|
135
|
+
|
136
|
+
# return a unique filename for this issue
|
137
|
+
def gen_filename
|
138
|
+
name = @issue["created"].strftime("%Y-%m-%d-") +
|
139
|
+
@issue["title"].gsub(/\W+/, "_") +
|
140
|
+
".yaml"
|
141
|
+
n = 1
|
142
|
+
while File.exist?(name)
|
143
|
+
name = File.basename(name, ".yaml") + "-" + n.to_s + ".yaml"
|
144
|
+
n += 1
|
145
|
+
end
|
146
|
+
|
147
|
+
name
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
data/lib/beastie.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
require_relative "beastie/version"
|
5
|
+
require_relative "beastie/issue"
|
6
|
+
|
7
|
+
module Beastie
|
8
|
+
|
9
|
+
# tell option parser to accept an existing directory
|
10
|
+
OptionParser.accept(Pathname) do |pn|
|
11
|
+
Pathname.new(pn) if pn
|
12
|
+
raise OptionParser::InvalidArgument, pn if not Dir.exist?(pn)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Bestie manages issues from the command line.
|
16
|
+
# Each issue is a text file in YAML, whose name is Jekyll-post like: DATE-TITLE.yml
|
17
|
+
#
|
18
|
+
# Two simple commands
|
19
|
+
# - beastie new (create a new issue in the current directory
|
20
|
+
# - beastie list (list relevant firelds of issues stored in the current directory)
|
21
|
+
|
22
|
+
class Runner
|
23
|
+
# the editor to use with the edit command
|
24
|
+
EDITOR = "emacsclient"
|
25
|
+
|
26
|
+
def self.run(args)
|
27
|
+
|
28
|
+
# options = {}
|
29
|
+
|
30
|
+
# global_options = OptionParser.new do |opts|
|
31
|
+
# opts.banner = "Usage: beastie [global options] <command> [[command options] <args>]"
|
32
|
+
|
33
|
+
# # Mandatory argument.
|
34
|
+
# opts.on("-d", "--directory DIR", Pathname,
|
35
|
+
# "Use DIR as the directory to store (and retrieve) issues") do |dir|
|
36
|
+
# options[:dir] = dir
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
|
40
|
+
# global_options.order!
|
41
|
+
command = args[0]
|
42
|
+
args.shift
|
43
|
+
|
44
|
+
case command
|
45
|
+
|
46
|
+
when "new"
|
47
|
+
issue = Issue.new
|
48
|
+
issue.ask
|
49
|
+
issue.save
|
50
|
+
|
51
|
+
when "edit"
|
52
|
+
issue_no = args[0].to_i
|
53
|
+
shell_editor = `echo ${EDITOR}`.chomp
|
54
|
+
editor = shell_editor == "" ? EDITOR : shell_editor
|
55
|
+
system("#{editor} #{Issue.filename issue_no}")
|
56
|
+
|
57
|
+
when "show"
|
58
|
+
issue_no = args[0].to_i
|
59
|
+
system("cat #{Issue.filename issue_no}")
|
60
|
+
|
61
|
+
when "change"
|
62
|
+
issue_no = args[0].to_i
|
63
|
+
field = args[1]
|
64
|
+
value = args[2]
|
65
|
+
issue = Issue.new
|
66
|
+
issue.load(Issue.filename issue_no)
|
67
|
+
issue.change field, value
|
68
|
+
issue.save
|
69
|
+
|
70
|
+
when "close"
|
71
|
+
issue_no = args[0].to_i
|
72
|
+
issue = Issue.new
|
73
|
+
issue.load(Issue.filename issue_no)
|
74
|
+
issue.change "status", "closed"
|
75
|
+
issue.save
|
76
|
+
|
77
|
+
when "list"
|
78
|
+
Beastie::Issue.list
|
79
|
+
|
80
|
+
when "help"
|
81
|
+
help
|
82
|
+
|
83
|
+
when "version"
|
84
|
+
puts "beastie version #{VERSION}"
|
85
|
+
|
86
|
+
else
|
87
|
+
help
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def self.help
|
95
|
+
puts "beastie command args"
|
96
|
+
puts ""
|
97
|
+
puts "A simple command-line bug-tracking system"
|
98
|
+
puts ""
|
99
|
+
puts "Commands:"
|
100
|
+
puts " new create a new issue in current directory"
|
101
|
+
puts " list list all issues stored in current directory"
|
102
|
+
puts " edit N edit issue with id N (where N is the output of the list command)"
|
103
|
+
puts " show N show issue with id N (where N is the output of the list command)"
|
104
|
+
puts " change N f v change value of field 'f' to 'v' in id N"
|
105
|
+
puts " close N shortcut for 'change N status closed'"
|
106
|
+
puts " version print version number"
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: beastie
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adolfo Villafiorita
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: A command-line issue and bug tracking system
|
42
|
+
email:
|
43
|
+
- adolfo.villafiorita@me.com
|
44
|
+
executables:
|
45
|
+
- beastie
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- .gitignore
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.textile
|
53
|
+
- Rakefile
|
54
|
+
- beastie.gemspec
|
55
|
+
- bin/beastie
|
56
|
+
- lib/beastie.rb
|
57
|
+
- lib/beastie/issue.rb
|
58
|
+
- lib/beastie/version.rb
|
59
|
+
homepage: https://github.com/avillafiorita/beastie
|
60
|
+
licenses:
|
61
|
+
- MIT
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.0.14
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: A command-line issue and bug tracking system which uses a file per bug, yaml
|
83
|
+
for data storage and it is not opinionated about versioning system, workflows, fieldsets,
|
84
|
+
etc. Useful for small and personal projects when high formality is not required.
|
85
|
+
test_files: []
|