tools: instantly generate the entire changelog in changelog_helper.v

This commit is contained in:
Alexander Medvednikov 2024-03-20 06:51:55 +03:00
parent 44c78ed761
commit cf7d6cd648
2 changed files with 185 additions and 61 deletions

View file

@ -1,3 +1,5 @@
## V 0.4.5 TODO
## V 0.4.4
*9 January 2024*

View file

@ -15,30 +15,47 @@ enum Category {
db
native
cgen
js_backend
comptime
tools
compiler_internals
examples
vfmt
os_support
}
/*
#### Improvements in the language
const category_titles = '#### Improvements in the language
#### Breaking changes
#### Checker improvements/fixes
#### Parser improvements
#### Compiler internals
#### Standard library
#### Web
#### ORM
#### Database drivers
#### Native backend
#### C backend
#### JavaScript backend
#### vfmt
#### Tools
#### Operating System support
#### Examples
*/
'
struct Line {
category Category
@ -48,22 +65,63 @@ struct Line {
const log_txt = 'log.txt'
struct App {
version string // e.g. "0.4.5"
total_lines int
mut:
result string // resulting CHANGELOG.md
counter int
}
const is_interactive = false
// Instantly updates CHANGELOG.md without confirming each line
fn no_interactive(version string) {
}
fn main() {
if !os.exists(log_txt) {
os.execute(git_log_cmd + ' > ' + log_txt)
println('log.txt generated, remove unnecessary commits from it and run the tool again')
mut version := ''
if os.args.len == 2 && os.args[1].starts_with('0.') {
version = os.args[1]
// no_interactive(version)
// return
} else {
println('Usage: v run tools/changelog_helper.v 0.4.5')
return
}
lines := os.read_lines(log_txt)!
changelog_txt := os.read_file('CHANGELOG.md')!.to_lower()
if !os.exists(log_txt) {
os.execute(git_log_cmd + ' > ' + log_txt)
println('log.txt generated')
// println('log.txt generated, remove unnecessary commits from it and run the tool again')
// return
}
mut lines := os.read_lines(log_txt)!
// Trim everything before current version, commit "(tag: 0.4.4) V 0.4.4"
mut prev_version := (version.replace('.', '').int() - 1).str()
prev_version = '0.${prev_version[0].ascii_str()}.${prev_version[1].ascii_str()}'
println('prev version=${prev_version}')
for i, line in lines {
if line == ('V ${prev_version}') {
lines = lines[..i].clone()
break
}
}
os.write_file(log_txt, lines.join('\n'))!
if true {
// return
}
mut app := &App{
total_lines: lines.len
}
// Write categories at the top first
app.result = os.read_file('CHANGELOG.md')!.replace_once('V ${version} TODO', 'V ${version}\n' +
category_titles)
os.write_file('CHANGELOG.md', app.result)!
changelog_txt := os.read_file('CHANGELOG.md')!.to_lower()
if true {
// println(changelog_txt)
// return
}
// mut counter := 0 // to display how many commits are left
for line in lines {
s := line.trim_space()
@ -81,7 +139,11 @@ fn main() {
continue
}
app.process_line(line)!
app.process_line(line.trim_space())!
}
println('writing changelog.md')
if !is_interactive {
os.write_file('CHANGELOG.md', app.result)!
}
println('done.')
}
@ -96,11 +158,19 @@ fn (mut app App) process_line(text string) ! {
}
prefix := text[..semicolon_pos]
// Get category based on keywords in the commit message/prefix
mut category := Category.improvements
mut category := Category.examples
if text.contains('checker:') {
category = .checker
} else if text.contains('cgen:') {
} else if is_examples(text) {
// Always skip examples and typos fixes
category = .examples
return
} else if is_os_support(text) {
category = .os_support
} else if is_cgen(text) {
category = .cgen
} else if is_js_backend(text) {
category = .js_backend
} else if is_db(text) {
category = .db
} else if is_stdlib(text) {
@ -121,9 +191,6 @@ fn (mut app App) process_line(text string) ! {
category = .native
} else if is_vfmt(text) {
category = .vfmt
} else if is_examples(text) {
// TODO maybe always skip these as well?
category = .examples
} else if text.contains('docs:') || text.contains('doc:') {
// Always skip docs
delete_processed_line_from_log(text)!
@ -133,6 +200,7 @@ fn (mut app App) process_line(text string) ! {
else {
return
}
println('process_line: cat=${category} "${text}"')
// Trim everything to the left of `:` for some commits (e.g. `checker: `)
mut s := text
@ -140,10 +208,13 @@ fn (mut app App) process_line(text string) ! {
// if true {
// exit(0)
//}
if semicolon_pos < 15 && prefix in ['checker', 'cgen'] {
if (semicolon_pos < 15
&& prefix in ['checker', 'cgen', 'parser', 'v.parser', 'ast', 'jsgen', 'v.gen.js', 'fmt', 'vfmt'])
|| (semicolon_pos < 30 && prefix.contains(', ')) {
s = '- ' + text[semicolon_pos + 2..].capitalize()
}
if is_interactive {
// Get input from the user
print('\033[H\033[J')
println('${app.counter} / ${app.total_lines}')
@ -155,7 +226,7 @@ fn (mut app App) process_line(text string) ! {
'' {
println('GOT ENTER')
line := Line{category, s}
save_line(line)!
save_line_interactive(line)!
}
'n', '0', 'no' {
// Ignore commit
@ -176,7 +247,7 @@ fn (mut app App) process_line(text string) ! {
} else {
unsafe {
line := Line{Category(custom_category - 1), s}
save_line(line)!
save_line_interactive(line)!
}
break
}
@ -185,21 +256,23 @@ fn (mut app App) process_line(text string) ! {
else {}
}
app.counter++
} else {
line := Line{category, s}
app.save_line(line)!
}
// Don't forget to remove the line we just processed from log.txt
delete_processed_line_from_log(text)!
}
fn save_line(line Line) ! {
println('save line ${line}')
mut txt := os.read_file('CHANGELOG.md')!
fn (mut app App) save_line(line Line) ! {
// println('save line ${line}')
app.result = line.write_at_category(app.result) or { return error('') }
}
// match line.category {
//.checker {
fn save_line_interactive(line Line) ! {
println('save line interactive ${line}')
mut txt := os.read_file('CHANGELOG.md')!
txt = line.write_at_category(txt) or { return error('') }
// println(txt.limit(1000))
//}
// else {}
//}
os.write_file('CHANGELOG.md', txt)!
}
@ -214,11 +287,13 @@ const category_map = {
.db: '#### Database drivers'
.native: '#### Native backend'
.cgen: '#### C backend'
.js_backend: '#### JavaScript backend'
.comptime: '#### Comptime'
.tools: '#### Tools'
.compiler_internals: '#### Compiler internals'
.examples: '#### Examples'
.vfmt: '#### vfmt'
.os_support: '#### Operating System'
}
fn (l Line) write_at_category(txt string) ?string {
@ -276,6 +351,7 @@ const db_strings = [
const improvements_strings = [
'all:',
'v:',
'coroutines:',
]
const parser_strings = [
@ -293,7 +369,7 @@ const stdlib_strings = [
'math:',
'math.big',
'crypto',
'sokol:',
'sokol',
'os:',
'rand:',
'math:',
@ -309,6 +385,10 @@ const stdlib_strings = [
'readline',
'cli:',
'eventbus:',
'encoding.',
'bitfield:',
'io:',
'log:',
]
fn is_stdlib(text string) bool {
@ -327,6 +407,25 @@ fn is_orm(text string) bool {
return is_xxx(text, orm_strings)
}
const cgen_strings = [
'cgen:',
'v.gen.c:',
]
fn is_cgen(text string) bool {
return is_xxx(text, cgen_strings)
}
const js_backend_strings = [
'js:',
'v.gen.js:',
'jsgen:',
]
fn is_js_backend(text string) bool {
return is_xxx(text, js_backend_strings)
}
const internal_strings = [
'scanner:',
'transformer:',
@ -348,6 +447,10 @@ const examples_strings = [
'tests',
'readme:',
'.md:',
'typos',
' typo',
'cleanup',
'clean up',
]
fn is_examples(text string) bool {
@ -362,6 +465,7 @@ const tools_strings = [
'gitignore',
'benchmark',
'v.help:',
'vtest',
]
fn is_tools(text string) bool {
@ -374,6 +478,7 @@ fn is_parser(text string) bool {
const web_strings = [
'vweb:',
'x.vweb:',
'websocket:',
'picoev:',
'mbedtls',
@ -404,6 +509,23 @@ fn is_vfmt(text string) bool {
return is_xxx(text, vfmt_strings)
}
const os_support_strings = [
'FreeBSD',
'freebsd',
'OpenBSD',
'openbsd',
'macOS',
'macos',
'Windows',
'windows',
'Linux',
'linux',
]
fn is_os_support(text string) bool {
return is_xxx(text, os_support_strings)
}
fn is_xxx(text string, words []string) bool {
for s in words {
if text.contains(s) {