step-up 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.stepuprc ADDED
@@ -0,0 +1,31 @@
1
+ notes:
2
+ after_versioned:
3
+ #strategy: "remove"
4
+ strategy: "keep"
5
+ section: "versioning"
6
+ changelog_message: "available on {version}"
7
+ sections:
8
+ - name: "changes"
9
+ prefix: "change: "
10
+ label: "Changes:"
11
+ tag: "change"
12
+ - name: "bugfixes"
13
+ prefix: "bugfix: "
14
+ label: "Bugfixes:"
15
+ tag: "bugfix"
16
+ - name: "features"
17
+ prefix: "feature: "
18
+ label: "Features:"
19
+ tag: "feature"
20
+ - name: "deploy_steps"
21
+ prefix: "deploy_step: "
22
+ label: "Deploy steps:"
23
+ tag: "deploy_step"
24
+ versioning:
25
+ version_mask: "v0.0.0.9.rc9"
26
+ version_levels:
27
+ - major
28
+ - minor
29
+ - tiny
30
+ - patch
31
+ - rc
data/README.md CHANGED
@@ -1,3 +1,32 @@
1
- # LastVersion: a project to versioning projects
1
+ # StepUp: a project to 'step up' projects
2
2
 
3
- LastVersion is an utility to manage versioning tags on git projects.
3
+ StepUp is an utility to manage versioning, based on source control management features (i.e. tags and notes of Git). That is, you don't need to keep track of your application version in a file anymore.
4
+ Initially it just supports Git as SCM. But more SCM's are planned in the future.
5
+
6
+ For now the only feature will help you to check the application version based on the default version pattern from StepUp.
7
+
8
+ The next (real soon) features to come are:
9
+ - create notes
10
+ - increment versions based on notes
11
+ - pattern of version defined by user
12
+ - and lots more
13
+
14
+ ### Installation
15
+
16
+ gem install step-up
17
+
18
+ ### Usage
19
+
20
+ 1) Enter your directory project
21
+
22
+ 2) Run StepUp and see the magic
23
+
24
+ ### Commands
25
+
26
+ stepup
27
+
28
+ Shows your application version
29
+
30
+ stepup -h
31
+
32
+ Shows the current features
data/Rakefile CHANGED
@@ -33,7 +33,7 @@ task :install => :build do
33
33
  if gem.nil?
34
34
  puts "could not install the gem"
35
35
  else
36
- sh "gem uninstall step-up && gem install #{ gem }"
36
+ sh "gem uninstall --ignore-dependencies --executables step-up; gem install #{ gem }"
37
37
  end
38
38
  end
39
39
 
data/lib/step-up.rb CHANGED
@@ -5,6 +5,7 @@ module StepUp
5
5
  autoload :Git, 'step-up/driver/git.rb'
6
6
  end
7
7
  autoload :GitExtensions, 'step-up/git_extensions.rb'
8
+ autoload :RangedNotes, 'step-up/ranged_notes.rb'
8
9
  module Parser
9
10
  autoload :VersionMask, 'step-up/parser/version_mask.rb'
10
11
  end
data/lib/step-up/cli.rb CHANGED
@@ -3,24 +3,269 @@ require 'step-up'
3
3
 
4
4
  module StepUp
5
5
  class CLI < Thor
6
- map %w(--version -v) => :gem_version
6
+ include Thor::Actions
7
+ map %w(--version -v) => :gem_version # $ stepup [--version|-v]
7
8
 
8
9
  default_task :version
9
10
 
10
- desc "", "show the last version of the application"
11
- def version
12
- puts StepUp::Driver::Git.last_version
11
+ desc "version ACTION [OPTIONS]", "manage versions of your project"
12
+ method_options %w(levels -L) => :boolean # $ stepup version [--levels|-L]
13
+ method_options %w(level -l) => :string, %w(steps -s) => :boolean, %w(message -m) => :string, :'no-editor' => :boolean # $ stepup version create [--level|-l <level-name>] [--steps|-s] [--message|-m <comment-string>] [--no-editor]
14
+ VERSION_ACTIONS = %w[show create help]
15
+ def version(action = nil)
16
+ action = "show" unless VERSION_ACTIONS.include?(action)
17
+ if self.respond_to?("version_#{action}")
18
+ send("version_#{action}")
19
+ else
20
+ puts "invalid action: #{action}"
21
+ end
13
22
  end
14
23
 
