mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
211 lines
5.6 KiB
V
211 lines
5.6 KiB
V
// Copyright (c) 2019-2023 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.1.0'
|
|
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"
|
|
|
|
If certain lines need to be skipped, use the --skip option. For instance,
|
|
the following command will skip lines containing "tool-version":
|
|
|
|
v bump --patch --skip "tool-version" [files...]
|
|
|
|
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
|
|
skip string
|
|
}
|
|
|
|
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 { return error('Failed to read file: ${input_file}') }
|
|
|
|
mut re := regex.regex_opt(semver_query) or { return error('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') && !(options.skip != ''
|
|
&& line.contains(options.skip)) {
|
|
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 { return error('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 {
|
|
return error('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 {
|
|
eprintln('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.')
|
|
skip: fp.string('skip', `s`, '', 'Skip lines matching this substring.').trim_space()
|
|
}
|
|
|
|
remaining := fp.finalize() or {
|
|
eprintln(fp.usage())
|
|
exit(1)
|
|
}
|
|
|
|
if options.show_help {
|
|
println(fp.usage())
|
|
exit(0)
|
|
}
|
|
|
|
validate_options(options) or {
|
|
eprintln(fp.usage())
|
|
exit(1)
|
|
}
|
|
|
|
files := remaining[1..]
|
|
|
|
if files.len == 0 {
|
|
if !os.exists('v.mod') {
|
|
eprintln('v.mod does not exist. You can create one using "v init".')
|
|
exit(1)
|
|
}
|
|
process_file('v.mod', options) or {
|
|
eprintln('Failed to process v.mod: ${err}')
|
|
exit(1)
|
|
}
|
|
}
|
|
|
|
for input_file in files {
|
|
if !os.exists(input_file) {
|
|
eprintln('File not found: ${input_file}')
|
|
exit(1)
|
|
}
|
|
process_file(input_file, options) or {
|
|
eprintln('Failed to process ${input_file}: ${err}')
|
|
exit(1)
|
|
}
|
|
}
|
|
}
|
|
|
|
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.')
|
|
}
|
|
}
|