version-manager 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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 95091c91ef29114e6b297b05e19cea5aceaf13c5
4
+ data.tar.gz: b7ae1053426aaedce3c9f8a3df80b31acaad1056
5
+ SHA512:
6
+ metadata.gz: f9be742bda8301a90e960543bf0ae4651cd225379c78b17593b2c1a6dd4e6a944cf7fade0b29325f6dba6f78d5f2d7925d0921a672b241b9de335de1844d5f7d
7
+ data.tar.gz: 23cf808131c0243ed53b01c1b96bf11a39887a33fa329dc4c689ac5c1d801ae3950bd0ccc16b0cd75599419a20082b36072c758bba4dc774293d93a5c804f53d
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1 @@
1
+ 2.3.1
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 Ilya Solo
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.
@@ -0,0 +1,2 @@
1
+ # version-manager
2
+ versioning lib (with Git support) written in Ruby
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "version/manager"
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/version-manager'
4
+ VersionManager::CLI.new(exec_name: File.basename(__FILE__)).start
@@ -0,0 +1,48 @@
1
+ require 'docopt'
2
+ require 'git'
3
+ require 'pathname'
4
+ require 'logger'
5
+
6
+ ROOT_DIR = Dir.pwd
7
+
8
+ module VersionManager
9
+ DEFAULTS = {
10
+ vcs: {
11
+ name: 'git',
12
+ default_commit_message: -> (version) { "Bumped to version #{version}" },
13
+ options: {
14
+ remote: 'origin',
15
+ master_branch: 'master',
16
+ log: Logger.new(STDOUT)
17
+ }
18
+ },
19
+ authorized_branches: {
20
+ major: '^\bmaster\b$',
21
+ minor: '^\bmaster\b$',
22
+ patch: '^\brelease-[a-zA-Z0-9.]*$\b$'
23
+ },
24
+ storage: {
25
+ filename: 'VERSION',
26
+ filepath: ROOT_DIR
27
+ },
28
+ version_name: -> (version) { "release-#{version.short_version}" }
29
+ }
30
+
31
+ def self.options
32
+ @options ||= DEFAULTS.dup
33
+ end
34
+
35
+ def self.options=(opts)
36
+ @options = opts
37
+ end
38
+ end
39
+
40
+ require_relative 'version-manager/vcs'
41
+ require_relative 'version-manager/vcs/git'
42
+
43
+ require_relative 'version-manager/version'
44
+ require_relative 'version-manager/cli'
45
+
46
+ require_relative 'version-manager/release_version'
47
+ require_relative 'version-manager/version_storage'
48
+ require_relative 'version-manager/make.rb'
@@ -0,0 +1,70 @@
1
+ module VersionManager
2
+ class CLI
3
+ def initialize(exec_name: __FILE__)
4
+ @exec_name = exec_name
5
+ end
6
+
7
+ def start
8
+ doc = <<~DOCOPT
9
+
10
+ Usage:
11
+ #{exec_name} make major
12
+ #{exec_name} make minor
13
+ #{exec_name} make patch
14
+ #{exec_name} latest
15
+ #{exec_name} -h | --help
16
+ #{exec_name} -v | --version
17
+
18
+ Options:
19
+ -h --help show this screen.
20
+ -v --version show version.
21
+ DOCOPT
22
+
23
+ begin
24
+ parse_options(Docopt::docopt(doc))
25
+ rescue StandardError => e
26
+ puts e.message
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :exec_name
33
+
34
+ def parse_options(options)
35
+ puts VersionManager::VERSION if options['--version']
36
+ checkout_to_latest_version if options['latest']
37
+ %w(major minor patch).each do |release|
38
+ next unless options[release]
39
+ break make_release(release.to_sym)
40
+ end
41
+ end
42
+
43
+ def checkout_to_latest_version
44
+ storage = build_storage
45
+ version = storage.latest_version
46
+ return puts 'There are no any versions.' unless version
47
+ VCS.build.checkout(version.branch)
48
+ end
49
+
50
+ def make_release(release_type)
51
+ storage = build_storage
52
+ version = storage.latest_version
53
+ version = retrieve_initial_version unless version
54
+ Make.new(version, VCS.build, storage).public_send("#{release_type}!")
55
+ end
56
+
57
+ def build_storage
58
+ storage_options = VersionManager.options[:storage]
59
+ VersionStorage.new(VCS.build, storage_options)
60
+ end
61
+
62
+ def retrieve_initial_version
63
+ puts 'There are no any versions. Please, input an initial one:'
64
+ ReleaseVersion.new(STDIN.gets)
65
+ rescue ArgumentError => e
66
+ puts e.message
67
+ retry
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,66 @@
1
+ module VersionManager
2
+ class Make
3
+ class BranchIsNotUpToDateError < StandardError
4
+ def message
5
+ 'Remote branch and local one are different. You need to update your branch or push your changes'
6
+ end
7
+ end
8
+
9
+ class ForbiddenBranchError < StandardError
10
+ def message
11
+ 'You can not do actions from this branch. Checkout to appropriate branch'
12
+ end
13
+ end
14
+
15
+ def initialize(version, vcs, version_storage)
16
+ @version = version
17
+ @vcs = vcs
18
+ @version_storage = version_storage
19
+ end
20
+
21
+ def major!
22
+ raise BranchIsNotUpToDateError unless vcs.master_state_actual?
23
+ raise ForbiddenBranchError unless appropriate_branch_for?('major')
24
+ default_strategy { |version| version.bump_major }
25
+ end
26
+
27
+ def minor!
28
+ raise BranchIsNotUpToDateError unless vcs.master_state_actual?
29
+ raise ForbiddenBranchError unless appropriate_branch_for?('minor')
30
+ default_strategy { |version| version.bump_minor }
31
+ end
32
+
33
+ def patch!
34
+ raise BranchIsNotUpToDateError unless vcs.state_actual?
35
+ raise ForbiddenBranchError unless appropriate_branch_for?('patch')
36
+ version.bump_patch
37
+ vcs.commit(version_storage.store(version), default_commit_message)
38
+ vcs.add_tag(version.to_s, default_commit_message)
39
+ vcs.push_tag(version.to_s)
40
+ vcs.push
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :version, :vcs, :version_storage
46
+
47
+ def appropriate_branch_for?(action)
48
+ authorized_mask = VersionManager.options[:authorized_branches][action.to_sym]
49
+ !authorized_mask || !vcs.current_branch.match(authorized_mask).nil?
50
+ end
51
+
52
+ def default_strategy
53
+ yield version
54
+ vcs.create_branch!(version.branch)
55
+ vcs.commit(version_storage.store(version), default_commit_message)
56
+ vcs.add_tag(version.to_s, default_commit_message)
57
+ vcs.push_tag(version.to_s)
58
+ vcs.push
59
+ end
60
+
61
+ def default_commit_message
62
+ message = VersionManager.options[:vcs][:default_commit_message]
63
+ message.respond_to?(:call) ? message.call(version) : message.to_s
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,74 @@
1
+ module VersionManager
2
+ class ReleaseVersion
3
+ include Comparable
4
+
5
+ class IncorrentFormat < StandardError
6
+ def message
7
+ 'Incorrect version format. You should pass an array with version components or a string'
8
+ end
9
+ end
10
+
11
+ def initialize(version_input)
12
+ version_components = version_input.dup
13
+ unless version_input.respond_to?(:to_ary)
14
+ version_components = version_input.scan(/(\d+)\.{1}(\d+)\.?(\d*)(?:--(\w+))?/).flatten
15
+ raise ArgumentError, 'Incorrect version format' if version_components.all?(&:nil?) || version_components.empty?
16
+ end
17
+ @major, @minor, @patch = version_components[0..2].map(&:to_i)
18
+ @special = version_components[3]
19
+ recalculate_parts
20
+ end
21
+
22
+ def to_str
23
+ res = parts.map(&:to_i).join('.')
24
+ [res, special].compact.join('--')
25
+ end
26
+ alias_method :to_s, :to_str
27
+
28
+ def short_version
29
+ [major, minor].map(&:to_i).join('.')
30
+ end
31
+
32
+ def branch
33
+ VersionManager.options[:version_name].call(self)
34
+ end
35
+
36
+ def <=>(other_version)
37
+ parts.zip(other_version.parts).
38
+ map { |this, other| this <=> other }.
39
+ find { |res| res != 0 } || 0
40
+ end
41
+
42
+ def bump_major
43
+ @major += 1
44
+ @minor = 0
45
+ @patch = 0
46
+ recalculate_parts
47
+ end
48
+
49
+ def bump_minor
50
+ @minor += 1
51
+ @patch = 0
52
+ recalculate_parts
53
+ end
54
+
55
+ def bump_patch
56
+ @patch += 1
57
+ recalculate_parts
58
+ end
59
+
60
+ def self.valid?(version)
61
+ new(version) && true
62
+ rescue ArgumentError
63
+ false
64
+ end
65
+
66
+ attr_reader :major, :minor, :patch, :special, :parts
67
+
68
+ private
69
+
70
+ def recalculate_parts
71
+ @parts = [major, minor, patch].map(&:to_i)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,30 @@
1
+ module VersionManager
2
+ module VCS
3
+ class BranchAlreadyExistsError < StandardError
4
+ def initialize(branch_name)
5
+ @branch_name = branch_name
6
+ end
7
+
8
+ def message
9
+ "Branch #{@branch_name} already exists"
10
+ end
11
+ end
12
+
13
+ class UnsupportedVCSError < StandardError
14
+ def initialize(vcs)
15
+ @vcs = vcs
16
+ end
17
+
18
+ def message
19
+ "VCS #{vcs} has not been supported yet"
20
+ end
21
+ end
22
+
23
+ def self.build
24
+ case VersionManager.options[:vcs][:name]
25
+ when 'git' then VersionManager::VCS::Git.new(VersionManager.options[:vcs][:options])
26
+ else raise UnsupportedVCSError
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,87 @@
1
+ module VersionManager
2
+ module VCS
3
+ class Git
4
+ def initialize(options)
5
+ @options = options
6
+ @git = ::Git.open(ROOT_DIR, options)
7
+ end
8
+
9
+ def create_branch!(branch_name)
10
+ raise VersionManager::VCS::BranchAlreadyExistsError.new(branch_name) if branch_exists?(branch_name)
11
+ checkout(branch_name)
12
+ end
13
+
14
+ def checkout(branch_name)
15
+ git.branch(branch_name).checkout
16
+ end
17
+
18
+ def show_file(branch, filepath)
19
+ git.object("#{branch}:#{filepath}").contents
20
+ rescue StandardError
21
+ nil
22
+ end
23
+
24
+ def commit(filepath, message)
25
+ git.lib.send(:command, 'add', filepath)
26
+ git.lib.send(:command, 'commit', "-m #{message}", '-o', "#{filepath}")
27
+ end
28
+
29
+ def add_tag(tag_name, message)
30
+ git.add_tag(tag_name, message: message, annotate: tag_name)
31
+ end
32
+
33
+ def push
34
+ git.pull(remote, current_branch) if find_remote_branch(current_branch)
35
+ git.push(remote, current_branch)
36
+ end
37
+
38
+ def push_tag(tag_name)
39
+ git.push(remote, tag_name)
40
+ end
41
+
42
+ def current_branch
43
+ git.current_branch
44
+ end
45
+
46
+ def master_state_actual?
47
+ git.revparse(master_branch_name) == git.revparse(remote_master_branch_name)
48
+ end
49
+
50
+ def state_actual?
51
+ head = ::Git.ls_remote['branches'][git.current_branch]
52
+ remote_head = find_remote_branch(git.current_branch).last
53
+ return unless remote_head
54
+ head[:sha] == remote_head[:sha]
55
+ end
56
+
57
+ def remote_branch_names
58
+ ::Git.ls_remote['remotes'].keys
59
+ end
60
+
61
+ private
62
+
63
+ attr_reader :git, :options
64
+
65
+ def branch_exists?(branch_name)
66
+ branches = ::Git.ls_remote['branches'].keys + ::Git.ls_remote['remotes'].keys
67
+ branches.any? { |b| b.split('/').last == branch_name }
68
+ end
69
+
70
+ def master_branch_name
71
+ options[:master_branch]
72
+ end
73
+
74
+ def remote
75
+ options[:remote]
76
+ end
77
+
78
+ def remote_master_branch_name
79
+ "#{options[:remote]}/#{master_branch_name}"
80
+ end
81
+
82
+ def find_remote_branch(branch_name)
83
+ ::Git.ls_remote['remotes'].find { |remote, _| branch_name == remote.split('/').last }
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,3 @@
1
+ module VersionManager
2
+ VERSION = '0.0.2'.freeze
3
+ end
@@ -0,0 +1,38 @@
1
+ module VersionManager
2
+ class VersionStorage
3
+ def initialize(vcs, storage_options)
4
+ @filename = storage_options[:filename]
5
+ @filepath = storage_options[:filepath]
6
+ @vcs = vcs
7
+ end
8
+
9
+ def store(version)
10
+ File.open(full_path, 'w') do |file|
11
+ file << version
12
+ end
13
+ full_path
14
+ end
15
+
16
+ def latest_version
17
+ versions = vcs.remote_branch_names.map do |name|
18
+ ReleaseVersion.new(name) if ReleaseVersion.valid?(name)
19
+ end
20
+ version = versions.compact.sort.last
21
+ file_content = vcs.show_file(version.branch, relative_path) if version
22
+ version = ReleaseVersion.new(file_content) if file_content && ReleaseVersion.valid?(file_content)
23
+ version
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :filename, :filepath, :vcs
29
+
30
+ def relative_path
31
+ Pathname.new(full_path).relative_path_from(Pathname.new(ROOT_DIR)).to_s
32
+ end
33
+
34
+ def full_path
35
+ File.expand_path(File.join(filepath, filename))
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'version-manager/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'version-manager'
8
+ spec.version = VersionManager::VERSION
9
+ spec.authors = ['isolo']
10
+ spec.email = ['ilya.i.solo@gmail.com']
11
+
12
+ spec.summary = 'Versioning lib (with Git support)'
13
+ spec.homepage = 'https://github.com/isolo/version-manager'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split("\n")
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.bindir = 'exe'
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.12'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_dependency 'docopt', '~> 0.5'
24
+ spec.add_dependency 'git', '~> 1.3'
25
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: version-manager
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - isolo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-28 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.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
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: docopt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.5'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: git
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ description:
70
+ email:
71
+ - ilya.i.solo@gmail.com
72
+ executables:
73
+ - manver
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".ruby-version"
79
+ - Gemfile
80
+ - LICENSE
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - exe/manver
86
+ - lib/version-manager.rb
87
+ - lib/version-manager/cli.rb
88
+ - lib/version-manager/make.rb
89
+ - lib/version-manager/release_version.rb
90
+ - lib/version-manager/vcs.rb
91
+ - lib/version-manager/vcs/git.rb
92
+ - lib/version-manager/version.rb
93
+ - lib/version-manager/version_storage.rb
94
+ - version-manager.gemspec
95
+ homepage: https://github.com/isolo/version-manager
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.5.1
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Versioning lib (with Git support)
119
+ test_files: []