Перейти к содержанию

Управление репозиториями и артефактами

Контекст

CodeScoring.Save хранит и раздаёт артефакты для команды разработки: сборщик публикует пакет, а IDE, CI-агенты и пользователи скачивают его по тому же URL, не выходя за периметр компании.

Все артефакты лежат в репозиториях, репозитории сгруппированы в проекты. Это та же иерархия, что и в интерфейсе:

Project (например, backend-team)
└── Repository (например, maven-central)
    └── Artifact (например, commons-lang3:3.12.0)

Проект — это контейнер, в котором живут репозитории команды. Репозиторий — конкретное хранилище одного формата (Maven, npm, Docker и т.д.) одного из двух типов. Артефакт — то, что в нём лежит и что качают клиенты.

Два типа репозиториев

CodeScoring.Save поддерживает только два типа репозиториев, и каждый решает свою задачу.

Proxy-репозиторий — кэширующий прокси к внешнему источнику (например, Maven Central или npmjs.com). Когда клиент впервые запрашивает артефакт, Save качает его из upstream, кэширует и отдаёт. Все последующие запросы идут уже из кэша — это ускоряет сборки и снижает зависимость от внешних сервисов. На proxy-репозитории дополнительно работают политики безопасности OSA Proxy (если он настроен), которые могут заблокировать скачивание небезопасных компонентов.

Hosted-репозиторий — собственное хранилище, в которое команда публикует свои артефакты. Сюда попадают внутренние библиотеки, проверенные сторонние компоненты и build-artifacts.

В одном проекте может быть произвольное количество репозиториев обоих типов.

URL-схема доступа

Внешний клиент (Maven, npm, Docker и т.д.) обращается к репозиторию по URL вида:

https://save.example.com/<format>/<project>/<repository>/<artifact-path>
Формат Шаблон
Maven https://save.example.com/maven/<project>/<repository>/<group>/<artifact>/<version>/<file>
npm https://save.example.com/npm/<project>/<repository>/<package>
NuGet https://save.example.com/nuget/<project>/<repository>/v3/index.json
PyPI https://save.example.com/pypi/<project>/<repository>/simple/
Go https://save.example.com/go/<project>/<repository> (как GOPROXY)
Raw https://save.example.com/raw/<project>/<repository>/<filepath>
OCI / Docker https://save.example.com/v2/<project>/<repository>/<image>/...

Для OCI / Docker используется стандартный префикс /v2/, как требует OCI Distribution Spec. Дополнительно поддерживается Nexus-совместимая маршрутизация плоских URL — см. Работа с OCI / docker.

Структура веб-интерфейса

Слева расположена вертикальная навигация со следующими разделами:

  • Projects — основной раздел: список проектов, внутри каждого — список репозиториев, внутри каждого репозитория — дерево артефактов;
  • Cleanup — политики автоматической очистки ненужных артефактов;
  • Settings — управление учётными записями, ролями, сервисными аккаунтами, конфигурацией и журналом аудита.

В верхней части интерфейса находится глобальный поиск — открывается по клавише / и ищет одновременно по проектам, репозиториям и артефактам с подсказками в реальном времени.

Базовый сценарий: подключить свой первый репозиторий

Шаг 1. Создайте проект

Проект — это контейнер, в котором будут жить репозитории. Удобно делить проекты по командам, продуктам или окружениям.

  1. В боковом меню выберите Projects.
  2. Нажмите кнопку Create project в правом верхнем углу.
  3. Заполните форму:
    • Name — человеко-читаемое название проекта (например, Backend Team);
    • Color — цвет для визуального различения проектов в списке;
    • Key — URL-safe идентификатор проекта (например, backend-team). Если оставить пустым, будет сгенерирован автоматически из Name, даже если Name не использует латиницу. Изменить Key после создания нельзя;
    • Description — необязательное описание проекта;
    • Cleanup policy — раздел двухпанельного селектора с уже созданными политиками очистки. Можно оставить пустым и привязать политики позже.
  4. Нажмите Create project.

