From edbced31b586b11627052a16474cac1e52151a24 Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Mon, 27 Jul 2026 22:49:36 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=BE=20=D0=B0=D0=B2=D1=82=D0=BE=D0=B2=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D0=BE=D0=BD=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20SemVer=20=D0=B8=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F?= =?UTF-8?q?=20v0.1.1=20=D0=B2=20=D1=84=D1=83=D1=82=D0=B5=D1=80=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- Dockerfile | 2 +- Jenkinsfile | 106 ++++++++++++++++++++++++-------- Jenkinsfile.deploy | 12 +++- Makefile | 37 +++++++++--- README.md | 23 +++++-- VERSION | 1 + app/main.py | 10 ++- app/static/css/app.css | 18 ++++++ app/templates/base.html | 7 ++- app/version.py | 127 +++++++++++++++++++++++++++++++++++++++ helm/wrapped/Chart.yaml | 4 +- helm/wrapped/values.yaml | 2 +- pyproject.toml | 2 +- scripts/bump_version.py | 27 +++++++++ 14 files changed, 327 insertions(+), 51 deletions(-) create mode 100644 VERSION create mode 100644 app/version.py create mode 100644 scripts/bump_version.py diff --git a/Dockerfile b/Dockerfile index 4010207..af6f4be 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN apt-get update \ WORKDIR /app -COPY pyproject.toml README.md ./ +COPY pyproject.toml README.md VERSION ./ COPY app ./app COPY alembic ./alembic COPY alembic.ini ./ diff --git a/Jenkinsfile b/Jenkinsfile index 88bae3c..bff29c0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,21 +1,15 @@ -// Wrapped — CI: multi-arch (amd64+arm64) build & push to Harbor + Docker Hub +// Wrapped — CI: auto SemVer bump + multi-arch push (Harbor + Docker Hub) + deploy // -// Триггер: push в main (webhook Gitea → Jenkins Multibranch / Pipeline from SCM). -// После успешного Build & push → trigger job DEPLOY_JOB (Jenkinsfile.deploy). +// Триггер: push в main (webhook Gitea → Jenkins Multibranch). +// Коммиты с [skip ci] в сообщении — без повторного bump (анти-цикл после version commit). // -// Credentials в Jenkins (Manage Credentials → System → Global): -// harbor-devops-tools-push-pull-access — Username with password (Harbor devops-tools) -// docker-hub — Username with password (Docker Hub) +// Credentials: +// harbor-devops-tools-push-pull-access — Harbor devops-tools +// docker-hub — Docker Hub +// gitea-ssh (или GIT_SSH_CREDENTIALS_ID) — SSH для push bump-коммита // -// Agent: cloud kubernetes, pod template default (labels docker / dind) -// -// Образы: -// hub.antropoff.ru/devops-tools/wrapped: -// inecs/wrapped: -// -// Связка Build → Deploy: -// Multibranch Deploy job, Script Path = Jenkinsfile.deploy -// путь по умолчанию: devops-tools/wrapped/wrapped-deploy/main (поменяй DEPLOY_JOB при другом имени) +// Версия: файл VERSION (+ pyproject / Helm). Теги образа: :, :, :latest +// Deploy: devops-tools/wrapped/wrapped-deploy/main pipeline { agent { label 'docker' } @@ -31,11 +25,11 @@ pipeline { HARBOR_REGISTRY = 'hub.antropoff.ru' HARBOR_IMAGE = 'hub.antropoff.ru/devops-tools/wrapped' DOCKERHUB_IMAGE = 'inecs/wrapped' - IMAGE_VERSION = '0.1.0' RELEASE_TAG = "${env.GIT_COMMIT?.take(7) ?: 'dev'}" BUILDX_BUILDER = "jenkins-wrapped-${env.BUILD_NUMBER}" - // Multibranch fullName (не display name): devops-tools » wrapped » wrapped-deploy » main DEPLOY_JOB = 'devops-tools/wrapped/wrapped-deploy/main' + // SSH credential для git push bump (тот же ключ, что SCM Gitea) + GIT_SSH_CREDENTIALS_ID = 'gitea-ssh' TZ = 'Europe/Moscow' } @@ -46,13 +40,42 @@ pipeline { } } - stage('Build & push') { + stage('Version') { when { anyOf { branch 'main' branch 'master' } } + steps { + container('docker') { + script { + sh 'apk add --no-cache python3 git openssh-client >/dev/null' + def lastMsg = sh(script: 'git log -1 --pretty=%B', returnStdout: true).trim() + if (lastMsg.contains('[skip ci]')) { + env.IMAGE_VERSION = sh(script: 'cat VERSION', returnStdout: true).trim() + env.SKIP_CI = '1' + echo "Skip CI for version-bump commit ([skip ci]); VERSION=${env.IMAGE_VERSION}" + } else { + env.IMAGE_VERSION = sh( + script: 'PYTHONPATH=. python3 scripts/bump_version.py patch', + returnStdout: true + ).trim() + env.SKIP_CI = '0' + echo "Bumped VERSION → ${env.IMAGE_VERSION}" + } + } + } + } + } + + stage('Build & push') { + when { + allOf { + anyOf { branch 'main'; branch 'master' } + expression { return env.SKIP_CI != '1' } + } + } steps { withCredentials([ usernamePassword( @@ -70,6 +93,9 @@ pipeline { sh ''' set -eux + test -n "${IMAGE_VERSION}" + echo "Building tags: ${IMAGE_VERSION}, ${RELEASE_TAG}, latest" + echo "$HARBOR_PASS" | docker login "$HARBOR_REGISTRY" -u "$HARBOR_USER" --password-stdin echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USER" --password-stdin @@ -77,7 +103,6 @@ pipeline { docker buildx create --name "$BUILDX_BUILDER" --driver docker-container --use docker buildx inspect --bootstrap >/dev/null - # Per-arch images → both registries (Harbor is happier than a single multi-arch --push) docker buildx build \ --platform linux/amd64 \ --provenance=false --sbom=false --push \ @@ -92,7 +117,6 @@ pipeline { -t "${DOCKERHUB_IMAGE}:${RELEASE_TAG}-arm64" \ -f Dockerfile . - # Multi-arch manifests: :sha, :0.1.0, :latest on each registry for IMAGE in "$HARBOR_IMAGE" "$DOCKERHUB_IMAGE"; do docker buildx imagetools create \ -t "${IMAGE}:${RELEASE_TAG}" \ @@ -103,9 +127,9 @@ pipeline { done echo "--- Harbor ---" - docker buildx imagetools inspect "${HARBOR_IMAGE}:${RELEASE_TAG}" | sed -n '1,40p' + docker buildx imagetools inspect "${HARBOR_IMAGE}:${IMAGE_VERSION}" | sed -n '1,40p' echo "--- Docker Hub ---" - docker buildx imagetools inspect "${DOCKERHUB_IMAGE}:${RELEASE_TAG}" | sed -n '1,40p' + docker buildx imagetools inspect "${DOCKERHUB_IMAGE}:${IMAGE_VERSION}" | sed -n '1,40p' docker buildx rm "$BUILDX_BUILDER" || true ''' @@ -114,11 +138,40 @@ pipeline { } } + stage('Commit version') { + when { + allOf { + anyOf { branch 'main'; branch 'master' } + expression { return env.SKIP_CI != '1' } + } + } + steps { + container('docker') { + sshagent(credentials: [env.GIT_SSH_CREDENTIALS_ID]) { + sh ''' + set -eux + git config user.email "jenkins@antropoff.ru" + git config user.name "Jenkins" + git add VERSION pyproject.toml helm/wrapped/Chart.yaml helm/wrapped/values.yaml + if git diff --cached --quiet; then + echo "No version files to commit" + exit 0 + fi + git commit -m "Bump version to ${IMAGE_VERSION} [skip ci]." + # Multibranch detached HEAD → push to branch name + BRANCH="${BRANCH_NAME:-main}" + git push origin "HEAD:refs/heads/${BRANCH}" + ''' + } + } + } + } + stage('Trigger deploy') { when { - anyOf { - branch 'main' - branch 'master' + allOf { + anyOf { branch 'main'; branch 'master' } + expression { return env.SKIP_CI != '1' } } } steps { @@ -141,12 +194,13 @@ pipeline { post { success { + echo "✓ Version: ${IMAGE_VERSION}" echo "✓ Harbor: ${HARBOR_IMAGE}:{${RELEASE_TAG},${IMAGE_VERSION},latest}" echo "✓ Docker Hub: ${DOCKERHUB_IMAGE}:{${RELEASE_TAG},${IMAGE_VERSION},latest}" echo "✓ Deploy: ${DEPLOY_JOB} (${DOCKERHUB_IMAGE}:${IMAGE_VERSION})" } failure { - echo "✗ Сборка, push или trigger deploy не удались — см. лог стадий" + echo "✗ Version / build / push / deploy — см. лог стадий" } always { script { diff --git a/Jenkinsfile.deploy b/Jenkinsfile.deploy index c0f065c..3752fee 100644 --- a/Jenkinsfile.deploy +++ b/Jenkinsfile.deploy @@ -34,8 +34,8 @@ pipeline { ) string( name: 'IMAGE_TAG', - defaultValue: '0.1.0', - description: 'Тег образа (0.1.0 / latest / short-sha после CI build)' + defaultValue: '', + description: 'Тег образа (SemVer из CI). Пусто = cat VERSION из репозитория' ) choice( name: 'IMAGE_PULL_POLICY', @@ -112,9 +112,15 @@ pipeline { exit 1 fi + # IMAGE_TAG пустой → из VERSION (автоверсионирование) + if [ -z "${IMAGE_TAG}" ]; then + IMAGE_TAG="$(tr -d '[:space:]' < VERSION)" + export IMAGE_TAG + fi + test -n "${IMAGE_TAG}" echo "Upgrading ${HELM_RELEASE}: ${IMAGE_REPOSITORY}:${IMAGE_TAG} (${IMAGE_PULL_POLICY})" - # Как addon: helm upgrade --install … --atomic --wait + # Как addon: helm upgrade … --atomic --wait # --reuse-values сохраняет Ingress/секреты/DB/S3 с кластера helm upgrade "$HELM_RELEASE" "$HELM_CHART" \ --namespace "$HELM_NAMESPACE" \ diff --git a/Makefile b/Makefile index d0b6bac..1016d1f 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ -.PHONY: help env build up down restart logs shell migrate revision release buildx-setup helm-package helm-lint clean ps push +.PHONY: help env build up down restart logs shell migrate revision release buildx-setup helm-package helm-lint clean ps push bump-patch bump-minor version-commit COMPOSE ?= docker compose IMAGE_NAME ?= wrapped -IMAGE_TAG ?= 0.1.0 +# SemVer from VERSION (source of truth); override: make release IMAGE_TAG=1.2.3 +IMAGE_TAG ?= $(shell cat VERSION 2>/dev/null || echo 0.1.0) # Docker Hub namespace → image: $(RELEASE_REGISTRY)/$(IMAGE_NAME):$(IMAGE_TAG) # Example: inecs/wrapped:0.1.0 RELEASE_REGISTRY ?= inecs @@ -27,17 +28,21 @@ help: @echo " make shell Shell into app container" @echo " make migrate Run alembic upgrade head" @echo " make revision m=\"msg\" Create alembic revision" + @echo " make bump-patch VERSION +0.0.1 (pyproject + Helm)" + @echo " make bump-minor VERSION +0.1.0" @echo " make release Multi-arch build & push $(FULL_IMAGE)" @echo " platforms: $(RELEASE_PLATFORMS)" - @echo " make push git add ., commit (prompt), push origin" + @echo " make push bump-patch + git add/commit (prompt) + push" @echo " make helm-lint Lint Helm chart" @echo " make helm-package Package Helm chart" @echo " make clean Remove containers, volumes, local image" @echo "" + @echo "Version: $$(cat VERSION 2>/dev/null || echo 0.1.0)" @echo "Release examples:" - @echo " make release IMAGE_TAG=0.1.0" - @echo " make release IMAGE_TAG=0.1.0 PUSH=0" - @echo " make release IMAGE_TAG=0.1.0 RELEASE_PLATFORMS=linux/arm64" + @echo " make release # tag from VERSION" + @echo " make release IMAGE_TAG=1.2.3" + @echo " make release PUSH=0" + @echo " make release RELEASE_PLATFORMS=linux/arm64" env: @test -f .env || cp .env.example .env @@ -131,8 +136,24 @@ clean: -docker buildx rm $(BUILDX_BUILDER) 2>/dev/null || true rm -rf dist -# Same flow as proxmox_api_simulator: stage all, multiline commit via Ctrl-D, push origin. -push: +# Same flow as proxmox_api_simulator / infra: bump SemVer, stage, multiline commit, push. +bump-patch: + @PYTHONPATH=. python3 scripts/bump_version.py patch + @echo "VERSION → $$(cat VERSION)" + +bump-minor: + @PYTHONPATH=. python3 scripts/bump_version.py minor + @echo "VERSION → $$(cat VERSION)" + +version-commit: + @git add VERSION pyproject.toml helm/wrapped/Chart.yaml helm/wrapped/values.yaml + @if git diff --cached --quiet; then \ + echo "Version unchanged — no version commit"; \ + else \ + git commit -m "Bump version to $$(cat VERSION)."; \ + fi + +push: bump-patch @set -e; \ git add .; \ echo "=== staged ==="; \ diff --git a/README.md b/README.md index 5a27af7..9603d6f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ | **Расшифровка** | Прокрутка превью текста по вертикали и горизонтали; кнопка **Копировать** рядом со скачиванием | | **Пароль** | Argon2-проверка *до* выдачи ciphertext; лимит попыток; пустой пароль попытку не тратит; структурированные ошибки в UI | | **Админка** | Стартовая **Статистика** (MinIO, wraps, audit); очистка audit с подтверждением; пагинация журнала | -| **Релиз** | `make release` / Jenkins CI+CD: multi-arch → Harbor и Docker Hub; Helm upgrade в k3s (`Jenkinsfile.deploy`, как addon-wrapped) | +| **Релиз** | Авто-SemVer (`VERSION`); Jenkins CI+CD: multi-arch → Harbor/Hub; Helm upgrade в k3s | Подробности — в разделах ниже (безопасность, админка, сборка образа). @@ -432,7 +432,20 @@ docker buildx build \ | Harbor | `hub.antropoff.ru/devops-tools/wrapped` | | Docker Hub | `inecs/wrapped` | -Теги на каждый реестр: `:`, `:0.1.0`, `:latest` (плюс служебные `:-amd64` / `:-arm64`). +Теги на каждый реестр: `:` (из `VERSION`, авто +0.0.1 на каждый CI build), `:`, `:latest`. + +### Версионирование + +Источник правды — файл [`VERSION`](VERSION) (SemVer). Синхронизируется в `pyproject.toml` и Helm (`Chart.yaml`, `values.yaml` image.tag). + +| Где | Поведение | +|-----|-----------| +| UI | справа в футере: `vX.Y.Z` | +| `make bump-patch` / `bump-minor` | ручной bump | +| `make push` | автоматически `bump-patch`, затем commit/push | +| Jenkins (`Jenkinsfile`) | на `main` автоматически `bump-patch`, образ с новым тегом, commit `Bump version to X.Y.Z [skip ci].`, затем Deploy | + +Коммиты с `[skip ci]` не бампят версию снова (анти-цикл). Credential для push bump: `GIT_SSH_CREDENTIALS_ID` (по умолчанию `gitea-ssh`) — тот же SSH-ключ, что для SCM. Credentials (как в актуальном `Jenkinsfile`): @@ -471,14 +484,14 @@ kubectl -n wrapped rollout status deployment/wrapped | Параметр job | Умолч. | Смысл | |--------------|--------|--------| | `IMAGE_REPOSITORY` | `inecs/wrapped` | как `wrapped_image_repository` | -| `IMAGE_TAG` | `0.1.0` | как `wrapped_image_tag` (`latest` / short-sha после build) | +| `IMAGE_TAG` | из `VERSION` / CI | SemVer образа (`Always` подтянет новый) | | `IMAGE_PULL_POLICY` | `Always` | как в addon | Credential: `k3s-kubeconfig` (Secret file) — тот же, что у devops.org.ru. -Рекомендуемая связка job’ов уже в [`Jenkinsfile`](Jenkinsfile): после успешного **Build & push** стадия **Trigger deploy** вызывает Multibranch job `Devops Tools/Wrapped Deploy/main` с параметрами `inecs/wrapped` / `0.1.0` / `Always` (`wait: true`). Если имя folder/job другое — поменяй `DEPLOY_JOB` в `Jenkinsfile`. +Рекомендуемая связка job’ов уже в [`Jenkinsfile`](Jenkinsfile): после **Version** + **Build & push** стадия **Trigger deploy** вызывает `devops-tools/wrapped/wrapped-deploy/main` с актуальным SemVer (`wait: true`). Credential SSH для version-commit: `gitea-ssh` (или поменяй `GIT_SSH_CREDENTIALS_ID`). -Ручной запуск Deploy по-прежнему: **Build with Parameters**. +Ручной запуск Deploy: **Build with Parameters** (пустой `IMAGE_TAG` → `cat VERSION`). ### Git (`make push`) diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..17e51c3 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.1 diff --git a/app/main.py b/app/main.py index 4617823..8bf72b9 100644 --- a/app/main.py +++ b/app/main.py @@ -14,6 +14,7 @@ from app.config import get_settings from app.db import SessionLocal from app.services.settings_service import get_or_create_settings from app.services.storage import storage +from app.version import get_app_version, get_app_version_label OPENAPI_TAGS = [ { @@ -73,9 +74,10 @@ def custom_openapi(app: FastAPI): def create_app() -> FastAPI: settings = get_settings() docs_on = settings.is_docs_enabled + app_version = get_app_version() app = FastAPI( title=settings.app_name, - version="0.1.0", + version=app_version, description=( "Wrapped — zero-knowledge one-time encrypted drop.\n\n" "Public wrap APIs are anonymous. Admin APIs require HTTP Basic." @@ -97,10 +99,14 @@ def create_app() -> FastAPI: app.include_router(admin.api_router) templates = Jinja2Templates(directory="app/templates") + templates.env.globals["app_version"] = app_version + templates.env.globals["app_version_label"] = get_app_version_label() + admin.templates.env.globals["app_version"] = app_version + admin.templates.env.globals["app_version_label"] = get_app_version_label() @app.get("/health", tags=["System"], summary="Health check", include_in_schema=docs_on) async def health(): - return {"status": "ok", "service": settings.app_name} + return {"status": "ok", "service": settings.app_name, "version": app_version} @app.get("/", include_in_schema=False) async def index(request: Request): diff --git a/app/static/css/app.css b/app/static/css/app.css index fef0014..6634cd5 100644 --- a/app/static/css/app.css +++ b/app/static/css/app.css @@ -886,6 +886,24 @@ html[data-theme="light"] .footer-pillars li { margin: 0.85rem 0 0; font-size: 0.8rem; color: var(--muted); + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + flex-wrap: wrap; +} +.footer-copy-left { + min-width: 0; +} +.footer-copy-version { + margin-left: auto; + font-family: var(--font-mono); + font-size: 0.72rem; + font-weight: 500; + letter-spacing: 0.02em; + color: var(--muted); + opacity: 0.9; + white-space: nowrap; } .footer-copy a { color: var(--accent-2); diff --git a/app/templates/base.html b/app/templates/base.html index 38ece9d..31c4c30 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -66,8 +66,11 @@ diff --git a/app/version.py b/app/version.py new file mode 100644 index 0000000..ddc85dc --- /dev/null +++ b/app/version.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import re +from functools import lru_cache +from pathlib import Path + +_VERSION_FILE = Path(__file__).resolve().parents[1] / "VERSION" +_PROJECT_ROOT = _VERSION_FILE.parent +_PYPROJECT_FILE = _PROJECT_ROOT / "pyproject.toml" +_CHART_FILE = _PROJECT_ROOT / "helm" / "wrapped" / "Chart.yaml" +_VALUES_FILE = _PROJECT_ROOT / "helm" / "wrapped" / "values.yaml" +_DEFAULT_VERSION = "0.1.0" +_VERSION_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)$") + + +def _read_version_file(path: Path) -> str | None: + try: + text = path.read_text(encoding="utf-8").strip() + except OSError: + return None + if _VERSION_RE.match(text): + return text + return None + + +@lru_cache +def get_app_version() -> str: + for path in ( + _VERSION_FILE, + Path("/app/VERSION"), + Path.cwd() / "VERSION", + ): + found = _read_version_file(path) + if found: + return found + try: + from importlib.metadata import version + + meta = version("wrapped") + if _VERSION_RE.match(meta): + return meta + except Exception: + pass + return _DEFAULT_VERSION + + +def get_app_version_label() -> str: + return f"v{get_app_version()}" + + +def parse_version(value: str) -> tuple[int, int, int]: + match = _VERSION_RE.match(value.strip()) + if not match: + raise ValueError(f"Invalid semantic version: {value!r}") + return int(match.group(1)), int(match.group(2)), int(match.group(3)) + + +def format_version(major: int, minor: int, patch: int) -> str: + return f"{major}.{minor}.{patch}" + + +def clear_version_cache() -> None: + get_app_version.cache_clear() + + +def write_project_version(version: str) -> None: + parse_version(version) + _VERSION_FILE.write_text(f"{version}\n", encoding="utf-8") + + pyproject = _PYPROJECT_FILE.read_text(encoding="utf-8") + pyproject, count = re.subn( + r'^version = ".*"$', + f'version = "{version}"', + pyproject, + count=1, + flags=re.MULTILINE, + ) + if count != 1: + raise RuntimeError("Failed to update pyproject.toml version") + _PYPROJECT_FILE.write_text(pyproject, encoding="utf-8") + + chart = _CHART_FILE.read_text(encoding="utf-8") + chart, chart_count = re.subn( + r"^version: .*$", + f"version: {version}", + chart, + count=1, + flags=re.MULTILINE, + ) + chart, app_count = re.subn( + r'^appVersion: ".*"$', + f'appVersion: "{version}"', + chart, + count=1, + flags=re.MULTILINE, + ) + if chart_count != 1 or app_count != 1: + raise RuntimeError("Failed to update Chart.yaml version") + _CHART_FILE.write_text(chart, encoding="utf-8") + + values = _VALUES_FILE.read_text(encoding="utf-8") + values, values_count = re.subn( + r'^(\s*tag:\s*)".*"$', + rf'\1"{version}"', + values, + count=1, + flags=re.MULTILINE, + ) + if values_count != 1: + raise RuntimeError("Failed to update helm values.yaml image.tag") + _VALUES_FILE.write_text(values, encoding="utf-8") + + clear_version_cache() + + +def bump_patch_version() -> str: + major, minor, patch = parse_version(get_app_version()) + version = format_version(major, minor, patch + 1) + write_project_version(version) + return version + + +def bump_minor_version() -> str: + major, minor, patch = parse_version(get_app_version()) + version = format_version(major, minor + 1, 0) + write_project_version(version) + return version diff --git a/helm/wrapped/Chart.yaml b/helm/wrapped/Chart.yaml index ad0bfdc..8e7bb5e 100644 --- a/helm/wrapped/Chart.yaml +++ b/helm/wrapped/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: wrapped description: Zero-knowledge one-time encrypted drop (Wrapped) type: application -version: 0.1.0 -appVersion: "0.1.0" +version: 0.1.1 +appVersion: "0.1.1" diff --git a/helm/wrapped/values.yaml b/helm/wrapped/values.yaml index cf105b2..7fdb91d 100644 --- a/helm/wrapped/values.yaml +++ b/helm/wrapped/values.yaml @@ -2,7 +2,7 @@ replicaCount: 1 image: repository: inecs/wrapped - tag: "0.1.0" + tag: "0.1.1" pullPolicy: IfNotPresent service: diff --git a/pyproject.toml b/pyproject.toml index 31c9664..1f6a44c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "wrapped" -version = "0.1.0" +version = "0.1.1" description = "Zero-knowledge one-time encrypted drop service" readme = "README.md" requires-python = ">=3.12" diff --git a/scripts/bump_version.py b/scripts/bump_version.py new file mode 100644 index 0000000..a75bb02 --- /dev/null +++ b/scripts/bump_version.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +"""CLI: bump project SemVer (VERSION + pyproject + Helm chart/values).""" + +from __future__ import annotations + +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) + +from app.version import bump_minor_version, bump_patch_version # noqa: E402 + + +def main(argv: list[str] | None = None) -> int: + args = argv if argv is not None else sys.argv[1:] + if len(args) != 1 or args[0] not in {"patch", "minor"}: + print("Usage: bump_version.py patch|minor", file=sys.stderr) + return 1 + version = bump_patch_version() if args[0] == "patch" else bump_minor_version() + print(version) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())