The bundle pool is ~884 MB and now travels through Git LFS, so a plain clone materialises all of it. Measured on this repository: 2.7 GB working tree, against 20 MB with smudge disabled -- the same 135x that decides whether one device or seven hundred can be provisioned from a git remote. bf-bootstrap exports GIT_LFS_SKIP_SMUDGE=1 for every git operation it performs (clone and fetch), so provisioning a device no longer implies downloading the package payload. The trade is explicit and documented rather than hidden: with smudge off the pool files on disk are 131-byte pointer texts, and the installer must not mistake one for a package. So the installer gained a guard. A pointer text satisfies every existence check, so without it the run died later inside `sha256sum -c` and reported a checksum mismatch -- a misdiagnosis that sends an operator looking for corruption when the payload was simply never fetched. It now recognises the pointer by content and names the remedy instead. Getting the payload is a deliberate second step, and both documented routes are real: a local mirror (the offline-correct path, and the one the fleet uses) or `git lfs pull --include='provisioning/offline-repo/built/pool/*'`, which fetches only the delivery pool and leaves the intake copy under packages/ unfetched -- verified: 1.8 GB and a real .deb in built/pool, 131-byte stub still in packages/. Tests: test-field-os-lifecycle.sh gains a scenario that lays down a pointer text where a .deb belongs and asserts three things -- the run refuses, the message names the cause, and it does not blame a checksum. Also asserts apt-get is never invoked. 16 scenarios, all passing. docs/27 is corrected where it had gone stale: it claimed a clone carries the packages and the installer downloads nothing, which stopped being true the moment the payload moved to LFS.
320 lines
17 KiB
Bash
Executable file
320 lines
17 KiB
Bash
Executable file
#!/usr/bin/env bash
|
||
# Blueforce one-click installer — single entry point for field setup.
|
||
#
|
||
# The field operator runs one command and answers no technical question:
|
||
#
|
||
# sudo ./scripts/install/blueforce-install.sh --dealer-id 12345678
|
||
#
|
||
# Packages always come from the local bundle the repository owner commits with
|
||
# the repository; nothing is downloaded at install time. The bundle is found
|
||
# automatically, in this order (the first complete candidate wins):
|
||
#
|
||
# 1. BF_OFFLINE_REPO operator override (must be valid),
|
||
# 2. <repo>/provisioning/offline-repo/built bundle committed to this repo,
|
||
# 3. /opt/blueforce/offline-repo bundle installed on the device.
|
||
#
|
||
# A missing or incomplete bundle stops the run before the first module, with a
|
||
# Turkish explanation, and changes nothing on the system (fail-closed).
|
||
#
|
||
# --offline is kept for backward compatibility (it used to be mandatory) but is
|
||
# no longer needed: it only forces the local bundle. --online exists for
|
||
# development/test hosts only and is never the default.
|
||
#
|
||
# User-facing messages are Turkish; code and comments are English.
|
||
# Exit codes: 0 success, 1 blocked or module failure, 2 usage error.
|
||
set -euo pipefail
|
||
|
||
INSTALLER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
REPO_ROOT_DIR="$(cd "$INSTALLER_DIR/../.." && pwd)"
|
||
MODULES_DIR="$INSTALLER_DIR/installer/modules"
|
||
MANIFEST_DIR="$REPO_ROOT_DIR/provisioning/offline-repo/manifests"
|
||
BUNDLE_IN_REPO="$REPO_ROOT_DIR/provisioning/offline-repo/built"
|
||
BUNDLE_ON_DEVICE="/opt/blueforce/offline-repo"
|
||
README_REF="provisioning/offline-repo/README.md"
|
||
LOG_FILE="${LOG_FILE:-/var/log/blueforce-install.log}"
|
||
STATE_DIR="${STATE_DIR:-/var/lib/blueforce}"
|
||
STATE_FILE="$STATE_DIR/install-state"
|
||
MODULES=(01-precheck 02-system 03-user 04-hostname 05-network 06-wireguard 07-ssh 08-rdp 09-rustdesk 10-docker 11-meg 12-monitoring 13-firewall 14-update-policy 15-logrotate 16-gui 17-healthcheck 18-final-check)
|
||
|
||
DEALER_ID="" FROM="" ONLY="" CHECK_MODE=0 RESUME=0 ASSUME_YES=0 FORCE_OFFLINE=0 ONLINE=0
|
||
# The operator's override is remembered before any default is applied: an
|
||
# explicitly set path must fail closed instead of being replaced silently.
|
||
REPO_OVERRIDE="${BF_OFFLINE_REPO:-}"
|
||
REPO_DIR="" REPO_ORIGIN="" BF_OFFLINE=0
|
||
REPO_SEARCH=()
|
||
|
||
usage() {
|
||
cat <<'EOF'
|
||
Kullanım: sudo ./blueforce-install.sh --dealer-id <8 haneli numara> [seçenekler]
|
||
|
||
Paketler bu betiğin yanındaki yerel paket deposundan gelir. İnternet, paket
|
||
indirme ve teknik bilgi gerekmez; tek zorunlu bilgi bayi numarasıdır:
|
||
|
||
sudo ./blueforce-install.sh --dealer-id 12345678
|
||
|
||
Seçenekler:
|
||
--dealer-id NUMARA Zorunlu. 8 haneli bayi numarası (ana makine adı bf-NUMARA).
|
||
--yes, -y Onay sormadan başla (otomatik kurulum).
|
||
--check Salt okunur denetim: sistemde hiçbir şeyi değiştirmez.
|
||
--from NN NN numaralı modülden başla.
|
||
--only AD Yalnız belirtilen modülü çalıştır (örn. --only 05-network).
|
||
--resume Daha önce tamamlanmış modülleri atla.
|
||
--help, -h Bu yardımı yaz.
|
||
|
||
Yalnız geliştirme/test için (sahada gerekmez):
|
||
--offline Yerel depoyu kullanmayı zorla; varsayılan davranış zaten budur.
|
||
--online Paketleri internetten çeker. ÜRETİM cihazlarında KULLANMAYIN.
|
||
|
||
Paket deposu şu sırayla aranır: BF_OFFLINE_REPO ortam değişkeni, depodaki
|
||
provisioning/offline-repo/built dizini, /opt/blueforce/offline-repo.
|
||
Depo bulunamazsa kurulum başlamaz. Ayrıntı: scripts/install/README.md
|
||
EOF
|
||
}
|
||
|
||
log() {
|
||
if [[ "$CHECK_MODE" -eq 1 ]]; then
|
||
printf '[installer check] %s\n' "$1" >&2
|
||
else
|
||
mkdir -p "$(dirname "$LOG_FILE")" "$STATE_DIR"
|
||
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) [installer] $1" | tee -a "$LOG_FILE"
|
||
fi
|
||
}
|
||
# notice: a message the operator must read. Always on stdout, and in apply mode
|
||
# also in the log file (the log stays the audit record of the run).
|
||
notice() {
|
||
printf '%s\n' "$1"
|
||
if [[ "$CHECK_MODE" -eq 0 ]]; then
|
||
mkdir -p "$(dirname "$LOG_FILE")"
|
||
printf '%s [installer] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$1" >> "$LOG_FILE"
|
||
fi
|
||
}
|
||
usage_error() { printf 'HATA: %s\n\n' "$1" >&2; usage >&2; exit 2; }
|
||
blocked_bundle() {
|
||
printf 'OFFLINE BLOCKED: %s\n' "$1" >&2
|
||
printf ' Depo : %s\n' "${REPO_DIR:-tanımsız}" >&2
|
||
printf ' Ne yapmalı : paket setini yeniden üretip git ile getirin (%s)\n' "$README_REF" >&2
|
||
printf ' ÇIKIŞ NEDENİ: yerel paket deposu doğrulanamadı; kurulum başlamadı ve\n' >&2
|
||
printf ' sistemde hiçbir değişiklik yapılmadı.\n' >&2
|
||
exit 1
|
||
}
|
||
record_state() { mkdir -p "$STATE_DIR"; local tmp="$STATE_FILE.tmp"; grep -v "^$1=" "$STATE_FILE" 2>/dev/null > "$tmp" || true; echo "$1=$2" >> "$tmp"; mv "$tmp" "$STATE_FILE"; }
|
||
state_of() { grep "^$1=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || true; }
|
||
|
||
# ------------------------------------------------------- bundle discovery -----
|
||
human_size() {
|
||
awk -v b="$1" 'BEGIN {
|
||
split("B KiB MiB GiB TiB", unit, " ")
|
||
i = 1
|
||
while (b >= 1024 && i < 5) { b /= 1024; i++ }
|
||
if (i == 1) printf "%d %s\n", b, unit[i]; else printf "%.1f %s\n", b, unit[i]
|
||
}'
|
||
}
|
||
repository_bytes() {
|
||
local dir="$1" bytes
|
||
bytes="$(du -sb "$dir" 2>/dev/null | awk '{print $1}')"
|
||
if [[ ! "$bytes" =~ ^[0-9]+$ ]]; then
|
||
bytes="$(du -sk "$dir" 2>/dev/null | awk '{print $1 * 1024}')"
|
||
fi
|
||
[[ "$bytes" =~ ^[0-9]+$ ]] || bytes=0
|
||
printf '%s' "$bytes"
|
||
}
|
||
repository_package_count() {
|
||
awk -F '\t' 'NR > 1 && NF >= 5 { n++ } END { printf "%d", n + 0 }' "$1/packages.lock.tsv"
|
||
}
|
||
repository_is_complete() {
|
||
local repo="$1"
|
||
[[ -n "$repo" && -d "$repo" && -s "$repo/Packages" && -s "$repo/Packages.gz" && -s "$repo/packages.lock.tsv" && -s "$repo/SHA256SUMS" ]]
|
||
}
|
||
# Fills REPO_DIR/REPO_ORIGIN and REPO_SEARCH (one readable line per candidate).
|
||
discover_offline_repository() {
|
||
local -a dirs=() labels=()
|
||
local i dir label
|
||
if [[ -n "$REPO_OVERRIDE" ]]; then
|
||
dirs=("$REPO_OVERRIDE"); labels=("BF_OFFLINE_REPO ortam değişkeni")
|
||
else
|
||
dirs=("$BUNDLE_IN_REPO" "$BUNDLE_ON_DEVICE")
|
||
labels=("depodaki hazır paket seti" "cihazdaki kurulu paket deposu")
|
||
fi
|
||
for i in "${!dirs[@]}"; do
|
||
dir="${dirs[$i]}"; label="${labels[$i]}"
|
||
if [[ ! -e "$dir" ]]; then
|
||
REPO_SEARCH+=("$label — yok: $dir")
|
||
continue
|
||
fi
|
||
if repository_is_complete "$dir"; then
|
||
REPO_DIR="$dir"; REPO_ORIGIN="$label"
|
||
REPO_SEARCH+=("$label — kullanılabilir: $dir")
|
||
return 0
|
||
fi
|
||
REPO_SEARCH+=("$label — eksik/bozuk: $dir (Packages, Packages.gz, packages.lock.tsv ve SHA256SUMS dosyalarının hepsi gerekli)")
|
||
done
|
||
return 1
|
||
}
|
||
report_missing_repository() {
|
||
local entry
|
||
printf 'HATA: Yerel paket deposu yok. Repo sorumlusuna başvurun: %s\n' "$README_REF" >&2
|
||
printf '\n' >&2
|
||
printf 'Kurulum paketleri yalnız yerel depodan kurar; bu makinede internetten paket\n' >&2
|
||
printf 'alınmaz. Depo olmadığı için paket adımları çalışamaz.\n' >&2
|
||
printf '\nAranan yerler:\n' >&2
|
||
for entry in "${REPO_SEARCH[@]}"; do
|
||
printf ' - %s\n' "$entry" >&2
|
||
done
|
||
printf '\nNe yapmalı:\n' >&2
|
||
printf ' 1) Paket setini git ile tam olarak getirin (repo sorumlusunun hazırladığı\n' >&2
|
||
printf ' provisioning/offline-repo/built dizini) ya da repo sorumlusunun verdiği\n' >&2
|
||
printf ' kopyayı /opt/blueforce/offline-repo altına koyun.\n' >&2
|
||
printf ' 2) Kurulumu yeniden çalıştırın: sudo ./blueforce-install.sh --dealer-id <numara>\n' >&2
|
||
printf '\nKuruluma hiç başlanmadı; sistemde hiçbir değişiklik yapılmadı.\n' >&2
|
||
printf 'ÇIKIŞ NEDENİ: yerel paket deposu bulunamadı.\n' >&2
|
||
exit 1
|
||
}
|
||
|
||
# ------------------------------------------------------ bundle validation -----
|
||
# Fail-closed: every check has to pass before the first module runs. The local
|
||
# APT source and the pinned versions are the only package contract of the
|
||
# install, so a bundle that does not match its own lock file is refused.
|
||
validate_offline_repository() {
|
||
local repo="$REPO_DIR" manifest pin package version source lpackage lversion larch lsha lsource
|
||
[[ "$repo" = /* && -d "$repo" ]] || blocked_bundle 'BF_OFFLINE_REPO mutlak bir yerel dizin olmalıdır.'
|
||
repository_is_complete "$repo" || blocked_bundle 'Yerel APT indeksi, kilit dosyası ya da checksum kaydı eksik; paket seti tamamlanmamış.'
|
||
# The pool is stored through Git LFS, so a checkout made with smudge disabled
|
||
# (bf-bootstrap does this deliberately: it avoids an 884 MB download) leaves
|
||
# 131-byte pointer texts where the .deb files should be. They satisfy `-f`
|
||
# above, so without this gate the run would die later inside `sha256sum -c`
|
||
# and report a checksum mismatch, which reads as a corrupt bundle instead of
|
||
# what it is: payload that was never fetched.
|
||
if [[ -e "$repo/pool" ]]; then
|
||
while IFS= read -r -d '' _pkgfile; do
|
||
if [[ "$(head -c 42 "$_pkgfile" 2>/dev/null)" == 'version https://git-lfs.github.com/spec/v1'* ]]; then
|
||
blocked_bundle "Paketler henüz indirilmemiş: depodaki dosyalar .deb değil, Git LFS işaretçisi ($(basename "$_pkgfile")). Çözüm: depo kökünde 'git lfs pull --include=\"provisioning/offline-repo/built/pool/*\"' çalıştırın, sonra bu kurulumu tekrarlayın."
|
||
fi
|
||
done < <(find "$repo/pool" -maxdepth 1 -type f -name '*.deb' -print0 2>/dev/null)
|
||
fi
|
||
(cd "$repo" && sha256sum -c SHA256SUMS >/dev/null) || blocked_bundle 'Yerel APT deposunun checksum doğrulaması başarısız.'
|
||
awk -F '\t' 'NR == 1 { if ($0 != "package\tversion\tarchitecture\tsha256\tsource") exit 1; next } $1 !~ /^[a-z0-9][a-z0-9+.-]*$/ || $2 == "" || $3 == "" || $4 !~ /^[a-f0-9]{64}$/ || $5 == "" { exit 1 } END { exit NR < 2 }' "$repo/packages.lock.tsv" || blocked_bundle 'Paket kilidi boş ya da biçimi bozuk.'
|
||
# Every locked row must be present in the pool: the lock file is the SBOM the
|
||
# bundle is installed from, so a row without its file is an incomplete set.
|
||
while IFS=$'\t' read -r lpackage lversion larch lsha lsource; do
|
||
[[ "$lpackage" == 'package' && "$lversion" == 'version' ]] && continue
|
||
[[ -n "$lsource" && -f "$repo/pool/$lsource" ]] || blocked_bundle "Kilitte yazılı paket depoda yok: $lpackage $lversion ($lsource)."
|
||
done < "$repo/packages.lock.tsv"
|
||
# The pin manifests are the reviewed "what must be installed" contract of the
|
||
# repository; when they travel with the checkout they are enforced too.
|
||
if [[ -d "$MANIFEST_DIR" ]]; then
|
||
for manifest in "$MANIFEST_DIR"/*.txt; do
|
||
[[ -e "$manifest" ]] || continue
|
||
[[ -s "$manifest" ]] || blocked_bundle "Gerekli pin manifestosu boş: $(basename "$manifest")."
|
||
grep -qvE '^[[:space:]]*(#|$)' "$manifest" || blocked_bundle "Gerekli pin manifestosunda paket pini yok: $(basename "$manifest")."
|
||
while IFS= read -r pin || [[ -n "$pin" ]]; do
|
||
[[ -z "$pin" || "$pin" == \#* ]] && continue
|
||
[[ "$pin" =~ ^[a-z0-9][a-z0-9+.-]*=[^[:space:]]+$ ]] && [[ "$pin" != *placeholder* && "$pin" != *REPLACE* && "$pin" != *replace* ]] || blocked_bundle "Geçersiz paket pini: $(basename "$manifest") içinde '$pin'."
|
||
package="${pin%%=*}"; version="${pin#*=}"
|
||
source="$(awk -F '\t' -v p="$package" -v v="$version" '$1 == p && $2 == v {print $5; exit}' "$repo/packages.lock.tsv")"
|
||
[[ -n "$source" && -f "$repo/pool/$source" ]] || blocked_bundle "Pinlenen paket depoda yok: $package=$version."
|
||
done < "$manifest"
|
||
done
|
||
else
|
||
log "BİLGİ: pin manifestoları bu makinede yok; sözleşme packages.lock.tsv üzerinden doğrulandı."
|
||
fi
|
||
# The local APT source. --check stays read-only: its apt configuration lives
|
||
# in a temporary directory and is removed on exit.
|
||
if [[ "$CHECK_MODE" -eq 1 ]]; then
|
||
CHECK_TMP="$(mktemp -d)"
|
||
trap 'rm -rf "${CHECK_TMP:-}"' EXIT
|
||
printf 'deb [trusted=yes] file:%s ./\n' "$repo" > "$CHECK_TMP/offline.list"
|
||
BF_OFFLINE_APT_CONFIG="$CHECK_TMP/apt.conf"
|
||
printf 'Dir::Etc::sourcelist "%s";\nDir::Etc::sourceparts "-";\nAcquire::Languages "none";\n' "$CHECK_TMP/offline.list" > "$BF_OFFLINE_APT_CONFIG"
|
||
chmod 600 "$CHECK_TMP/offline.list" "$BF_OFFLINE_APT_CONFIG"
|
||
else
|
||
install -d -m 700 "$STATE_DIR"
|
||
printf 'deb [trusted=yes] file:%s ./\n' "$repo" > "$STATE_DIR/offline.list"
|
||
chmod 600 "$STATE_DIR/offline.list"
|
||
BF_OFFLINE_APT_CONFIG="$(mktemp)"; chmod 600 "$BF_OFFLINE_APT_CONFIG"
|
||
printf 'Dir::Etc::sourcelist "%s";\nDir::Etc::sourceparts "-";\nAcquire::Languages "none";\n' "$STATE_DIR/offline.list" > "$BF_OFFLINE_APT_CONFIG"
|
||
fi
|
||
export BF_OFFLINE_APT_CONFIG
|
||
}
|
||
|
||
# --------------------------------------------------------------- arguments ----
|
||
while [[ $# -gt 0 ]]; do case "$1" in
|
||
--dealer-id) DEALER_ID="${2:-}"; shift 2;;
|
||
--offline) FORCE_OFFLINE=1; shift;;
|
||
--online) ONLINE=1; shift;;
|
||
--from) FROM="${2:-}"; shift 2;;
|
||
--only) ONLY="${2:-}"; shift 2;;
|
||
--check) CHECK_MODE=1; shift;;
|
||
--resume) RESUME=1; shift;;
|
||
--yes|-y) ASSUME_YES=1; shift;;
|
||
--help|-h) usage; exit 0;;
|
||
*) usage_error "Bilinmeyen parametre: $1";;
|
||
esac; done
|
||
[[ -n "$DEALER_ID" ]] || usage_error 'Zorunlu parametre eksik: --dealer-id <8 haneli bayi numarası>. Örnek: sudo ./blueforce-install.sh --dealer-id 12345678'
|
||
[[ "$DEALER_ID" =~ ^[0-9]{8}$ ]] || usage_error "Geçersiz bayi numarası '$DEALER_ID': 8 haneli rakam olmalıdır (örn. 12345678)."
|
||
[[ "$FORCE_OFFLINE" -eq 1 && "$ONLINE" -eq 1 ]] && usage_error 'Bir kurulumda hem --offline hem --online verilemez.'
|
||
|
||
# ------------------------------------------------------------------- mode -----
|
||
# Default (no flag): find the local bundle and install from it. The flag the
|
||
# operator used to have to remember is now only a way to insist on it.
|
||
if [[ "$ONLINE" -eq 1 ]]; then
|
||
BF_OFFLINE=0
|
||
notice 'DİKKAT: --online yalnız geliştirme/test içindir. Paketler uzak APT kaynaklarından'
|
||
notice ' çekilir; ÜRETİM cihazlarında KULLANMAYIN.'
|
||
export BF_OFFLINE_REPO=""
|
||
else
|
||
if ! discover_offline_repository; then
|
||
report_missing_repository
|
||
fi
|
||
BF_OFFLINE=1
|
||
notice "Yerel paket deposu bulundu: $(repository_package_count "$REPO_DIR") paket, $(human_size "$(repository_bytes "$REPO_DIR")")"
|
||
notice " Depo : $REPO_DIR"
|
||
notice " Kaynak : $REPO_ORIGIN"
|
||
notice ' Kip : yerel kurulum. İnternet, paket indirme ve ek anahtar gerekmez.'
|
||
if [[ "$FORCE_OFFLINE" -eq 1 ]]; then
|
||
log 'BİLGİ: --offline verildi; yerel depo zaten varsayılan kaynaktır.'
|
||
fi
|
||
validate_offline_repository
|
||
fi
|
||
export DEALER_ID BF_HOSTNAME="bf-${DEALER_ID}" BF_DEVICE="BF-${DEALER_ID}" STATE_DIR BF_OFFLINE BF_OFFLINE_REPO="${REPO_DIR:-}"
|
||
|
||
if [[ "$CHECK_MODE" -eq 0 && "$ASSUME_YES" -eq 0 ]]; then
|
||
if ! read -r -p "Kurulum $BF_DEVICE olarak başlatılsın mı? [e/H] " answer; then
|
||
printf '\nKurulum iptal edildi: onay alınamadı (terminal yok). Otomatik kurulum için --yes ekleyin.\n' >&2
|
||
exit 2
|
||
fi
|
||
[[ "$answer" =~ ^[Ee]$ ]] || { printf 'Kurulum iptal edildi.\n'; exit 2; }
|
||
fi
|
||
|
||
RUN=(); for m in "${MODULES[@]}"; do
|
||
if [[ -n "$ONLY" ]]; then [[ "$m" == "$ONLY" || "$m" == "$ONLY"* ]] && RUN+=("$m"); continue; fi
|
||
[[ -n "$FROM" && "${m%%-*}" < "$FROM" ]] && continue
|
||
if [[ "$CHECK_MODE" -eq 0 && "$RESUME" -eq 1 && "$(state_of "$m")" == OK ]]; then log "SKIP $m (already OK)"; continue; fi
|
||
RUN+=("$m")
|
||
done
|
||
[[ ${#RUN[@]} -gt 0 ]] || { echo "No modules selected." >&2; exit 2; }
|
||
log "=== install start dealer=$BF_DEVICE mode=$([[ "$CHECK_MODE" -eq 1 ]] && echo check || echo apply) source=$([[ "$BF_OFFLINE" -eq 1 ]] && echo local || echo remote) modules=${RUN[*]} ==="
|
||
failures=0
|
||
for m in "${RUN[@]}"; do
|
||
script="$MODULES_DIR/$m.sh"; if [[ ! -x "$script" ]]; then log "FAIL $m (script missing)"; [[ "$CHECK_MODE" -eq 0 ]] && record_state "$m" FAIL; failures=$((failures+1)); break; fi
|
||
args=(); [[ "$CHECK_MODE" -eq 1 ]] && args+=(--check)
|
||
set +e; if [[ "$CHECK_MODE" -eq 1 ]]; then LOG_FILE=/dev/null bash "$script" "${args[@]}"; else LOG_FILE="$LOG_FILE" bash "$script"; fi; rc=$?; set -e
|
||
case "$rc" in 0) log "OK $m"; [[ "$CHECK_MODE" -eq 0 ]] && record_state "$m" OK;; 3) log "SKIP $m"; [[ "$CHECK_MODE" -eq 0 ]] && record_state "$m" SKIP;; *) log "FAIL $m (exit $rc)"; [[ "$CHECK_MODE" -eq 0 ]] && record_state "$m" FAIL; failures=$((failures+1)); [[ "$CHECK_MODE" -eq 0 ]] && exit 1;; esac
|
||
done
|
||
[[ "$CHECK_MODE" -eq 1 && "$failures" -gt 0 ]] && exit 1
|
||
if [[ "$CHECK_MODE" -eq 0 && "$BF_OFFLINE" -eq 1 && "$failures" -eq 0 ]]; then
|
||
install -d -m 700 "$STATE_DIR"
|
||
umask 077
|
||
python3 - "$STATE_DIR/state.json" "$DEALER_ID" <<'PY'
|
||
import json, os, sys, tempfile, uuid
|
||
path, dealer = sys.argv[1:]
|
||
state={'phase':'PROVISIONED_OFFLINE','enrollment_status':'PENDING','fleet_status':'PENDING','device_id':'BF-'+dealer,'provisioning_id':str(uuid.uuid4())}
|
||
fd,tmp=tempfile.mkstemp(prefix='state.json.',dir=os.path.dirname(path)); os.fchmod(fd,0o600)
|
||
with os.fdopen(fd,'w') as f: json.dump(state,f,sort_keys=True); f.write('\n')
|
||
os.replace(tmp,path)
|
||
PY
|
||
chmod 600 "$STATE_DIR/state.json"
|
||
log "OFFLINE COMPLETE: device is PROVISIONED_OFFLINE; enrollment is required before READY"
|
||
notice "Kurulum tamam: $BF_DEVICE yerel paket deposundan kuruldu."
|
||
notice 'Cihaz şu an PROVISIONED_OFFLINE durumunda; kayıt (enrollment) tamamlanana kadar READY sayılmaz.'
|
||
fi
|
||
log "=== install done dealer=$BF_DEVICE failures=$failures ==="
|