Управление репозиториями и артефактами
Контекст
CodeScoring.Save хранит и раздаёт артефакты для команды разработки: сборщик публикует пакет, а IDE, CI-агенты и пользователи скачивают его по тому же URL, не выходя за периметр компании.
Все артефакты лежат в репозиториях, репозитории сгруппированы в проекты. Это та же иерархия, что и в интерфейсе:
Проект — это контейнер, в котором живут репозитории команды. Репозиторий — конкретное хранилище одного формата (Maven, npm, Docker и т.д.) одного из двух типов. Артефакт — то, что в нём лежит и что качают клиенты.
Два типа репозиториев
CodeScoring.Save поддерживает только два типа репозиториев, и каждый решает свою задачу.
Proxy-репозиторий — кэширующий прокси к внешнему источнику (например, Maven Central или npmjs.com). Когда клиент впервые запрашивает артефакт, Save качает его из upstream, кэширует и отдаёт. Все последующие запросы идут уже из кэша — это ускоряет сборки и снижает зависимость от внешних сервисов. На proxy-репозитории дополнительно работают политики безопасности OSA Proxy (если он настроен), которые могут заблокировать скачивание небезопасных компонентов.
Hosted-репозиторий — собственное хранилище, в которое команда публикует свои артефакты. Сюда попадают внутренние библиотеки, проверенные сторонние компоненты и build-artifacts.
В одном проекте может быть произвольное количество репозиториев обоих типов.
URL-схема доступа
Внешний клиент (Maven, npm, Docker и т.д.) обращается к репозиторию по URL вида:
Для OCI / Docker используется стандартный префикс /v2/, как требует OCI Distribution Spec. Дополнительно поддерживается Nexus-совместимая маршрутизация плоских URL — см. Работа с OCI / docker.
Структура веб-интерфейса
Слева расположена вертикальная навигация со следующими разделами:
- Projects — основной раздел: список проектов, внутри каждого — список репозиториев, внутри каждого репозитория — дерево артефактов;
- Cleanup — политики автоматической очистки ненужных артефактов;
- Settings — управление учётными записями, ролями, сервисными аккаунтами, конфигурацией и журналом аудита.
В верхней части интерфейса находится глобальный поиск — открывается по клавише / и ищет одновременно по проектам, репозиториям и артефактам с подсказками в реальном времени.
Базовый сценарий: подключить свой первый репозиторий
Шаг 1. Создайте проект
Проект — это контейнер, в котором будут жить репозитории. Удобно делить проекты по командам, продуктам или окружениям.
- В боковом меню выберите Projects.
- Нажмите кнопку Create project в правом верхнем углу.
- Заполните форму:
- Name — человеко-читаемое название проекта (например,
Backend Team); - Color — цвет для визуального различения проектов в списке;
- Key — URL-safe идентификатор проекта (например,
backend-team). Если оставить пустым, будет сгенерирован автоматически из Name, даже если Name не использует латиницу. Изменить Key после создания нельзя; - Description — необязательное описание проекта;
- Cleanup policy — раздел двухпанельного селектора с уже созданными политиками очистки. Можно оставить пустым и привязать политики позже.
- Name — человеко-читаемое название проекта (например,
- Нажмите Create project.
После создания откроется страница проекта со списком репозиториев (пока пустым) и его метаданными в шапке.
Name отображается в интерфейсе и может быть изменено в любой момент. Key участвует во всех URL и API-путях, поэтому его смена потребовала бы переподписки клиентов.
Шаг 2. Создайте репозиторий в проекте
Внутри проекта создаётся репозиторий — конкретное хранилище для артефактов одного формата.
- Откройте только что созданный проект.
- Нажмите Create repository в правом верхнем углу страницы проекта.
- Заполните общие поля:
- Name — человеко-читаемое название (например,
Maven Central (proxy)); - Color — цвет для удобства;
- Key — URL-safe идентификатор (например,
maven-central). Изменить после создания нельзя; - Description — необязательное описание.
- Name — человеко-читаемое название (например,
- Выберите Format — один из поддерживаемых пакетных форматов:
maven,npm,docker(OCI),nuget,pypi,go,raw. После создания формат поменять нельзя. - Выберите Type:
- Proxy — для проксирования внешнего реестра;
- Hosted — для собственного хранилища.
- Если выбран тип Proxy, появятся дополнительные поля:
- Proxy URL — адрес upstream-репозитория (например,
https://repo1.maven.org/maven2/); - Cache TTL, seconds — время жизни кэша метаданных в секундах. Для типичных внешних реестров достаточно
86400(сутки).
- Proxy URL — адрес upstream-репозитория (например,
- При необходимости привяжите Cleanup policy — список уже созданных политик автоматической очистки.
- Нажмите Create repository.
После создания откроется страница репозитория. В шапке отображается статус (Enabled/Disabled), формат, тип, даты создания и обновления, а для proxy-репозитория — кликабельная ссылка Remote URL, ведущая на upstream.
Статус (Enabled/Disabled) задаётся на форме редактирования: откройте репозиторий, нажмите Edit repository в шапке, переключите верхний радио-переключатель Status, нажмите Save. Disabled-репозиторий не отдаёт и не принимает запросы, но не удаляется и не теряет своих артефактов.
Шаг 3. Получите готовые сниппеты для подключения клиентов
На странице репозитория в правой части шапки находится кнопка со значком «звено цепи». По клику она открывает popover Useful snippets — набор готовых фрагментов конфигурации и команд, который зависит от формата репозитория.
- Нажмите кнопку звено цепи в шапке репозитория.
- В появившейся карточке прокрутите список сниппетов:
- каждый сниппет состоит из заголовка (например,
settings.xml,.npmrc,pip.conf,docker login), блока кода с подсветкой синтаксиса и краткого описания; - набор сниппетов формируется на стороне сервера по формату и типу репозитория. Для Maven обычно показывается фрагмент
<mirror>дляsettings.xmlи<repository>дляpom.xml; для Docker —docker loginиdocker pull; для npm — строка для.npmrc; и так далее.
- каждый сниппет состоит из заголовка (например,
- Нажмите кнопку Copy справа от нужного сниппета — он скопируется в буфер обмена.
- Вставьте сниппет в конфигурационный файл клиента или выполните команду в терминале.
Шаг 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 by —
Name,Date addedилиDate modified; - Sort direction —
A to ZилиZ to A.
Кнопка-фильтр (иконка воронки) для proxy-репозиториев открывает дополнительный переключатель Show uncached artifacts. По умолчанию дерево показывает только то, что уже лежит в локальном кэше. С включённым флагом Save также подгружает в дерево артефакты, известные upstream, но ещё не скачанные — это удобно, чтобы предварительно посмотреть, что доступно в проксируемом источнике, не запуская реальный pull. Это доступно не для всех форматов репозиториев из-за различия протоколов.
Действия над одним артефактом
После выбора артефакта в правой панели доступны:
- Download — скачивание файла локально;
- Lock artifact — пометить артефакт как
release. После lock артефакт нельзя удалить или перезаписать обычнымupload— это нужно, чтобы выпущенные версии не могли быть подменены; - Delete artifact — удалить файл из репозитория (для hosted) или из кэша (для proxy).
Снять флаг release можно только через API: PUT /api/v1/artifacts/release?id=<artifact-id> с {"is_release": false}.
Групповые действия
Слева от каждой строки дерева есть чекбокс. После выделения нескольких файлов в верхней части появляется панель с действиями:
- Lock — массово зафиксировать выделенные артефакты;
- Delete — массово удалить.
Обе операции атомарны для всей выборки.
Подключение клиентов и аутентификация
Save поддерживает несколько способов аутентификации, чтобы покрыть как интерактивные клиенты, так и CI-конвейеры. Конкретный способ выбирается клиентом самостоятельно через заголовок Authorization (или специальные заголовки формата).
Bearer-токен с тремя сегментами через точку классифицируется как JWT, остальные — как opaque npm-токен. Префикс sa$ в Basic Auth — это маркер service-аккаунта (robot).
Robot-аккаунты для CI/CD
Для интеграции с CI/CD не рекомендуется использовать личные пароли — они привязаны к сотруднику и быстро устаревают. Вместо этого создаётся robot-аккаунт — служебная учётная запись с отдельной ролью и долгоживущим API-ключом.
Создание через веб-интерфейс
- В боковом меню откройте
Settings -> Robot accounts. - Нажмите Create robot account в правом верхнем углу.
- Заполните поля:
- 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.
- Login — служебное имя для аутентификации (например,
- Нажмите 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-аккаунта.
После закрытия окна посмотреть или восстановить тот же ключ нельзя — Save хранит только хэш. Скопируйте ключ и сохраните его в secret-storage CI (GitLab CI variables, GitHub Actions secrets, HashiCorp Vault) до того, как закроете окно. Если ключ утерян, перевыпустите его (вручную в форме редактирования robot-аккаунта или через API POST /admin/robots/<id>/rotate-key) — после ротации старое значение становится недействительным.
Создание через API
В ответе вернётся api_key, который выдаётся только один раз при создании — сохраните его в secret-storage CI.
Использование в клиенте
username = sa$<robot-name>, password = <api-key> — обычный Basic Auth:
$ в shell
В bash символ $ в одинарных кавычках сохраняется как есть, а в двойных — экранируется как \$. В YAML / TOML / XML конфигах экранирование не требуется.
Заголовок 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:
Политики очистки
Чтобы хранилище не разрасталось бесконечно, на репозитории привязываются политики очистки — правила автоматического удаления (или удержания) артефактов. Save поддерживает пять типов политик; для большинства задач достаточно простых, для сложных AND/OR-условий используется тип expression.
Создание политики
- В боковом меню откройте Cleanup.
- Нажмите Create policy.
- Заполните общие поля: Display name, Description, при необходимости — Schedule (cron-выражение из пяти полей: минуты, часы, день, месяц, день недели).
- Выберите Policy type — определяет основное правило очистки:
- Укажите Formats, к которым применяется политика. Если оставить пустым, политика будет применяться ко всем форматам.
- Поставьте флаг Active, чтобы политика начала работать сразу.
- Для типа
expressionдополнительно соберите дерево критериев — каждый критерий это сочетаниеtype, оператора сравнения (eq,ne,gt,lt,matches,contains) и значения; критерии можно объединять группами с логическим операторомANDилиOR. - Нажмите Create policy.
Доступные критерии для expression
Полный список — GET /api/v1/enums/cleanup-criterion-types.
Привязка политики к репозиторию
Привязка происходит на форме создания или редактирования репозитория (или проекта — тогда политика каскадно применяется ко всем его репозиториям).
- Откройте репозиторий и нажмите Edit в шапке.
- Прокрутите до раздела Cleanup policy.
- В левой колонке (Available) — все существующие политики, в правой (Applied) — уже привязанные. Перенесите нужные политики стрелкой между колонками.
- Сохраните изменения.
Ручной запуск и предварительный просмотр через API
Управление доступом
Управление пользователями, ролями и правами на проекты и репозитории выполняется отдельным сервисом cs-auth. API проксируется через Save под префиксами /api/v1/auth/* и /api/v1/admin/*. Способы аутентификации описаны выше в разделе Подключение клиентов и аутентификация.
В веб-интерфейсе нужные разделы находятся в Settings:
- Roles — описания ролей с набором разрешений;
- Users — учётные записи людей;
- Robot accounts — служебные учётные записи (см. выше).
Конкретные эндпоинты для управления пользователями, ролями и project membership — это поверхность API сервиса cs-auth. Save прозрачно их форвардит, но в своём swagger не описывает.
Уровни доступа
Права в CodeScoring.Save гранулярны, в формате <resource>.<action>. Роли в cs-auth определяются пользователем и представляют собой набор таких разрешений.
На уровне репозитория (артефакты внутри репозитория):
На уровне репозитория (сам репозиторий):
На уровне проекта (cascade-permissions, распространяются на все репозитории и артефакты проекта):
Глобальные (только через global scope роли):
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
Добавление участника:
Для пользователей role_id обязателен, для robot-аккаунтов (флаг is_service=true) — может быть опущен.
Просмотр списка участников:
Смена роли существующему участнику:
Удаление участника из проекта:
Те же действия через API
Каждое действие, описанное выше для интерфейса, доступно и через REST API. Это полезно для скриптов первоначальной настройки, инфраструктуры как кода (Terraform / Ansible) и интеграционных тестов.
Создание проекта
Создание Proxy-репозитория
Создание Hosted-репозитория
Назначение политики очистки
Создание политики очистки
Создание политики на основе выражения
Мониторинг
Журнал аудита
Журнал аудита фиксирует все изменения в проектах, репозиториях, артефактах, ролях и robot-аккаунтах. В UI он доступен:
- Глобально —
Settings -> Audit log; - По проекту — на странице проекта в шапке справа есть иконка журнала, ведущая на отфильтрованный по проекту журнал.
Каждая запись содержит время события, инициатора, тип ресурса и его идентификатор, тип действия и набор полей с подробностями (имя загруженного артефакта, изменённые параметры репозитория и т.п.). Записи группируются по дням и отображаются в виде таймлайна, а ссылки внутри записи кликабельны (открывается соответствующий проект, репозиторий, политика, пользователь или роль).
Журнал можно выгрузить за период через кнопку Export в шапке (форматы JSON или HTML).
Статистика репозитория
Текущие агрегированные показатели по репозиторию доступны через API:
Ответ:
Эндпоинт возвращает только агрегированные показатели. Поле cache_hit_ratio присутствует только для proxy-репозиториев.
Состояние сервиса
Глобальный health-check сервиса. Отдельных health-эндпоинтов на репозиторий нет: доступность upstream проверяется фоновыми воркерами и отражается через метрики Prometheus.
Логи
Логи централизованные, в формате JSON. Фильтрация по конкретному репозиторию — через стандартные средства log-aggregator'а.
