make_release 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9fbb23cdf86da813754fd4e9c9a91a152ee45600
4
+ data.tar.gz: ac4a0c3b38dc3015566c298ed0120eae68cbf81e
5
+ SHA512:
6
+ metadata.gz: bdc3e3cc59912ed6ed74b017b6335f3ff23692142e40ba21d090aedeb6d12b6f26f440bc17dad7fe2f7ca5ca3c5efca2b17936be600d8a0f03c744816de43d9c
7
+ data.tar.gz: d663f98969f8810629ecb41aed8df0a8fd57c7e61d367827be19a2fa96663a98ae29f757fddd6b8e7a0fb91004b1c634250cc5b922fe59995ad733f71b368f02
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.3
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in make_release.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # MakeRelease
2
+
3
+ This is a standalone utility which will collect multiple JIRA stories and programmatically builds a release candidate branch.
4
+ - Can compare multiple feature branches against a single production branch (typically `master`)
5
+ - Displays the SHAs for all stories not yet merged into production
6
+ - Produce a release-candidate branch
7
+
8
+
9
+ ## Installation
10
+
11
+ Install the `mkrelease` executable
12
+
13
+ $ gem install make_release
14
+
15
+ ## Usage
16
+
17
+ Usage: mkrelease [options]
18
+
19
+ Common Options:
20
+ -d, --directory DIR Use DIR as our source directory
21
+ -m, --master BRANCH Specify a master branch (default: master)
22
+ -r, --release-version VER Specify the release version (REQUIRED)
23
+ -s, --source BRANCH Use BRANCH as our starting branch to compare against (may be used more than once)
24
+
25
+ Additional Options:
26
+ -q, --silent Run quietly (same as --no-verbose)
27
+ -v, --[no-]verbose Run verbosely (default)
28
+
29
+ Informational:
30
+ -h, --help Show this message
31
+ -V, --version Show version (and exit)
32
+ --diff Display a list of stories from all sources which haven't been merged into master
33
+ --dryrun Don't actually modify any files, just show what would happen
34
+ --debug Run with debugging options (use with caution)
35
+
36
+ ## Development
37
+
38
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
39
+
40
+ To install this gem onto your local machine, run `gem install make_release`. To release a new version, update the version number in `globals.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
41
+
42
+ ## Contributing
43
+
44
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dyoung522/make_release
45
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "make_release"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/mkrelease ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby -w
2
+
3
+ $LOAD_PATH.unshift('./lib')
4
+ require 'make_release'
5
+
6
+ MakeRelease.run!
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,17 @@
1
+ require 'open3'
2
+
3
+ module MakeRelease
4
+ module Git
5
+
6
+ def self.log(dir, branch)
7
+ cmd = "git log --no-merges --pretty='%H|%s' #{branch}"
8
+ Open3.popen3(cmd, chdir: dir) do |i,o,e,t|
9
+ if t.value != 0
10
+ raise RuntimeError, "Unable to obtain gitlog for #{branch} in #{dir}"
11
+ end
12
+ o.read.split("\n")
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module MakeRelease
2
+ module Globals
3
+ require 'yaml'
4
+
5
+ VERSION = '0.1.1'
6
+ IDENT = 'make_release'
7
+ BINIDENT = 'mkrelease'
8
+ AUTHOR = 'Donovan C. Young'
9
+ AEMAIL = 'dyoung522@gmail.com'
10
+ SUMMARY = %q{Creates a release candidate}
11
+ DESCRIPTION = %q{Merges a list of JIRA stories from multiple branches into a release candidate}
12
+ HOMEPAGE = "https://github.com/dyoung522/#{IDENT}"
13
+ LICENSE = 'MIT'
14
+ VSTRING = "#{IDENT} v.#{VERSION} - #{AUTHOR}, 2015"
15
+ end
16
+ end
@@ -0,0 +1,95 @@
1
+ require 'ostruct'
2
+ require 'optparse'
3
+
4
+ module MakeRelease
5
+ class Options
6
+
7
+ def self.default_options
8
+ {
9
+ directory: '.',
10
+ master: 'master',
11
+ source: [],
12
+ release: nil,
13
+ diff: false,
14
+ dryrun: false,
15
+ verbose: true,
16
+ debug: false,
17
+ stories: nil
18
+ }
19
+ end
20
+
21
+ def self.defaults
22
+ Struct.new( *Options.default_options.keys ).new( *Options.default_options.values )
23
+ end
24
+
25
+ def self.parse( argv_opts = [] )
26
+ options = self.defaults
27
+
28
+ opt_parser = OptionParser.new do |opts|
29
+ opts.banner = "Usage: #{Globals::BINIDENT} [options]"
30
+ opts.separator ''
31
+ opts.separator 'Common Options:'
32
+
33
+ opts.on('-d', '--directory DIR', 'Use DIR as our source directory') do |dir|
34
+ dir = File.expand_path(dir.strip)
35
+ if Dir.exist?(dir)
36
+ options.directory = dir
37
+ else
38
+ puts "Whoops: '#{dir}' is not a valid directory."
39
+ exit 2
40
+ end
41
+ end
42
+
43
+ opts.on('-m', '--master BRANCH', 'Specify a master branch (default: master)') do |master|
44
+ options.master = master
45
+ end
46
+
47
+ opts.on('-r', '--release-version VER', 'Specify the release version (REQUIRED)') do |rver|
48
+ options.release = rver
49
+ end
50
+
51
+ opts.on('-s', '--source BRANCH',
52
+ 'Use BRANCH as our starting branch to compare against (may be used more than once)') do |branch|
53
+ options.source << branch unless options.source.include?(branch)
54
+ end
55
+
56
+ opts.separator ''
57
+ opts.separator 'Additional Options:'
58
+
59
+ opts.on('-q', '--silent', 'Run quietly (same as --no-verbose)') { options.verbose = false }
60
+ opts.on('-v', '--[no-]verbose', 'Run verbosely (default)') { |v| options.verbose = v }
61
+
62
+ opts.separator ''
63
+ opts.separator 'Informational:'
64
+
65
+ opts.on('-h', '--help', 'Show this message') { puts Globals::VSTRING + "\n\n"; puts opts; exit 255; }
66
+ opts.on('-V', '--version', 'Show version (and exit)') { puts Globals::VSTRING; exit 255; }
67
+ opts.on('--diff', "Display a list of stories from all sources which haven't been merged into master") { options.diff = true }
68
+ opts.on('--dryrun', %q{Don't actually modify any files, just show what would happen}) { options.dryrun = true }
69
+ opts.on('--debug', 'Run with debugging options (use with caution)') { options.debug = true }
70
+ end
71
+
72
+ opt_parser.parse!(argv_opts)
73
+
74
+ validate_options(options)
75
+ end
76
+
77
+ def self.validate_options(opts)
78
+ # raise OptionParser::MissingArgument, 'A release version (-r) is required' if opts.release.nil?
79
+
80
+ if opts.release && opts.release !~ /^v?(\d+\.)?(\d+\.)?(\*|\d+)/
81
+ raise RuntimeError, 'Release version must follow semantic versioning'
82
+ end
83
+
84
+ if opts.source.include?(opts.master)
85
+ raise RuntimeError, 'Source branches cannot include the master branch'
86
+ end
87
+
88
+ opts.source = ['develop'] if opts.source.empty?
89
+ opts.master = 'master' if opts.master.nil? || opts.master.strip == ''
90
+
91
+ opts
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,104 @@
1
+ require 'make_release/story'
2
+ require 'make_release/git'
3
+
4
+ module MakeRelease
5
+ class Stories
6
+
7
+ def initialize( opts = Options.defaults )
8
+ @options = opts
9
+ @stories = opts[:stories] || {}
10
+ @directory = opts[:directory] || '.'
11
+ @branches = _get_branches(opts[:master], opts[:source])
12
+
13
+ get_stories if @stories == {}
14
+ end
15
+
16
+ attr_accessor :branches, :directory
17
+ attr_reader :stories
18
+ alias dir directory
19
+
20
+ def each
21
+ @stories.values.each do |stories|
22
+ stories.each { |story| yield story }
23
+ end
24
+ end
25
+
26
+ def to_s
27
+ @stories.each do |branch, stories|
28
+ puts "#{branch.capitalize} -->"
29
+ stories.each { |s| puts s.to_s }
30
+ end
31
+ end
32
+
33
+ def source
34
+ @branches[1, @branches.size]
35
+ end
36
+
37
+ def master
38
+ @branches[0]
39
+ end
40
+
41
+ def master=(new_master)
42
+ @stories[master] = []
43
+ @branches[0] = new_master
44
+ get_stories(new_master)
45
+ end
46
+
47
+ def shas
48
+ source.map do |branch|
49
+ stories[branch].map { |s| s.sha }
50
+ end.flatten
51
+ end
52
+
53
+ def source_stories
54
+ story_index = {}
55
+
56
+ source.each do |branch|
57
+ stories[branch].each { |s| story_index[s.sha] = s }
58
+ end
59
+
60
+ story_index.values
61
+ end
62
+
63
+ def add_story( branch, story )
64
+ (@stories[branch] ||= []).push story
65
+ end
66
+
67
+ def find( branch, sha )
68
+ raise ArgumentError, "Invalid environment #{branch}" unless @branches.include?(branch)
69
+ @stories[branch].each { |story| return true if story.sha == sha }
70
+ false
71
+ end
72
+
73
+ def diff
74
+ stories = []
75
+ opts = @options
76
+
77
+ source_stories.each do |story|
78
+ stories << story unless find(master, story.sha)
79
+ end
80
+
81
+ opts.source = ['diff']
82
+ opts.stories = {'diff' => stories.flatten}
83
+ Stories.new opts
84
+ end
85
+
86
+ private
87
+
88
+ def _get_branches(master, sources)
89
+ branches = [] << (master.nil? ? 'master' : master)
90
+ branches << (sources.empty? ? ['develop'] : sources)
91
+ branches.flatten
92
+ end
93
+
94
+ def get_stories(branches = @branches)
95
+ branches.to_a.each do |branch|
96
+ Git.log(@directory, branch).each do |line|
97
+ add_story branch, Story.new(line)
98
+ end
99
+ end
100
+ end
101
+
102
+ end
103
+ end
104
+
@@ -0,0 +1,53 @@
1
+ module MakeRelease
2
+
3
+ class Story
4
+ def initialize( story )
5
+ unless story =~ /\S+|\S+/
6
+ raise ArgumentError, "story must follow 'SHA|description' format"
7
+ end
8
+
9
+ @sha, @description = story.split('|')
10
+
11
+ raise ArgumentError if @sha.nil? || @description.nil?
12
+ end
13
+ attr_reader :sha
14
+
15
+ def split_story( description = @description )
16
+ raise RuntimeError 'description cannot be blank' unless description
17
+
18
+ stories = []
19
+ story_pattern = /\[?(((SRMPRT|OSMCLOUD)\-\d+)|NO-JIRA)\]?[,:\-\s]+\s*(.*)$/
20
+ line = description.match(story_pattern)
21
+
22
+ if line.nil? # did not find a JIRA ticket pattern
23
+ stories.push 'NO-JIRA'
24
+ desc = description.strip
25
+ else
26
+ stories.push line.captures[0]
27
+ desc = line.captures[3].strip
28
+ end
29
+
30
+ # Perform recursion if there are multiple tickets in the description
31
+ if desc =~ story_pattern
32
+ new_story, new_desc = split_story desc
33
+ stories.push new_story
34
+ desc = new_desc
35
+ end
36
+
37
+ [stories.flatten, desc]
38
+ end
39
+
40
+ def tickets
41
+ (split_story)[0]
42
+ end
43
+
44
+ def desc
45
+ (split_story)[1]
46
+ end
47
+
48
+ def to_s
49
+ '[%07.07s] %s - %s' % [sha, tickets.join(', '), desc]
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,56 @@
1
+ require 'make_release/globals'
2
+ require 'make_release/options'
3
+ require 'make_release/stories'
4
+
5
+ module MakeRelease
6
+ def self.not_implemented( feature )
7
+ puts "Sorry, #{feature} has not yet been implemented"
8
+ exit 2
9
+ end
10
+
11
+ def self.run!
12
+ begin
13
+ opts = Options.parse ARGV
14
+ rescue => error
15
+ puts error
16
+ exit 1
17
+ end
18
+
19
+ puts opts.inspect if opts.debug
20
+
21
+ stories = Stories.new(opts)
22
+
23
+ puts stories.inspect if opts.debug
24
+
25
+ if opts.diff
26
+
27
+ if opts.verbose
28
+ puts "From #{stories.directory}"
29
+ puts "-> All stories from #{stories.source.join(', ')}"
30
+ puts "-> Which are not in #{stories.master}"
31
+ stories.diff.each do |story|
32
+ puts "%-120.120s" % story.to_s
33
+ end
34
+ else
35
+ puts stories.diff.shas
36
+ end
37
+
38
+ else
39
+
40
+ if opts.verbose
41
+ puts "All stories from #{stories.directory}"
42
+ stories.branches.each do |branch|
43
+ puts "\n#{branch.capitalize}\n\n"
44
+ stories.stories[branch].each do |story|
45
+ puts "%-120.120s" % story.to_s
46
+ end
47
+ end
48
+ else
49
+ not_implemented('--silent')
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+ end
56
+
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'make_release/globals'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = MakeRelease::Globals::IDENT
10
+ spec.version = MakeRelease::Globals::VERSION
11
+ spec.authors = [MakeRelease::Globals::AUTHOR]
12
+ spec.email = [MakeRelease::Globals::AEMAIL]
13
+
14
+ spec.summary = MakeRelease::Globals::SUMMARY
15
+ spec.description = MakeRelease::Globals::DESCRIPTION
16
+ spec.homepage = MakeRelease::Globals::HOMEPAGE
17
+ spec.license = MakeRelease::Globals::LICENSE
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ spec.bindir = 'bin'
21
+ spec.executables = ['mkrelease']
22
+ spec.require_paths = ['./lib']
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.10'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.0'
27
+ end
28
+
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: make_release
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Donovan C. Young
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-27 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.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: Merges a list of JIRA stories from multiple branches into a release candidate
56
+ email:
57
+ - dyoung522@gmail.com
58
+ executables:
59
+ - mkrelease
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - ".rspec"
65
+ - ".ruby-version"
66
+ - ".travis.yml"
67
+ - Gemfile
68
+ - README.md
69
+ - Rakefile
70
+ - bin/console
71
+ - bin/mkrelease
72
+ - bin/setup
73
+ - lib/make_release.rb
74
+ - lib/make_release/git.rb
75
+ - lib/make_release/globals.rb
76
+ - lib/make_release/options.rb
77
+ - lib/make_release/stories.rb
78
+ - lib/make_release/story.rb
79
+ - make_release.gemspec
80
+ homepage: https://github.com/dyoung522/make_release
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - "./lib"
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.4.5.1
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Creates a release candidate
104
+ test_files: []