import datetime
import re
from pathlib import Path
from invoke import task
SEMVER = r"\d+\.\d+\.\d+"
def _read_cargo_version():
cargo = Path("Cargo.toml").read_text()
match = re.search(r'^version = "([^"]+)"', cargo, re.MULTILINE)
if not match:
raise RuntimeError("Could not find version in Cargo.toml")
return match.group(1)
VERSION_FILES = [
("Cargo.toml", r'^(version = ")' + SEMVER + r'(")', r"\g<1>{new}\g<2>"),
(
"docs/sqc.1",
r'("sqc )' + SEMVER + r'(")',
r"\g<1>{new}\g<2>",
),
(
"docs/sqc.1",
r'(\.TH SQC 1 ")[^"]*(")',
r"\g<1>{date}\g<2>",
),
]
@task
def bump_version(c, new_version=None):
current = _read_cargo_version()
if not new_version:
print(f"Current version (Cargo.toml): {current}")
print("\nFiles that would be updated:")
for path, *_ in VERSION_FILES:
print(f" {path}")
print("\nRun: invoke bump-version --new-version X.Y.Z")
return
if not re.fullmatch(SEMVER, new_version):
raise SystemExit(f"--new-version must look like X.Y.Z, got '{new_version}'")
today = datetime.date.today().strftime("%B %Y")
changed = []
for path, pattern, tmpl in VERSION_FILES:
p = Path(path)
if not p.exists():
continue
text = p.read_text()
replacement = tmpl.format(new=new_version, date=today)
updated = re.sub(pattern, replacement, text, flags=re.MULTILINE)
if updated != text:
p.write_text(updated)
changed.append(path)
if changed:
print(f"Bumped -> {new_version} in:")
for f in sorted(set(changed)):
print(f" {f}")
print(
"\nNext: review `git diff`, commit, then "
f"`git tag v{new_version} && git push && git push origin v{new_version}`"
)
else:
print("No version strings matched — nothing changed.")
@task
def check(c):
c.run("pre-commit run --all-files", pty=True)
@task
def build(c, release=False):
cmd = "cargo build"
if release:
cmd += " --release"
c.run(cmd, pty=True)
@task
def test(c):
c.run("cargo test", pty=True)