mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
cmd: add v bump (#12798)
This commit is contained in:
parent
2ced182816
commit
f0969698e2
188
cmd/tools/vbump.v
Normal file
188
cmd/tools/vbump.v
Normal file
@ -0,0 +1,188 @@
|
||||
// Copyright (c) 2019-2021 Subhomoy Haldar. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
|
||||
module main
|
||||
|
||||
import flag
|
||||
import os
|
||||
import regex
|
||||
import semver
|
||||
|
||||
const (
|
||||
tool_name = os.file_name(os.executable())
|
||||
tool_version = '0.0.1'
|
||||
tool_description = '\n Bump the semantic version of the v.mod and/or specified files.
|
||||
|
||||
The first instance of a version number is replaced with the new version.
|
||||
Additionally, the line affected must contain the word "version" in any
|
||||
form of capitalization. For instance, the following lines will be
|
||||
recognized by the heuristic:
|
||||
|
||||
tool_version = \'1.2.1\'
|
||||
version: \'0.2.42\'
|
||||
VERSION = "1.23.8"
|
||||
|
||||
Examples:
|
||||
Bump the patch version in v.mod if it exists
|
||||
v bump --patch
|
||||
Bump the major version in v.mod and vls.v
|
||||
v bump --major v.mod vls.v
|
||||
Upgrade the minor version in sample.v only
|
||||
v bump --minor sample.v
|
||||
'
|
||||
semver_query = r'((0)|([1-9]\d*)\.){2}(0)|([1-9]\d*)(\-[\w\d\.\-_]+)?(\+[\w\d\.\-_]+)?'
|
||||
)
|
||||
|
||||
struct Options {
|
||||
show_help bool
|
||||
major bool
|
||||
minor bool
|
||||
patch bool
|
||||
}
|
||||
|
||||
type ReplacementFunction = fn (re regex.RE, input string, start int, end int) string
|
||||
|
||||
fn replace_with_increased_patch_version(re regex.RE, input string, start int, end int) string {
|
||||
version := semver.from(input[start..end]) or { return input }
|
||||
return version.increment(.patch).str()
|
||||
}
|
||||
|
||||
fn replace_with_increased_minor_version(re regex.RE, input string, start int, end int) string {
|
||||
version := semver.from(input[start..end]) or { return input }
|
||||
return version.increment(.minor).str()
|
||||
}
|
||||
|
||||
fn replace_with_increased_major_version(re regex.RE, input string, start int, end int) string {
|
||||
version := semver.from(input[start..end]) or { return input }
|
||||
return version.increment(.major).str()
|
||||
}
|
||||
|
||||
fn get_replacement_function(options Options) ReplacementFunction {
|
||||
if options.patch {
|
||||
return replace_with_increased_patch_version
|
||||
} else if options.minor {
|
||||
return replace_with_increased_minor_version
|
||||
} else if options.major {
|
||||
return replace_with_increased_major_version
|
||||
}
|
||||
return replace_with_increased_patch_version
|
||||
}
|
||||
|
||||
fn process_file(input_file string, options Options) {
|
||||
lines := os.read_lines(input_file) or { panic('Failed to read file: $input_file') }
|
||||
|
||||
mut re := regex.regex_opt(semver_query) or { panic('Could not create a RegEx parser.') }
|
||||
|
||||
repl_fn := get_replacement_function(options)
|
||||
|
||||
mut new_lines := []string{cap: lines.len}
|
||||
mut replacement_complete := false
|
||||
|
||||
for line in lines {
|
||||
// Copy over the remaining lines normally if the replacement is complete
|
||||
if replacement_complete {
|
||||
new_lines << line
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if replacement is necessary
|
||||
updated_line := if line.to_lower().contains('version') {
|
||||
replacement_complete = true
|
||||
re.replace_by_fn(line, repl_fn)
|
||||
} else {
|
||||
line
|
||||
}
|
||||
new_lines << updated_line
|
||||
}
|
||||
|
||||
// Add a trailing newline
|
||||
new_lines << ''
|
||||
|
||||
backup_file := input_file + '.cache'
|
||||
|
||||
// Remove the backup file if it exists.
|
||||
os.rm(backup_file) or {}
|
||||
|
||||
// Rename the original to the backup.
|
||||
os.mv(input_file, backup_file) or { panic('Failed to copy file: $input_file') }
|
||||
|
||||
// Process the old file and write it back to the original.
|
||||
os.write_file(input_file, new_lines.join_lines()) or {
|
||||
panic('Failed to write file: $input_file')
|
||||
}
|
||||
|
||||
// Remove the backup file.
|
||||
os.rm(backup_file) or {}
|
||||
|
||||
if replacement_complete {
|
||||
println('Bumped version in $input_file')
|
||||
} else {
|
||||
println('No changes made in $input_file')
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if os.args.len < 2 {
|
||||
println('Usage: $tool_name [options] [file1 file2 ...]
|
||||
$tool_description
|
||||
Try $tool_name -h for more help...')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
|
||||
fp.application(tool_name)
|
||||
fp.version(tool_version)
|
||||
fp.description(tool_description)
|
||||
fp.arguments_description('[file1 file2 ...]')
|
||||
fp.skip_executable()
|
||||
|
||||
options := Options{
|
||||
show_help: fp.bool('help', `h`, false, 'Show this help text.')
|
||||
patch: fp.bool('patch', `p`, false, 'Bump the patch version.')
|
||||
minor: fp.bool('minor', `n`, false, 'Bump the minor version.')
|
||||
major: fp.bool('major', `m`, false, 'Bump the major version.')
|
||||
}
|
||||
|
||||
if options.show_help {
|
||||
println(fp.usage())
|
||||
exit(0)
|
||||
}
|
||||
|
||||
validate_options(options) or { panic(err) }
|
||||
|
||||
files := os.args[3..]
|
||||
|
||||
if files.len == 0 {
|
||||
if !os.exists('v.mod') {
|
||||
println('v.mod does not exist. You can create one using "v init".')
|
||||
exit(1)
|
||||
}
|
||||
process_file('v.mod', options)
|
||||
}
|
||||
|
||||
for input_file in files {
|
||||
if !os.exists(input_file) {
|
||||
println('File not found: $input_file')
|
||||
exit(1)
|
||||
}
|
||||
process_file(input_file, options)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_options(options Options) ? {
|
||||
if options.patch && options.major {
|
||||
return error('Cannot specify both --patch and --major.')
|
||||
}
|
||||
|
||||
if options.patch && options.minor {
|
||||
return error('Cannot specify both --patch and --minor.')
|
||||
}
|
||||
|
||||
if options.major && options.minor {
|
||||
return error('Cannot specify both --major and --minor.')
|
||||
}
|
||||
|
||||
if !(options.patch || options.major || options.minor) {
|
||||
return error('Must specify one of --patch, --major, or --minor.')
|
||||
}
|
||||
}
|
95
cmd/tools/vbump_test.v
Normal file
95
cmd/tools/vbump_test.v
Normal file
@ -0,0 +1,95 @@
|
||||
import os
|
||||
|
||||
struct BumpTestCase {
|
||||
file_name string
|
||||
contents string
|
||||
line int
|
||||
expected_patch string
|
||||
expected_minor string
|
||||
expected_major string
|
||||
}
|
||||
|
||||
const test_cases = [
|
||||
BumpTestCase{
|
||||
file_name: 'v.mod'
|
||||
contents: "Module {
|
||||
name: 'Sample'
|
||||
description: 'Sample project'
|
||||
version: '1.2.6'
|
||||
license: 'MIT'
|
||||
dependencies: []
|
||||
}
|
||||
|
||||
"
|
||||
line: 3
|
||||
expected_patch: " version: '1.2.7'"
|
||||
expected_minor: " version: '1.3.0'"
|
||||
expected_major: " version: '2.0.0'"
|
||||
},
|
||||
BumpTestCase{
|
||||
file_name: 'random_versions.vv'
|
||||
contents: "
|
||||
1.1.2
|
||||
1.2.5
|
||||
3.21.73
|
||||
version = '1.5.1'
|
||||
|
||||
"
|
||||
line: 4
|
||||
expected_patch: "version = '1.5.2'"
|
||||
expected_minor: "version = '1.6.0'"
|
||||
expected_major: "version = '2.0.0'"
|
||||
},
|
||||
BumpTestCase{
|
||||
file_name: 'sample_tool.v'
|
||||
contents: "// Module comment and copyright information
|
||||
import os
|
||||
import flag
|
||||
|
||||
const (
|
||||
tool_name = os.file_name(os.executable())
|
||||
tool_version = '0.1.33'
|
||||
)
|
||||
fn main() {
|
||||
// stuff
|
||||
}
|
||||
"
|
||||
line: 6
|
||||
expected_patch: " tool_version = '0.1.34'"
|
||||
expected_minor: " tool_version = '0.2.0'"
|
||||
expected_major: " tool_version = '1.0.0'"
|
||||
},
|
||||
]
|
||||
|
||||
fn run_individual_test(case BumpTestCase) ? {
|
||||
vexe := @VEXE
|
||||
|
||||
temp_dir := os.temp_dir()
|
||||
test_file := os.join_path_single(temp_dir, case.file_name)
|
||||
|
||||
os.rm(test_file) or {}
|
||||
os.write_file(test_file, case.contents) ?
|
||||
|
||||
{
|
||||
os.execute_or_exit('$vexe bump --patch $test_file')
|
||||
patch_lines := os.read_lines(test_file) ?
|
||||
assert patch_lines[case.line] == case.expected_patch
|
||||
}
|
||||
{
|
||||
os.execute_or_exit('$vexe bump --minor $test_file')
|
||||
minor_lines := os.read_lines(test_file) ?
|
||||
assert minor_lines[case.line] == case.expected_minor
|
||||
}
|
||||
{
|
||||
os.execute_or_exit('$vexe bump --major $test_file')
|
||||
major_lines := os.read_lines(test_file) ?
|
||||
assert major_lines[case.line] == case.expected_major
|
||||
}
|
||||
os.rm(test_file) ?
|
||||
}
|
||||
|
||||
fn test_all_bump_cases() {
|
||||
for case in test_cases {
|
||||
run_individual_test(case) or { panic(err) }
|
||||
}
|
||||
}
|
@ -59,6 +59,7 @@ const (
|
||||
'build-examples',
|
||||
'build-tools',
|
||||
'build-vbinaries',
|
||||
'bump',
|
||||
'check-md',
|
||||
'complete',
|
||||
'compress',
|
||||
|
28
cmd/v/help/bump.txt
Normal file
28
cmd/v/help/bump.txt
Normal file
@ -0,0 +1,28 @@
|
||||
Usage: v bump [options] [file1 file2 ...]
|
||||
|
||||
Description:
|
||||
Bump the semantic version of the v.mod and/or specified files.
|
||||
|
||||
The first instance of a version number is replaced with the new version.
|
||||
Additionally, the line affected must contain the word "version" in any
|
||||
form of capitalization. For instance, the following lines will be
|
||||
recognized by the heuristic:
|
||||
|
||||
tool_version = '1.2.1'
|
||||
version: '0.2.42'
|
||||
VERSION = "1.23.8"
|
||||
|
||||
Examples:
|
||||
Bump the patch version in v.mod if it exists
|
||||
v bump --patch
|
||||
Bump the major version in v.mod and vls.v
|
||||
v bump --major v.mod vls.v
|
||||
Upgrade the minor version in sample.v only
|
||||
v bump --minor sample.v
|
||||
|
||||
|
||||
Options:
|
||||
-h, --help Show this help text.
|
||||
-m, --major Bump the major version.
|
||||
-n, --minor Bump the minor version.
|
||||
-p, --patch Bump the patch version.
|
Loading…
Reference in New Issue
Block a user