декумуляція: на скільки вистачить без внесків #150
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: ci | |
| on: | |
| push: { branches: [main] } | |
| pull_request: | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| # Версія одна на всі місця: go.mod, Dockerfile і deploy/proxmox-lxc.sh | |
| # мали три різні, і «яка в нас насправді» ставало питанням. | |
| with: { go-version-file: go.mod } | |
| # Формат — окремим кроком і першим: діф, у якому перемішані правка й | |
| # вирівнювання, читати неможливо, а gofmt цю різницю прибирає назавжди. | |
| - name: Формат | |
| run: | | |
| if [ -n "$(gofmt -l .)" ]; then | |
| echo "неформатовані файли:" | |
| gofmt -l . | |
| echo "запусти: make fmt" | |
| exit 1 | |
| fi | |
| - run: go vet ./... | |
| # Екшен саме v9: v6 ставить golangci-lint 1.x, який конфіг формату v2 | |
| # (.golangci.yml) відкидає як «invalid elements». Версію лінтера | |
| # закріплюємо, щоб локальний `make lint` і CI бачили одне й те саме. | |
| - name: Лінтер | |
| uses: golangci/golangci-lint-action@v9 | |
| with: { version: v2.12.2 } | |
| # -race, бо MQTT-публікатор, добовий планувальник і HTTP-сервер ділять | |
| # один стор: саме та форма програми, де гонку не побачиш очима. | |
| - run: go test ./... -race -count=1 | |
| # Контракт: фікстура oddinvest/state має бути згенерована поточним кодом. | |
| # Репозиторій ha-oddinvest у своєму CI підтягує contract/ звідси і ганяє | |
| # свої парсери проти цих фікстур. | |
| - run: go test ./internal/state -run TestFixtureUpToDate -count=1 | |
| ui: | |
| name: спільний UI | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Збірки в UI немає навмисно (сервіс ставиться в LXC без Node, модулі | |
| # віддаються браузеру як є), тож бандлера, який ловив би друкарські | |
| # помилки, теж немає. Ці дві перевірки — його заміна. | |
| - uses: actions/setup-node@v4 | |
| with: { node-version: "22" } | |
| # 1. Синтаксис кожного модуля. package.json у каталозі каже node, | |
| # що це ESM, а не CommonJS. | |
| - name: Синтаксис модулів | |
| working-directory: internal/api/web | |
| run: | | |
| for f in $(find js -name '*.js'); do | |
| node --check "$f" || exit 1 | |
| done | |
| # 2. Чи резолвиться КОЖЕН імпорт. node --check бачить лише синтаксис і | |
| # пропускає «does not provide an export named X» — найімовірнішу | |
| # помилку після перейменування чи переносу функції між модулями. | |
| - name: Граф імпортів | |
| working-directory: internal/api/web | |
| run: | | |
| cat > /tmp/imports.mjs <<'EOF' | |
| globalThis.HTMLElement = class { attachShadow() { return {}; } }; | |
| globalThis.customElements = { get: () => undefined, define: () => {} }; | |
| globalThis.localStorage = { getItem: () => null, setItem: () => {} }; | |
| globalThis.Document = { prototype: {} }; | |
| import { readdirSync, statSync } from "node:fs"; | |
| const walk = (d) => readdirSync(d).flatMap((f) => | |
| statSync(`${d}/${f}`).isDirectory() ? walk(`${d}/${f}`) : [`${d}/${f}`]); | |
| let bad = 0; | |
| for (const m of walk("js").filter((f) => f.endsWith(".js")).sort()) { | |
| try { await import(`${process.cwd()}/${m}`); console.log(`ok ${m}`); } | |
| catch (e) { bad++; console.log(`FAIL ${m}\n ${e.message.split("\n")[0]}`); } | |
| } | |
| process.exit(bad ? 1 : 0); | |
| EOF | |
| node /tmp/imports.mjs | |
| # 3. Чи ВИЗНАЧЕНЕ кожне вжите імʼя. Дві перевірки вище цього не бачать: | |
| # модуль із `fmtCur(...)` у тілі й без `fmtCur` у шапці імпорту має | |
| # правильний синтаксис, чисто резолвиться — і падає в браузері. | |
| # Саме так після розбиття portfolio.js на модулі розділ «Портфель» | |
| # поклався на «fmtCur is not defined», а ще два імені зникли тихо. | |
| # | |
| # eslint тут CI-only: ставиться через npx і в застосунок не | |
| # потрапляє — правило «жодних залежностей у web/» не порушене. | |
| - name: Невизначені імена | |
| run: npx --yes eslint@9 internal/api/web/js | |
| # 3. Манiфест описує те, що є насправді: за ним синхронізується | |
| # вендорена копія в ha-oddinvest, і застарілі хеші зробили б із | |
| # перевірки дрейфу в тому репозиторії пустушку. | |
| - name: Манiфест свіжий | |
| run: sh scripts/gen-ui-manifest.sh --check |