После создания откроется страница проекта со списком репозиториев (пока пустым) и его метаданными в шапке.

Key и Name

Name отображается в интерфейсе и может быть изменено в любой момент. Key участвует во всех URL и API-путях, поэтому его смена потребовала бы переподписки клиентов.

Шаг 2. Создайте репозиторий в проекте

Внутри проекта создаётся репозиторий — конкретное хранилище для артефактов одного формата.

  1. Откройте только что созданный проект.
  2. Нажмите Create repository в правом верхнем углу страницы проекта.
  3. Заполните общие поля:
    • Name — человеко-читаемое название (например, Maven Central (proxy));
    • Color — цвет для удобства;
    • Key — URL-safe идентификатор (например, maven-central). Изменить после создания нельзя;
    • Description — необязательное описание.
  4. Выберите Format — один из поддерживаемых пакетных форматов: maven, npm, docker (OCI), nuget, pypi, go, raw. После создания формат поменять нельзя.
  5. Выберите Type:
    • Proxy — для проксирования внешнего реестра;
    • Hosted — для собственного хранилища.
  6. Если выбран тип Proxy, появятся дополнительные поля:
    • Proxy URL — адрес upstream-репозитория (например, https://repo1.maven.org/maven2/);
    • Cache TTL, seconds — время жизни кэша метаданных в секундах. Для типичных внешних реестров достаточно 86400 (сутки).
  7. При необходимости привяжите Cleanup policy — список уже созданных политик автоматической очистки.
  8. Нажмите Create repository.

После создания откроется страница репозитория. В шапке отображается статус (Enabled/Disabled), формат, тип, даты создания и обновления, а для proxy-репозитория — кликабельная ссылка Remote URL, ведущая на upstream.

Изменение статуса репозитория

Статус (Enabled/Disabled) задаётся на форме редактирования: откройте репозиторий, нажмите Edit repository в шапке, переключите верхний радио-переключатель Status, нажмите Save. Disabled-репозиторий не отдаёт и не принимает запросы, но не удаляется и не теряет своих артефактов.

Шаг 3. Получите готовые сниппеты для подключения клиентов

На странице репозитория в правой части шапки находится кнопка со значком «звено цепи». По клику она открывает popover Useful snippets — набор готовых фрагментов конфигурации и команд, который зависит от формата репозитория.

  1. Нажмите кнопку звено цепи в шапке репозитория.
  2. В появившейся карточке прокрутите список сниппетов:
    • каждый сниппет состоит из заголовка (например, settings.xml, .npmrc, pip.conf, docker login), блока кода с подсветкой синтаксиса и краткого описания;
    • набор сниппетов формируется на стороне сервера по формату и типу репозитория. Для Maven обычно показывается фрагмент <mirror> для settings.xml и <repository> для pom.xml; для Docker — docker login и docker pull; для npm — строка для .npmrc; и так далее.
  3. Нажмите кнопку Copy справа от нужного сниппета — он скопируется в буфер обмена.
  4. Вставьте сниппет в конфигурационный файл клиента или выполните команду в терминале.

Шаг 4. Опубликуйте и скачайте первый артефакт

Дальше всё происходит уже на стороне клиента — Save принимает стандартные запросы пакетного менеджера. Для hosted-репозитория обычный путь — публикация через mvn deploy, npm publish, twine upload или docker push. Для proxy-репозитория — обычный pull, после которого артефакт окажется в кэше Save.

При первой загрузке может потребоваться аутентификация — см. раздел Подключение клиентов и аутентификация.

После того, как клиент впервые что-то загрузит или скачает, артефакт появится в дереве на странице репозитория.

Работа с артефактами в репозитории

Страница репозитория состоит из двух колонок: слева — дерево артефактов с поиском и сортировкой, справа — детали выбранного артефакта.

Просмотр дерева

Дерево показывает структуру репозитория: папки и файлы с иконками. Файлы со значком «замок» — это «защищённые» (locked/release) артефакты, которые нельзя удалить или перезаписать без дополнительных шагов. Раскрытие папок происходит лениво: дочерние элементы подгружаются по клику.

При клике на файл его детали открываются справа: имя, размер, путь, контрольная сумма, метаданные формата (например, Maven groupId/artifactId/version или Docker tags).

Поиск, сортировка и фильтры

Над деревом расположено поле Search artifacts: введите подстроку и нажмите Enter — появится плоский список найденных артефактов с подсветкой контекста. Клик по строке откроет деталки соответствующего артефакта.

Рядом с поиском расположены две кнопки.

Кнопка Sort управляет сортировкой дерева:

  • Sort byName, Date added или Date modified;
  • Sort directionA to Z или Z to A.

Кнопка-фильтр (иконка воронки) для proxy-репозиториев открывает дополнительный переключатель Show uncached artifacts. По умолчанию дерево показывает только то, что уже лежит в локальном кэше. С включённым флагом Save также подгружает в дерево артефакты, известные upstream, но ещё не скачанные — это удобно, чтобы предварительно посмотреть, что доступно в проксируемом источнике, не запуская реальный pull. Это доступно не для всех форматов репозиториев из-за различия протоколов.

Действия над одним артефактом

После выбора артефакта в правой панели доступны:

  • Download — скачивание файла локально;
  • Lock artifact — пометить артефакт как release. После lock артефакт нельзя удалить или перезаписать обычным upload — это нужно, чтобы выпущенные версии не могли быть подменены;
  • Delete artifact — удалить файл из репозитория (для hosted) или из кэша (для proxy).

Lock — необратимая операция в UI

Снять флаг release можно только через API: PUT /api/v1/artifacts/release?id=<artifact-id> с {"is_release": false}.

Групповые действия

Слева от каждой строки дерева есть чекбокс. После выделения нескольких файлов в верхней части появляется панель с действиями:

  • Lock — массово зафиксировать выделенные артефакты;
  • Delete — массово удалить.

Обе операции атомарны для всей выборки.

Подключение клиентов и аутентификация

Save поддерживает несколько способов аутентификации, чтобы покрыть как интерактивные клиенты, так и CI-конвейеры. Конкретный способ выбирается клиентом самостоятельно через заголовок Authorization (или специальные заголовки формата).

Способ Заголовок Тип в cs-auth Когда применять
Basic Auth (пользователь) Authorization: Basic base64(<username>:<password>) basic Интерактивная работа из IDE, ручные curl и mvn/pip/npm от имени конкретного пользователя
Basic Auth (robot-аккаунт) Authorization: Basic base64(sa$<robot-name>:<api-key>) api_key CI/CD-пайплайны, runners сборки, любые сервисные интеграции
Bearer JWT Authorization: Bearer <jwt> bearer_jwt Сессионные запросы из веб-интерфейса и других сервисов после логина через cs-auth
Bearer (opaque token) Authorization: Bearer <opaque-token> npm_token Любой не-JWT bearer-токен; на практике — npm-клиент после npm adduser / npm login
X-NuGet-ApiKey X-NuGet-ApiKey: <api-key> nuget_key Только NuGet-формат, для совместимости с dotnet nuget push
Docker v2 token Authorization: Bearer <docker-jwt> docker_token OCI-клиенты после прохождения цикла 401 → retry → 200

Bearer-токен с тремя сегментами через точку классифицируется как JWT, остальные — как opaque npm-токен. Префикс sa$ в Basic Auth — это маркер service-аккаунта (robot).

Robot-аккаунты для CI/CD

Для интеграции с CI/CD не рекомендуется использовать личные пароли — они привязаны к сотруднику и быстро устаревают. Вместо этого создаётся robot-аккаунт — служебная учётная запись с отдельной ролью и долгоживущим API-ключом.

Создание через веб-интерфейс

  1. В боковом меню откройте Settings -> Robot accounts.
  2. Нажмите Create robot account в правом верхнем углу.
  3. Заполните поля:
    • Login — служебное имя для аутентификации (например, ci-builder). Save сам добавит к введённому значению префикс для service-аккаунта;
    • Name — человеко-читаемое название (например, CI Builder Bot);
    • Description — необязательное описание;
    • Expires in — дата истечения ключа. Под полем доступны быстрые пресеты Week, Month, Year. Если ключ должен жить вечно, поставьте флаг Never справа от выбора даты — поле выбора даты автоматически блокируется;
    • Global permissions — глобальные разрешения для робота;
    • Scoped permissions — разрешения, ограниченные одним, несколькими (или всеми сразу) проектами или репозиториями. Переключатель Scope выбирает уровень (Projects / Repositories), затем для каждого scope добавляются записи Add project permissions / Add repository permissions.
  4. Нажмите Create robot account.

После успешного создания на экране автоматически открывается модальное окно с API-ключом — это пароль для Basic Auth.

Окно с API-ключом

После нажатия Create robot account Save открывает окно с API-ключом. В тем доступны:

  • API key — поле в режиме «только для чтения» с самим значением ключа и кнопкой Copy, копирующей его в буфер обмена;
  • Key prefix — короткий префикс ключа. По нему можно опознать ключ в журнале аудита, не раскрывая полный секрет;
  • Expires at — дата истечения (если она задана);
  • Warning — текст с сервера, например Store this API key securely — it cannot be retrieved again.

Под секретом находится кнопка I have copied the key, которая закрывает окно и переводит на страницу деталей созданного robot-аккаунта.

API-ключ показывается только один раз

После закрытия окна посмотреть или восстановить тот же ключ нельзя — Save хранит только хэш. Скопируйте ключ и сохраните его в secret-storage CI (GitLab CI variables, GitHub Actions secrets, HashiCorp Vault) до того, как закроете окно. Если ключ утерян, перевыпустите его (вручную в форме редактирования robot-аккаунта или через API POST /admin/robots/<id>/rotate-key) — после ротации старое значение становится недействительным.

Создание через API

curl -X POST https://save.example.com/api/v1/admin/robots \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <admin-jwt>" \
  -d '{
    "username": "ci-builder",
    "display_name": "CI Builder Bot",
    "description": "Robot account for CI/CD pipelines",
    "permissions": {
      "project": {
        "backend-team": ["project_artifacts.download", "project_artifacts.upload"]
      }
    },
    "key_expires_in_seconds": 7776000
  }'

