pgit 0.0.2
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 +1 -0
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +22 -0
- data/README.markdown +32 -0
- data/Rakefile +24 -0
- data/bin/pgit +97 -0
- data/lib/pgit.rb +14 -0
- data/lib/pgit/configuration.rb +77 -0
- data/lib/pgit/current_branch.rb +23 -0
- data/lib/pgit/current_project.rb +62 -0
- data/lib/pgit/installer.rb +32 -0
- data/lib/pgit/name_parser.rb +57 -0
- data/lib/pgit/story.rb +48 -0
- data/lib/pgit/story_branch.rb +30 -0
- data/lib/pgit/story_branch/application.rb +19 -0
- data/lib/pgit/version.rb +3 -0
- data/lib/pgit_configuration.rb +57 -0
- data/man/pgit.1 +18 -0
- data/man/pgit.1.html +96 -0
- data/man/pgit.1.ronn +15 -0
- data/pgit.gemspec +21 -0
- data/pgit.rdoc +5 -0
- data/spec/pgit/application_spec.rb +16 -0
- data/spec/pgit/configuration_spec.rb +158 -0
- data/spec/pgit/current_branch_spec.rb +72 -0
- data/spec/pgit/current_project_spec.rb +80 -0
- data/spec/pgit/installer_spec.rb +158 -0
- data/spec/pgit/pgit_spec.rb +33 -0
- data/spec/pgit/story_branch/application_spec.rb +48 -0
- data/spec/pgit/story_branch/name_parser_spec.rb +42 -0
- data/spec/pgit/story_branch_spec.rb +90 -0
- data/spec/pgit/story_spec.rb +54 -0
- metadata +110 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a72ba79fbaa4bfba38e13548cff4ac44812fb0b1
|
4
|
+
data.tar.gz: fd94693b8ff782640064f0a134d822b4922ab4bd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5491115049d8c35b86df684a793be62ad71a7b3678379a3eb5337be7692c7e67903cd3a088cd0b729c419473db5a0a6d69031ec5386426de3a4e9f0a3cb3de75
|
7
|
+
data.tar.gz: ea5e67ecc7a8ca90e475ec261fec625581b6bbe92bd337d25631f345df57d8efee6dead19f0a49ca9db735da4569d4e152797513b5b1a0c29860feb0d6372d7a
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.un~
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color -fd
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pgit (0.0.2)
|
5
|
+
gli (= 2.12.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
coderay (1.1.0)
|
11
|
+
gli (2.12.2)
|
12
|
+
method_source (0.8.2)
|
13
|
+
pry (0.10.1)
|
14
|
+
coderay (~> 1.1.0)
|
15
|
+
method_source (~> 0.8.1)
|
16
|
+
slop (~> 3.4)
|
17
|
+
rake (10.4.2)
|
18
|
+
slop (3.6.0)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
pgit!
|
25
|
+
pry
|
26
|
+
rake
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Edderic
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# PGit
|
2
|
+
|
3
|
+
## Example Usage
|
4
|
+
|
5
|
+
Assuming that:
|
6
|
+
- you have a project that uses Pivotal Tracker and Git that is listed under ~/.pgit.rc.yml
|
7
|
+
- your working directory is in that project,
|
8
|
+
- you want to automatically branch out based on the story title
|
9
|
+
- the story title with story id 10102004 is "Implement a really cool feature"
|
10
|
+
|
11
|
+
```
|
12
|
+
$ pgit story_branch -s 10102004
|
13
|
+
```
|
14
|
+
|
15
|
+
will create a branch for you named `implement-really-cool-feature-10102004`
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
1. Install via RubyGems:
|
20
|
+
|
21
|
+
```
|
22
|
+
$ gem install pgit
|
23
|
+
```
|
24
|
+
|
25
|
+
2. Setup configuration file:
|
26
|
+
|
27
|
+
```
|
28
|
+
$ pgit install
|
29
|
+
```
|
30
|
+
|
31
|
+
This will generate a YAML file under `~/.pgit.rc.yml`. Edit that file to add information about projects that you're working on. Each project needs a project `id`, Pivotal Tracker `api_token`, and `path`
|
32
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
require 'rdoc/task'
|
5
|
+
|
6
|
+
Rake::RDocTask.new do |rd|
|
7
|
+
rd.main = "README.rdoc"
|
8
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
9
|
+
rd.title = 'PGit'
|
10
|
+
end
|
11
|
+
|
12
|
+
spec = eval(File.read('pgit.gemspec'))
|
13
|
+
|
14
|
+
Gem::PackageTask.new(spec) do |pkg|
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Run specs'
|
18
|
+
require 'rake/testtask'
|
19
|
+
Rake::TestTask.new(:spec) do |s|
|
20
|
+
s.libs << "spec"
|
21
|
+
s.test_files = FileList['spec/**/*_test.rb']
|
22
|
+
end
|
23
|
+
|
24
|
+
task :default => [:spec]
|
data/bin/pgit
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'gli'
|
3
|
+
require 'pgit'
|
4
|
+
include GLI::App
|
5
|
+
|
6
|
+
program_desc 'Optimize your Pivotal Tracker and Git workflow'
|
7
|
+
|
8
|
+
version Pgit::VERSION
|
9
|
+
|
10
|
+
subcommand_option_handling :normal
|
11
|
+
arguments :strict
|
12
|
+
|
13
|
+
# desc 'Path to the config file'
|
14
|
+
# default_value "#{ENV['HOME']}/.pgit.rc.yml"
|
15
|
+
# arg_name '/path/to/.pivotal.yml'
|
16
|
+
# flag [:c,:config]
|
17
|
+
|
18
|
+
# desc 'Bypasses the app asking for confirmation'
|
19
|
+
# switch [:force]
|
20
|
+
|
21
|
+
# desc 'PivotalTracker API Token'
|
22
|
+
# arg_name 'secr3tT0ken123'
|
23
|
+
# flag [:'api-token']
|
24
|
+
|
25
|
+
desc "Installs the pgit configuration file"
|
26
|
+
command :install do |c|
|
27
|
+
c.action do |global_options,options,args|
|
28
|
+
PGit::Installer.new(global_options, options, args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# desc 'Start, finish, etc. Pivotal Tracker story branches'
|
33
|
+
desc 'Start Pivotal Tracker story branches'
|
34
|
+
long_desc "Makes a cURL request to Pivotal Tracker to fetch the story, based
|
35
|
+
on the given story id, parses the title, appends it with the story id. It then
|
36
|
+
creates a branch name with that parsed title and does a `git checkout` to it. "
|
37
|
+
|
38
|
+
command :story_branch do |c|
|
39
|
+
c.desc "Parses the title of the given Pivotal Tracker story,\n" +
|
40
|
+
" makes the branch name, and does a checkout"
|
41
|
+
c.arg_name 'STORY_ID'
|
42
|
+
c.flag :s, :start
|
43
|
+
|
44
|
+
# c.desc "Merge back to staging, remove local and remote (origin?) branches"
|
45
|
+
# c.switch :f, :finish
|
46
|
+
|
47
|
+
# c.desc "Attempts to join the story-branch that, presumably, already exists"
|
48
|
+
# c.arg_name 'STORY_ID'
|
49
|
+
# c.flag :j, :join
|
50
|
+
|
51
|
+
c.action do |global_options,options,args|
|
52
|
+
PGit::StoryBranch::Application.new(global_options, options, args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# desc 'Wraps `git commit -m` and prepends the message with story id of the branch'
|
57
|
+
|
58
|
+
# command :commit do |c|
|
59
|
+
# arg_name 'Describe arguments to commit here'
|
60
|
+
# c.action do |global_options,options,args|
|
61
|
+
# puts "commit command ran"
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
|
65
|
+
|
66
|
+
desc 'Initializes configuration file'
|
67
|
+
|
68
|
+
arg_name 'path/to/config_file'
|
69
|
+
|
70
|
+
# command :init_config do |c|
|
71
|
+
# c.action do |global_options,options,args|
|
72
|
+
# puts "init_config command ran"
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
|
76
|
+
pre do |global,command,options,args|
|
77
|
+
# Pre logic here
|
78
|
+
# Return true to proceed; false to abort and not call the
|
79
|
+
# chosen command
|
80
|
+
# Use skips_pre before a command to skip this block
|
81
|
+
# on that command only
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
post do |global,command,options,args|
|
86
|
+
# Post logic here
|
87
|
+
# Use skips_post before a command to skip this
|
88
|
+
# block on that command only
|
89
|
+
end
|
90
|
+
|
91
|
+
on_error do |exception|
|
92
|
+
# Error logic here
|
93
|
+
# return false to skip default error handling
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
exit run(ARGV)
|
data/lib/pgit.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'pgit/configuration'
|
3
|
+
require 'pgit/current_branch'
|
4
|
+
require 'pgit/current_project'
|
5
|
+
require 'pgit/installer'
|
6
|
+
require 'pgit/name_parser'
|
7
|
+
require 'pgit/story'
|
8
|
+
require 'pgit/story_branch'
|
9
|
+
require 'pgit/story_branch/application'
|
10
|
+
require 'pgit/version.rb'
|
11
|
+
require 'yaml'
|
12
|
+
|
13
|
+
# Add requires for other files you add to your project here, so
|
14
|
+
# you just need to require this one file in your bin file
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#
|
2
|
+
# Loads the Pivotal-Git configuration file
|
3
|
+
#
|
4
|
+
# - config_path is the path to the pivotal-git configuration file
|
5
|
+
# This loads the file. Throws an error if a key (such as api_token, path, id)
|
6
|
+
# is missing. Check #general_error_message for more info
|
7
|
+
#
|
8
|
+
|
9
|
+
module PGit
|
10
|
+
class Configuration
|
11
|
+
def self.default_options
|
12
|
+
{
|
13
|
+
'projects' => [
|
14
|
+
{
|
15
|
+
'api_token' => 'somepivotalatoken124',
|
16
|
+
'id' => '12345',
|
17
|
+
"path" => "~/some/path/to/a/pivotal-git/project"
|
18
|
+
},
|
19
|
+
{
|
20
|
+
'api_token' => 'somepivotalatoken124',
|
21
|
+
'id' => '23429070',
|
22
|
+
"path" => "~/some/other/pivotal-git/project"
|
23
|
+
}
|
24
|
+
]
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(config_path = '~/.pgit.rc.yml')
|
29
|
+
@expanded_path = File.expand_path(config_path)
|
30
|
+
if File.exists? @expanded_path
|
31
|
+
config_file = File.open(@expanded_path, 'r')
|
32
|
+
@yaml = YAML.load(config_file)
|
33
|
+
|
34
|
+
validate_existence_of_at_least_one_project
|
35
|
+
validate_presence_of_items_in_each_project
|
36
|
+
else
|
37
|
+
raise missing_config_default
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_yaml
|
42
|
+
@yaml
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def missing_config_default
|
48
|
+
"Default configuration file does not exist. Please run `pgit install`"
|
49
|
+
end
|
50
|
+
|
51
|
+
def general_error_message
|
52
|
+
"Please have the following layout:\n" + YAML.dump(PGit::Configuration.default_options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_presence_of_items_in_each_project
|
56
|
+
projects = @yaml["projects"]
|
57
|
+
all_present = projects.all? do |project|
|
58
|
+
project["api_token"] &&
|
59
|
+
project["path"] &&
|
60
|
+
project["id"]
|
61
|
+
end
|
62
|
+
|
63
|
+
unless all_present
|
64
|
+
raise "Error: Must have a path, id, and api_token for each project.\n" +
|
65
|
+
general_error_message
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_existence_of_at_least_one_project
|
70
|
+
unless @yaml["projects"]
|
71
|
+
raise "Error: #{@expanded_path} needs at least one project.\n" +
|
72
|
+
general_error_message
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PGit
|
2
|
+
class CurrentBranch
|
3
|
+
def initialize
|
4
|
+
@branches = `git branch`
|
5
|
+
|
6
|
+
raise @branches unless current.any?
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
current.first.gsub(/\*\s*/, '')
|
11
|
+
end
|
12
|
+
|
13
|
+
def story_id
|
14
|
+
name.scan(/\d+$/).first
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def current
|
20
|
+
@branches.scan(/\*.+/)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#
|
2
|
+
# Decides what the "current project" is, in relation to the pwd.
|
3
|
+
#
|
4
|
+
# - config_yaml: has the project configurations. It has at least one
|
5
|
+
# project. Each project has an (pivotal) api_token, path, and (pivotal)
|
6
|
+
# id
|
7
|
+
#
|
8
|
+
|
9
|
+
module PGit
|
10
|
+
class CurrentProject
|
11
|
+
def initialize(config_yaml)
|
12
|
+
@current_project = find_current_project(config_yaml)
|
13
|
+
end
|
14
|
+
|
15
|
+
def pwd
|
16
|
+
project_path = @current_project["path"]
|
17
|
+
File.expand_path(project_path, __FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def id
|
21
|
+
@current_project["id"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def api_token
|
25
|
+
@current_project["api_token"]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def validate_pwd_match_at_least_one(matching_projects)
|
31
|
+
if matching_projects.length == 0
|
32
|
+
raise "None of the project paths matches the working directory"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def escape_slashes(project_path)
|
37
|
+
project_path.gsub('/','\/')
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_matching_projects(projects)
|
41
|
+
projects.select do |project|
|
42
|
+
project_path = project["path"]
|
43
|
+
extended_path = File.expand_path(project_path, __FILE__)
|
44
|
+
escaped_project = escape_slashes(extended_path)
|
45
|
+
Dir.pwd.match(/#{escaped_project}/)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_current_project(config_yaml)
|
50
|
+
projects = config_yaml["projects"]
|
51
|
+
matching_projects = find_matching_projects(projects)
|
52
|
+
|
53
|
+
validate_pwd_match_at_least_one(matching_projects)
|
54
|
+
find_best_match(matching_projects)
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_best_match(matching_projects)
|
58
|
+
matching_projects.sort! { |a,b| b["path"].length <=> a["path"].length }
|
59
|
+
matching_projects.first
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PGit
|
2
|
+
class Installer
|
3
|
+
def initialize(glob_opts, opts, args)
|
4
|
+
file_path = "~/.pgit.rc.yml"
|
5
|
+
@expanded_path = File.expand_path(file_path)
|
6
|
+
|
7
|
+
if File.exists? @expanded_path
|
8
|
+
raise "Error: #{file_path} already exists"
|
9
|
+
else
|
10
|
+
ask_continue
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def ask_continue
|
15
|
+
puts "*** Installing example pgit configuration file under ~/.pgit.rc.yml. Continue? [Y/n]"
|
16
|
+
if STDIN.gets.chomp.match(/y/i)
|
17
|
+
puts "Saving example pgit config in ~/.pgit.rc.yml..."
|
18
|
+
write_example_pgit_rc_file
|
19
|
+
else
|
20
|
+
puts "Aborting installation..."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def write_example_pgit_rc_file
|
25
|
+
File.open(@expanded_path, 'w') do |f|
|
26
|
+
YAML.dump(PGit::Configuration.default_options, f)
|
27
|
+
end
|
28
|
+
|
29
|
+
puts "Saved! Please edit ~/.pgit.rc.yml and add the proper Pivotal Tracker API tokens, id, and file paths for each project"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#
|
2
|
+
# Parses pivotal story titles and turns them into git branch names
|
3
|
+
# - story.name: title of the pivotal tracker story
|
4
|
+
#
|
5
|
+
# - story_id: id of the pivotal tracker story
|
6
|
+
#
|
7
|
+
# ex: Fly so high in the sky 12345 becomes fly-so-high-sky-12345
|
8
|
+
module PGit
|
9
|
+
class StoryBranch
|
10
|
+
class NameParser
|
11
|
+
def initialize(story)
|
12
|
+
@story = story
|
13
|
+
@story_name = story.name
|
14
|
+
end
|
15
|
+
|
16
|
+
def story_id
|
17
|
+
@story.id
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
remove_fluff_words
|
22
|
+
remove_non_alphanumeric_characters
|
23
|
+
downcase
|
24
|
+
add_story_id
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def remove_non_alphanumeric_characters
|
30
|
+
words = @story_name.split(' ')
|
31
|
+
split_words = words.each { |word| word.gsub!(/\W/, '') }
|
32
|
+
@story_name = split_words.select do |word|
|
33
|
+
!word.empty?
|
34
|
+
end.join('-')
|
35
|
+
end
|
36
|
+
|
37
|
+
def remove_fluff_words
|
38
|
+
fluff_words = %w{the on of}
|
39
|
+
fluff_words.each { |fluff_word| @story_name.gsub!(/\b#{fluff_word}\b/i, '') }
|
40
|
+
remove_extraneous_white_spaces
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove_extraneous_white_spaces
|
44
|
+
@story_name.strip!
|
45
|
+
@story_name.gsub!(/\s+/, ' ')
|
46
|
+
end
|
47
|
+
|
48
|
+
def downcase
|
49
|
+
@story_name.downcase!
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_story_id
|
53
|
+
"#{@story_name}-#{story_id}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|