Skip to content

Продвижение артефактов и разрешение версий ​

Решение: ADR 0047. Схема БД 22.

Сборка идёт от CI к production двумя совместимыми способами.

  • Стадии — управляемые метки артефакта (qa, staging, release, prod). Их может быть несколько, до 16. Меняются только через promotion API, каждое изменение попадает в журнал.
  • Репозитории-этапы — артефакт переходит между репозиториями (dev → staging → prod) без повторной передачи байтов, в режиме copy или move.

Свободные метки (labels) остаются тегами и не заменяют стадии: их меняет любой с правом annotation.write, истории у них нет.

Права ​

ДействиеManaged-ключFile key, пользователь или группа
Список стадий, история артефактаartifact.readread
Журнал репозитория, артефакты со стадиейartifact.listread
Добавить или снять стадиюartifact.promote + artifact.readread + write
Promote copyв источнике artifact.read + content.read, в цели artifact.promoteread в источнике, write в цели
Promote moveдополнительно artifact.promote в источникеwrite в обоих
packages/resolvepackage.readread
packages/content (скачать выбранную версию)content.readread

Ключу агента развёртывания достаточно content.read: он скачивает «последнюю версию в release», не получая доступа к каталогу.

Стадии ​

sh
curl -X PUT -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"comment":"smoke passed"}' \
  "$ARKVORY/api/v1/repositories/dev/artifacts/$ID/stages/qa"
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
  "$ARKVORY/api/v1/repositories/dev/artifacts/$ID/stages/qa"
  • Имя стадии: ^[a-z0-9][a-z0-9_.-]{0,31}$.
  • Повторное добавление не меняет время и комментарий. Снятие отсутствующей стадии возвращает 204.
  • Артефакт со стадией защищён от retention: блокер promotion_stage.
  • GET R/artifacts/{id}/stages — текущие стадии.
  • GET R/stages?stage=release — все артефакты со стадией, страницы по 100.
  • GET R/artifacts/{id}/promotions и GET R/promotions?after=N — журнал в порядке возрастания sequence. CI/CD опрашивает его с сохранённым курсором.

Перенос между репозиториями ​

sh
curl -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"target":"prod","mode":"copy","stages":["release"],"comment":"CAB-142"}' \
  "$ARKVORY/api/v1/repositories/staging/artifacts/$ID/promote"

Что происходит при переносе:

  • Байты не передаются заново: локальное хранилище создаёт жёсткую ссылку на неизменяемый blob.
  • Копия получает метки, metadata, коллекции и идентичность UPack источника, а также указанные стадии.
  • Ответ 201 возвращает новую копию. Повтор отвечает 200 с той же копией.
  • Если в цели уже есть та же версия пакета с тем же SHA-256, возвращается существующий артефакт; с другим SHA-256 — 409.
  • move логически удаляет источник в той же транзакции, что и публикация копии. Стадии источника переходят в копию. Если у источника есть блокеры удаления (внешние ссылки, история файлов, вложения), весь перенос отклоняется с 409 и ничего не публикуется.
  • Квота и ARKVORY_CAPACITY_BYTES учитывают копию как обычную загрузку. Физически байты общие до удаления последней ссылки.
  • Прерванный перенос оставляет pending-резерв с обычным сроком жизни. Повтор тем же пользователем продолжает его, иначе резерв удаляет очистка.
  • Вложения (attachments) и привязки путей файлов не переносятся.

Разрешение версии ​

GET R/packages/resolve возвращает версию, artifact ID, SHA-256, размер и стадии. GET|HEAD R/packages/content сразу отдаёт байты выбранной версии и заголовки X-Arkvory-Artifact-Id и X-Arkvory-Package-Version.

ПараметрЗначение
nameобязательное имя пакета; group — группа, по умолчанию корневая
versionточная версия (без учёта регистра)
rangeSemVer-диапазон: 1.2.3, ^1.2, ~1.2.3, 1.x, >=1.0.0 <2.0.0, 1.0.0 - 1.4.0, ^1 || ^3
stageтолько версии с этой стадией
prereleasetrue включает prerelease-версии; без него они выбираются только при точной версии или диапазоне с тем же major.minor.patch
orderversion (по умолчанию) — старшая подходящая SemVer; promoted — последняя продвинутая, требует stage

order=promoted делает откат обычной операцией: снимите стадию с неудачной версии или продвиньте прежнюю ещё раз. Повторное добавление существующей стадии время не обновляет, поэтому для отката стадию сначала снимают.

URL по имени разрешается на каждом запросе. Для продолжения скачивания передавайте If-Range с полученным ETag или скачивайте по artifactId из resolve.

sh
curl -fL -H "Authorization: Bearer $DEPLOY_TOKEN" -o app.upack \
  "$ARKVORY/api/v1/repositories/prod/packages/content?name=app&range=%5E1.4&stage=release"

CLI ​

sh
arkvoryctl promote $ID --repository staging --to prod --stage release --comment CAB-142
arkvoryctl promote $ID --repository staging --to prod --move
arkvoryctl stages add $ID qa --repository dev --comment "smoke passed"
arkvoryctl stages artifacts --repository prod --stage release
arkvoryctl packages resolve app --repository prod --range ^1.4 --stage release --json
arkvoryctl packages download app ./app.upack --repository prod --stage release --order promoted
arkvoryctl promotions journal --repository prod --after 120 --json

packages download сначала разрешает версию, затем скачивает её по artifact ID с проверкой SHA-256 и продолжением после обрыва.

Консоль ​

Карточка артефакта показывает размер, время загрузки и SHA-256, стадии с кнопками снятия, форму добавления стадии, форму продвижения (только в репозитории, где у пользователя есть artifact.promote) и историю. Перенос требует подтверждения. В списке пакетов колонка «Стадии» заполняется одним запросом GET R/stages?ids=… на страницу.

SDK ​

ts
const prod = client.inRepository('prod');
await client
  .inRepository('staging')
  .promotions.promote(id, { target: 'prod', stages: ['release'] });
const current = await prod.packages.resolve({ name: 'app', range: '^1.4', stage: 'release' });
await prod.stages.add(current.artifactId, 'canary');
const { items, next } = await prod.promotions.journal({ after: cursor });

Arkvory · ProAnimaStudio