В ответе вернётся api_key, который выдаётся только один раз при создании — сохраните его в secret-storage CI.

Использование в клиенте

username = sa$<robot-name>, password = <api-key> — обычный Basic Auth:

curl -u "sa\$ci-builder:<api-key>" https://save.example.com/...

Экранирование $ в shell

В bash символ $ в одинарных кавычках сохраняется как есть, а в двойных — экранируется как \$. В YAML / TOML / XML конфигах экранирование не требуется.

API-ключ NuGet — особенность реализации

Заголовок X-NuGet-ApiKey поддерживается напрямую: cs-auth классифицирует его как тип nuget_key и валидирует по 12-символьному префиксу ключа. Для NuGet работают оба варианта, но мы рекомендуем Basic Auth с robot-аккаунтом — это единообразно с остальными форматами, и в журнале аудита явно отражается личность robot'а.

Анонимный доступ на чтение

Если включён глобальный флаг AllowAnonymousRead, неаутентифицированным запросам выдаётся PermissionSet с правами projects.view, project_repos.view, project_artifacts.view, project_artifacts.download для всех проектов. Для Docker / OCI этот режим всё равно требует цикла 401 → /v2/token → 200, иначе Docker-клиент не сможет работать (см. Работа с OCI / docker).

Текущее значение флага видно в Settings -> Configuration. Изменение значения выполняется через API:

