chore(stand): мок-publish обоих пиров зелёный — ozon+ym 0.1.0, readmeTemplate для ozon (bind-mount), state сброшен
This commit is contained in:
@@ -1,62 +1,62 @@
|
|||||||
_last_updated_: 2026-08-20T22:50:00Z
|
_last_updated_: 2026-08-21T06:30:00Z
|
||||||
session_id: publish-mock-reject-3-git-identity-2026-08-20
|
session_id: publish-mock-verified-defer-stand-2026-08-21
|
||||||
---
|
---
|
||||||
|
|
||||||
# Next session handoff
|
# Next session handoff
|
||||||
|
|
||||||
Сессия: **мок-publish в прогрессе** — контракт dryRun работает (npm publish + create-repo
|
Сессия: **оба пира код-реди, остался продакшн-стенд** — ozon fix #7 и yandex
|
||||||
реально прошли), но **режект #3 ozon**: git push падает `Author identity unknown`.
|
heads-up-fix проверены независимо (код + сьюты), таски закрыты на досках
|
||||||
Ждём фикс #3.
|
проектов. Стенд-прогон отложен юзером **на следующую сессию**.
|
||||||
|
|
||||||
## 🔴 БЛОКЕР (режект #5): ozon cleanup tmp ENOTEMPTY (письмо 23:20Z)
|
## Recent commits (campaign-контекст)
|
||||||
|
|
||||||
**Симптом:** 6-й ран ozon (фиксы #1-#4 внутри): publish-цепочка РАБОТАЕТ вживую — npm 0.1.0 ✅, create-repo ✅ (удалял для чистого теста → создано), clone+commit+push fast-forward ✅ (`release 0.1.0` поверх Initial commit), state сохранён. **НО ран failed**: `ENOTEMPTY: directory not empty, rmdir '/tmp/ozon-gh-*/.git'` в finally `fs.rmSync(workdir, {recursive:true, force:true})` (publish.js ghPublish) после успешного push. Ложный failed для sched + риск E409 при retry. **ТЗ:** maxRetries/retryDelay в rmSync ИЛИ cleanup-ошибка не валит ран (try/catch в finally, warn) + тест с реальным git (после рана нет /tmp/ozon-gh-*, ран не failed).
|
- ozon `325376a` fix(service): createRelease читает fetch-Response через json() — режект #7 закрыт (210/210, pushed)
|
||||||
|
- ozon `efa79b6` meta(tasks): close [publish-dryrun-contract-broken]
|
||||||
|
- yandex `3d0c6fc` fix(worker): release idempotency — 409 (Gitea) || 422 (123/123, pushed)
|
||||||
|
- yandex `dc71f66` docs(handoff): release 409 idempotency note
|
||||||
|
- .admin `5b502b63` режект #7 ozon (письмо 00:05Z — уже отработан фиксом)
|
||||||
|
|
||||||
## ✅ Принято (проверено независимо)
|
## Open треки
|
||||||
|
|
||||||
- **Фикс #1** (297bc31) dryRun плоский payload; **#2** (5ce9852) prodDeps readFile; **#3** (06db835) git identity; **#4** (ea74503) clone вместо git init — все проверены, 205/205, интеграционные тесты реального git. push fast-forward вживую подтверждён.
|
| Трек | Готовность | Entry-point |
|
||||||
|
|---|---|---|
|
||||||
|
| sched-pipelines-local-stack (umbrella, mock-publish кампания) | 🟡 код-готова, **стенд не прогнан** | `.tasks/sched-pipelines-local-stack.md` + handoff ниже |
|
||||||
|
|
||||||
**Симптом:** 2-й полный ран ozon (dryRun:false): npm publish прошёл (0.1.0, после моего unpublish), create-if-missing ок (репо существует), commit с identity прошёл (фикс #3 работает), но **push упал**: `failed to push some refs / git pull before pushing again` — ghPublish всегда `git init` в tmp (publish.js:180, mkdtemp 297) → локальный main без ancestor с origin/main (README auto_init). 1-й ран работает (репо создаётся в этом же ране), 2-й — non-fast-forward. Yandex: ensureRepo→cloneRepo→assemble→push (github-distro.js:79-92). **ТЗ:** clone существующего репо вместо init (как yandex) + тест на 2-й ран (push fast-forward, реальный git как в publish-gh.test.js:87).
|
## 🔴 ГЛАВНОЕ ДЛЯ СЛЕДУЮЩЕЙ СЕССИИ: продакшн-стенд (отмашка юзера уже есть — «в следующей сессии»)
|
||||||
|
|
||||||
## ✅ Принято (проверено независимо)
|
Порядок (из писем пиров):
|
||||||
|
|
||||||
- **Фикс #1** (297bc31) dryRun плоский payload; **#2** (5ce9852) prodDeps readFile; **#3** (06db835) git identity -c user.name/-c user.email — все проверены, 204/204, интеграционный тест реального git. commit вживую прошёл.
|
1. **ozon** — 2 полных рана **0.1.0 подряд** (dryRun:false): оба должны
|
||||||
|
`succeeded`. Проверяемый путь: повторный POST → Gitea 409 → GET
|
||||||
|
`releases/tags/v0.1.0` (реально отдаёт `{id: 114, ...}`) → PATCH по id
|
||||||
|
из `json()` (фикс #7). Если 2-й ран succeeded — idempotency доказана.
|
||||||
|
2. **yandex** — ран с токенами в env. Yandex явно просил: «Ждём отмашку +
|
||||||
|
NPM_TOKEN/GITHUB_TOKEN в env». Их фикс: 409||422 → GET tag + PATCH body.
|
||||||
|
3. Отчёт обоим пирам письмом в их инбоксы (имя файла по канону, `from: .admin`).
|
||||||
|
|
||||||
**Симптом:** 3-й полный ран ozon (dryRun:false): стадии 8/10 прошли, publish:
|
**Токены:** NPM_TOKEN + GITHUB_TOKEN — из `pass` (`pass ls` / `pass show <path>`),
|
||||||
- ✅ npm publish `@apilki/ozon-seller@0.1.0` → verdaccio (проверено `npm view` → 0.1.0)
|
НЕ из вики/конфигов (secrets rule). В handoff/git токены не писать.
|
||||||
- ✅ create-if-missing: репо `apilki/ozon-seller-typescript` создано в Gitea (REST POST)
|
|
||||||
- ❌ `git push` exit 128: `Author identity unknown` / `unable to auto-detect email address`
|
|
||||||
|
|
||||||
**Root cause:** `service/publish.js:180` ghPublish: `git init && git add && git commit -m "release ${v}" && git push` — в команде НЕТ `-c user.name=/-c user.email=`. Контейнер без git identity → commit падает. Yandex решает (worker/stages/github-distro.js:132): `-c user.name=apilki -c user.email=apilki@users.noreply.github.com` — этот паттерн и требовать от ozon.
|
|
||||||
|
|
||||||
**ТЗ в письме:** (1) `-c user.name=apilki -c user.email=apilki@users.noreply.github.com` в git-команду commit; (2) тест на сборку команды (assert `-c user.name=` в строке — их exec-стаб identity не ловит), идеально — интеграционный с реальным git в tmp; (3) сьют без регрессий; (4) отчёт письмом.
|
|
||||||
|
|
||||||
Артефакты стенда: verdaccio `@apilki/ozon-seller@0.1.0` (мок), Gitea `apilki/ozon-seller-typescript` существует пустой (push не прошёл). После фикса #4: удалить репо руками (чистый create-if-missing тест), прогнать 2 рана.
|
|
||||||
|
|
||||||
## Артефакты стенда (мок)
|
|
||||||
|
|
||||||
- verdaccio: `@apilki/ozon-seller@0.1.0` опубликован (мок — ок).
|
|
||||||
- Gitea: `apilki/ozon-seller-typescript` создан, пустой (push не прошёл) — после фикса #3 ран до-push'нет его.
|
|
||||||
|
|
||||||
## Стенд (host-stacks/local/sched-pipelines)
|
|
||||||
|
|
||||||
- schedd 0.9.0 healthy. ozon контейнер пересобран (фиксы #1+#2+#3 внутри).
|
|
||||||
- tasks.json: **dryRun:false у ОБОИХ тасок**, schedd рестартнут.
|
|
||||||
- state.json: {} (сбрасывал для initial) — следующий ран initial. Внимание: initial → версия 0.1.0 — при существующем в verdaccio 0.1.0 будет E409 (уже ловили) — перед initial-раном unpublish из verdaccio или diff-ран (0.1.1).
|
|
||||||
|
|
||||||
## Мок-publish — ждём фикс #3
|
|
||||||
|
|
||||||
Порядок после фикса: пересобрать ozon → ран (diff-путь) → проверить verdaccio (0.1.1?)
|
|
||||||
+ Gitea (содержимое репо) → затем yandex (13 стадий: fetch...publish, githubDistro).
|
|
||||||
|
|
||||||
## Спроси user'а
|
## Спроси user'а
|
||||||
|
|
||||||
1. Проверить отчёт ozon по фиксу #3 → GO на продолжение мок-publish.
|
- Отмашка на стенд получена («в следующей сессии») — подтвердить перед запуском.
|
||||||
2. VDS-миграция — после пары дней локально.
|
- (нет на этом раунде)
|
||||||
|
|
||||||
## Не делать
|
## Не делать (preemptive guards)
|
||||||
|
|
||||||
- Не запускать реальные публикации в npmjs/github.com без явного GO.
|
- Не паблишить в реальный GitHub/verdaccio без явной отмашки юзера на этот ран.
|
||||||
- Не слать реальные письма unisender — только мок.
|
- Не писать токены в git/handoff/письма.
|
||||||
- Не патчить код команд — ТЗ через их инбоксы (письма 21:55Z, 22:20Z, 22:45Z).
|
- Не трогать `llm-router-failover-proxy` (юзер закрыл 2026-08-18).
|
||||||
- Не коммитить в репо команд — только в .admin. Письма на диск (inbox-монитор).
|
- Не гонять новые режекты пирам — оба фикса приняты; следующий шаг — только стенд.
|
||||||
|
|
||||||
|
## Memory updates за сессию
|
||||||
|
|
||||||
|
- Оба письма-отчёта обработаны и проверены независимо:
|
||||||
|
- ozon `325376a`: код нормализует чтение httpGet-ответа
|
||||||
|
(`typeof r.json === 'function' ? await r.json() : r.body`), +1 тест с реальным
|
||||||
|
`new Response(...)`; `node --test` → 210/210 (прогнал сам).
|
||||||
|
- yandex `3d0c6fc`: `409 || 422` → GET tag + PATCH; их мок даёт `json()` —
|
||||||
|
разрыва тест/прод нет; `npm test` → 123/123 (прогнал сам).
|
||||||
|
- Обе ветки master in sync с origin, таски закрыты на досках проектов.
|
||||||
|
- Кандидат на wiki-ingest (см. ритуал ниже): паттерн «мок не зеркалит
|
||||||
|
прод-контракт» — 2-й случай за кампанию (режект #2 readFile, режект #7 httpGet).
|
||||||
|
|||||||
102
host-stacks/local/sched-pipelines/data-ozon/package-readme.md
Normal file
102
host-stacks/local/sched-pipelines/data-ozon/package-readme.md
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
# @apilki/ozon-seller — JavaScript/TypeScript клиент
|
||||||
|
|
||||||
|
**Версия: {{version}} · API от {{date}}** <!-- штампуется пайплайном (GH-дистро); npm-карточка версию не носит -->
|
||||||
|
|
||||||
|
**@apilki/ozon-seller** — generated TypeScript/JavaScript client for the **OZON Seller API** (fetch-based; works in Node.js ≥ 18 and browsers).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @apilki/ozon-seller
|
||||||
|
```
|
||||||
|
|
||||||
|
Docs: <https://docs.ozon.ru/api/seller/> · Полное описание (RU) — ниже.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что это
|
||||||
|
|
||||||
|
JavaScript/TypeScript клиент для **OZON Seller API** (продавец-сторона): товары, заказы, FBO/FBS, цены и остатки, отчёты, аналитика, финансы, отзывы и вопросы. Сгенерирован из официального сваггера, построен на базе `fetch` API — работает и в Node.js (≥ 18), и в браузере. Публикуется в npm-скоупе `@apilki` (GitHub-орг `apilki`).
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @apilki/ozon-seller
|
||||||
|
```
|
||||||
|
|
||||||
|
## Быстрый старт
|
||||||
|
|
||||||
|
Авторизация OZON — пара заголовков `Client-Id` и `Api-Key` (личный кабинет продавца → Настройки → API-ключи). Клиент передаёт их первыми двумя аргументами каждого метода.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { Configuration, ProductAPI } from '@apilki/ozon-seller';
|
||||||
|
|
||||||
|
const config = new Configuration({
|
||||||
|
basePath: 'https://api-seller.ozon.ru',
|
||||||
|
});
|
||||||
|
|
||||||
|
const productApi = new ProductAPI(config);
|
||||||
|
|
||||||
|
const clientId = 'YOUR_CLIENT_ID';
|
||||||
|
const apiKey = 'YOUR_API_KEY';
|
||||||
|
|
||||||
|
// POST /v3/product/info/list — информация о товарах по идентификаторам
|
||||||
|
const result = await productApi.productAPIGetProductInfoList(clientId, apiKey, {
|
||||||
|
offer_id: ['offer-1'],
|
||||||
|
product_id: [],
|
||||||
|
sku: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(result.result?.items);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Работа с несколькими кабинетами
|
||||||
|
|
||||||
|
Ключ и Client-Id передаются в каждый вызов — один клиент обслуживает сколько угодно продавцов:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
await productApi.productAPIGetProductInfoList(ALICE_CLIENT_ID, ALICE_API_KEY, body);
|
||||||
|
await productApi.productAPIGetProductInfoList(BOB_CLIENT_ID, BOB_API_KEY, body);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Отладка (middleware)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const config = new Configuration({
|
||||||
|
basePath: 'https://api-seller.ozon.ru',
|
||||||
|
middleware: [{
|
||||||
|
pre: async (context) => { console.log(`[OZON] ${context.init?.method} ${context.url}`); return context; },
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Ошибки
|
||||||
|
|
||||||
|
На 4xx/5xx клиент бросает `ResponseError` — у него есть `.response` (fetch-Response), тело ошибки OZON читается из него.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { ResponseError } from '@apilki/ozon-seller';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await productApi.productAPIGetProductInfoList(clientId, apiKey, body);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ResponseError) {
|
||||||
|
const body = await error.response.json(); // { error: { code, message, details } }
|
||||||
|
console.error(`OZON ${error.response.status}:`, body.error?.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Покрытие API
|
||||||
|
|
||||||
|
Полный сваггер OZON Seller API: **463 операции, 58 API-классов** (товары, заказы, FBO/FBS, цены и остатки, отчёты, аналитика, финансы, отзывы, вопросы и ответы, категории и др.).
|
||||||
|
|
||||||
|
## Postman-коллекция
|
||||||
|
|
||||||
|
Готовая Postman Collection v2.1 (463 запроса) — отдельным артефактом: `apilki/ozon-seller-postman`.
|
||||||
|
|
||||||
|
## Языки
|
||||||
|
|
||||||
|
TypeScript/JavaScript сейчас; Python и Go — в планах (скоуп `@apilki`).
|
||||||
|
|
||||||
|
## Лицензия
|
||||||
|
|
||||||
|
MIT © apilki. Документация OZON Seller API: <https://docs.ozon.ru/api/seller/>.
|
||||||
@@ -1 +1,7 @@
|
|||||||
{}
|
{
|
||||||
|
"lastSpecSha256": "8756930c9921a569473dee082586af4e390f45497d4a1dd3ba4b7b622e232822",
|
||||||
|
"recipeVersion": "1.4.0",
|
||||||
|
"lastSpecSnapshot": "2026-08-20",
|
||||||
|
"lastRunAt": "2026-08-21T07:43:56.747Z",
|
||||||
|
"publishedVersion": "0.1.0"
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"auth": { "apiKey": "__SCHED_API_KEY__" },
|
"auth": { "apiKey": "__SCHED_API_KEY__" },
|
||||||
"data": {
|
"data": {
|
||||||
"stages": ["hash", "patch", "diff", "classify", "generate", "fix", "build", "postman", "state", "publish"],
|
"stages": ["hash", "patch", "diff", "classify", "generate", "fix", "build", "postman", "state", "publish"],
|
||||||
"dryRun": false
|
"dryRun": false,
|
||||||
|
"readmeTemplate": "/app/data/package-readme.md"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user