15 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 627e163103 |
fix(tradein): TTL-деактивация не исполняется, пока сбор по источнику лежит (#2659) (#2710)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 2m56s
Deploy Trade-In / build-backend (push) Successful in 1m1s
Deploy Trade-In / deploy (push) Successful in 1m16s
|
|||
| f5b39e6fc9 |
chore(tradein): разбор мёртвого кода — подключить, удалить или задокументировать (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m57s
Восемь находок «написано, покрыто тестами, ни разу не сработало» разведены на три разных диагноза. Две из восьми оказались не мёртвым кодом, а оборванной проводкой. ПОДКЛЮЧЕНО Загрузчик ДОМ.РФ. Loader и CLI существуют с #2013, а Handler'а в product_handlers и строки в scrape_schedules не было — вызвать его было нечем. На проде 29 978 строк staging с ОДНИМ loaded_at (2026-07-12), то есть ровно один ручной запуск, 24 дня без обновления. Отсюда кормятся houses.year_built/material_walls/total_floors и дальше listings.year_built — когортный фильтр эстиматора. Недельный такт, окно 03:00-04:00 UTC (до импорта ДКП и дневных агрегатов). filters_hash. Парсер читал estimation.sale.data.filtersHash, а Циан кладёт ключ уровнем выше — estimation.sale.filtersHash. Колонка пуста 0/1658, при том что в сохранённых сырых ответах хеш есть у 139/139 и все значения различны. Путь исправлен, 139 строк восстановлены бэкфиллом из raw_payload. has_panorama. Разбирался парсером, лежал в карте приоритетов, обещан публичным контрактом market.v_houses — и не попадал в houses ни одной строкой кода (0 из 9366). Пишется там, где yandex_valuation уже держит и house_id, и мету. Гейт честности: парсер отдаёт bool, а не bool|None, поэтому false пишем только при подтверждённо отрисованной странице (есть год или этажность) — иначе NULL, а не выдуманный false. УДАЛЕНО Дедуп-обёртки эстиматора _phys_dedup_key / _extract_street_token: 25 ссылок, все из тестов. Хуже, чем просто мёртвые — _phys_dedup_key утверждала правило «ключ = кадастр ИЛИ улица», которого в боевом дедупе нет (_union_find_phys_dedup держит оба композита и сливает по любому совпадению). Тесты переведены на живые функции. Тиерные коэффициенты выкупа asking_to_sold_ratios_tiered + asking_to_sold_tier_bounds: ноль читателей и писателей, флага tier_aware_ratio_enabled не существует. Посчитаны один раз при накатке 098 (computed_at 2026-06-27) — тогда как живая asking_to_sold_ratios обновляется ежедневно (2026-08-05). Методика сохранена в 098. Колонки без писателя: listings.merged_into (113 уже называла её мёртвой) и house_sources.raw_payload вместе с GIN-индексом по всегда-NULL колонке. v_data_quality.price_disagreements_count: у всех 89 699 объявлений ровно один источник, показатель структурно не мог быть ненулевым, а ноль читался как «расхождений нет». ЗАДОКУМЕНТИРОВАНО BROWSER_BLOCK_RESOURCES выставлен во всех трёх прод-контейнерах, а код перестал его читать в #1812. Блокировка при этом не ослабла (image глушит camoufox block_images, font/media — дефолт списка типов), мёртв только выключатель. Сервис теперь говорит об этом на старте: молча игнорируемая ручка опаснее отсутствующей. v_price_divergence / v_cross_source_health оставлены как задел, но в COMMENT написано, почему они пусты структурно: боевой путь загрузки зовёт upsert_listing_source напрямую и не зовёт match_or_create_listing. house_sources.ext_url пуст 46 813/46 813, но входит в публичный контракт market.v_house_sources — оставлен и подписан. Refs #2674 |
|||
| 4b4ab8b34c |
fix(tradein/imv): счётчики прогона в total_seen/new_count + лог дрейфа ремонта (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m51s
По ревью PR #2675. 1. counters прогона не заполняли выделенные колонки. _column_counts (scrape_runs.py) берёт total_seen из ключей total_seen|lots_fetched и new_count из new_count|lots_inserted — ни одного из них в дикте не было, поэтому все 39 прогонов этого source лежат в БД с total_seen=0. А mark_done по этой же колонке шлёт алерт «3 подряд done с нулевым результатом» (#2625): даже идеальный прогон с 50 сохранёнными считался бы нулевым и через три дня выстрелил бы ложной тревогой про капчу. Добавлены total_seen=checked и new_count=saved. Трейд-офф назван в комментарии: на исчерпанной очереди checked=0 три дня подряд тоже даст алерт — но пустая очередь при ежедневном расписании это и правда сигнал. 2. _map_renovation_type молча схлопывал в 'cosmetic' любое незнакомое непустое значение. Сегодня в проде ровно четыре канонических, живого эффекта нет, но дрейф вокабуляра реален (70950 строк listings с пустым нормализованным ремонтом). Добавлен logger.debug на случай «непустое, но не в карте» — паритет с house_type_normalizer, который такой лог уже пишет. 3. Обоснование дефолта 'cosmetic' в докстринге заменено на более сильное по данным: это одновременно МОДА и МЕДИАННАЯ категория популяции (standard 7984 / good 7116 / needs_repair 4738 / excellent 2562; кумулятивно needs_repair 21.2%, +standard 56.8%), то есть наилучшая одиночная догадка, а не просто «не край шкалы». Там же названа асимметрия: поштучный путь эстиматора при неизвестном ремонте IMV вообще не зовёт, а домовой дефолтит — решение осознанное (иначе теряем ещё ~32% домов очереди), чтобы следующий читатель не принял это за недосмотр. Refs #2674 |
|||
| 0815319e1c |
fix(tradein/imv): домовая оценка перестаёт врать про ремонт и тип дома (#2674)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 7s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m51s
Три дефекта в house_imv_backfill, найденные системным поиском (эпик #2674/#2673).
1. Тип ремонта был захардкожен литералом 'cosmetic' — все 2685 запросов ушли
как «косметический ремонт», хотя мода repair_state по объявлениям тех же
домов другая: standard 4564 / good 4118 / needs_repair 2279 / excellent 1631
(косметика лишь 36%). Теперь renovation_type берётся из mode(repair_state)
в том же агрегате, что уже считает медианы комнат/площади/этажа, и проходит
через существующий estimator._IMV_REPAIR_MAP (ленивый импорт — estimator
тянет scraper_adapters, а тот импортирует этот модуль). Второго словаря не
заводим. Неизвестный ремонт (498 домов из 2685) остаётся 'cosmetic': это
середина порядковой шкалы required < cosmetic < euro < designer, а не край,
системного сдвига в одну сторону не даёт.
2. Неизвестный тип дома молча становился 'panel' — и когда типа нет вовсе, и
когда он есть, но не совпал со словарём. Панель почти самый дешёвый класс
(медиана по нашим же 2685 оценкам: block 122.6k < panel 128.8k <
brick 131.1k < monolithic 145.9k руб/м2), то есть дефолт систематически
занижал. На проде так уехали 363 дома совсем без типа и 75 домов с
camelCase-типом из Циана (56 из них monolithBrick — минус 11.7% против
monolithic). Теперь сырое значение прогоняется через общий
scraper_kit.house_type_normalizer.normalize_house_type (знает monolithBrick /
gasSilicateBlock / aerocreteBlock / stalin и SCREAMING-вокабуляр Яндекса),
дефолт 'panel' убран: тип не распознан → house_type=None → дом помечается
no_params ('unknown house_type') и запрос к площадке не тратится. 'other' и
'wireframe' намеренно НЕ маппятся — честного соответствия у них нет.
3. Прогон не умел падать: 31 прогон подряд с saved=0 и ~35 ошибками из 50
помечен 'done'. Тот же класс, что #2670/#2657 — успех определялся как «не
поймали известное исключение». Теперь saved=0 при errors>0 → mark_failed.
Ноль сохранённых БЕЗ ошибок (всё отфильтровано в skipped) остаётся done.
Балкон/лоджия оставлены константами намеренно: покрытие listings.has_balcony
13.8%, listings.balcony_loggia 9.4%, и колонки противоречат друг другу (по
has_balcony «есть» у 62%, а по balcony_loggia самый частый случай — loggia
5650 против balcony 2794). Мода по одному-двум объявлениям на таком покрытии —
шум, а не данные.
Причина, по которой бэкфилл не сохранил НИ ОДНОЙ оценки за 34 дня, — вне этого
модуля и здесь не чинится (детали и числа в описании PR): 1240 домов легли на
отказе браузерного сайдкара «нет прокси» (гейт #2616, 05.07-02.08), а после
возврата прокси 05.08 — 23 на Page.evaluate «Execution context was destroyed»
в tradein-browser и 12 на 403 Авито.
Refs #2674
|
|||
| b800760c24 |
fix(tradein/scraper): фильтр skipped в админке + освежение схлопнутой строки (#2658)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m3s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m46s
Правки по ревью PR #2662. Фильтр статуса. `GET /admin/scrape/runs?status=skipped` отдавал 422 — 'skipped' не было в Literal, а во фронте не было чипа. Строки рисовались, но задать вопрос «что сейчас пропускается» на единственной поверхности, построенной ровно для этого, было нельзя. Добавлено в оба места (translateStatus «пропущено» и нейтральный бейдж уже умели). Схлопывание освежает строку. UPDATE двигал только finished_at/heartbeat_at, из-за чего живой стрик замерзал: списки прогонов сортируют ORDER BY started_at DESC и берут limit=20, поэтому 37-дневный пропуск утонул бы под свежими прогонами других источников — след в базе есть, на экране нет. Теперь started_at = NOW(), а начало стрика переезжает в counters.first_skip_at; сортировку общего списка не трогаем (она про все источники, чинить надо было одну строку). Там же обновляется counters.detail — иначе в строке 37 дней висел текст «протухли 1 день назад», хотя именно эта цифра и есть предмет issue. jsonb_set заменён на `||` + jsonb_build_object: три вложенных jsonb_set читать в 3 ночи невозможно, а NULL в jsonb_set обнуляет весь counters. Поиск последней строки. `ORDER BY id DESC` не ложится на индекс (source, started_at DESC) из миграции 015 — для unknown_source (тикает каждые 60 с бессрочно) это отбор всех строк источника с сортировкой раз в минуту. Теперь ORDER BY started_at DESC, id DESC. session_expires_at получил valid_only: предупреждение «скоро протухнут» считает срок ИМЕННО той записи, которую взял load_session — при нескольких аккаунтах свежайшая-любая может быть чужой протухшей строкой. Диагностика после None по-прежнему смотрит на свежайшую любую (валидных там нет по определению). Запись пропуска намеренно НЕ обёрнута в свой try/except: если db.execute падает, то падает и claim следующего расписания в этом же тике — тик срывается в любом случае, а глушить исключение здесь значило бы вернуть ровно тот немой пропуск, ради которого заведён #2658. Самовосстановление через 60 с. |
|||
| 0b54b96984 |
fix(tradein/scraper): пропуск расписания пишет строку прогона со статусом skipped (#2658)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI Trade-In / changes (pull_request) Successful in 8s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m41s
Пропуск наступившего окна был немым: logger + сдвиг next_run_at, ни строки в scrape_runs, ни изменения last_run_at. cian_history_backfill так простоял 37 дней на протухших куках Циана и снаружи выглядел работающим — next_run_at исправно двигался вперёд, а docker-логи с warning'ом терялись на каждом редеплое. Статус 'skipped' заведён ещё миграцией 015 и локализован во фронте («пропущено»), но в проде имел 0 строк — механизм построен и ни разу не использован. Задействуем его во всех пяти местах, где расписание пропускалось без следа: kit `_claim_run` (already_running / concurrent_claim / running_appeared_under_lock), kit `scheduler_loop` (unknown_source) и продуктовый cian `pre_claim`. Причина — слаг в `error`, по нему «нет кук» отличается от «уже бежит» запросом, а не грепом логов. Подряд идущие одинаковые пропуски схлопываются в одну строку со счётчиком `counters.skips`: «уже бежит» и «неизвестный source» не двигают next_run_at и иначе плодили бы строку каждый тик (60 с). Алерт про куки жил в недостижимой ветке: он стоял там, где verify_session вернул None, а на протухших куках load_session сам фильтрует expires_at_estimate > NOW() и отдаёт None ещё в первой, немой ветке. Теперь алерт в обеих ветках и через logger.error — в scraper-контейнере GlitchTip поднят с LoggingIntegration (event_level=ERROR), поэтому прежний capture_message(level="warning") событием не становился. Плюс предупреждение ЗАРАНЕЕ (COOKIE_EXPIRY_WARN_DAYS=5) в том же pre_claim: обновление кук — ручная операция, алерт по факту протухания приходит, когда сбор уже встал. Монитор нулевых прогонов (#2625) не трогаем: обе alert-выборки отбирают failed/banned/done/cancelled, поэтому 'skipped' в стрик не попадает и его не прерывает — пропуск не «прогон вернул ноль лотов», смешивать нельзя. |
|||
|
|
b586b5ff68 |
fix(tradein/snapshot): починить зависающий запрос снапшотов и добавить бюджет времени (#2607)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Successful in 2m37s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Root cause: event-diff CTE джойнил "today" (снимок за CURRENT_DATE) с "prior" (DISTINCT ON по всей listing_source_snapshots, ~2.6-2.8M строк) обычным JOIN. Планировщик оценивал today в 1 строку (свежевставленные в той же транзакции строки ANALYZE ещё не видел) → Nested Loop без Materialize пересчитывал DISTINCT ON по всей таблице заново на каждую из ~80-140k реальных строк today (EXPLAIN на проде: cost≈300k на этом шаге) — прогон не укладывался ни в 6h zombie-порог, ни в сутки, каждую ночь минимум с 19 июля. Переписано на JOIN LATERAL (per-row indexed point-lookup через idx_lss_source_date, cost упал до ~4.4/строку). Плюс budget_sec → SET LOCAL statement_timeout как defense-in-depth (по образцу geocode_missing_listings) — задача теперь честно падает в mark_failed вместо того чтобы висеть сутками, если план когда-нибудь разрегрессирует снова. Зомби-детектор (reap_zombies) не тронут — он только помечает scrape_runs.status, не убивает backend (нет pid/application_name в схеме run'а); pg_terminate_backend для этого — отдельный follow-up, не в этом PR. |
||
|
|
f44ed4043c |
feat(tradein/db): авто-refresh ценовых бэндов по городам + порог для малых городов (#2576)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 12s
CI / changes (pull_request) Successful in 13s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 2m21s
Миграция 178 засеяла deal_city_price_bands разово (N>=30 сделок), без scheduler'а на refresh. Город без строки падал на глобальный DEAL_MIN_PPM2=50000 (ЕКБ-калибровка) — для дешёвых городов области это не anti-outlier guard, а cut-off легитимного рынка (Североуральск median ~21.7k). Замер по прод-данным: 289 из 369 не-ЕКБ городов (1265 сделок) не имели строки и падали на ЕКБ-порог. - 194_deal_city_price_bands_tiers.sql — трёхуровневая схема (tier колонка): full (N>=30, own p1/p99, unchanged) / rough (N 10-29, own p1 floor + фикс. ceiling 800000) / region_fallback (N 1-9, pooled областной p1=15263 вместо ЕКБ-порога). Екатеринбург по-прежнему не в таблице — estimator fallback byte-identical. - 195_scrape_schedules_seed_deal_city_price_bands_refresh.sql — scrape_schedules row, окно 07:00-08:00 UTC (после rosreestr_dkp_import + asking_to_sold_ratio_refresh). - app/tasks/deal_city_price_bands_refresh.py — периодический re-derive (kit-scheduler, byte-identical 194 derivation), без DELETE (множество городов монотонно растёт). - app/services/product_handlers.py — регистрация Handler для нового source. Валидация: scratch-БД (syntax_check) в прод-контейнере, synthetic данные на границах тиров (N=9/10/29/30) + Екатеринбург/non-rosreestr/NULL exclusion, оба файла применены дважды (идемпотентность подтверждена), scratch-БД удалена. |
||
|
|
07275c3c97 |
feat(tradein): монитор свежести СберИндекса (audit п.1)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 51s
Добавляет sber_freshness_monitor по образцу deals_freshness_monitor: staleness данных СберИндекса теперь видна на MONITOR-частоте, а не тонет в per-estimate warning'ах estimator._load_sber_index_series (#audit-5a). - app/tasks/sber_freshness_monitor.py: чистая evaluate_sber_freshness() (frozen-now, без БД) + check_sber_freshness() (один SELECT max(period_month) вторичного сегмента по региону, #R2-H1 фильтр как в эстиматоре; WARNING при stale, mark_done при алерте — это монитор, не сбой; mark_failed только при пустой таблице). - app/services/product_handlers.py: _job_sber_freshness_monitor + Handler в build_product_handlers (run_in_executor, как deals-монитор). - data/sql/180_seed_sber_freshness_monitor.sql: seed scrape_schedules (enabled, daily 09:00-10:00 UTC, lag_allowance_days=25). - tests/test_sber_freshness_monitor.py: frozen-now (fresh/stale/границы) + FakeDB (fresh/stale/empty/кастомный lag) + свойства миграции + registry. Порог алерта: sber_index_max_age_days (35) + lag_allowance (25) = 60д. +25 — запас на инхерентный лаг публикации источника (1-2 мес), чтобы не шуметь на штатном отставании. Прод 2026-07-12: max=2026-05-01, age=72д > 60 → alert=1. |
||
| f93ef5374d |
feat(tradein/domclick): production Layer B detail-backfill orchestrator (#2000) (#2436)
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 47s
Deploy Trade-In / build-backend (push) Successful in 1m30s
Deploy Trade-In / deploy (push) Successful in 1m32s
|
|||
| 5fa505b809 |
fix(tradein/scrapers): подключить geoportal coords backfill в scheduler (#1967) (#2346)
All checks were successful
Deploy Trade-In / changes (push) Successful in 9s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 1m30s
Deploy Trade-In / build-backend (push) Successful in 52s
Deploy Trade-In / deploy (push) Successful in 1m48s
|
|||
|
|
90731da537 |
feat(tradein): location-coef MVP через FDW-мост к OSM POI Птицы (#2045)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 8s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m36s
Финальный PR issue #2045 (BE-3): GET /api/v1/trade-in/location-coef для LocationDrawer. FDW foreign table -> локальное зеркало osm_poi_ekb_local (TRUNCATE+INSERT, тот же паттерн что cad_buildings_local/cadastral_geo_match, избегает ~1.16s/row FDW round-trip) -> straight-line POI-скоринг, портированный из Site Finder poi_score.py::compute_poi_weighted_top7 (CATEGORY_WEIGHTS as-is, радиус 1200м для квартир вместо Ptica 2000м для участков). score->coef - новая MVP-эвристика (0.95..1.05, не откалибрована на реальных дельтах). Graceful fallback (не 500, не фабрикуем факторы): пустая/не отрефрешенная osm_poi_ekb_local или отсутствие lat/lon у оценки -> coef=1.0, factors=[], geo_source="unavailable". Scheduler: source=osm_poi_ekb_refresh, daily, зарегистрирован и в боевом dispatch (scheduler.py), и в kit-registry (product_handlers.py) - иначе test_kit_registry_completeness падает на ship-dark инварианте (#2192). Frontend wiring (mappers.ts/LocationDrawer.tsx) - вне scope, отдельная задача после проверки endpoint'а curl'ом на деплое. |
||
| ac19c4f7b3 |
feat(tradein/monitor): алерт на staleness данных deals по max(deal_date) (#2212) (#2226)
All checks were successful
Deploy Trade-In / changes (push) Successful in 8s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 1m35s
Deploy Trade-In / build-backend (push) Successful in 56s
Deploy Trade-In / deploy (push) Successful in 1m2s
|
|||
| f5b0076e6f |
fix(tradein): deactivate_stale для domklik/n1 + честная freshness по scraped_at (#2204) (#2221)
All checks were successful
Deploy Trade-In / changes (push) Successful in 9s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 1m30s
Deploy Trade-In / build-backend (push) Successful in 58s
Deploy Trade-In / deploy (push) Successful in 2m57s
|
|||
| bcdec5ebd4 |
feat(rewire): scheduler_main → kit-scheduler behind USE_KIT_SCHEDULER flag, ship-dark (#2192)
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 37s
Deploy Trade-In / test (push) Successful in 1m38s
Deploy Trade-In / build-backend (push) Successful in 50s
Deploy Trade-In / deploy (push) Successful in 58s
|