feat(orquestra): enforce semver and conventional commits
This commit is contained in:
55
docs/deployment.md
Normal file
55
docs/deployment.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Deploy del monolito MercadoDeVida
|
||||
|
||||
## ¿Basta con copiar `project/`?
|
||||
|
||||
Sí, para levantar el monolito en un host basta con traer la carpeta `project/` completa y ejecutar:
|
||||
|
||||
```bash
|
||||
cd project
|
||||
./scripts/monolith.sh prod start
|
||||
```
|
||||
|
||||
o, para reemplazar una versión anterior ya corriendo:
|
||||
|
||||
```bash
|
||||
cd project
|
||||
./scripts/monolith.sh prod restart
|
||||
```
|
||||
|
||||
## Qué hace `monolith.sh prod start/restart`
|
||||
|
||||
1. Limpia procesos antiguos del proyecto y listeners en puertos gestionados.
|
||||
2. Arranca PostgreSQL y Redis con Docker Compose.
|
||||
3. Instala dependencias con `npm ci` en backend, admin, TPV, frontend y storefront.
|
||||
4. Ejecuta migraciones.
|
||||
5. Compila backend y frontends.
|
||||
6. Arranca servicios con `nohup` y guarda PID/logs en `project/.runtime/prod/`.
|
||||
7. Verifica HTTP para cada servicio.
|
||||
|
||||
Servicios por defecto:
|
||||
|
||||
| Servicio | Puerto |
|
||||
|---|---:|
|
||||
| Backend API | 3000 |
|
||||
| Admin | 3001 |
|
||||
| TPV | 3002 |
|
||||
| Frontend | 3003 |
|
||||
| Storefront SEO | 3004 |
|
||||
|
||||
## Requisitos del host
|
||||
|
||||
- Node.js >= 22
|
||||
- npm
|
||||
- Docker + Docker Compose
|
||||
- Bash, curl, lsof
|
||||
- Variables/secretos en `project/.env`
|
||||
|
||||
Si no existe `.env`, el script crea uno de desarrollo. En producción real conviene crear `.env` explícito antes de arrancar.
|
||||
|
||||
## Lo que NO gestiona
|
||||
|
||||
- Traefik/Nginx/Authelia o certificados TLS.
|
||||
- Arranque automático tras reinicio del host.
|
||||
- Backups de base de datos.
|
||||
|
||||
Para producción estable conviene envolver `./scripts/monolith.sh prod restart` con systemd, supervisor o el orquestador elegido.
|
||||
83
docs/versioning.md
Normal file
83
docs/versioning.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Versionado semántico y Conventional Commits
|
||||
|
||||
## Fuente de verdad
|
||||
|
||||
La versión del producto vive en:
|
||||
|
||||
```txt
|
||||
project/VERSION
|
||||
```
|
||||
|
||||
Los `package.json` del monolito deben mantenerse sincronizados con ese valor.
|
||||
|
||||
## Conventional Commits obligatorios
|
||||
|
||||
Formato:
|
||||
|
||||
```txt
|
||||
<type>[optional scope][!]: <description>
|
||||
```
|
||||
|
||||
Tipos permitidos:
|
||||
|
||||
- `feat`
|
||||
- `fix`
|
||||
- `docs`
|
||||
- `chore`
|
||||
- `refactor`
|
||||
- `perf`
|
||||
- `test`
|
||||
- `build`
|
||||
- `ci`
|
||||
- `style`
|
||||
- `revert`
|
||||
|
||||
Ejemplos:
|
||||
|
||||
```txt
|
||||
feat(orders): add awaiting-payment notifications
|
||||
fix(monolith): clean stale deployments before start
|
||||
chore(release): v0.2.0
|
||||
```
|
||||
|
||||
## Herramientas Orquestra
|
||||
|
||||
Validar un mensaje:
|
||||
|
||||
```bash
|
||||
python3 scripts/validate_conventional_commit.py --message "fix(monolith): clean stale deployments"
|
||||
```
|
||||
|
||||
Instalar hook local de Git:
|
||||
|
||||
```bash
|
||||
./scripts/install_git_hooks.sh
|
||||
```
|
||||
|
||||
Bump semver:
|
||||
|
||||
```bash
|
||||
python3 scripts/version.py bump patch
|
||||
python3 scripts/version.py bump minor --commit --tag
|
||||
python3 scripts/version.py bump major --commit --tag
|
||||
```
|
||||
|
||||
Ver versión actual:
|
||||
|
||||
```bash
|
||||
python3 scripts/version.py show
|
||||
```
|
||||
|
||||
## Cierre de features
|
||||
|
||||
`scripts/commit_feature.sh` genera commits convencionales automáticamente usando el tipo del ticket:
|
||||
|
||||
- `feature` → `feat(...)`
|
||||
- `fix` / `bug` → `fix(...)`
|
||||
- `chore` → `chore(...)`
|
||||
|
||||
`verify.sh` valida:
|
||||
|
||||
- `project/VERSION` como semver.
|
||||
- `package.json` sincronizados.
|
||||
- último commit compatible con Conventional Commits.
|
||||
1
project/VERSION
Normal file
1
project/VERSION
Normal file
@@ -0,0 +1 @@
|
||||
0.1.0
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Commit and optionally push a completed feature
|
||||
# Commit and optionally push a completed feature using Conventional Commits.
|
||||
# Usage: ./scripts/commit_feature.sh <feature_id>
|
||||
|
||||
set -euo pipefail
|
||||
@@ -16,7 +16,50 @@ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Stage all changes (excluding node_modules, dist, .next, etc.)
|
||||
feature_meta() {
|
||||
python3 - "$FEATURE_ID" <<'PY'
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
fid = sys.argv[1]
|
||||
path = Path('backlog/features.json')
|
||||
commit_type = 'feat'
|
||||
title = 'completed feature'
|
||||
|
||||
if path.exists():
|
||||
data = json.loads(path.read_text(encoding='utf-8'))
|
||||
for feature in data.get('features', []):
|
||||
if str(feature.get('id')) == fid:
|
||||
ftype = str(feature.get('type', 'feature')).lower()
|
||||
commit_type = {
|
||||
'feature': 'feat',
|
||||
'fix': 'fix',
|
||||
'bug': 'fix',
|
||||
'chore': 'chore',
|
||||
}.get(ftype, 'feat')
|
||||
title = feature.get('title') or feature.get('description') or title
|
||||
break
|
||||
|
||||
subject = re.sub(r'\s+', ' ', str(title)).strip()
|
||||
subject = subject[0].lower() + subject[1:] if subject else 'completed feature'
|
||||
subject = subject.rstrip('.')
|
||||
print(commit_type)
|
||||
print(subject[:120])
|
||||
PY
|
||||
}
|
||||
|
||||
mapfile -t META < <(feature_meta)
|
||||
COMMIT_TYPE="${META[0]:-feat}"
|
||||
COMMIT_SUBJECT="${META[1]:-completed feature}"
|
||||
SCOPE=$(printf '%s' "$FEATURE_ID" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9._/-' '-')
|
||||
COMMIT_MSG="$COMMIT_TYPE($SCOPE): $COMMIT_SUBJECT"
|
||||
|
||||
# Validate before touching git state.
|
||||
python3 scripts/validate_conventional_commit.py --message "$COMMIT_MSG"
|
||||
|
||||
# Stage all changes (excluding node_modules, dist, .next, etc. via .gitignore)
|
||||
git add -A
|
||||
|
||||
# Check if there are changes to commit
|
||||
@@ -25,24 +68,10 @@ if git diff --cached --quiet; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create commit message
|
||||
COMMIT_MSG="feat($FEATURE_ID): completed feature"
|
||||
|
||||
# Check if there's a description in the feature
|
||||
if [ -f "work/current.md" ]; then
|
||||
# Extract first line of description if available
|
||||
DESC=$(grep -m1 "^## Description" work/current.md -A1 2>/dev/null | tail -1 | sed 's/^[[:space:]]*//' || true)
|
||||
if [ -n "$DESC" ]; then
|
||||
COMMIT_MSG="$COMMIT_MSG
|
||||
|
||||
$DESC"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Commit
|
||||
git commit -m "$COMMIT_MSG"
|
||||
|
||||
echo "[OK] Committed $FEATURE_ID"
|
||||
echo "[OK] Committed $FEATURE_ID: $COMMIT_MSG"
|
||||
|
||||
# Check if remote exists and push
|
||||
if git remote get-url origin >/dev/null 2>&1; then
|
||||
|
||||
3
scripts/git-hooks/commit-msg
Executable file
3
scripts/git-hooks/commit-msg
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
python3 scripts/validate_conventional_commit.py --file "$1"
|
||||
@@ -149,9 +149,10 @@ copy_dir_update "$SOURCE_DIR/harness" "$TARGET_DIR/harness"
|
||||
copy_dir_update "$SOURCE_DIR/platforms/pi" "$TARGET_DIR/platforms/pi"
|
||||
copy_dir_update "$SOURCE_DIR/docs" "$TARGET_DIR/docs"
|
||||
|
||||
for file in scripts/verify.sh scripts/agent_status.py scripts/new_ticket.py scripts/pi_orquestra.sh scripts/run_stage.py scripts/commit_feature.sh scripts/close_feature.py scripts/install.sh; do
|
||||
for file in scripts/verify.sh scripts/agent_status.py scripts/new_ticket.py scripts/pi_orquestra.sh scripts/run_stage.py scripts/commit_feature.sh scripts/close_feature.py scripts/validate_conventional_commit.py scripts/version.py scripts/install_git_hooks.sh scripts/install.sh; do
|
||||
copy_file_update "$SOURCE_DIR/$file" "$TARGET_DIR/$file"
|
||||
done
|
||||
copy_dir_update "$SOURCE_DIR/scripts/git-hooks" "$TARGET_DIR/scripts/git-hooks"
|
||||
|
||||
append_gitignore_block "$TARGET_DIR/.gitignore"
|
||||
|
||||
@@ -176,7 +177,7 @@ if [ -d "$TARGET_DIR/.pi/subagents" ] || [ -f "$TARGET_DIR/.pi/subagents.json" ]
|
||||
warn "Found .pi/subagents or .pi/subagents.json in target. Orquestra does not delete user Pi config automatically; remove Orquestra-owned stale subagent files manually before running verify.sh."
|
||||
fi
|
||||
|
||||
chmod +x "$TARGET_DIR/scripts/verify.sh" "$TARGET_DIR/scripts/pi_orquestra.sh" "$TARGET_DIR/scripts/run_stage.py" "$TARGET_DIR/scripts/install.sh"
|
||||
chmod +x "$TARGET_DIR/scripts/verify.sh" "$TARGET_DIR/scripts/pi_orquestra.sh" "$TARGET_DIR/scripts/run_stage.py" "$TARGET_DIR/scripts/install.sh" "$TARGET_DIR/scripts/validate_conventional_commit.py" "$TARGET_DIR/scripts/version.py" "$TARGET_DIR/scripts/install_git_hooks.sh" "$TARGET_DIR/scripts/git-hooks/commit-msg"
|
||||
|
||||
cat <<EOF
|
||||
|
||||
@@ -184,6 +185,7 @@ Orquestra installed/updated safely in: $TARGET_DIR
|
||||
|
||||
Next steps:
|
||||
cd "$TARGET_DIR"
|
||||
./scripts/install_git_hooks.sh
|
||||
./scripts/verify.sh
|
||||
./scripts/pi_orquestra.sh
|
||||
EOF
|
||||
|
||||
16
scripts/install_git_hooks.sh
Executable file
16
scripts/install_git_hooks.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
|
||||
cd "$ROOT"
|
||||
|
||||
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
|
||||
echo '[FAIL] Not inside a git repository' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
chmod +x scripts/git-hooks/commit-msg scripts/validate_conventional_commit.py scripts/version.py
|
||||
git config core.hooksPath scripts/git-hooks
|
||||
|
||||
echo '[OK] Git hooks installed: core.hooksPath=scripts/git-hooks'
|
||||
echo '[OK] commit-msg now enforces Conventional Commits'
|
||||
93
scripts/validate_conventional_commit.py
Executable file
93
scripts/validate_conventional_commit.py
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate Conventional Commit messages for Orquestra.
|
||||
|
||||
Allowed format:
|
||||
<type>[optional scope][!]: <description>
|
||||
|
||||
Examples:
|
||||
feat(orders): add awaiting-payment notifications
|
||||
fix(DEPLOY-MONOLITH-CLEANUP): clean stale deployments
|
||||
chore(release)!: v1.0.0
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ALLOWED_TYPES = {
|
||||
'build',
|
||||
'chore',
|
||||
'ci',
|
||||
'docs',
|
||||
'feat',
|
||||
'fix',
|
||||
'perf',
|
||||
'refactor',
|
||||
'revert',
|
||||
'style',
|
||||
'test',
|
||||
}
|
||||
|
||||
HEADER_RE = re.compile(
|
||||
r'^(?P<type>build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)'
|
||||
r'(?:\((?P<scope>[A-Za-z0-9._/-]+)\))?'
|
||||
r'(?P<breaking>!)?: (?P<description>\S.{0,119})$'
|
||||
)
|
||||
|
||||
|
||||
def validate(message: str) -> tuple[bool, str | None]:
|
||||
lines = message.strip('\n').splitlines()
|
||||
if not lines or not lines[0].strip():
|
||||
return False, 'empty commit message'
|
||||
|
||||
header = lines[0].strip()
|
||||
match = HEADER_RE.match(header)
|
||||
if not match:
|
||||
return (
|
||||
False,
|
||||
"first line must be Conventional Commit: "
|
||||
"<type>[optional scope][!]: <description>",
|
||||
)
|
||||
|
||||
commit_type = match.group('type')
|
||||
if commit_type not in ALLOWED_TYPES:
|
||||
return False, f"unsupported type '{commit_type}'"
|
||||
|
||||
description = match.group('description').strip()
|
||||
if description.endswith('.'):
|
||||
return False, 'description must not end with a period'
|
||||
|
||||
if any(line.rstrip() != line for line in lines):
|
||||
return False, 'commit message contains trailing whitespace'
|
||||
|
||||
if len(lines) > 1 and lines[1].strip():
|
||||
return False, 'second line must be blank before body/footer'
|
||||
|
||||
return True, None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description='Validate Conventional Commit message')
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument('--file', type=Path, help='commit message file')
|
||||
group.add_argument('--message', help='commit message string')
|
||||
args = parser.parse_args()
|
||||
|
||||
message = args.message if args.message is not None else args.file.read_text(encoding='utf-8')
|
||||
ok, error = validate(message)
|
||||
if ok:
|
||||
return 0
|
||||
|
||||
print(f'[FAIL] Invalid commit message: {error}', file=sys.stderr)
|
||||
print('', file=sys.stderr)
|
||||
print('Examples:', file=sys.stderr)
|
||||
print(' feat(orders): add awaiting-payment notifications', file=sys.stderr)
|
||||
print(' fix(monolith): clean stale deployments before start', file=sys.stderr)
|
||||
print(' chore(release): v0.2.0', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main())
|
||||
@@ -39,6 +39,11 @@ required=(
|
||||
"scripts/run_stage.py"
|
||||
"scripts/commit_feature.sh"
|
||||
"scripts/close_feature.py"
|
||||
"scripts/validate_conventional_commit.py"
|
||||
"scripts/version.py"
|
||||
"scripts/install_git_hooks.sh"
|
||||
"scripts/git-hooks/commit-msg"
|
||||
"project/VERSION"
|
||||
"platforms/pi/README.md"
|
||||
"platforms/pi/extensions/orquestra-status/index.ts"
|
||||
"platforms/pi/extensions/orquestra-web-fetch.ts"
|
||||
@@ -84,6 +89,55 @@ else
|
||||
ok "Sin archivos de producto/código en la raíz"
|
||||
fi
|
||||
|
||||
python3 - <<'PY'
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
root = pathlib.Path('.')
|
||||
version_path = root / 'project' / 'VERSION'
|
||||
semver = re.compile(r'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:[-+][0-9A-Za-z.-]+)?$')
|
||||
try:
|
||||
version = version_path.read_text(encoding='utf-8').strip()
|
||||
except Exception as e:
|
||||
print(f'[FAIL] project/VERSION no se puede leer: {e}')
|
||||
sys.exit(1)
|
||||
if not semver.match(version):
|
||||
print(f'[FAIL] project/VERSION no es semver válido: {version}')
|
||||
sys.exit(1)
|
||||
|
||||
for rel in [
|
||||
'project/package.json',
|
||||
'project/apps/admin/package.json',
|
||||
'project/apps/pos/package.json',
|
||||
'project/frontend/package.json',
|
||||
'project/storefront/package.json',
|
||||
]:
|
||||
path = root / rel
|
||||
if not path.exists():
|
||||
continue
|
||||
data = json.loads(path.read_text(encoding='utf-8'))
|
||||
if data.get('version') != version:
|
||||
print(f"[FAIL] {rel} version {data.get('version')} != project/VERSION {version}")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
msg = subprocess.check_output(['git', 'log', '-1', '--pretty=%B'], text=True).strip()
|
||||
except Exception:
|
||||
msg = ''
|
||||
if msg:
|
||||
result = subprocess.run(['python3', 'scripts/validate_conventional_commit.py', '--message', msg])
|
||||
if result.returncode != 0:
|
||||
print('[FAIL] Último commit no cumple Conventional Commits')
|
||||
sys.exit(1)
|
||||
|
||||
print(f'[OK] semver válido y sincronizado: {version}')
|
||||
print('[OK] último commit cumple Conventional Commits')
|
||||
PY
|
||||
if [ $? -ne 0 ]; then EXIT_CODE=1; fi
|
||||
|
||||
echo ""
|
||||
echo "── 2) Validando requisitos Pi ─────────────────────────"
|
||||
if command -v pi >/dev/null 2>&1; then
|
||||
|
||||
128
scripts/version.py
Executable file
128
scripts/version.py
Executable file
@@ -0,0 +1,128 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user