15
- desc "notes [object]", "show notes for the next version"
16
- method_options :clean => :boolean
17
- def notes(commit_base = nil)
18
- puts StepUp::Driver::Git.unversioned_notes(commit_base, options[:clean])
24
+ desc "init", "adds .stepuprc to your project and prepare your local repository to use notes"
25
+ method_options :update => :boolean
26
+ def init
27
+ content = File.read(File.expand_path("../config/step-up.yml", __FILE__))
28
+ if options[:update] || ! File.exists?(".stepuprc")
29
+ puts "#{File.exists?(".stepuprc") ? 'updating' : 'creating' } .stepuprc"
30
+ File.open(".stepuprc", "w") do |f|
31
+ f.write content
32
+ end
33
+ end
34
+ remotes_with_notes = driver.fetched_remotes('notes')
35
+ unfetched_remotes = driver.fetched_remotes - remotes_with_notes
36
+ unless remotes_with_notes.any? || unfetched_remotes.empty?
37
+ if unfetched_remotes.size > 1
38
+ remote = choose(unfetched_remotes, "Which remote would you like to fetch notes?")
39
+ return unless unfetched_remotes.include?(remote)
40
+ else
41
+ remote = unfetched_remotes.first
42
+ end
43
+ puts "Adding attribute below to .git/config (remote.#{ remote })"
44
+ puts " fetch = +refs/notes/*:refs/notes/*"
45
+ `git config --add remote.#{ remote }.fetch +refs/notes/*:refs/notes/*`
46
+ end
47
+ end
48
+
49
+ desc "changelog --top=<num> --format={default|wiki|html}", "show changelog from each version tag"
50
+ method_options %w[top -n] => :numeric
51
+ method_options %w[format -f] => :string
52
+ def changelog
53
+ log = []
54
+ method_name = "changelog_format_#{ options[:format] }"
55
+ method_name = "changelog_format_default" unless respond_to?(method_name)
56
+ driver.all_version_tags.each_with_index do |tag, index|
57
+ break if options[:top] && index >= options[:top]
58
+ log << send(method_name, tag)
59
+ end
60
+ puts log.join("\n\n")
61
+ end
62
+
63
+ desc "notes ACTION [object] [OPTIONS]", "show notes for the next version"
64
+ method_options :clean => :boolean, :steps => :boolean, :"-m" => :string, :since => :string
65
+ def notes(action = "show", commit_base = nil)
66
+ unless %w[show add remove help].include?(action)
67
+ commit_base ||= action
68
+ action = "show"
69
+ end
70
+ if self.respond_to?("notes_#{action}")
71
+ send("notes_#{action}", commit_base)
72
+ else
73
+ puts "invalid action: #{action}"
74
+ end
19
75
  end
20
76
 
21
77
  desc "-v, --version", "show the last version of the gem"
22
78
  def gem_version
23
79
  puts StepUp::VERSION
24
80
  end