curl -X PUT https://save.example.com/api/v1/admin/config \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <admin-jwt>" \
  -d '{"key": "AllowAnonymousRead", "value": "true"}'

Политики очистки

Чтобы хранилище не разрасталось бесконечно, на репозитории привязываются политики очистки — правила автоматического удаления (или удержания) артефактов. Save поддерживает пять типов политик; для большинства задач достаточно простых, для сложных AND/OR-условий используется тип expression.

Создание политики

  1. В боковом меню откройте Cleanup.
  2. Нажмите Create policy.
  3. Заполните общие поля: Display name, Description, при необходимости — Schedule (cron-выражение из пяти полей: минуты, часы, день, месяц, день недели).
  4. Выберите Policy type — определяет основное правило очистки:
Тип политики Назначение Параметр value
delete-snapshots-older-than Удалять SNAPSHOT/dev-версии старше N дней количество дней, например 30
keep-latest-versions Оставлять только N последних версий каждого артефакта, остальное удалять N, например 5
delete-by-age Удалять любые артефакты старше N дней количество дней
delete-by-size Удалять самые старые артефакты при превышении лимита размера размер в гигабайтах целым числом, например 50
expression Гибкое DSL-правило с AND/OR-комбинацией критериев вместо value передаётся объект expression
  1. Укажите Formats, к которым применяется политика. Если оставить пустым, политика будет применяться ко всем форматам.
  2. Поставьте флаг Active, чтобы политика начала работать сразу.
  3. Для типа expression дополнительно соберите дерево критериев — каждый критерий это сочетание type, оператора сравнения (eq, ne, gt, lt, matches, contains) и значения; критерии можно объединять группами с логическим оператором AND или OR.
  4. Нажмите Create policy.

