Платформа изнутри
Console API v2, формат файла сборки и особенности поведения платформы, о которых стоит знать.
На этой странице – про платформу, а не про инструмент: как устроен Console API, из чего
состоит файл сборки и как платформа ведёт себя в случаях, которые с первого раза удивляют.
Всё это записано потому, что документация платформы такие углы не покрывает, а elemctl
пришлось их изучить на практике.
Для работы с инструментом эта страница не нужна. Она пригодится, когда что-то ведёт себя странно и хочется понять, что происходит внутри, – или когда вы пишете собственный клиент.
Контракт Console API v2
Общий префикс: {base}/console/api/v2. Тела запросов и ответов – JSON, кроме
загрузки сборки. Имена полей пишутся в kebab-case.
Новый справочник консоли называет основной версией Console API 2.1 с префиксом
{base}/console/api/v2.1, а версия 2.0 лежит рядом и работает. elemctl остается на 2.0 и
обращается к 2.1 только за тем, чего в 2.0 нет: за расширениями, примененными в приложении.
На путь, для которого у консоли нет обработчика, она отвечает 401, а не 404, с текстом
Handler of HTTP request "[<метод>] <путь>" in application "console" not found. – так
сервер старше метода от него и отказывается. Новый токен получает тот же ответ, поэтому
elemctl читает текст ответа до обновления токена и называет ответ методом, которого
сервер не знает, а не неудачным входом.
Приложения
GET /applications- список. Query-параметрnameу метода есть, но платформа его игнорирует и возвращает полный список. Это проверено живым вызовом, поэтому по имени фильтрует клиент.GET /applications/{id}- карточка. Значимые поля ответа:id,status,uri(адрес работающего приложения),error(текст ошибки, если есть),technology-version,date-updated,display-name,publication-context,source(объект с информацией об источнике, содержит в т.ч.project-version- версию применённой сборки).POST /applications- создать. Тело:source- объект{"type": "repository"}плюс ровно один из ключей:project-version-id(id сборки-источника) либоimage-id(id проекта);display-name,publication-context- имя и путь публикации;development-mode- булево, создавать ли среду разработки;- необязательные
space-id,technology-version.
DELETE /applications/{id}- удалить.PUT /applications/{id}/status/start- запустить.PUT /applications/{id}/status/stop- остановить.POST /applications/{id}/actions/debug- данные для сессии отладки (ApplicationDebugInfo:{"debug-token": ..., "debug-address": ...}). Тело запроса пустое; требует включённой отладки на сервере (config/debug.ymlenabled: true).POST /applications/{id}/project/update- применить сборку к приложению. Тело:{"source": {"type": "repository", "image-id": "<id сборки>"}}либо{"source": {"type": "repository", "project-id": "<id>", "assembly-version": "<версия>"}}(assembly-version необязательна). Сборку расширения принимает тот же метод и применяет рядом со сборкой приложения:source.project-version-idкарточки по-прежнему называет сборку приложения, а расширение появляется вextension-projectsметодаGET /v2.1/applications/{id}/project, с версией сборки вassembly-version.elemctlпроверяет такое применение по этому перечню.POST /applications/{id}/project/export– сборка, на которой работает приложение; справочник описывает метод и в 2.0, и в 2.1. Тело запроса пустое, ответ – сам архив,application/octet-streamбезContent-Disposition, с файлами сборки, упакованными заново. Консоль берет его у сервера приложения, и расширений, примененных в приложении, в нем нет. Во время обновления и после неудачного обновления ответом приходит сборка, загруженная для этого обновления. Сборка, загруженная в проект, возвращается с номером, который дал ей сервер, в манифесте. Обработчик 2.1 принимает и недокументированный параметр запросаextension-idи тогда отвечает сборкой этого расширения.elemctl apps exportзаписывает архив под именем{Имя} {Версия}.xasm.POST /applications/{id}/dumps- создать дамп. Тело:include-users,include-binary-data(булевы),description(строка).GET /applications/{id}/dumps/{dumpId}- статус дампа.GET /applications/{id}/users– пользователи, подключённые к приложению, каждый{user-list-id, user-id, presentation, is-admin, token-access-enabled}. Среди них и пользователи панели управления: их платформа подключает сама и называет такого пользователя по логину.token-access-enabled– доступ пользователя к HTTP-сервисам приложения по токену; без него вызов сервиса токеном этого пользователя получает 500 “Token access is denied”.PUT /applications/{id}/users/change-token-access– переключить этот доступ. Тело:{"user-list-id", "user-id", "enable-access"}, все три обязательны; ответ – подключение в том виде, в каком оно стало. Пользователь, не подключённый к приложению, получает 500 “Can’t change user token access” без объяснения причины. Метод описан и в v2, и в v2.1.elemctl apps token-accessсначала находит подключение, а после переключения перечитывает признак; весь перечень печатаетelemctl apps users.GET /me– пользователь, которому принадлежат реквизиты:id,login,presentation,user-list-id.GET /v2.1/applications/{id}/project– проекты, которые работают в приложении:application-projectи примененные расширения вextension-projects, у каждогоid,project-id,assembly-id,enabled,order,vendor-name,project-name,project-presentation,project-versionиassembly-version.id– ид расширения на сервере приложения:ИдизПроект.yamlрасширения, тотconfiguration-id, которым ответила его загрузка. Вassembly-idрасширения консоль кладет ид его проекта, так что сборку это поле не называет. Тот же путь в 2.0 отвечает одним проектом приложения.POST /v2.1/applications/{id}/project/{ExtensionId}/export– сборка примененного расширения. Тела нет, ответ – сам архив,application/octet-streamбезContent-Disposition, в нем файлы, с которыми сборку загрузили. Сегмент пути –idрасширения, регистр букв не важен. Справочник называет параметр идентификатором проекта расширения, но наproject-idсервер отвечает тем же голым 500 “Unable to export extension … from application …”, что и на неизвестное значение. Остановленное приложение отдает сборку так же. В 2.0 такого метода нет.elemctl apps export-extensionсначала ищет расширение по любому из его имен и сохраняет архив под именем{Имя} {Версия}.xasm.
Статусы приложения: стабильные Running, Stopped, Error; переходные
Starting, Stopping, Initializing, Updating, Frozen, Creating,
Deleting. Во время переходов поле status может быть и пустым. Справочник
описывает status простой строкой и значений не перечисляет, поэтому перечень
собран по живым проверкам. Deleting держится, пока идёт задача
DeleteApplication, а потом приложение остаётся в перечне со статусом
Deleted. UNKNOWN заглавными встречен у приложения, у которого
пропали файлы базы: его нельзя было ни запустить, ни удалить – удаление
начинается с выгрузки данных. Этот статус консоль дает состоянию приложения
на сервере, которому у нее нет имени, поэтому ничто не говорит, что он
окончательный, и клиент не считает его ни стабильным, ни переходным:
ожидание статуса терпит UNKNOWN минуту подряд, потом останавливается и
называет статус, а не ждет до конца всего срока.
Версия технологии
- Чтение - из поля
technology-versionкарточки приложения (отдельный endpoint чтения есть не во всех версиях платформы - не использовать). - Обновление:
POST /tasks/group-tasks/update-applications-technology, тело{"technology-version": "<версия>", "applications": ["<app-id>"]}. Возвращает групповую задачу; её статус -GET /tasks/group-tasks/{taskId}.
Пространства и проекты
GET /spaces- список пространств.GET /projects- список проектов;GET /projects/{id}- карточка;DELETE /projects/{id}- удаление. Вид проекта лежит вproject-kind:Application,LibraryилиExtension, аGroup- у группы проектов, которую перечень несет рядом с ее участниками.
Сборки проекта (assemblies)
- Загрузка файла сборки - бинарный POST (Content-Type
application/octet-stream, тело - байты файла):POST /projects/{id}/assemblies- добавить сборку в существующий проект;POST /projects- создать новый проект из сборки либо добавить сборку в проект с темИд, что записан в ееПроект.yaml;POST /spaces/{space-id}/projects- то же, но новый проект заводится в этом пространстве. Пространство загрузки в проект уходит необязательным query-параметромspace-id, так, как его пишет справочник. Живая проверка показала, что сервер его читает: ид, который не UUID, отклоняет ответом 400, а неизвестное пространство – ответом 404.SpaceIdв PascalCase, который клиент отправлял раньше, проходил непрочитанным. УPOST /projectsпараметра пространства в справочнике нет, и сервер не читает там ни одного написания: новый проект, загруженный сspace-id=not-a-uuid, всё равно создался. Поэтому пространство нового проекта уходит в путьPOST /spaces/{space-id}/projects. Неизвестное пространство этот метод отклоняет ответом 404, а сборку, уИдкоторой проект уже есть, добавляет в этот проект, как иPOST /projects. Коммит, из которого собрана сборка, уходит параметромcommit-id: справочник описывает его уPOST /projects/{id}/assemblies, и сервер кладёт его в карточку сборки. У того же метода справочник перечисляет ещёbranch-name,commit-messageиmodified, и живая проверка показала, что сервер их принимает, а в карточке не показывает ни одного: егоbranch-name– это ветка групповой разработки, а не git, аmodified=1он отклоняет ответом
CommitId,BranchNameиCommitMessageв PascalCase – вовсе не параметры метода, их сервер игнорирует. УPOST /projectsпараметра коммита в справочнике нет, поэтому сборка, которая создаёт свой проект, коммита не несёт.elemctlотправляет один коммит, а ветку, признак незакоммиченных правок и каталог исходников держит в локальном реестре загрузок. Версию архива две загрузки понимают по-разному. Загрузка в проект,POST /projects/{id}/assemblies, её вовсе не читает: базу сервер берёт изВерсияописания проекта, а номер дает наибольший, какой он когда-либо выдавал в этой базе, плюс один. Проверено живьём: архив1.0.0-i1стал1.0.0-1, следующий,1.0.0-10001, –1.0.0-2,1.0.0-2, который в проекте уже был, –1.0.0-3, а1.0.1-7–1.0.0-502в проекте, чье описание говорит1.0.0. Номер удаленной сборки остается за ней: перечень ее больше не показывает, а сервер все равно считает от него. Когда с верха перечня сняли1.0.0-4, следующая загрузка стала1.0.0-5, а после снятия1.0.0-50и1.0.0-51–1.0.0-52, хотя перечень кончался на1.0.0-8; сборка, снятая из середины перечня, ничего не меняет. Прочитать счетчик негде: в карточке проекта его нет, перечень удаленную сборку не показывает, а карточка удаленной сборки отвечает 404. Эта загрузка отвечает карточкой сборки, и еёassembly-version– номер, который выдал сервер. Загрузка без ид проекта,POST /projects, версию архива сохраняет как есть, в том числе с нечисловым суффиксом, а на версию, которая в проекте уже есть, отвечает 409 (ниже). Номер удаленной сборки такая загрузка может занять снова, и счет проекта от этого назад не идет: архив1.0.0-2лег под этим номером после того, как прежнюю1.0.0-2сняли, а следующая загрузка в проект получила1.0.0-8, а не1.0.0-3. Поэтому суффикс версии, например номер запуска CI, переживает второй путь и никогда первый.elemctl deployназывает версию, которую сервер не сохранит, до сборки, а сборку, которую он пронумеровал иначе, – сразу после загрузки. Без явной версии деплой считает номер от большего из перечня сборок и локального реестра загрузок, который помнит номера, полученные загрузками этой машины, поэтому сборка, которую эта машина загрузила и сняла, счет назад не отбрасывает. Сборки, загруженной с другой машины и потом снятой, нет ни там, ни там, и тогда строка после загрузки говорит, что номер назначил сервер. Ответ несёт id созданной сборки в одном из полей:image-id,assembly-idилиid. Проверять их надо в этом порядке. Там же лежит объектartifact, который называет проект, куда сборка легла:artifact-id- ид проекта, он открывается карточкой проекта;configuration-id-ИдизПроект.yaml;name- представление проекта. Панель показывает проект под представлением последней залитой сборки, то есть подПредставлениеиз ееПроект.yaml, а не подNameиз манифеста: сборки с однимИд, у каждой из которых были новыеNameи представление, всякий раз оставляли в карточке проекта представление. Поэтому сборка, залитая в проект под другим именем, молча этот проект переименовывает, а сборка с родным именем возвращает имя обратно. Про такое несовпадениеelemctlпредупреждает перед загрузкой. Загрузка без ид проекта переименовывает проект так же, когда ееИдведет в проект с другим именем. До такой загрузки сравнивать нечего: проект выбирает сервер, а метода, который находит проект поИд, нет. Поэтомуbuilds uploadчитает перечень проектов до загрузки и сверяет с нимartifactответа: называет проект, куда легла сборка, говорит, создала ли его загрузка, и предупреждает, если она его переименовала.
- Проект опознается по
ИдизПроект.yaml, этоconfiguration-idответа. ПоэтомуPOST /projectsсоздает проект не всегда: если живой проект с такимИдесть, сначала в пространстве загрузки, потом в любом другом, сборка просто добавляется в него, какие бы поставщик и имя ни стояли в манифесте, и он же приходит вartifact-id. ПараVendor+Name– не ключ, а ограничение: у пары в пространстве один проект. Живой опыт развел их: сборкиacme/crm,acme/crm-nextиglobex/crmс однимИдлегли в один проект, и он всякий раз брал имя очередной сборки, а сборкаacme/crmс новымИдполучила 409. Ответ 409ALREADY_EXISTSприходит в двух случаях. Первый: залита версия, которая уже есть, текст ответа - “Версия сборки … уже присутствует в группе проекта”. Второй: новыйИдс поставщиком и именем, которые уже держит другой проект пространства, текст - “Сборка с именем поставщика … уже зарегистрирована в другом проекте”; проект, который консоль завела под этотИд, уходит вместе с отказом. Отдельный одноразовый проект под те же исходники получается только с новымИди новой парой вместе. GET /projects/{id}/assemblies- список сборок. Элемент содержитassembly-version, строку вида1.0-42, и id в полеidлибоimage-id. Ответ приходит массивом или объектом, у которого список лежит в полеitemsилиassemblies. Страниц у метода нет, и общего числа он не сообщает. Параметрыlimit,size,pageSize,count,top,maxResults,page,pageNumber,offset,skip,fromиstartметод игнорирует: ответ на все эти запросы совпадает побайтно, а ни в заголовках, ни в теле нет ни счётчика, ни курсора. Проверено живыми вызовами на двух установках. Платформа сама удаляет сборки, которыми никто не пользуется, и возраст тут ни при чем: справка вендора называет это автоматическим удалением неиспользуемых сборок. Делает это консоль в один момент – когда приложение заканчивает применение сборки: фоновое задание перебирает сборки проекта этого приложения с той же базовой версией, что у сборки, на которой оно теперь работает. Остаются релизная сборка, защищенная сборка, сборка проекта по умолчанию, сборка, на которой работает живое приложение проекта или которая в него загружена, и сборка с наибольшим номером этой базы. Остальное снимается, не больше десяти сборок за одно применение. Сборка, загруженная без ид проекта (POST /projects), защищена с момента загрузки, как и та, из которой создан репозиторий проекта, и никакое применение ее не снимет, а сборка, загруженная в проект по ид (POST /projects/{id}/assemblies), не защищена. Библиотечная сборка, от которой не зависит ни одна оставшаяся сборка, уходит вместе со сборками своей группы. Сборки проекта-расширения вне уборки вовсе. Расширение применяется тем же обновлением приложения, и задание после него начинает со сборки, которую называет карточка приложения: перебирает проект самого приложения и снимает неиспользуемые сборки этой базы там, а проект расширения хранит все сборки, в том числе те, на которых не работает ни одно приложение, пока их не удалят вручную или вместе с проектом. Живая проверка сошлась с исходником: приложение работало на1.0.0-1своего проекта, в тот же проект по ид были загружены1.0.0-2и1.0.0-3, а в проекте расширения лежали сборки с1.0.0-1по1.0.0-4. Применение сборки расширения1.0.0-2сняло1.0.0-2проекта приложения и ничего в проекте расширения, а применение1.0.0-4следом оставило сборки расширения1.0.0-2и1.0.0-3на месте. Поэтому проект, к приложениям которого ничего не применяют, хранит все сборки, а сборка, загруженная впрок, переживает применение другой, только пока у нее наибольший номер. Так написано в исходниках консоли из дистрибутива сервера, и живая проверка на проекте из девяти сборок сошлась с ними в каждой строке. Создание приложения за две минуты не сняло ничего. Применение1.0.0-3еще до ответа сняло1.0.0-2,1.0.0-4и1.0.0-11, а оставило1.0.0-1и1.0.0-10, обе загружены без ид проекта,1.0.0-3,1.0.0-12с наибольшим номером и обе сборки2.0.0. Применение1.0.0-12сняло1.0.0-3. Применение2.0.0-1не сняло ничего, в том числе1.0.0-12: это применение смотрело только на2.0.0. Раньше видели и такое: из семнадцати сборок одной дневной серии в перечне осталась одна, та, на которой работает приложение, а сборка двухмесячной давности жива: она у проекта первая, а загрузка, которая создает проект, идет без ид проекта. Отсюда требование к клиенту: называть перечень остатком, а не историей проекта и не ее страницей.GET /projects/{id}/assemblies/{version}- карточка сборки,DELETE .../{version}- удаление. Последний сегмент - это версия, как метод её и называет:assembly-versionилиproject-version, строка вида1.0-42. Ид карточки сюда не подходит: на UUID метод отвечает 404 “Assembly with versionnot found”. Проверено живыми вызовами на двух установках разного возраста, ведут себя одинаково. При этом ид - тоже адрес, который есть на руках: его печатает перечень сборок и возвращает загрузка. Поэтому elemctlпринимает обе формы. Он находит по списку сборок ту версию, которую метод ждёт, а если адрес отклонён с 400 или 404, пробует сегментом и ид - на случай установки, которой нужна эта форма. Версия сборки, загруженной в проект, – номер, который дал сервер, а не версия её архива. Удаление сборки платформа отклоняет с 500, пока живо созданное из неё приложение. Когда это приложение действительно исчезло, тот же запрос проходит. Сборка, которая лишь участвовала в применении, пусть даже откатившемся, удаляется без препятствий.
Версии сборок сравнивают по числовому суффиксу после последнего дефиса: 1.0-10
новее, чем 1.0-9. Лексикографическое сравнение даёт неверный порядок.
Ветки среды разработки
GET /branches- список; необязательные queryproject-id,name.GET /branches/{id}- карточка. Поля:name,kind,project,application,source-branch,deletion-mark,version-stamp.POST /branches- создать. Тело:name,kind: "development",project: {"id": "<id>"}, необязательноapplication: {"id": "<id>"}.PUT /branches/{id}- изменить. Платформа держит оптимистическую блокировку, поэтому сначала прочитайте карточку, а затем отправьте тело, собранное из текущих значений. Этоname,kind,deletion-markиversion-stamp, который обязательно вернуть как есть. Поляsource-branchиapplicationсвернуть до{"id": ...}, а если ид нет, то до{"name": ...}. Чтобы перепривязать ветку к приложению, заменитеapplicationна{"id": "<новый app-id>"}.- Изменения ветки принимает тот же
PUT /branches/{id}с дополнительным ключом телаwrite-parameters: {"merge": true}. DELETE /branches/{id}- удалить ветку.
Инструмент работает только с документированным Console API v2. Внутренние, то есть недокументированные, API консоли платформы он не использует и не описывает.
Списки пользователей
Список пользователей держит либо пользователей приложения, либо пользователей
панели управления. У приложения список свой и назван по нему, а карточка
приложения указывает на него полем default-user-list. У панели управления
список один на установку.
GET /user-lists- список:id,presentation,space-id. Фильтра по имени на сервере нет.GET /user-lists/{id}- полная карточка.POST /user-listsсоздаёт список и требует карточку целиком: на неполное тело он отвечает 500 с обманчивым текстом “Failed to parse json”.DELETE /user-lists/{id}удаляет.GET|PUT /user-lists/{id}/settings/self-registration-{enabled, phone-required, email-required}, это “Разрешить пользователям регистрироваться самостоятельно” из панели.GET|POST /user-lists/{id}/settings/account-services-settings,PUT|DELETE .../{account-service-id}- сервисы учётных записей. Запись:{account-service-id, account-service-type, local-id, enabled, create-user-on-auth, additional-settings}; типLocalаутентифицирует паролем, остальные (OIDC,Cas,ActiveDirectory,Esia) внешние. Обе записи требуют запись целиком.GET|POST|DELETE /applications/{id}/userlists- ид списков, подключённых к приложению. Обратите внимание на написание: здесьuserlists, а на верхнем уровнеuser-lists. Собственных настроек у связи нет.GET /user-lists/{id}/users– пользователи списка, среди полейidиlogin. Запись несёт и токены доступа пользователя вместе с секретами, так что в журнал такой ответ как есть не попадает.
Что стоит знать, прежде чем на это опираться:
- правила разбора ответа сервиса учётных записей принимаются под ключом
userPropertiesCalculationRules. Этоpresentation-rule,email-rule,phone-ruleиresponse-kind. Схема самого справочника называет полеcalculation-rules, но на такое написание приходит 400. GET правила не возвращает никогда: настройка только на запись, и клиент API не может подтвердить, что она применилась; - состава форм аутентификации приложения в API нет вовсе, как и настройки подключения “пользователи списка подключаются автоматически при входе”. Это остаётся в панели;
- GET сервиса учётных записей отдаёт
client_secretOIDC-клиента открытым текстом, так что таким ответам не место в логах и отчётах как есть; - неизвестный путь Console API отвечает 401 с текстом “Handler of HTTP request … not found”, а не 404. При переборе поверхности именно это и говорит, что метода нет.
Задачи приложений
GET /tasks/application-tasks - список задач всех приложений. Серверного фильтра
нет, фильтровать приходится на клиенте. Поля задачи: id, application-id,
status (в т.ч. Error, Failed), operation-type, error-message,
start-date (ISO 8601, может оканчиваться на Z).
Формат файла сборки (.xasm / .xlib)
Файл сборки - ZIP-архив (deflate):
-
в корне
Assembly.yaml- манифест, плоские пары ключ-значение:ManifestVersion: 1.0 | 1.1 ProjectKind: Application | Library | Extension Vendor: <поставщик> Name: <имя проекта> Version: <версия, например 1.0-42> Created: <UTC, формат YYYY.MM.DD HH:MM:SS> BranchName: <имя git-ветки> CommitId: <хэш коммита>Для библиотеки (
ProjectKind: Library) в конце добавляется строкаRelease:(пустое значение); расширение файла.xlib, для приложения и расширения –.xasm.Расширение (
ProjectKind: Extension) пишется сManifestVersion: 1.1, остальные виды – с1.0. Читателя манифеста сервер выбирает по его версии, а читатель 1.0 знает толькоApplicationиLibrary: расширение с манифестом 1.0 применение отвергает с “Неизвестный вид проекта: "Extension"”. Ту же пару консоль пишет расширению. -
далее файлы проекта путями
{vendor}/{name}/...- относительно корня репозитория. Каталог проекта обязан лежать по схеме{repo}/{vendor}/{name}/Проект.yaml. Разделители путей в архиве - прямые слэши (и на Windows).
Имя файла сборки: {Имя} {Version}.xasm (через пробел).
Метаданные проекта берутся из Проект.yaml. Это YAML, и достаточно разобрать
плоские пары “ключ: значение” верхнего уровня, а вложенные строки с отступом
пропустить. Двуязычие исходников платформа заявляет сама, дескриптор с
английскими ключами применяется штатно, поэтому каждый ключ читается в обоих
написаниях: Имя/Name, Поставщик/Vendor, Версия/Version (базовая,
например 1.0) и ВидПроекта/ProjectKind. Значение Библиотека или
Library означает библиотеку, Расширение или Extension – расширение, все прочее –
приложение. Если встретились сразу
оба написания ключа, побеждает русское. Двуязычны и имена служебных файлов:
конвертер платформы принимает Project.yaml и Проект.yaml, Subsystem.yaml
и Подсистема.yaml. У английского дескриптора английские и значения
перечислений, например VisibilityScope: Global.
Если версия сборки не задана явно, она собирается как {базовая версия}-{N+1},
где N - счетчик из версии последней сборки проекта той же базовой версии.
deploy берет за N больший из двух номеров: номер последней сборки в перечне и
наибольший номер, который локальный реестр загрузок помнит для проекта в этой
базе. Сервер считает от наибольшего номера, какой он когда-либо выдавал, считая
и удаленные сборки. Проект, поднятый на новую базовую версию, снова начинает с
-1, какие бы номера ни
набрала старая база. Когда последней сборки этой базы нет, суффикс берётся из
номера запуска CI в окружении: первое числовое значение из CI_PIPELINE_IID,
GITHUB_RUN_NUMBER, BUILD_NUMBER, именно в этом порядке. Иначе чистый рабочий
каталог CI давал бы -1 каждый раз. Нет и номера CI - остаётся
{базовая версия}-1.
Git-метаданные, то есть хэш коммита и имя ветки, берутся из git-репозитория, который содержит каталог проекта. Если git недоступен, поля остаются пустыми.
Отбор файлов в архив:
- включаются только расширения:
.yaml .xbsl .xbql .md .txt .json(исходники),.png .svg .jpg .jpeg .gif .webp .ico(изображения),.css .htm .html .js .woff .woff2 .ttf .eot(веб-ресурсы); - внутри каталога
Ресурсыотбор по расширению не действует, на любом уровне и в подкаталогах тоже: по документации платформы ресурс - это произвольный файл, поэтому оттуда берётся всё; - файлы описания клиента SOAP-сервиса включаются, где бы они ни лежали:
<ИмяКлиента>.Wsdl.<n>и<ИмяКлиента>.Xsd. Платформа держит их рядом с элементом проекта и читает по имени; - исключаются каталоги
.git,.claude,.github,__pycache__,node_modules,.venvи все скрытые (начинающиеся с точки); - исключаются файлы
.gitignore,.env,.DS_Storeи файлы*.xasm,*.xlib.
Разбор готового архива
Операция, обратная сборке: по файлу .xasm или .xlib читаются манифест,
свойства проекта из его Проект.yaml внутри архива и состав. Она нужна, чтобы
подключить библиотеку к проекту, не разворачивая её исходники.
Раскладка внутри проекта; о составе достоверно говорят только каталоги:
- каталог первого уровня - это подсистема. Файл
Подсистема.yamlнеобязателен, у подсистемы библиотеки его может не быть вовсе, поэтому при поиске подсистем на него опираться нельзя; - вложенный каталог подсистемы - это пакет. Файла-описания у пакета нет, и каждый каталог даёт сегмент имени;
- полное имя типа:
{vendor}::{name}::{подсистема}[::{пакет}]::{ИмяТипа}. То же имя без последнего сегмента указывается вИспользованиеиимпорт.
Наружу, в подключивший проект, видны только типы с ОбластьВидимости: Глобально. По умолчанию стоит ВПодсистеме, а глобальная область пишется явно.
Совместимость проверяется по свойству РежимСовместимости файла Проект.yaml.
Свойства ВерсияТехнологии в Проект.yaml не существует: оно относится к
телу запроса Console API на создание приложения, а не к файлу проекта.
Поведение платформы, которое приходится учитывать
- Тихий откат применения. Когда применение сборки к приложению падает,
например из-за ошибки компиляции, платформа молча откатывает приложение на
предыдущую сборку и запускает его. Статус
Runningуспеха не означает. Достоверно результат проверяют так:- взять задачи приложения (п. 4.6) со статусом
ErrorилиFailed, у которыхstart-dateне раньше начала работы. Старые ошибки из истории не в счёт; - сверить фактически применённую версию, поле
source.project-versionкарточки приложения, с версией загруженной сборки; - сборку расширения сверить вместо этого с расширениями приложения,
extension-projectsметодаGET /v2.1/applications/{id}/project: карточка называет только сборку приложения. Расширение должно работать на версии сборки и быть включено (enabled). Сервер без Console API 2.1 этого не скажет, поэтому там применение расширения остается непроверенным, а не объявляется откатом; - для сведения сделать контрольный GET по
uriприложения. Коды 401 и 403 нормальны для закрытых приложений и успеху не противоречат.
- взять задачи приложения (п. 4.6) со статусом
- Пустой каркас при создании. На части конфигураций платформы приложение,
созданное с источником “проект”, то есть с
image-id, равным ид проекта, выходит пустым, без данных проекта. Надёжный источник - конкретная сборка вproject-version-id, например последняя сборка проекта. - Удаление с черновиками. Если в среде разработки приложения есть
неопубликованные правки,
DELETE /applications/{id}возвращает 400 сFAILED_PRECONDITIONв теле. Принудительного удаления в API нет, остаётся панель управления. Инструмент обязан дать понятную подсказку. - Готовность нового приложения. После создания приложение какое-то время
держится в переходных статусах и без
uri, поэтому нужно предусмотреть ожидание готовности: появилсяuriи статус стал стабильным. СтатусErrorво время ожидания - сразу ошибка. - Перезапуск после применения.
project/updateможет сам перезапустить приложение. После вызова дождитесь выхода из переходных статусов. Если итог неRunning, остановите приложение, когда оно не вStopped, дождитесьStopped, запустите и дождитесьRunning. Разумные сроки ожидания: остановка около 3 минут, запуск и стабилизация около 5 минут, опрос примерно раз в 10 секунд. - Статус
Errorконечный. СтабильныйError, например после неудачного применения, - это сразу ошибка: покажите тексты ошибок задач приложения. Останавливать и перезапускать такое приложение не надо, ждать другого статуса тоже: изErrorоно вStoppedне переходит, и ожидание просто выберет весь отведённый срок. - Windows. Временные файлы и кеши - только через
tempfile. Вывод консоли надо перевести в UTF-8 вызовомreconfigureдля stdout и stderr, иначе кириллица ломается. - Проект опознается по
Ид. Смотрите загрузку сборки выше: проект опознается поИдизПроект.yaml, а параVendor+Nameиз манифеста лишь должна быть свободна от других проектов пространства. Загрузка без ид проекта означает не “создать новый проект”, а “положить туда, где этомуИдместо”. - Удаление идёт в фоне и по порядку.
DELETE /applications/{id}возвращается сразу, а приложение ещё какое-то время живёт с задачейDeleteApplication. Пока оно существует, удаление сборки, из которой оно создано, отклоняется с 500. Порядок уборки такой: удалить приложение, дождаться, когда его карточка ответит 404 или статус станетDeleted, и только затем удалять сборку. - Компиляция серверная и происходит при применении. Локальная сборка
только упаковывает архив. Синтаксис, типы и видимость исходников проверяет
серверный компилятор, когда сборку применяют или создают из неё приложение.
Отдельной конечной точки “скомпилировать” нет. Из этого и из пунктов 8 и 9
собран
elemctl probe: исходники уходят в свой же проект сборкой с одноразовой версией, до компилятора добираются через одноразовое приложение, а потом обе сущности удаляются. Рабочее приложение при этом ничем не рискует. - Вход в только что созданное приложение. Новое приложение получает
собственный пустой список пользователей, и карточка указывает на него полем
default-user-list. Вход по логину и паролю в нём выключен, сервиса учётных записей у него нет. Поэтому учётные записи, которыми входят в другие приложения, здесь не работают. Подключение чужого списка пользователей черезPOST /applications/{id}/userlistsвместе с включением локального входа этого не меняет. Работает учётная запись панели управления: её пользователей платформа подключает к приложению сама, и они входят сразу. Это полезно знать, когда поднимаешь стенд под задачу: из карточки способ входа не виден, а перебирать учётки не стоит, потому что у пользователя есть счётчик неудачных попыток. - Сервер ещё стартует. После запуска или обновления сервер поднимает
консоль не сразу: после одного обновления она молчала тринадцать минут. Всё
это время на любой запрос к консоли, включая запрос токена, сервер отвечает
404 с текстом
Application "console" not found. Тот же код получает отсутствующее приложение или сборка, и отличает их только текст.elemctlраспознаёт такой ответ в своём клиенте и говорит, что он значит: сервер стартует, повторите, когда/consoleначнёт отвечать 302.deployпережидает старт сам, не дольше--server-start-timeoutсекунд (по умолчанию 900, 0 – не ждать), и продолжает, как только консоль ответит. Запрос, который консоль отвергла, она не обработала, поэтому повторять его безопасно. - Снятие применяется без вопросов. Сборку, в исходниках которой больше нет
табличной части, сервер применяет как есть: табличная часть уходит, а с ней и
её строки. Ни отказа, ни предупреждения, задача заканчивается успешно, и
приложение работает дальше; так было со справочником, у которого в табличной
части была строка. Так же уходят реквизит табличной части и реквизит самого
объекта, вместе со значениями, и справочник, чьего описания в исходниках
больше нет, вместе со всей таблицей: так было с пустым справочником. Та же
потеря – описание, сделанное заново в своём файле, под прежними путём и
именем, но с новым
Ид: справочник с двумя строками вышел из такого применения пустым, а задача закончилась успешно. Сужение длины и смена типа пересоздают данные объекта, поэтомуelemctl deployих без явного разрешения отклоняет, и так же отклоняет снятый целиком справочник, документ или регистр. Снятие реквизита или табличной части он называет до сборки и идёт дальше: это сознательная правка исходников, и строка говорит, что применение заберёт.