make_release 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 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: []