mistilteinn 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.mistilteinn/config.yaml +9 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/README.markdown +84 -0
- data/Rakefile +20 -0
- data/bin/.gitkeep +0 -0
- data/bin/mistilteinn +42 -0
- data/lib/mistilteinn/cli.rb +6 -0
- data/lib/mistilteinn/cli/command.rb +47 -0
- data/lib/mistilteinn/cli/create.rb +10 -0
- data/lib/mistilteinn/cli/hooks/commit-msg +14 -0
- data/lib/mistilteinn/cli/hooks/common.sh +60 -0
- data/lib/mistilteinn/cli/hooks/pre-commit +22 -0
- data/lib/mistilteinn/cli/init.rb +46 -0
- data/lib/mistilteinn/cli/is_inside.rb +17 -0
- data/lib/mistilteinn/cli/list.rb +12 -0
- data/lib/mistilteinn/cli/self_check.rb +59 -0
- data/lib/mistilteinn/cli/templates/config.yaml +12 -0
- data/lib/mistilteinn/config.rb +54 -0
- data/lib/mistilteinn/git.rb +30 -0
- data/lib/mistilteinn/http_util.rb +42 -0
- data/lib/mistilteinn/ticket.rb +19 -0
- data/lib/mistilteinn/ticket/entry.rb +13 -0
- data/lib/mistilteinn/ticket/git_config.rb +157 -0
- data/lib/mistilteinn/ticket/github.rb +37 -0
- data/lib/mistilteinn/ticket/redmine.rb +49 -0
- data/lib/mistilteinn/version.rb +6 -0
- data/mistilteinn.gemspec +22 -0
- data/spec/.gitkeep +0 -0
- data/spec/config_spec.rb +46 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/ticket/redmine_spec.rb +86 -0
- metadata +126 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
Mistilteinn for shell
|
2
|
+
==============================
|
3
|
+
[![Build Status](https://secure.travis-ci.org/mistilteinn/mistilteinn-shell.png)](http://travis-ci.org/mistilteinn/mistilteinn-shell)
|
4
|
+
|
5
|
+
OVERVIEW
|
6
|
+
------------------------------
|
7
|
+
|
8
|
+
Mistilteinn for shell is a comamnd to support a development style for
|
9
|
+
using git and redmine.
|
10
|
+
|
11
|
+
Prerequires
|
12
|
+
------------------------------
|
13
|
+
|
14
|
+
* Ruby 1.8.7
|
15
|
+
* RubyGems 1.4.2 or later
|
16
|
+
* git-hooks http://github.com/mistilteinn/git-hooks
|
17
|
+
* git subcommands https://github.com/mistilteinn/git-tools
|
18
|
+
|
19
|
+
Install
|
20
|
+
------------------------------
|
21
|
+
|
22
|
+
### Prerequires
|
23
|
+
|
24
|
+
$ bundle install --path vendor/bundle
|
25
|
+
|
26
|
+
### Run
|
27
|
+
|
28
|
+
$ bundle exec mistilteinn
|
29
|
+
|
30
|
+
### Run test
|
31
|
+
|
32
|
+
$ bundle exec rake spec
|
33
|
+
|
34
|
+
### Package
|
35
|
+
|
36
|
+
$ bundle exec rake build
|
37
|
+
|
38
|
+
### Install to system
|
39
|
+
|
40
|
+
$ bundle exec rake build
|
41
|
+
$ cd pkg
|
42
|
+
$ gem install mistilteinn-*.gem
|
43
|
+
|
44
|
+
Usage
|
45
|
+
------------------------------
|
46
|
+
|
47
|
+
### self cehck
|
48
|
+
|
49
|
+
Check if miltilteinn works:
|
50
|
+
|
51
|
+
$ mistilteinn self-check
|
52
|
+
|
53
|
+
### init
|
54
|
+
|
55
|
+
Initialize working directory for mistilteinn:
|
56
|
+
|
57
|
+
$ mistilteinn init
|
58
|
+
$ vim .mistilteinn/config.yaml
|
59
|
+
|
60
|
+
### list ticket
|
61
|
+
|
62
|
+
$ mistilteinn list
|
63
|
+
|
64
|
+
### create ticket
|
65
|
+
|
66
|
+
$ mistilteinn create hoge
|
67
|
+
|
68
|
+
AUTHOR
|
69
|
+
------------------------------
|
70
|
+
|
71
|
+
* @mzp
|
72
|
+
* @suer
|
73
|
+
|
74
|
+
License
|
75
|
+
-----------------------
|
76
|
+
|
77
|
+
The MIT License (MIT) Copyright (c) 2012 codefirst.org
|
78
|
+
|
79
|
+
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:
|
80
|
+
|
81
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
82
|
+
|
83
|
+
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.
|
84
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- mode:ruby -*-
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/clean'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
require "bundler/gem_tasks"
|
7
|
+
|
8
|
+
CLEAN.include(
|
9
|
+
"**/*.db"
|
10
|
+
)
|
11
|
+
|
12
|
+
CLOBBER.include(
|
13
|
+
"pkg"
|
14
|
+
)
|
15
|
+
|
16
|
+
RSpec::Core::RakeTask.new do |t|
|
17
|
+
t.pattern = 'spec/**/*_spec.rb'
|
18
|
+
end
|
19
|
+
|
20
|
+
task :default => [:spec, :clean]
|
data/bin/.gitkeep
ADDED
File without changes
|
data/bin/mistilteinn
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
require 'pp'
|
3
|
+
require 'subcommand'
|
4
|
+
require 'mistilteinn/config'
|
5
|
+
require 'mistilteinn/git'
|
6
|
+
require 'mistilteinn/ticket'
|
7
|
+
require 'mistilteinn/cli'
|
8
|
+
|
9
|
+
include Subcommands
|
10
|
+
|
11
|
+
options = {}
|
12
|
+
global_options do|opts|
|
13
|
+
options[:config] = File.expand_path '.mistilteinn/config.yaml', Mistilteinn::Git.root
|
14
|
+
opts.on('-c CONFIG', '--config=CONIF') do|config|
|
15
|
+
options[:config] = config
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def curry(*args)
|
20
|
+
lambda{|obj| obj.send(*args)}
|
21
|
+
end
|
22
|
+
|
23
|
+
commands = Mistilteinn::Cli::Command.commands.map(&curry(:new, self))
|
24
|
+
|
25
|
+
name = opt_parse
|
26
|
+
|
27
|
+
begin
|
28
|
+
config = Mistilteinn::Config.load options[:config]
|
29
|
+
rescue => e
|
30
|
+
puts "#{options[:config]}: #{e.message}"
|
31
|
+
exit 1
|
32
|
+
end
|
33
|
+
|
34
|
+
obj = commands.find{|c| c.name == (name || '-').to_sym }
|
35
|
+
if obj then
|
36
|
+
obj.run config, ARGV
|
37
|
+
else
|
38
|
+
puts <<END
|
39
|
+
Please see '#{$0} help'
|
40
|
+
END
|
41
|
+
exit 1
|
42
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
|
3
|
+
module Mistilteinn
|
4
|
+
module Cli
|
5
|
+
class Command
|
6
|
+
attr_reader :its, :config, :args
|
7
|
+
|
8
|
+
def initialize(obj)
|
9
|
+
obj.command name do|opts|
|
10
|
+
opts.description = 'show current ticket list'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def its
|
15
|
+
klass = Mistilteinn::Ticket[config.ticket.source]
|
16
|
+
klass.new config
|
17
|
+
end
|
18
|
+
|
19
|
+
def run(config, args)
|
20
|
+
@config = config
|
21
|
+
@args = args
|
22
|
+
action
|
23
|
+
end
|
24
|
+
|
25
|
+
@@klass = []
|
26
|
+
class << self
|
27
|
+
def inherited(k)
|
28
|
+
@@klass << k
|
29
|
+
end
|
30
|
+
|
31
|
+
def commands
|
32
|
+
@@klass
|
33
|
+
end
|
34
|
+
|
35
|
+
def name(x)
|
36
|
+
define_method(:name){ x.to_sym }
|
37
|
+
end
|
38
|
+
|
39
|
+
def desc(x)
|
40
|
+
define_method(:desc){ x }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
|
3
|
+
getGitBranchName()
|
4
|
+
{
|
5
|
+
branch="$(git symbolic-ref HEAD 2>/dev/null)" ||
|
6
|
+
"$(git describe --contains --all HEAD)"
|
7
|
+
echo ${branch##refs/heads/}
|
8
|
+
}
|
9
|
+
|
10
|
+
isOnMasterBranch()
|
11
|
+
{
|
12
|
+
if [ "$(getGitBranchName)" = "master" ]; then
|
13
|
+
return 0
|
14
|
+
fi
|
15
|
+
return 1
|
16
|
+
}
|
17
|
+
|
18
|
+
appendMsgTo1stLine()
|
19
|
+
{
|
20
|
+
mv $1 $1.$$
|
21
|
+
if [ -s "$1.$$" ]; then
|
22
|
+
if head -1 "$1.$$" | grep "$2" > /dev/null; then
|
23
|
+
cp "$1.$$" "$1"
|
24
|
+
else
|
25
|
+
sed '1s/$/ '"$2"'/' "$1.$$" > $1
|
26
|
+
fi
|
27
|
+
else
|
28
|
+
echo "$2" > "$1"
|
29
|
+
fi
|
30
|
+
rm -f $1.$$
|
31
|
+
}
|
32
|
+
|
33
|
+
extractTicketId()
|
34
|
+
{
|
35
|
+
echo "$(getGitBranchName)" \
|
36
|
+
| awk 'BEGIN{ FS="[/]"}
|
37
|
+
$1 == "id" { printf "refs #%s", $2 }
|
38
|
+
$2 == "id" { printf "refs #%s", $3 }'
|
39
|
+
}
|
40
|
+
|
41
|
+
hasTicketId()
|
42
|
+
{
|
43
|
+
first="$(git cat-file -p $1 \
|
44
|
+
| sed '1,/^$/d' | head -1 \
|
45
|
+
| sed '/.*refs #[0-9][0-9]*.*/!d')"
|
46
|
+
|
47
|
+
if [ -n "${first}" ]; then
|
48
|
+
echo "true"
|
49
|
+
else
|
50
|
+
echo "false"
|
51
|
+
fi
|
52
|
+
}
|
53
|
+
|
54
|
+
extractParents()
|
55
|
+
{
|
56
|
+
parents="$(git cat-file -p $1 \
|
57
|
+
| grep '^parent [0-9a-f]\{40\}$')"
|
58
|
+
echo "${parents##parent }"
|
59
|
+
}
|
60
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
|
3
|
+
if [ -z "$(git branch)" ]; then
|
4
|
+
exit 0
|
5
|
+
fi
|
6
|
+
|
7
|
+
if [ -n "${GIT_DIR}" ]; then
|
8
|
+
hooksdir="./${GIT_DIR}/hooks/"
|
9
|
+
else
|
10
|
+
hooksdir="./"
|
11
|
+
fi
|
12
|
+
|
13
|
+
. "${hooksdir}common.sh"
|
14
|
+
|
15
|
+
isOnMasterBranch
|
16
|
+
if [ $? -eq 0 ]; then
|
17
|
+
echo "can't commit on master branch."
|
18
|
+
echo "please commit on topic branch."
|
19
|
+
exit 1
|
20
|
+
fi
|
21
|
+
|
22
|
+
exit 0
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- coding: undecided -*-
|
2
|
+
require 'pathname'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'mistilteinn/git'
|
5
|
+
|
6
|
+
module Mistilteinn::Cli
|
7
|
+
class Init < ::Mistilteinn::Cli::Command
|
8
|
+
name :init
|
9
|
+
desc 'initialize this repository for mistilteinn'
|
10
|
+
|
11
|
+
def action
|
12
|
+
copy( children('hooks'), git_root('.git/hooks') )
|
13
|
+
copy( children('templates'), git_root('.mistilteinn') )
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def children(name)
|
18
|
+
path = Pathname(File.dirname(__FILE__)) + name
|
19
|
+
path.children
|
20
|
+
end
|
21
|
+
|
22
|
+
def git_root(name)
|
23
|
+
path = Pathname(::Mistilteinn::Git.root) + name
|
24
|
+
path.tap {
|
25
|
+
unless path.exist?
|
26
|
+
puts "mkdir -p #{config}"
|
27
|
+
config.mkpath
|
28
|
+
end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def copy(files, dir)
|
33
|
+
puts "copy to #{dir}"
|
34
|
+
files.each do|src|
|
35
|
+
print "generate #{src.basename}..."
|
36
|
+
dest = dir + src.basename
|
37
|
+
if dest.exist? then
|
38
|
+
puts "skip"
|
39
|
+
else
|
40
|
+
FileUtils.copy src, dest
|
41
|
+
puts "ok"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
require 'mistilteinn/git'
|
3
|
+
|
4
|
+
module Mistilteinn::Cli
|
5
|
+
class IsInside < ::Mistilteinn::Cli::Command
|
6
|
+
name 'is-inside'
|
7
|
+
desc 'check if current working direcotry is mistilteinn'
|
8
|
+
|
9
|
+
def action
|
10
|
+
if ::Mistilteinn::Git::work_tree? and config.exist? then
|
11
|
+
exit 0
|
12
|
+
else
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Mistilteinn::Cli
|
2
|
+
class SelfCheck < ::Mistilteinn::Cli::Command
|
3
|
+
name 'self-check'
|
4
|
+
desc 'check whether mistilteinn works'
|
5
|
+
|
6
|
+
def action
|
7
|
+
section "ticket source"
|
8
|
+
check("source type") {
|
9
|
+
its.class rescue error("'#{config.ticket.source}' is not valid.")
|
10
|
+
}
|
11
|
+
check(its.class){ its.check }
|
12
|
+
|
13
|
+
section "git"
|
14
|
+
check_if("inside work tree?") { Mistilteinn::Git.work_tree? }
|
15
|
+
check_if("git-now subcommand") { Mistilteinn::Git.command? 'now' }
|
16
|
+
check_if("git-master subcommand") { Mistilteinn::Git.command? 'master' }
|
17
|
+
check_if("git-hooks subcommand") { Mistilteinn::Git.command? 'hooks' }
|
18
|
+
|
19
|
+
puts '','Works! Have a good programming!!'
|
20
|
+
rescue => e
|
21
|
+
puts <<END
|
22
|
+
#{e.message}
|
23
|
+
|
24
|
+
Oh, Mistilteinn does not work.
|
25
|
+
Please check your system or configure file.
|
26
|
+
END
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def error(msg)
|
31
|
+
raise StandardError.new(msg)
|
32
|
+
end
|
33
|
+
|
34
|
+
def section(title)
|
35
|
+
puts <<END
|
36
|
+
|
37
|
+
------------------------------
|
38
|
+
#{title}
|
39
|
+
------------------------------
|
40
|
+
END
|
41
|
+
end
|
42
|
+
|
43
|
+
def check(title, &f)
|
44
|
+
print title, " => "
|
45
|
+
STDOUT.flush
|
46
|
+
puts f.call
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_if(title, &p)
|
50
|
+
check(title) {
|
51
|
+
if p[] then
|
52
|
+
"ok"
|
53
|
+
else
|
54
|
+
error("error")
|
55
|
+
end
|
56
|
+
}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
ticket:
|
2
|
+
# redmine, github or gitconfig
|
3
|
+
source: redmine
|
4
|
+
|
5
|
+
redmine:
|
6
|
+
url: https://example.com/redmine/
|
7
|
+
project: some-project-name
|
8
|
+
apikey: your_key
|
9
|
+
# or 'git config redmine.apikey your_key'
|
10
|
+
|
11
|
+
github:
|
12
|
+
url: https://github.com/mistilteinn/mistilteinn-shell
|
@@ -0,0 +1,54 @@
|
|
1
|
+
#! /opt/local/bin/ruby -w
|
2
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
3
|
+
|
4
|
+
require 'yaml'
|
5
|
+
require 'ostruct'
|
6
|
+
require 'mistilteinn/git'
|
7
|
+
|
8
|
+
module Mistilteinn
|
9
|
+
class ConfigError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class Config
|
13
|
+
def self.load(path)
|
14
|
+
if File.exist? path then
|
15
|
+
self.new(YAML.load_file(path), path)
|
16
|
+
else
|
17
|
+
self.new({} , path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(hash, path = nil)
|
22
|
+
@hash = hash
|
23
|
+
@path = path
|
24
|
+
end
|
25
|
+
|
26
|
+
def exist?
|
27
|
+
File.exist?(@path) if @path
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(name, *args)
|
31
|
+
if args.empty? then
|
32
|
+
key = name.to_s
|
33
|
+
WrapObj.new(key, @hash[key] || {} )
|
34
|
+
else
|
35
|
+
super(name, *args)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class WrapObj
|
40
|
+
def initialize(name, hash)
|
41
|
+
@name = name
|
42
|
+
@hash = hash
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing(name, *args)
|
46
|
+
key = name.to_s
|
47
|
+
super(name, *args) unless args.empty?
|
48
|
+
@hash[key] or
|
49
|
+
::Mistilteinn::Git.config("#{@name}.#{key}") or
|
50
|
+
raise ConfigError.new("no config(#{@name}.#{key})")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#! /opt/local/bin/ruby -w
|
2
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
3
|
+
|
4
|
+
module Mistilteinn
|
5
|
+
module Git
|
6
|
+
class << self
|
7
|
+
def config(name)
|
8
|
+
cmd "git config #{name}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def root
|
12
|
+
cmd "git rev-parse --show-toplevel"
|
13
|
+
end
|
14
|
+
|
15
|
+
def work_tree?()
|
16
|
+
cmd("git rev-parse --is-inside-work-tree") == "true"
|
17
|
+
end
|
18
|
+
|
19
|
+
def command?(cmd)
|
20
|
+
cmd("which git-#{cmd}") != nil
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def cmd(str)
|
25
|
+
str = %x(#{str} 2>/dev/null || echo "").strip
|
26
|
+
str.empty? ? nil : str
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#! /opt/local/bin/ruby -w
|
2
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
3
|
+
|
4
|
+
require 'open-uri'
|
5
|
+
require 'json'
|
6
|
+
require 'net/http'
|
7
|
+
require 'net/https'
|
8
|
+
|
9
|
+
module Mistilteinn
|
10
|
+
module HttpUtil
|
11
|
+
class HttpError < StandardError; end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def get_json(url, params={})
|
15
|
+
url = url.to_s + '?' + params.map{|key,value| "#{key}=#{value}" }.join("&")
|
16
|
+
begin
|
17
|
+
open(url) do |io|
|
18
|
+
JSON.parse(io.read)
|
19
|
+
end
|
20
|
+
rescue => e
|
21
|
+
raise HttpError.new("#{e.message} (#{url})")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def post_json(url, headers, data)
|
26
|
+
http = Net::HTTP.new(url.host, url.port)
|
27
|
+
http.use_ssl = url.scheme == 'https'
|
28
|
+
|
29
|
+
http.start do
|
30
|
+
ret = http.post(url.path,
|
31
|
+
data.to_json,
|
32
|
+
headers.merge("Content-Type" => "application/json"))
|
33
|
+
case ret
|
34
|
+
when Net::HTTPSuccess
|
35
|
+
else
|
36
|
+
raise HttpError.new("#{ret} (#{url})")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'mistilteinn/ticket/redmine'
|
2
|
+
require 'mistilteinn/ticket/github'
|
3
|
+
require 'mistilteinn/ticket/git_config'
|
4
|
+
|
5
|
+
module Mistilteinn
|
6
|
+
module Ticket
|
7
|
+
def [](name)
|
8
|
+
case name.to_sym
|
9
|
+
when :redmine
|
10
|
+
Mistilteinn::Ticket::Redmine
|
11
|
+
when :github
|
12
|
+
Mistilteinn::Ticket::Github
|
13
|
+
when :gitconfig
|
14
|
+
Mistilteinn::Ticket::GitConfig
|
15
|
+
end
|
16
|
+
end
|
17
|
+
module_function :[]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
module Mistilteinn
|
5
|
+
module Ticket
|
6
|
+
class Config
|
7
|
+
def initialize(config_hash, path)
|
8
|
+
@config = config_hash
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
attr_reader :config
|
12
|
+
|
13
|
+
def has_key?(key)
|
14
|
+
@config.has_key?(key) || @config.has_key?(key.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(*args)
|
18
|
+
assignment(args[0], args[1])
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](key)
|
22
|
+
key = key.to_s if key.class == Symbol
|
23
|
+
method_missing(key)
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(name, *args)
|
27
|
+
name = name.to_s
|
28
|
+
is_assignment = create_if_nil = false
|
29
|
+
if name[-1..-1] == '=' then
|
30
|
+
is_assignment = true
|
31
|
+
name.slice! -1
|
32
|
+
end
|
33
|
+
if name[-1..-1] == '_' then
|
34
|
+
create_if_nil = true
|
35
|
+
name.slice! -1
|
36
|
+
end
|
37
|
+
|
38
|
+
return assignment(name, args[0]) if is_assignment
|
39
|
+
|
40
|
+
config_object = get_config_object(@path+[name.to_s])
|
41
|
+
return nil if config_object.config.empty? and not create_if_nil
|
42
|
+
|
43
|
+
if config_object.config.class == Hash then
|
44
|
+
config_object
|
45
|
+
else
|
46
|
+
config_object.config
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
def get_config_object(path)
|
52
|
+
return @@config_objects[path] if @@config_objects.has_key? path
|
53
|
+
|
54
|
+
if @config.has_key? path[-1] then
|
55
|
+
@@config_objects[path] = Config::new(@config[path[-1]], path)
|
56
|
+
else
|
57
|
+
@config[path[-1]] = {}
|
58
|
+
@@config_objects[path] = Config::new({}, path)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def Config::init
|
63
|
+
@@config_objects = {[] => Config::parse_config}
|
64
|
+
@@config_objects[[]]
|
65
|
+
end
|
66
|
+
|
67
|
+
def Config::parse_config
|
68
|
+
config_hash = {}
|
69
|
+
get_by_regexp('".*"').each do |path,value|
|
70
|
+
subconfig = config_hash
|
71
|
+
elems = path.split '.'
|
72
|
+
elems[0..-2].each do |path|
|
73
|
+
subconfig[path] = {} if subconfig[path].nil?
|
74
|
+
subconfig = subconfig[path]
|
75
|
+
end
|
76
|
+
subconfig[elems[-1]] = value
|
77
|
+
end
|
78
|
+
Config::new(config_hash, [])
|
79
|
+
end
|
80
|
+
|
81
|
+
def Config::get_by_regexp(regex)
|
82
|
+
%x(git config --get-regexp #{regex}).split(/\n/).map do |line|
|
83
|
+
line.match(/(.+?) (.*)/)[1..2]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def Config::set(name, value)
|
88
|
+
%x(git config #{name} "#{value}")
|
89
|
+
end
|
90
|
+
|
91
|
+
def assignment(key, value)
|
92
|
+
Config::set("#{@path.join('.')}.#{key}", value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class GitConfig
|
97
|
+
class ConfigError < StandardError; end
|
98
|
+
|
99
|
+
def initialize(config)
|
100
|
+
@config = Config::init
|
101
|
+
end
|
102
|
+
|
103
|
+
def tickets
|
104
|
+
lastTicketNo = @config.ticket.ticketno.to_i
|
105
|
+
(1...lastTicketNo).map do |id|
|
106
|
+
data = @config.ticket["id/#{id}"]
|
107
|
+
::Mistilteinn::Ticket::Entry.new(id, data.subject, data.status)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def create(title = "")
|
112
|
+
ticketFormat = <<END
|
113
|
+
Subject: #{title}
|
114
|
+
Author: #{@config.user.name}
|
115
|
+
Date: #{Time.now}
|
116
|
+
Status: new
|
117
|
+
Description: |-
|
118
|
+
|
119
|
+
END
|
120
|
+
|
121
|
+
editTempFile(ticketFormat) do |f, modified|
|
122
|
+
return if not modified and title.empty?
|
123
|
+
|
124
|
+
ticket = @config.ticket
|
125
|
+
ticketNo = (ticket.ticketno || "1").to_i
|
126
|
+
|
127
|
+
YAML.load_documents(f) do |yaml|
|
128
|
+
yaml.each do |key, value|
|
129
|
+
ticket["id/#{ticketNo}_"][key] = value
|
130
|
+
end
|
131
|
+
end
|
132
|
+
ticket.ticketno = (ticketNo+1).to_s
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def edit(id)
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
def editTempFile(initialString, &proc)
|
141
|
+
tmp = Tempfile.new("tmp")
|
142
|
+
tmp.write initialString
|
143
|
+
tmp.close
|
144
|
+
|
145
|
+
editor = @config.core_.editor || ENV["EDITOR"]
|
146
|
+
system "#{editor} #{tmp.path}"
|
147
|
+
File.open(tmp.path) do |f|
|
148
|
+
modified = f.read != initialString
|
149
|
+
f.rewind
|
150
|
+
proc.call(f, modified)
|
151
|
+
end
|
152
|
+
tmp.unlink
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
require 'mistilteinn/http_util'
|
3
|
+
require 'mistilteinn/ticket/entry'
|
4
|
+
|
5
|
+
module Mistilteinn
|
6
|
+
module Ticket
|
7
|
+
class Github
|
8
|
+
class ConfigError < StandardError; end
|
9
|
+
|
10
|
+
API_URL_ROOT = "https://api.github.com/"
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
@url = config.github.url
|
14
|
+
unless @url
|
15
|
+
raise ConfigError.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def tickets
|
20
|
+
::Mistilteinn::HttpUtil::get_json(api("issues")).map do |issue|
|
21
|
+
::Mistilteinn::Ticket::Entry.new(issue['number'],
|
22
|
+
issue['title'],
|
23
|
+
issue['state'])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def create(title)
|
28
|
+
# FIXME or DIE
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def api(name)
|
33
|
+
URI(API_URL_ROOT) + "./repos/#{URI(@url).path}/#{name}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
#! /opt/local/bin/ruby -w
|
2
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
3
|
+
require 'uri'
|
4
|
+
require 'mistilteinn/http_util'
|
5
|
+
require 'mistilteinn/ticket/entry'
|
6
|
+
|
7
|
+
module Mistilteinn
|
8
|
+
module Ticket
|
9
|
+
class Redmine
|
10
|
+
def initialize(config)
|
11
|
+
@config = config.redmine
|
12
|
+
end
|
13
|
+
|
14
|
+
def tickets
|
15
|
+
HttpUtil.get_json(api('issues'),
|
16
|
+
{ :project_id => @config.project,
|
17
|
+
:key => @config.apikey})['issues'].map{|entry|
|
18
|
+
::Mistilteinn::Ticket::Entry.new(entry['id'],
|
19
|
+
entry['subject'],
|
20
|
+
entry['status']['name'])
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def create(title)
|
25
|
+
HttpUtil.post_json(api('issues'),
|
26
|
+
{ "X-Redmine-API-Key" => @config.apikey },
|
27
|
+
{ :issue => {
|
28
|
+
:project_id => @config.project,
|
29
|
+
:subject => title,
|
30
|
+
}})
|
31
|
+
end
|
32
|
+
|
33
|
+
def check
|
34
|
+
begin
|
35
|
+
HttpUtil.get_json(api('users/current'),
|
36
|
+
{ :key => @config.apikey})
|
37
|
+
'ok'
|
38
|
+
rescue HttpUtil::HttpError => e
|
39
|
+
"Error: #{e.message}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def api(name)
|
45
|
+
URI(@config.url + '/') + "#{name}.json"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/mistilteinn.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
2
|
+
require File.expand_path('../lib/mistilteinn/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["MIZUNO Hiroki"]
|
6
|
+
gem.email = ["mzp.ppp@gmail.com"]
|
7
|
+
gem.description = %q{Mistilteinn is a command to support a development with git and redmine.}
|
8
|
+
gem.summary = %q{Mistilteinn is a command to support a development with git and redmine.}
|
9
|
+
gem.homepage = "https://github.com/mistilteinn/mistilteinn-shell"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "mistilteinn"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Mistilteinn::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'json', ['>= 0'] unless defined? JSON
|
19
|
+
gem.add_dependency 'subcommand', ['>= 0']
|
20
|
+
gem.add_development_dependency 'rspec', ['>= 0']
|
21
|
+
gem.add_development_dependency 'rake', ['>= 0']
|
22
|
+
end
|
data/spec/.gitkeep
ADDED
File without changes
|
data/spec/config_spec.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#! /opt/local/bin/ruby -w
|
2
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
spec 'config' do
|
7
|
+
describe 'load from file' do
|
8
|
+
before do
|
9
|
+
File.should_receive(:exist?) { true }
|
10
|
+
YAML.should_receive(:load_file) {
|
11
|
+
{'ticket' => {
|
12
|
+
'source' => 'local',
|
13
|
+
'path' => '/path/to/ticket.txt'
|
14
|
+
}}}
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'ticket' do
|
18
|
+
subject { Mistilteinn::Config.load('config.yml').ticket }
|
19
|
+
its(:source) { should == 'local' }
|
20
|
+
its(:path) { should == '/path/to/ticket.txt' }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'get config from git-config instead of ymal' do
|
25
|
+
before do
|
26
|
+
File.should_receive(:exist?) { true }
|
27
|
+
YAML.should_receive(:load_file) {
|
28
|
+
{'ticket' => {
|
29
|
+
'source' => 'local',
|
30
|
+
'path' => '/path/to/ticket.txt'
|
31
|
+
}}}
|
32
|
+
Mistilteinn::Git.should_receive(:config).with('ticket.apikey') { 'xxxx' }
|
33
|
+
@config = Mistilteinn::Config.load('config.yml')
|
34
|
+
end
|
35
|
+
|
36
|
+
subject { @config.ticket }
|
37
|
+
its(:apikey) { should == 'xxxx' }
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'load error' do
|
41
|
+
before { @config = Mistilteinn::Config.new({}) }
|
42
|
+
it {
|
43
|
+
expect { @config.ticket.x }.to raise_error(Mistilteinn::ConfigError)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#! /opt/local/bin/ruby -w
|
2
|
+
# -*- mode:ruby; coding:utf-8 -*-
|
3
|
+
|
4
|
+
require 'spec_helper'
|
5
|
+
require 'uri'
|
6
|
+
require 'mistilteinn/http_util'
|
7
|
+
|
8
|
+
spec 'ticket/redmine' do
|
9
|
+
before do
|
10
|
+
@config = Mistilteinn::Config.new({ 'redmine' => {
|
11
|
+
'url' => 'http://example.com/redmine',
|
12
|
+
'project' => 'some-project',
|
13
|
+
'apikey' => 'key' }})
|
14
|
+
@redmine = Mistilteinn::Ticket::Redmine.new @config
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'ticket list' do
|
18
|
+
before do
|
19
|
+
Mistilteinn::HttpUtil.should_receive(:get_json).
|
20
|
+
with(URI('http://example.com/redmine/issues.json'),
|
21
|
+
{ :project_id => 'some-project', :key => 'key' }) do
|
22
|
+
{ "issues" =>
|
23
|
+
[
|
24
|
+
{ "id" => "1", "subject" => "Ticket A", "status" => { "name" => "open" }},
|
25
|
+
{ "id" => "2", "subject" => "Ticket B", "status" => { "name" => "close" }}
|
26
|
+
]}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
subject { @redmine.tickets }
|
31
|
+
its(:length) { should == 2 }
|
32
|
+
|
33
|
+
describe 'Ticket A' do
|
34
|
+
subject { @redmine.tickets[0] }
|
35
|
+
its(:id) { should == "1" }
|
36
|
+
its(:name) { should == "Ticket A" }
|
37
|
+
its(:status) { should == "open" }
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'Ticket B' do
|
41
|
+
subject { @redmine.tickets[1] }
|
42
|
+
its(:id) { should == "2" }
|
43
|
+
its(:name) { should == "Ticket B" }
|
44
|
+
its(:status) { should == "close" }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'create ticket' do
|
49
|
+
it do
|
50
|
+
Mistilteinn::HttpUtil.should_receive(:post_json).
|
51
|
+
with(URI('http://example.com/redmine/issues.json'),
|
52
|
+
{ 'X-Redmine-API-Key' => 'key' },
|
53
|
+
{ :issue => {
|
54
|
+
:project_id => 'some-project',
|
55
|
+
:subject => 'hogehoge'
|
56
|
+
}})
|
57
|
+
@redmine.create 'hogehoge'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'self-check' do
|
62
|
+
context 'success' do
|
63
|
+
before do
|
64
|
+
Mistilteinn::HttpUtil.should_receive(:get_json).
|
65
|
+
with(URI('http://example.com/redmine/users/current.json'),
|
66
|
+
{ :key => 'key' }) do {} end
|
67
|
+
end
|
68
|
+
|
69
|
+
subject { @redmine }
|
70
|
+
its(:check) { should == 'ok' }
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'fail' do
|
74
|
+
before do
|
75
|
+
Mistilteinn::HttpUtil.should_receive(:get_json).
|
76
|
+
with(URI('http://example.com/redmine/users/current.json'),
|
77
|
+
{ :key => 'key' }) do
|
78
|
+
raise Mistilteinn::HttpUtil::HttpError.new('foo bar')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
subject { @redmine }
|
83
|
+
its(:check) { should == 'Error: foo bar' }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mistilteinn
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- MIZUNO Hiroki
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: &70337693909060 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70337693909060
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: subcommand
|
27
|
+
requirement: &70337693908460 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70337693908460
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &70337693907860 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70337693907860
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: &70337693895220 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70337693895220
|
58
|
+
description: Mistilteinn is a command to support a development with git and redmine.
|
59
|
+
email:
|
60
|
+
- mzp.ppp@gmail.com
|
61
|
+
executables:
|
62
|
+
- mistilteinn
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- .gitignore
|
67
|
+
- .mistilteinn/config.yaml
|
68
|
+
- .rspec
|
69
|
+
- Gemfile
|
70
|
+
- README.markdown
|
71
|
+
- Rakefile
|
72
|
+
- bin/.gitkeep
|
73
|
+
- bin/mistilteinn
|
74
|
+
- lib/mistilteinn/cli.rb
|
75
|
+
- lib/mistilteinn/cli/command.rb
|
76
|
+
- lib/mistilteinn/cli/create.rb
|
77
|
+
- lib/mistilteinn/cli/hooks/commit-msg
|
78
|
+
- lib/mistilteinn/cli/hooks/common.sh
|
79
|
+
- lib/mistilteinn/cli/hooks/pre-commit
|
80
|
+
- lib/mistilteinn/cli/init.rb
|
81
|
+
- lib/mistilteinn/cli/is_inside.rb
|
82
|
+
- lib/mistilteinn/cli/list.rb
|
83
|
+
- lib/mistilteinn/cli/self_check.rb
|
84
|
+
- lib/mistilteinn/cli/templates/config.yaml
|
85
|
+
- lib/mistilteinn/config.rb
|
86
|
+
- lib/mistilteinn/git.rb
|
87
|
+
- lib/mistilteinn/http_util.rb
|
88
|
+
- lib/mistilteinn/ticket.rb
|
89
|
+
- lib/mistilteinn/ticket/entry.rb
|
90
|
+
- lib/mistilteinn/ticket/git_config.rb
|
91
|
+
- lib/mistilteinn/ticket/github.rb
|
92
|
+
- lib/mistilteinn/ticket/redmine.rb
|
93
|
+
- lib/mistilteinn/version.rb
|
94
|
+
- mistilteinn.gemspec
|
95
|
+
- spec/.gitkeep
|
96
|
+
- spec/config_spec.rb
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
- spec/ticket/redmine_spec.rb
|
99
|
+
homepage: https://github.com/mistilteinn/mistilteinn-shell
|
100
|
+
licenses: []
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 1.8.16
|
120
|
+
signing_key:
|
121
|
+
specification_version: 3
|
122
|
+
summary: Mistilteinn is a command to support a development with git and redmine.
|
123
|
+
test_files:
|
124
|
+
- spec/config_spec.rb
|
125
|
+
- spec/spec_helper.rb
|
126
|
+
- spec/ticket/redmine_spec.rb
|