refactor(release): adopt global create_release.py from opencode-config #33

Merged
slaid098 merged 4 commits from refactor/release/adopt-global-script into master 2026-08-12 19:45:24 +03:00
Owner

Что сделано

  • Удалены локальные scripts/create_release.py и scripts/release_notes.py (заменены глобальными из opencode-config).
  • Удалён tests/test_create_release.py (тестировал экстрактор release_notes.py, теперь глобальный — тестируется в opencode-config issue #65, в voice_assistant не нужен).
  • В .github/workflows/release.yml шаг "Create Forgejo release" переписан: скачивание глобальных create_release.py + release_notes.py через curl.exe из opencode-config@main, запуск с RELEASE_PLATFORM="Windows 10+ (64-bit)". Шаг "Package release" переименован zip в voice-assistant-windows-x64.zip (имя, которое глобальный скрипт ищет в CWD: {pyproject.name}-{RUNNER_OS.lower()}-{RUNNER_ARCH.lower()}.zip).
  • В README.md секции Requirements (EN) и Требования (RU): убрано упоминание "Python 3.13" (встроен в exe через PyInstaller), оставлено Windows 10+ (64-bit).

Почему

  • Единый источник истины для логики релиза во всех репо slaid098 — глобальный create_release.py в opencode-config (issue #65, PR #66, commit e00a060). Дублирование логики в каждом репо → drift, баги в одном репо не фиксятся в других.
  • Платформа Windows 10+ (64-bit) теперь детерминированно попадает в body релиза через RELEASE_PLATFORM env var (глобальный скрипт формирует ## Системные требования заголовок), а не через CHANGELOG (PR #31 уже убрал платформу из CHANGELOG).

Watch out

  • Зависимость от opencode-config issue #65 (PR #66) — глобальный скрипт должен быть в main ветке opencode-config. Если его нет или он сломан — релиз упадёт. Это single point of failure, но оправдано единым источником истины.
  • $GITHUB_SERVER_URL — Forgejo runner auto-set env var (НЕ $FORGEJO_URL, который может не быть в runner env). Глобальный скрипт поддерживает оба (FORGEJO_URL || GITHUB_SERVER_URL), но в workflow используется именно $GITHUB_SERVER_URL.
  • curl.exe — обязательно суффикс .exe в PowerShell 5.1; curl без суффикса — алиас на Invoke-WebRequest, который ломает multipart-загрузку ассетов (gotcha из memory: forgejo-release-gotchas #7).
  • Spec отклонения (зафиксированы, не додумано): (1) YAML в issue использовал bash-syntax (\ line-continuation, $VAR без $env:, curl alias) — не работает в PowerShell 5.1, адаптировано под curl.exe + $env:VAR + Set-Location. (2) Spec утверждал "глобальная утилита сама сформирует имя при upload" — неверно: скрипт ищет локальный файл {name}-{os}-{arch}.zip в CWD (см. main() line 302-303), поэтому шаг "Package release" переименован в voice-assistant-windows-x64.zip и запуск идёт из dist/. (3) Запуск из dist/ нужен т.к. скрипт ищет zip в CWD, а zip лежит в dist/.
  • uv.lock — НЕ коммитится: diff requires-python>=3.12→3.13 — остаточный мусор от PR #23, не относится к задаче.

Pending

  • PATCH существующего релиза v0.1.0 на Forgejo: переименовать attachment voice-assistant.zipvoice-assistant-windows-x64.zip и обновить body (добавить ## Системные требования заголовок с Windows 10+ (64-bit)) — отдельная ручная операция через API.
  • Перенос тестов экстрактора release_notes.py в opencode-config (issue #65 уже слит, но тесты могли остаться — проверить).

Closes #32

## Что сделано - Удалены локальные `scripts/create_release.py` и `scripts/release_notes.py` (заменены глобальными из `opencode-config`). - Удалён `tests/test_create_release.py` (тестировал экстрактор `release_notes.py`, теперь глобальный — тестируется в `opencode-config` issue #65, в voice_assistant не нужен). - В `.github/workflows/release.yml` шаг "Create Forgejo release" переписан: скачивание глобальных `create_release.py` + `release_notes.py` через `curl.exe` из `opencode-config@main`, запуск с `RELEASE_PLATFORM="Windows 10+ (64-bit)"`. Шаг "Package release" переименован zip в `voice-assistant-windows-x64.zip` (имя, которое глобальный скрипт ищет в CWD: `{pyproject.name}-{RUNNER_OS.lower()}-{RUNNER_ARCH.lower()}.zip`). - В `README.md` секции Requirements (EN) и Требования (RU): убрано упоминание "Python 3.13" (встроен в exe через PyInstaller), оставлено `Windows 10+ (64-bit)`. ## Почему - Единый источник истины для логики релиза во всех репо `slaid098` — глобальный `create_release.py` в `opencode-config` (issue #65, PR #66, commit e00a060). Дублирование логики в каждом репо → drift, баги в одном репо не фиксятся в других. - Платформа `Windows 10+ (64-bit)` теперь детерминированно попадает в body релиза через `RELEASE_PLATFORM` env var (глобальный скрипт формирует `## Системные требования` заголовок), а не через CHANGELOG (PR #31 уже убрал платформу из CHANGELOG). ## Watch out - **Зависимость от `opencode-config` issue #65 (PR #66)** — глобальный скрипт должен быть в `main` ветке `opencode-config`. Если его нет или он сломан — релиз упадёт. Это single point of failure, но оправдано единым источником истины. - **`$GITHUB_SERVER_URL`** — Forgejo runner auto-set env var (НЕ `$FORGEJO_URL`, который может не быть в runner env). Глобальный скрипт поддерживает оба (`FORGEJO_URL` || `GITHUB_SERVER_URL`), но в workflow используется именно `$GITHUB_SERVER_URL`. - **`curl.exe`** — обязательно суффикс `.exe` в PowerShell 5.1; `curl` без суффикса — алиас на `Invoke-WebRequest`, который ломает multipart-загрузку ассетов (gotcha из memory: forgejo-release-gotchas #7). - **Spec отклонения (зафиксированы, не додумано):** (1) YAML в issue использовал bash-syntax (`\` line-continuation, `$VAR` без `$env:`, `curl` alias) — не работает в PowerShell 5.1, адаптировано под `curl.exe` + `$env:VAR` + `Set-Location`. (2) Spec утверждал "глобальная утилита сама сформирует имя при upload" — неверно: скрипт ищет локальный файл `{name}-{os}-{arch}.zip` в CWD (см. `main()` line 302-303), поэтому шаг "Package release" переименован в `voice-assistant-windows-x64.zip` и запуск идёт из `dist/`. (3) Запуск из `dist/` нужен т.к. скрипт ищет zip в CWD, а zip лежит в `dist/`. - **`uv.lock`** — НЕ коммитится: diff `requires-python>=3.12→3.13` — остаточный мусор от PR #23, не относится к задаче. ## Pending - PATCH существующего релиза v0.1.0 на Forgejo: переименовать attachment `voice-assistant.zip` → `voice-assistant-windows-x64.zip` и обновить body (добавить `## Системные требования` заголовок с `Windows 10+ (64-bit)`) — отдельная ручная операция через API. - Перенос тестов экстрактора `release_notes.py` в `opencode-config` (issue #65 уже слит, но тесты могли остаться — проверить). Closes #32
docs(readme): drop Python 3.13 from requirements, keep Windows 10+ only
All checks were successful
CI / lint (pull_request) Successful in 1m38s
CI / complexity (pull_request) Successful in 1m38s
CI / typecheck (pull_request) Successful in 1m42s
CI / test (pull_request) Successful in 3m51s
ae42069b8c
Author
Owner

Code Review Summary

Summary

PR adopts the global create_release.py from opencode-config — good direction (single source of truth for release logic). Removal of local scripts and tests is clean (no dangling imports). However, the workflow has a critical path-resolution bug that will make every release fail on the next tag push, plus a README inconsistency.

Critical (must fix before merge)

  • .github/workflows/release.yml:78-79 [correctness] Set-Location dist breaks pyproject.toml lookup in the global script. The global create_release.py main() calls read_project_name(Path("pyproject.toml")) (resolves against CWD) and build_body(Path("CHANGELOG.md"), ...). After Set-Location dist, CWD is dist/, but:

    • pyproject.toml is at repo root → NOT in dist/read_project_name calls _die("pyproject.toml not found at pyproject.toml")sys.exit(1). Release step fails, no release created.
    • CHANGELOG.md is copied to dist/voice-assistant/CHANGELOG.md (subdirectory) → NOT at dist/CHANGELOG.mdextract_changelog_section returns "" (non-fatal, but release body loses changelog notes).
    • The zip dist/voice-assistant-windows-x64.zip IS found correctly (only thing that works in dist/).

    Fix: copy pyproject.toml and CHANGELOG.md into dist/ (not dist/voice-assistant/) before Set-Location. Add to the "Package release" step or the "Create Forgejo release" step:

    Copy-Item pyproject.toml dist/
    Copy-Item CHANGELOG.md dist/
    

    Then Set-Location dist; python ..\scripts\create_release.py will find all three files (pyproject.toml, CHANGELOG.md, voice-assistant-windows-x64.zip) in CWD.

  • README.md:68 and README.md:131 [docs] Stale zip filename. The workflow now produces voice-assistant-windows-x64.zip, but both EN and RU Quick Start still say "Download voice-assistant.zip". Users following the instructions won't find that file in releases.
    Fix: Replace voice-assistant.zipvoice-assistant-windows-x64.zip on both lines (68 and 131).

Cross-file impact: missing paired update

PR меняет zip filename writer (release.yml:68: voice-assistant.zipvoice-assistant-windows-x64.zip). README читает этот filename (README.md:68, README.md:131):

  • README EN:68 говорит "Download voice-assistant.zip"
  • README RU:131 говорит "Скачайте voice-assistant.zip"
  • workflow теперь пишет voice-assistant-windows-x64.zip → README врёт пользователю

ТЗ на fix:

  • Что: обновить оба упоминания voice-assistant.zipvoice-assistant-windows-x64.zip
  • Где: README.md:68 (EN), README.md:131 (RU)
  • Контракт: filename в README совпадает с -DestinationPath в release.yml:68
  • Тесты: не требуется (docs)

Добавьте fix в этот PR. ~2 строки в README.

Warnings (should fix)

  • .github/workflows/release.yml:76-77 [security] No integrity verification of downloaded scripts. curl.exe -fsSL ... -o scripts/create_release.py downloads from opencode-config@main without pinning to a commit SHA or verifying a checksum. If main is compromised or a bad commit lands, the release pipeline executes arbitrary code with GITHUB_TOKEN (full repo access). The PR body acknowledges "single point of failure" but frames it as availability, not integrity/supply-chain.
    Fix: Pin to a specific commit: $env:GITHUB_SERVER_URL/slaid098/opencode-config/raw/commit/<sha>/.opencode/scripts/create_release.py. Update the SHA when bumping the global script.

  • uv.lock [hygiene] Working tree has a modified uv.lock (visible at checkout: M uv.lock) that is NOT part of the PR diff (5 changed files, uv.lock absent). PR body notes "residual trash from PR #23". Not blocking for this PR, but the branch has stale local state — ensure it doesn't accidentally get committed in a follow-up.

Positives

  • Clean removal: scripts/create_release.py, scripts/release_notes.py, tests/test_create_release.py deleted with no dangling imports (grep confirmed no from scripts.release_notes / from scripts.create_release anywhere in src/ or tests/).
  • Test collection not broken: no remaining test references the deleted modules.
  • PowerShell correctness: curl.exe (not curl alias), $env:GITHUB_SERVER_URL (not $GITHUB_SERVER_URL), $ErrorActionPreference = 'Stop' — all correct for PowerShell 5.1.
  • RELEASE_PLATFORM: "Windows 10+ (64-bit)" correctly injected → global script builds ## Системные требования heading deterministically.
  • README Python 3.13 removal is correct (bundled via PyInstaller, not a runtime requirement).
  • PR body is thorough — 4 headings filled, ## Watch out honestly documents the 3 spec deviations (PowerShell adaptation, zip rename, dist/ CWD) with line references to the global script.

Verdict: REQUEST_CHANGES

## Code Review Summary ### Summary PR adopts the global `create_release.py` from `opencode-config` — good direction (single source of truth for release logic). Removal of local scripts and tests is clean (no dangling imports). However, the workflow has a **critical path-resolution bug** that will make every release fail on the next tag push, plus a README inconsistency. ### Critical (must fix before merge) - **`.github/workflows/release.yml:78-79`** [correctness] `Set-Location dist` breaks `pyproject.toml` lookup in the global script. The global `create_release.py` `main()` calls `read_project_name(Path("pyproject.toml"))` (resolves against CWD) and `build_body(Path("CHANGELOG.md"), ...)`. After `Set-Location dist`, CWD is `dist/`, but: - `pyproject.toml` is at repo root → NOT in `dist/` → `read_project_name` calls `_die("pyproject.toml not found at pyproject.toml")` → `sys.exit(1)`. **Release step fails, no release created.** - `CHANGELOG.md` is copied to `dist/voice-assistant/CHANGELOG.md` (subdirectory) → NOT at `dist/CHANGELOG.md` → `extract_changelog_section` returns `""` (non-fatal, but release body loses changelog notes). - The zip `dist/voice-assistant-windows-x64.zip` IS found correctly (only thing that works in `dist/`). Fix: copy `pyproject.toml` and `CHANGELOG.md` into `dist/` (not `dist/voice-assistant/`) before `Set-Location`. Add to the "Package release" step or the "Create Forgejo release" step: ```powershell Copy-Item pyproject.toml dist/ Copy-Item CHANGELOG.md dist/ ``` Then `Set-Location dist; python ..\scripts\create_release.py` will find all three files (`pyproject.toml`, `CHANGELOG.md`, `voice-assistant-windows-x64.zip`) in CWD. - **`README.md:68` and `README.md:131`** [docs] Stale zip filename. The workflow now produces `voice-assistant-windows-x64.zip`, but both EN and RU Quick Start still say "Download `voice-assistant.zip`". Users following the instructions won't find that file in releases. Fix: Replace `voice-assistant.zip` → `voice-assistant-windows-x64.zip` on both lines (68 and 131). ## Cross-file impact: missing paired update PR меняет zip filename writer (`release.yml:68`: `voice-assistant.zip` → `voice-assistant-windows-x64.zip`). README читает этот filename (`README.md:68`, `README.md:131`): - README EN:68 говорит "Download `voice-assistant.zip`" - README RU:131 говорит "Скачайте `voice-assistant.zip`" - workflow теперь пишет `voice-assistant-windows-x64.zip` → README врёт пользователю ТЗ на fix: - Что: обновить оба упоминания `voice-assistant.zip` → `voice-assistant-windows-x64.zip` - Где: `README.md:68` (EN), `README.md:131` (RU) - Контракт: filename в README совпадает с `-DestinationPath` в `release.yml:68` - Тесты: не требуется (docs) Добавьте fix в этот PR. ~2 строки в README. ### Warnings (should fix) - **`.github/workflows/release.yml:76-77`** [security] No integrity verification of downloaded scripts. `curl.exe -fsSL ... -o scripts/create_release.py` downloads from `opencode-config@main` without pinning to a commit SHA or verifying a checksum. If `main` is compromised or a bad commit lands, the release pipeline executes arbitrary code with `GITHUB_TOKEN` (full repo access). The PR body acknowledges "single point of failure" but frames it as availability, not integrity/supply-chain. Fix: Pin to a specific commit: `$env:GITHUB_SERVER_URL/slaid098/opencode-config/raw/commit/<sha>/.opencode/scripts/create_release.py`. Update the SHA when bumping the global script. - **`uv.lock`** [hygiene] Working tree has a modified `uv.lock` (visible at checkout: `M uv.lock`) that is NOT part of the PR diff (5 changed files, uv.lock absent). PR body notes "residual trash from PR #23". Not blocking for this PR, but the branch has stale local state — ensure it doesn't accidentally get committed in a follow-up. ### Positives - Clean removal: `scripts/create_release.py`, `scripts/release_notes.py`, `tests/test_create_release.py` deleted with no dangling imports (grep confirmed no `from scripts.release_notes` / `from scripts.create_release` anywhere in `src/` or `tests/`). - Test collection not broken: no remaining test references the deleted modules. - PowerShell correctness: `curl.exe` (not `curl` alias), `$env:GITHUB_SERVER_URL` (not `$GITHUB_SERVER_URL`), `$ErrorActionPreference = 'Stop'` — all correct for PowerShell 5.1. - `RELEASE_PLATFORM: "Windows 10+ (64-bit)"` correctly injected → global script builds `## Системные требования` heading deterministically. - README Python 3.13 removal is correct (bundled via PyInstaller, not a runtime requirement). - PR body is thorough — 4 headings filled, `## Watch out` honestly documents the 3 spec deviations (PowerShell adaptation, zip rename, `dist/` CWD) with line references to the global script. ### Verdict: REQUEST_CHANGES
fix(release): copy pyproject.toml+CHANGELOG.md to dist, sync README zip name
All checks were successful
CI / lint (pull_request) Successful in 1m39s
CI / complexity (pull_request) Successful in 1m39s
CI / typecheck (pull_request) Successful in 1m43s
CI / test (pull_request) Successful in 3m53s
6742fd2583
Author
Owner

Code Review Summary

Обе критические проблемы из предыдущего review исправлены корректно, новых проблем не появилось. CI green.

Исправления из предыдущего review

Problem 1 — release.yml Set-Location ломает lookup pyproject.toml:

  • Copy-Item pyproject.toml dist/ (строка 78) и Copy-Item CHANGELOG.md dist/ (строка 79) добавлены ДО Set-Location dist (строка 80). Порядок правильный — копирование в dist/ выполняется из корня репо, до смены CWD.
  • Проверено против исходника глобального create_release.py из opencode-config@main: скрипт ищет Path("pyproject.toml"), Path("CHANGELOG.md") и Path(zip_name) через относительные пути в CWD. После Set-Location dist CWD = dist/, все три файла (pyproject.toml, CHANGELOG.md, voice-assistant-windows-x64.zip) находятся там. Контракт соблюдён.

Problem 2 — README stale zip filename:

  • voice-assistant.zipvoice-assistant-windows-x64.zip в EN (README:68) и RU (README:131). rg "voice-assistant\.zip" (без -windows-x64) — 0 совпадений, stale-имя полностью удалено из репо.
  • Имя zip в README совпадает с release.yml:68 и с именем, которое генерирует глобальный скрипт: asset_filename(name, os_token) = {name}-{os}-{arch}.zip = voice-assistant-windows-x64.zip (pyproject.toml name = "voice-assistant", RUNNER_OS=Windows, RUNNER_ARCH=X64). Тройная консистентность.

Cross-file impact analysis

  • PR удаляет локальные scripts/create_release.py, scripts/release_notes.py, tests/test_create_release.py (writers). rg по репо — никто не импортирует удалённые модули, кроме удалённого теста. Чисто.
  • Связь writer↔reader: release.yml (скачивает и запускает глобальный скрипт) ↔ глобальный create_release.py (ожидает файлы в CWD). Обе стороны обновлены в одном PR — окно сломанного main закрыто.
  • RELEASE_PLATFORM: "Windows 10+ (64-bit)" (release.yml:72) совпадает с README:75 (EN) и README:138 (RU). Глобальный скрипт использует этот env var для формирования ## Системные требования заголовка в body релиза.

Positives

  • Порядок Copy-ItemSet-Location правильный (копирование до смены CWD — критично для относительных путей скрипта).
  • curl.exe с суффиксом .exe — корректно для PowerShell 5.1 (без суффикса curl — алиас на Invoke-WebRequest, ломает multipart).
  • $ErrorActionPreference = 'Stop' — fail-fast на любом шаге.
  • PR body подробно документирует spec-отклонения и обоснования (запуск из dist/, curl.exe, $GITHUB_SERVER_URL vs $FORGEJO_URL).
  • uv.lock явно отмечен как не относящийся к задаче и не закоммичен.

Suggestions (info, not blocking)

  • release.yml:81 [style] python ..\scripts\create_release.py — обратный слэш корректен для PowerShell на Windows, но можно рассмотреть python scripts/create_release.py после Set-Location dist (скрипт лежит в scripts/ относительно корня, а CWD = dist/, поэтому ..\scripts\ правильно). Не менять — текущий вариант рабочий.

Verdict: APPROVE

## Code Review Summary Обе критические проблемы из предыдущего review исправлены корректно, новых проблем не появилось. CI green. ### Исправления из предыдущего review **Problem 1 — release.yml Set-Location ломает lookup pyproject.toml:** ✅ - `Copy-Item pyproject.toml dist/` (строка 78) и `Copy-Item CHANGELOG.md dist/` (строка 79) добавлены ДО `Set-Location dist` (строка 80). Порядок правильный — копирование в `dist/` выполняется из корня репо, до смены CWD. - Проверено против исходника глобального `create_release.py` из `opencode-config@main`: скрипт ищет `Path("pyproject.toml")`, `Path("CHANGELOG.md")` и `Path(zip_name)` через относительные пути в CWD. После `Set-Location dist` CWD = `dist/`, все три файла (`pyproject.toml`, `CHANGELOG.md`, `voice-assistant-windows-x64.zip`) находятся там. Контракт соблюдён. **Problem 2 — README stale zip filename:** ✅ - `voice-assistant.zip` → `voice-assistant-windows-x64.zip` в EN (README:68) и RU (README:131). `rg "voice-assistant\.zip"` (без `-windows-x64`) — 0 совпадений, stale-имя полностью удалено из репо. - Имя zip в README совпадает с release.yml:68 и с именем, которое генерирует глобальный скрипт: `asset_filename(name, os_token)` = `{name}-{os}-{arch}.zip` = `voice-assistant-windows-x64.zip` (pyproject.toml `name = "voice-assistant"`, RUNNER_OS=Windows, RUNNER_ARCH=X64). Тройная консистентность. ### Cross-file impact analysis - PR удаляет локальные `scripts/create_release.py`, `scripts/release_notes.py`, `tests/test_create_release.py` (writers). `rg` по репо — никто не импортирует удалённые модули, кроме удалённого теста. Чисто. - Связь writer↔reader: release.yml (скачивает и запускает глобальный скрипт) ↔ глобальный `create_release.py` (ожидает файлы в CWD). Обе стороны обновлены в одном PR — окно сломанного main закрыто. - `RELEASE_PLATFORM: "Windows 10+ (64-bit)"` (release.yml:72) совпадает с README:75 (EN) и README:138 (RU). Глобальный скрипт использует этот env var для формирования `## Системные требования` заголовка в body релиза. ### Positives - Порядок `Copy-Item` → `Set-Location` правильный (копирование до смены CWD — критично для относительных путей скрипта). - `curl.exe` с суффиксом `.exe` — корректно для PowerShell 5.1 (без суффикса `curl` — алиас на `Invoke-WebRequest`, ломает multipart). - `$ErrorActionPreference = 'Stop'` — fail-fast на любом шаге. - PR body подробно документирует spec-отклонения и обоснования (запуск из `dist/`, `curl.exe`, `$GITHUB_SERVER_URL` vs `$FORGEJO_URL`). - `uv.lock` явно отмечен как не относящийся к задаче и не закоммичен. ### Suggestions (info, not blocking) - **release.yml:81** [style] `python ..\scripts\create_release.py` — обратный слэш корректен для PowerShell на Windows, но можно рассмотреть `python scripts/create_release.py` после `Set-Location dist` (скрипт лежит в `scripts/` относительно корня, а CWD = `dist/`, поэтому `..\scripts\` правильно). Не менять — текущий вариант рабочий. ### Verdict: APPROVE
slaid098 merged commit 5d291f196c into master 2026-08-12 19:45:24 +03:00
slaid098 deleted branch refactor/release/adopt-global-script 2026-08-12 19:45:24 +03:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
slaid098/voice_assistant!33
No description provided.