Доступные критерии для expression

Полный список — GET /api/v1/enums/cleanup-criterion-types.

Группа type Назначение
Возраст created_before Возраст артефакта (дней)
last_downloaded_days Дней с момента последней загрузки
not_downloaded_since Никогда не скачивался или не скачивался с указанной даты
Версия version_pattern Glob-паттерн по версии
is_snapshot, is_prerelease, is_release Маркеры snapshot / pre-release / release
Имя/путь name_pattern, path_pattern Glob-паттерны
Размер size_greater_than, size_less_than Размер артефакта
Docker docker_tag, docker_untagged Тег / отсутствие тега
Maven maven_classifier, maven_packaging Classifier / packaging
npm npm_scope Скоуп пакета
PyPI pypi_package_type sdist, bdist_wheel, …
NuGet nuget_is_prerelease NuGet-флаг pre-release
OCI oci_artifact_type Тип OCI-артефакта (Helm, Cosign, SBOM, …)
Retention keep_latest Оставлять N последних версий

Привязка политики к репозиторию

Привязка происходит на форме создания или редактирования репозитория (или проекта — тогда политика каскадно применяется ко всем его репозиториям).

  1. Откройте репозиторий и нажмите Edit в шапке.
  2. Прокрутите до раздела Cleanup policy.
  3. В левой колонке (Available) — все существующие политики, в правой (Applied) — уже привязанные. Перенесите нужные политики стрелкой между колонками.
  4. Сохраните изменения.

Ручной запуск и предварительный просмотр через API

# Сухой прогон — посмотреть, что было бы удалено, без сохранения политики
curl -X POST https://save.example.com/api/v1/cleanup/preview \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "repository_id": 42,
    "policy_type": "delete-snapshots-older-than",
    "value": "30"
  }'
# Запуск уже сохранённых политик (с опциональным dry_run)
curl -X POST "https://save.example.com/api/v1/cleanup/execute?project=backend-team" \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "policies": ["delete-old-snapshots", "keep-latest-5"],
    "dry_run": true
  }'

Управление доступом

