Compare commits

..

No commits in common. "main" and "v1.2.0" have entirely different histories.
main ... v1.2.0

8 changed files with 19 additions and 29 deletions

5
.gitignore vendored
View file

@ -7,10 +7,7 @@ dist/
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
# Local build cache (prod/test/old portable folders per version) # Distribution staging folders (built per-version, attached to GitHub Releases)
builds/
# Legacy staging folders (kept for compatibility with old checkouts)
portable-v*/ portable-v*/
# Local backup of release archives (kept locally for history, not in repo) # Local backup of release archives (kept locally for history, not in repo)

View file

@ -6,11 +6,6 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
## [Unreleased] ## [Unreleased]
## [1.2.1] - 2026-05-18
### Changed
- The `Update available (vX.Y.Z)` hint in the header is now a clickable hyperlink that opens the GitHub releases page (OSC 8 terminal hyperlink). Modern terminals (Windows Terminal, VS Code, WezTerm, most Linux/macOS terminals) render it as a link — `Ctrl+Click` to follow. Older consoles show the plain text, so nothing breaks.
- Russian tagline tightened: dropped the `для инженера` phrase, the wording was carried over from an earlier draft and felt out of place.
## [1.2.0] - 2026-05-18 ## [1.2.0] - 2026-05-18
### Added ### Added
- Russian UI translation. On first launch the application asks which language to use (`1) English`, `2) Русский`) and writes the answer to a fresh `config.ini` next to `dhcpsrv.exe`. To change the language later, edit `language = en` / `language = ru` in that file — the comment at the top of the file explains how, in both languages. - Russian UI translation. On first launch the application asks which language to use (`1) English`, `2) Русский`) and writes the answer to a fresh `config.ini` next to `dhcpsrv.exe`. To change the language later, edit `language = en` / `language = ru` in that file — the comment at the top of the file explains how, in both languages.
@ -51,8 +46,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
- Scrollback cleared on startup so mouse-wheel doesn't expose pre-launch text. - Scrollback cleared on startup so mouse-wheel doesn't expose pre-launch text.
- MIT licensed. - MIT licensed.
[Unreleased]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.2.1...HEAD [Unreleased]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.2.0...HEAD
[1.2.1]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.2.0...v1.2.1
[1.2.0]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.3...v1.2.0 [1.2.0]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.3...v1.2.0
[1.1.3]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.2...v1.1.3 [1.1.3]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.2...v1.1.3
[1.1.2]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.1...v1.1.2 [1.1.2]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.1...v1.1.2

View file

