Switches the operator path from bash-only to a Python console, keeps the bash
menu as a fallback, and makes the offline package set travel with the repository
so the person installing a device never downloads anything.
bfos/ (stdlib only, no textual/rich: those would need a download, which defeats
the offline goal):
- simple mode offers three choices (Kur / Durum / Çıkış); the detailed mode with
all 21 fleet operations and the local installer is reachable from the menu or
with --full, so a first-time operator is not buried in options
- gate parity with the bash console: scope read from the playbook file and
refused on mismatch, production needs a typed token plus a separate approval,
release-gated runs need their wave name typed, destructive runs need a second
approval, "all" walks wave by wave and stops at the first failure
- Turkish throughout; audit log at /var/log/blueforce-console.log (600, falls
back to the user state dir) with a single redaction choke point
- bfos/ui_gui.py adds an optional tkinter window over the same operations layer;
headless and missing-tkinter cases explain themselves in Turkish
admin/bf-bootstrap.sh: links the Python console as `bf` (primary) and keeps
`bf-menu` (fallback), reports both, and recommends `sudo bf`.
Offline packages are committable now (this was the blocking contradiction):
- .gitignore ignored `Packages`, `Packages.gz` and `packages.lock.tsv` everywhere,
so a built bundle could never be committed and the installer would always have
been told to download. provisioning/offline-repo/built/ is now allowed through
while scratch output stays untracked
- provisioning/offline-repo/{add-packages.sh,build-index.sh,repo-manifest.yaml}
plus a Turkish step-by-step README for the repository owner
- the over-broad `*token*` ignore rule is narrowed to `*[-_.]token*` so ordinary
filenames are not silently excluded while token files still are
Tests: run-all.sh now runs the Python suites too (previously it collected only
*.sh, so 212 Python assertions were silently skipped) and executes from the repo
root. 11/11 pass, exit 0.
Two test expectations were corrected rather than the code: a read-only
wave-scoped report legitimately asks for no gate token (the bash console never
did either), so that assertion now checks the command carries operation_wave and
operation_confirmed, and a new test asserts release-gated runs still require
their token. The bootstrap completion contract moved from `sudo bf-menu` to
`sudo bf`.
64 lines
2.3 KiB
Python
Executable file
64 lines
2.3 KiB
Python
Executable file
#!/bin/sh
|
||
''''command -v python3 >/dev/null 2>&1 || { printf '%s\n' "HATA: python3 bulunamadı. bfos operatör konsolu için Python 3.8 veya üzeri gerekir." "" "Yedek yol (bash): sudo admin/bf-menu ya da kuruluysa: sudo bf-menu" >&2; exit 3; }; exec python3 "$0" "$@" # '''
|
||
"""bfos operator console — thin entry point (sh/Python polyglot).
|
||
|
||
The file is valid POSIX shell *and* valid Python:
|
||
|
||
* run as ``admin/bf`` (or as the ``bf`` symlink that
|
||
``admin/bf-bootstrap.sh`` creates), the shell line above checks that
|
||
``python3`` exists and, when it does not, prints a Turkish error and points at
|
||
the proven bash console (``admin/bf-menu``) — a device without Python is still
|
||
operational,
|
||
* everything after that line is the Python entry point: it puts the repository
|
||
root on ``sys.path`` and calls :func:`bfos.cli.main`. Nothing else lives here;
|
||
all logic is in the ``bfos`` package next to it.
|
||
|
||
Usage: sudo bfos [--full] [--dry-run] [--check] [--repo YOL] [--version] [--lang tr]
|
||
"""
|
||
|
||
import os
|
||
import sys
|
||
|
||
MIN_PYTHON = (3, 8)
|
||
|
||
if sys.version_info < MIN_PYTHON: # pragma: no cover - depends on the interpreter
|
||
sys.stderr.write(
|
||
"HATA: Python sürümü çok eski: {0}. bfos için Python {1}.{2}+ gerekir.\n".format(
|
||
sys.version.split()[0], MIN_PYTHON[0], MIN_PYTHON[1]
|
||
)
|
||
)
|
||
sys.stderr.write("Yedek yol (bash): sudo admin/bf-menu\n")
|
||
raise SystemExit(3)
|
||
|
||
|
||
def _repository_root():
|
||
"""First candidate directory that actually contains the bfos package."""
|
||
here = os.path.dirname(os.path.realpath(__file__))
|
||
candidates = [
|
||
os.environ.get("BF_REPO", ""),
|
||
os.path.dirname(here), # <repo>/admin/bf -> <repo>
|
||
here,
|
||
os.getcwd(),
|
||
]
|
||
for candidate in candidates:
|
||
if candidate and os.path.isfile(os.path.join(candidate, "bfos", "__init__.py")):
|
||
return candidate
|
||
return ""
|
||
|
||
|
||
ROOT = _repository_root()
|
||
if not ROOT:
|
||
sys.stderr.write(
|
||
"HATA: bfos paketi bulunamadı. Konsolu Blueforce deposunun içinden çalıştırın,\n"
|
||
"ya da --repo YOL ile depo yolunu belirtin.\n"
|
||
"Yedek yol (bash): sudo admin/bf-menu\n"
|
||
)
|
||
raise SystemExit(3)
|
||
|
||
if ROOT not in sys.path:
|
||
sys.path.insert(0, ROOT)
|
||
|
||
from bfos.cli import main # noqa: E402 (sys.path is prepared above)
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|