4 Commits

Author SHA1 Message Date
nocode 0de970239f feat(build): refactor build script with interactive version and variant selection
Replace automatic version reading from VERSION file with interactive prompts for version (major.minor.build) and ISO variant name (e.g., cli, gnome, kde).

Changes:
- Remove automatic VERSION file reading; now serves as default with prompt
- Add interactive prompts for version and variant name with validation
- Simplify ISO naming from FWS-v{major}-{minor}-{build}-{date}-x86_64.iso to fws-{version}-{variant}.iso
- Update hostname from 'archiso' to 'fws'
- Add custom FWS logo for fastfetch with color configuration
- Improve error messages and user feedback

This enables flexible build variants and streamlines the build process.
2026-06-23 11:02:41 +02:00
nocode 7d6bba42b2 feat(anaconda): add hostname field to user spoke and fix keyboard layout by territory
Replace keyboard correction logic in kickstart with anaconda package patches:

- Add "Nom de machine" (hostname) field to the User spoke in anaconda GUI
- user.py writes the hostname value to /tmp/fws-hostname for the kickstart %post
- Simplify kickstart to read from /tmp/fws-hostname instead of live hostname
- Fix keyboard layout selection to use full locale (e.g. fr_CH) instead of just language (fr)
  to correctly derive territory-specific keymaps (ch(fr) for Switzerland, not fr for France)
- Remove outdated fix_keyboard() function from kickstart

Also add --network=host flag to podman run in setup-aur.sh to ensure DNS and internet
connectivity through the host stack instead of the bridge (fixes timeout issues on
certain networks with firewalld/NAT).

Bump anaconda pkgrel from 2 to 4.
2026-06-23 10:48:32 +02:00
nocode 1184ba17d9 fix(releng): correct hostname and keyboard mapping in anaconda post-installation
Fix two bugs in the anaconda post-installation script:

1. Hostname handling: Anaconda writes an empty /etc/hostname to the target system. Now we preserve the hostname set during installation from the live environment, falling back to 'fws' if using default values.

2. Keyboard mapping: Anaconda derives keyboard layout from language only, ignoring territory. For locales with territory variants (e.g., fr_CH for Switzerland), we now apply territory-specific keyboard maps to both console (vconsole.conf) and X11 (xorg.conf.d) using langtable for proper mapping.
2026-06-23 08:40:42 +02:00
nocode d25ab9b4da fix(build): add --network=host to podman run commands for dns connectivity
Add --network=host flag to podman run commands in both build.sh and build-offi.sh to fix DNS resolution issues.

The podman bridge network was unable to route to external hosts on certain wifi networks due to firewalld/NAT misconfiguration after network changes, resulting in "core.db: Resolving timed out" errors. By sharing the host's network stack, the container inherits the same network connectivity including DNS resolution via 127.0.0.53/systemd-resolved that is available on the host.
2026-06-23 08:06:53 +02:00
9 changed files with 189 additions and 26 deletions
+1 -1
View File
@@ -1 +1 @@
0.5.3 0.5.4
+45 -20
View File
@@ -138,7 +138,12 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
"${SUDO[@]}" podman pull "$IMAGE" "${SUDO[@]}" podman pull "$IMAGE"
echo "==> Relance dans le conteneur ($SELF)..." echo "==> Relance dans le conteneur ($SELF)..."
"${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \ # --network=host : le réseau bridge de podman ne route plus vers l'extérieur
# sur ce wifi (firewalld/NAT cassé après changement de réseau → « core.db :
# Resolving timed out », même avec --dns). En partageant la pile réseau de
# l'hôte (qui, lui, a internet + DNS via 127.0.0.53/systemd-resolved joignable
# dans son propre netns), le conteneur récupère une connectivité identique.
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
-e FWS_IN_CONTAINER=1 \ -e FWS_IN_CONTAINER=1 \
-v "$REPO_DIR":"$REPO_DIR" \ -v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \ -v fws-pacman-cache:/var/cache/pacman/pkg \
@@ -188,29 +193,49 @@ WORK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "==> Dossier de travail : $WORK_DIR" echo "==> Dossier de travail : $WORK_DIR"
# ============================================================ # ============================================================
# Lecture et validation de la version (fichier VERSION) # Version (major.minor.build) + nom de la variante.
# La version est DEMANDÉE ; le fichier VERSION sert de DÉFAUT (taper Entrée =
# reprendre sa valeur). Produit fws-<major.minor.build>-<nom>.iso
# (ex. fws-1.5.0-cli.iso). Nécessite un terminal (read).
# ============================================================ # ============================================================
# Défaut éventuel lu dans le fichier VERSION (doit être major.minor.build).
VERSION_FILE="$WORK_DIR/VERSION" VERSION_FILE="$WORK_DIR/VERSION"
[ -f "$VERSION_FILE" ] \ VERSION_DEFAULT=""
|| { echo -e "\e[31mFichier VERSION introuvable : $VERSION_FILE\e[0m"; exit 1; } if [ -f "$VERSION_FILE" ]; then
VERSION_DEFAULT="$(tr -d ' \t\r\n' < "$VERSION_FILE")"
# Trim espaces / tabulations / CR / LF [[ "$VERSION_DEFAULT" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || VERSION_DEFAULT=""
VERSION="$(tr -d ' \t\r\n' < "$VERSION_FILE")"
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo -e "\e[31mVERSION invalide : '$VERSION' (attendu Major.Minor.Build, ex. 1.2.3)\e[0m"
exit 1
fi fi
IFS='.' read -r MAJOR MINOR BUILD <<< "$VERSION" while :; do
BUILD_DATE="$(date +%Y.%m.%d)" if [ -n "$VERSION_DEFAULT" ]; then
ISO_VERSION="v${MAJOR}-${MINOR}-${BUILD}-${BUILD_DATE}" read -rp "==> Version (major.minor.build) [Entrée = $VERSION_DEFAULT] : " VERSION \
OUT_DIR="$WORK_DIR/out/v${MAJOR}/v${MAJOR}.${MINOR}" || { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
ISO_FILE="$OUT_DIR/FWS-${ISO_VERSION}-x86_64.iso" [ -n "$VERSION" ] || VERSION="$VERSION_DEFAULT" # vide → on reprend le fichier VERSION
else
read -rp "==> Version (major.minor.build, ex. 1.5.0) : " VERSION \
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
fi
[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && break
echo -e "\e[31m Format attendu : major.minor.build (ex. 1.5.0)\e[0m" >&2
done
while :; do
read -rp "==> Nom de la variante (ex. cli, gnome, kde) : " ISO_TAG \
|| { echo -e "\e[31mEntrée requise — lance le script en interactif.\e[0m" >&2; exit 1; }
# normalise : minuscules, lettres / chiffres / tirets uniquement
ISO_TAG="$(printf '%s' "$ISO_TAG" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9-')"
[ -n "$ISO_TAG" ] && break
echo -e "\e[31m Nom requis (lettres, chiffres, tirets)\e[0m" >&2
done
echo "==> Version officielle : ${MAJOR}.${MINOR}.${BUILD}" IFS='.' read -r MAJOR MINOR BUILD <<< "$VERSION"
echo "==> Dossier de sortie : $OUT_DIR" # iso_version interne (passé à mkarchiso) ; l'ISO finale est renommée plus bas.
echo "==> ISO finale : FWS-${ISO_VERSION}-x86_64.iso" ISO_VERSION="${VERSION}-${ISO_TAG}"
OUT_DIR="$WORK_DIR/out/v${MAJOR}/v${MAJOR}.${MINOR}"
ISO_FILE="$OUT_DIR/fws-${VERSION}-${ISO_TAG}.iso"
echo "==> Version : ${VERSION} · variante : ${ISO_TAG}"
echo "==> ISO finale : fws-${VERSION}-${ISO_TAG}.iso"
echo "==> Dossier : $OUT_DIR"
# pacman ≥ 7 sandboxe les ops FS via landlock, non supporté par le kernel # pacman ≥ 7 sandboxe les ops FS via landlock, non supporté par le kernel
# WSL ni (selon le noyau) par le conteneur → désactivation. Idempotent, # WSL ni (selon le noyau) par le conteneur → désactivation. Idempotent,
@@ -319,4 +344,4 @@ echo "==> Nettoyage..."
rm -rf /tmp/fws-build/work rm -rf /tmp/fws-build/work
echo "==> Build officiel terminé ! ISO : $FINAL" echo "==> Build officiel terminé ! ISO : $FINAL"
echo -e "\e[32mScript de build officiel (v${MAJOR}.${MINOR}.${BUILD}) terminé avec succès.\e[0m" echo -e "\e[32mScript de build officiel (fws-${VERSION}-${ISO_TAG}) terminé avec succès.\e[0m"
+6 -1
View File
@@ -133,7 +133,12 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
"${SUDO[@]}" podman pull "$IMAGE" "${SUDO[@]}" podman pull "$IMAGE"
echo "==> Relance dans le conteneur ($SELF)..." echo "==> Relance dans le conteneur ($SELF)..."
"${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \ # --network=host : le réseau bridge de podman ne route plus vers l'extérieur
# sur ce wifi (firewalld/NAT cassé après changement de réseau → « core.db :
# Resolving timed out », même avec --dns). En partageant la pile réseau de
# l'hôte (qui, lui, a internet + DNS via 127.0.0.53/systemd-resolved joignable
# dans son propre netns), le conteneur récupère une connectivité identique.
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
-e FWS_IN_CONTAINER=1 \ -e FWS_IN_CONTAINER=1 \
-v "$REPO_DIR":"$REPO_DIR" \ -v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \ -v fws-pacman-cache:/var/cache/pacman/pkg \
+1 -1
View File
@@ -1 +1 @@
archiso fws
@@ -5,5 +5,14 @@
# On utilise donc un autre nom. # On utilise donc un autre nom.
_fastfetch_rc=$? _fastfetch_rc=$?
if [ "${_fastfetch_rc}" -eq 0 ]; then if [ "${_fastfetch_rc}" -eq 0 ]; then
fastfetch # Logo FWS maison (4 carrés + Tux) au lieu du logo Arch détecté par défaut.
fastfetch \
--logo-type file \
--logo /usr/local/share/fws/logo.txt \
--logo-color-1 '38;2;30;160;100' \
--logo-color-2 '38;2;45;125;210' \
--logo-color-3 '38;2;240;180;40' \
--logo-color-4 '38;2;25;85;145' \
--logo-color-5 '38;2;28;28;34' \
--logo-color-6 '38;2;245;165;35'
fi fi
@@ -0,0 +1,9 @@
$1████████ $2██$5████$2██
$1████████ $2█$5██$2██$5██$2█
$1████████ $2█$5██████$2█
$1████████ $2██$6█$2██$6█$2██
$3████████ $4████████
$3████████ $4████████
$3████████ $4████████
$3████████ $4████████
@@ -89,6 +89,17 @@ SYSROOT=/mnt/sysroot
# DNS pour que « pacman -S <bureau> » fonctionne dans le chroot. # DNS pour que « pacman -S <bureau> » fonctionne dans le chroot.
cp -f /etc/resolv.conf "$SYSROOT/etc/resolv.conf" 2>/dev/null || true cp -f /etc/resolv.conf "$SYSROOT/etc/resolv.conf" 2>/dev/null || true
# --- Hostname : valeur saisie dans le champ « Nom de machine » du spoke User --
# Anaconda ne capte PAS le hostname du spoke Réseau (→ /etc/hostname vide). On a
# donc ajouté un champ Hostname au spoke Utilisateur (cf. patch du paquet
# anaconda) : user.py écrit la valeur dans /tmp/fws-hostname (live). On la
# nettoie (caractères de hostname valides) et on l'écrit dans la cible ; défaut
# « fws » si vide/absent → plus jamais de hostname vide ni « archiso ».
HN=""
[ -f /tmp/fws-hostname ] && HN="$(tr -cd 'a-zA-Z0-9._-' < /tmp/fws-hostname)"
[ -n "$HN" ] || HN="fws"
echo "$HN" > "$SYSROOT/etc/hostname"
# Choix du bureau (écrit par l'addon « Choix du bureau », à venir ; valeur lue # Choix du bureau (écrit par l'addon « Choix du bureau », à venir ; valeur lue
# par le %post chrooté ci-dessous). « cli » par défaut si absent. # par le %post chrooté ci-dessous). « cli » par défaut si absent.
if [ -f /tmp/fws-desktop ]; then if [ -f /tmp/fws-desktop ]; then
+102 -1
View File
@@ -25,7 +25,7 @@
# ============================================================================ # ============================================================================
pkgname=anaconda pkgname=anaconda
pkgver=45.8 pkgver=45.8
pkgrel=2 pkgrel=4
pkgdesc="The Anaconda installer (porté sur Arch pour FWS, payload liveimg)" pkgdesc="The Anaconda installer (porté sur Arch pour FWS, payload liveimg)"
arch=('x86_64') arch=('x86_64')
url="https://github.com/rhinstaller/anaconda" url="https://github.com/rhinstaller/anaconda"
@@ -57,6 +57,107 @@ source=("anaconda-$pkgver.tar.gz::https://github.com/rhinstaller/anaconda/archiv
sha256sums=('5ef1464bff7f28a4a09a821cbb0121f09ef504ec169288d742373f86807ec4ed') sha256sums=('5ef1464bff7f28a4a09a821cbb0121f09ef504ec169288d742373f86807ec4ed')
_srcdir="anaconda-anaconda-$pkgver" _srcdir="anaconda-anaconda-$pkgver"
prepare() {
cd "$srcdir/$_srcdir"
# ── FWS : clavier par défaut selon le TERRITOIRE de la locale ──────────────
# set_layouts() amont fait « locale = get_language_id(self.language) » →
# get_language_id JETTE le territoire (fr_CH.UTF-8 → 'fr') → list_keyboards('fr')
# = 'fr(oss)' (France) au lieu de 'ch(fr)' (Suisse). On passe la LOCALE COMPLÈTE
# (self.language) : langtable.list_keyboards('fr_CH.UTF-8') = 'ch(fr)'. La
# validation l'accepte (is_valid_langcode → parse_locale().language non vide).
# → le clavier du GUI ET du système installé suivent enfin la locale choisie.
sed -i \
's/^\(\s*\)locale = get_language_id(self\.language)/\1locale = self.language # FWS: garder le territoire (fr_CH -> ch(fr))/' \
pyanaconda/modules/localization/localization.py
# Garde-fou : casse le build si le motif amont a changé (le patch silencieux).
grep -q 'locale = self.language # FWS' \
pyanaconda/modules/localization/localization.py
# ── FWS : champ « Nom de machine » dans le spoke Utilisateur ───────────────
# Anaconda ne propage PAS le hostname (le champ du spoke Réseau est perdu chez
# nous → /etc/hostname vide). On ajoute donc un champ Hostname à l'onglet de
# création de compte (ligne 2 de la grille, libre) ; user.py écrit la valeur
# dans /tmp/fws-hostname, que le %post du kickstart applique à /etc/hostname.
python3 - <<'PYEOF'
import glob
# 1) user.glade : insérer label + entry "Host name" APRÈS le bloc username_entry.
glades = [p for p in glob.glob("pyanaconda/**/user.glade", recursive=True)
if 'username_entry' in open(p, encoding='utf-8').read()]
assert glades, "FWS: user.glade introuvable"
g = glades[0]
s = open(g, encoding='utf-8').read()
assert 'id="hostname_entry"' not in s, "FWS: glade déjà patché"
i = s.index('id="username_entry"')
k = s.index('</packing>', i) # fin du packing de username_entry
j = s.index('</child>', k) + len('</child>') # fin du <child> EXTERNE (pas l'accessible)
block = '''
<child>
<object class="GtkLabel" id="hostname_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="xpad">10</property>
<property name="label" translatable="yes" context="GUI|User">_Host name</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">hostname_entry</property>
<property name="xalign">1</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="hostname_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="width-chars">50</property>
<child internal-child="accessible">
<object class="AtkObject" id="hostname_entry-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Host Name</property>
</object>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>'''
open(g, 'w', encoding='utf-8').write(s[:j] + block + s[j:])
# 2) user.py (GUI) : récupérer le widget + écrire /tmp/fws-hostname dans apply().
pys = glob.glob("pyanaconda/ui/gui/spokes/user.py")
assert pys, "FWS: user.py introuvable"
p = pys[0]
s = open(p, encoding='utf-8').read()
anchor = 'self._username_entry = self.builder.get_object("username_entry")'
assert anchor in s, "FWS: ancre username_entry absente"
s = s.replace(anchor,
anchor + '\n self._hostname_entry = self.builder.get_object("hostname_entry")',
1)
ap = 'set_user_list(self._users_module, self._user_list, remove_unset=True)'
assert ap in s, "FWS: ancre apply() absente"
s = s.replace(ap, ap + '''
# FWS : mémorise le nom de machine saisi (cible pas encore montée) pour
# le %post du kickstart, qui l'écrira dans /etc/hostname.
try:
_hn = self._hostname_entry.get_text().strip()
except Exception:
_hn = ""
try:
with open("/tmp/fws-hostname", "w") as _f:
_f.write(_hn)
except OSError:
pass''', 1)
open(p, 'w', encoding='utf-8').write(s)
print("FWS: champ Hostname ajouté au spoke Utilisateur (glade + user.py)")
PYEOF
}
build() { build() {
cd "$srcdir/$_srcdir" cd "$srcdir/$_srcdir"
# Glade désactivé (catalogue designer, inutile au runtime) → pas de dép glade. # Glade désactivé (catalogue designer, inutile au runtime) → pas de dép glade.
+4 -1
View File
@@ -114,7 +114,10 @@ if [ -z "$FWS_IN_CONTAINER" ] && ! host_is_arch; then
"${SUDO[@]}" podman pull "$IMAGE" "${SUDO[@]}" podman pull "$IMAGE"
info "Relance dans le conteneur (setup-aur.sh)…" info "Relance dans le conteneur (setup-aur.sh)…"
"${SUDO[@]}" podman run --rm "${TTY[@]}" --privileged \ # --network=host : le bridge podman ne route plus vers l'extérieur sur
# certains réseaux (firewalld/NAT, → « Resolving timed out »). On partage la
# pile réseau de l'hôte (internet + DNS OK) pour les pacman/makepkg/AUR.
"${SUDO[@]}" podman run --rm "${TTY[@]}" --network=host --privileged \
-e FWS_IN_CONTAINER=1 \ -e FWS_IN_CONTAINER=1 \
-v "$REPO_DIR":"$REPO_DIR" \ -v "$REPO_DIR":"$REPO_DIR" \
-v fws-pacman-cache:/var/cache/pacman/pkg \ -v fws-pacman-cache:/var/cache/pacman/pkg \