@ -3,7 +3,7 @@
[![Latest release](https://img.shields.io/github/v/release/Engelgardt23/dhcpsrv)](https://github.com/Engelgardt23/dhcpsrv/releases/latest) [![Latest release](https://img.shields.io/github/v/release/Engelgardt23/dhcpsrv)](https://github.com/Engelgardt23/dhcpsrv/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
🇺🇸 English | [🇷🇺 Русский](README.ru.md) 🇬🇧 English | [🇷🇺 На русском](README.ru.md)
A tiny portable **DHCP server** for the laptop of a storage/server engineer. A tiny portable **DHCP server** for the laptop of a storage/server engineer.
One double-click — pick a NIC — done. Live table of clients, ping status, packet counters. No install, no Python required on the target machine. One double-click — pick a NIC — done. Live table of clients, ping status, packet counters. No install, no Python required on the target machine.
@ -45,8 +45,8 @@ The asset is `dhcpsrv-portable-vX.Y.Z.zip` (~12 MB).
└─────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────┘
┌─ Clients ───────────────────────────────────────────────────────────────┐ ┌─ Clients ───────────────────────────────────────────────────────────────┐
│ # │ IP │ Hostname │ MAC │ Last seen │ Ping │ │ # │ IP │ Hostname │ MAC │ Last seen │ Ping │
│ 1 │ 10.10.10.2 │ server-01 │ a0:c5:f2:13:57:46 │ 17:42:18 │ OK │ │ 1 │ 10.10.10.2 │ vegman-r120 │ a0:c5:f2:13:57:46 │ 17:42:18 │ OK │
│ 2 │ 10.10.10.3 │ server-02 │ 70:b3:d5:11:22:33 │ 17:42:21 │ -- │ │ 2 │ 10.10.10.3 │ vegman-s220 │ 70:b3:d5:11:22:33 │ 17:42:21 │ -- │
└─────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────┘
┌─ Events ────────────────────────────────────────────────────────────────┐ ┌─ Events ────────────────────────────────────────────────────────────────┐
│ [17:42:18] DISCOVER a0:c5:f2:13:57:46 → OFFER 10.10.10.2 │ │ [17:42:18] DISCOVER a0:c5:f2:13:57:46 → OFFER 10.10.10.2 │
@ -56,7 +56,7 @@ The asset is `dhcpsrv-portable-vX.Y.Z.zip` (~12 MB).
## Typical scenarios ## Typical scenarios
- **Server with shared LOM** — one cable into the BMC/host port, BMC and the host OS both get IPs from this DHCP. - **VEGMAN with shared LOM** — one cable into the BMC/host port, BMC and the host OS both get IPs from this DHCP.
- **8-port switch** — laptop on one port, up to 7 servers on the rest; the 50-address pool covers everyone. - **8-port switch** — laptop on one port, up to 7 servers on the rest; the 50-address pool covers everyone.
- **Direct cable into a dedicated Mgmt port** — single client (the BMC). - **Direct cable into a dedicated Mgmt port** — single client (the BMC).

View file

@ -3,7 +3,7 @@
[![Последний релиз](https://img.shields.io/github/v/release/Engelgardt23/dhcpsrv)](https://github.com/Engelgardt23/dhcpsrv/releases/latest) [![Последний релиз](https://img.shields.io/github/v/release/Engelgardt23/dhcpsrv)](https://github.com/Engelgardt23/dhcpsrv/releases/latest)
[![Лицензия: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Лицензия: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[🇺🇸 English](README.md) | 🇷🇺 Русский [🇬🇧 English](README.md) | 🇷🇺 На русском
Маленький портативный **DHCP-сервер** для ноутбука инженера хранения / серверов. Маленький портативный **DHCP-сервер** для ноутбука инженера хранения / серверов.
Двойной клик — выбрал сетевую — готово. Живая таблица клиентов, статус ping, счётчики пакетов. Ничего не устанавливается, Python на целевой машине не нужен. Двойной клик — выбрал сетевую — готово. Живая таблица клиентов, статус ping, счётчики пакетов. Ничего не устанавливается, Python на целевой машине не нужен.
@ -46,8 +46,8 @@
└─────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────┘
┌─ Клиенты ───────────────────────────────────────────────────────────────┐ ┌─ Клиенты ───────────────────────────────────────────────────────────────┐
│ # │ IP │ Имя хоста │ MAC │ Последний │ Пинг │ │ # │ IP │ Имя хоста │ MAC │ Последний │ Пинг │
│ 1 │ 10.10.10.2 │ server-01 │ a0:c5:f2:13:57:46 │ 17:42:18 │ OK │ │ 1 │ 10.10.10.2 │ vegman-r120 │ a0:c5:f2:13:57:46 │ 17:42:18 │ OK │
│ 2 │ 10.10.10.3 │ server-02 │ 70:b3:d5:11:22:33 │ 17:42:21 │ -- │ │ 2 │ 10.10.10.3 │ vegman-s220 │ 70:b3:d5:11:22:33 │ 17:42:21 │ -- │
└─────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────┘
┌─ События ───────────────────────────────────────────────────────────────┐ ┌─ События ───────────────────────────────────────────────────────────────┐
│ [17:42:18] DISCOVER a0:c5:f2:13:57:46 → OFFER 10.10.10.2 │ │ [17:42:18] DISCOVER a0:c5:f2:13:57:46 → OFFER 10.10.10.2 │
@ -57,7 +57,7 @@
## Типичные сценарии ## Типичные сценарии
- **Сервер с общим LOM** — один кабель в порт BMC/host, и BMC и хост-ОС получают IP с этого DHCP. - **VEGMAN с общим LOM** — один кабель в порт BMC/host, и BMC и хост-ОС получают IP с этого DHCP.
- **8-портовый свитч** — ноут на одном порту, до 7 серверов на остальных; пул из 50 адресов покрывает всех. - **8-портовый свитч** — ноут на одном порту, до 7 серверов на остальных; пул из 50 адресов покрывает всех.
- **Прямой кабель в выделенный Mgmt-порт** — один клиент (BMC). - **Прямой кабель в выделенный Mgmt-порт** — один клиент (BMC).

View file

@ -6,5 +6,5 @@ The single source of truth for the project version. Bump this before tagging
a release; CI reads the tag, the code reads this constant. a release; CI reads the tag, the code reads this constant.
""" """
__version__ = "1.2.2" __version__ = "1.2.0"
GITHUB_REPO = "engel/dhcpsrv" # на Forgejo (git.engelgardt23.ru) GITHUB_REPO = "Engelgardt23/dhcpsrv"

View file

@ -11,7 +11,7 @@ from rich.console import Console
from rich.prompt import Confirm, Prompt from rich.prompt import Confirm, Prompt
from rich.table import Table from rich.table import Table
from . import __version__, GITHUB_REPO from . import __version__
from .platform_win import enable_vt, require_admin from .platform_win import enable_vt, require_admin
from .update_check import check_for_update from .update_check import check_for_update
from .network import list_adapters, set_static_ip, revert_to_dhcp from .network import list_adapters, set_static_ip, revert_to_dhcp
@ -53,12 +53,10 @@ def main() -> None:
title = f"[bold cyan]dhcpsrv v{__version__}[/] {t('tagline')}" title = f"[bold cyan]dhcpsrv v{__version__}[/] {t('tagline')}"
latest = check_for_update() latest = check_for_update()
if latest: if latest:
release_url = f"https://git.engelgardt23.ru/{GITHUB_REPO}/releases/latest"
notice = t("update_available", tag=latest)
header = Table.grid(expand=True) header = Table.grid(expand=True)
header.add_column(justify="left", ratio=1) header.add_column(justify="left", ratio=1)
header.add_column(justify="right") header.add_column(justify="right")
header.add_row(title, f"[dim][link={release_url}]{notice}[/link][/]") header.add_row(title, f"[dim]{t('update_available', tag=latest)}[/]")
console.print(header) console.print(header)
else: else:
console.print(title) console.print(title)

View file

@ -20,7 +20,7 @@ STRINGS: dict[str, dict[str, str]] = {
"en": { "en": {
# app.py / startup # app.py / startup
"tagline": "- portable laptop-side DHCP server", "tagline": "- portable laptop-side DHCP server",
"update_available": "Update available ({tag})", "update_available": "update available ({tag})",
"available_adapters": "Available adapters", "available_adapters": "Available adapters",
"no_adapters": "No suitable wired adapters found.", "no_adapters": "No suitable wired adapters found.",
"select_adapter": "Select adapter number", "select_adapter": "Select adapter number",
@ -56,8 +56,8 @@ STRINGS: dict[str, dict[str, str]] = {
"no_events": "(no events yet)", "no_events": "(no events yet)",
}, },
"ru": { "ru": {
"tagline": "— портативный DHCP-сервер", "tagline": "— портативный DHCP-сервер для инженера",
"update_available": "Доступно обновление ({tag})", "update_available": "доступно обновление ({tag})",
"available_adapters": "Доступные адаптеры", "available_adapters": "Доступные адаптеры",
"no_adapters": "Подходящие проводные адаптеры не найдены.", "no_adapters": "Подходящие проводные адаптеры не найдены.",
"select_adapter": "Введите номер адаптера", "select_adapter": "Введите номер адаптера",

View file

@ -29,8 +29,9 @@ def check_for_update() -> str | None:
currently running version. Returns None when up to date, offline, or on currently running version. Returns None when up to date, offline, or on
any error the caller decides how (or whether) to render the hint.""" any error the caller decides how (or whether) to render the hint."""
try: try:
url = f"https://git.engelgardt23.ru/api/v1/repos/{GITHUB_REPO}/releases/latest" url = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest"
req = urllib.request.Request(url, headers={ req = urllib.request.Request(url, headers={
"Accept": "application/vnd.github+json",
"User-Agent": f"dhcpsrv/{__version__}", "User-Agent": f"dhcpsrv/{__version__}",
}) })
with urllib.request.urlopen(req, timeout=3) as r: with urllib.request.urlopen(req, timeout=3) as r: