chlog 0.9.0 → 1.7.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of chlog might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bin/chlog +574 -0
- data/lib/chlog.rb +4 -408
- metadata +7 -8
- data/exe/chlog +0 -82
- data/lib/chlog/version.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e1d93a01689db7e2f0c0373dc438fdc85288b62d2d6e9cd00b2d4d129c19141
|
4
|
+
data.tar.gz: 68d992c752532b379cc963ae1eed3c47dadaef89be5061e20c4b4cc8319df997
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7246c0677b62be2f9a59f32c8b2c4a75412a9584c58c687bb58e7d405a5124ad81ac85c516aa0668c59c7e885a8f2449b75c30d66a5de33906f46deb939bd78c
|
7
|
+
data.tar.gz: d4574cae6c37ac58525528145d5c1c59fa055fe8a0796065c1bc5cf507bbc996f998cabd06562f54cdaeebbe4f4e9d181b733a23c1f696548def5c9f618e9596
|
data/bin/chlog
ADDED
@@ -0,0 +1,574 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# ------------------------------------------------------
|
3
|
+
# File : chlog.rb
|
4
|
+
# Authors : ccmywish <ccmywish@qq.com>
|
5
|
+
# Created on : <2022-03-18>
|
6
|
+
# Last modified : <2022-11-29>
|
7
|
+
#
|
8
|
+
# chlog:
|
9
|
+
#
|
10
|
+
# Maintain your project's Changelog on the cli.
|
11
|
+
#
|
12
|
+
# ------------------------------------------------------
|
13
|
+
|
14
|
+
require 'chlog'
|
15
|
+
require 'highline'
|
16
|
+
require 'date'
|
17
|
+
|
18
|
+
HL = HighLine.new
|
19
|
+
|
20
|
+
$today = Date.today.to_s
|
21
|
+
|
22
|
+
UNRELEASED_TITLE = "## [Unreleased](#) (#$today)"
|
23
|
+
|
24
|
+
CHLOG_TEMPLATE = <<EOT
|
25
|
+
# Changelog
|
26
|
+
|
27
|
+
#{UNRELEASED_TITLE}
|
28
|
+
|
29
|
+
<br>
|
30
|
+
|
31
|
+
## [Initialize](#) (#$today)
|
32
|
+
|
33
|
+
<br>
|
34
|
+
|
35
|
+
<hr>
|
36
|
+
|
37
|
+
This Changelog is maintained with [chlog](https://github.com/ccmywish/chlog)
|
38
|
+
|
39
|
+
EOT
|
40
|
+
|
41
|
+
$Changelog_file = `git rev-parse --show-toplevel`.chomp + '/CHANGELOG.md'
|
42
|
+
|
43
|
+
def get_changelog
|
44
|
+
file = $Changelog_file
|
45
|
+
if File.exists? file
|
46
|
+
return File.read file
|
47
|
+
else
|
48
|
+
puts "chlog: Auto generate #$Changelog_file"
|
49
|
+
File.write(file, CHLOG_TEMPLATE)
|
50
|
+
return File.read file
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def help
|
56
|
+
puts <<EOH
|
57
|
+
chlog (v#{CHLOG_GEM_VERSION}): Help maintain the Changelog of your projects
|
58
|
+
|
59
|
+
usage:
|
60
|
+
|
61
|
+
chlog => Generate CHANGELOG.md in git root
|
62
|
+
chlog -r 3.14 => Release version to v3.14
|
63
|
+
chlog -g [-m] [--sub] => Reuse last Git log [main category/sub category]
|
64
|
+
chlog [--sub] log => Determine main category [sub category] add log
|
65
|
+
chlog -n [--sub] log => Add log to New features [sub category]
|
66
|
+
chlog -e [--sub] log => Add log to Enhancements [sub category]
|
67
|
+
chlog -b [--sub] log => Add log to Bug fixes [sub category]
|
68
|
+
chlog -s [--sub] log => Add log to Security [sub category]
|
69
|
+
chlog -c [--sub] log => Add log to Compatibility[sub category]
|
70
|
+
chlog -d [--sub] log => Add log to Deprecations [sub category]
|
71
|
+
|
72
|
+
EOH
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def match_unreleased?(str)
|
77
|
+
str =~ /^## \[Unreleased\]\(.*\) \(\d{4}-\d\d-\d\d\)/
|
78
|
+
end
|
79
|
+
|
80
|
+
def match_unreleased_fail!(str)
|
81
|
+
unless match_unreleased?(str)
|
82
|
+
puts "chlog: Unmathed format with chlog"
|
83
|
+
# "#{lns[1][1..]}"
|
84
|
+
puts " Unreleased version must be the third line.", ""
|
85
|
+
exit -1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def match_new_features?(str)
|
90
|
+
str =~ /^### New features:/
|
91
|
+
end
|
92
|
+
|
93
|
+
def match_enhancements?(str)
|
94
|
+
str =~ /^### Enhancements:/
|
95
|
+
end
|
96
|
+
|
97
|
+
def match_bug_fixes?(str)
|
98
|
+
str =~ /^### Bug fixes:/
|
99
|
+
end
|
100
|
+
|
101
|
+
def match_security?(str)
|
102
|
+
str =~ /^### Security:/
|
103
|
+
end
|
104
|
+
|
105
|
+
def match_compatibility?(str)
|
106
|
+
str =~ /^### Compatibility:/
|
107
|
+
end
|
108
|
+
|
109
|
+
def match_deprecations?(str)
|
110
|
+
str =~ /^### Deprecations:/
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
def next_version_index(lns_array)
|
115
|
+
nvi = lns_array[3..].each_with_index {break _2 if _1.start_with?("## [") }
|
116
|
+
nvi += 3
|
117
|
+
end
|
118
|
+
|
119
|
+
def next_category_index(lns_array)
|
120
|
+
nci = lns_array[3...nvi].each_with_index {break _2 if _1.start_with?("### ") }
|
121
|
+
|
122
|
+
# The not match return value is not nil!! But an array!!
|
123
|
+
if nci.is_a(Integer)
|
124
|
+
nci += 3
|
125
|
+
else
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
#
|
132
|
+
# ### New features:
|
133
|
+
#
|
134
|
+
# - xaaaaa
|
135
|
+
# - xbbbbb
|
136
|
+
# - xccccc [-> this]
|
137
|
+
#
|
138
|
+
def find_main_category_last_list_item_index(lns,
|
139
|
+
main_cat_index,
|
140
|
+
first_lower_order_category_index)
|
141
|
+
mci = main_cat_index
|
142
|
+
nci = first_lower_order_category_index
|
143
|
+
|
144
|
+
list_i = lns[mci+2..nci].each_with_index do
|
145
|
+
break _2 if _1 !~ /^- .*/
|
146
|
+
end
|
147
|
+
list_i += mci+2 - 1
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
ORDER_TABLE = %w[
|
152
|
+
new_features
|
153
|
+
enhancements
|
154
|
+
bug_fixes
|
155
|
+
security
|
156
|
+
compatibility
|
157
|
+
deprecations
|
158
|
+
]
|
159
|
+
|
160
|
+
# New features
|
161
|
+
# Enhancements
|
162
|
+
# Bug fixes
|
163
|
+
# Security
|
164
|
+
# Compatibility
|
165
|
+
# Deprecations
|
166
|
+
CATEGORY_TABLE = ORDER_TABLE.map do
|
167
|
+
_1.split('_').join(' ').capitalize
|
168
|
+
end
|
169
|
+
|
170
|
+
MATCH_TABLE = ORDER_TABLE.map do
|
171
|
+
('match_' + _1 + '?').to_sym
|
172
|
+
end
|
173
|
+
|
174
|
+
METHOD_HASH_TABLE = ([1,2,3,4,5,6].zip ORDER_TABLE.map {|m| ('add_to_' + m).to_sym }).to_h
|
175
|
+
|
176
|
+
|
177
|
+
|
178
|
+
def first_lower_order_category_index(lns_array, category_order)
|
179
|
+
|
180
|
+
o = category_order
|
181
|
+
|
182
|
+
nvi = next_version_index(lns_array)
|
183
|
+
|
184
|
+
while o < MATCH_TABLE.size
|
185
|
+
lns_array[0...nvi].each_with_index do
|
186
|
+
if send(MATCH_TABLE[o], _1)
|
187
|
+
return _2
|
188
|
+
end
|
189
|
+
end
|
190
|
+
o += 1
|
191
|
+
end
|
192
|
+
|
193
|
+
# Next version above
|
194
|
+
return (nvi - 2)
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
|
199
|
+
#
|
200
|
+
# A meta function: do the real work other 6 functions delegate
|
201
|
+
#
|
202
|
+
# This function detect the main category, and then handle the
|
203
|
+
# sub category.
|
204
|
+
#
|
205
|
+
def meta_add_to_a_category(log, main_category_order, category)
|
206
|
+
|
207
|
+
content = get_changelog
|
208
|
+
lns = content.lines
|
209
|
+
|
210
|
+
match_unreleased_fail!(lns[2])
|
211
|
+
|
212
|
+
nvi = next_version_index(lns)
|
213
|
+
# To find the existing category line
|
214
|
+
main_cat_i = lns[0...nvi].each_with_index do
|
215
|
+
# Adjust to index of the order table
|
216
|
+
break _2 if send(MATCH_TABLE[main_category_order-1], _1)
|
217
|
+
end
|
218
|
+
|
219
|
+
#
|
220
|
+
# Always update to latest time when making a log
|
221
|
+
#
|
222
|
+
# We must add a "\n" because every element of the array
|
223
|
+
# is a sentence with a trailing newline, then they can
|
224
|
+
# be 'joined' to a working long string
|
225
|
+
#
|
226
|
+
lns[2] = UNRELEASED_TITLE + "\n"
|
227
|
+
|
228
|
+
f_l_o_c_i = first_lower_order_category_index(lns, main_category_order)
|
229
|
+
|
230
|
+
if main_cat_i.class != Integer
|
231
|
+
################################################################################
|
232
|
+
# The main category not exists
|
233
|
+
################################################################################
|
234
|
+
|
235
|
+
|
236
|
+
########################################
|
237
|
+
if $sub_category
|
238
|
+
|
239
|
+
# Because the main category not exists,
|
240
|
+
# nor does the sub category
|
241
|
+
#
|
242
|
+
sub_cat_i = f_l_o_c_i
|
243
|
+
to_wr = lns[0...sub_cat_i].join + "### #{category}:\n\n" + "**#{$sub_category}**\n\n" + ' - ' + log + "\n\n"
|
244
|
+
File.write($Changelog_file, to_wr + lns[sub_cat_i..].join)
|
245
|
+
|
246
|
+
puts "chlog: Add log to #{category}/#{$sub_category}"
|
247
|
+
return
|
248
|
+
end # end of if $sub_category
|
249
|
+
########################################
|
250
|
+
|
251
|
+
|
252
|
+
# situation: no sub category
|
253
|
+
|
254
|
+
# Not need adjust to the index of the order table
|
255
|
+
log_loc = f_l_o_c_i
|
256
|
+
puts "chlog: Add '#{category}' category"
|
257
|
+
to_wr = lns[0...(log_loc)].join + "### #{category}:\n\n" + '- ' + log + "\n"
|
258
|
+
File.write($Changelog_file, to_wr + lns[log_loc-1..].join)
|
259
|
+
|
260
|
+
else
|
261
|
+
################################################################################
|
262
|
+
# The main category exists
|
263
|
+
################################################################################
|
264
|
+
|
265
|
+
|
266
|
+
########################################
|
267
|
+
if $sub_category
|
268
|
+
|
269
|
+
# from first list item to next main cat
|
270
|
+
# search if sub cat already exists
|
271
|
+
sub_cat_i = lns[main_cat_i+2...f_l_o_c_i].each_with_index do
|
272
|
+
break _2 if _1 =~ /^\*\*#{$sub_category}\*\*/
|
273
|
+
end
|
274
|
+
|
275
|
+
if !sub_cat_i.is_a?(Integer)
|
276
|
+
# the sub category not exists
|
277
|
+
|
278
|
+
sub_cat_i = 2 + find_main_category_last_list_item_index(lns,main_cat_i,f_l_o_c_i)
|
279
|
+
to_wr = lns[0...sub_cat_i].join + "**#{$sub_category}**\n\n" + ' - ' + log + "\n\n"
|
280
|
+
File.write($Changelog_file, to_wr + lns[sub_cat_i..].join)
|
281
|
+
else
|
282
|
+
# the sub category exists
|
283
|
+
|
284
|
+
sub_cat_i += main_cat_i+2
|
285
|
+
to_wr = lns[0..sub_cat_i+1].join + ' - ' + log + "\n"
|
286
|
+
File.write($Changelog_file, to_wr + lns[sub_cat_i+2..].join)
|
287
|
+
end
|
288
|
+
|
289
|
+
puts "chlog: Add log to #{category}/#{$sub_category}"
|
290
|
+
return
|
291
|
+
end # end of if $sub_category
|
292
|
+
########################################
|
293
|
+
|
294
|
+
|
295
|
+
|
296
|
+
# situation: no sub category
|
297
|
+
|
298
|
+
to_wr = lns[0..main_cat_i+1].join + '- ' + log + "\n"
|
299
|
+
|
300
|
+
# main category list should be separated with
|
301
|
+
# sub category
|
302
|
+
if lns[main_cat_i+2] =~ /\*\*.*/
|
303
|
+
to_wr += "\n"
|
304
|
+
end
|
305
|
+
File.write($Changelog_file, to_wr + lns[main_cat_i+2..].join)
|
306
|
+
end
|
307
|
+
|
308
|
+
puts "chlog: Add log to #{category}"
|
309
|
+
try_auto_commit(log)
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
#
|
314
|
+
# main category order: 1
|
315
|
+
#
|
316
|
+
def add_to_new_features(log)
|
317
|
+
meta_add_to_a_category(log, 1, "New features")
|
318
|
+
end
|
319
|
+
|
320
|
+
#
|
321
|
+
# main category order: 2
|
322
|
+
#
|
323
|
+
def add_to_enhancements(log)
|
324
|
+
meta_add_to_a_category(log, 2, "Enhancements")
|
325
|
+
end
|
326
|
+
|
327
|
+
#
|
328
|
+
# main_category_order: 3
|
329
|
+
#
|
330
|
+
def add_to_bug_fixes(log)
|
331
|
+
meta_add_to_a_category(log, 3, "Bug fixes")
|
332
|
+
end
|
333
|
+
|
334
|
+
#
|
335
|
+
# main_category_order: 4
|
336
|
+
#
|
337
|
+
def add_to_security(log)
|
338
|
+
meta_add_to_a_category(log, 4, "Security")
|
339
|
+
end
|
340
|
+
|
341
|
+
#
|
342
|
+
# main_category_order: 5
|
343
|
+
#
|
344
|
+
def add_to_compatibility(log)
|
345
|
+
meta_add_to_a_category(log, 5, "Compatibility")
|
346
|
+
end
|
347
|
+
|
348
|
+
#
|
349
|
+
# The last of order table
|
350
|
+
# main_category_order: 6
|
351
|
+
#
|
352
|
+
def add_to_deprecations(log)
|
353
|
+
meta_add_to_a_category(log, 6, "Deprecations")
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
#
|
358
|
+
# Release a version
|
359
|
+
#
|
360
|
+
def release_new_version(ver)
|
361
|
+
if ver.nil?
|
362
|
+
puts "chlog: No version supplied! "
|
363
|
+
puts " Use chlog -r <version>!",""
|
364
|
+
exit 1
|
365
|
+
end
|
366
|
+
|
367
|
+
if !ver.downcase.start_with?('v') and ver[0].match? /\d/
|
368
|
+
ver = 'v' + ver
|
369
|
+
end
|
370
|
+
|
371
|
+
content = get_changelog
|
372
|
+
lns = content.lines
|
373
|
+
|
374
|
+
match_unreleased_fail!(lns[2])
|
375
|
+
|
376
|
+
header = <<EOF
|
377
|
+
# Changelog
|
378
|
+
|
379
|
+
## [Unreleased](#) (#$today)
|
380
|
+
|
381
|
+
<br>
|
382
|
+
|
383
|
+
EOF
|
384
|
+
|
385
|
+
new_version = "## [#{ver}](#) (#$today)\n"
|
386
|
+
new_cont = header + new_version + lns[3..].join('')
|
387
|
+
File.write($Changelog_file, new_cont)
|
388
|
+
puts "Release #{ver} in Changelog!"
|
389
|
+
log = "Release #{ver}"
|
390
|
+
try_auto_commit(log)
|
391
|
+
end
|
392
|
+
|
393
|
+
|
394
|
+
#
|
395
|
+
# Help user interactively
|
396
|
+
#
|
397
|
+
def ask_user_category
|
398
|
+
category = HL.choose do |menu|
|
399
|
+
menu.index_color = :rgb_77bbff
|
400
|
+
menu.prompt = "What main category?"
|
401
|
+
# return number!!! not string!!!
|
402
|
+
menu.choices(*CATEGORY_TABLE)
|
403
|
+
end
|
404
|
+
|
405
|
+
option = CATEGORY_TABLE.index(category)
|
406
|
+
|
407
|
+
if $sub_category.nil?
|
408
|
+
sub = HL.ask "What sub category? [NIL/sub]"
|
409
|
+
case sub
|
410
|
+
when ''
|
411
|
+
$sub_category = nil
|
412
|
+
else
|
413
|
+
$sub_category = sub
|
414
|
+
end
|
415
|
+
end
|
416
|
+
return option
|
417
|
+
end
|
418
|
+
|
419
|
+
|
420
|
+
def add_log_sensitive(log, with_commit_msg: true)
|
421
|
+
|
422
|
+
# Be careful the when statements order
|
423
|
+
# Match the case you want first
|
424
|
+
option = case log
|
425
|
+
when /deprecat/i, /^remove/i, /^not/i then 6
|
426
|
+
when /^new/i, /^add/i, /new feature/i, /^support/i then 1
|
427
|
+
when /^fix/i, /repair/i, /bug/i then 3
|
428
|
+
when /compatible/i, /compatibility/i then 5
|
429
|
+
when /security/i, /secure/i, /cve/i then 4
|
430
|
+
when /feature/i, /update/i, /problem/i, /issue/i then 2
|
431
|
+
else
|
432
|
+
# Just a fake option to yield main_category -> nil
|
433
|
+
10
|
434
|
+
end
|
435
|
+
|
436
|
+
|
437
|
+
main_category = CATEGORY_TABLE[option-1]
|
438
|
+
if main_category.nil?
|
439
|
+
re = 'need ask user'
|
440
|
+
else
|
441
|
+
re = HL.ask "Add to '#{main_category}'? [Y/n]"
|
442
|
+
end
|
443
|
+
if re == '' || re.downcase == 'y' || re.downcase == 'yes'
|
444
|
+
send(METHOD_HASH_TABLE[option], log)
|
445
|
+
else
|
446
|
+
option = ask_user_category
|
447
|
+
send(METHOD_HASH_TABLE[option+1], log)
|
448
|
+
end
|
449
|
+
|
450
|
+
try_auto_commit(with_commit_msg)
|
451
|
+
end
|
452
|
+
|
453
|
+
|
454
|
+
#
|
455
|
+
# Try auto commit every time
|
456
|
+
#
|
457
|
+
def try_auto_commit(msg = nil)
|
458
|
+
|
459
|
+
files = `git diff --exit-code --name-only`
|
460
|
+
files = files.split
|
461
|
+
if (files.size == 1) && (files[0] == "CHANGELOG.md")
|
462
|
+
re = HL.ask "chlog: commit the Changelog? [Y/n]"
|
463
|
+
if re == "" || re.downcase == 'y' || re.downcase == 'yes'
|
464
|
+
|
465
|
+
system "git add #$Changelog_file"
|
466
|
+
# Can't merge the git add op here, because 'system' only treat them
|
467
|
+
# as one command integrally
|
468
|
+
system <<~COMMAND
|
469
|
+
git commit -m "Update the Changelog with info: #{msg}"
|
470
|
+
COMMAND
|
471
|
+
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
|
477
|
+
#
|
478
|
+
# Reuse git commit log
|
479
|
+
#
|
480
|
+
def add_with_git(first, second)
|
481
|
+
|
482
|
+
main_category = nil
|
483
|
+
|
484
|
+
if first =~ /^-([nebscd])$/
|
485
|
+
main_category = $1
|
486
|
+
elsif first =~ /^--(.*)/
|
487
|
+
$sub_category = $1
|
488
|
+
end
|
489
|
+
|
490
|
+
if second =~ /^-([nebscd])$/
|
491
|
+
main_category = $1
|
492
|
+
elsif second =~ /^--(.*)/
|
493
|
+
$sub_category = $1
|
494
|
+
end
|
495
|
+
|
496
|
+
|
497
|
+
gitlog = `git log --oneline -n 1`
|
498
|
+
log = gitlog.split(' ')
|
499
|
+
log = log[1..].join(' ')
|
500
|
+
puts "last commit: #{log}"
|
501
|
+
|
502
|
+
|
503
|
+
if main_category.nil?
|
504
|
+
return add_log_sensitive(log)
|
505
|
+
end
|
506
|
+
|
507
|
+
main_cat_to_num = %w[n 1 e 2 b 3 s 4 c 5 d 6].to_h { [_1,_1] }
|
508
|
+
option = main_cat_to_num[main_category]
|
509
|
+
|
510
|
+
m = METHOD_HASH_TABLE[option]
|
511
|
+
if m
|
512
|
+
send(m, log)
|
513
|
+
else
|
514
|
+
# Not with commit msg, because we are just using the last commit msg
|
515
|
+
add_log_sensitive(log, with_commit_msg: false)
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
|
520
|
+
#############
|
521
|
+
# main
|
522
|
+
#############
|
523
|
+
|
524
|
+
if $*.size == 0
|
525
|
+
if File.exists? $Changelog_file
|
526
|
+
puts "chlog: Already exists Changelog (#$Changelog_file)"
|
527
|
+
puts " Use chlog -h to see options"
|
528
|
+
else
|
529
|
+
get_changelog
|
530
|
+
end
|
531
|
+
exit
|
532
|
+
end
|
533
|
+
|
534
|
+
action = $*[0]
|
535
|
+
if $*[1] =~ /--(.*)/
|
536
|
+
puts "-> DEBUG: #$1"
|
537
|
+
$sub_category = $1
|
538
|
+
log = $*[2..].join(' ')
|
539
|
+
else
|
540
|
+
$sub_category = nil
|
541
|
+
log = $*[1..].join(' ')
|
542
|
+
end
|
543
|
+
|
544
|
+
|
545
|
+
begin
|
546
|
+
|
547
|
+
case action
|
548
|
+
when "-h", "--help" then help()
|
549
|
+
when "-n" then add_to_new_features(log)
|
550
|
+
when "-e" then add_to_enhancements(log)
|
551
|
+
when "-b" then add_to_bug_fixes(log)
|
552
|
+
when "-s" then add_to_security(log)
|
553
|
+
when "-c" then add_to_compatibility(log)
|
554
|
+
when "-d" then add_to_deprecations(log)
|
555
|
+
when "-r" then release_new_version($*[1])
|
556
|
+
when "-g" then add_with_git($*[1], $*[2])
|
557
|
+
else
|
558
|
+
if action =~ /^-[^-]*$/
|
559
|
+
puts "chlog: Unknown option!"
|
560
|
+
exit 1
|
561
|
+
else
|
562
|
+
if $*[0] =~ /^--(.*)/
|
563
|
+
$sub_category = $1
|
564
|
+
log = $*[1..].join(' ')
|
565
|
+
else
|
566
|
+
$sub_category = nil
|
567
|
+
log = $*[0..].join(' ')
|
568
|
+
end
|
569
|
+
add_log_sensitive(log)
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
rescue Interrupt
|
574
|
+
end
|
data/lib/chlog.rb
CHANGED
@@ -1,417 +1,13 @@
|
|
1
1
|
# ------------------------------------------------------
|
2
2
|
# File : chlog.rb
|
3
|
-
# Authors :
|
3
|
+
# Authors : ccmywish <ccmywish@qq.com>
|
4
4
|
# Created on : <2022-04-15>
|
5
|
-
# Last modified : <
|
5
|
+
# Last modified : <2022-11-29>
|
6
6
|
#
|
7
7
|
# chlog:
|
8
8
|
#
|
9
9
|
# Maintain your project's Changelog on the cli.
|
10
|
+
#
|
10
11
|
# ------------------------------------------------------
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
class Chlog::Logger
|
15
|
-
|
16
|
-
attr_accessor :sub_category, # 存储次标题
|
17
|
-
:log, # 存储log
|
18
|
-
:highline # HighLine 实例
|
19
|
-
:changelog # CHANGELOG.md 位置
|
20
|
-
|
21
|
-
|
22
|
-
def initialize
|
23
|
-
require 'highline'
|
24
|
-
@highline = HighLine.new
|
25
|
-
|
26
|
-
require 'open3'
|
27
|
-
# Prevent current directory is not git directory
|
28
|
-
Open3.popen3("git rev-parse --show-toplevel") do |i, o, err, t|
|
29
|
-
if err.read.include?("fatal: not a git repository")
|
30
|
-
puts "chlog: Not a git directory!" or exit(false)
|
31
|
-
else
|
32
|
-
@changelog = `git rev-parse --show-toplevel`.chomp + '/CHANGELOG.md'
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
require 'date'
|
38
|
-
TODAY = Date.today.to_s
|
39
|
-
|
40
|
-
UNRELEASED_TITLE = "## [Unreleased](#) (#{TODAY})"
|
41
|
-
|
42
|
-
TEMPLATE = <<~EOT
|
43
|
-
# Changelog
|
44
|
-
|
45
|
-
#{UNRELEASED_TITLE}
|
46
|
-
|
47
|
-
<br>
|
48
|
-
|
49
|
-
## [Initialize](#) (#{TODAY})
|
50
|
-
|
51
|
-
<br>
|
52
|
-
|
53
|
-
<hr>
|
54
|
-
|
55
|
-
This Changelog is maintained with [chlog](https://github.com/ccmywish/chlog)
|
56
|
-
|
57
|
-
EOT
|
58
|
-
|
59
|
-
|
60
|
-
def generate_changelog
|
61
|
-
file = @changelog
|
62
|
-
if File.exist? file
|
63
|
-
puts "chlog: Already exists Changelog (#@changelog)" or return false
|
64
|
-
else
|
65
|
-
File.write(file, TEMPLATE)
|
66
|
-
puts "chlog: Generate #@changelog OK!" or return true
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
|
71
|
-
def get_changelog
|
72
|
-
file = @changelog
|
73
|
-
if File.exist? file
|
74
|
-
return File.read file
|
75
|
-
else
|
76
|
-
abort "chlog: No Changelog exists, use 'chlog -g' to generate!"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
|
-
class Chlog::Logger
|
84
|
-
|
85
|
-
def match_unreleased?(str)
|
86
|
-
str =~ /^## \[Unreleased\]\(.*\) \(\d{4}-\d\d-\d\d\)/
|
87
|
-
end
|
88
|
-
|
89
|
-
def match_unreleased_fail!(str)
|
90
|
-
unless match_unreleased?(str)
|
91
|
-
puts "chlog: Unmatched format with chlog"
|
92
|
-
puts
|
93
|
-
# "#{lns[1][1..]}"
|
94
|
-
abort "Unreleased version must be the third line"
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def match_new_features?(str)
|
99
|
-
str =~ /^### New features:/
|
100
|
-
end
|
101
|
-
|
102
|
-
def match_enhancements?(str)
|
103
|
-
str =~ /^### Enhancements:/
|
104
|
-
end
|
105
|
-
|
106
|
-
def match_bug_fixes?(str)
|
107
|
-
str =~ /^### Bug fixes:/
|
108
|
-
end
|
109
|
-
|
110
|
-
def match_security?(str)
|
111
|
-
str =~ /^### Security:/
|
112
|
-
end
|
113
|
-
|
114
|
-
def match_compatibility?(str)
|
115
|
-
str =~ /^### Compatibility:/
|
116
|
-
end
|
117
|
-
|
118
|
-
def match_deprecations?(str)
|
119
|
-
str =~ /^### Deprecations:/
|
120
|
-
end
|
121
|
-
|
122
|
-
|
123
|
-
def next_version_index(lns_array)
|
124
|
-
nvi = lns_array[3..].each_with_index {break _2 if _1.start_with?("## [") }
|
125
|
-
nvi += 3
|
126
|
-
end
|
127
|
-
|
128
|
-
def next_category_index(lns_array)
|
129
|
-
nci = lns_array[3...nvi].each_with_index {break _2 if _1.start_with?("### ") }
|
130
|
-
|
131
|
-
# The not match return value is not nil!! But an array!!
|
132
|
-
if nci.is_a(Integer) then nci += 3
|
133
|
-
else nil end
|
134
|
-
end
|
135
|
-
|
136
|
-
=begin
|
137
|
-
### New features:
|
138
|
-
|
139
|
-
- xaaaaa
|
140
|
-
- xbbbbb
|
141
|
-
- xccccc [-> this]
|
142
|
-
=end
|
143
|
-
def find_main_category_last_list_item_index(lns,
|
144
|
-
main_cat_index,
|
145
|
-
first_lower_order_category_index)
|
146
|
-
mci = main_cat_index
|
147
|
-
nci = first_lower_order_category_index
|
148
|
-
|
149
|
-
list_i = lns[mci+2..nci].each_with_index do
|
150
|
-
break _2 if _1 !~ /^- .*/
|
151
|
-
end
|
152
|
-
list_i += mci+2 - 1
|
153
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
LIST__Feature = %w[
|
157
|
-
new_features enhancements bug_fixes
|
158
|
-
security compatibility deprecations
|
159
|
-
]
|
160
|
-
|
161
|
-
# New features
|
162
|
-
# Enhancements
|
163
|
-
# Bug fixes
|
164
|
-
# Security
|
165
|
-
# Compatibility
|
166
|
-
# Deprecations
|
167
|
-
LIST__Main_Category = LIST__Feature.map do
|
168
|
-
_1.split('_').join(' ').capitalize
|
169
|
-
end
|
170
|
-
|
171
|
-
# match_new_features?
|
172
|
-
# match_enhancements?
|
173
|
-
# match_ ...
|
174
|
-
LIST__Match_Method = LIST__Feature.map do
|
175
|
-
('match_' + _1 + '?').to_sym
|
176
|
-
end
|
177
|
-
|
178
|
-
=begin
|
179
|
-
{
|
180
|
-
1 => :add_to_new_features,
|
181
|
-
2 => :add_to_enhancements,
|
182
|
-
...
|
183
|
-
}
|
184
|
-
=end
|
185
|
-
HASH__Order_To_AddTo_Method = ([1,2,3,4,5,6].zip LIST__Feature.map {|m| ('add_to_' + m).to_sym }).to_h
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
def take_action(action)
|
190
|
-
options = "nebscd"
|
191
|
-
|
192
|
-
=begin
|
193
|
-
{
|
194
|
-
"-n" => "new_features", "-e" => "enhancements", "-b" => "bug_fixes",
|
195
|
-
"-s" => "security", "-c" => "compatibility", "-d" => "deprecations"
|
196
|
-
}
|
197
|
-
=end
|
198
|
-
action_table = options.chars.map do |op|
|
199
|
-
'-' + op
|
200
|
-
end.zip(LIST__Feature).to_h
|
201
|
-
|
202
|
-
public_send "add_to_#{action_table[action]}"
|
203
|
-
end
|
204
|
-
|
205
|
-
|
206
|
-
def first_lower_order_category_index(lns_array, category_order)
|
207
|
-
|
208
|
-
o = category_order
|
209
|
-
|
210
|
-
nvi = next_version_index(lns_array)
|
211
|
-
|
212
|
-
while o < LIST__Match_Method.size
|
213
|
-
lns_array[0...nvi].each_with_index do
|
214
|
-
return _2 if send(LIST__Match_Method[o], _1)
|
215
|
-
end
|
216
|
-
o += 1
|
217
|
-
end
|
218
|
-
|
219
|
-
# Next version above
|
220
|
-
return (nvi - 2)
|
221
|
-
end
|
222
|
-
|
223
|
-
|
224
|
-
# A meta function: do the real work other 6 functions delegate
|
225
|
-
#
|
226
|
-
# This function detect the main category, and then handle the
|
227
|
-
# sub category.
|
228
|
-
def meta_add_to_a_category(log, main_category_order, category)
|
229
|
-
|
230
|
-
content = get_changelog
|
231
|
-
lns = content.lines
|
232
|
-
|
233
|
-
match_unreleased_fail!(lns[2])
|
234
|
-
|
235
|
-
nvi = next_version_index(lns)
|
236
|
-
# To find the existing category line
|
237
|
-
main_cat_i = lns[0...nvi].each_with_index do
|
238
|
-
# Adjust to index of the order table
|
239
|
-
break _2 if send(LIST__Match_Method[main_category_order-1], _1)
|
240
|
-
end
|
241
|
-
|
242
|
-
#
|
243
|
-
# Always update to latest time when making a log
|
244
|
-
#
|
245
|
-
# We must add a "\n" because every element of the array
|
246
|
-
# is a sentence with a trailing newline, then they can
|
247
|
-
# be 'joined' to a working long string
|
248
|
-
#
|
249
|
-
lns[2] = UNRELEASED_TITLE + "\n"
|
250
|
-
|
251
|
-
f_l_o_c_i = first_lower_order_category_index(lns, main_category_order)
|
252
|
-
|
253
|
-
if main_cat_i.class != Integer
|
254
|
-
################################################################################
|
255
|
-
# The main category not exists
|
256
|
-
################################################################################
|
257
|
-
|
258
|
-
|
259
|
-
########################################
|
260
|
-
if @sub_category
|
261
|
-
|
262
|
-
# Because the main category not exists,
|
263
|
-
# nor does the sub category
|
264
|
-
#
|
265
|
-
sub_cat_i = f_l_o_c_i
|
266
|
-
to_wr = lns[0...sub_cat_i].join + "### #{category}:\n\n" + "**#{@sub_category}**\n\n" + ' - ' + log + "\n\n"
|
267
|
-
File.write(@changelog, to_wr + lns[sub_cat_i..].join)
|
268
|
-
|
269
|
-
puts "chlog: Add log to #{category}/#{@sub_category}"
|
270
|
-
return
|
271
|
-
end # end of if @sub_category
|
272
|
-
########################################
|
273
|
-
|
274
|
-
# situation: no sub category
|
275
|
-
|
276
|
-
# Not need adjust to the index of the order table
|
277
|
-
log_loc = f_l_o_c_i
|
278
|
-
puts "chlog: Add '#{category}' category"
|
279
|
-
to_wr = lns[0...(log_loc)].join + "### #{category}:\n\n" + '- ' + log + "\n"
|
280
|
-
File.write(@changelog, to_wr + lns[log_loc-1..].join)
|
281
|
-
|
282
|
-
else
|
283
|
-
################################################################################
|
284
|
-
# The main category exists
|
285
|
-
################################################################################
|
286
|
-
|
287
|
-
|
288
|
-
########################################
|
289
|
-
if @sub_category
|
290
|
-
|
291
|
-
# from first list item to next main cat
|
292
|
-
# search if sub cat already exists
|
293
|
-
sub_cat_i = lns[main_cat_i+2...f_l_o_c_i].each_with_index do
|
294
|
-
break _2 if _1 =~ /^\*\*#{@sub_category}\*\*/
|
295
|
-
end
|
296
|
-
|
297
|
-
if !sub_cat_i.is_a?(Integer)
|
298
|
-
# the sub category not exists
|
299
|
-
|
300
|
-
sub_cat_i = 2 + find_main_category_last_list_item_index(lns,main_cat_i,f_l_o_c_i)
|
301
|
-
to_wr = lns[0...sub_cat_i].join + "**#{@sub_category}**\n\n" + ' - ' + log + "\n\n"
|
302
|
-
File.write(@changelog, to_wr + lns[sub_cat_i..].join)
|
303
|
-
else
|
304
|
-
# the sub category exists
|
305
|
-
|
306
|
-
sub_cat_i += main_cat_i+2
|
307
|
-
to_wr = lns[0..sub_cat_i+1].join + ' - ' + log + "\n"
|
308
|
-
File.write(@changelog, to_wr + lns[sub_cat_i+2..].join)
|
309
|
-
end
|
310
|
-
|
311
|
-
puts "chlog: Add log to #{category}/#{@sub_category}"
|
312
|
-
return
|
313
|
-
end # end of if @sub_category
|
314
|
-
########################################
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
# situation: no sub category
|
319
|
-
|
320
|
-
to_wr = lns[0..main_cat_i+1].join + '- ' + log + "\n"
|
321
|
-
|
322
|
-
# main category list should be separated with
|
323
|
-
# sub category
|
324
|
-
if lns[main_cat_i+2] =~ /\*\*.*/
|
325
|
-
to_wr += "\n"
|
326
|
-
end
|
327
|
-
File.write(@changelog, to_wr + lns[main_cat_i+2..].join)
|
328
|
-
end
|
329
|
-
|
330
|
-
puts "chlog: Add log to #{category}"
|
331
|
-
end
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
# main category order: 1
|
336
|
-
def add_to_new_features
|
337
|
-
meta_add_to_a_category(@log, 1, "New features")
|
338
|
-
end
|
339
|
-
|
340
|
-
# main category order: 2
|
341
|
-
def add_to_enhancements
|
342
|
-
meta_add_to_a_category(@log, 2, "Enhancements")
|
343
|
-
end
|
344
|
-
|
345
|
-
# main_category_order: 3
|
346
|
-
def add_to_bug_fixes
|
347
|
-
meta_add_to_a_category(@log, 3, "Bug fixes")
|
348
|
-
end
|
349
|
-
|
350
|
-
# main_category_order: 4
|
351
|
-
def add_to_security
|
352
|
-
meta_add_to_a_category(@log, 4, "Security")
|
353
|
-
end
|
354
|
-
|
355
|
-
# main_category_order: 5
|
356
|
-
def add_to_compatibility
|
357
|
-
meta_add_to_a_category(@log, 5, "Compatibility")
|
358
|
-
end
|
359
|
-
|
360
|
-
# The last of order table
|
361
|
-
# main_category_order: 6
|
362
|
-
def add_to_deprecations
|
363
|
-
meta_add_to_a_category(@log, 6, "Deprecations")
|
364
|
-
end
|
365
|
-
|
366
|
-
# Release a version
|
367
|
-
def release_new_version(ver)
|
368
|
-
if ver.nil?
|
369
|
-
puts "chlog: No version supplied!"; puts; abort "Use chlog -r <version>!"
|
370
|
-
end
|
371
|
-
|
372
|
-
if !ver.downcase.start_with?('v') and ver[0].match? /\d/
|
373
|
-
ver = 'v' + ver
|
374
|
-
end
|
375
|
-
|
376
|
-
content = get_changelog
|
377
|
-
lns = content.lines
|
378
|
-
|
379
|
-
match_unreleased_fail!(lns[2])
|
380
|
-
|
381
|
-
header = <<~EOF
|
382
|
-
# Changelog
|
383
|
-
|
384
|
-
## [Unreleased](#) (#{TODAY})
|
385
|
-
|
386
|
-
<br>
|
387
|
-
|
388
|
-
EOF
|
389
|
-
|
390
|
-
new_version = "## [#{ver}](#) (#{TODAY})\n"
|
391
|
-
new_cont = header + new_version + lns[3..].join('')
|
392
|
-
File.write(@changelog, new_cont)
|
393
|
-
puts "chlog: Release #{ver} in Changelog!" or return true
|
394
|
-
end
|
395
|
-
|
396
|
-
|
397
|
-
# Help user interactively
|
398
|
-
def ask_user_category
|
399
|
-
category = @highline.choose do |menu|
|
400
|
-
menu.index_color = :rgb_77bbff
|
401
|
-
menu.prompt = "What main category?"
|
402
|
-
# return number!!! not string!!!
|
403
|
-
menu.choices(*LIST__Main_Category)
|
404
|
-
end
|
405
|
-
|
406
|
-
option = LIST__Main_Category.index(category)
|
407
|
-
|
408
|
-
if @sub_category.nil?
|
409
|
-
sub = @highline.ask "What sub category? [NIL/sub]"
|
410
|
-
case sub
|
411
|
-
when '' then @sub_category = nil
|
412
|
-
else @sub_category = sub end
|
413
|
-
end
|
414
|
-
return option
|
415
|
-
end
|
416
|
-
|
417
|
-
end
|
13
|
+
CHLOG_GEM_VERSION = "1.7.2"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chlog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- ccmywish
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -34,9 +34,8 @@ executables:
|
|
34
34
|
extensions: []
|
35
35
|
extra_rdoc_files: []
|
36
36
|
files:
|
37
|
-
-
|
37
|
+
- bin/chlog
|
38
38
|
- lib/chlog.rb
|
39
|
-
- lib/chlog/version.rb
|
40
39
|
homepage: https://github.com/ccmywish/chlog
|
41
40
|
licenses:
|
42
41
|
- MIT
|
@@ -58,8 +57,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
58
57
|
- !ruby/object:Gem::Version
|
59
58
|
version: '0'
|
60
59
|
requirements: []
|
61
|
-
rubygems_version: 3.
|
60
|
+
rubygems_version: 3.3.7
|
62
61
|
signing_key:
|
63
62
|
specification_version: 4
|
64
|
-
summary:
|
63
|
+
summary: 'chlog: help maintain Changelog'
|
65
64
|
test_files: []
|
data/exe/chlog
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# ------------------------------------------------------
|
3
|
-
# File : chlog.rb
|
4
|
-
# Authors : Aoran Zeng <ccmywish@qq.com>
|
5
|
-
# Created on : <2022-03-18>
|
6
|
-
# Last modified : <2023-05-09>
|
7
|
-
#
|
8
|
-
# chlog:
|
9
|
-
#
|
10
|
-
# Maintain your project's Changelog on the cli.
|
11
|
-
# ------------------------------------------------------
|
12
|
-
|
13
|
-
require 'chlog'
|
14
|
-
|
15
|
-
module Chlog::CLI
|
16
|
-
|
17
|
-
def self.run
|
18
|
-
|
19
|
-
args = $*.dup
|
20
|
-
|
21
|
-
if args.size == 0
|
22
|
-
help or exit
|
23
|
-
end
|
24
|
-
|
25
|
-
action = args[0]
|
26
|
-
|
27
|
-
case action
|
28
|
-
when "-h", "--help" then help or exit end
|
29
|
-
|
30
|
-
logger = Chlog::Logger.new
|
31
|
-
|
32
|
-
case action
|
33
|
-
when "-g" then exit logger.generate_changelog
|
34
|
-
when "-r" then exit logger.release_new_version(args[1])
|
35
|
-
end
|
36
|
-
|
37
|
-
if args[1] =~ /^--(.*)/
|
38
|
-
logger.sub_category = $1
|
39
|
-
logger.log = args[2..].join(' ')
|
40
|
-
else
|
41
|
-
logger.sub_category = nil
|
42
|
-
logger.log = args[1..].join(' ')
|
43
|
-
end
|
44
|
-
|
45
|
-
case action
|
46
|
-
when /-[nebscd]/
|
47
|
-
logger.take_action(action)
|
48
|
-
else
|
49
|
-
# action =~ /^-[^-]*$/
|
50
|
-
abort "chlog: Unknown option!"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
def self.help
|
56
|
-
puts <<~EOH
|
57
|
-
chlog (v#{Chlog::GEM_VERSION}): Help maintain the Changelog of your projects
|
58
|
-
|
59
|
-
Usage:
|
60
|
-
|
61
|
-
chlog -g Generate CHANGELOG.md in git root
|
62
|
-
chlog -r <3.14> Release version to v3.14
|
63
|
-
|
64
|
-
Add log:
|
65
|
-
|
66
|
-
chlog <-main category> [--sub category] <log>
|
67
|
-
|
68
|
-
Main Category:
|
69
|
-
|
70
|
-
-n Add log to New features
|
71
|
-
-e Add log to Enhancements
|
72
|
-
-b Add log to Bug fixes
|
73
|
-
-s Add log to Security
|
74
|
-
-c Add log to Compatibility
|
75
|
-
-d Add log to Deprecations
|
76
|
-
|
77
|
-
EOH
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
Chlog::CLI.run
|
data/lib/chlog/version.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
# ---------------------------------------------------------------
|
2
|
-
# File : version.rb
|
3
|
-
# Authors : Aoran Zeng <ccmywish@qq.com>
|
4
|
-
# Created on : <2023-05-09>
|
5
|
-
# Last modified : <2023-05-09>
|
6
|
-
#
|
7
|
-
# version:
|
8
|
-
#
|
9
|
-
# Lib version
|
10
|
-
# ---------------------------------------------------------------
|
11
|
-
|
12
|
-
module Chlog
|
13
|
-
|
14
|
-
GEM_VERSION = "0.9.0"
|
15
|
-
|
16
|
-
end
|