release-gem 0.1.0 → 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 +4 -4
- data/Gemfile.lock +78 -0
- data/lib/release/Rakefile +5 -0
- data/lib/release/gem/gem_action.rb +295 -0
- data/lib/release/gem/gem_cli_action.rb +157 -0
- data/lib/release/gem/gem_engine.rb +113 -0
- data/lib/release/gem/vcs_action.rb +224 -0
- data/lib/release/gem/vcs_cli_action.rb +228 -0
- data/lib/release/gem/version.rb +1 -1
- data/tasks/standard.rake +50 -0
- data/templates/standard_cli_flow.rb +56 -0
- data/templates/standard_flow.rb +260 -0
- metadata +11 -1
@@ -0,0 +1,224 @@
|
|
1
|
+
|
2
|
+
require 'gvcs'
|
3
|
+
require 'git_cli'
|
4
|
+
|
5
|
+
module Release
|
6
|
+
module Gem
|
7
|
+
module Action
|
8
|
+
|
9
|
+
class VcsActionError < StandardError; end
|
10
|
+
|
11
|
+
class VcsAction
|
12
|
+
include TR::CondUtils
|
13
|
+
|
14
|
+
attr_accessor :ui
|
15
|
+
|
16
|
+
def initialize(root, opts = { })
|
17
|
+
raise VcsActionError, "root cannot be null" if is_empty?(root)
|
18
|
+
@ws = Gvcs::Workspace.new(root)
|
19
|
+
|
20
|
+
oopts = opts || {}
|
21
|
+
@ui = oopts[:ui]
|
22
|
+
@engine = oopts[:engine]
|
23
|
+
st, path = @ws.workspace_root
|
24
|
+
|
25
|
+
#@gitDir = File.join(path.strip,".git")
|
26
|
+
#p @gitDir
|
27
|
+
#@gitBack = File.join(path.strip,".git_bak")
|
28
|
+
#p @gitBack
|
29
|
+
end
|
30
|
+
|
31
|
+
#def enable_dev_mode
|
32
|
+
# FileUtils.cp_r(@gitDir,@gitBack, remove_destination: true)
|
33
|
+
#end
|
34
|
+
|
35
|
+
#def dev_mode_end
|
36
|
+
# if File.exist?(@gitBack)
|
37
|
+
# FileUtils.mv(@gitBack, @gitDir)
|
38
|
+
# end
|
39
|
+
#end
|
40
|
+
|
41
|
+
def exec(&block)
|
42
|
+
instance_eval(&block) if block
|
43
|
+
end
|
44
|
+
|
45
|
+
def commit(msg = nil, &block)
|
46
|
+
|
47
|
+
res = :value
|
48
|
+
if block
|
49
|
+
|
50
|
+
counter = 0
|
51
|
+
loop do
|
52
|
+
|
53
|
+
stgDir, stgFiles = @ws.staged_files
|
54
|
+
modDir, modFiles = @ws.modified_files
|
55
|
+
newDir, newFiles = @ws.new_files
|
56
|
+
delDir, delFiles = @ws.deleted_files
|
57
|
+
|
58
|
+
modFiles.delete_if { |f| stgFiles.include?(f) }
|
59
|
+
modDir.delete_if { |f| stgDir.include?(f) }
|
60
|
+
|
61
|
+
# block should call vcs for add, remove, ignore and other operations
|
62
|
+
res = block.call(:select_files_to_commit, { modified: { files: modFiles, dirs: modDir }, new: { files: newFiles, dirs: newDir }, deleted: { files: delFiles, dirs: delDir }, staged: { files: stgFiles, dirs: stgDir }, vcs: self, counter: counter } )
|
63
|
+
|
64
|
+
break if res == :skip or res == :done
|
65
|
+
|
66
|
+
counter += 1
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
if res == :done
|
71
|
+
|
72
|
+
stgDir, stgFiles = @ws.staged_files
|
73
|
+
block.call(:staged_elements_of_commit, { files: stgFiles, dirs: stgDir })
|
74
|
+
|
75
|
+
msg = block.call(:commit_message) if is_empty?(msg)
|
76
|
+
raise VcsActionError, "Commit message is empty" if is_empty?(msg)
|
77
|
+
|
78
|
+
cp "Commit with user message : #{msg}"
|
79
|
+
st, res = @ws.commit(msg)
|
80
|
+
if st
|
81
|
+
block.call(:commit_successful, res) if block
|
82
|
+
else
|
83
|
+
block.call(:commit_failed, res) if block
|
84
|
+
end
|
85
|
+
[st, res]
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
else
|
90
|
+
|
91
|
+
msg = "Auto commit all ('-am' flag) by gem-release gem" if is_empty?(msg)
|
92
|
+
cp msg
|
93
|
+
# changed files only without new files
|
94
|
+
@ws.commit_all(msg)
|
95
|
+
end
|
96
|
+
|
97
|
+
res
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def tag(*args, &block)
|
102
|
+
|
103
|
+
opts = args.first || { }
|
104
|
+
tag = opts[:tag]
|
105
|
+
msg = opts[:message]
|
106
|
+
|
107
|
+
if not @ws.tag_points_at?("HEAD")
|
108
|
+
|
109
|
+
if is_empty?(tag)
|
110
|
+
raise VcsActionError, "tag name cannot be empty" if not block
|
111
|
+
tag = block.call(:tag_name)
|
112
|
+
raise VcsActionError, "tag name cannot be empty" if is_empty?(tag)
|
113
|
+
end
|
114
|
+
|
115
|
+
if is_empty?(msg)
|
116
|
+
if block
|
117
|
+
msg = block.call(:tag_message)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
cp "tagged with name : #{tag} and message : #{msg}"
|
122
|
+
st, res = @ws.create_tag(tag, msg)
|
123
|
+
if st
|
124
|
+
block.call(:tagging_success, res) if block
|
125
|
+
else
|
126
|
+
block.call(:tagging_failed, res) if block
|
127
|
+
end
|
128
|
+
|
129
|
+
[st, res]
|
130
|
+
|
131
|
+
else
|
132
|
+
|
133
|
+
block.call(:no_tagging_required) if block
|
134
|
+
[true, "No tagging required"]
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
def push(remote = nil, branch= nil, &block)
|
140
|
+
|
141
|
+
remoteConf = @ws.remote_config
|
142
|
+
if is_empty?(remoteConf)
|
143
|
+
if block
|
144
|
+
remote = block.call(:no_remote_repos_defined)
|
145
|
+
end
|
146
|
+
else
|
147
|
+
if is_empty?(remote)
|
148
|
+
if block
|
149
|
+
remote = block.call(:select_remote, remoteConf)
|
150
|
+
else
|
151
|
+
remote = remoteConf.keys.first
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
raise VcsActionError, "Push repository remote cannot be empty" if is_empty?(remote)
|
157
|
+
|
158
|
+
if remote != :skip
|
159
|
+
|
160
|
+
branch = @ws.current_branch if is_empty?(branch)
|
161
|
+
|
162
|
+
|
163
|
+
if is_local_ahead_of_remote?("#{remote}/#{branch}", branch)
|
164
|
+
|
165
|
+
cp "pushing to #{remote}/#{branch}"
|
166
|
+
st, res = @ws.push_changes_with_tags(remote, branch)
|
167
|
+
|
168
|
+
if st
|
169
|
+
block.call(:push_successful, res) if block
|
170
|
+
else
|
171
|
+
block.call(:push_failed, res) if block
|
172
|
+
end
|
173
|
+
|
174
|
+
[st, res]
|
175
|
+
|
176
|
+
else
|
177
|
+
block.call(:no_changes_to_push, { remote: remote, branch: branch }) if block
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
def add_to_staging(*files)
|
184
|
+
@ws.add_to_staging(*files)
|
185
|
+
end
|
186
|
+
|
187
|
+
def ignore(*files)
|
188
|
+
@ws.ignore(*files)
|
189
|
+
end
|
190
|
+
|
191
|
+
def remove_from_staging(*files)
|
192
|
+
@ws.remove_from_staging(*files)
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
private
|
197
|
+
def method_missing(mtd, *args, &block)
|
198
|
+
if @ws.respond_to?(mtd)
|
199
|
+
@ws.send(mtd, *args, &block)
|
200
|
+
else
|
201
|
+
if not @engine.nil? and @engine.respond_to?(mtd)
|
202
|
+
@engine.send(mtd, *args, &block)
|
203
|
+
else
|
204
|
+
super
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def cp(msg)
|
210
|
+
Gem.cul(@ui, msg)
|
211
|
+
end
|
212
|
+
def ce(msg)
|
213
|
+
Gem.cue(@ui, msg)
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
|
218
|
+
end # class VcsAction
|
219
|
+
|
220
|
+
end # module Action
|
221
|
+
|
222
|
+
end # module Gem
|
223
|
+
|
224
|
+
end # module Release
|
@@ -0,0 +1,228 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
require 'tty/prompt'
|
4
|
+
require_relative 'vcs_action'
|
5
|
+
|
6
|
+
module Release
|
7
|
+
module Gem
|
8
|
+
module Cli
|
9
|
+
class VcsAction
|
10
|
+
def initialize(root, opts = { })
|
11
|
+
opts = {} if opts.nil?
|
12
|
+
opts[:ui] = TTY::Prompt.new
|
13
|
+
@inst = Action::VcsAction.new(root,opts)
|
14
|
+
@prmt = TTY::Prompt.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def exec(&block)
|
18
|
+
instance_eval(&block) if block
|
19
|
+
end
|
20
|
+
|
21
|
+
def commit(*args, &block)
|
22
|
+
res = @inst.commit do |ops, *args|
|
23
|
+
|
24
|
+
preset = false
|
25
|
+
if block
|
26
|
+
res = block.call(ops, *args)
|
27
|
+
if res.nil?
|
28
|
+
preset = true
|
29
|
+
else
|
30
|
+
res
|
31
|
+
end
|
32
|
+
else
|
33
|
+
preset = true
|
34
|
+
end
|
35
|
+
|
36
|
+
if preset
|
37
|
+
|
38
|
+
case ops
|
39
|
+
when :select_files_to_commit
|
40
|
+
mfiles = args.first
|
41
|
+
@prmt.puts "\n Files already added to staging : ".yellow
|
42
|
+
mfiles[:staged].each do |k,v|
|
43
|
+
v.each do |vv|
|
44
|
+
@prmt.puts " * #{vv}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
@prmt.puts ""
|
49
|
+
|
50
|
+
sel = @prmt.multi_select "\n Following are files that could be added to version control : ".yellow do |m|
|
51
|
+
|
52
|
+
[:modified, :new, :deleted].each do |cat|
|
53
|
+
mfiles[cat].each do |k,v|
|
54
|
+
v.each do |vv|
|
55
|
+
m.choice vv, vv.path
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
m.choice "Skip", :skip if mfiles[:counter] == 0
|
62
|
+
m.choice "Done", :done
|
63
|
+
m.choice "Abort", :abort
|
64
|
+
end
|
65
|
+
|
66
|
+
if sel.include?(:abort)
|
67
|
+
raise Release::Gem::Abort, "User aborted"
|
68
|
+
elsif sel.include?(:skip)
|
69
|
+
:skip
|
70
|
+
else
|
71
|
+
res = :done if sel.include?(:done)
|
72
|
+
s = sel.clone
|
73
|
+
s.delete_if { |e| e == :done }
|
74
|
+
if not_empty?(s)
|
75
|
+
st, cres = add_to_staging(*s) if not_empty?(s)
|
76
|
+
if st
|
77
|
+
@prmt.puts "\n Files added successfully".green
|
78
|
+
else
|
79
|
+
@prmt.puts "\n Files failed to be added. Message was : #{cres}".red
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
res
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
when :commit_message
|
88
|
+
msg = ""
|
89
|
+
loop do
|
90
|
+
msg = @prmt.ask("\n Commit message : ".yellow, required: true)
|
91
|
+
confirm = @prmt.yes?(" Commit message : #{msg}\n Proceed? No to provide a new commit message ".yellow)
|
92
|
+
if confirm
|
93
|
+
break
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
msg
|
98
|
+
|
99
|
+
when :staged_elements_of_commit
|
100
|
+
|
101
|
+
elements = args.first
|
102
|
+
@prmt.puts "\n Following files/directories shall be committed in this session : ".yellow
|
103
|
+
elements.each do |k,v|
|
104
|
+
v.each do |vv|
|
105
|
+
@prmt.puts " * #{vv}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
when :commit_successful
|
110
|
+
@prmt.puts "\n Changes committed".green
|
111
|
+
|
112
|
+
when :commit_failed
|
113
|
+
@prmt.puts "\n Changes failed to be committed. Error was : #{args.first}"
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end # commit
|
118
|
+
|
119
|
+
end # Commit
|
120
|
+
|
121
|
+
def tag(*args, &block)
|
122
|
+
|
123
|
+
@inst.tag(*args) do |ops, *args|
|
124
|
+
|
125
|
+
preset = false
|
126
|
+
if block
|
127
|
+
res = block.call(ops, *args)
|
128
|
+
if res.nil?
|
129
|
+
preset = true
|
130
|
+
else
|
131
|
+
res
|
132
|
+
end
|
133
|
+
else
|
134
|
+
preset = true
|
135
|
+
end
|
136
|
+
|
137
|
+
if preset
|
138
|
+
|
139
|
+
case ops
|
140
|
+
when :tag_message
|
141
|
+
@prmt.ask("\n Please provide message for the tag : ".yellow, value: "Auto tagging by gem-release gem during releasing version #{@selVer}", required: true)
|
142
|
+
|
143
|
+
when :tagging_success
|
144
|
+
@prmt.puts "\n Tagging of source code is successful.".green
|
145
|
+
|
146
|
+
when :tagging_failed
|
147
|
+
@prmt.puts "\n Tagging of source code failed. Error was : #{args.first}".red
|
148
|
+
|
149
|
+
when :no_tagging_required
|
150
|
+
@prmt.puts "\n No tagging required. Source head is the tagged item ".green
|
151
|
+
|
152
|
+
end
|
153
|
+
end # preset ?
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end # tag
|
158
|
+
|
159
|
+
def push(*args, &block)
|
160
|
+
|
161
|
+
@inst.push do |ops, *args|
|
162
|
+
preset = false
|
163
|
+
if block
|
164
|
+
res = block.call(ops, *args)
|
165
|
+
if res.nil?
|
166
|
+
preset = true
|
167
|
+
else
|
168
|
+
res
|
169
|
+
end
|
170
|
+
else
|
171
|
+
preset = true
|
172
|
+
end
|
173
|
+
|
174
|
+
if preset
|
175
|
+
|
176
|
+
case ops
|
177
|
+
when :select_remote
|
178
|
+
val = args.first
|
179
|
+
sel = @prmt.select("\n Please select one of the remote config below : ") do |m|
|
180
|
+
val.each do |k,v|
|
181
|
+
m.choice k, k
|
182
|
+
end
|
183
|
+
m.choice "Skip pushing source code", :skip
|
184
|
+
m.choice "Abort", :abort
|
185
|
+
end
|
186
|
+
raise Release::Gem::Abort, "User aborted" if sel == :abort
|
187
|
+
|
188
|
+
sel
|
189
|
+
|
190
|
+
when :no_remote_repos_defined
|
191
|
+
add = @prmt.yes?("\n No remote configuration defined. Add one now?")
|
192
|
+
if add
|
193
|
+
name = @prmt.ask("\n Name of the repository : ", value: "origin", required: true)
|
194
|
+
url = @prmt.ask("\n URL of the repository : ", required: true)
|
195
|
+
|
196
|
+
st, res = add_remote(name, url)
|
197
|
+
if st
|
198
|
+
@prmt.puts "\n Remote configuration added successfully".green
|
199
|
+
name
|
200
|
+
else
|
201
|
+
raise Release::Gem::Abort, "Failed to add remote configuration. Error was : #{res}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
when :push_successful
|
206
|
+
@prmt.puts "\n Push success!".green
|
207
|
+
|
208
|
+
when :push_failed
|
209
|
+
@prmt.puts "\nPush failed. Error was : #{args.first}".red
|
210
|
+
|
211
|
+
when :no_changes_to_push
|
212
|
+
val = args.first
|
213
|
+
@prmt.puts "\n Local is in sync with remote (#{val[:remote]}/#{val[:branch]}). Push is not required. "
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
end # push
|
220
|
+
|
221
|
+
def method_missing(mtd, *args, &block)
|
222
|
+
@inst.send(mtd, *args, &block)
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
data/lib/release/gem/version.rb
CHANGED
data/tasks/standard.rake
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
require_relative '../lib/release/gem'
|
4
|
+
require 'tty/prompt'
|
5
|
+
require 'colorize'
|
6
|
+
|
7
|
+
namespace :gem do
|
8
|
+
desc "Release GEM standard workflow (gem-release gem)"
|
9
|
+
task :release do
|
10
|
+
# check for local flow
|
11
|
+
custom = Dir.glob(File.join(Dir.getwd, "*.relflow"))
|
12
|
+
if custom.length > 0
|
13
|
+
|
14
|
+
pmt = TTY::Prompt.new
|
15
|
+
|
16
|
+
begin
|
17
|
+
if custom.length > 1
|
18
|
+
sel = pmt.select("\n There are more than 1 release flow (*.relflow files) detected. Please select one for this session : ".yellow) do |m|
|
19
|
+
custom.each do |f|
|
20
|
+
m.choice f, f
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require sel
|
25
|
+
|
26
|
+
else
|
27
|
+
|
28
|
+
#require custom.first
|
29
|
+
load custom.first
|
30
|
+
|
31
|
+
end
|
32
|
+
rescue TTY::Reader::InputInterrupt => ex
|
33
|
+
pmt.puts "\n Aborted"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
stdFlow = File.join(File.dirname(__FILE__),"..","templates","standard_cli_flow")
|
37
|
+
require stdFlow
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "Copy the standard flow to project local so modification of the flow is possible"
|
42
|
+
task :customize_flow do
|
43
|
+
stdFlow = File.join(File.dirname(__FILE__),"..","templates","standard_cli_flow.rb")
|
44
|
+
dest = File.join(Dir.getwd, "custom_flow.relflow")
|
45
|
+
FileUtils.cp stdFlow, dest
|
46
|
+
puts "\n Standard flow copied to #{dest}\n".green
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'release/gem'
|
4
|
+
|
5
|
+
puts "\n Standard GEM CLI release flow version #{Release::Gem::VERSION}\n".yellow
|
6
|
+
|
7
|
+
begin
|
8
|
+
|
9
|
+
Release::Gem.engine(:gem, root: Dir.getwd) do
|
10
|
+
|
11
|
+
# step 1 : run test
|
12
|
+
run_test(:rspec)
|
13
|
+
|
14
|
+
gem_cli_action do
|
15
|
+
|
16
|
+
# step 2 : check dependency
|
17
|
+
release_dependencies
|
18
|
+
|
19
|
+
# step 3 : build the gem
|
20
|
+
st, ver = build
|
21
|
+
if st
|
22
|
+
# step 4, push the gem to rubygems
|
23
|
+
push(version: ver)
|
24
|
+
install(version: ver)
|
25
|
+
end
|
26
|
+
|
27
|
+
end # gem_cli_action
|
28
|
+
|
29
|
+
vcs_cli_action do
|
30
|
+
@selVer = value(:selected_version)
|
31
|
+
|
32
|
+
# step 6 : commit vcs
|
33
|
+
commit
|
34
|
+
|
35
|
+
# step 7 : tag the source code
|
36
|
+
tag( tag: @selVer )
|
37
|
+
|
38
|
+
# step 8 : Push the source code
|
39
|
+
push
|
40
|
+
|
41
|
+
end # vcs_action block
|
42
|
+
|
43
|
+
end # Release::Gem::Engine block
|
44
|
+
|
45
|
+
puts "\n *** GEM standard release flow done!\n".green
|
46
|
+
|
47
|
+
rescue Release::Gem::Abort => ex
|
48
|
+
STDERR.puts "\n -- Aborted by user. Message was : #{ex.message}\n".red
|
49
|
+
rescue TTY::Reader::InputInterrupt => ex
|
50
|
+
rescue Exception => ex
|
51
|
+
STDERR.puts "\n -- Error thrown. Message was : #{ex.message}".red
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
|