81
+
82
+ protected
83
+
84
+ def changelog_format_default(tag)
85
+ tag_info = driver.version_tag_info(tag)
86
+ created_at = tag_info[:date].strftime("%b/%d %Y %H:%M %z")
87
+ "\033[0;33m#{tag} (#{created_at} by #{tag_info[:tagger]})\033[0m\n\n#{ tag_info[:message] }"
88
+ end
89
+
90
+ def changelog_format_html(tag)
91
+ tag_info = driver.version_tag_info(tag)
92
+ created_at = tag_info[:date].strftime("%b/%d %Y %H:%M %z")
93
+ log = []
94
+ log << "<h3 class=\"changelog_header\">#{tag} (#{created_at} by #{tag_info[:tagger]})</h3>"
95
+ log << " <pre class=\"changelog_description\">"
96
+ log << tag_info[:message].gsub(/^/, ' ')
97
+ log << " </pre>"
98
+ log << "<br/>"
99
+ log.join("\n")
100
+ end
101
+
102
+ def changelog_format_wiki(tag)
103
+ tag_info = driver.version_tag_info(tag)
104
+ created_at = tag_info[:date].strftime("%b/%d %Y %H:%M %z")
105
+ log = []
106
+ log << "== #{tag} (#{created_at} by #{tag_info[:tagger]}) ==\n"
107
+ log << tag_info[:message].gsub(/^(\s+)-/, '\1*')
108
+ log.join("\n")
109
+ end
110
+
111
+ def notes_show(commit_base = nil)
112
+ message = []
113
+ message << "Showing notes since #{ options[:since] }#{ " (including notes of tags: #{ ranged_notes.scoped_tags.join(", ")})" if ranged_notes.scoped_tags.any? }" unless options[:since].nil?
114
+ message << "---"
115
+ message << get_notes
116
+ puts message.join("\n")
117
+ end
118
+
119
+ def notes_remove(commit_base)
120
+ commit_base = "HEAD" if commit_base.nil?
121
+ ranged_notes = StepUp::RangedNotes.new(driver, nil, commit_base)
122
+ notes = ranged_notes.notes_of(ranged_notes.last_commit).as_hash
123
+ sections = notes.keys
124
+ if sections.empty?
125
+ puts "No notes found"
126
+ else
127
+ if sections.size > 1
128
+ section = choose(sections, "Which section you want to remove notes?")
129
+ return unless sections.include?(section)
130
+ else
131
+ section = sections.first
132
+ end
133
+ steps = driver.steps_to_remove_notes(section, ranged_notes.last_commit)
134
+ print_or_run(steps, options[:steps])
135
+ end
136
+ end
137
+
138
+ def notes_add(commit_base = nil)
139
+ message = options[:m]
140
+ message = nil if options[:m] =~ /^(|m)$/
141
+ message ||= get_message("Note message:\n>>", " >")
142
+ unless message.empty?
143
+ section = choose(CONFIG.notes_sections.names, "Choose a section to add the note:")
144
+ return if section.nil? || ! CONFIG.notes_sections.names.include?(section)
145
+ steps = driver.steps_for_add_notes(section, message, commit_base)
146
+ print_or_run(steps, options[:steps])
147
+ end
148
+ end
149
+
150
+ def version_show
151
+ if options[:levels]
152
+ puts "Current version levels:"
153
+ version_levels.each do |level|
154
+ puts " - #{level}"
155
+ end
156
+ else
157
+ puts driver.last_version_tag("HEAD", true)
158
+ end
159
+ end
160
+
161
+ def version_create
162
+ level = options[:level] || version_levels.last
163
+ message = get_notes(true, get_custom_message)
164
+ message = edit_message(driver.class::VERSION_MESSAGE_FILE_PATH, message) unless options[:'no-editor']
165
+
166
+ if message.strip.empty?
167
+ puts "\ninvalid version message: too short"
168
+ return
169
+ end
170
+
171
+ if version_levels.include? level
172
+ steps = driver.steps_to_increase_version(level, "HEAD", message)
173
+ print_or_run(steps, options[:steps])
174
+ else
175
+ puts "invalid version create option: #{level}"
176
+ end
177
+ end
178
+
179
+ private
180
+
181
+ def edit_message(temp_file, initial_content)
182
+ File.open(temp_file, "w"){ |f| f.write initial_content }
183
+ editor = driver.editor_name
184
+ if editor =~ /\w/
185
+ if editor =~ /^vim?\b/
186
+ system "#{ editor } #{ temp_file }"
187
+ else
188
+ `#{ editor } #{ temp_file } && wait $!`
189
+ end
190
+ File.read(temp_file).rstrip
191
+ end
192
+ end
193
+
194
+ def driver
195
+ @driver ||= StepUp::Driver::Git.new
196
+ end
197
+
198
+ def ranged_notes
199
+ unless defined? @ranged_notes
200
+ tag = options[:since] || driver.last_version_tag
201
+ if tag =~ /[1-9]/
202
+ tag = tag.gsub(/\+\d*$/, '')
203
+ else
204
+ tag = nil
205
+ end
206
+ @ranged_notes = StepUp::RangedNotes.new(driver, tag, "HEAD")
207
+ end
208
+ @ranged_notes
209
+ end
210
+
211
+ def get_notes(clean = options[:clean], custom_message = nil)
212
+ changelog_options = {}
213
+ changelog_options[:mode] = :with_objects unless clean
214
+ changelog_options[:custom_message] = custom_message
215
+ notes = (options[:since].nil? ? ranged_notes.notes : ranged_notes.all_notes)
216
+ notes.as_hash.to_changelog(changelog_options)
217
+ end
218
+
219
+ def choose(list, statement)
220
+ puts statement
221
+ list.each_with_index do |item, index|
222
+ puts " #{ index + 1 }. #{ item }"
223
+ end
224
+ value = ask(">>")
225
+ if value =~ /^\d+$/
226
+ value = value.to_i
227
+ value > 0 && list.size > value - 1 ? list[value - 1] : nil
228
+ else
229
+ value.empty? ? nil : value
230
+ end
231
+ end
232
+
233
+ def get_message(prompt1, prompt2, total_breaks = 1)
234
+ lines = []
235
+ prompt = prompt1
236
+ empty = total_breaks - 1
237
+ line = nil
238
+ begin
239
+ lines << line unless line.nil?
240
+ line = raw_ask(prompt)
241
+ prompt = prompt2
242
+ empty = line.empty? ? empty + 1 : 0
243
+ end while empty < total_breaks
244
+ lines.join("\n").gsub(/\n+\z/, '').gsub(/\A\n+/, '')
245
+ end
246
+
247
+ def raw_ask(statement, color = nil)
248
+ say("#{statement} ", color)
249
+ $stdin.gets.chomp
250
+ end
251
+
252
+ def version_levels
253
+ CONFIG["versioning"]["version_levels"]
254
+ end
255
+
256
+ def print_or_run(steps, print)
257
+ if print
258
+ puts steps.join("\n")
259
+ else
260
+ steps.each do |step|
261
+ run step
262
+ end
263
+ end
264
+ end
265
+
266
+ def get_custom_message
267
+ message = options[:message]
268
+ (message && !message.strip.empty?) ? message : nil
269
+ end
25
270
  end
26
271
  end
@@ -2,7 +2,72 @@ require 'yaml'
2
2
  module StepUp
3
3
  CONFIG = {}
4
4
 
5
- def self.load_config path
5
+ module ConfigExt
6
+ def method_missing(m, *args, &block)
7
+ super unless self.key?(m.to_s)
8
+ value = self[m.to_s]
9
+ if value.is_a?(Hash) && ! value.kind_of?(ConfigExt)
10
+ class << value
11
+ include ConfigExt
12
+ end
13
+ end
14
+ value
15
+ end
16
+ end
17
+ module ConfigSectionsExt
18
+ def names
19
+ map{ |section| section.is_a?(String) ? section : section["name"] }
20
+ end
21
+
22
+ def prefixes
23
+ map{ |section| section.is_a?(String) ? to_prefix(section) : (section["prefix"] || to_prefix(section["name"])) }
24
+ end
25
+
26
+ def labels
27
+ map{ |section| section.is_a?(String) ? to_label(section) : (section["label"] || to_label(section["name"])) }
28
+ end
29
+
30
+ def tags
31
+ map{ |section| section.is_a?(String) ? to_tag(section) : (section["tag"] || to_tag(section["name"])) }
32
+ end
33
+
34
+ def label(section)
35
+ labels[names.index(section)]
36
+ end
37
+
38
+ def tag(section)
39
+ tags[names.index(section)]
40
+ end
41
+
42
+ private
43
+
44
+ def to_prefix(name)
45
+ "#{ (name.respond_to?(:singularize) ? name.singularize : name).gsub(/_/, ' ') }: "
46
+ end
47
+
48
+ def to_label(name)
49
+ "#{ name.capitalize.gsub(/_/, ' ') }:"
50
+ end
51
+
52
+ def to_tag(name)
53
+ name.respond_to?(:singularize) ? name.singularize : name
54
+ end
55
+ end
56
+ class << CONFIG
57
+ include ConfigExt
58
+
59
+ def notes_sections
60
+ sections = notes.sections
61
+ unless sections.kind_of?(ConfigSectionsExt)
62
+ class << sections
63
+ include ConfigSectionsExt
64
+ end
65
+ end
66
+ sections
67
+ end
68
+ end
69
+
70
+ def self.load_config(path)
6
71
  return CONFIG unless File.exists? path
7
72
  CONFIG.merge! YAML.load_file(path)
8
73
  rescue TypeError => e
@@ -10,5 +75,5 @@ module StepUp
10
75
  end
11
76
 
12
77
  load_config File.expand_path('../config/step-up.yml', __FILE__)
13
- load_config '.versionrc' # from working folder
78
+ load_config '.stepuprc' # from working folder
14
79
  end
@@ -5,13 +5,25 @@ notes:
5
5
  section: "versioning"
6
6
  changelog_message: "available on {version}"
7
7
  sections:
8
- - changes
9
- - bugfixes
10
- - features
11
- - deploy_steps
8
+ - name: "changes"
9
+ prefix: "change: "
10
+ label: "Changes:"
11
+ tag: "change"
12
+ - name: "bugfixes"
13
+ prefix: "bugfix: "
14
+ label: "Bugfixes:"
15
+ tag: "bugfix"
16
+ - name: "features"
17
+ prefix: "feature: "
18
+ label: "Features:"
19
+ tag: "feature"
20
+ - name: "deploy_steps"
21
+ prefix: "deploy_step: "
22
+ label: "Deploy steps:"
23
+ tag: "deploy_step"
12
24
  versioning:
13
25
  version_mask: "v0.0.0.9.rc9"
14
- version_parts:
26
+ version_levels:
15
27
  - major
16
28
  - minor
17
29
  - tiny