Управление пользователями, ролями и правами на проекты и репозитории выполняется отдельным сервисом cs-auth. API проксируется через Save под префиксами /api/v1/auth/* и /api/v1/admin/*. Способы аутентификации описаны выше в разделе Подключение клиентов и аутентификация.

В веб-интерфейсе нужные разделы находятся в Settings:

  • Roles — описания ролей с набором разрешений;
  • Users — учётные записи людей;
  • Robot accounts — служебные учётные записи (см. выше).

API cs-auth

Конкретные эндпоинты для управления пользователями, ролями и project membership — это поверхность API сервиса cs-auth. Save прозрачно их форвардит, но в своём swagger не описывает.

Уровни доступа

Права в CodeScoring.Save гранулярны, в формате <resource>.<action>. Роли в cs-auth определяются пользователем и представляют собой набор таких разрешений.

На уровне репозитория (артефакты внутри репозитория):

Permission Что разрешает
artifacts.view Просмотр списка и метаданных артефактов
artifacts.download Скачивание артефактов
artifacts.upload Публикация артефактов (актуально для hosted)
artifacts.delete Удаление артефактов
artifacts.lock Lock/unlock одного или всех артефактов

На уровне репозитория (сам репозиторий):

Permission Что разрешает
repos.view Просмотр настроек репозитория и статистики
repos.edit Изменение настроек репозитория
repos.delete Удаление репозитория

На уровне проекта (cascade-permissions, распространяются на все репозитории и артефакты проекта):

Permission Что разрешает
projects.view, projects.create, projects.edit, projects.delete Управление проектами
project_repos.view, project_repos.create, project_repos.delete, project_repos.edit Управление всеми репозиториями проекта
project_artifacts.view, project_artifacts.upload, project_artifacts.download, project_artifacts.delete, project_artifacts.lock, project_artifacts.unlock Все операции с артефактами в любом репозитории проекта

Глобальные (только через global scope роли):

Permission Что разрешает
cleanup.view, cleanup.edit, cleanup.delete Управление политиками очистки
roles.create, roles.delete Управление ролями
users.create, users.delete Управление пользователями
config.view, config.manage Просмотр и редактирование конфигурации
audit.view Просмотр журнала аудита

Cascade-логика

Project-scope permissions автоматически cascade на repository-scope: пользователь с project_artifacts.download на проекте получит право artifacts.download на любой репозиторий внутри этого проекта. Для wildcard-доступа ко всем проектам / репозиториям используется ключ *.

Привязка пользователя или robot-аккаунта к проекту

Привязка участника к проекту — это назначение конкретной роли конкретному пользователю или robot-аккаунту в рамках одного проекта. Назначенный участник получает все разрешения роли, а в шапке проекта он отображается как member.

Через веб-интерфейс

Раздел в разработке

Раздел Members на странице проекта пока находится в разработке. Backend поддерживает все необходимые операции (/api/v1/admin/projects/<project>/members), но в UI вкладки Members на странице проекта ещё нет.

До появления вкладки выполните операции через API ниже.

Через API

Добавление участника:

curl -X POST https://save.example.com/api/v1/admin/projects/backend-team/members \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <admin-jwt>" \
  -d '{
    "user_id": 5,
    "role_id": 2
  }'

Для пользователей role_id обязателен, для robot-аккаунтов (флаг is_service=true) — может быть опущен.

Просмотр списка участников:

curl -u "<admin-username>:<admin-password>" \
  "https://save.example.com/api/v1/admin/projects/backend-team/members?limit=50"

Смена роли существующему участнику:

curl -X PUT https://save.example.com/api/v1/admin/projects/backend-team/members/5 \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <admin-jwt>" \
  -d '{"role_id": 3}'

Удаление участника из проекта:

curl -X DELETE https://save.example.com/api/v1/admin/projects/backend-team/members/5 \
  -H "Authorization: Bearer <admin-jwt>"

Те же действия через API

Каждое действие, описанное выше для интерфейса, доступно и через REST API. Это полезно для скриптов первоначальной настройки, инфраструктуры как кода (Terraform / Ansible) и интеграционных тестов.

Создание проекта

curl -X POST https://save.example.com/api/v1/projects \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "name": "backend-team",
    "display_name": "Backend Team",
    "description": "Backend services and microservices",
    "color": "#4A90D9"
  }'

Создание Proxy-репозитория

curl -X POST https://save.example.com/api/v1/repos \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "project": "backend-team",
    "name": "maven-central",
    "display_name": "Maven Central (proxy)",
    "format": "maven",
    "repository_type": "proxy",
    "remote_url": "https://repo1.maven.org/maven2/",
    "cache_ttl": 86400
  }'

Создание Hosted-репозитория

curl -X POST https://save.example.com/api/v1/repos \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "project": "backend-team",
    "name": "internal-maven",
    "display_name": "Internal Maven",
    "format": "maven",
    "repository_type": "hosted"
  }'

Назначение политики очистки

curl -X POST "https://save.example.com/api/v1/cleanup/assignments?project=backend-team" \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "policies": [
      {"policy_name": "delete-old-snapshots", "priority": 10, "enabled": true}
    ]
  }'

Создание политики очистки

curl -X POST https://save.example.com/api/v1/cleanup/policies \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "name": "delete-old-snapshots",
    "display_name": "Delete Old Maven Snapshots",
    "description": "Удалять SNAPSHOT-версии Maven старше 30 дней",
    "policy_type": "delete-snapshots-older-than",
    "value": "30",
    "formats": ["maven"],
    "enabled": true,
    "schedule": "0 2 * * *"
  }'

Создание политики на основе выражения

curl -X POST https://save.example.com/api/v1/cleanup/policies \
  -H "Content-Type: application/json" \
  -u "<username>:<password>" \
  -d '{
    "name": "clean-old-prereleases",
    "display_name": "Clean Old Pre-releases",
    "policy_type": "expression",
    "formats": ["maven", "npm"],
    "expression": {
      "action": "delete",
      "logical_operator": "and",
      "criteria": [
        {"type": "is_prerelease", "value": "true"},
        {"type": "created_before", "value": "14"}
      ]
    },
    "enabled": true
  }'

Мониторинг

Журнал аудита

Журнал аудита фиксирует все изменения в проектах, репозиториях, артефактах, ролях и robot-аккаунтах. В UI он доступен:

  • ГлобальноSettings -> Audit log;
  • По проекту — на странице проекта в шапке справа есть иконка журнала, ведущая на отфильтрованный по проекту журнал.

Каждая запись содержит время события, инициатора, тип ресурса и его идентификатор, тип действия и набор полей с подробностями (имя загруженного артефакта, изменённые параметры репозитория и т.п.). Записи группируются по дням и отображаются в виде таймлайна, а ссылки внутри записи кликабельны (открывается соответствующий проект, репозиторий, политика, пользователь или роль).

Журнал можно выгрузить за период через кнопку Export в шапке (форматы JSON или HTML).

# Аудит-журнал по конкретному репозиторию через API
curl -u "<username>:<password>" \
  "https://save.example.com/api/v1/admin/audit?resource_type=repository&q=maven-central&limit=50"

Статистика репозитория

Текущие агрегированные показатели по репозиторию доступны через API:

curl https://save.example.com/api/v1/repos/<repository_id>/stats \
  -u "<username>:<password>"

Ответ:

{
  "id": 42,
  "name": "maven-central",
  "project_name": "backend-team",
  "artifact_count": 1523,
  "total_size": 16413032448,
  "cache_hit_ratio": 0.85,
  "last_activity": "2026-03-17T10:30:00Z"
}

Поля статистики

Эндпоинт возвращает только агрегированные показатели. Поле cache_hit_ratio присутствует только для proxy-репозиториев.

Состояние сервиса

curl https://save.example.com/health

Глобальный health-check сервиса. Отдельных health-эндпоинтов на репозиторий нет: доступность upstream проверяется фоновыми воркерами и отражается через метрики Prometheus.

Логи

Логи централизованные, в формате JSON. Фильтрация по конкретному репозиторию — через стандартные средства log-aggregator'а.

Страница была полезна?