129 lines
4.4 KiB
Python
Executable File
129 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Semantic version helper for MercadoDeVida.
|
|
|
|
Source of truth: project/VERSION.
|
|
Mirrors the version into package.json files that are part of project/.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
VERSION_FILE = ROOT / 'project' / 'VERSION'
|
|
PACKAGE_FILES = [
|
|
ROOT / 'project' / 'package.json',
|
|
ROOT / 'project' / 'apps' / 'admin' / 'package.json',
|
|
ROOT / 'project' / 'apps' / 'pos' / 'package.json',
|
|
ROOT / 'project' / 'frontend' / 'package.json',
|
|
ROOT / 'project' / 'storefront' / 'package.json',
|
|
]
|
|
PACKAGE_LOCK_FILES = [
|
|
ROOT / 'project' / 'package-lock.json',
|
|
ROOT / 'project' / 'apps' / 'admin' / 'package-lock.json',
|
|
ROOT / 'project' / 'apps' / 'pos' / 'package-lock.json',
|
|
ROOT / 'project' / 'frontend' / 'package-lock.json',
|
|
ROOT / 'project' / 'storefront' / 'package-lock.json',
|
|
]
|
|
SEMVER_RE = re.compile(r'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:[-+][0-9A-Za-z.-]+)?$')
|
|
|
|
|
|
def read_version() -> str:
|
|
version = VERSION_FILE.read_text(encoding='utf-8').strip()
|
|
if not SEMVER_RE.match(version):
|
|
raise SystemExit(f'[FAIL] Invalid semver in {VERSION_FILE}: {version}')
|
|
return version
|
|
|
|
|
|
def write_version(version: str) -> None:
|
|
if not SEMVER_RE.match(version):
|
|
raise SystemExit(f'[FAIL] Invalid semver: {version}')
|
|
VERSION_FILE.write_text(version + '\n', encoding='utf-8')
|
|
for path in PACKAGE_FILES:
|
|
if not path.exists():
|
|
continue
|
|
data = json.loads(path.read_text(encoding='utf-8'))
|
|
data['version'] = version
|
|
path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
|
|
for path in PACKAGE_LOCK_FILES:
|
|
if not path.exists():
|
|
continue
|
|
data = json.loads(path.read_text(encoding='utf-8'))
|
|
if 'version' in data:
|
|
data['version'] = version
|
|
root_package = data.get('packages', {}).get('')
|
|
if isinstance(root_package, dict) and 'version' in root_package:
|
|
root_package['version'] = version
|
|
path.write_text(json.dumps(data, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
|
|
|
|
|
|
def bump(version: str, part: str) -> str:
|
|
major, minor, patch = [int(p) for p in version.split('-', 1)[0].split('+', 1)[0].split('.')]
|
|
if part == 'major':
|
|
return f'{major + 1}.0.0'
|
|
if part == 'minor':
|
|
return f'{major}.{minor + 1}.0'
|
|
if part == 'patch':
|
|
return f'{major}.{minor}.{patch + 1}'
|
|
raise SystemExit(f'[FAIL] Unsupported bump part: {part}')
|
|
|
|
|
|
def run(cmd: list[str]) -> None:
|
|
subprocess.run(cmd, cwd=ROOT, check=True)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description='Manage project semantic version')
|
|
sub = parser.add_subparsers(dest='command', required=True)
|
|
|
|
sub.add_parser('show', help='print current version')
|
|
|
|
bump_parser = sub.add_parser('bump', help='bump semantic version')
|
|
bump_parser.add_argument('part', choices=['major', 'minor', 'patch'])
|
|
bump_parser.add_argument('--commit', action='store_true', help='create conventional release commit')
|
|
bump_parser.add_argument('--tag', action='store_true', help='create git tag vX.Y.Z (requires --commit or clean index)')
|
|
|
|
set_parser = sub.add_parser('set', help='set exact semantic version')
|
|
set_parser.add_argument('version')
|
|
|
|
args = parser.parse_args()
|
|
current = read_version()
|
|
|
|
if args.command == 'show':
|
|
print(current)
|
|
return 0
|
|
|
|
if args.command == 'set':
|
|
write_version(args.version)
|
|
print(f'[OK] Version set: {current} -> {args.version}')
|
|
return 0
|
|
|
|
next_version = bump(current, args.part)
|
|
write_version(next_version)
|
|
print(f'[OK] Version bumped: {current} -> {next_version}')
|
|
|
|
if args.commit:
|
|
msg = f'chore(release): v{next_version}'
|
|
run(['python3', 'scripts/validate_conventional_commit.py', '--message', msg])
|
|
run([
|
|
'git',
|
|
'add',
|
|
'project/VERSION',
|
|
*[str(p.relative_to(ROOT)) for p in PACKAGE_FILES if p.exists()],
|
|
*[str(p.relative_to(ROOT)) for p in PACKAGE_LOCK_FILES if p.exists()],
|
|
])
|
|
run(['git', 'commit', '-m', msg])
|
|
|
|
if args.tag:
|
|
run(['git', 'tag', f'v{next_version}'])
|
|
print(f'[OK] Tagged v{next_version}')
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(main())
|