6 Commits

Author SHA1 Message Date
engel 215e264e5f v1.2.3: fully Gitea-hosted; self-contained CI
Release / build (push) Successful in 47s
2026-06-01 13:44:49 +03:00
engel 99573cba5c ci: resolve version in pwsh (bash shell broken on Windows host runner)
Release / build (push) Successful in 29s
2026-06-01 12:42:59 +03:00
engel ac1c40d754 ci: make release workflow fully self-contained (no GitHub actions)
Release / build (push) Failing after 13s
Replace actions/checkout + actions/setup-python (pulled from github.com by
default) with a manual git clone at the tag and the runner's system Python.
2026-06-01 12:40:41 +03:00
engel 9ef788fe5b ci: use built-in Gitea Actions token for release publish
Drops the need for a manually-managed RELEASE_TOKEN secret; adds
contents:write permission so github.token can create the release.
2026-06-01 12:36:26 +03:00
engel 27304992a0 chore: cut GitHub, make project Gitea-only
- relink README/CHANGELOG/pyproject/SECURITY to git.engelgardt23.ru
- port CI from GitHub Actions to .gitea/workflows (publishes Gitea release via API)
- rename GITHUB_REPO -> REPO; drop GitHub mentions in code/docs/comments
- remove .github (workflow + issue templates); drop made-by lines
2026-06-01 12:35:28 +03:00
engel 07b93d1382 chore: switch update-check and release URLs to self-hosted Forgejo
GitHub-репо заморожены; релизы теперь на git.engelgardt23.ru.
2026-05-28 07:14:45 +00:00
13 changed files with 71 additions and 144 deletions
@@ -8,24 +8,30 @@ on:
permissions: permissions:
contents: write contents: write
# Self-contained: no external (GitHub-hosted) actions. Runs on a self-hosted
# Windows runner (label `windows`) that already has Python 3.12 on PATH.
jobs: jobs:
build: build:
runs-on: windows-latest runs-on: windows
steps: steps:
- uses: actions/checkout@v4 - name: Checkout repository at tag
shell: pwsh
- name: Set up Python env:
uses: actions/setup-python@v5 TOKEN: ${{ github.token }}
with: run: |
python-version: '3.12' $base = "${{ github.server_url }}" -replace '^https://', ''
$url = "https://oauth2:$env:TOKEN@$base/${{ github.repository }}.git"
git clone --depth 1 --branch "$env:GITHUB_REF_NAME" $url .
- name: Install build dependencies - name: Install build dependencies
run: python -m pip install --upgrade pip pyinstaller rich run: python -m pip install --upgrade pip pyinstaller rich
- name: Resolve version from tag - name: Resolve version from tag
id: ver id: ver
shell: bash shell: pwsh
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" run: |
$v = "$env:GITHUB_REF_NAME" -replace '^v', ''
"version=$v" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Build executable - name: Build executable
run: python -m PyInstaller --onefile --uac-admin --console --name dhcpsrv --icon assets/icon.ico --paths src dhcpsrv-launcher.py run: python -m PyInstaller --onefile --uac-admin --console --name dhcpsrv --icon assets/icon.ico --paths src dhcpsrv-launcher.py
@@ -39,7 +45,6 @@ jobs:
Copy-Item dist/dhcpsrv.exe $folder/ Copy-Item dist/dhcpsrv.exe $folder/
@" @"
dhcpsrv v$ver - portable edition dhcpsrv v$ver - portable edition
made by engelgardt
Minimal laptop-side DHCP server for storage/server engineers. Minimal laptop-side DHCP server for storage/server engineers.
@@ -71,10 +76,19 @@ jobs:
"$hash $zip" | Out-File -FilePath "$zip.sha256" -Encoding ASCII -NoNewline "$hash $zip" | Out-File -FilePath "$zip.sha256" -Encoding ASCII -NoNewline
Get-Content "$zip.sha256" Get-Content "$zip.sha256"
- name: Create release - name: Publish Gitea release
uses: softprops/action-gh-release@v2 shell: pwsh
with: env:
files: | TOKEN: ${{ github.token }}
dhcpsrv-portable-v${{ steps.ver.outputs.version }}.zip run: |
dhcpsrv-portable-v${{ steps.ver.outputs.version }}.zip.sha256 $ver = '${{ steps.ver.outputs.version }}'
generate_release_notes: true $tag = "v$ver"
$api = "${{ github.server_url }}/api/v1/repos/${{ github.repository }}"
$hdr = @{ Authorization = "token $env:TOKEN" }
$body = @{ tag_name = $tag; name = $tag; draft = $false; prerelease = $false } | ConvertTo-Json
$rel = Invoke-RestMethod -Method Post -Uri "$api/releases" -Headers $hdr -ContentType 'application/json' -Body $body
$rid = $rel.id
foreach ($f in @("dhcpsrv-portable-v$ver.zip", "dhcpsrv-portable-v$ver.zip.sha256")) {
curl.exe -s -H "Authorization: token $env:TOKEN" -F "attachment=@$f" "$api/releases/$rid/assets?name=$f" | Out-Null
Write-Host "uploaded $f"
}
-56
View File
@@ -1,56 +0,0 @@
name: Bug report
description: Something doesn't work as expected
labels: ["bug"]
body:
- type: input
id: version
attributes:
label: Version
description: Visible in the startup banner.
placeholder: v1.1.0
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
placeholder: |
1. ...
2. ...
3. ...
validations:
required: true
- type: textarea
id: expected
attributes:
label: What you expected to happen
validations:
required: true
- type: textarea
id: actual
attributes:
label: What actually happened
description: Paste any error output verbatim. Screenshots are welcome.
validations:
required: true
- type: input
id: os
attributes:
label: Windows version
placeholder: e.g. Windows 11 24H2
- type: input
id: network
attributes:
label: Network setup (if relevant)
placeholder: e.g. USB Realtek NIC to a server's BMC port via an 8-port switch
- type: textarea
id: extra
attributes:
label: Anything else?
description: Workarounds tried, related links, etc.
-5
View File
@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/Engelgardt23/dhcpsrv/security/advisories/new
about: Please report security issues privately via GitHub Security Advisories — not as a public issue.
@@ -1,23 +0,0 @@
name: Feature request
description: Suggest a new feature or an improvement
labels: ["enhancement"]
body:
- type: textarea
id: motivation
attributes:
label: What's the use case?
description: What are you trying to do, and why is the current behavior not enough?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
+17 -11
View File
@@ -6,9 +6,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
## [Unreleased] ## [Unreleased]
## [1.2.3] - 2026-06-01
### Changed
- Project is now fully self-hosted on Gitea (`git.engelgardt23.ru`): update-check, links and badges no longer reference GitHub.
- CI moved to self-contained Gitea Actions (no GitHub-hosted actions); releases are built on a self-hosted Windows runner and published to the local instance.
## [1.2.1] - 2026-05-18 ## [1.2.1] - 2026-05-18
### Changed ### 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. - The `Update available (vX.Y.Z)` hint in the header is now a clickable hyperlink that opens the 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. - 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
@@ -35,11 +40,11 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
## [1.1.0] - 2026-05-16 ## [1.1.0] - 2026-05-16
### Added ### Added
- Auto-update check on startup. Polls GitHub `/releases/latest` with a 3-second timeout. If a newer version is available, prints a yellow notice and offers to open the download page in your browser. Silent on offline / API errors. - Auto-update check on startup. Polls the Gitea instance `/releases/latest` with a 3-second timeout. If a newer version is available, prints a yellow notice and offers to open the download page in your browser. Silent on offline / API errors.
## [1.0.0] - 2026-05-16 ## [1.0.0] - 2026-05-16
### Added ### Added
- First public release on GitHub. - First public release.
- Single portable `.exe` (~12 MB) — no Python required on target machines. - Single portable `.exe` (~12 MB) — no Python required on target machines.
- Full-screen TUI built on `rich`: header with server config + live counters (Leases / Pkts / DISCOVER / REQUEST / RELEASE), clients table (`#`, IP, Hostname, MAC, Last seen, Ping), scrolling events panel. - Full-screen TUI built on `rich`: header with server config + live counters (Leases / Pkts / DISCOVER / REQUEST / RELEASE), clients table (`#`, IP, Hostname, MAC, Last seen, Ping), scrolling events panel.
- Hardcoded sensible defaults: server `10.10.10.1/24`, pool `10.10.10.2..10.10.10.51` (50 addresses), lease `7200 s`, TFTP option (66/150) = server IP. - Hardcoded sensible defaults: server `10.10.10.1/24`, pool `10.10.10.2..10.10.10.51` (50 addresses), lease `7200 s`, TFTP option (66/150) = server IP.
@@ -51,11 +56,12 @@ 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://git.engelgardt23.ru/engel/dhcpsrv/compare/v1.2.3...HEAD
[1.2.1]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.2.0...v1.2.1 [1.2.3]: https://git.engelgardt23.ru/engel/dhcpsrv/compare/v1.2.1...v1.2.3
[1.2.0]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.3...v1.2.0 [1.2.1]: https://git.engelgardt23.ru/engel/dhcpsrv/compare/v1.2.0...v1.2.1
[1.1.3]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.2...v1.1.3 [1.2.0]: https://git.engelgardt23.ru/engel/dhcpsrv/compare/v1.1.3...v1.2.0
[1.1.2]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.1...v1.1.2 [1.1.3]: https://git.engelgardt23.ru/engel/dhcpsrv/compare/v1.1.2...v1.1.3
[1.1.1]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.1.0...v1.1.1 [1.1.2]: https://git.engelgardt23.ru/engel/dhcpsrv/compare/v1.1.1...v1.1.2
[1.1.0]: https://github.com/Engelgardt23/dhcpsrv/compare/v1.0.0...v1.1.0 [1.1.1]: https://git.engelgardt23.ru/engel/dhcpsrv/compare/v1.1.0...v1.1.1
[1.0.0]: https://github.com/Engelgardt23/dhcpsrv/releases/tag/v1.0.0 [1.1.0]: https://git.engelgardt23.ru/engel/dhcpsrv/compare/v1.0.0...v1.1.0
[1.0.0]: https://git.engelgardt23.ru/engel/dhcpsrv/releases/tag/v1.0.0
+5 -6
View File
@@ -6,15 +6,14 @@
``` ```
dhcpsrv/ dhcpsrv/
├── .github/ ├── .gitea/
── workflows/release.yml ← CI: tag-driven build + GitHub Release ── workflows/release.yml ← CI: tag-driven build + Gitea Release
│ └── ISSUE_TEMPLATE/ ← bug / feature / security routing
├── src/dhcpsrv/ ← package source (≤200 lines per module) ├── src/dhcpsrv/ ← package source (≤200 lines per module)
│ ├── __init__.py ← single source of truth for __version__ │ ├── __init__.py ← single source of truth for __version__
│ ├── __main__.py ← entry: python -m dhcpsrv │ ├── __main__.py ← entry: python -m dhcpsrv
│ ├── app.py ← main flow, wires everything │ ├── app.py ← main flow, wires everything
│ ├── platform_win.py ← VT enable + UAC self-elevate │ ├── platform_win.py ← VT enable + UAC self-elevate
│ ├── update_check.py ← GitHub /releases/latest poll │ ├── update_check.py ← Gitea /releases/latest poll
│ ├── network.py ← list_adapters, netsh, ping (no shared state) │ ├── network.py ← list_adapters, netsh, ping (no shared state)
│ ├── dhcp.py ← DhcpConfig, DhcpServer, packet parse/build │ ├── dhcp.py ← DhcpConfig, DhcpServer, packet parse/build
│ └── ui.py ← rich-based full-screen TUI │ └── ui.py ← rich-based full-screen TUI
@@ -64,7 +63,7 @@ flag tells PyInstaller where to find the `dhcpsrv` package itself. Output:
4. Tag: `git tag vX.Y.Z`. 4. Tag: `git tag vX.Y.Z`.
5. Push: `git push && git push --tags`. 5. Push: `git push && git push --tags`.
That's it. GitHub Actions picks up the tag, builds the exe, writes the SHA-256, and creates the GitHub Release with the zip attached. That's it. Gitea Actions picks up the tag, builds the exe, writes the SHA-256, and creates the Gitea Release with the zip attached.
## Where features go ## Where features go
@@ -76,4 +75,4 @@ That's it. GitHub Actions picks up the tag, builds the exe, writes the SHA-256,
| Something shown in the header | `ui.py``Ui._render_header` | | Something shown in the header | `ui.py``Ui._render_header` |
| A startup check or banner line | `app.py``main()` | | A startup check or banner line | `app.py``main()` |
| A change to UAC / VT logic | `platform_win.py` | | A change to UAC / VT logic | `platform_win.py` |
| Tweaking the GitHub update-check UX | `update_check.py` | | Tweaking the update-check UX | `update_check.py` |
+3 -5
View File
@@ -1,6 +1,6 @@
# dhcpsrv # dhcpsrv
[![Latest release](https://img.shields.io/github/v/release/Engelgardt23/dhcpsrv)](https://github.com/Engelgardt23/dhcpsrv/releases/latest) [![Latest release](https://img.shields.io/gitea/v/release/engel/dhcpsrv?gitea_url=https%3A%2F%2Fgit.engelgardt23.ru&label=release)](https://git.engelgardt23.ru/engel/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)
@@ -10,13 +10,11 @@ One double-click — pick a NIC — done. Live table of clients, ping status, pa
Built for the “plug the cable in, watch a BMC pop up with an IP” workflow during firmware updates, recovery, and benchmarks. Built for the “plug the cable in, watch a BMC pop up with an IP” workflow during firmware updates, recovery, and benchmarks.
> **Made by engelgardt.**
--- ---
## Download ## Download
Grab the latest release: [**releases page**](https://github.com/Engelgardt23/dhcpsrv/releases/latest). Grab the latest release: [**releases page**](https://git.engelgardt23.ru/engel/dhcpsrv/releases/latest).
The asset is `dhcpsrv-portable-vX.Y.Z.zip` (~12 MB). The asset is `dhcpsrv-portable-vX.Y.Z.zip` (~12 MB).
## Run ## Run
@@ -39,7 +37,7 @@ The asset is `dhcpsrv-portable-vX.Y.Z.zip` (~12 MB).
## What's on screen ## What's on screen
``` ```
┌─ dhcpsrv v1.0.0 made by engelgardt ────────────────────────────────────┐ ┌─ dhcpsrv v1.0.0 ────────────────────────────────────────────────────────┐
│ Server: 10.10.10.1/255.255.255.0 Pool: 10.10.10.210.10.10.51 … │ │ Server: 10.10.10.1/255.255.255.0 Pool: 10.10.10.210.10.10.51 … │
│ Leases: 3/50 Pkts: 47 DISCOVER: 12 REQUEST: 11 RELEASE: 0 │ │ Leases: 3/50 Pkts: 47 DISCOVER: 12 REQUEST: 11 RELEASE: 0 │
└─────────────────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────────────────┘
+2 -4
View File
@@ -1,6 +1,6 @@
# dhcpsrv # dhcpsrv
[![Последний релиз](https://img.shields.io/github/v/release/Engelgardt23/dhcpsrv)](https://github.com/Engelgardt23/dhcpsrv/releases/latest) [![Последний релиз](https://img.shields.io/gitea/v/release/engel/dhcpsrv?gitea_url=https%3A%2F%2Fgit.engelgardt23.ru&label=release)](https://git.engelgardt23.ru/engel/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) | 🇷🇺 Русский
@@ -10,13 +10,11 @@
Сделан под сценарий «воткнул кабель, увидел как BMC получил IP» во время прошивки, восстановления и бенчмарков. Сделан под сценарий «воткнул кабель, увидел как BMC получил IP» во время прошивки, восстановления и бенчмарков.
> **Автор: engelgardt.**
--- ---
## Скачать ## Скачать
Последний релиз: [**страница релизов**](https://github.com/Engelgardt23/dhcpsrv/releases/latest). Последний релиз: [**страница релизов**](https://git.engelgardt23.ru/engel/dhcpsrv/releases/latest).
Архив `dhcpsrv-portable-vX.Y.Z.zip` (~12 МБ). Архив `dhcpsrv-portable-vX.Y.Z.zip` (~12 МБ).
## Запуск ## Запуск
+2 -4
View File
@@ -6,16 +6,14 @@ on the host — so vulnerability reports are very welcome.
## Supported versions ## Supported versions
Only the latest tagged release on GitHub is supported. Older versions will not Only the latest tagged release is supported. Older versions will not
get fixes; please upgrade first. get fixes; please upgrade first.
## How to report a vulnerability ## How to report a vulnerability
**Please do not open a public issue** for security-sensitive findings. **Please do not open a public issue** for security-sensitive findings.
Use GitHub's private security advisories: go to the Report privately by email to the maintainer at engelgardt2024@gmail.com.
[Security tab](../../security/advisories/new) of this repo and click
"Report a vulnerability". GitHub will route it privately.
Please include: Please include:
- The version you tested (the startup banner is enough). - The version you tested (the startup banner is enough).
+2 -2
View File
@@ -13,8 +13,8 @@ dependencies = ["rich>=13"]
dynamic = ["version"] dynamic = ["version"]
[project.urls] [project.urls]
Homepage = "https://github.com/Engelgardt23/dhcpsrv" Homepage = "https://git.engelgardt23.ru/engel/dhcpsrv"
Issues = "https://github.com/Engelgardt23/dhcpsrv/issues" Issues = "https://git.engelgardt23.ru/engel/dhcpsrv/issues"
[project.scripts] [project.scripts]
dhcpsrv = "dhcpsrv.app:main" dhcpsrv = "dhcpsrv.app:main"
+2 -3
View File
@@ -1,10 +1,9 @@
""" """
dhcpsrv - portable laptop-side DHCP server for storage/server engineers. dhcpsrv - portable laptop-side DHCP server for storage/server engineers.
made by engelgardt
The single source of truth for the project version. Bump this before tagging 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.1" __version__ = "1.2.3"
GITHUB_REPO = "Engelgardt23/dhcpsrv" REPO = "engel/dhcpsrv" # self-hosted Gitea (git.engelgardt23.ru)
+2 -2
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__, REPO
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,7 +53,7 @@ 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://github.com/{GITHUB_REPO}/releases/latest" release_url = f"https://git.engelgardt23.ru/{REPO}/releases/latest"
notice = t("update_available", tag=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)
+5 -6
View File
@@ -1,16 +1,16 @@
""" """
Auto-update check. Auto-update check.
On startup, ask GitHub for the latest release tag. If it's newer than the On startup, ask the Gitea instance for the latest release tag. If it's newer
local `__version__`, return the tag string so the caller can show a quiet than the local `__version__`, return the tag string so the caller can show a
hint in the header. Silent on any error (offline, rate-limit, etc.). quiet hint in the header. Silent on any error (offline, rate-limit, etc.).
""" """
from __future__ import annotations from __future__ import annotations
import json import json
import urllib.request import urllib.request
from . import __version__, GITHUB_REPO from . import __version__, REPO
def _parse_version(s: str) -> tuple[int, int, int]: def _parse_version(s: str) -> tuple[int, int, int]:
@@ -29,9 +29,8 @@ 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://api.github.com/repos/{GITHUB_REPO}/releases/latest" url = f"https://git.engelgardt23.ru/api/v1/repos/{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: