Internal functionality implementation.
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
.*sw?
|
||||||
|
update_version
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
import std/[dirs, json, paths, sequtils, strutils, syncio, tables]
|
||||||
|
import std/nre except toSeq
|
||||||
|
import docopt, zero_functional
|
||||||
|
|
||||||
|
const USAGE = """Usage:
|
||||||
|
update_nim_package_version bump <part> [<src-file> ...] [options]
|
||||||
|
update_nim_package_version set <new-version> [<src-file> ...] [options]
|
||||||
|
update_nim_package_version get [<src-file> ...] [options]
|
||||||
|
update_nim_package_version interactive [<src-file> ...] [options]
|
||||||
|
update_nim_package_version test
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
-l, --lang <language> Choose the language/ecosystem to consider. Valid
|
||||||
|
values are: 'nim' or 'node'. If not provided, this
|
||||||
|
is auto-detected by the presence of either a *.nimble
|
||||||
|
or package.json file.
|
||||||
|
Details:
|
||||||
|
|
||||||
|
bump
|
||||||
|
|
||||||
|
Assuming the project with a semver-like versioning scheme, update one of
|
||||||
|
the version parts by one. <version-part> must be one of 'major', 'minor',
|
||||||
|
'patch', or 'last'. Semver looks like 'major.minor.patch' Last is a special
|
||||||
|
case to support looser version strings that end in '.x' like 'alpha.1' or
|
||||||
|
'1.5' and matches '.*(\.\d+)$'
|
||||||
|
|
||||||
|
The matched portion is interpreted as an integer and incremented by one.
|
||||||
|
|
||||||
|
set
|
||||||
|
|
||||||
|
Update the version string entirely with a new string. In this case, the
|
||||||
|
versioning scheme of the project doesn't matter
|
||||||
|
|
||||||
|
get
|
||||||
|
|
||||||
|
Just return the existing version.
|
||||||
|
|
||||||
|
interactive
|
||||||
|
|
||||||
|
Update the version interactively.
|
||||||
|
|
||||||
|
<src-file>
|
||||||
|
|
||||||
|
Sometimes it is useful to encode the version in source files via constants.
|
||||||
|
Passing arguments as <src-files> instructs update_version to modify these
|
||||||
|
source files directly. It does so by matching constant assignments via
|
||||||
|
regex and replacing the previous version string with the new version
|
||||||
|
string. When inspecting the provided files, update_version matches the
|
||||||
|
following patterns for constanst assignments to consider:
|
||||||
|
|
||||||
|
const <package-name>_VERSION* = "<prev-version-string>"
|
||||||
|
export const <package-name>_VERSION = "<prev-version-string>"
|
||||||
|
|
||||||
|
Specifically it uses the following regexes:
|
||||||
|
|
||||||
|
TODO
|
||||||
|
"""
|
||||||
|
|
||||||
|
const UV_VERSION = "1.0.0"
|
||||||
|
|
||||||
|
type
|
||||||
|
LangType = enum lNim, lNode
|
||||||
|
SemVerParts = enum major, minor, patch, prerelease, buildmetadata
|
||||||
|
|
||||||
|
type PackageVersion = object
|
||||||
|
packageFile: Path
|
||||||
|
version: string
|
||||||
|
name: string
|
||||||
|
|
||||||
|
case lang: LangType
|
||||||
|
of lNim:
|
||||||
|
discard
|
||||||
|
of lNode:
|
||||||
|
nodePackage: JsonNode
|
||||||
|
|
||||||
|
|
||||||
|
# Taken from
|
||||||
|
# https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
|
||||||
|
#
|
||||||
|
# See also:
|
||||||
|
# https://regex101.com/r/Ly7O1x/3/
|
||||||
|
#
|
||||||
|
let SemVerPattern =
|
||||||
|
re"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"
|
||||||
|
|
||||||
|
proc parseNimblePackage(dir: Path): PackageVersion =
|
||||||
|
for fe in walkDir(dir):
|
||||||
|
if fe.kind == pcFile and
|
||||||
|
fe.path.splitFile.ext == ".nimble":
|
||||||
|
|
||||||
|
let content = readFile($fe.path)
|
||||||
|
for v in content.splitLines:
|
||||||
|
if v.startsWith("version"):
|
||||||
|
return PackageVersion(
|
||||||
|
lang: lNim,
|
||||||
|
packageFile: fe.path,
|
||||||
|
version: v.split("=")[^1].strip(chars = {' ', '"'}),
|
||||||
|
name: $fe.path.splitFile.name)
|
||||||
|
|
||||||
|
|
||||||
|
raise newException(IOError, "No valid .nimble file found in $#" % [$dir])
|
||||||
|
|
||||||
|
|
||||||
|
proc parseNodePackage(dir: Path): PackageVersion =
|
||||||
|
result.packageFile = dir / Path("package.json")
|
||||||
|
result.lang = lNode
|
||||||
|
result.nodePackage = parseFile($result.packageFile)
|
||||||
|
|
||||||
|
if not result.nodePackage.hasKey("name") or
|
||||||
|
not result.nodePackage.hasKey("version") or
|
||||||
|
result.nodePackage["name"].kind != JString or
|
||||||
|
result.nodePackage["version"].kind != JString:
|
||||||
|
raise newException(ValueError,
|
||||||
|
"package.json does not have valid 'name' and 'version' fields.")
|
||||||
|
|
||||||
|
result.name = result.nodePackage{"name"}.getStr
|
||||||
|
result.version = result.nodePackage{"version"}.getStr
|
||||||
|
|
||||||
|
|
||||||
|
proc incrementLastVersionPart(version: string): string =
|
||||||
|
let versionParts = toSeq(findIter(version, re"([^\d.]+)?\.?(\d+)"))
|
||||||
|
let lastVersionPartMatch = versionParts[^1]
|
||||||
|
let lastVersionPartInt = parseInt(lastVersionPartMatch.captures[1])
|
||||||
|
return
|
||||||
|
version[0..<lastVersionPartMatch.captureBounds[1].a] &
|
||||||
|
$(lastVersionPartInt + 1) &
|
||||||
|
version[lastVersionPartMatch.captureBounds[1].b+1 .. ^1]
|
||||||
|
|
||||||
|
|
||||||
|
let VERSION_DEFAULTS: TableRef[SemVerParts, string] = newTable([
|
||||||
|
(major, "0"),
|
||||||
|
(minor, "0"),
|
||||||
|
(patch, "0"),
|
||||||
|
(prerelease, ""),
|
||||||
|
(buildmetadata, "")])
|
||||||
|
|
||||||
|
|
||||||
|
proc incrementSemverPart(
|
||||||
|
version: string,
|
||||||
|
part: SemVerParts,
|
||||||
|
defaults = VERSION_DEFAULTS): string =
|
||||||
|
|
||||||
|
result = ""
|
||||||
|
|
||||||
|
let matchOpt = match(version, SemVerPattern)
|
||||||
|
|
||||||
|
if matchOpt.isNone:
|
||||||
|
raise newException(ValueError,
|
||||||
|
"Version [$#] is not a valid Semantic Version number" % version)
|
||||||
|
|
||||||
|
let m = matchOpt.get
|
||||||
|
|
||||||
|
let versionParts = newTable[SemVerParts, string]()
|
||||||
|
for p in SemVerParts.items:
|
||||||
|
if p == part:
|
||||||
|
if m.captures.contains($p):
|
||||||
|
versionParts[p] = incrementLastVersionPart(m.captures[$p])
|
||||||
|
elif p < prerelease or defaults[p].len > 0:
|
||||||
|
versionParts[p] = defaults[p]
|
||||||
|
# if this part specifically has been requested to be incremented but
|
||||||
|
# the default is empty, we are still going to give it *something*
|
||||||
|
elif p == prerelease: versionParts[p] = "prerelease.0"
|
||||||
|
elif p == buildmetadata: versionParts[p] = "build.0"
|
||||||
|
elif not m.captures.contains($p) or p > part: versionParts[p] = defaults[p]
|
||||||
|
else: versionParts[p] = m.captures[$p]
|
||||||
|
|
||||||
|
result = "$#.$#.$#" % [
|
||||||
|
versionParts[major], versionParts[minor], versionParts[patch] ]
|
||||||
|
|
||||||
|
if versionParts[prerelease].len > 0:
|
||||||
|
result &= "-" & versionParts[prerelease]
|
||||||
|
|
||||||
|
if versionParts[buildmetadata].len > 0:
|
||||||
|
result &= "+" & versionParts[buildmetadata]
|
||||||
|
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
let args = docopt(USAGE, version = UV_VERSION)
|
||||||
|
|
||||||
|
if args["bump"]:
|
||||||
|
discard
|
||||||
|
elif args["set"]:
|
||||||
|
discard
|
||||||
|
elif args["get"]:
|
||||||
|
discard
|
||||||
|
elif args["interactive"]:
|
||||||
|
discard
|
||||||
|
elif args["test"]:
|
||||||
|
|
||||||
|
# incrementLastVersionPart
|
||||||
|
assert incrementLastVersionPart("1.0.0") == "1.0.1"
|
||||||
|
assert incrementLastVersionPart("1.0.0-alpha.1") == "1.0.0-alpha.2"
|
||||||
|
assert incrementLastVersionPart("cicd_alphe.1-prerelease") == "cicd_alphe.2-prerelease"
|
||||||
|
assert incrementLastVersionPart("2024.04.1") == "2024.04.2"
|
||||||
|
|
||||||
|
# incrementSemverPart
|
||||||
|
assert incrementSemverPart("1.0.0", major) == "2.0.0"
|
||||||
|
assert incrementSemverPart("1.0.0", minor) == "1.1.0"
|
||||||
|
assert incrementSemverPart("1.0.0", patch) == "1.0.1"
|
||||||
|
assert incrementSemverPart("1.5.10", patch) == "1.5.11"
|
||||||
|
assert incrementSemverPart("1.5.10", minor) == "1.6.0"
|
||||||
|
assert incrementSemverPart("1.5.10-alpha.1", prerelease) == "1.5.10-alpha.2"
|
||||||
|
assert incrementSemverPart("1.5.10-alpha.1+build.10", prerelease) == "1.5.10-alpha.2"
|
||||||
|
assert incrementSemverPart("1.5.10-alpha.1+build.10", buildmetadata) == "1.5.10-alpha.1+build.11"
|
||||||
|
assert incrementSemverPart("1.5.10", buildmetadata) == "1.5.10+build.0"
|
||||||
|
|
||||||
|
echo "All tests passed."
|
||||||
@@ -10,4 +10,4 @@ bin = @["update_version"]
|
|||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
requires @["nim >= 1.0.4", "docopt >= 0.7.1", "nimble"]
|
requires @["nim >= 1.0.4", "docopt >= 0.7.1", "zero_functional"]
|
||||||
|
|||||||
Reference in New Issue
Block a user