Test packaging #2

Merged
GarandPLG merged 2 commits from test_packaging into main 2026-10-19 15:56:30 +02:00
15 changed files with 409 additions and 12 deletions

No files matched your search

+109
View File
@@ -0,0 +1,109 @@
name: Linux package builds (test)
on:
push:
pull_request:
jobs:
linux:
runs-on: ubuntu-latest
container: node:22-bookworm
env:
CARGO_HOME: /root/.cargo
RUSTUP_HOME: /root/.rustup
RUST_VERSION: 1.99.1
steps:
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Install packaging dependencies
run: |
apt-get update
apt-get install -y --no-install-recommends ca-certificates curl build-essential pkg-config libasound2-dev python3 rpm cpio desktop-file-utils squashfs-tools p7zip-full file flatpak ostree zstd
- name: Rust toolchain cache
uses: https://github.com/actions/cache@v4
with:
path: |
/root/.rustup
/root/.cargo/bin
key: rust-toolchain-v1-linux-x64-${{ env.RUST_VERSION }}
- name: Install Rust
run: |
if [ ! -x "$CARGO_HOME/bin/rustup" ]; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup-init.sh
sh /tmp/rustup-init.sh -y --profile minimal --default-toolchain none --no-modify-path
fi
"$CARGO_HOME/bin/rustup" toolchain install "$RUST_VERSION" --profile minimal --component rustfmt --component clippy
"$CARGO_HOME/bin/rustup" default "$RUST_VERSION"
echo "$CARGO_HOME/bin" >> "$GITHUB_PATH"
- name: Cargo cache
uses: https://github.com/actions/cache@v4
with:
path: |
/root/.cargo/registry
/root/.cargo/git
target
key: packages-v1-linux-x64-${{ env.RUST_VERSION }}-${{ hashFiles('Cargo.lock') }}-${{ hashFiles('Cargo.toml', 'src/**', 'tests/**') }}
restore-keys: |
packages-v1-linux-x64-${{ env.RUST_VERSION }}-${{ hashFiles('Cargo.lock') }}-
- name: Compile Linux binary
id: compile
run: cargo build --release --locked
- name: Build deb
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
run: bash packaging/build.sh deb
- name: Build rpm
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
run: bash packaging/build.sh rpm
- name: Build AppImage
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
run: bash packaging/build.sh appimage
- name: Build Flatpak
if: ${{ !cancelled() && steps.compile.outcome == 'success' }}
run: bash packaging/build.sh flatpak
- name: Save test packages
if: ${{ !cancelled() }}
uses: https://github.com/actions/upload-artifact@v3
with:
name: linux-packages-x86_64
path: dist/*
if-no-files-found: error
retention-days: 7
aur:
runs-on: ubuntu-latest
container: archlinux:base-devel
steps:
- name: Install Arch dependencies
run: |
pacman -Syu --noconfirm --needed git nodejs rust cargo pkgconf alsa-lib python zstd
node --version
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Build AUR recipe from this commit
run: |
version=$(python -c 'import tomllib; print(tomllib.load(open("Cargo.toml", "rb"))["package"]["version"])')
recipe_version=$(bash -c 'source packaging/aur/PKGBUILD; printf "%s" "$pkgver"')
test "$version" = "$recipe_version"
mkdir -p /tmp/aur-build
cp packaging/aur/PKGBUILD /tmp/aur-build/
tar --exclude=.git --exclude=target --exclude=dist --transform="s,^\./,war-in-tunnels-$version/," -czf "/tmp/aur-build/war-in-tunnels-$version.tar.gz" .
cd /tmp/aur-build
checksum=$(sha256sum "war-in-tunnels-$version.tar.gz" | cut -d ' ' -f 1)
sed -i "s/sha256sums=('SKIP')/sha256sums=('$checksum')/" PKGBUILD
useradd -m builder
chown -R builder:builder /tmp/aur-build
runuser -u builder -- bash -c 'cd /tmp/aur-build && makepkg --cleanbuild --noconfirm && makepkg --printsrcinfo > .SRCINFO'
pacman -U --noconfirm ./*.pkg.tar.zst
cd /tmp
war-in-tunnels --version
- name: Save Arch test package and recipe
uses: https://github.com/actions/upload-artifact@v3
with:
name: arch-package-x86_64
path: |
/tmp/aur-build/*.pkg.tar.zst
/tmp/aur-build/PKGBUILD
/tmp/aur-build/.SRCINFO
/tmp/aur-build/*.tar.gz
if-no-files-found: error
retention-days: 7
+1
View File
@@ -1,4 +1,5 @@
/target
/result
/dist
war_in_tunnels.log
+4 -2
View File
@@ -23,14 +23,16 @@ nix develop -c cargo run -- --help
nix develop -c cargo run -- --disable-audio --view skirmish
```
Ostatnie polecenie wymaga interaktywnego terminala. `shellHook` sam wykonuje `cargo run --locked -- -h`, więc wejście do środowiska także kompiluje program i drukuje pomoc. Przy kontrolach używaj `--locked`, aby nie aktualizować `Cargo.lock`. Formatowanie stosuj wyłącznie do uzgodnionych zmian; nie używaj automatycznego `clippy --fix` do porządkowania całego repozytorium.
Ostatnie polecenie wymaga interaktywnego terminala. `shellHook` drukuje dostępne polecenia, bez automatycznej kompilacji. Przy kontrolach używaj `--locked`, aby nie aktualizować `Cargo.lock`. Formatowanie stosuj wyłącznie do uzgodnionych zmian; nie używaj automatycznego `clippy --fix` do porządkowania całego repozytorium.
Stan bazowy po konfiguracji lintów: 45 testów i formatowanie przechodzą; Clippy zgłasza 50 ostrzeżeń dla biblioteki. `clippy::unused_unit` jest dozwolony ze względu na wymagane jawne `-> ()`. Zakres i polecenia opisano w [TESTING.md](TESTING.md). Nie traktuj tego jako potwierdzenia działania pełnej rozgrywki. Nowa nietrywialna logika wymaga małego testu regresji. Nie dodawaj zależności ani warstw abstrakcji bez potrzeby.
Aktualny zestaw obejmuje 46 testów. Stan bazowy Clippy to 50 ostrzeżeń dla biblioteki. `clippy::unused_unit` jest dozwolony ze względu na wymagane jawne `-> ()`. Zakres i polecenia opisano w [TESTING.md](TESTING.md). Nie traktuj tego jako potwierdzenia działania pełnej rozgrywki. Nowa nietrywialna logika wymaga małego testu regresji. Nie dodawaj zależności ani warstw abstrakcji bez potrzeby.
`Cargo.toml` wymaga Rust 1.88 lub nowszego, zabrania `unsafe` we własnym kodzie i ignorowania wyników `must_use`, a w release włącza kontrolę przepełnień. CI Gitea w `.gitea/workflows/rust.yml` sprawdza formatowanie, kompilację, testy debug/release i Clippy bez Nix, przez Rust 1.99.1 instalowany rustup; lokalnie nadal używaj Nix. Oddzielny `.gitea/workflows/nix.yml` buduje `.#default` z `default.nix`, z testami pakietu i kontrolą instalacji. Nie dodawaj `-D warnings` przed uporządkowaniem istniejących ostrzeżeń.
Audio wymaga ALSA i urządzenia odtwarzania; do pracy nad TUI używaj `--disable-audio`. `--mute` nadal uruchamia wątek audio. Ścieżki zasobów i pakowanie sprawdzaj osobno przy zmianach audio/Nix. Rozróżniaj lokalny build Cargo, pakiet `.#develop` i domyślny pakiet Nix.
`.gitea/workflows/packages.yml` testuje paczki Linux x86_64 bez Nix: deb, rpm, AppImage, Flatpak oraz przepis AUR budowany przez makepkg. Nie publikuje wydań ani tagów. Przepisy, layout i ograniczenia opisano w [PACKAGING.md](PACKAGING.md); zmiany pakowania muszą zachować muzykę obok binarki i działanie poza katalogiem repozytorium.
## Konwencje kodu
- Formatowanie określa `rustfmt.toml`: styl Rust 2024, wcięcia po 2 spacje, szerokość docelowa 100 znaków i zakończenia LF. Stosuj `nix develop -c cargo fmt --all`; kontroluj przez `nix develop -c cargo fmt --all -- --check`.
+3 -2
View File
@@ -24,6 +24,7 @@ Wszystkie ścieżki poniżej są względem katalogu głównego repozytorium.
| Formatowanie | `rustfmt.toml` | Rustfmt, styl 2024, wcięcia po 2 spacje, docelowo 100 znaków i zakończenia LF |
| Kontrole CI | `.gitea/workflows/rust.yml` | Gitea Actions w `node:22-bookworm`: Rust 1.99.1 przez rustup, cache toolchaina i Cargo, formatowanie, kompilacja, testy debug/release i Clippy |
| Budowa paczki Nix | `.gitea/workflows/nix.yml` | Oddzielny workflow buduje `.#default`, czyli `default.nix` przez nixpkgs przypięte w `flake.lock`, z testami i kontrolą instalacji pakietu |
| Paczki Linux | `.gitea/workflows/packages.yml`, `packaging/` | Testowe deb, rpm, AppImage, Flatpak oraz makepkg dla AUR; bez wydań i tagów; szczegóły w `docs/PACKAGING.md` |
| Licencja | `LICENSE`, `Cargo.toml`, `default.nix` | MIT, Copyright (c) 2026 GarandPLG |
| Zasoby | `soundtracks/default/`, `ui_draft/` | Dwa pliki OGG i tekstowe szkice ekranów |
@@ -104,7 +105,7 @@ Postęp bieżącego kroku jest udostępniany przez `MovingTask::get_movement_pro
Główny wątek wysyła `AudioCmd`; osobny wątek obsługuje głośność, mute, część i soundtrack. Cache wykorzystuje `Lazy`, `RwLock`, `Arc` i bajty OGG. Calm ładuje `calm.ogg`; Buildup, Assault i Outro korzystają obecnie z `test.ogg`. Błędy urządzenia, odczytu i dekodowania mogą zakończyć wątek przez `expect`.
Rodio zapętla źródło przez `Decoder::new_looped`. Wątek sterujący blokuje się na odbiorniku (`for cmd in rx`), zamiast aktywnie sprawdzać komendy i koniec utworu. Zmiana części lub soundtracku dopisuje zapętlone źródło i pomija bieżące przez `skip_one`. Zamknięcie kanału kończy wątek. Test `audio_repeats_after_end_of_file` sprawdza przekroczenie końca rzeczywistego pliku OGG bez urządzenia audio.
Rodio zapętla źródło przez `Decoder::new_looped`. Wątek sterujący blokuje się na odbiorniku (`for cmd in rx`), zamiast aktywnie sprawdzać komendy i koniec utworu. Zmiana części lub soundtracku dopisuje zapętlone źródło i pomija bieżące przez `skip_one`. Zamknięcie kanału kończy wątek. Soundtracki są szukane obok rzeczywistej binarki, z fallbackiem do bieżącego katalogu dla Cargo. Testy audio obejmują zapętlanie OGG i ładowanie zasobów poza repozytorium, bez urządzenia audio.
`flake.nix` dostarcza devShell dla `x86_64-linux`, domyślny pakiet z `default.nix` i pakiet `.#develop` budowany przez Naersk. Obie ścieżki pakowania kopiują soundtracki do `$out/bin` i zmieniają nazwę binarki na `war-in-tunnels`. `default.nix` ma stały `cargoHash`; po zmianie zależności wymaga osobnej weryfikacji. Samo `cargo test` nie sprawdza pakowania.
@@ -116,7 +117,7 @@ To obserwacje statyczne, nie lista napraw wykonanych w tej sesji:
- **Zaznaczenie:** wybrany górnik czeka podczas rysowania trasy; inne jednostki wykonują zadania normalnie.
- **Audio:** usunięto aktywne odpytywanie; pomiar CPU na docelowym urządzeniu i odsłuch pozostają do wykonania. Test dekodera nie weryfikuje urządzenia audio.
- **Granice UI:** małe terminale, resize i trzy poziomy zoomu wymagają interaktywnego sprawdzenia. Indeksowanie planszy zakłada poprawne współrzędne.
- **Pokrycie:** 45 testów CLI, sterowania, renderowania, planszy, wykonywania zadań i dekodowania audio; szczegóły w [TESTING.md](TESTING.md). Workflow Gitea sprawdza też profil release; nie obejmuje pełnej rozgrywki. Clippy zgłasza 50 ostrzeżeń biblioteki, `unused_unit` jest jawnie dozwolony. Nie uruchamiać automatycznych poprawek poza zakresem zadania.
- **Pokrycie:** 46 testów CLI, sterowania, renderowania, planszy, wykonywania zadań i dekodowania audio; szczegóły w [TESTING.md](TESTING.md). Workflow Gitea sprawdza też profil release; nie obejmuje pełnej rozgrywki. Clippy zgłasza 50 ostrzeżeń biblioteki, `unused_unit` jest jawnie dozwolony. Nie uruchamiać automatycznych poprawek poza zakresem zadania.
## Gdzie zacząć konkretną zmianę
+37
View File
@@ -0,0 +1,37 @@
# Testowe paczki Linux
Workflow `.gitea/workflows/packages.yml` uruchamia się po pushu i dla pull requestów. Nie tworzy tagów, wydań ani wpisów w AUR/Flathub. Paczki są niesygnowanymi artefaktami CI przechowywanymi przez 7 dni. Używa `upload-artifact@v3` dla zgodności z Gitea.
Obsługiwana architektura to x86_64. Są to formaty dystrybucji Linux, nie kompilacje Windows/macOS.
Job AUR instaluje `nodejs` przed checkoutem: runner 0.6.1 uruchamia akcje JavaScript poleceniem `node`, którego obraz `archlinux:base-devel` nie zawiera. Jest to zależność CI (checkout i upload-artifact), nie zależność gry ani PKGBUILD.
| Format | Budowanie i kontrola |
|---|---|
| `.deb` | Rust 1.99.1 w Debian Bookworm, `dpkg-deb`, odczyt metadanych i uruchomienie rozpakowanej binarki z `--version` |
| `.rpm` | Ta sama binarka, `rpmbuild` i `packaging/war-in-tunnels.spec`, automatyczne zależności ELF, ekstrakcja i `--version` |
| AppImage | AppDir z binarką, muzyką i libasound.so.2; appimagetool 1.9.1; ekstrakcja narzędzia i paczki przez 7z, uruchomienie AppRun z `--version` bez FUSE |
| AUR | `packaging/aur/PKGBUILD`, lokalne archiwum bieżącego checkoutu z SHA256, `makepkg` jako zwykły użytkownik w Arch Linux; kompilacja, testy, instalacja paczki i `--version`; eksport .SRCINFO |
| Flatpak | Binarka z Debiana pakowana poleceniami `flatpak build-init`, `build-finish`, `build-export`, `build-bundle`; runtime org.freedesktop.Platform 25.08; instalacja paczki w kontenerze, kontrola soundtracku i `--version` zainstalowanej binarki bez sandboxa |
`packaging/stage.sh` instaluje binarkę i soundtracki w `PREFIX/lib/war-in-tunnels`, launcher w `PREFIX/bin`, licencję, plik desktop z `Terminal=true` i ikonę SVG. Nie zmienia katalogu roboczego, więc opcja `--log` nadal zapisuje log w katalogu wybranym przez użytkownika. Audio najpierw szuka soundtracków obok rzeczywistej binarki, a następnie w bieżącym katalogu dla lokalnego Cargo. Test audio sprawdza działanie poza repozytorium.
W kontenerze Debian z zależnościami workflow można wykonać:
```sh
cargo build --release --locked
bash packaging/build.sh deb
bash packaging/build.sh rpm
bash packaging/build.sh appimage
bash packaging/build.sh flatpak
```
Lokalnie na hoście projektu polecenia Cargo uruchamiaj przez `nix develop -c`; binarka zbudowana przez Nix może zależeć od `/nix/store`, dlatego do paczek dystrybucyjnych używaj kompilacji kontenerowej. Skrypty oczekują bieżącego katalogu repozytorium i zapisują wyniki do `dist/`.
Przepis AUR jest testowy: nie wskazuje publicznego tarballa wydania. Workflow dostarcza lokalny tarball oraz zastępuje `SKIP` rzeczywistym SHA256. Wersja `pkgver` musi odpowiadać Cargo.toml; CI to sprawdza. Artefakt AUR zawiera paczkę, przepis, .SRCINFO i pasujące źródła.
Flatpak jest testem pakowania istniejącej binarki, nie kompilacją wewnątrz SDK Flatpak. Bookworm dostarcza glibc 2.36, runtime 25.08 jest nowszy. Pakowana jest biblioteka ALSA; uprawnienia obejmują PulseAudio i urządzenia dla bezpośredniego ALSA. `build-export --disable-sandbox` wyłącza wyłącznie sandbox walidatora własnej ikony z repozytorium, nie walidację ani sandbox gotowej aplikacji. Tworzenie paczki nie potrzebuje privileged ani zmiany polityki Dockera. Instalacja w Dockerze może zgłaszać bwrap dla hooków integracji pulpitu mimo poprawnego zainstalowania paczki; sprawdzamy kod wyjścia, pliki i binarkę. Pełnego uruchomienia TUI i audio w sandboxie te kontrole nie zastępują. Runtime nie jest zawarty w pliku .flatpak; użytkownik musi go zainstalować z Flathub. CI używa --no-related, aby nie pobierać opcjonalnych dodatków GPU i tłumaczeń.
Paczki Debian, RPM i AppImage mają bazę ABI Bookworm (glibc >= 2.36); nie gwarantują działania na starszych dystrybucjach. AppImage zawiera ALSA, ale korzysta z konfiguracji audio hosta. Testy nie potwierdzają odsłuchu ani otwierania terminala z menu desktop. Appimagetool jest przypięty do wersji; jego domyślny runtime jest pobierany z upstream podczas pakowania. Rust i Cargo mają cache oddzielny od zwykłego workflow Rust; dla cache wymagany jest działający serwer runnera. Arch i Flatpak pobierają własne zależności bez trwałego cache.
Dokumentacja narzędzi: [AppImage](https://github.com/AppImage/appimagetool/tree/1.9.1), [Flatpak](https://docs.flatpak.org/en/latest/flatpak-command-reference.html), [makepkg](https://man.archlinux.org/man/makepkg.8).
+18
View File
@@ -1,5 +1,23 @@
# Dziennik prac
## 2026-10-19 — Node.js wymagany przez akcje joba AUR
Log Gitea wykazał brak `node` podczas uruchamiania checkout@v4 i jego kroku post. Arch base-devel nie zawiera Node.js, a runner 0.6.1 wywołuje akcje JavaScript przez `node` w kontenerze. Dodano `nodejs` do instalacji zależności przed checkoutem oraz kontrolę `node --version`. Node.js jest zależnością CI, nie paczki gry. Poprzednia próba lokalna weryfikowała makepkg, lecz nie uruchamiała akcji JavaScript; uzupełniono ten brak.
Weryfikacja w jednorazowym Arch base-devel: zainstalowano Node.js, `node --version` zwróciło v26.12.0; sprawdzono `node --check` rzeczywistych entrypointów checkout@v4 i upload-artifact@v3. Uruchomienie checkoutu doszło do oczekiwanego błędu brakującego inputu token (bez poświadczeń i klonowania repozytorium), potwierdzając poprawny start akcji. `nix develop -c cargo check --all-targets --locked` i `git diff --check` przeszły. Nie powtarzano budowania gry ani nie uruchamiano pełnego workflow na serwerze; transport artefaktów pozostaje do potwierdzenia na Gitea.
## 2026-10-19 — testowe paczki Linux poza Nix
Dodano `.gitea/workflows/packages.yml` dla push i pull request: job Debian kompiluje Rust 1.99.1 i tworzy deb, rpm, AppImage oraz Flatpak; job Arch kompiluje i testuje lokalny przepis AUR przez makepkg jako użytkownik builder. Workflow zapisuje artefakty przez 7 dni, nie tworzy wydań, tagów ani publikacji AUR/Flathub. Zależności i toolchain Cargo mają cache; Flatpak i Arch nie mają jeszcze trwałego cache. Obsługiwana architektura: x86_64.
Przepisy i wspólna instalacja są w `packaging/`, a zakres i ograniczenia w `docs/PACKAGING.md`. Pakowane są muzyka, licencja, launcher zachowujący katalog roboczy, plik desktop Terminal=true i ikona SVG. Flatpak pakuje binarkę z Debiana do runtime 25.08, bez kompilacji w SDK i bez zagnieżdżonego sandboxa. Przepis AUR dostaje archiwum checkoutu z rzeczywistym SHA256; nie jest jeszcze przepisem publikowanego wydania. Dodano `dist/` do .gitignore.
Audio szuka zasobów obok rzeczywistej binarki z fallbackiem do katalogu roboczego, aby instalowane paczki działały poza repozytorium bez zmiany miejsca zapisu logów. Dodano regresję uruchamianą w osobnym procesie z przeniesioną binarką testu i soundtrackami. Łącznie 46 testów integracyjnych.
Weryfikacja: cargo check --all-targets --locked, cargo test --all-targets --locked (46 testów), cargo fmt --all -- --check i cargo clippy --all-targets --locked przez nix develop przeszły; Clippy nadal ma 50 ostrzeżeń biblioteki. Przeszły git diff --check i bash -n. W jednorazowym Debianie rzeczywiście skompilowano grę, utworzono deb i rpm, rozpakowano i sprawdzono --version; zbudowano też AppImage, rozpakowano je i uruchomiono AppRun --version. Narzędzie i wynik AppImage rozpakowywane są przez 7z, bez FUSE i kolizji z binfmt hosta. Osobny Arch Linux wykonał makepkg, testy release i doctest, instalację paczki oraz --version. Flatpak został wyeksportowany, zapisany do pliku i zainstalowany w kontenerze; zweryfikowano soundtrack i --version zainstalowanej binarki. Wyłączono sandbox wyłącznie walidatora własnej ikony przy build-export; instalacja może zgłaszać ostrzeżenia bwrap dla hooków integracji pulpitu w standardowym Dockerze. Nie wyłączono sandboxa gotowej aplikacji. Instalacja runtime i aplikacji używa --no-related, aby nie pobierać opcjonalnych sterowników GPU ani tłumaczeń dla testu TUI.
Nie sprawdzono pełnej rozgrywki, odsłuchu, uruchamiania z menu pulpitu ani działania na starszych dystrybucjach; pełny workflow i transport artefaktów wymagają przebiegu na Gitea. Testowe kontenery miały repozytorium tylko do odczytu; nie instalowano paczek na hoście. Usunięto tymczasowy kontener war-packages-check i obraz war-in-tunnels-packaging-test:local.
## 2026-10-19 — oddzielny workflow budowania default.nix
Dodano `.gitea/workflows/nix.yml` dla push i pull request. Workflow w `node:22-bookworm` instaluje sudo i Nix (KVM wyłączone), następnie buduje `.#default` z przypiętym `flake.lock`, bez aktualizacji lockfile i bez devShell. Obejmuje testy buildRustPackage oraz versionCheckHook. Nie zmieniono workflow Rust ani konfiguracji runnera; nie dodano trwałego cache Nix.
+6 -4
View File
@@ -16,10 +16,10 @@ Testy korzystają z wbudowanego mechanizmu Rust, Clap i `ratatui::backend::TestB
| `tests/app.rs` | 7 | Nawigacja menu, wybór ekranów, wyjście, ignorowanie zwolnienia klawisza, komendy audio i zamknięty kanał, panel, zoom, ruch, renderowanie wszystkich widoków bez mutacji stanu, wejście przed inicjalizacją |
| `tests/board.rs` | 7 | Rozmieszczenie baz/złóż/jednostek, granice kursora i kamery, zatwierdzanie/anulowanie ścieżki, cofanie z powtórzonym polem, duży viewport, resize i zoom |
| `tests/tasks.rs` | 7 | Przyrost i limit kopania, reset etapu, pusta ścieżka, kolejność zadań, timer MovingTask, całkowity postęp, zachowanie liczby etapów przy konwersji, formatowanie postępu aktywnych i oczekujących zadań |
| `tests/audio.rs` | 1 | Zapętlanie rzeczywistego pliku OGG po jego końcu |
| `tests/audio.rs` | 2 | Zapętlanie rzeczywistego pliku OGG po jego końcu i ładowanie soundtracków obok binarki poza katalogiem repozytorium |
| `tests/digging.rs` | 18 | Wykonywanie kopania i ruchu, kolejki, 3 sekundy na krok, wejście po wykopaniu, blokady i walidacja, kolory i procenty w TUI, podział trasy Moving/Digging, ochrona struktur, zmiana rodzaju zadania, panel Tab z postępem etapu i całego zadania |
Łącznie: 45 testów. Wszystkie testy znajdują się w `tests/` i sprawdzają publiczne interfejsy biblioteki lub binarkę. Funkcja `load_audio` jest eksportowana przez `app::threads`, dzięki czemu test korzysta z tego samego dekodera co odtwarzanie.
Łącznie: 46 testów. Wszystkie testy znajdują się w `tests/` i sprawdzają publiczne interfejsy biblioteki lub binarkę. Funkcja `load_audio` jest eksportowana przez `app::threads`, dzięki czemu test korzysta z tego samego dekodera co odtwarzanie.
Wybrane zestawy i pojedynczy przypadek:
@@ -41,10 +41,12 @@ nix develop -c cargo fmt --all -- --check
nix develop -c cargo clippy --all-targets --locked
```
`shellHook` środowiska Nix dodatkowo wykonuje `cargo run --locked -- -h`. Wynik pomocy nie jest wynikiem testów; właściwy raport zawiera podsumowania `test result`.
`shellHook` środowiska Nix wyświetla dostępne polecenia; nie kompiluje automatycznie programu.
## CI i profile kompilacji
Testowe paczki deb, rpm, AppImage, AUR i Flatpak sprawdza niezależny `.gitea/workflows/packages.yml`, bez publikowania wydań. Zasady budowania, artefakty i ograniczenia opisano w [PACKAGING.md](PACKAGING.md).
Osobny workflow `.gitea/workflows/nix.yml` (push i pull request) instaluje Nix w `node:22-bookworm` i wykonuje `nix build .#default --no-update-lock-file --no-link --print-out-paths --print-build-logs`. Atrybut `.#default` wywołuje `pkgs.callPackage ./default.nix {}` z nixpkgs przypiętym w `flake.lock`; nie buduje wariantu `.#develop` opartego na Fenix. Budowa obejmuje standardową fazę testów `buildRustPackage` i włączony w pakiecie `versionCheckHook`. Nie wymaga devShell ani uruchamiania gry. Instalator potrzebuje sudo; KVM jest wyłączone. Workflow nie konfiguruje trwałego cache magazynu Nix, więc nowy job może ponownie pobierać zależności. Kontrole Rust opisane poniżej pozostają niezależne.
Workflow `.gitea/workflows/rust.yml` uruchamia kontrole po pushu i dla pull requestów. Wymaga włączonych Actions w repozytorium Gitea i runnera Linux x86_64 o etykiecie `ubuntu-latest`, obsługującego akcje Node. Nazwy akcji zawierają pełne adresy GitHub. Konfiguracja nie rejestruje runnera ani nie włącza Actions na serwerze; zobacz [instrukcję Gitea](https://docs.gitea.com/usage/actions/quickstart/).
@@ -53,7 +55,7 @@ Workflow określa obraz zadania jawnie jako `node:22-bookworm`. Kontener działa
Wersję Rust w CI określa `RUST_VERSION` w workflow, niezależnie od lokalnego `flake.lock`. Formatowanie, kompilacja, testy i Clippy są oddzielnymi krokami Cargo. Polecenia kompilujące projekt mają `--locked`; Clippy nie używa `-D warnings` z powodu istniejących ostrzeżeń. `actions/cache@v4` zachowuje osobno toolchain oraz rejestr Cargo, repozytoria git i `target`. Klucze uwzględniają wersję Rust, a dla Cargo także manifest, lockfile i kod; klucze częściowe pozwalają ponownie wykorzystać wcześniejsze kompilacje. Działanie cache wymaga dostępnego serwera cache runnera. Pierwszy przebieg wypełnia cache; błędy pobierania cache mogą spowodować ponowną instalację i kompilację.
Release ma `overflow-checks = true`. Ten sam zestaw 45 testów można wykonać z kontrolą przepełnień w profilu produkcyjnym:
Release ma `overflow-checks = true`. Ten sam zestaw 46 testów można wykonać z kontrolą przepełnień w profilu produkcyjnym:
```sh
nix develop -c cargo test --all-targets --release --locked
+28
View File
@@ -0,0 +1,28 @@
# Test recipe: CI supplies the source archive from the checked-out commit.
pkgname=war-in-tunnels
pkgver=0.1.0
pkgrel=1
pkgdesc='Underground strategy game in your terminal'
arch=('x86_64')
url='https://gitea.garandplg.com/GarandPLG/war-in-tunnels'
license=('MIT')
depends=('alsa-lib' 'gcc-libs' 'glibc')
makedepends=('cargo' 'pkgconf')
options=('!debug')
source=("$pkgname-$pkgver.tar.gz")
sha256sums=('SKIP')
build() {
cd "$srcdir/$pkgname-$pkgver"
cargo build --release --locked
}
check() {
cd "$srcdir/$pkgname-$pkgver"
cargo test --release --locked
}
package() {
cd "$srcdir/$pkgname-$pkgver"
sh packaging/stage.sh "$pkgdir/usr"
}
+91
View File
@@ -0,0 +1,91 @@
#!/bin/bash
set -euo pipefail
format=${1:?expected deb, rpm, appimage or flatpak}
version=$(python3 -c 'import tomllib; print(tomllib.load(open("Cargo.toml", "rb"))["package"]["version"])')
mkdir -p dist
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
sh packaging/stage.sh "$work/root/usr"
desktop-file-validate "$work/root/usr/share/applications/com.garandplg.WarInTunnels.desktop"
"$work/root/usr/bin/war-in-tunnels" --version | grep -Fx "war_in_tunnels $version"
test -s "$work/root/usr/lib/war-in-tunnels/soundtracks/default/calm.ogg"
case "$format" in
deb)
mkdir -p "$work/root/DEBIAN"
cat > "$work/root/DEBIAN/control" <<EOF
Package: war-in-tunnels
Version: $version
Architecture: amd64
Maintainer: GarandPLG
Depends: libasound2, libc6 (>= 2.36), libgcc-s1
Section: games
Priority: optional
Description: Underground strategy game in your terminal
EOF
output="$PWD/dist/war-in-tunnels_${version}_amd64.deb"
dpkg-deb --root-owner-group --build "$work/root" "$output"
dpkg-deb --info "$output"
dpkg-deb --extract "$output" "$work/extracted"
"$work/extracted/usr/bin/war-in-tunnels" --version
;;
rpm)
rpmbuild -bb packaging/war-in-tunnels.spec \
--define "_topdir $work/rpmbuild" \
--define "package_version $version" \
--define "staging $work/root" \
--define '_build_id_links none'
cp "$work"/rpmbuild/RPMS/x86_64/*.rpm dist/
rpm -qp --requires dist/*.rpm
mkdir "$work/extracted"
rpm2cpio dist/*.rpm | (cd "$work/extracted" && cpio -idm --quiet)
"$work/extracted/usr/bin/war-in-tunnels" --version
;;
appimage)
appdir="$work/WarInTunnels.AppDir"
mv "$work/root" "$appdir"
cp packaging/com.garandplg.WarInTunnels.desktop "$appdir/"
cp packaging/com.garandplg.WarInTunnels.svg "$appdir/"
mkdir -p "$appdir/usr/lib/bundled"
# Bundle ALSA; glibc and the ELF loader come from the host.
cp -L /usr/lib/x86_64-linux-gnu/libasound.so.2 "$appdir/usr/lib/bundled/"
cat > "$appdir/AppRun" <<'EOF'
#!/bin/sh
set -eu
appdir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
export LD_LIBRARY_PATH="$appdir/usr/lib/bundled${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
exec "$appdir/usr/bin/war-in-tunnels" "$@"
EOF
chmod +x "$appdir/AppRun"
curl -fL --retry 3 https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-x86_64.AppImage -o "$work/appimagetool"
# Extract without executing the runtime: works without FUSE and with host binfmt handlers.
7z x "$work/appimagetool" "-o$work/appimagetool-dir" > /dev/null
output="$PWD/dist/war-in-tunnels-${version}-x86_64.AppImage"
ARCH=x86_64 "$work/appimagetool-dir/AppRun" --no-appstream "$appdir" "$output"
7z x "$output" "-o$work/appimage-check" > /dev/null
"$work/appimage-check/AppRun" --version
;;
flatpak)
appid=com.garandplg.WarInTunnels
# Package the Debian-built binary against a newer glibc runtime; no nested sandbox is needed.
flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak --user install -y --noninteractive --no-related flathub org.freedesktop.Platform//25.08
flatpak build-init "$work/flatpak" "$appid" org.freedesktop.Platform org.freedesktop.Platform 25.08
cp -a "$work/root/usr/." "$work/flatpak/files/"
install -Dm755 /usr/lib/x86_64-linux-gnu/libasound.so.2 "$work/flatpak/files/lib/libasound.so.2"
flatpak build-finish --command=war-in-tunnels --socket=pulseaudio --device=all "$work/flatpak"
# The icon is repository-owned; its validator cannot create a sandbox in standard Docker.
flatpak build-export --disable-sandbox "$work/repo" "$work/flatpak" test
output="$PWD/dist/war-in-tunnels-${version}-x86_64.flatpak"
flatpak build-bundle --runtime-repo=https://flathub.org/repo/flathub.flatpakrepo "$work/repo" "$output" "$appid" test
flatpak --user install -y --noninteractive --no-related "$output"
location=$(flatpak --user info --show-location "$appid")
test -s "$location/files/lib/war-in-tunnels/soundtracks/default/calm.ogg"
"$location/files/bin/war-in-tunnels" --version
;;
*)
echo "Unknown package format: $format" >&2
exit 1
;;
esac
@@ -0,0 +1,8 @@
[Desktop Entry]
Type=Application
Name=War in Tunnels
Comment=Underground strategy game in your terminal
Exec=war-in-tunnels
Icon=com.garandplg.WarInTunnels
Terminal=true
Categories=Game;StrategyGame;
+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128" viewBox="0 0 128 128">
<rect width="128" height="128" rx="16" fill="#20252b"/>
<path d="M20 104V56a44 44 0 0 1 88 0v48H88V56a24 24 0 0 0-48 0v48Z" fill="#bd985e"/>
<path d="M51 91V59l13 15 13-15v32" fill="none" stroke="#e9edf0" stroke-width="7"/>
</svg>

After

Width:  |  Height:  |  Size: 325 B

+18
View File
@@ -0,0 +1,18 @@
#!/bin/sh
set -eu
prefix=${1:?installation prefix required}
binary=${2:-target/release/war_in_tunnels}
install -Dm755 "$binary" "$prefix/lib/war-in-tunnels/war-in-tunnels"
mkdir -p "$prefix/bin"
cat > "$prefix/bin/war-in-tunnels" <<'EOF'
#!/bin/sh
set -eu
bindir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
exec "$bindir/../lib/war-in-tunnels/war-in-tunnels" "$@"
EOF
chmod +x "$prefix/bin/war-in-tunnels"
cp -r soundtracks "$prefix/lib/war-in-tunnels/"
install -Dm644 LICENSE "$prefix/share/licenses/war-in-tunnels/LICENSE"
install -Dm644 packaging/com.garandplg.WarInTunnels.desktop "$prefix/share/applications/com.garandplg.WarInTunnels.desktop"
install -Dm644 packaging/com.garandplg.WarInTunnels.svg "$prefix/share/icons/hicolor/scalable/apps/com.garandplg.WarInTunnels.svg"
+21
View File
@@ -0,0 +1,21 @@
Name: war-in-tunnels
Version: %{package_version}
Release: 1
Summary: Underground strategy game in your terminal
License: MIT
URL: https://gitea.garandplg.com/GarandPLG/war-in-tunnels
Requires: alsa-lib
%description
A terminal-based underground strategy game.
%install
mkdir -p %{buildroot}
cp -a %{staging}/. %{buildroot}/
%files
/usr/bin/war-in-tunnels
/usr/lib/war-in-tunnels
/usr/share/applications/com.garandplg.WarInTunnels.desktop
/usr/share/icons/hicolor/scalable/apps/com.garandplg.WarInTunnels.svg
%license /usr/share/licenses/war-in-tunnels/LICENSE
+13 -3
View File
@@ -8,6 +8,7 @@ use std::{
collections::HashMap,
fs::read,
io::{BufReader, Cursor},
path::{Path, PathBuf},
process::exit,
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard, mpsc::Receiver},
};
@@ -49,15 +50,24 @@ static PRELOADED_SOUNDTRACKS: Lazy<RwLock<HashMap<Soundtrack, Arc<SoundtrackCach
});
fn load_soundtrack_folder(folder: &str) -> SoundtrackCache {
let base: String = format!("soundtracks/{}", folder);
let soundtrack_root: PathBuf = std::env::current_exe()
.ok()
.and_then(|executable: PathBuf| -> Option<PathBuf> {
executable
.parent()
.map(|directory: &Path| -> PathBuf { directory.join("soundtracks") })
})
.filter(|directory: &PathBuf| -> bool { directory.is_dir() })
.unwrap_or_else(|| -> PathBuf { PathBuf::from("soundtracks") });
let base: PathBuf = soundtrack_root.join(folder);
let mut cache: HashMap<SoundrackParts, Vec<u8>> = HashMap::new();
macro_rules! load {
($part:ident, $file:expr) => {
let path = format!("{}/{}", base, $file);
let path: PathBuf = base.join($file);
cache.insert(
SoundrackParts::$part,
read(&path).expect(&format!("cannot read {}", path)),
read(&path).expect(&format!("cannot read {}", path.display())),
);
};
}
+47 -1
View File
@@ -1,7 +1,9 @@
use rodio::Decoder;
use std::{
fs::read,
fs::{self, read},
io::{BufReader, Cursor},
path::PathBuf,
process::{Command, Output},
};
use war_in_tunnels::app::threads::{SoundrackParts, Soundtrack, load_audio};
@@ -17,3 +19,47 @@ fn audio_repeats_after_end_of_file() {
assert_eq!(source.by_ref().take(sample_count).count(), sample_count);
assert_eq!(source.take(1024).count(), 1024);
}
#[test]
fn packaged_audio_loads_without_project_working_directory() -> () {
if std::env::var_os("WAR_IN_TUNNELS_PACKAGED_AUDIO_TEST").is_some() {
let mut source: rodio::source::Amplify<
rodio::decoder::LoopedDecoder<BufReader<Cursor<Vec<u8>>>>,
> = load_audio(&Soundtrack::Default, &SoundrackParts::Calm);
assert_eq!(source.by_ref().take(1024).count(), 1024);
return;
}
let directory: PathBuf = std::env::temp_dir().join(format!(
"war-in-tunnels-packaged-audio-{}",
std::process::id()
));
fs::create_dir_all(directory.join("soundtracks/default")).unwrap();
fs::create_dir_all(directory.join("working-directory")).unwrap();
let executable: PathBuf = directory.join("audio-test");
fs::copy(std::env::current_exe().unwrap(), &executable).unwrap();
for filename in ["calm.ogg", "test.ogg"] {
fs::copy(
PathBuf::from("soundtracks/default").join(filename),
directory.join("soundtracks/default").join(filename),
)
.unwrap();
}
let output: Output = Command::new(executable)
.args([
"--exact",
"packaged_audio_loads_without_project_working_directory",
"--nocapture",
])
.env("WAR_IN_TUNNELS_PACKAGED_AUDIO_TEST", "1")
.current_dir(directory.join("working-directory"))
.output()
.unwrap();
fs::remove_dir_all(directory).unwrap();
assert!(
output.status.success(),
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}