This commit is contained in:
Mateusz Gruszczyński committed 2026-10-22 13:03:12 +02:00
1 parent feb9d91a3d
commit 1f0daa0679
25 files changed
+174 -1035

No files matched your search

+29 -37
View File
@@ -1,17 +1,21 @@
# RustProxy unified package builder
Wydanie wdrożeniowe (odchudzone): zawiera panel, konfigurację Nginx oraz wszystkie skrypty budowania Linux, OPNsense i FreeBSD. Nie zawiera pustych szkieletów projektów ani testów developerskich. Istniejące katalogi `data/`, `gpg/` i plik `.env` pozostają nienaruszone podczas rozpakowywania.
Jeden kontener buildera buduje z jednego commita RustProxy:
- Linux x86_64: DEB + podpisane repo APT (`rustproxy` **oraz osobny** `rustproxy-docker-agent`),
- Linux x86_64: RPM + podpisane repo DNF/YUM (`rustproxy` **oraz osobny** `rustproxy-docker-agent`),
- FreeBSD x86_64: cross-build bez VM FreeBSD,
- OPNsense: `os-rustproxy-<version>_<revision>.pkg` + statyczne repo `pkg`,
- **natywne FreeBSD** (opcjonalnie): `rustproxy-<version>_<revision>.pkg` + osobny katalog repo `pkg` pod `/freebsd/` (bez zależności OPNsense).
- **natywne FreeBSD** (domyślnie włączone): `rustproxy-<version>_<revision>.pkg` + osobny katalog repo `pkg` pod `/freebsd/` (bez zależności OPNsense).
**Agent Docker jest pakowany wyłącznie na Linux (DEB/RPM); brak paczki FreeBSD pkg dla agenta.** Nie ma Docker-in-Docker, SCP ani osobnego watchera repo OPNsense. FreeBSD sysroot, clang/lld i Rust target są częścią głównego obrazu buildera. OPNsense dostaje osobny publiczny adres. Gałąź Linux, gałąź OPNsense i opcjonalna gałąź natywnego FreeBSD są budowane oraz publikowane niezależnie, więc błąd testów Linux nie blokuje pakietu FreeBSD.
**Agent Docker jest pakowany wyłącznie na Linux (DEB/RPM); brak paczki FreeBSD pkg dla agenta.** Nie ma Docker-in-Docker, SCP ani osobnego watchera repo OPNsense. FreeBSD sysroot, clang/lld i Rust target są częścią głównego obrazu buildera. OPNsense dostaje osobny publiczny adres. Gałąź Linux, gałąź OPNsense i gałąź natywnego FreeBSD są budowane oraz publikowane niezależnie, więc błąd testów Linux nie blokuje pakietu FreeBSD.
## Panel repozytorium (RustProxy Package Registry)
The dashboard interface defaults to **English (`en-GB`)**, including its dates, navigation, notifications, and installation instructions. The **Docker discovery agent** installation card is shown only for **Linux (DEB/RPM)**, never for OPNsense or native FreeBSD. Package listings/API also omit any stray agent PKG files for the FreeBSD channels.
Strony startowe wszystkich trzech repozytoriów działają teraz jako **lekka aplikacja Python** (`dashboard/`) za istniejącym Nginx. Wygląd nawiązuje do panelu RustProxy 0.34.6 (ciemny, jasny i kremowy motyw). **APT, RPM i pkg są nadal obsługiwane bezpośrednio przez Nginx**, więc mechanizm aktualizacji paczek, ścieżki plików, podpisy GPG i atomowe przełączanie `current` nie zmieniają się.
Panel udostępnia:
@@ -26,7 +30,7 @@ Adresy paneli (bez zmian domen i portów):
```text
REPOSITORY_URL/ Linux DEB + RPM
OPNSENSE_REPOSITORY_URL/ OPNsense pkg
OPNSENSE_REPOSITORY_URL/freebsd/ Native FreeBSD pkg (opcjonalnie)
OPNSENSE_REPOSITORY_URL/freebsd/ Native FreeBSD pkg
```
Publiczne API, np. `https://twoje-repo.example/api/v1/packages`, zwraca JSON z `builds`, `packages` i `summary`. API jest dostępne na obu hostach. Panel nie zapisuje nic w repo; odczytuje wyłącznie opublikowany snapshot `data/repository/current`. Kontener Python nie publikuje portu hosta i ma dostęp **tylko do odczytu**.
@@ -45,8 +49,6 @@ docker compose --env-file .env up -d repository-dashboard repository-http
curl -fsS http://127.0.0.1:18765/api/v1/packages
curl -fsS http://127.0.0.1:18765/healthz
# testy komponentu Python (bez Dockera)
python3 -m unittest discover -s tests -p 'test_dashboard.py' -v
```
Panel zależy wyłącznie od biblioteki standardowej Pythona, bez dodatkowych pakietów pip. Jeżeli nie ma jeszcze buildów, pokaże komunikat **brak paczek**, a nie fikcyjne wersje.
@@ -102,7 +104,7 @@ opn-rustproxy.linuxiarz.pl -> http://builder:18766
publikacja Linux os-rustproxy rustproxy.pkg
pkg repo pkg repo
* FreeBSD native: FREEBSD_NATIVE_ENABLED=1
* FreeBSD native: enabled by default; FREEBSD_NATIVE_ENABLED=0 disables it
```
`cargo test` i `go test ./...` (agenta) należą tylko do gałęzi Linux. Jeśli testy nie przejdą, DEB/RPM nie są publikowane, ale builder nadal uruchamia cross-build FreeBSD i może opublikować repo OPNsense. Stan jest śledzony osobno w `last_linux_commit`, `last_opnsense_commit` i (jeśli włączone) `last_freebsd_commit`; kolejny cykl ponawia tylko brakującą gałąź.
@@ -208,7 +210,7 @@ APPLY_FREEBSD_PATCHES=1
`OPNSENSE_RETENTION=10` zachowuje ostatnie 10 wersji pakietu dla danej serii/architektury OPNsense. `0` wyłącza usuwanie starych wersji.
`FREEBSD_NATIVE_ENABLED=0` wyłącza natywny pakiet FreeBSD (domyślnie, bez zmian istniejącej instalacji). Włączenie `1` tworzy oddzielny kanał publikacji i dodaje go do cyklicznego buildera. `FREEBSD_NATIVE_RETENTION=10` ogranicza liczbę zachowanych wersji. Repo używa tego samego publicznego hosta/portu co OPNsense, ale pod oddzielnym prefiksem `OPNSENSE_REPOSITORY_URL/freebsd/`.
`FREEBSD_NATIVE_ENABLED=1` (domyślnie) włącza natywny pakiet FreeBSD i oddzielny kanał publikacji. `0` wyłącza budowanie natywnego FreeBSD. **Uwaga przy aktualizacji:** istniejący plik `.env` z `FREEBSD_NATIVE_ENABLED=0` ma pierwszeństwo przed nową wartością domyślną — zmień go ręcznie na `1`, inaczej pakiet FreeBSD nadal nie powstanie. `FREEBSD_NATIVE_RETENTION=10` ogranicza liczbę zachowanych wersji. Repo używa tego samego publicznego hosta/portu co OPNsense, ale pod oddzielnym prefiksem `OPNSENSE_REPOSITORY_URL/freebsd/`.
## Automatyczny Rust toolchain podczas budowania
@@ -290,7 +292,7 @@ configctl rustproxy start
```
Obsługa miękkiego przeładowania zwiększa `OPNSENSE_PACKAGING_REVISION` do `4`.
Pakowanie Linux ma `PACKAGING_REVISION=5`, a natywne FreeBSD `FREEBSD_NATIVE_PACKAGING_REVISION=1`. Każdy kanał otrzymuje osobną aktualizację przy zmianie swojej rewizji.
Pakowanie Linux ma `PACKAGING_REVISION=5`, a natywne FreeBSD `FREEBSD_NATIVE_PACKAGING_REVISION=2`. Każdy kanał otrzymuje osobną aktualizację przy zmianie swojej rewizji.
## DEB/RPM: dostęp do plików strony i zewnętrznych certyfikatów
@@ -312,11 +314,7 @@ zewnętrzne PEM wymagają zaufanych katalogów właściciela `root`/`rustproxy`.
Pakiet nie przypisuje automatycznie szerokich grup do użytkownika usługi,
nie zmienia uprawnień witryn ani nie nadpisuje istniejących drop-inów.
Test pakowania, bez podpisywania i kompilacji Rust:
```sh
./tests/linux-path-access.sh
```
Test pakowania jest dostępny w pełnym repozytorium developerskim (nie jest dołączony do archiwum wdrożeniowego).
## Rotacja logów RustProxy
@@ -325,7 +323,7 @@ Test pakowania, bez podpisywania i kompilacji Rust:
**Uwaga FreeBSD:** `newsyslog` działa przez zmianę nazwy pliku, nie ma `copytruncate`. Pliki JSONL i inne logi otwarte bezpośrednio przez aplikację muszą być przez nią ponownie otwierane po rotacji. W przeciwnym razie proces może pisać do poprzedniego (przeniesionego) pliku; zachowanie nie jest identyczne z Linux. Reguły są instalowane, ale na działającym OPNsense należy zweryfikować sposób otwierania plików przez RustProxy.
Bieżąca rewizja pakowania Linux wynosi `PACKAGING_REVISION=5` i `OPNSENSE_PACKAGING_REVISION=4`; nowy kanał używa `FREEBSD_NATIVE_PACKAGING_REVISION=1`. `check-and-build.sh` sprawdza osobno rewizje pakowania Linux, OPNsense i FreeBSD native; nowa wersja samej wtyczki przebudowuje **tylko OPNsense**, nawet przy niezmienionym SHA aplikacji. Rewizja pakietu PKG zwiększy się automatycznie. W przypadku zmiany rewizji Linux analogicznie odświeżane są DEB/RPM. Konfiguracja w `/etc/logrotate.d/rustproxy` jest oznaczona jako `noreplace`, więc lokalna wersja zmodyfikowana przez administratora może zostać zachowana (zależnie od menedżera pakietów).
Bieżąca rewizja pakowania Linux wynosi `PACKAGING_REVISION=5` i `OPNSENSE_PACKAGING_REVISION=4`; nowy kanał używa `FREEBSD_NATIVE_PACKAGING_REVISION=2`. `check-and-build.sh` sprawdza osobno rewizje pakowania Linux, OPNsense i FreeBSD native; nowa wersja samej wtyczki przebudowuje **tylko OPNsense**, nawet przy niezmienionym SHA aplikacji. Rewizja pakietu PKG zwiększy się automatycznie. W przypadku zmiany rewizji Linux analogicznie odświeżane są DEB/RPM. Konfiguracja w `/etc/logrotate.d/rustproxy` jest oznaczona jako `noreplace`, więc lokalna wersja zmodyfikowana przez administratora może zostać zachowana (zależnie od menedżera pakietów).
Kontrola po zainstalowaniu:
@@ -337,11 +335,7 @@ logrotate -d /etc/logrotate.d/rustproxy
newsyslog -n -v -f /usr/local/etc/newsyslog.conf.d/rustproxy.conf
```
Pakietowe testy integracyjne (lokalnie, bez budowy aplikacji):
```sh
./tests/log-rotation.sh
```
Testy integracyjne są dostępne w pełnym repozytorium developerskim.
## Dane
@@ -379,11 +373,7 @@ curl -f http://127.0.0.1:18766/healthz
## Testy statyczne
```bash
./tests/static.sh
./tests/linux-path-access.sh
./tests/check-rustproxy-source.sh /sciezka/do/RustProxy
```
Skrypty diagnostyczne są dostępne w pełnym repozytorium developerskim; wydanie wdrożeniowe zawiera tylko pliki potrzebne do uruchomienia.
## Klucz GPG
@@ -425,16 +415,16 @@ Ustawienia środowiskowe: `CLEAN_TARGET_AFTER_BUILD=0` wyłącza czyszczenie;
**Ważne:** CLI `reload` wymaga uruchomionego RustProxy, zainicjalizowanego administratora i API osiągalnego przez adres loopback. Zmiana adresów/portów nasłuchujących wymaga `restart`, a nie `reload`.
## Natywne repo FreeBSD — wersja developerska
## Natywne repo FreeBSD — włączone domyślnie
W pliku `.env` ustaw:
Dla nowej instalacji FreeBSD native włącza się automatycznie. Jeśli masz już `.env`, **sprawdź**, czy ma:
```env
FREEBSD_NATIVE_ENABLED=1
FREEBSD_NATIVE_RETENTION=10
```
Następnie `./start.sh`. Automatyczna publikacja katalogu `pkg` pojawi się pod:
Następnie `./start.sh` (przebudowa kontenerów buildera i panelu). Natywna paczka pojawi się pod:
```text
${OPNSENSE_REPOSITORY_URL}/freebsd/
@@ -461,15 +451,17 @@ service rustproxy reload
To osobny pakiet od `os-rustproxy`: nie należy instalować obu pakietów naraz, bo oba używają nazwy usługi i ścieżek RustProxy.
**Bezpieczeństwo repo:** aktualnie katalog `pkg` jest **niesygnowany**, analogicznie do istniejącego repo OPNsense (`signature_type: none`). To nadaje się do testów DEV na zaufanej infrastrukturze. Przed produkcyjnym wdrożeniem należy dodać podpis katalogu `pkg`, bezpieczną dystrybucję klucza publicznego i wykonać instalację oraz aktualizację na rzeczywistym FreeBSD. Generator katalogu i testy offline nie zastępują testu klienta `pkg` na FreeBSD.
**Bezpieczeństwo repo:** aktualnie katalog `pkg` jest **niesygnowany**, analogicznie do istniejącego repo OPNsense (`signature_type: none`). To nadaje się do testów DEV na zaufanej infrastrukturze. Przed produkcyjnym wdrożeniem należy dodać podpis katalogu `pkg`, bezpieczną dystrybucję klucza publicznego i wykonać instalację oraz aktualizację na rzeczywistym FreeBSD. Generator katalogu korzysta z formatu repozytorium `pkg` w wersji **2** dla FreeBSD 15 (OPNsense zachowuje dotychczasowy format), ale testy offline nie zastępują testu klienta `pkg` na prawdziwym FreeBSD.
Pakietowe testy offline:
Pakietowe testy offline są dostępne w pełnym repozytorium developerskim (nie w wydaniu wdrożeniowym).
```sh
./tests/static.sh
./tests/linux-path-access.sh
./tests/opnsense-plugin.sh
./tests/log-rotation.sh
./tests/freebsd-native.sh
./tests/toolchains.sh
```
## Troubleshooting: no native FreeBSD build
1. Ensure `.env` contains `FREEBSD_NATIVE_ENABLED=1` (older installations often set `0`).
2. Rebuild/start: `docker compose --env-file .env up -d --build rust-package-builder repository-dashboard`.
3. Follow the builder logs: `docker compose --env-file .env logs -f rust-package-builder`.
4. Look for `build plan: ... freebsd=1` and then `FreeBSD native publication completed:`. If compilation fails, inspect the **first** `error:` in the same log. The OPNsense package uses the same FreeBSD cross-compiled executable but native FreeBSD publishes independently.
5. Check `data/repository/current/freebsd/BUILD-INFO.txt` and `data/repository/current/freebsd/meta.conf` (`version: 2;`). The API `/api/v1/packages` then shows the published FreeBSD package/version/build time.
The development test `tests/freebsd-release.sh` uses a simulated ELF to verify packaging/catalog/atomic publication without Docker. It **does not prove Rust compilation or installation on real FreeBSD**. For end-to-end validation, run `pkg update -f` and `pkg install rustproxy` on FreeBSD 15.1 amd64 against the published URL. The catalog is **not signed**, so only use a trusted HTTPS source during testing.
+16 -27
View File
@@ -1,16 +1,14 @@
516d7009f920efa26f60acbd859fa62cd3c7e23953c805173150145b1428a033 .env.example
c92255b37b57e5356a10e88a8be14c856e9ef4372379c48423d69db7a1365625 .env.example
17298e7669ed82b4c98abfce803e1a3f0a265b0abdc2567bf1316aa15e98aad3 Dockerfile
ef47c4a3fad51d17ad690b0d6aa440d6ef3e355c5e9bd1824368f5450b4b7957 README.md
16821f51ece84db491da19b64f1129710fb46288e9807abe31e1000fe33f72df README.md
862263fa1f46c20f0d1e4dac5ffcc75abd55c08211b2c3864c5f8764b9d87793 dashboard/.dockerignore
ced0339cb93478c2faf7b7b74215fe03375086e11ca957bd8892b52802fbbd3a dashboard/Dockerfile
4bd84a56b756d6109a4fde23c6fcb2fef1b290302312a55046b8487cb9c36d74 dashboard/app.py
345dbb2c4293405092445e159ce23053894d0c2335457f7299622ca42382dc05 dashboard/web/app.js
bd2ba3a11ee715ba41a94f3301bec4ce83d7177489190a755e165112df50f112 dashboard/app.py
6447c64295ac62dd88d03fc796344f8702d6af54586720bb3e1240eb4f16db42 dashboard/web/app.js
f1ac0fb5c4da97db09ff328cb86f4bc763fbdd84d7310d6e8e713be1c8707bb9 dashboard/web/favicon.svg
c7427bf31438cbbec9152c93674088bb2a16ea1d9a499b3c5c85beee2c543dda dashboard/web/index.html
a3834770bcc9c0548bf387fb52795f3f6cb15b253f7d3819df9be147b04ebb8e dashboard/web/style.css
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 data/.gitkeep
328b8b8075208e0c92793cf70c6d8999775faf94abf5d839101e6d3ba35597cf docker-compose.yml
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 gpg/.gitkeep
df2483e63e8559f41da83712e94b5f216faff38f4786fd2c804eac4576645c23 dashboard/web/index.html
2b5a34085be972372401abc9af16e2c14cc4e682fac448afb7c6f364d61b8649 dashboard/web/style.css
ebb5e6eefe375ca1357d96f6a7344424c43df0bc51bc01ee66a459e54ef4b3c7 docker-compose.yml
7da761300aef6cf22ce28c1c7a55414b692814023e4d540a3f3ec4111c62926c http/nginx.conf
77e898aebb54f8acf39d3eb1fb348e7f41532da1b0a9e1820a717fdcde406f71 opnsense/patches/0.20.0/0001-opnsense-build.patch
80bb419b980793f3a57b4fda0dc132a872186b3a07e0e6db2585c6057ab4f882 opnsense/plugin/+POST_INSTALL.pre
@@ -43,32 +41,23 @@ c8dde46217c008bfc31420c19ecbd31e3db6d71ee48f99be856ad40ca3c7f490 packaging/free
79122afbd5964a87c0d933abe8b34745d4189e2a586d5918a82efbb9b2f50864 packaging/rustproxy-filesystem-access.md
0f7970888117b4bc810ad3f6abc6796f189d34e4863875b2501785e689471be6 packaging/rustproxy.logrotate
eee3cb8959cb90cd1b5b320ec4674892e5d1a987049d1028dae39c3bdc9f3dc7 scripts/apply-freebsd-patches.sh
5751d1e6fd0a5b0347ae6f80fb906e4ae306a34b51f0cf6837f2c062b6729f22 scripts/build-freebsd-release.sh
a869ece6aef358abd5901474c5c2201e74bfc56d24bfe9931ad5cc3837aea215 scripts/build-freebsd-release.sh
d197c25fd346a2cd2a516434a8d9fab304ae6fbf3b215e4f9ea35e6efbc4f417 scripts/build-linux-release.sh
01aaa5ecd63e15f3a5436ca394d26308f7616117233dcac8a74a6db73ab68142 scripts/build-opnsense-release.sh
5db9f6e5c782bb9afcf455688dea7c1de1d6070c510bbedc9408af5b0fe509b1 scripts/build-opnsense.sh
b795c04884f75cae9154af80dfa5ec418b95c8991ed2927b8953c39e3f01a4e8 scripts/build-release.sh
1e43a0e6706c0457ab04b155fa566edd509fc52bc4023eb8c44d4271a944e62c scripts/check-and-build.sh
16bafbba1eb2000b42c754bae92fee43301e57e0859e2cef17be2550006ab111 scripts/build-opnsense.sh
ed581639a606323d4e91e632bc40a9be7037570f91cdef4264e4db0ca130ee8e scripts/build-release.sh
62fc5506b3786a1c943299a3d8107224dadb1b5bdee068acbbd4f649e7edc13b scripts/check-and-build.sh
cba164bde8967045bfb76de7eff46d4407dfb7ed2bc861fbc600782551d1629e scripts/ensure-rust-toolchain.sh
62d45e193b06c8fae02705b5214092f13a916cf0c637bbc793b4c46eac6a39fd scripts/entrypoint.sh
621bc5e0bba15cba16271d7a02e7d34ea3652b88fb481a46b134f2dac01b7f4f scripts/generate-freebsd-index.sh
6cc8f506dd72189af34ed22334467e9af022690860edcc902e80b2ae976384ea scripts/entrypoint.sh
c8402314cbcf87c30eb521a273a13628eccb78112acb85e0e85c9a7679550cca scripts/generate-freebsd-index.sh
4220710396d0670d5c9ebcc31c6c4d59073d2b1c192f08268a3ea49164d1dcbd scripts/generate-index.sh
80f49932346268f2cd63cbfe4814c666cd665661e82c25f2e99883929ecae0c0 scripts/generate-opnsense-index.sh
a21afa240ca0869c2f92af7a73899f270361b2b8677b897646479452185196fe scripts/generate-opnsense-repo.py
094c603f30f6a41bddeedfefa9e2c260dc97f849f0a690475ebe128ce4b49475 scripts/generate-opnsense-repo.py
40186599540f6886188abda5dac72bc85d15b9b71ce20788061131bfcd519cb7 scripts/init-storage.sh
d078c62c54464095e7ddd2071c58a2ac3abd972f3aaa5b4bad0596735eca170c scripts/lib.sh
4fdb7f98e83a262e9ad22f08fd5dd776993009f7b22623dfa9b04f554e48ac3c scripts/lib.sh
de4fbbb973d9cddb170ef1e9f4f6f7896018ed5e6184594c9636c0215adf5233 scripts/normalize-systemd-unit.py
0eca8ef60f15dce0c5651fc8a55de10e0d9a11f3816f04153608abcbe2e2e1d8 scripts/package-freebsd.sh
b098fa629f9fcc1a54baab3d46b28721eb582c314e084b3210259bf157c124a6 scripts/package-opnsense.sh
4ad289bc2c0e400b39a8d1bcf86395d7576c88e71b678ba8b893ccb72ac35465 scripts/prepare-agent-package.sh
46908bfac309688340be72edded60efaecd765aa151f520a2491952619f53a43 scripts/prepare-package.sh
57aae30a7ace2253e80f454e542bccf932e081457482718d7a4822e796b0c882 start.sh
f0bde843d75330bc49e3ab5547e739d8fbda8d74d0b12dfdbdb7648847e91e88 tests/check-rustproxy-source.sh
a55cc19cba2729ea3e8d82e8e5e90d734ba77c0e0d3f56a1b432e2e7e5316dc5 tests/docker-agent-linux.sh
e82c52ef15183a539e96909a39e44db68c8301543950e145008b563524b0b9b9 tests/freebsd-native.sh
84e9822fe9bbf87dd574b32fc2df5eebf317d3f26677a46035a49eb16044a4c1 tests/linux-path-access.sh
c5781dd2335cc85e72daffe807c7ba24481b6c2e8bc95f4012be4ea1baeac9a8 tests/log-rotation.sh
aab57cd66dc157dc2359d8d57612c174570a7ea09fe753428dce8faad231ba3c tests/opnsense-plugin.sh
1227d503f784f7765c5950b603e4258ea155c2fdc3779886a7b0c403320be2a8 tests/static.sh
ff0fe09dcd33124644a9babdf951193ebb358015997b302c7e3775a6c7a4d762 tests/test_dashboard.py
c8de9ebbf51e017643d0735f27b960a6cf3c9b7122f6de32858f6b7dcc7ba9e9 tests/toolchains.sh
2b6d2dc393be6dd5ba5184e6ccd068a8faf193bcd280f349a4b1da890187a79f start.sh
+3
View File
@@ -138,6 +138,9 @@ def package_entry(path: Path, current: Path, site: str, kind: str, channel: str,
if not parsed or not path.is_file():
return None
name, version, arch = parsed
# Discovery agent is packaged for Linux DEB/RPM only; never advertise it as FreeBSD pkg.
if channel in ('opnsense', 'freebsd') and name == 'rustproxy-docker-agent':
return None
try:
stat = path.stat()
except OSError:
+45 -41
View File
@@ -5,10 +5,10 @@
const el = (id) => document.getElementById(id);
const CHANNEL_LABELS = {linux:'Linux',opnsense:'OPNsense',freebsd:'FreeBSD'};
const INSTALL_NAMES = {deb:'Debian / Ubuntu',rpm:'RHEL / Rocky / Alma / Fedora',opnsense:'OPNsense',freebsd:'FreeBSD native'};
const SECTION_LABELS = {overview:'Przegląd',packages:'Paczki i wersje',install:'Instalacja',repository:'Repozytoria'};
const SECTION_LABELS = {overview:'Overview',packages:'Packages & versions',install:'Installation',repository:'Repositories'};
const esc = (value) => String(value ?? '').replace(/[&<>"']/g, (char) => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char]));
const collator = new Intl.Collator('pl', {numeric:true,sensitivity:'base'});
const formatNumber = new Intl.NumberFormat('pl-PL');
const collator = new Intl.Collator('en', {numeric:true,sensitivity:'base'});
const formatNumber = new Intl.NumberFormat('en-GB');
const site = ['linux','opnsense','freebsd'].includes(document.body.dataset.site) ? document.body.dataset.site : 'linux';
const state = {
site, catalog:null, section:'overview', installing:site === 'linux' ? 'deb' : site,
@@ -20,13 +20,13 @@
if (!value) return '—';
const time = new Date(value);
if (Number.isNaN(time.valueOf())) return '—';
return new Intl.DateTimeFormat('pl-PL', {year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'}).format(time);
return new Intl.DateTimeFormat('en-GB', {year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'}).format(time);
};
const formatShortDate = (value) => {
if (!value) return '—';
const time = new Date(value);
if (Number.isNaN(time.valueOf())) return '—';
return new Intl.DateTimeFormat('pl-PL', {day:'2-digit',month:'short',year:'numeric'}).format(time);
return new Intl.DateTimeFormat('en-GB', {day:'2-digit',month:'short',year:'numeric'}).format(time);
};
const formatSize = (value) => {
const size = Number(value);
@@ -43,7 +43,7 @@
return '#';
};
const origin = (url) => typeof url === 'string' ? url.replace(/\/$/, '') : '';
const dateLabel = (kind) => kind === 'build' ? 'Data zapisana przez builder' : 'Data pliku (starsze buildy bez znacznika czasu)';
const dateLabel = (kind) => kind === 'build' ? 'Build timestamp recorded by the builder' : 'File modification time (older builds without a build timestamp)';
const setText = (id, value) => {const node = el(id);if (node) node.textContent = value;};
const versionText = (b) => b?.app_version ? `v${b.app_version}` : '—';
@@ -82,7 +82,7 @@
? `PKG · ${build?.opnsense_series || 'FreeBSD'}` : `PKG · ${build?.freebsd_abi || 'Native'}`;
const version = build?.package_version || (live ? build?.app_version : '') || '—';
return `<article class="build-card${live ? '' : ' off'}">
<div class="build-card-top"><span>${esc(CHANNEL_LABELS[channel].toUpperCase())} / ${channel === 'linux' ? 'SIGNED' : 'PKG'}</span><span class="pill ${live ? 'live' : 'missing'}">${live ? 'PUBLISHED' : 'BRAK BUILDU'}</span></div>
<div class="build-card-top"><span>${esc(CHANNEL_LABELS[channel].toUpperCase())} / ${channel === 'linux' ? 'SIGNED' : 'PKG'}</span><span class="pill ${live ? 'live' : 'missing'}">${live ? 'PUBLISHED' : 'NOT BUILT'}</span></div>
<div class="build-card-version">${esc(version)}</div><div class="build-card-sub">${esc(subtitle)}</div>
<div class="build-card-details"><div><small>BUILD DATE</small><strong title="${esc(dateLabel(build?.date_source))}">${esc(formatDate(build?.built_at))}</strong></div><div><small>COMMIT</small><strong class="commit">${esc(commit)}</strong></div></div>
</article>`;
@@ -92,7 +92,7 @@
return `<span class="format-label${pkg.format === 'PKG' ? ' pkg' : ''}">${esc(pkg.format)}</span>`;
}
function tableRow(pkg, compact = false) {
const download = `<a class="download-link" href="${esc(safeLink(pkg.download_url))}" target="_blank" rel="noopener noreferrer" download title="Pobierz ${esc(pkg.name)} ${esc(pkg.version)}">${compact ? 'Pobierz' : 'Pobierz plik'} <span>↗</span></a>`;
const download = `<a class="download-link" href="${esc(safeLink(pkg.download_url))}" target="_blank" rel="noopener noreferrer" download title="Download ${esc(pkg.name)} ${esc(pkg.version)}">${compact ? 'Download' : 'Download file'} <span>↗</span></a>`;
const first = `<span class="artifact-name" title="${esc(pkg.name)}">${esc(pkg.name)}</span><span class="artifact-arch">${esc(pkg.architecture)}</span>`;
const format = `${formatBadge(pkg)}<span class="channel-tag ${pkg.format === 'PKG' ? 'pkg' : ''}">${esc(CHANNEL_LABELS[pkg.channel] || pkg.channel)}</span>`;
const date = `<span class="date-cell" title="${esc(dateLabel(pkg.date_source))}">${esc(formatDate(pkg.built_at))}</span>`;
@@ -112,29 +112,29 @@
setText('stat-channels', `${data.summary?.channels_published || 0} / 3`);
setText('stat-date', formatShortDate(data.summary?.latest_build_at));
setText('stat-version', heroVersion);
setText('stat-release', main?.package_version ? `Pakiet: ${main.package_version}` : 'Brak opublikowanej wersji');
setText('stat-date-sub', 'Data ostatniej publikacji');
setText('stat-release', main?.package_version ? `Package: ${main.package_version}` : 'No release published yet');
setText('stat-date-sub', 'Latest publication date');
setText('pipeline-updated', formatDate(data.generated_at));
const ready = !!data.ready;
el('hero-status').innerHTML = `<span class="pulse-dot"></span> ${ready ? 'REPOZYTORIUM AKTYWNE' : 'OCZEKIWANIE NA BUILD'}`;
el('hero-status').innerHTML = `<span class="pulse-dot"></span> ${ready ? 'REPOSITORY ONLINE' : 'WAITING FOR BUILD'}`;
el('hero-status').classList.toggle('waiting', !ready);
el('sidebar-status').classList.toggle('waiting', !ready);
el('sidebar-status').querySelector('span').textContent = ready ? 'REPOSITORY ONLINE' : 'OCZEKIWANIE NA DANE';
el('sidebar-status').querySelector('span').textContent = ready ? 'REPOSITORY ONLINE' : 'WAITING FOR DATA';
const side = el('side-channels');
side.innerHTML = ['linux','opnsense','freebsd'].map((channel) => `<span class="side-channel${data.builds[channel]?.published ? '' : ' disabled'}"><i class="tiny-dot"></i>${esc(CHANNEL_LABELS[channel])}<small>${channel === 'linux' ? 'APT / DNF' : 'PKG'}</small></span>`).join('');
el('build-grid').innerHTML = ['linux','opnsense','freebsd'].map((channel) => buildCard(data.builds[channel],channel)).join('');
const recent = data.packages.slice(0,6);
el('recent-rows').innerHTML = recent.length ? recent.map((pkg) => tableRow(pkg,true)).join('') : '<tr><td colspan="5" class="empty-table">Nie opublikowano jeszcze żadnych paczek. Katalog zapełni się po pierwszym buildzie.</td></tr>';
const recent = data.packages.filter((pkg) => state.site === 'linux' || pkg.channel === state.site).slice(0,6);
el('recent-rows').innerHTML = recent.length ? recent.map((pkg) => tableRow(pkg,true)).join('') : '<tr><td colspan="5" class="empty-table">No packages published yet. The catalog will populate after the first build.</td></tr>';
}
function filteredPackages() {
const arr = state.catalog?.packages || [];
const search = state.search.trim().toLocaleLowerCase('pl-PL');
const search = state.search.trim().toLocaleLowerCase('en-GB');
const filtered = arr.filter((pkg) => {
if (state.channel !== 'all' && pkg.channel !== state.channel) return false;
if (state.format !== 'all' && pkg.format !== state.format) return false;
if (!search) return true;
return [pkg.name,pkg.version,pkg.channel,pkg.format,pkg.architecture].some((field) => String(field ?? '').toLocaleLowerCase('pl-PL').includes(search));
return [pkg.name,pkg.version,pkg.channel,pkg.format,pkg.architecture].some((field) => String(field ?? '').toLocaleLowerCase('en-GB').includes(search));
});
if (state.sort === 'name') filtered.sort((a,b) => collator.compare(a.name,b.name) || collator.compare(b.version,a.version));
if (state.sort === 'version') filtered.sort((a,b) => collator.compare(b.version,a.version) || collator.compare(a.name,b.name));
@@ -147,10 +147,10 @@
if (!data) return;
const all = filteredPackages();
const shown = all.slice(0,state.rows);
setText('packages-total', `${formatNumber.format(data.summary.packages_count)} paczek`);
setText('filter-count', `${formatNumber.format(all.length)} ${all.length === 1 ? 'wynik' : 'wyników'}`);
setText('result-range', all.length ? `Wyświetlono ${shown.length} z ${all.length}` : 'Brak pasujących paczek');
el('catalog-rows').innerHTML = shown.length ? shown.map((pkg) => tableRow(pkg)).join('') : '<tr><td colspan="6" class="empty-table">Brak paczek spełniających kryteria. Zmień filtry lub poczekaj na publikację.</td></tr>';
setText('packages-total', `${formatNumber.format(data.summary.packages_count)} packages`);
setText('filter-count', `${formatNumber.format(all.length)} ${all.length === 1 ? 'result' : 'results'}`);
setText('result-range', all.length ? `Showing ${shown.length} of ${all.length}` : 'No matching packages');
el('catalog-rows').innerHTML = shown.length ? shown.map((pkg) => tableRow(pkg)).join('') : '<tr><td colspan="6" class="empty-table">No packages match these filters. Adjust your search or check back after a new release.</td></tr>';
el('show-more').hidden = shown.length >= all.length;
}
@@ -163,9 +163,9 @@
const component = data.apt_component || 'main';
if (kind === 'deb') {
return {
description:'Dodaj podpisane repozytorium APT, odśwież listę pakietów i zainstaluj RustProxy.',
description:'Add the signed APT repository, update the package list, and install RustProxy.',
signature:'GPG SIGNED',
note:'Obsługiwane: Debian / Ubuntu · architektura amd64.',
note:'Supported: Debian / Ubuntu · amd64 architecture.',
reference:`${linux}/apt/dists/${encodeURIComponent(distribution)}/InRelease`,
agent:'sudo apt install rustproxy-docker-agent',
code:[
@@ -182,9 +182,9 @@
}
if (kind === 'rpm') {
return {
description:'Dodaj podpisane repozytorium DNF/YUM i zainstaluj serwer RustProxy.',
description:'Add the signed DNF/YUM repository and install the RustProxy server.',
signature:'GPG SIGNED',
note:'Obsługiwane: RHEL / Rocky / Alma / Fedora · architektura x86_64.',
note:'Supported: RHEL / Rocky / Alma / Fedora · x86_64 architecture.',
reference:`${linux}/rpm/x86_64/repodata/repomd.xml`,
agent:'sudo dnf install rustproxy-docker-agent',
code:[
@@ -206,11 +206,11 @@
}
if (kind === 'opnsense') {
return {
description:'Pobierz konfigurację repozytorium pkg i zainstaluj plugin os-rustproxy.',
description:'Fetch the pkg repository configuration and install the os-rustproxy plugin.',
signature:'UNSIGNED PKG',
note:'Repozytorium pkg nie ma podpisanego katalogu. Korzystaj tylko z zaufanego hosta HTTPS.',
note:'The pkg repository index is unsigned. Only use a trusted HTTPS host.',
reference:`${opnsense}/RustProxy.conf`,
agent:'sudo apt install rustproxy-docker-agent',
agent:null,
code:[
'mkdir -p /usr/local/etc/pkg/repos',
`fetch -o /usr/local/etc/pkg/repos/RustProxy.conf ${opnsense}/RustProxy.conf`,
@@ -220,11 +220,11 @@
};
}
return {
description:'Zainstaluj natywny pakiet rustproxy i włącz usługę rc.d.',
description:'Install the native rustproxy package and enable its rc.d service.',
signature:'UNSIGNED PKG',
note:'Dostępne po ustawieniu FREEBSD_NATIVE_ENABLED=1. Katalog pkg jest niesygnowany.',
note:'The FreeBSD pkg index is unsigned. Only use a trusted HTTPS source.',
reference:`${freebsd}/RustProxy-FreeBSD.conf`,
agent:'sudo dnf install rustproxy-docker-agent',
agent:null,
code:[
'mkdir -p /usr/local/etc/pkg/repos',
`fetch -o /usr/local/etc/pkg/repos/RustProxy-FreeBSD.conf ${freebsd}/RustProxy-FreeBSD.conf`,
@@ -251,7 +251,12 @@
el('install-signature').classList.toggle('live', instructionsData.signature === 'GPG SIGNED');
setText('install-command', instructionsData.code);
setText('install-note', instructionsData.note);
setText('agent-command', instructionsData.agent);
// Docker discovery agent is only built for Linux DEB/RPM.
const hasDockerAgent = (kind === 'deb' || kind === 'rpm') && !!instructionsData.agent;
el('agent-card').hidden = !hasDockerAgent;
el('install-extra-grid').classList.toggle('single-card', !hasDockerAgent);
setText('trust-index', hasDockerAgent ? '02' : '01');
setText('agent-command', hasDockerAgent ? instructionsData.agent : '');
el('install-reference').href = safeLink(instructionsData.reference);
}
@@ -269,7 +274,7 @@
const distribution = encodeURIComponent(data.apt_distribution || 'stable');
el('endpoints-grid').innerHTML = [
endpointsCard('Linux','GPG SIGNED',url.linux,[
{name:'Klucz publiczny GPG',url:`${url.linux}/repo-key.gpg`},
{name:'GPG public key',url:`${url.linux}/repo-key.gpg`},
{name:'APT InRelease',url:`${url.linux}/apt/dists/${distribution}/InRelease`},
{name:'RPM repomd.xml',url:`${url.linux}/rpm/x86_64/repodata/repomd.xml`},
{name:'Build info',url:`${url.linux}/LINUX-BUILD-INFO.txt`},
@@ -290,13 +295,13 @@
el('metadata-table').innerHTML = ['linux','opnsense','freebsd'].map((channel) => {
const b = data.builds[channel] || {};
const rows = [
['WERSJA APLIKACJI',b.app_version || '—','latest-value'],
['WERSJA PACZKI',b.package_version || '—',''],
['DATA BUILDU',formatDate(b.built_at),''],
['APPLICATION VERSION',b.app_version || '—','latest-value'],
['PACKAGE VERSION',b.package_version || '—',''],
['BUILD DATE',formatDate(b.built_at),''],
['SOURCE COMMIT',b.source_commit || '—',''],
['REWIZJA PAKOWANIA',b.packaging_revision || '—',''],
['PACKAGING REVISION',b.packaging_revision || '—',''],
];
return `<div class="meta-block"><h3>${esc(CHANNEL_LABELS[channel])}</h3>${rows.map(([label,value,klass]) => `<div class="meta-row"><small>${esc(label)}</small><strong class="${klass}"${label === 'DATA BUILDU' ? ` title="${esc(dateLabel(b.date_source))}"` : ''}>${esc(value)}</strong></div>`).join('')}</div>`;
return `<div class="meta-block"><h3>${esc(CHANNEL_LABELS[channel])}</h3>${rows.map(([label,value,klass]) => `<div class="meta-row"><small>${esc(label)}</small><strong class="${klass}"${label === 'BUILD DATE' ? ` title="${esc(dateLabel(b.date_source))}"` : ''}>${esc(value)}</strong></div>`).join('')}</div>`;
}).join('');
}
@@ -307,7 +312,6 @@
renderInstall();
renderRepositories();
setText('last-sync', formatDate(data.generated_at));
setText('footer-time', formatDate(data.generated_at));
el('last-sync').innerHTML = `<i></i> ${esc(formatDate(data.generated_at))}`;
}
@@ -325,7 +329,7 @@
el('error-banner').hidden = true;
} catch (err) {
el('error-banner').hidden = false;
setText('last-sync','Błąd odczytu');
setText('last-sync','Load failed');
console.error('RustProxy repository:',err);
} finally {
state.refreshing = false;
@@ -349,8 +353,8 @@
input.remove();
if (!copied) throw new Error('Clipboard unsupported');
}
toast('Skopiowano do schowka');
} catch (_) { toast('Nie udało się skopiować — zaznacz polecenia ręcznie'); }
toast('Copied to clipboard');
} catch (_) { toast('Copy failed. Select and copy the command manually.'); }
}
function init() {
+44 -49
View File
@@ -1,5 +1,5 @@
<!doctype html>
<html lang="pl" data-theme="dark">
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
@@ -18,14 +18,13 @@
<span class="brand-text">RustProxy <small>PACKAGE REGISTRY</small></span>
</a>
<div class="edition">INDEPENDENT BY DESIGN <span>BUILD DISTRIBUTION</span></div>
<div class="sidebar-label">WORKSPACE / 01</div>
<nav class="main-navigation" aria-label="Nawigacja panelu">
<button class="nav active" data-section="overview" aria-current="page"><span>01</span><b>Przegląd</b><span class="nav-arrow">↗</span></button>
<button class="nav" data-section="packages"><span>02</span><b>Paczki i wersje</b><span class="nav-arrow">↗</span></button>
<button class="nav" data-section="install"><span>03</span><b>Instalacja</b><span class="nav-arrow">↗</span></button>
<button class="nav" data-section="repository"><span>04</span><b>Repozytoria</b><span class="nav-arrow">↗</span></button>
<nav class="main-navigation" aria-label="Dashboard navigation">
<button class="nav active" data-section="overview" aria-current="page"><span>01</span><b>Overview</b><span class="nav-arrow">↗</span></button>
<button class="nav" data-section="packages"><span>02</span><b>Packages &amp; versions</b><span class="nav-arrow">↗</span></button>
<button class="nav" data-section="install"><span>03</span><b>Installation</b><span class="nav-arrow">↗</span></button>
<button class="nav" data-section="repository"><span>04</span><b>Repositories</b><span class="nav-arrow">↗</span></button>
</nav>
<div class="sidebar-label sidebar-label--second">DOSTĘPNE KANAŁY</div>
<div class="sidebar-label sidebar-label--second">AVAILABLE CHANNELS</div>
<div class="side-channels" id="side-channels">
<span class="side-channel"><i class="tiny-dot"></i> Linux <small>APT / DNF</small></span>
<span class="side-channel"><i class="tiny-dot"></i> OPNsense <small>PKG</small></span>
@@ -34,100 +33,96 @@
<div class="sidebar-bottom">
<div class="small-label">REPOSITORY ENGINE</div>
<p>Python / HTTP API<br>Nginx / Signed APT + RPM<br>FreeBSD pkg catalog</p>
<div class="local-tag" id="sidebar-status"><i></i><span>OCZEKIWANIE NA DANE</span></div>
<div class="local-tag" id="sidebar-status"><i></i><span>WAITING FOR DATA</span></div>
<div class="copyright">© RustProxy <span id="year"></span></div>
</div>
</aside>
<div class="workspace">
<header class="topbar">
<div class="crumb"><span>RUSTPROXY</span><b>/</b><span>REPOSITORY</span><b>/</b><strong id="crumb-section">Przegląd</strong></div>
<div class="crumb"><span>RUSTPROXY</span><b>/</b><span>REPOSITORY</span><b>/</b><strong id="crumb-section">Overview</strong></div>
<div class="top-actions">
<span class="live-sync" id="last-sync"><i></i> Odczyt danych…</span>
<button class="quiet icon-button" id="refresh" title="Odśwież listę paczek" aria-label="Odśwież dane">
<span class="live-sync" id="last-sync"><i></i> Loading data…</span>
<button class="quiet icon-button" id="refresh" title="Refresh package list" aria-label="Refresh data">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 7v5h-5M4 17v-5h5"/><path d="M5 9a8 8 0 0 1 13.4-2.2L20 12M4 12l1.6 5.2A8 8 0 0 0 19 15"/></svg>
</button>
<button class="quiet theme-switch" id="theme-toggle" title="Zmień motyw" aria-label="Zmień motyw"><span class="theme-glyph">◐</span><span id="theme-name">Dark</span></button>
<button class="quiet theme-switch" id="theme-toggle" title="Change theme" aria-label="Change theme"><span class="theme-glyph">◐</span><span id="theme-name">Dark</span></button>
</div>
</header>
<main class="main-content">
<div class="error-banner" id="error-banner" hidden role="status">Nie można pobrać katalogu. Automatyczna próba ponowienia za minutę. <button id="retry-inline">Spróbuj teraz</button></div>
<div class="error-banner" id="error-banner" hidden role="status">Could not load the catalog. Retrying automatically in one minute. <button id="retry-inline">Retry now</button></div>
<section class="view" id="view-overview" aria-labelledby="overview-title">
<div class="page-head">
<div>
<div class="eyebrow"><span class="eyebrow-line"></span> DISTRIBUTION CONTROL ROOM <span class="eyebrow-separator">/</span> LIVE CATALOG</div>
<h1 id="overview-title">Pakiety. Wersje.<br><em>Pełna kontrola.</em></h1>
<p class="lead">Jedno miejsce dla wydań RustProxy na Linux, OPNsense i FreeBSD. Aktualne buildy, daty publikacji i gotowe instrukcje instalacji.</p>
</div>
<div class="page-head-side"><div class="subtle-label">STATUS / LIVE DATA</div><span class="active-status" id="hero-status"><span class="pulse-dot"></span> ODCZYTYWANIE</span><div class="muted-detail">Odświeżanie automatyczne co 60 sekund</div></div>
<section class="view" id="view-overview" aria-label="Overview">
<div class="overview-statusbar">
<span class="active-status" id="hero-status"><span class="pulse-dot"></span> LOADING</span>
<span class="muted-detail">Refreshes every 60 seconds</span>
</div>
<div class="hero-grid">
<div class="hero-release">
<div class="hero-header"><span><span class="dot-amber"></span> NAJNOWSZE WYDANIE <span id="hero-channel">LINUX</span></span><span class="hero-corner">R / P — 01</span></div>
<div class="hero-header"><span><span class="dot-amber"></span> LATEST RELEASE <span id="hero-channel">LINUX</span></span><span class="hero-corner">R / P — 01</span></div>
<div class="hero-version-label">RUSTPROXY / APPLICATION VERSION</div>
<div class="hero-version" id="hero-version">—</div>
<div class="hero-foot">
<div><small>ZBUDOWANO / OPUBLIKOWANO</small><strong id="hero-date">Brak informacji</strong></div>
<div><small>BUILT / PUBLISHED</small><strong id="hero-date">Not available</strong></div>
<div><small>SOURCE COMMIT</small><strong class="mono" id="hero-commit">—</strong></div>
<button class="hero-link" data-go-section="packages">Pokaż wydania <span aria-hidden="true">↗</span></button>
<button class="hero-link" data-go-section="packages">View releases <span aria-hidden="true">↗</span></button>
</div>
<div class="hero-background-rings" aria-hidden="true"></div>
</div>
<div class="pipeline-panel">
<div class="pipeline-head"><span class="small-label">BUILD PIPELINE</span><span class="pipeline-live"><i class="tiny-dot"></i> READ ONLY</span></div>
<p>Od źródeł do gotowych paczek.</p>
<p>From source code to installable packages.</p>
<div class="pipeline-list">
<div class="pipeline-step"><span class="step-index">01</span><span class="step-info"><b>Git / source</b><small>RustProxy upstream</small></span><span class="step-right">GIT <span>↗</span></span></div>
<div class="pipeline-connector"></div>
<div class="pipeline-step"><span class="step-index">02</span><span class="step-info"><b>Build / package</b><small>Rust + Go + FreeBSD</small></span><span class="step-right">CI <span>↗</span></span></div>
<div class="pipeline-connector"></div>
<div class="pipeline-step"><span class="step-index">03</span><span class="step-info"><b>Signed repository</b><small>APT / RPM / PKG</small></span><span class="step-right step-success">LIVE <span>●</span></span></div>
<div class="pipeline-step"><span class="step-index">03</span><span class="step-info"><b>Repository publish</b><small>APT / RPM / PKG</small></span><span class="step-right step-success">LIVE <span>●</span></span></div>
</div>
<div class="pipeline-footer">Ostatni odczyt <span id="pipeline-updated">—</span></div>
<div class="pipeline-footer">Last sync <span id="pipeline-updated">—</span></div>
</div>
</div>
<div class="stats-grid" aria-label="Statystyki repozytorium">
<div class="stat"><span>01 / PACZEK W KATALOGU</span><strong id="stat-packages">—</strong><small>Wszystkie opublikowane artefakty</small></div>
<div class="stat"><span>02 / AKTYWNE KANAŁY</span><strong id="stat-channels">—</strong><small>Linux · OPNsense · FreeBSD</small></div>
<div class="stat"><span>03 / OSTATNI BUILD</span><strong class="stat-date" id="stat-date">—</strong><small id="stat-date-sub">UTC / czas publikacji</small></div>
<div class="stat"><span>04 / AKTUALNA WERSJA</span><strong id="stat-version">—</strong><small id="stat-release">Wersja aplikacji</small></div>
<div class="stats-grid" aria-label="Repository statistics">
<div class="stat"><span>01 / CATALOG PACKAGES</span><strong id="stat-packages">—</strong><small>All published artifacts</small></div>
<div class="stat"><span>02 / ACTIVE CHANNELS</span><strong id="stat-channels">—</strong><small>Linux · OPNsense · FreeBSD</small></div>
<div class="stat"><span>03 / LATEST BUILD</span><strong class="stat-date" id="stat-date">—</strong><small id="stat-date-sub">Latest publication time</small></div>
<div class="stat"><span>04 / CURRENT VERSION</span><strong id="stat-version">—</strong><small id="stat-release">Application version</small></div>
</div>
<div class="section-heading"><div><span class="section-index">01 / RELEASE CHANNELS</span><h2>Ostatnie buildy</h2></div><button class="text-link" data-go-section="packages">Cały katalog <span>↗</span></button></div>
<div class="build-grid" id="build-grid"><div class="skeleton-card">Ładowanie kanałów…</div></div>
<div class="section-heading"><div><span class="section-index">01 / RELEASE CHANNELS</span><h2>Latest builds</h2></div><button class="text-link" data-go-section="packages">View catalog <span>↗</span></button></div>
<div class="build-grid" id="build-grid"><div class="skeleton-card">Loading channels…</div></div>
<div class="section-heading section-heading--second"><div><span class="section-index">02 / RECENT ARTIFACTS</span><h2>Najnowsze paczki</h2></div><button class="text-link" data-go-section="packages">Przeglądaj paczki <span>↗</span></button></div>
<div class="section-heading section-heading--second"><div><span class="section-index">02 / RECENT ARTIFACTS</span><h2>Recent packages</h2></div><button class="text-link" data-go-section="packages">Browse packages <span>↗</span></button></div>
<div class="card recent-card">
<div class="table-scroller"><table class="data-table"><thead><tr><th>PAKIET</th><th>FORMAT / KANAŁ</th><th>WERSJA PACZKI</th><th>DATA BUILDU</th><th class="table-action-col">PLIK</th></tr></thead><tbody id="recent-rows"><tr><td colspan="5" class="empty-table">Ładowanie danych…</td></tr></tbody></table></div>
<div class="table-scroller"><table class="data-table"><thead><tr><th>PACKAGE</th><th>FORMAT / CHANNEL</th><th>PACKAGE VERSION</th><th>BUILD DATE</th><th class="table-action-col">FILE</th></tr></thead><tbody id="recent-rows"><tr><td colspan="5" class="empty-table">Loading data…</td></tr></tbody></table></div>
</div>
</section>
<section class="view" id="view-packages" aria-labelledby="packages-title" hidden>
<div class="page-head compact-head"><div><div class="eyebrow"><span class="eyebrow-line"></span> ARTIFACT EXPLORER <span class="eyebrow-separator">/</span> VERSIONS</div><h1 id="packages-title">Katalog <em>paczek.</em></h1><p class="lead">Wyszukuj, filtruj, sortuj i pobieraj opublikowane pliki. Wszystkie dane są odczytywane bezpośrednio z aktualnego repozytorium.</p></div><div class="count-pill" id="packages-total">0 paczek</div></div>
<div class="filter-panel"><div class="search-field"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m16.5 16.5 4 4"/></svg><input id="search-input" type="search" placeholder="Szukaj nazwy, wersji, architektury..." aria-label="Szukaj paczek" autocomplete="off"></div><label class="filter-input">KANAŁ<select id="filter-channel"><option value="all">Wszystkie</option><option value="linux">Linux</option><option value="opnsense">OPNsense</option><option value="freebsd">FreeBSD</option></select></label><label class="filter-input">FORMAT<select id="filter-format"><option value="all">Wszystkie</option><option value="DEB">DEB</option><option value="RPM">RPM</option><option value="PKG">PKG</option></select></label><label class="filter-input">SORTOWANIE<select id="filter-sort"><option value="date">Najnowsze</option><option value="version">Wersja ↓</option><option value="name">Nazwa A–Z</option><option value="size">Rozmiar ↓</option></select></label></div>
<div class="result-topline"><span id="filter-count">Ładowanie…</span><span>Wersje i daty pochodzą z paczek i metadanych buildu</span></div>
<div class="card catalog-card"><div class="table-scroller"><table class="data-table data-table--catalog"><thead><tr><th>PAKIET</th><th>KANAŁ</th><th>WERSJA</th><th>DATA BUILDU</th><th>ROZMIAR</th><th>POBIERZ</th></tr></thead><tbody id="catalog-rows"><tr><td colspan="6" class="empty-table">Ładowanie danych…</td></tr></tbody></table></div><div class="catalog-bottom"><span id="result-range">—</span><button class="quiet show-more" id="show-more" hidden>Załaduj kolejne 25 ↓</button></div></div>
<div class="page-head compact-head"><div><div class="eyebrow"><span class="eyebrow-line"></span> ARTIFACT EXPLORER <span class="eyebrow-separator">/</span> VERSIONS</div><h1 id="packages-title">Package <em>catalog.</em></h1><p class="lead">Search, filter, sort, and download published files. All details are read directly from the current repository.</p></div><div class="count-pill" id="packages-total">0 packages</div></div>
<div class="filter-panel"><div class="search-field"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m16.5 16.5 4 4"/></svg><input id="search-input" type="search" placeholder="Search by name, version, architecture..." aria-label="Search packages" autocomplete="off"></div><label class="filter-input">CHANNEL<select id="filter-channel"><option value="all">All</option><option value="linux">Linux</option><option value="opnsense">OPNsense</option><option value="freebsd">FreeBSD</option></select></label><label class="filter-input">FORMAT<select id="filter-format"><option value="all">All</option><option value="DEB">DEB</option><option value="RPM">RPM</option><option value="PKG">PKG</option></select></label><label class="filter-input">SORT BY<select id="filter-sort"><option value="date">Newest first</option><option value="version">Version ↓</option><option value="name">Name A–Z</option><option value="size">Size ↓</option></select></label></div>
<div class="result-topline"><span id="filter-count">Loading…</span><span>Versions and dates are read from packages and build metadata</span></div>
<div class="card catalog-card"><div class="table-scroller"><table class="data-table data-table--catalog"><thead><tr><th>PACKAGE</th><th>CHANNEL</th><th>VERSION</th><th>BUILD DATE</th><th>SIZE</th><th>DOWNLOAD</th></tr></thead><tbody id="catalog-rows"><tr><td colspan="6" class="empty-table">Loading data…</td></tr></tbody></table></div><div class="catalog-bottom"><span id="result-range">—</span><button class="quiet show-more" id="show-more" hidden>Load 25 more ↓</button></div></div>
</section>
<section class="view" id="view-install" aria-labelledby="install-title" hidden>
<div class="page-head compact-head"><div><div class="eyebrow"><span class="eyebrow-line"></span> GET STARTED <span class="eyebrow-separator">/</span> INSTALLATION</div><h1 id="install-title">Zainstaluj <em>RustProxy.</em></h1><p class="lead">Gotowe polecenia dodania repozytorium i instalacji. Wybierz platformę, skopiuj komendy do terminala.</p></div></div>
<div class="install-tabs" role="tablist" aria-label="Wybór systemu"><button role="tab" aria-selected="true" data-install="deb" class="install-tab active">Debian / Ubuntu <small>APT · amd64</small></button><button role="tab" aria-selected="false" data-install="rpm" class="install-tab">RHEL / Rocky <small>DNF · x86_64</small></button><button role="tab" aria-selected="false" data-install="opnsense" class="install-tab">OPNsense <small>FreeBSD pkg</small></button><button role="tab" aria-selected="false" data-install="freebsd" class="install-tab">FreeBSD <small>Native pkg</small></button></div>
<div class="install-card card"><div class="install-card-head"><div><span class="section-index">TERMINAL / INSTALL</span><h2 id="install-tab-title">Debian / Ubuntu</h2><p id="install-description">Dodanie podpisanego repo APT i instalacja serwera.</p></div><span class="pill" id="install-signature">GPG SIGNED</span></div><div class="code-window"><div class="code-topbar"><span><i class="tiny-dot"></i><i class="tiny-dot"></i><i class="tiny-dot"></i> <b>install.sh</b></span><button class="copy-code" id="copy-install" data-copy-target="install-command">Kopiuj <span>↗</span></button></div><pre><code id="install-command">Przygotowywanie poleceń…</code></pre></div><div class="install-foot"><span id="install-note">Wymagane uprawnienia sudo.</span><a id="install-reference" class="text-link" href="#">Pliki repozytorium ↗</a></div></div>
<div class="install-extra-grid"><div class="card hint-card"><span class="hint-icon">01</span><h3>Docker discovery agent</h3><p>Linux ma również osobną paczkę <code>rustproxy-docker-agent</code>. Instalujesz ją niezależnie od głównego serwera.</p><div class="inline-command"><code id="agent-command">sudo apt install rustproxy-docker-agent</code><button data-copy-target="agent-command" class="quiet">Kopiuj</button></div></div><div class="card hint-card"><span class="hint-icon">02</span><h3>Podpisy i zaufanie</h3><p>Repozytoria APT i DNF w builderze są podpisane GPG. Repozytoria OPNsense i natywnego FreeBSD używają obecnie katalogów <code>signature_type: none</code>; korzystaj z zaufanego źródła HTTPS.</p></div></div>
<div class="page-head compact-head"><div><div class="eyebrow"><span class="eyebrow-line"></span> GET STARTED <span class="eyebrow-separator">/</span> INSTALLATION</div><h1 id="install-title">Install <em>RustProxy.</em></h1><p class="lead">Ready-to-run commands to add the repository and install RustProxy. Select a platform and copy the commands to your terminal.</p></div></div>
<div class="install-tabs" role="tablist" aria-label="Choose operating system"><button role="tab" aria-selected="true" data-install="deb" class="install-tab active">Debian / Ubuntu <small>APT · amd64</small></button><button role="tab" aria-selected="false" data-install="rpm" class="install-tab">RHEL / Rocky <small>DNF · x86_64</small></button><button role="tab" aria-selected="false" data-install="opnsense" class="install-tab">OPNsense <small>FreeBSD pkg</small></button><button role="tab" aria-selected="false" data-install="freebsd" class="install-tab">FreeBSD <small>Native pkg</small></button></div>
<div class="install-card card"><div class="install-card-head"><div><span class="section-index">TERMINAL / INSTALL</span><h2 id="install-tab-title">Debian / Ubuntu</h2><p id="install-description">Add the signed APT repository and install the server.</p></div><span class="pill" id="install-signature">GPG SIGNED</span></div><div class="code-window"><div class="code-topbar"><span><i class="tiny-dot"></i><i class="tiny-dot"></i><i class="tiny-dot"></i> <b>install.sh</b></span><button class="copy-code" id="copy-install" data-copy-target="install-command">Copy <span>↗</span></button></div><pre><code id="install-command">Preparing commands…</code></pre></div><div class="install-foot"><span id="install-note">Requires sudo privileges.</span><a id="install-reference" class="text-link" href="#">Repository files ↗</a></div></div>
<div class="install-extra-grid single-card" id="install-extra-grid"><div class="card hint-card" id="agent-card" hidden><span class="hint-icon">01</span><h3>Docker discovery agent</h3><p>Linux also provides a separate <code>rustproxy-docker-agent</code> package. Install it separately from the main server.</p><div class="inline-command"><code id="agent-command">sudo apt install rustproxy-docker-agent</code><button data-copy-target="agent-command" class="quiet">Copy</button></div></div><div class="card hint-card"><span class="hint-icon" id="trust-index">01</span><h3>Signatures and trust</h3><p>APT and DNF repositories are GPG-signed. OPNsense and native FreeBSD currently use <code>signature_type: none</code> catalogs; use a trusted HTTPS source.</p></div></div>
</section>
<section class="view" id="view-repository" aria-labelledby="repository-title" hidden>
<div class="page-head compact-head"><div><div class="eyebrow"><span class="eyebrow-line"></span> REPOSITORY ENDPOINTS <span class="eyebrow-separator">/</span> METADATA</div><h1 id="repository-title">Źródła <em>pakietów.</em></h1><p class="lead">Adresy publicznych repozytoriów, podpisów i katalogów paczek. Aktualne metadane pochodzą z opublikowanych buildów.</p></div></div>
<div class="page-head compact-head"><div><div class="eyebrow"><span class="eyebrow-line"></span> REPOSITORY ENDPOINTS <span class="eyebrow-separator">/</span> METADATA</div><h1 id="repository-title">Package <em>sources.</em></h1><p class="lead">Public repository URLs, signatures, and package indexes. Current metadata comes from published builds.</p></div></div>
<div class="endpoints-grid" id="endpoints-grid"></div>
<div class="section-heading section-heading--second"><div><span class="section-index">SOURCE TRACE</span><h2>Informacje o wydaniach</h2></div></div>
<div class="card metadata-card"><div class="meta-table" id="metadata-table"><span>Wczytywanie…</span></div></div>
<div class="section-heading section-heading--second"><div><span class="section-index">SOURCE TRACE</span><h2>Release metadata</h2></div></div>
<div class="card metadata-card"><div class="meta-table" id="metadata-table"><span>Loading…</span></div></div>
</section>
<footer class="page-footer"><span>RUSTPROXY <b>/</b> PACKAGE REGISTRY</span><span>Live catalog · Python / Nginx <b>·</b> <span id="footer-time">—</span></span></footer>
<footer class="page-footer"><span>RUSTPROXY <b>/</b> PACKAGE REGISTRY</span></footer>
</main>
</div>
</div>
+6
View File
@@ -246,6 +246,7 @@ h3{font-size:16px;font-weight:575;letter-spacing:-.3px}
.code-window pre{color:var(--text);font:12px/1.9 var(--mono);overflow:auto;margin:0;padding:21px 24px;min-height:170px;tab-size:2;white-space:pre}
.install-foot{display:flex;justify-content:space-between;gap:15px;margin-top:17px;color:var(--muted);font-size:11px}
.install-extra-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:16px}
.install-extra-grid.single-card{grid-template-columns:1fr}
.hint-card{padding:24px 26px}
.hint-icon{display:grid;place-items:center;width:34px;height:34px;border:1px solid var(--pill-live-line);color:var(--green);font:11px var(--mono);margin-bottom:18px}
.hint-card h3{margin-bottom:10px}
@@ -388,3 +389,8 @@ h3{font-size:16px;font-weight:575;letter-spacing:-.3px}
.page-footer{flex-direction:column;gap:7px}
}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}*,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}}
/* Compact overview: no decorative marketing hero. */
.overview-statusbar{display:flex;gap:15px;align-items:center;justify-content:flex-end;flex-wrap:wrap;margin:0 0 18px}
.overview-statusbar .muted-detail{margin-top:0}
.main-navigation{margin-top:26px}
+1 -1
View File
@@ -25,7 +25,7 @@ services:
FREEBSD_RUST_TOOLCHAIN: "${FREEBSD_RUST_TOOLCHAIN:-1.88.0}"
APPLY_FREEBSD_PATCHES: "${APPLY_FREEBSD_PATCHES:-1}"
RUN_LINUX_TESTS: "${RUN_LINUX_TESTS:-1}"
FREEBSD_NATIVE_ENABLED: "${FREEBSD_NATIVE_ENABLED:-0}"
FREEBSD_NATIVE_ENABLED: "${FREEBSD_NATIVE_ENABLED:-1}"
FREEBSD_NATIVE_RETENTION: "${FREEBSD_NATIVE_RETENTION:-10}"
volumes:
- ./data:/var/lib/package-builder
+9 -5
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -Eeuo pipefail
source /opt/rust-package-builder/scripts/lib.sh
BUILDER_ROOT=${BUILDER_ROOT:-/opt/rust-package-builder}
source "$BUILDER_ROOT/scripts/lib.sh"
: "${SHA:?SHA required}"
: "${SRC:?SRC required}"
@@ -22,7 +23,7 @@ trap cleanup_freebsd_release EXIT
FREEBSD_BINARY="$OUT/rust-proxy-freebsd"
if [[ ! -x "$FREEBSD_BINARY" ]]; then
# Reuse the same FreeBSD cross-build as OPNsense if it succeeded first.
/opt/rust-package-builder/scripts/build-opnsense.sh "$SRC" "$FREEBSD_BINARY" "$BINARY_NAME" "$WORK"
"$BUILDER_ROOT/scripts/build-opnsense.sh" "$SRC" "$FREEBSD_BINARY" "$BINARY_NAME" "$WORK"
fi
FREEBSD_PACKAGE_REVISION=$SOURCE_REVISION
@@ -43,7 +44,7 @@ if (( FREEBSD_PACKAGE_REVISION == 0 )); then
fi
FREEBSD_PACKAGE_VERSION="${VERSION}_${FREEBSD_PACKAGE_REVISION}"
FREEBSD_PACKAGE_FILE="$OUT/rustproxy-${FREEBSD_PACKAGE_VERSION}.pkg"
/opt/rust-package-builder/scripts/package-freebsd.sh \
"$BUILDER_ROOT/scripts/package-freebsd.sh" \
"$SRC" "$FREEBSD_BINARY" "$FREEBSD_PACKAGE_FILE" "$FREEBSD_PACKAGE_VERSION"
mkdir -p "$RELEASE_DIR/apt" "$RELEASE_DIR/rpm/x86_64" "$RELEASE_DIR/opnsense" "$RELEASE_DIR/freebsd"
@@ -70,7 +71,7 @@ fi
FREEBSD_PACKAGE_DIR="$RELEASE_DIR/freebsd/$FREEBSD_ABI"
mkdir -p "$FREEBSD_PACKAGE_DIR"
cp -f "$FREEBSD_PACKAGE_FILE" "$FREEBSD_PACKAGE_DIR/"
/opt/rust-package-builder/scripts/generate-opnsense-repo.py "$RELEASE_DIR/freebsd" --retention "$FREEBSD_NATIVE_RETENTION"
"$BUILDER_ROOT/scripts/generate-opnsense-repo.py" "$RELEASE_DIR/freebsd" --retention "$FREEBSD_NATIVE_RETENTION" --repo-version 2
NATIVE_URL="${OPNSENSE_REPOSITORY_URL%/}/freebsd"
cat > "$RELEASE_DIR/freebsd/RustProxy-FreeBSD.conf" <<CONF
RustProxyNative: {
@@ -97,15 +98,18 @@ freebsd_abi=$FREEBSD_ABI
freebsd_arch=$FREEBSD_ARCH
package=$FREEBSD_ABI/$(basename "$FREEBSD_PACKAGE_FILE")
INFO
/opt/rust-package-builder/scripts/generate-freebsd-index.sh "$RELEASE_DIR/freebsd/index.html"
"$BUILDER_ROOT/scripts/generate-freebsd-index.sh" "$RELEASE_DIR/freebsd/index.html"
[[ -s "$RELEASE_DIR/freebsd/index.html" ]]
[[ -s "$RELEASE_DIR/freebsd/meta.conf" ]]
[[ -s "$RELEASE_DIR/freebsd/packagesite.pkg" ]]
[[ -s "$RELEASE_DIR/freebsd/data.pkg" ]]
grep -Fqx "version: 2;" "$RELEASE_DIR/freebsd/meta.conf" || fatal "unexpected FreeBSD pkg repository format"
[[ -s "$RELEASE_DIR/freebsd/rustproxy-latest.pkg" ]]
bsdtar -tf "$RELEASE_DIR/freebsd/packagesite.pkg" | grep -Fx 'packagesite.yaml' >/dev/null || fatal 'invalid FreeBSD packagesite.pkg'
bsdtar -xOf "$FREEBSD_PACKAGE_DIR/$(basename "$FREEBSD_PACKAGE_FILE")" +COMPACT_MANIFEST >/dev/null || fatal 'invalid FreeBSD package manifest'
# Fail before switching the live repository if the public package is missing or empty.
[[ -s "$FREEBSD_PACKAGE_DIR/$(basename "$FREEBSD_PACKAGE_FILE")" ]] || fatal 'missing FreeBSD native package artifact'
log 'publishing native FreeBSD repository branch'
rm -f "$REPOSITORY/current.new"
+4 -3
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -Eeuo pipefail
source /opt/rust-package-builder/scripts/lib.sh
BUILDER_ROOT=${BUILDER_ROOT:-/opt/rust-package-builder}
source "$BUILDER_ROOT/scripts/lib.sh"
SOURCE_DIR=${1:?source path required}
OUTPUT_BINARY=${2:?output binary required}
@@ -17,7 +18,7 @@ rsync -a --delete \
"$SOURCE_DIR/" "$BUILD_SOURCE/"
if [[ "${APPLY_FREEBSD_PATCHES:-1}" == "1" ]]; then
/opt/rust-package-builder/scripts/apply-freebsd-patches.sh "$BUILD_SOURCE"
"$BUILDER_ROOT/scripts/apply-freebsd-patches.sh" "$BUILD_SOURCE"
else
log "FreeBSD/OPNsense source patches disabled"
fi
@@ -29,7 +30,7 @@ export CARGO_TARGET_X86_64_UNKNOWN_FREEBSD_RUSTFLAGS="-C link-arg=--target=x86_6
log "cross-compiling $BINARY_NAME for x86_64-unknown-freebsd${FREEBSD_TARGET_VERSION}"
cd "$BUILD_SOURCE"
FREEBSD_RUST_TOOLCHAIN=$(/opt/rust-package-builder/scripts/ensure-rust-toolchain.sh "$BUILD_SOURCE" freebsd)
FREEBSD_RUST_TOOLCHAIN=$("$BUILDER_ROOT/scripts/ensure-rust-toolchain.sh" "$BUILD_SOURCE" freebsd)
if [[ -f Cargo.lock ]]; then
cargo "+$FREEBSD_RUST_TOOLCHAIN" build --release --locked --target x86_64-unknown-freebsd
else
+1 -1
View File
@@ -146,7 +146,7 @@ FREEBSD_STATE=""
[[ -r "$DATA_DIR/state/last_linux_commit" ]] && LINUX_STATE=$(cat "$DATA_DIR/state/last_linux_commit")
[[ -r "$DATA_DIR/state/last_opnsense_commit" ]] && OPNSENSE_STATE=$(cat "$DATA_DIR/state/last_opnsense_commit")
[[ -r "$DATA_DIR/state/last_freebsd_commit" ]] && FREEBSD_STATE=$(cat "$DATA_DIR/state/last_freebsd_commit")
if [[ "$LINUX_STATE" == "$SHA" && "$OPNSENSE_STATE" == "$SHA" && ( "${FREEBSD_NATIVE_ENABLED:-0}" != "1" || "$FREEBSD_STATE" == "$SHA" ) ]]; then
if [[ "$LINUX_STATE" == "$SHA" && "$OPNSENSE_STATE" == "$SHA" && ( "${FREEBSD_NATIVE_ENABLED:-1}" != "1" || "$FREEBSD_STATE" == "$SHA" ) ]]; then
printf '%s\n' "$SHA" | atomic_write "$DATA_DIR/state/last_commit"
log "all branches complete for $SHA"
fi
+1 -1
View File
@@ -87,7 +87,7 @@ if opnsense_release_complete_for_commit "$REMOTE_SHA"; then
printf '%s\n' "$REMOTE_SHA" | atomic_write "$DATA_DIR/state/last_opnsense_commit"
fi
if [[ "${FREEBSD_NATIVE_ENABLED:-0}" == "1" ]]; then
if [[ "${FREEBSD_NATIVE_ENABLED:-1}" == "1" ]]; then
BUILD_FREEBSD=1
if freebsd_release_complete_for_commit "$REMOTE_SHA"; then
BUILD_FREEBSD=0
+4 -1
View File
@@ -16,7 +16,7 @@ FREEBSD_ARCH=${FREEBSD_ARCH:-freebsd:15:x86:64}
FREEBSD_RUST_TOOLCHAIN=${FREEBSD_RUST_TOOLCHAIN:-1.88.0}
APPLY_FREEBSD_PATCHES=${APPLY_FREEBSD_PATCHES:-1}
RUN_LINUX_TESTS=${RUN_LINUX_TESTS:-1}
FREEBSD_NATIVE_ENABLED=${FREEBSD_NATIVE_ENABLED:-0}
FREEBSD_NATIVE_ENABLED=${FREEBSD_NATIVE_ENABLED:-1}
FREEBSD_NATIVE_RETENTION=${FREEBSD_NATIVE_RETENTION:-10}
[[ "$POLL_INTERVAL" =~ ^[0-9]+$ ]] || fatal "POLL_INTERVAL must be an integer"
@@ -27,6 +27,9 @@ FREEBSD_NATIVE_RETENTION=${FREEBSD_NATIVE_RETENTION:-10}
[[ "$RUN_LINUX_TESTS" =~ ^[01]$ ]] || fatal "RUN_LINUX_TESTS must be 0 or 1"
[[ "$FREEBSD_NATIVE_ENABLED" =~ ^[01]$ ]] || fatal "FREEBSD_NATIVE_ENABLED must be 0 or 1"
[[ "$FREEBSD_NATIVE_RETENTION" =~ ^[0-9]+$ ]] || fatal "FREEBSD_NATIVE_RETENTION must be a nonnegative integer"
if [[ "$FREEBSD_NATIVE_ENABLED" != "1" ]]; then
log 'WARNING: native FreeBSD build is disabled (FREEBSD_NATIVE_ENABLED=0); no FreeBSD packages will be published'
fi
export DATA_DIR=${DATA_DIR:-/var/lib/package-builder}
export SOURCE_REPO SOURCE_BRANCH REPOSITORY_URL OPNSENSE_REPOSITORY_URL POLL_INTERVAL
+2 -1
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -Eeuo pipefail
source /opt/rust-package-builder/scripts/lib.sh
BUILDER_ROOT=${BUILDER_ROOT:-/opt/rust-package-builder}
source "$BUILDER_ROOT/scripts/lib.sh"
OUT=${1:?output file required}
URL="${OPNSENSE_REPOSITORY_URL%/}/freebsd"
export OUT URL FREEBSD_ABI
+4 -3
View File
@@ -91,7 +91,7 @@ def make_tar(dst: Path, member_name: str, payload: bytes):
os.replace(tmp, dst)
def rebuild(repo: Path, retention: int):
def rebuild(repo: Path, retention: int, repo_version: int = 1):
packages = collect_packages(repo)
if enforce_retention(packages, retention):
packages = collect_packages(repo)
@@ -111,7 +111,7 @@ def rebuild(repo: Path, retention: int):
make_tar(repo / "data.pkg", "data", data)
meta = (
'version: 1;\n'
f'version: {repo_version};\n'
'packing_format: "tar";\n'
'manifests: "packagesite.yaml";\n'
'manifests_archive: "packagesite";\n'
@@ -130,11 +130,12 @@ def main():
parser = argparse.ArgumentParser(description="Build a static FreeBSD pkg repository catalogue")
parser.add_argument("repo", type=Path, help="repository root")
parser.add_argument("--retention", type=int, default=10, help="versions kept per package/ABI; 0 disables pruning")
parser.add_argument("--repo-version", type=int, choices=(1, 2), default=1, help="pkg repository format; 2 for native FreeBSD 15")
args = parser.parse_args()
repo = args.repo.resolve()
repo.mkdir(parents=True, exist_ok=True)
rebuild(repo, args.retention)
rebuild(repo, args.retention, args.repo_version)
if __name__ == "__main__":
+1 -1
View File
@@ -4,7 +4,7 @@ set -Eeuo pipefail
# Increment when package payload/installation semantics change without a source commit.
PACKAGING_REVISION=5
OPNSENSE_PACKAGING_REVISION=4
FREEBSD_NATIVE_PACKAGING_REVISION=1
FREEBSD_NATIVE_PACKAGING_REVISION=2
log() {
printf '%s %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$*"
+4 -1
View File
@@ -104,6 +104,9 @@ fi
[[ -f "$ENV_FILE" ]] || write_env_file
load_env_file
if [[ "${FREEBSD_NATIVE_ENABLED:-1}" == "0" ]]; then
warn "Native FreeBSD builds are DISABLED by existing .env; set FREEBSD_NATIVE_ENABLED=1 and rerun ./start.sh to publish native PKG"
fi
: "${SOURCE_REPO:?Uzupelnij SOURCE_REPO w .env albo ustaw zmienna przed uruchomieniem start.sh}"
: "${REPOSITORY_URL:?Uzupelnij REPOSITORY_URL dla APT/RPM}"
@@ -185,7 +188,7 @@ printf 'APT/RPM URL: %s/\n' "${REPOSITORY_URL%/}"
printf 'APT/RPM local HTTP port: %s\n' "$HTTP_PORT"
printf 'OPNsense pkg URL: %s/\n' "${OPNSENSE_REPOSITORY_URL%/}"
printf 'OPNsense local HTTP port: %s\n' "$OPNSENSE_HTTP_PORT"
[[ "${FREEBSD_NATIVE_ENABLED:-0}" == "1" ]] && printf 'Native FreeBSD pkg URL: %s/freebsd/\n' "${OPNSENSE_REPOSITORY_URL%/}"
[[ "${FREEBSD_NATIVE_ENABLED:-1}" == "1" ]] && printf 'Native FreeBSD pkg URL: %s/freebsd/\n' "${OPNSENSE_REPOSITORY_URL%/}"
[[ -n "$fingerprint" ]] && printf 'GPG fingerprint: %s\n' "$fingerprint"
printf 'Publiczny katalog: %s\n\n' "$DATA_DIR/repository"
printf 'Health Linux: http://127.0.0.1:%s/healthz\n' "$HTTP_PORT"
-21
View File
@@ -1,21 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
SRC=${1:?usage: check-rustproxy-source.sh /path/to/RustProxy}
for f in Cargo.toml grafana/rustproxy-overview.json src/main/bootstrap.rs; do
[[ -f "$SRC/$f" ]] || { echo "missing: $f" >&2; exit 1; }
done
grep -q '^name = "rust-proxy"' "$SRC/Cargo.toml"
grep -q 'RUST_PROXY_CONFIG' "$SRC/src/main/bootstrap.rs"
grep -q 'Reload,' "$SRC/src/main/bootstrap.rs"
# Old RustProxy releases shipped full Debian metadata. The 0.34.x source
# archive no longer does; the builder's own nFPM fallback is authoritative.
if [[ -f "$SRC/debian/rustproxy.service" ]]; then
grep -q 'ExecStart=' "$SRC/debian/rustproxy.service"
else
[[ -f "$SRC/deploy/rustproxy.service" ]] || { echo 'no reference systemd unit found' >&2; exit 1; }
grep -q '^ExecReload=' "$SRC/deploy/rustproxy.service"
fi
echo 'RustProxy source supports --config and soft reload: OK'
-65
View File
@@ -1,65 +0,0 @@
#!/usr/bin/env bash
# Offline verification of the agent-specific nFPM packaging pipeline.
set -Eeuo pipefail
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
SRC=${1:-}
if [[ ! -d "${SRC:-}/docker-agent" ]]; then
SRC="$WORK/source"
mkdir -p "$SRC/docker-agent"
cat > "$SRC/docker-agent/rustproxy-docker-agent.service" <<'UNIT'
[Service]
User=rustproxy-docker-agent
Group=docker
EnvironmentFile=/etc/rustproxy/docker-agent.env
ExecStart=/usr/local/bin/rustproxy-docker-agent
StateDirectory=rustproxy-docker-agent
UNIT
cat > "$SRC/docker-agent/docker-agent.env.example" <<'ENV'
RUSTPROXY_DOCKER_AGENT_TOKEN=replace-with-at-least-24-random-characters
ENV
echo '# Docker Agent' > "$SRC/docker-agent/README.md"
fi
if [[ -f "$SRC/docker-agent/go.mod" && $(command -v go || true) ]]; then
(cd "$SRC/docker-agent" && GOWORK=off CGO_ENABLED=0 go test ./... && \
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o "$WORK/agent" .)
else
cat > "$WORK/agent" <<'MOCK'
#!/bin/sh
exit 0
MOCK
chmod 0755 "$WORK/agent"
fi
export BUILDER_ROOT="$ROOT" VERSION=0.34.2 PACKAGE_RELEASE=4
export MAINTAINER='Package QA <qa@example.invalid>' HOMEPAGE=https://example.invalid
export GPG_PRIVATE_KEY_FILE="$WORK/test-key.asc"
"$ROOT/scripts/prepare-agent-package.sh" "$SRC" "$WORK/package" "$WORK/agent"
PKG="$WORK/package"
test -x "$PKG/payload/usr/bin/rustproxy-docker-agent"
grep -Fqx 'ExecStart=/usr/bin/rustproxy-docker-agent' "$PKG/payload/usr/lib/systemd/system/rustproxy-docker-agent.service"
grep -Fq 'config|noreplace' "$PKG/nfpm-deb.yaml"
grep -Fq 'config|noreplace' "$PKG/nfpm-rpm.yaml"
grep -q 'type: origin' "$PKG/nfpm-deb.yaml"
grep -q 'signature:' "$PKG/nfpm-rpm.yaml"
! grep -q '\.pkg' "$PKG/nfpm-deb.yaml"
for script in "$PKG"/scripts/*; do sh -n "$script"; done
for script in "$PKG"/scripts/*postinstall.sh; do
grep -Fq '/usr/bin/rustproxy-docker-agent init' "$script"
grep -Fq 'systemctl enable rustproxy-docker-agent.service' "$script"
done
if python3 -c 'import yaml' >/dev/null 2>&1; then
python3 - "$PKG" <<'PY_YAML'
import yaml, pathlib, sys
pkg=pathlib.Path(sys.argv[1])
for kind in ('deb','rpm'):
x=yaml.safe_load((pkg/f'nfpm-{kind}.yaml').read_text())
assert x['name']=='rustproxy-docker-agent'
assert x['version']=='0.34.2'
assert x['arch']=='amd64'
assert len(x['contents'])==4
assert x['scripts']['postinstall']
assert any(c['dst']=='/etc/rustproxy/docker-agent.env' and c['type']=='config|noreplace' for c in x['contents'])
PY_YAML
fi
echo 'Docker Agent package manifests, service layout and lifecycle scripts: OK'
-96
View File
@@ -1,96 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
WORK=$(mktemp -d)
SLEEP_PID=""
cleanup() {
[[ -z "$SLEEP_PID" ]] || kill "$SLEEP_PID" 2>/dev/null || true
rm -rf "$WORK"
}
trap cleanup EXIT
for tool in file zstd tar python3; do
command -v "$tool" >/dev/null 2>&1 || { echo "SKIP FreeBSD native packaging: missing $tool"; exit 0; }
done
mkdir -p "$WORK/source" "$WORK/repo/FreeBSD:15:amd64" "$WORK/run"
printf '[package]\nname = "rust-proxy"\nversion = "0.34.1"\n' > "$WORK/source/Cargo.toml"
python3 - "$WORK/bin" <<'PY'
from pathlib import Path
import struct,sys
p=Path(sys.argv[1]); p.write_bytes(b'\x7fELF\x02\x01\x01\x09'+bytes(8)+struct.pack('<HHIQQQIHHHHHH',2,62,1,0,0,0,0,64,56,0,64,0,0));p.chmod(0o755)
PY
export BUILDER_ROOT="$ROOT" FREEBSD_ABI=FreeBSD:15:amd64 FREEBSD_ARCH=freebsd:15:x86:64
"$ROOT/scripts/package-freebsd.sh" "$WORK/source" "$WORK/bin" "$WORK/rustproxy-0.34.1_2.pkg" 0.34.1_2
zstd -dc "$WORK/rustproxy-0.34.1_2.pkg" | tar -xOf - +MANIFEST > "$WORK/manifest.json"
python3 - "$WORK/manifest.json" <<'PY'
import json,sys
m=json.load(open(sys.argv[1])); names=set(m['files'])
assert m['name']=='rustproxy'
assert m['origin']=='www/rustproxy'
assert m['version']=='0.34.1_2'
assert m['abi']=='FreeBSD:15:amd64'
assert '/usr/local/sbin/rust-proxy' in names
assert '/usr/local/etc/rc.d/rustproxy' in names
assert '/usr/local/etc/newsyslog.conf.d/rustproxy.conf' in names
assert len(names)==3
assert not any('/opnsense/' in n or 'syshook' in n for n in names)
assert 'post-install' in m['scripts'] and 'pre-deinstall' in m['scripts']
PY
# Test the actual rc.d reload command without a running FreeBSD machine.
cat > "$WORK/rc.subr" <<'SH'
load_rc_config() { :; }
checkyesno() { eval 'v=${'"$1"'-NO}'; [ "$v" = YES ]; }
run_rc_command() { case "$1" in reload) "$reload_cmd" ;; *) return 1 ;; esac; }
SH
cat > "$WORK/su" <<'SH'
#!/bin/sh
printf '%s\n' "$*" > "$TEST_ARGS"
SH
chmod +x "$WORK/su"
python3 - "$ROOT/packaging/freebsd/rustproxy.rc" "$WORK/rc" "$WORK" <<'PY'
from pathlib import Path
import sys
src=Path(sys.argv[1]).read_text();w=Path(sys.argv[3]);
src=src.replace('. /etc/rc.subr', f'. "{w}/rc.subr"')
src=src.replace('/usr/bin/su -m', f'"{w}/su" -m')
Path(sys.argv[2]).write_text(src)
PY
sleep 120 & SLEEP_PID=$!
printf '%s\n' "$SLEEP_PID" > "$WORK/run/rustproxy.pid"
export TEST_ARGS="$WORK/cli-args" rustproxy_run_dir="$WORK/run"
export rustproxy_config=/usr/local/etc/rustproxy/config.json rustproxy_enable=YES
sh "$WORK/rc" reload
rg_args=$(cat "$TEST_ARGS")
[[ "$rg_args" == *'--data-dir /var/db/rustproxy'* ]]
[[ "$rg_args" == *'--config /usr/local/etc/rustproxy/config.json'* ]]
[[ "$rg_args" == *'--admin-listen 127.0.0.1:9090 reload'* ]]
export rustproxy_config='/usr/local/etc/rustproxy/config.json;touch /tmp/bad'
if sh "$WORK/rc" reload >/dev/null 2>&1; then
echo 'unsafe config path was accepted' >&2; exit 1
fi
unset rustproxy_config
# Catalog generation: mock FreeBSD bsdtar on Linux using zstd + tar.
mkdir -p "$WORK/mockbin"
cat > "$WORK/mockbin/bsdtar" <<'SH'
#!/bin/sh
case "$1" in
-xOf) zstd -dc "$2" | tar -xOf - "$3" ;;
*) exit 2 ;;
esac
SH
chmod +x "$WORK/mockbin/bsdtar"
cp "$WORK/rustproxy-0.34.1_2.pkg" "$WORK/repo/FreeBSD:15:amd64/"
cp "$WORK/rustproxy-0.34.1_2.pkg" "$WORK/repo/rustproxy-latest.pkg"
PATH="$WORK/mockbin:$PATH" python3 "$ROOT/scripts/generate-opnsense-repo.py" "$WORK/repo" --retention 10
python3 - "$WORK/repo/packagesite.pkg" <<'PY'
import json,tarfile,sys
with tarfile.open(sys.argv[1]) as archive:
content=archive.extractfile('packagesite.yaml').read().decode().splitlines()
assert len(content)==1,content
pkg=json.loads(content[0]);assert pkg['name']=='rustproxy'
assert pkg['path']=='FreeBSD:15:amd64/rustproxy-0.34.1_2.pkg',pkg['path']
PY
echo 'FreeBSD native package, rc.d reload and repository catalog: OK'
-67
View File
@@ -1,67 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
mkdir -p "$WORK/source"
printf '#!/bin/sh\nexit 0\n' > "$WORK/rust-proxy"
chmod 0755 "$WORK/rust-proxy"
export BUILDER_ROOT="$ROOT" PACKAGE_NAME=rustproxy BINARY_NAME=rust-proxy
export VERSION=1.0.0 PACKAGE_RELEASE=1 DESCRIPTION=RustProxy LICENSE=UNKNOWN
export MAINTAINER=test@example.invalid HOMEPAGE=https://example.invalid
export GPG_PRIVATE_KEY_FILE=/nonexistent/test.gpg
check_unit() {
local unit=$1
# A read-only home does not conceal /home, unlike ProtectHome=true/tmpfs.
[[ $(grep -c '^ProtectHome=read-only$' "$unit") -eq 1 ]]
! grep -Eq '^ProtectHome=(true|yes|tmpfs|false|no)$' "$unit"
grep -Fxq 'ProtectSystem=strict' "$unit"
grep -Fxq 'User=rustproxy' "$unit"
grep -Fxq 'Group=rustproxy' "$unit"
grep -Fxq 'NoNewPrivileges=true' "$unit"
grep -Fxq 'CapabilityBoundingSet=CAP_NET_BIND_SERVICE' "$unit"
grep -Fq 'ReadWritePaths=-/etc/rustproxy' "$unit"
grep -Fxq 'ExecReload=/usr/bin/rust-proxy --data-dir /var/lib/rustproxy --admin-listen 127.0.0.1:9090 reload' "$unit"
}
# Current RustProxy archive has no debian unit; both nFPM targets use fallback.
"$ROOT/scripts/prepare-package.sh" "$WORK/source" "$WORK/fallback" "$WORK/rust-proxy"
check_unit "$WORK/fallback/payload/usr/lib/systemd/system/rustproxy.service"
for platform in deb rpm; do
grep -Fq '/usr/lib/systemd/system/rustproxy.service' "$WORK/fallback/nfpm-$platform.yaml"
grep -Fq '/usr/share/doc/rustproxy/FILESYSTEM_ACCESS.md' "$WORK/fallback/nfpm-$platform.yaml"
done
cmp "$ROOT/packaging/rustproxy-filesystem-access.md" \
"$WORK/fallback/payload/usr/share/doc/rustproxy/FILESYSTEM_ACCESS.md"
# Some source releases ship their own unit. Normalize its outdated home policy
# rather than letting it override the new package default.
mkdir -p "$WORK/source/debian"
cat > "$WORK/source/debian/rustproxy.service" <<'UNIT'
[Unit]
Description=RustProxy unit supplied with a source release
[Service]
User=rustproxy
Group=rustproxy
ExecStart=/usr/bin/rust-proxy --data-dir /var/lib/rustproxy serve
ProtectSystem=strict
ReadWritePaths=/var/lib/rustproxy /var/log/rustproxy
ProtectHome=true
NoNewPrivileges=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
UNIT
python3 "$ROOT/scripts/normalize-systemd-unit.py" "$WORK/source/debian/rustproxy.service" rust-proxy
check_unit "$WORK/source/debian/rustproxy.service"
# A custom unit without ProtectHome must also get the new safe policy.
sed -i '/^ProtectHome=/d' "$WORK/source/debian/rustproxy.service"
python3 "$ROOT/scripts/normalize-systemd-unit.py" "$WORK/source/debian/rustproxy.service" rust-proxy
check_unit "$WORK/source/debian/rustproxy.service"
echo 'Linux DEB/RPM external path access policy: OK'
-91
View File
@@ -1,91 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
# The bundled Linux rules must match the expected user-provided settings.
diff -u "$ROOT/packaging/rustproxy.logrotate" - <<'CONFIG'
/var/log/rustproxy/*.jsonl /var/log/rustproxy/*.log /var/log/rustproxy/vhosts/*.jsonl {
daily
maxsize 512M
rotate 14
missingok
notifempty
compress
delaycompress
copytruncate
su rustproxy rustproxy
}
CONFIG
# All three log groups must also be tracked by native FreeBSD newsyslog.
OPNSENSE_CONF="$ROOT/opnsense/plugin/src/etc/newsyslog.conf.d/rustproxy.conf"
for pattern in '/var/log/rustproxy/*.jsonl' '/var/log/rustproxy/*.log' '/var/log/rustproxy/vhosts/*.jsonl'; do
grep -Fq "$pattern " "$OPNSENSE_CONF"
done
[[ $(grep -c '^/var/log/rustproxy/' "$OPNSENSE_CONF") == 3 ]]
grep -Fq '14 100M $D0 GBEZPN' "$OPNSENSE_CONF"
grep -Fq '14 100M $D0 GBEZP /var/run/rustproxy/daemon.pid SIGHUP' "$OPNSENSE_CONF"
grep -Fq ' -H -P ${pidfile}' "$ROOT/opnsense/plugin/src/etc/rc.d/rustproxy"
grep -Fq 'COPY packaging/ /opt/rust-package-builder/packaging/' "$ROOT/Dockerfile"
grep -Fq 'packaging_revision=$PACKAGING_REVISION' "$ROOT/scripts/build-linux-release.sh"
grep -Fq 'packaging_revision=$OPNSENSE_PACKAGING_REVISION' "$ROOT/scripts/build-opnsense-release.sh"
# Test staging and native PKG manifest without compiling Rust or signing RPM/DEB.
for tool in file zstd tar sha256sum python3; do
command -v "$tool" >/dev/null 2>&1 || { echo "SKIP packaging integration: missing $tool"; exit 0; }
done
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
mkdir -p "$WORK/source" "$WORK/linux"
if command -v logrotate >/dev/null 2>&1; then
# Test parser compatibility even if the local test machine has no rustproxy user.
sed 's/su rustproxy rustproxy/su root root/' "$ROOT/packaging/rustproxy.logrotate" > "$WORK/logrotate-test.conf"
logrotate -d -s "$WORK/logrotate.state" "$WORK/logrotate-test.conf" > "$WORK/logrotate-output" 2>&1
grep -Fq 'reading config file' "$WORK/logrotate-output"
fi
printf '[package]\nname = "rust-proxy"\nversion = "1.0.0"\n' > "$WORK/source/Cargo.toml"
python3 - "$WORK/bin" <<'PY'
from pathlib import Path
import struct, sys
p = Path(sys.argv[1])
# ELF header with FreeBSD OSABI is enough to check the OPNsense packaging path.
e_ident = b'\x7fELF\x02\x01\x01\x09' + bytes(8)
header = struct.pack('<HHIQQQIHHHHHH', 2, 62, 1, 0, 0, 0, 0, 64, 56, 0, 64, 0, 0)
p.write_bytes(e_ident + header)
p.chmod(0o755)
PY
export BUILDER_ROOT="$ROOT" PACKAGE_NAME=rustproxy BINARY_NAME=rust-proxy
export VERSION=1.0.0 PACKAGE_RELEASE=2 DESCRIPTION=RustProxy LICENSE=UNKNOWN
export MAINTAINER=test@example.invalid HOMEPAGE=https://example.invalid
export GPG_PRIVATE_KEY_FILE=/nonexistent/test.gpg
"$ROOT/scripts/prepare-package.sh" "$WORK/source" "$WORK/linux" "$WORK/bin"
cmp "$ROOT/packaging/rustproxy.logrotate" "$WORK/linux/payload/etc/logrotate.d/rustproxy"
UNIT="$WORK/linux/payload/usr/lib/systemd/system/rustproxy.service"
grep -Fq 'ExecReload=/usr/bin/rust-proxy --data-dir /var/lib/rustproxy --admin-listen 127.0.0.1:9090 reload' "$UNIT"
grep -Fq 'ReadWritePaths=-/etc/rustproxy' "$UNIT"
grep -Fxq 'ProtectHome=read-only' "$UNIT"
grep -Fxq 'ProtectSystem=strict' "$UNIT"
for metadata in nfpm-deb.yaml nfpm-rpm.yaml; do
grep -Fq '/etc/logrotate.d/rustproxy' "$WORK/linux/$metadata"
grep -Fq 'config|noreplace' "$WORK/linux/$metadata"
grep -Fq ' - logrotate' "$WORK/linux/$metadata"
done
"$ROOT/scripts/package-opnsense.sh" "$WORK/source" "$WORK/bin" "$WORK/os-rustproxy.pkg" '1.0.0_20'
zstd -dc "$WORK/os-rustproxy.pkg" | tar -xOf - +MANIFEST > "$WORK/manifest.json"
python3 - "$WORK/manifest.json" <<'PY'
import json, sys
with open(sys.argv[1]) as f:
manifest=json.load(f)
path='/usr/local/etc/newsyslog.conf.d/rustproxy.conf'
assert path in manifest['files'], f'{path} missing from PKG manifest'
assert path in manifest['files'] and len(manifest['files'][path]) == 64
assert '/usr/local/etc/rc.d/rustproxy' in manifest['files']
assert '/usr/local/etc/rc.syshook.d/start/50-rustproxy.start' in manifest['files']
assert '/usr/local/etc/rc.syshook.d/10-rustproxy.start' not in manifest['files']
PY
zstd -dc "$WORK/os-rustproxy.pkg" | tar -xOf - /usr/local/etc/newsyslog.conf.d/rustproxy.conf > "$WORK/packaged-newsyslog.conf"
cmp "$OPNSENSE_CONF" "$WORK/packaged-newsyslog.conf"
echo 'log rotation package integration: OK'
-151
View File
@@ -1,151 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
SRC="$ROOT/opnsense/plugin/src"
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
RC="$SRC/etc/rc.d/rustproxy"
HOOK="$SRC/etc/rc.syshook.d/start/50-rustproxy.start"
SERVICE="$SRC/opnsense/scripts/rustproxy/service.sh"
MODEL="$SRC/opnsense/mvc/app/models/OPNsense/RustProxy/RustProxy.xml"
FORM="$SRC/opnsense/mvc/app/controllers/OPNsense/RustProxy/forms/general.xml"
TEMPLATE="$SRC/opnsense/service/templates/OPNsense/RustProxy/rustproxy.conf"
[[ -f "$HOOK" && -x "$HOOK" ]] || { echo 'Boot syshook must exist and be executable' >&2; exit 1; }
[[ ! -e "$SRC/etc/rc.syshook.d/10-rustproxy.start" ]] || { echo 'Old hook path still exists' >&2; exit 1; }
for script in "$RC" "$HOOK" "$SERVICE"; do
sh -n "$script"
done
php -l "$SRC/opnsense/mvc/app/models/OPNsense/RustProxy/Migrations/M1_5_0.php" >/dev/null
# Ensure GUI, model and Jinja template agree about the new setting.
python3 - "$MODEL" "$FORM" "$TEMPLATE" <<'PY'
import pathlib, sys, xml.etree.ElementTree as ET
model = ET.parse(sys.argv[1]).getroot()
form = ET.parse(sys.argv[2]).getroot()
template = pathlib.Path(sys.argv[3]).read_text()
assert model.find('version').text == '1.5.0'
field = model.find('./items/general/updateCheckIntervalSecs')
assert field is not None and field.attrib['type'] == 'IntegerField'
assert field.findtext('Default') == '3600'
assert field.findtext('MinimumValue') == '60'
assert field.findtext('MaximumValue') == '604800'
assert 'rustproxy.general.updateCheckIntervalSecs' in [e.text for e in form.iter('id')]
assert 'rustproxy_update_check_interval_secs=' in template
assert 'OPNsense.rustproxy.general.updateCheckIntervalSecs' in template
PY
# Isolate the rc.d argument composition without running FreeBSD-only commands.
cat > "$WORK/rc.subr" <<'EOF'
load_rc_config() { :; }
run_rc_command() {
if [ "$1" = reload ]; then "$reload_cmd"; else printf '%s\n' "$command_args"; fi
}
EOF
python3 - "$RC" "$WORK/rc-test" "$WORK" <<'PY'
from pathlib import Path
import sys
src = Path(sys.argv[1]).read_text()
work = Path(sys.argv[3])
src = src.replace('. /etc/rc.subr', '. "'+str(work/'rc.subr')+'"')
src = src.replace('config_file="/usr/local/etc/rustproxy/opnsense.conf"', 'config_file="'+str(work/'opnsense.conf')+'"')
Path(sys.argv[2]).write_text(src)
PY
assert_interval() {
local val=$1
printf 'enabled="1"\nrustproxy_update_check_interval_secs="%s"\n' "$val" > "$WORK/opnsense.conf"
local args
args=$(sh "$WORK/rc-test" start)
[[ "$args" == *"--update-check-interval-secs $val serve"* ]] || {
echo "Wrong update interval arguments: $args" >&2; exit 1;
}
}
assert_interval 3600
assert_interval 7200
assert_interval 60
assert_interval 604800
printf 'enabled="1"\n' > "$WORK/opnsense.conf"
args=$(sh "$WORK/rc-test" start)
[[ "$args" == *'--update-check-interval-secs 3600 serve'* ]] || exit 1
# Exercise the rc.d soft reload itself with a live mock child and CLI.
mkdir -p "$WORK/bin"
cat > "$WORK/bin/mock-cli" <<'EOF'
#!/bin/sh
printf '%s\n' "$*" > "$TEST_CLI_ARGS"
EOF
chmod +x "$WORK/bin/mock-cli"
printf 'enabled="1"\nrustproxy_config_path="/usr/local/etc/rustproxy/config.json"\n' > "$WORK/opnsense.conf"
echo "$$" > "$WORK/rustproxy.pid"
export TEST_CLI_ARGS="$WORK/cli-args"
rustproxy_run_dir="$WORK" rustproxy_command="$WORK/bin/mock-cli" sh "$WORK/rc-test" reload
args=$(cat "$TEST_CLI_ARGS")
[[ "$args" == *'--data-dir /var/db/rustproxy'* ]]
[[ "$args" == *'--config /usr/local/etc/rustproxy/config.json'* ]]
[[ "$args" == *'--admin-listen 127.0.0.1:9090 reload'* ]]
printf 'enabled="1"\nrustproxy_config_path="/usr/local/etc/rustproxy/config.json;touch /tmp/bad"\n' > "$WORK/opnsense.conf"
if rustproxy_run_dir="$WORK" rustproxy_command="$WORK/bin/mock-cli" sh "$WORK/rc-test" reload >/dev/null 2>&1; then
echo 'Unsafe OPNsense config path was accepted' >&2; exit 1
fi
for invalid in 0 59 604801 nope '1;touch /tmp/pwn'; do
printf 'enabled="1"\nrustproxy_update_check_interval_secs="%s"\n' "$invalid" > "$WORK/opnsense.conf"
if sh "$WORK/rc-test" start >/dev/null 2>&1; then
echo "Interval '$invalid' should have failed" >&2; exit 1
fi
done
# Mock configd + rc.d so we can exercise the actual hook and service wrapper.
mkdir -p "$WORK/bin"
cat > "$WORK/bin/configctl" <<'EOF'
#!/bin/sh
printf 'template %s\n' "$*" >> "$TEST_LOG"
if [ "${FAIL_RENDER:-0}" = 1 ]; then
exit 2
fi
printf 'enabled="%s"\n' "${ENABLED:-1}" > "$TEST_CONFIG"
EOF
cat > "$WORK/bin/rustproxy-rc" <<'EOF'
#!/bin/sh
case "$1" in
status) [ -f "$TEST_PID" ] ;;
onestart) printf 'start\n' >> "$TEST_LOG"; touch "$TEST_PID" ;;
onestop) printf 'stop\n' >> "$TEST_LOG"; rm -f "$TEST_PID" ;;
reload) printf 'reload\n' >> "$TEST_LOG" ;;
*) exit 1 ;;
esac
EOF
chmod +x "$WORK/bin/configctl" "$WORK/bin/rustproxy-rc"
python3 - "$HOOK" "$SERVICE" "$WORK" <<'PY'
from pathlib import Path
import sys
hook = Path(sys.argv[1]).read_text()
service = Path(sys.argv[2]).read_text()
w = Path(sys.argv[3])
hook = hook.replace('/usr/local/sbin/configctl', str(w/'bin/configctl'))
hook = hook.replace('/usr/local/opnsense/scripts/rustproxy/service.sh', str(w/'service-test'))
service = service.replace('/usr/local/etc/rustproxy/opnsense.conf', str(w/'opnsense.conf'))
service = service.replace('/usr/local/etc/rc.d/rustproxy', str(w/'bin/rustproxy-rc'))
(w/'hook-test').write_text(hook)
(w/'service-test').write_text(service)
PY
chmod +x "$WORK/hook-test" "$WORK/service-test"
export TEST_LOG="$WORK/actions.log" TEST_CONFIG="$WORK/opnsense.conf" TEST_PID="$WORK/pidfile"
: > "$TEST_LOG"
ENABLED=1 "$WORK/hook-test"
[[ $(grep -cx start "$TEST_LOG") == 1 ]] || { echo 'Boot did not start service' >&2; exit 1; }
ENABLED=1 "$WORK/hook-test"
[[ $(grep -cx start "$TEST_LOG") == 1 ]] || { echo 'Duplicate supervisor start' >&2; exit 1; }
ENABLED=1 "$WORK/service-test" reload
[[ $(grep -cx reload "$TEST_LOG") == 1 ]] || { echo 'Soft reload not passed through' >&2; exit 1; }
[[ $(grep -cx stop "$TEST_LOG" || true) == 0 ]] || { echo 'Soft reload restarted service' >&2; exit 1; }
rm -f "$TEST_PID"
ENABLED=0 "$WORK/hook-test"
[[ $(grep -cx start "$TEST_LOG") == 1 ]] || { echo 'Disabled service started' >&2; exit 1; }
if FAIL_RENDER=1 ENABLED=1 "$WORK/hook-test" >/dev/null 2>&1; then
echo 'Broken configuration reload was not reported' >&2; exit 1
fi
[[ $(grep -cx start "$TEST_LOG") == 1 ]] || { echo 'Started despite failed template rendering' >&2; exit 1; }
echo 'OPNsense boot / update interval / soft reload checks: OK'
-79
View File
@@ -1,79 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
for f in "$ROOT"/start.sh "$ROOT"/scripts/*.sh "$ROOT"/tests/*.sh; do
bash -n "$f"
done
python3 -m py_compile "$ROOT/scripts/generate-opnsense-repo.py"
# Main image/build dependencies.
grep -q 'FROM rockylinux:9' "$ROOT/Dockerfile"
grep -q 'FREEBSD_RELEASE=15.1-RELEASE' "$ROOT/Dockerfile"
grep -q 'x86_64-unknown-freebsd' "$ROOT/Dockerfile"
grep -q '/opt/freebsd-sysroot' "$ROOT/Dockerfile"
# Coordinator uses one checkout but independent branches.
grep -q 'build-linux-release.sh' "$ROOT/scripts/build-release.sh"
grep -q 'build-opnsense-release.sh' "$ROOT/scripts/build-release.sh"
grep -q 'build-freebsd-release.sh' "$ROOT/scripts/build-release.sh"
grep -q 'package-freebsd.sh' "$ROOT/scripts/build-freebsd-release.sh"
grep -q 'FREEBSD_NATIVE_PACKAGING_REVISION=1' "$ROOT/scripts/lib.sh"
grep -Fq 'FREEBSD_NATIVE_ENABLED: "${FREEBSD_NATIVE_ENABLED:-0}"' "$ROOT/docker-compose.yml"
grep -Fq 'location /freebsd/' "$ROOT/http/nginx.conf"
grep -Fq 'FREEBSD_NATIVE_ENABLED=0' "$ROOT/.env.example"
grep -Fq 'ExecReload=/usr/bin/' "$ROOT/scripts/normalize-systemd-unit.py"
grep -q 'OPNsense branch will still run' "$ROOT/scripts/build-release.sh"
# Linux tests gate Linux packaging only.
grep -Fq 'cargo "+$LINUX_RUST_TOOLCHAIN" test --release' "$ROOT/scripts/build-linux-release.sh"
grep -Fq 'CGO_ENABLED=0 go test ./...' "$ROOT/scripts/build-linux-release.sh"
grep -Fq 'prepare-agent-package.sh' "$ROOT/scripts/build-linux-release.sh"
grep -Fq 'AGENT_DEB_FILE=' "$ROOT/scripts/build-linux-release.sh"
grep -Fq 'AGENT_RPM_FILE=' "$ROOT/scripts/build-linux-release.sh"
grep -Fq 'rustproxy-docker-agent' "$ROOT/scripts/generate-index.sh"
grep -Fq 'COPY --from=go-toolchain' "$ROOT/Dockerfile"
grep -q 'PACKAGING_REVISION=5' "$ROOT/scripts/lib.sh"
grep -Fq 'ensure-rust-toolchain.sh' "$ROOT/scripts/build-linux-release.sh"
grep -Fq 'ensure-rust-toolchain.sh' "$ROOT/scripts/build-opnsense.sh"
grep -Fq 'nfpm package' "$ROOT/scripts/build-linux-release.sh"
grep -q 'aptly .*publish' "$ROOT/scripts/build-linux-release.sh"
grep -q 'createrepo_c' "$ROOT/scripts/build-linux-release.sh"
[[ -x "$ROOT/tests/linux-path-access.sh" ]]
! grep -q 'cargo test' "$ROOT/scripts/build-opnsense-release.sh"
! grep -q 'cargo test' "$ROOT/scripts/build-opnsense.sh"
# FreeBSD/OPNsense branch.
grep -q 'build-opnsense.sh' "$ROOT/scripts/build-opnsense-release.sh"
grep -q 'package-opnsense.sh' "$ROOT/scripts/build-opnsense-release.sh"
grep -q 'generate-opnsense-repo.py' "$ROOT/scripts/build-opnsense-release.sh"
grep -q 'last_opnsense_commit' "$ROOT/scripts/build-opnsense-release.sh"
test -f "$ROOT/opnsense/plugin/src/etc/rc.d/rustproxy"
test -x "$ROOT/opnsense/plugin/src/etc/rc.syshook.d/start/50-rustproxy.start"
[[ ! -e "$ROOT/opnsense/plugin/src/etc/rc.syshook.d/10-rustproxy.start" ]]
grep -q 'OPNSENSE_PACKAGING_REVISION=4' "$ROOT/scripts/lib.sh"
test -f "$ROOT/opnsense/patches/0.20.0/0001-opnsense-build.patch"
# Per-branch state/retry logic.
grep -q 'last_linux_commit' "$ROOT/scripts/check-and-build.sh"
grep -q 'last_opnsense_commit' "$ROOT/scripts/check-and-build.sh"
grep -q 'BUILD_LINUX' "$ROOT/scripts/check-and-build.sh"
grep -q 'BUILD_OPNSENSE' "$ROOT/scripts/check-and-build.sh"
grep -q 'RUN_LINUX_TESTS=${RUN_LINUX_TESTS:-1}' "$ROOT/scripts/entrypoint.sh"
grep -Fq 'RUN_LINUX_TESTS: "${RUN_LINUX_TESTS:-1}"' "$ROOT/docker-compose.yml"
# Two public endpoints from one nginx container.
grep -Fq '${REPOSITORY_URL:?REPOSITORY_URL is required}' "$ROOT/docker-compose.yml"
grep -Fq '${OPNSENSE_REPOSITORY_URL:?OPNSENSE_REPOSITORY_URL is required}' "$ROOT/docker-compose.yml"
grep -Fq '0.0.0.0:${HTTP_PORT:-18765}:8080' "$ROOT/docker-compose.yml"
grep -Fq '0.0.0.0:${OPNSENSE_HTTP_PORT:-18766}:8081' "$ROOT/docker-compose.yml"
grep -q 'listen 8080' "$ROOT/http/nginx.conf"
grep -q 'listen 8081' "$ROOT/http/nginx.conf"
grep -q 'root /srv/repository/opnsense' "$ROOT/http/nginx.conf"
grep -q 'DEFAULT_HTTP_PORT=18765' "$ROOT/start.sh"
grep -q 'DEFAULT_OPNSENSE_HTTP_PORT=18766' "$ROOT/start.sh"
grep -q 'OPNSENSE_REPOSITORY_URL=https://opn-rustproxy.linuxiarz.pl' "$ROOT/.env.example"
grep -q 'RUN_LINUX_TESTS=1' "$ROOT/.env.example"
echo 'static checks: OK'
-157
View File
@@ -1,157 +0,0 @@
#!/usr/bin/env python3
"""Run: python3 -m unittest discover -s tests -p 'test_dashboard.py' -v"""
from __future__ import annotations
import importlib.util
import json
import os
import tempfile
import threading
import unittest
from datetime import datetime, timezone
from http.server import ThreadingHTTPServer
from pathlib import Path
from urllib.error import HTTPError
from urllib.request import Request, urlopen
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location("dashboard_app", ROOT / "dashboard" / "app.py")
app = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(app)
class RepoFixture:
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.repo = Path(self.temp.name) / "repository"
self.r1 = self.repo / "releases" / "r1"
self.r1.mkdir(parents=True)
(self.repo / "current").symlink_to("releases/r1")
def make_file(self, name, *, contents="package-bytes", timestamp=1735853760):
path = self.r1 / name
path.parent.mkdir(parents=True,exist_ok=True)
path.write_text(contents,encoding="utf-8")
os.utime(path,(timestamp,timestamp))
return path
def make_catalog(self):
self.make_file("apt/pool/r/rustproxy/rustproxy_0.34.6-2_amd64.deb")
self.make_file("apt/pool/r/rustproxy/rustproxy-docker-agent_0.34.6-2_amd64.deb")
self.make_file("rpm/x86_64/rustproxy-0.34.6-2.x86_64.rpm")
self.make_file("rpm/x86_64/rustproxy-docker-agent-0.34.6-2.x86_64.rpm")
self.make_file("opnsense/26.7/amd64/os-rustproxy-0.34.6_123.pkg")
self.make_file("opnsense/packagesite.pkg",contents="index")
self.make_file("opnsense/os-rustproxy-latest.pkg",contents="duplicate")
self.make_file("freebsd/FreeBSD:15:amd64/rustproxy-0.34.6_123.pkg")
self.make_file("freebsd/rustproxy-latest.pkg",contents="duplicate")
self.make_file("LINUX-BUILD-INFO.txt",contents="app_version=0.34.6\npackage_release=2\nsource_commit=abc123\nbuilt_at=2026-10-22T08:41:00Z\n")
self.make_file("opnsense/BUILD-INFO.txt",contents="app_version=0.34.6\npackage_version=0.34.6_123\nsource_commit=abc123\nbuilt_at=2026-10-22T08:43:00Z\n")
self.make_file("freebsd/BUILD-INFO.txt",contents="app_version=0.34.6\npackage_version=0.34.6_123\nsource_commit=abc123\nbuilt_at=2026-10-22T08:45:00Z\n")
class CatalogTests(RepoFixture, unittest.TestCase):
def test_scans_real_file_layout_without_index_duplicates(self):
self.make_catalog()
catalog = app.generate_catalog(self.repo)
self.assertEqual(6, catalog["summary"]["packages_count"])
self.assertEqual(3, catalog["summary"]["channels_published"])
self.assertEqual(4, catalog["summary"]["channel_counts"]["linux"])
self.assertEqual(1, catalog["summary"]["channel_counts"]["opnsense"])
self.assertEqual(1, catalog["summary"]["channel_counts"]["freebsd"])
self.assertEqual("0.34.6-2",catalog["builds"]["linux"]["package_version"])
self.assertEqual("2026-10-22T08:45:00Z",catalog["summary"]["latest_build_at"])
linux = [p for p in catalog["packages"] if p["format"] == "RPM"]
self.assertEqual({"rustproxy","rustproxy-docker-agent"}, {p["name"] for p in linux})
self.assertEqual("2026-10-22T08:41:00Z", linux[0]["built_at"])
self.assertEqual("build",linux[0]["date_source"])
opnsense = next(p for p in catalog["packages"] if p["channel"] == "opnsense")
self.assertTrue(opnsense["download_url"].endswith("/26.7/amd64/os-rustproxy-0.34.6_123.pkg"))
freebsd = next(p for p in catalog["packages"] if p["channel"] == "freebsd")
self.assertIn("/freebsd/FreeBSD%3A15%3Aamd64/",freebsd["download_url"])
self.assertEqual("2026-10-22T08:45:00Z",freebsd["built_at"])
def test_mtime_backwards_compatibility(self):
self.make_file("LINUX-BUILD-INFO.txt",contents="app_version=0.21.0\npackage_release=5\n")
self.make_file("rpm/x86_64/rustproxy-0.21.0-5.x86_64.rpm")
catalog = app.generate_catalog(self.repo)
self.assertEqual("file", catalog["builds"]["linux"]["date_source"])
self.assertEqual("file", catalog["packages"][0]["date_source"])
self.assertEqual("0.21.0-5",catalog["packages"][0]["version"])
self.assertTrue(catalog["packages"][0]["built_at"].endswith("Z"))
def test_symlink_rotation_gives_current_packages(self):
self.make_file("rpm/x86_64/rustproxy-0.1.0-1.x86_64.rpm")
v2 = self.repo / "releases" / "r2"
v2.mkdir()
(v2 / "rpm/x86_64").mkdir(parents=True)
(v2 / "rpm/x86_64/rustproxy-0.2.0-1.x86_64.rpm").write_bytes(b"new package")
before = app.generate_catalog(self.repo)
self.assertEqual("0.1.0-1",before["packages"][0]["version"])
(self.repo / "current.new").symlink_to("releases/r2")
os.replace(self.repo / "current.new",self.repo / "current")
after = app.generate_catalog(self.repo)
self.assertEqual("0.2.0-1",after["packages"][0]["version"])
def test_empty_repo_and_untrusted_symlink(self):
first = app.generate_catalog(self.repo)
self.assertFalse(first["ready"])
self.assertEqual([],first["packages"])
(self.repo / "current").unlink()
(self.repo / "current").symlink_to(self.temp.name) # outside repo root
second = app.generate_catalog(self.repo)
self.assertFalse(second["ready"])
self.assertEqual([],second["packages"])
def test_regexes_and_metadata_parser(self):
self.assertEqual(("rustproxy-docker-agent","0.34.6-2","x86_64"),app.parse_filename("rustproxy-docker-agent-0.34.6-2.x86_64.rpm","rpm"))
self.assertEqual(("os-rustproxy","0.34.6_123","amd64"),app.parse_filename("os-rustproxy-0.34.6_123.pkg","pkg"))
self.assertIsNone(app.parse_filename("packagesite.pkg","pkg"))
self.assertIsNone(app.parse_filename("rustproxy-latest.pkg","pkg"))
self.assertIsNone(app.parse_filename("repomd.xml","rpm"))
self.assertEqual({},app.parse_metadata(self.repo/"missing-info.txt"))
class HttpTests(RepoFixture, unittest.TestCase):
def setUp(self):
super().setUp()
self.make_catalog()
original_cache = app.CACHE
app.CACHE = app.CatalogCache(self.repo,ttl=1)
self.addCleanup(lambda: setattr(app,"CACHE",original_cache))
self.server = ThreadingHTTPServer(("127.0.0.1",0),app.DashboardHandler)
self.thread = threading.Thread(target=self.server.serve_forever,daemon=True)
self.thread.start()
self.addCleanup(self.server.server_close)
self.addCleanup(self.server.shutdown)
self.base = f"http://127.0.0.1:{self.server.server_port}"
def test_http_catalog_page_assets_head(self):
with urlopen(self.base + "/api/v1/packages") as response:
self.assertIn("no-store",response.headers["Cache-Control"])
catalog = json.load(response)
self.assertEqual(6,len(catalog["packages"]))
request = Request(self.base + "/",headers={"X-Repo-Site":"opnsense"})
with urlopen(request) as response:
html = response.read().decode()
self.assertIn('data-site="opnsense"',html)
self.assertIn("Package Registry",html)
self.assertIn("Content-Security-Policy",response.headers)
with urlopen(self.base + "/freebsd/") as response:
self.assertIn('data-site="freebsd"',response.read().decode())
with urlopen(self.base + "/ui/style.css") as response:
self.assertIn("text/css",response.headers["Content-Type"])
self.assertIn("--accent:#f69b74",response.read().decode())
request = Request(self.base + "/ui/app.js", method="HEAD")
with urlopen(request) as response:
self.assertEqual(200,response.status)
self.assertEqual(b"",response.read())
with self.assertRaises(HTTPError) as raised:
urlopen(self.base + "/../something")
self.assertEqual(404,raised.exception.code)
if __name__ == "__main__":
unittest.main()
-136
View File
@@ -1,136 +0,0 @@
#!/usr/bin/env bash
# Offline simulation of rustup installation/selection; no Rust or network required.
set -Eeuo pipefail
ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
mkdir -p "$WORK/bin" "$WORK/source" "$WORK/state"
cat > "$WORK/bin/rustup" <<'MOCK'
#!/usr/bin/env bash
set -Eeuo pipefail
state=${MOCK_RUSTUP_STATE:?}
case "$1" in
run)
channel=$2
[[ -r "$state/$channel.version" ]] || exit 1
printf 'rustc %s (mock)\n' "$(cat "$state/$channel.version")"
;;
toolchain)
[[ "$2" == install ]] || exit 7
channel=$3
[[ ${MOCK_INSTALL_FAIL:-0} != 1 ]] || exit 9
case "$channel" in
stable) version=${MOCK_NEW_STABLE:-1.100.0} ;;
*) case "$channel" in *.*.*) version=$channel ;; *) version=${channel}.0 ;; esac ;;
esac
echo "$version" > "$state/$channel.version"
printf 'install %s\n' "$channel" >> "$state/calls"
;;
target)
action=$2
if [[ "$action" == list ]]; then
[[ "$3" == --toolchain && "$5" == --installed ]] || exit 7
[[ -f "$state/$4.target" ]] && cat "$state/$4.target"
elif [[ "$action" == add ]]; then
[[ "$4" == --toolchain ]] || exit 7
echo "$3" > "$state/$5.target"
printf 'target %s %s\n' "$5" "$3" >> "$state/calls"
else exit 7
fi
;;
*) printf 'Unexpected rustup arguments: %s\n' "$*" >&2; exit 7 ;;
esac
MOCK
chmod +x "$WORK/bin/rustup"
export PATH="$WORK/bin:$PATH" MOCK_RUSTUP_STATE="$WORK/state" TOML_PYTHON=python3
TOOL="$ROOT/scripts/ensure-rust-toolchain.sh"
reset_state() { rm -f "$WORK/state/"*; }
manifest() { printf '[package]\nname = "rust-proxy"\nversion = "0.31.2"\nrust-version = "%s"\n' "$1" > "$WORK/source/Cargo.toml"; rm -f "$WORK/source/rust-toolchain" "$WORK/source/rust-toolchain.toml"; }
# No update or download when installed stable already satisfies the manifest.
reset_state
manifest 1.99
printf '1.100.0\n' > "$WORK/state/stable.version"
result=$($TOOL "$WORK/source" linux)
[[ "$result" == stable ]]
[[ ! -e "$WORK/state/calls" ]]
# Upgrade stable only when too old, then reuse it on subsequent checks.
reset_state
printf '1.98.1\n' > "$WORK/state/stable.version"
result=$($TOOL "$WORK/source" linux)
[[ "$result" == stable ]]
grep -Fqx 'install stable' "$WORK/state/calls"
[[ $($TOOL "$WORK/source" linux) == stable ]]
[[ $(wc -l < "$WORK/state/calls") == 1 ]]
# FreeBSD default 1.88 is auto raised to the project's 1.99 MSRV with its own target.
reset_state
result=$(FREEBSD_RUST_TOOLCHAIN=1.88.0 "$TOOL" "$WORK/source" freebsd)
[[ "$result" == 1.99 ]]
grep -Fqx 'install 1.99' "$WORK/state/calls"
grep -Fqx 'target 1.99 x86_64-unknown-freebsd' "$WORK/state/calls"
[[ $(FREEBSD_RUST_TOOLCHAIN=1.88.0 "$TOOL" "$WORK/source" freebsd) == 1.99 ]]
[[ $(wc -l < "$WORK/state/calls") == 2 ]]
# A deliberate rust-toolchain.toml pin is respected; do not silently override it.
reset_state
cat > "$WORK/source/rust-toolchain.toml" <<'PIN'
[toolchain]
channel = "1.98.0"
PIN
if "$TOOL" "$WORK/source" linux >/dev/null 2>&1; then
printf 'An incompatible explicit pin should have failed\n' >&2
exit 1
fi
[[ ! -e "$WORK/state/calls" ]] || { echo 'Should not install an incompatible pin' >&2; exit 1; }
rm "$WORK/source/rust-toolchain.toml"
# Pin that meets the project requirement is installed without changing stable.
reset_state
cat > "$WORK/source/rust-toolchain.toml" <<'PIN'
[toolchain]
channel = "1.99.0"
PIN
[[ $($TOOL "$WORK/source" linux) == 1.99.0 ]]
grep -Fqx 'install 1.99.0' "$WORK/state/calls"
rm "$WORK/source/rust-toolchain.toml"
# RustProxy-like rust-toolchain.toml (stable) also upgrades stale stable for FreeBSD.
reset_state
manifest 1.99
cat > "$WORK/source/rust-toolchain.toml" <<'PIN'
[toolchain]
channel = "stable"
profile = "minimal"
components = ["rustfmt", "clippy"]
PIN
printf '1.98.1\n' > "$WORK/state/stable.version"
[[ $(FREEBSD_RUST_TOOLCHAIN=1.88.0 "$TOOL" "$WORK/source" freebsd) == stable ]]
grep -Fqx 'install stable' "$WORK/state/calls"
grep -Fqx 'target stable x86_64-unknown-freebsd' "$WORK/state/calls"
rm "$WORK/source/rust-toolchain.toml"
# Workspace-inherited rust-version is resolved through TOML, not grep.
reset_state
cat > "$WORK/source/Cargo.toml" <<'MANIFEST'
[workspace.package]
rust-version = "1.99"
[package]
name = "rust-proxy"
version = "0.31.2"
rust-version = { workspace = true }
MANIFEST
printf '1.100.0\n' > "$WORK/state/stable.version"
[[ $($TOOL "$WORK/source" linux) == stable ]]
# An unavailable upgrade must be a hard failure.
reset_state
manifest 1.99
printf '1.98.1\n' > "$WORK/state/stable.version"
if MOCK_INSTALL_FAIL=1 "$TOOL" "$WORK/source" linux >/dev/null 2>&1; then
printf 'A failed rustup update should abort the build\n' >&2
exit 1
fi
echo 'Rust toolchain selection: 8 offline scenarios OK'