From 2f30bd5bd3713135031a2f3e0a41c71663529059 Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Mon, 27 Jul 2026 14:37:13 +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=20Jenkinsfile=20=D0=B4=D0=BB=D1=8F=20multi-arch=20push?= =?UTF-8?q?=20=D0=B2=20Harbor=20=D0=B8=20Docker=20Hub;=20=D0=BE=D0=B1?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=BB=D1=91=D0=BD=20README.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Jenkinsfile | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 24 +++++++++- 2 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..1433054 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,131 @@ +// Wrapped — CI: multi-arch (amd64+arm64) build & push to Harbor + Docker Hub +// +// Триггер: push в main (webhook Gitea → Jenkins Multibranch / Pipeline from SCM). +// +// 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) +// +// Agent: cloud kubernetes, pod template default (labels docker / dind) +// +// Образы: +// hub.antropoff.ru/devops-tools/wrapped: +// inecs/wrapped: + +pipeline { + agent { label 'docker' } + + options { + buildDiscarder(logRotator(numToKeepStr: '20')) + disableConcurrentBuilds() + timeout(time: 60, unit: 'MINUTES') + timestamps() + } + + environment { + 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}" + TZ = 'Europe/Moscow' + } + + stages { + stage('Checkout') { + steps { + checkout scm + } + } + + stage('Build & push') { + when { + anyOf { + branch 'main' + branch 'master' + } + } + steps { + withCredentials([ + usernamePassword( + credentialsId: 'harbor-devops-tools-push-pull-access', + usernameVariable: 'HARBOR_USER', + passwordVariable: 'HARBOR_PASS' + ), + usernamePassword( + credentialsId: 'docker-hub', + usernameVariable: 'DOCKERHUB_USER', + passwordVariable: 'DOCKERHUB_PASS' + ) + ]) { + container('docker') { + sh ''' + set -eux + + echo "$HARBOR_PASS" | docker login "$HARBOR_REGISTRY" -u "$HARBOR_USER" --password-stdin + echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USER" --password-stdin + + docker buildx rm "$BUILDX_BUILDER" 2>/dev/null || true + 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 \ + -t "${HARBOR_IMAGE}:${RELEASE_TAG}-amd64" \ + -t "${DOCKERHUB_IMAGE}:${RELEASE_TAG}-amd64" \ + -f Dockerfile . + + docker buildx build \ + --platform linux/arm64 \ + --provenance=false --sbom=false --push \ + -t "${HARBOR_IMAGE}:${RELEASE_TAG}-arm64" \ + -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}" \ + -t "${IMAGE}:${IMAGE_VERSION}" \ + -t "${IMAGE}:latest" \ + "${IMAGE}:${RELEASE_TAG}-amd64" \ + "${IMAGE}:${RELEASE_TAG}-arm64" + done + + echo "--- Harbor ---" + docker buildx imagetools inspect "${HARBOR_IMAGE}:${RELEASE_TAG}" | sed -n '1,40p' + echo "--- Docker Hub ---" + docker buildx imagetools inspect "${DOCKERHUB_IMAGE}:${RELEASE_TAG}" | sed -n '1,40p' + + docker buildx rm "$BUILDX_BUILDER" || true + ''' + } + } + } + } + } + + post { + success { + echo "✓ Harbor: ${HARBOR_IMAGE}:{${RELEASE_TAG},${IMAGE_VERSION},latest}" + echo "✓ Docker Hub: ${DOCKERHUB_IMAGE}:{${RELEASE_TAG},${IMAGE_VERSION},latest}" + } + failure { + echo "✗ Сборка или push не удались — см. лог стадии Build & push" + } + always { + script { + try { + container('docker') { + sh 'docker buildx rm "$BUILDX_BUILDER" 2>/dev/null || true' + } + } catch (Ignored) { + // agent may already be gone + } + } + } + } +} diff --git a/README.md b/README.md index 78497ee..06e4e3a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ - RU / EN, светлая и тёмная тема - CAPTCHA: Cloudflare Turnstile и/или hCaptcha - админка: **статистика**, настройки, audit (пагинация + очистка), danger purge -- Docker Compose и Helm; multi-arch образ (`amd64`/`arm64`) на Docker Hub: [`inecs/wrapped`](https://hub.docker.com/r/inecs/wrapped) +- Docker Compose и Helm; multi-arch образ (`amd64`/`arm64`) на Docker Hub: [`inecs/wrapped`](https://hub.docker.com/r/inecs/wrapped) и Harbor `hub.antropoff.ru/devops-tools/wrapped` (Jenkins CI) Ссылка для получателя: `/w/#` или токен `wrapped_v1..`. Ключ шифрования в URL-фрагменте (`#...`) на сервер не уходит. @@ -27,7 +27,7 @@ | **Расшифровка** | Прокрутка превью текста по вертикали и горизонтали; кнопка **Копировать** рядом со скачиванием | | **Пароль** | Argon2-проверка *до* выдачи ciphertext; лимит попыток; пустой пароль попытку не тратит; структурированные ошибки в UI | | **Админка** | Стартовая **Статистика** (MinIO, wraps, audit); очистка audit с подтверждением; пагинация журнала | -| **Релиз** | `make release` собирает **multi-arch** манифест `linux/amd64` + `linux/arm64` через Buildx и пушит только `inecs/wrapped:` | +| **Релиз** | `make release` / Jenkins CI: **multi-arch** `linux/amd64` + `linux/arm64` → Harbor `devops-tools/wrapped` и Docker Hub `inecs/wrapped` (`:sha`, `:0.1.0`, `:latest`) | Подробности — в разделах ниже (безопасность, админка, сборка образа). @@ -423,6 +423,26 @@ docker buildx build \ После успешного push Makefile печатает краткий `imagetools inspect` (платформы в манифесте). Образ при старте: `alembic upgrade head` + uvicorn `:8000`. Healthcheck: `GET /health`. +### Jenkins CI (Harbor + Docker Hub) + +После push в ветку **`main`** Jenkins собирает multi-arch образ (`linux/amd64` + `linux/arm64`) и пушит в оба реестра: + +| Реестр | Образ | +|--------|--------| +| Harbor | `hub.antropoff.ru/devops-tools/wrapped` | +| Docker Hub | `inecs/wrapped` | + +Теги на каждый реестр: `:`, `:0.1.0`, `:latest` (плюс служебные `:-amd64` / `:-arm64` для сборки манифеста). + +Credentials в Jenkins: + +| ID | Тип | Назначение | +|----|-----|------------| +| `harbor-antropoff` | Username/password | robot для `hub.antropoff.ru` | +| `dockerhub-inecs` | Username/password | Docker Hub `inecs` + access token | + +Job: Multibranch Pipeline или Pipeline from SCM на этот репозиторий + webhook на push. Pipeline описан в корневом [`Jenkinsfile`](Jenkinsfile). + ### Git (`make push`) Как в proxmox_api_simulator: