open source · одно ядро на Rust · MIT
Спецификации API,которые запускаются.
Опишите каждый эндпоинт в текстовых .outry-файлах рядом с кодом. Запускайте их из терминала, редактора, приложения или CI — а когда код изменится, Outry покажет, какие запросы ему больше не соответствуют.
curl -fsSL https://raw.githubusercontent.com/1rowvy/outry/master/install.sh | sh~/shop-api $ ✓ api/auth/login.outry Login POST 200 84ms ✓ api/users/create.outry CreateUser POST 201 41ms ✓ status == 201 ✓ body matches User ✓ api/orders/create.outry CreateOrder POST 201 57ms ✓ body.total == 25 ✓ api/orders/wait-paid.outry WaitUntilPaid GET 200 3 polls 2.1s ✓ api/flows/checkout.outry Checkout flow 5 requests 2.4s 5 passed, 0 failed · 1 login, cached 4× · 2.6s
01 / формат
Один файл — это документация, запрос и тест.
Запрос .outry читается как HTTP, который он отправляет. Наведите на часть, чтобы увидеть, что она делает.
// Creates an order for the current user.CreateOrder: POST /v1/orders { only: [dev, staging] headers { Authorization: "Bearer ${Login().body.token}" Idempotency-Key: uuid() } body { customer: CreateUser(name: "Ada").body.id, items: [{ sku: "BOOK-1", qty: 2 }], } expect { status == 201 body.total == 25 body matches Order } save order_id = body.id}
- 01Имя
Запросы называются как функции и уникальны в проекте. Импорты не нужны.
- 02Окружения
onlyограничивает, где запрос может выполняться — напрямую или через вызов. - 03Вызовы
Login()— это запрос. Вызван десять раз — отправлен один раз за прогон. - 04Тело
Литералы как в JSON, с выражениями, функциями вроде
uuid()и вызовами. - 05Проверки
Каждая строка — условие. Выполняются все; при ошибке видны обе стороны.
- 06Формы
matches Orderпроверяет структуру — именованной формой или JSON Schema. - 07Сохранение
Типизированные значения для следующих запросов, хранятся вне репозитория.
02 / синхронизация
Он замечает, когда код меняется.
Outry разбирает Go-сервис через tree-sitter и сравнивает каждый запрос с его обработчиком: метод, путь, поля и типы тела, query, заголовки, middleware, тип ответа. Расхождение роняет CI на нужной строке — вместе с исправлением.
- 1поле структуры переименовано
- 2outry check
- 3--fix правит запрос
package users type CreateUser struct { Name string `json:"name"`FullName string `json:"full_name"` Email string `json:"email"`} func (h *Handler) Create(c *gin.Context) { var in CreateUser if err := c.ShouldBindJSON(&in); err != nil { c.JSON(400, errBody(err)) return } c.JSON(201, h.svc.Create(in))}
CreateUser: POST /v1/users { handler: users.Create body { name: "Ada",full_name: "Ada", email: "ada@example.com", } expect { status == 201 body matches User }}
$ outry check api/v1/users/post.outry:5:5: error: required field `full_name` (string) is missing from body ← internal/api/users.go:4 api/v1/users/post.outry:5:5: error: body field `name` is not in users.CreateUser ← internal/api/users.go:4 9 files, 2 environments, 12 Go routes: 2 errors, 0 warnings; 2 fixable with `outry import go --fix`$ outry import go --fix ~ api/v1/users/post.outry POST /v1/users internal/api/users.go:8 fixed 2 differences in 1 file $ outry check 9 files, 2 environments, 12 Go routes: 0 errors, 0 warnings# git diff internal/api/users.go - Name string `json:"name"` + FullName string `json:"full_name"`
chi · gin · net/http (паттерны Go 1.22) · префиксы групп · Mount между пакетами · middleware
03 / композиция
Запросы складываются как функции.
flow — это сценарий. Вызовы сами подтягивают зависимости, делят одну банку cookies и один кеш вызовов, а каждый шаг виден в трассе с ответом и временем.
// A user buys one item and pays.flow Checkout { user = CreateUser(name: "Ada") order = CreateOrder(customer: user.body.id) Pay(order: order.body.id) WaitUntilPaid(id: order.body.id) expect { GetOrder(id: order.body.id).body.status == "paid" } save last_order = order.body.id}
04 / архитектура
Один движок везде.
Парсер, вычислитель, HTTP, переменные, секреты и импорт из Go живут в outry-core. Всё остальное — тонкие обёртки, поэтому то, что проходит у вас, проходит и в CI.
- outry-coreRust · парсер · eval · reqwest · tree-sitter
- CLIrun · check · fmt · import
- ПриложениеTauri 2 · автообновление
- VS CodeLSP + просмотр ответа
- Любой редакторNeovim · Helix · Zed
- CIаннотации · JSON Lines
05 / переменные
У каждого значения ровно один источник.
Переменные ищутся по фиксированной цепочке, а outry vars показывает, из какого слоя пришло каждое значение. Секреты — в системном хранилище паролей или в OUTRY_*.
- 1
--varпереопределение на один прогон - 2
saveсохранено из ответов - 3
OUTRY_<NAME>переменные окружения, секреты CI - 4
env.toml [env.X] → [vars]лежит в репозитории - 5
keyringхранилище паролей, по запросу
$ outry vars -e dev project shop-api · env dev base env.toml http://localhost:8080 token keychain eyJ…(212 chars) order_id saved o_81f page --var 2 api_key missing not set: outry secret set api_key --env dev
06 / CI
Спецификация и есть тесты.
Сначала статические проверки — синтаксис, граф вызовов, переменные каждого окружения, форматирование, расхождения с Go — без единого запроса. Потом прогон на живом сервисе.
name: API testson: [push, pull_request] jobs: api: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: curl -fsSL https://raw.githubusercontent.com/1rowvy/outry/master/install.sh | sh - run: outry check --format github - run: outry run api --env ci --no-keyring --fail-fast env: OUTRY_TOKEN: ${{ secrets.API_TOKEN }}
error variable `api_key` is not defined in staging, prod
0 passed1 failed2 could not start07 / документация
Читайте документацию.
Руководства разбирают реальные задачи; справочник описывает каждое ключевое слово, флаг и ключ конфига.
Начало
- Начало работыУстановите Outry, создайте проект и отправьте первый запрос за минуту.
- Пример проектаСервис на Gin, в папке api/ которого есть все возможности Outry, — запустите его, поменяйте код и посмотрите, как запросы это заметят.
- Outry и OpenAPIOpenAPI описывает, что API обещает. Outry проверяет, что работающий сервис это обещание выполняет, а запросы всё ещё соответствуют коду.
- УстановкаУстановка CLI и приложения Outry и их обновление.
Руководства
- Вызовы запросов и проверкиПроверяйте ответы через expect, вызывайте один запрос из другого, описывайте сценарии через flow.
- Переменные и окруженияОкружения в env.toml, общие переменные, переопределения и откуда берётся каждое значение.
- СекретыТокены и пароли — в системном хранилище паролей локально и в переменных окружения в CI.
- Outry в CIИспользуйте файлы запросов как API-тесты в GitHub Actions и других CI.
- Импорт роутов из GoЗапросы .outry для всех роутов chi, gin и net/http в Go-сервисе.
- ПриложениеПросмотр, редактирование и отправка запросов в приложении Outry.
- РедакторыПравка .outry в VS Code, Neovim, Helix и любом редакторе с LSP-клиентом — ошибки, автодополнение, переход к определению и отправка запросов из `outry lsp`.
- Файлы .httpПрежний формат запросов .http — он по-прежнему поддерживается — и перевод в .outry.
Справочник
CLI коротко
outry run <пути|имена>- отправить запросы, выполнить проверки
outry check- статический анализ, окружения, fmt, Go
outry fmt- канонический стиль, как gofmt
outry import go- создать и сверить запросы по роутам
outry vars -e prod- все переменные и их источники
outry secret set token- положить секрет в хранилище
outry lsp- языковой сервер для любого редактора
outry convert- .http → .outry