Compare commits
No commits in common. "master" and "4.4" have entirely different histories.
|
|
@ -6,7 +6,7 @@ jobs:
|
||||||
container:
|
container:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- run: pacman --noconfirm -Syu bandit
|
- run: pacman --noconfirm -Syu bandit
|
||||||
- name: Security checkup with Bandit
|
- name: Security checkup with Bandit
|
||||||
run: bandit -r archinstall || exit 0
|
run: bandit -r archinstall || exit 0
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ jobs:
|
||||||
container:
|
container:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- name: Prepare arch
|
- name: Prepare arch
|
||||||
run: |
|
run: |
|
||||||
pacman-key --init
|
pacman-key --init
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ jobs:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
options: --privileged
|
options: --privileged
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||||
- name: Install pre-dependencies
|
- name: Install pre-dependencies
|
||||||
run: |
|
run: |
|
||||||
pacman -Sy --noconfirm tree git python-pyparted python-setuptools python-sphinx python-sphinx_rtd_theme python-build python-installer python-wheel
|
pacman -Sy --noconfirm tree git python-pyparted python-setuptools python-sphinx python-sphinx_rtd_theme python-build python-installer python-wheel
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
options: --privileged
|
options: --privileged
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- run: pwd
|
- run: pwd
|
||||||
- run: find .
|
- run: find .
|
||||||
- run: cat /etc/os-release
|
- run: cat /etc/os-release
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ jobs:
|
||||||
container:
|
container:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- name: Prepare arch
|
- name: Prepare arch
|
||||||
run: |
|
run: |
|
||||||
pacman-key --init
|
pacman-key --init
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ jobs:
|
||||||
container:
|
container:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- name: Prepare arch
|
- name: Prepare arch
|
||||||
run: |
|
run: |
|
||||||
pacman-key --init
|
pacman-key --init
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ jobs:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
options: --privileged
|
options: --privileged
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- name: Prepare arch
|
- name: Prepare arch
|
||||||
run: |
|
run: |
|
||||||
pacman-key --init
|
pacman-key --init
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ jobs:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
options: --privileged
|
options: --privileged
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- name: Prepare arch
|
- name: Prepare arch
|
||||||
run: |
|
run: |
|
||||||
pacman-key --init
|
pacman-key --init
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ jobs:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
options: --privileged
|
options: --privileged
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- name: Prepare arch
|
- name: Prepare arch
|
||||||
run: |
|
run: |
|
||||||
pacman-key --init
|
pacman-key --init
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,6 @@ jobs:
|
||||||
ruff_format_check:
|
ruff_format_check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- uses: astral-sh/ruff-action@278981a28ce3188b1e39527901f38254bf3aac89 # v4.1.0
|
- uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0
|
||||||
- run: ruff format --diff
|
- run: ruff format --diff
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,5 @@ jobs:
|
||||||
ruff:
|
ruff:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- uses: astral-sh/ruff-action@278981a28ce3188b1e39527901f38254bf3aac89 # v4.1.0
|
- uses: astral-sh/ruff-action@0ce1b0bf8b818ef400413f810f8a11cdbda0034b # v4.0.0
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ jobs:
|
||||||
name: Validate translations
|
name: Validate translations
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- name: Install gettext
|
- name: Install gettext
|
||||||
run: sudo apt-get update && sudo apt-get install -y gettext
|
run: sudo apt-get update && sudo apt-get install -y gettext
|
||||||
- name: Run translation checks
|
- name: Run translation checks
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ jobs:
|
||||||
image: archlinux/archlinux:latest
|
image: archlinux/archlinux:latest
|
||||||
options: --privileged
|
options: --privileged
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
||||||
- run: pwd
|
- run: pwd
|
||||||
- run: find .
|
- run: find .
|
||||||
- run: cat /etc/os-release
|
- run: cat /etc/os-release
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
default_stages: ['pre-commit']
|
default_stages: ['pre-commit']
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.16.0
|
rev: v0.15.17
|
||||||
hooks:
|
hooks:
|
||||||
# fix unused imports and sort them
|
# fix unused imports and sort them
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
|
@ -31,7 +31,7 @@ repos:
|
||||||
args: [--config=.flake8]
|
args: [--config=.flake8]
|
||||||
fail_fast: true
|
fail_fast: true
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v2.3.0
|
rev: v2.1.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
args: [
|
args: [
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ from archinstall.lib.models.users import Password, User, UserSerialization
|
||||||
from archinstall.lib.plugins import load_plugin
|
from archinstall.lib.plugins import load_plugin
|
||||||
from archinstall.lib.translationhandler import Language, tr, translation_handler
|
from archinstall.lib.translationhandler import Language, tr, translation_handler
|
||||||
from archinstall.lib.utils.format import as_key_value_pair
|
from archinstall.lib.utils.format import as_key_value_pair
|
||||||
|
from archinstall.lib.utils.util import is_valid_path
|
||||||
from archinstall.lib.version import get_version
|
from archinstall.lib.version import get_version
|
||||||
from archinstall.tui.components import tui
|
from archinstall.tui.components import tui
|
||||||
|
|
||||||
|
|
@ -403,7 +404,7 @@ class ArchConfig:
|
||||||
) -> None:
|
) -> None:
|
||||||
save_path = dest_path or DEFAULT_SAVE_PATH
|
save_path = dest_path or DEFAULT_SAVE_PATH
|
||||||
|
|
||||||
if not save_path.is_dir():
|
if not is_valid_path(save_path):
|
||||||
warn(
|
warn(
|
||||||
f'Destination directory {save_path} does not exist or is not a directory\n.',
|
f'Destination directory {save_path} does not exist or is not a directory\n.',
|
||||||
'Configuration files can not be saved',
|
'Configuration files can not be saved',
|
||||||
|
|
@ -415,7 +416,7 @@ class ArchConfig:
|
||||||
self.save_user_creds(save_path, password=password)
|
self.save_user_creds(save_path, password=password)
|
||||||
|
|
||||||
def save_user_config(self, dest_path: Path) -> None:
|
def save_user_config(self, dest_path: Path) -> None:
|
||||||
if not dest_path.is_dir():
|
if not is_valid_path(dest_path):
|
||||||
error(f'Invalid path {dest_path}. User configuration could not be saved.')
|
error(f'Invalid path {dest_path}. User configuration could not be saved.')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
@ -429,7 +430,7 @@ class ArchConfig:
|
||||||
dest_path: Path,
|
dest_path: Path,
|
||||||
password: str | None = None,
|
password: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
if not dest_path.is_dir():
|
if not is_valid_path(dest_path):
|
||||||
error(f'Invalid path {dest_path}. User credentials could not be saved.')
|
error(f'Invalid path {dest_path}. User credentials could not be saved.')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import platform
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from parted import Device, Disk, DiskException, FileSystem, Geometry, IOException, Partition, PartitionException, freshDisk, getAllDevices, getDevice, newDisk
|
from parted import Device, Disk, DiskException, FileSystem, Geometry, IOException, Partition, PartitionException, freshDisk, getAllDevices, getDevice, newDisk
|
||||||
|
|
@ -11,7 +10,6 @@ from archinstall.lib.disk.utils import (
|
||||||
find_lsblk_info,
|
find_lsblk_info,
|
||||||
get_all_lsblk_info,
|
get_all_lsblk_info,
|
||||||
get_lsblk_info,
|
get_lsblk_info,
|
||||||
linux_root_guid,
|
|
||||||
mount,
|
mount,
|
||||||
udev_sync,
|
udev_sync,
|
||||||
umount,
|
umount,
|
||||||
|
|
@ -28,6 +26,7 @@ from archinstall.lib.models.device import (
|
||||||
LsblkInfo,
|
LsblkInfo,
|
||||||
ModificationStatus,
|
ModificationStatus,
|
||||||
PartitionFlag,
|
PartitionFlag,
|
||||||
|
PartitionGUID,
|
||||||
PartitionModification,
|
PartitionModification,
|
||||||
PartitionTable,
|
PartitionTable,
|
||||||
SubvolumeModification,
|
SubvolumeModification,
|
||||||
|
|
@ -340,7 +339,6 @@ class DeviceHandler:
|
||||||
block_device: BDevice,
|
block_device: BDevice,
|
||||||
disk: Disk,
|
disk: Disk,
|
||||||
requires_delete: bool,
|
requires_delete: bool,
|
||||||
arch: str | None = None,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
# when we require a delete and the partition to be (re)created
|
# when we require a delete and the partition to be (re)created
|
||||||
# already exists then we have to delete it first
|
# already exists then we have to delete it first
|
||||||
|
|
@ -396,7 +394,7 @@ class DeviceHandler:
|
||||||
|
|
||||||
if disk.type == PartitionTable.GPT.value:
|
if disk.type == PartitionTable.GPT.value:
|
||||||
if part_mod.is_root():
|
if part_mod.is_root():
|
||||||
partition.type_uuid = linux_root_guid(arch).bytes
|
partition.type_uuid = PartitionGUID.LINUX_ROOT_X86_64.bytes
|
||||||
elif PartitionFlag.LINUX_HOME not in part_mod.flags and part_mod.is_home():
|
elif PartitionFlag.LINUX_HOME not in part_mod.flags and part_mod.is_home():
|
||||||
partition.setFlag(PartitionFlag.LINUX_HOME.flag_id)
|
partition.setFlag(PartitionFlag.LINUX_HOME.flag_id)
|
||||||
|
|
||||||
|
|
@ -538,19 +536,11 @@ class DeviceHandler:
|
||||||
# don't touch existing partitions
|
# don't touch existing partitions
|
||||||
filtered_part = [p for p in modification.partitions if not p.exists()]
|
filtered_part = [p for p in modification.partitions if not p.exists()]
|
||||||
|
|
||||||
arch = platform.machine()
|
|
||||||
|
|
||||||
for part_mod in filtered_part:
|
for part_mod in filtered_part:
|
||||||
# if the entire disk got nuked then we don't have to delete
|
# if the entire disk got nuked then we don't have to delete
|
||||||
# any existing partitions anymore because they're all gone already
|
# any existing partitions anymore because they're all gone already
|
||||||
requires_delete = modification.wipe is False
|
requires_delete = modification.wipe is False
|
||||||
self._setup_partition(
|
self._setup_partition(part_mod, modification.device, disk, requires_delete=requires_delete)
|
||||||
part_mod,
|
|
||||||
modification.device,
|
|
||||||
disk,
|
|
||||||
requires_delete=requires_delete,
|
|
||||||
arch=arch,
|
|
||||||
)
|
|
||||||
|
|
||||||
disk.commit()
|
disk.commit()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import CalledProcessError
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from archinstall.lib.command import SysCommand, run
|
from archinstall.lib.command import SysCommand
|
||||||
from archinstall.lib.exceptions import DiskError, SysCallError
|
from archinstall.lib.exceptions import DiskError, SysCallError
|
||||||
from archinstall.lib.log import debug, info, warn
|
from archinstall.lib.log import debug, info, warn
|
||||||
from archinstall.lib.models.device import LsblkInfo, PartitionGUID
|
from archinstall.lib.models.device import LsblkInfo
|
||||||
|
|
||||||
|
|
||||||
class LsblkOutput(BaseModel):
|
class LsblkOutput(BaseModel):
|
||||||
|
|
@ -30,18 +29,19 @@ def _fetch_lsblk_info(
|
||||||
cmd.append(str(dev_path))
|
cmd.append(str(dev_path))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = run(cmd)
|
worker = SysCommand(cmd)
|
||||||
except CalledProcessError as err:
|
except SysCallError as err:
|
||||||
# Get the output minus the message/info from lsblk if it returns a non-zero exit code.
|
# Get the output minus the message/info from lsblk if it returns a non-zero exit code.
|
||||||
if stdout := err.stdout:
|
if err.worker_log:
|
||||||
debug(f'Error calling lsblk: {stdout.decode().rstrip()}')
|
debug(f'Error calling lsblk: {err.worker_log.decode()}')
|
||||||
|
|
||||||
if dev_path:
|
if dev_path:
|
||||||
raise DiskError(f'Failed to read disk "{dev_path}" with lsblk')
|
raise DiskError(f'Failed to read disk "{dev_path}" with lsblk')
|
||||||
|
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
return LsblkOutput.model_validate_json(result.stdout)
|
output = worker.output(remove_cr=False)
|
||||||
|
return LsblkOutput.model_validate_json(output)
|
||||||
|
|
||||||
|
|
||||||
def get_lsblk_info(
|
def get_lsblk_info(
|
||||||
|
|
@ -196,10 +196,3 @@ def swapon(path: Path) -> None:
|
||||||
SysCommand(['swapon', str(path)])
|
SysCommand(['swapon', str(path)])
|
||||||
except SysCallError as err:
|
except SysCallError as err:
|
||||||
raise DiskError(f'Could not enable swap {path}:\n{err.message}')
|
raise DiskError(f'Could not enable swap {path}:\n{err.message}')
|
||||||
|
|
||||||
|
|
||||||
def linux_root_guid(arch: str | None) -> PartitionGUID:
|
|
||||||
if arch == 'aarch64':
|
|
||||||
return PartitionGUID.LINUX_ROOT_AARCH64
|
|
||||||
|
|
||||||
return PartitionGUID.LINUX_ROOT_X86_64
|
|
||||||
|
|
|
||||||
|
|
@ -660,12 +660,16 @@ class Installer:
|
||||||
# in the first column of the entry; check for both cases.
|
# in the first column of the entry; check for both cases.
|
||||||
entry_re = re.compile(rf'#{lang}(\.{encoding})?{modifier} {encoding}')
|
entry_re = re.compile(rf'#{lang}(\.{encoding})?{modifier} {encoding}')
|
||||||
|
|
||||||
|
lang_value = None
|
||||||
for index, line in enumerate(locale_gen_lines):
|
for index, line in enumerate(locale_gen_lines):
|
||||||
if entry_re.match(line):
|
if entry_re.match(line):
|
||||||
locale_gen_lines[index] = line.removeprefix('#')
|
uncommented_line = line.removeprefix('#')
|
||||||
|
locale_gen_lines[index] = uncommented_line
|
||||||
locale_gen.write_text(''.join(locale_gen_lines))
|
locale_gen.write_text(''.join(locale_gen_lines))
|
||||||
|
lang_value = uncommented_line.split()[0]
|
||||||
break
|
break
|
||||||
else:
|
|
||||||
|
if lang_value is None:
|
||||||
error(f"Invalid locale: language '{locale_config.sys_lang}', encoding '{locale_config.sys_enc}'")
|
error(f"Invalid locale: language '{locale_config.sys_lang}', encoding '{locale_config.sys_enc}'")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
@ -675,7 +679,7 @@ class Installer:
|
||||||
error(f'Failed to run locale-gen on target: {e}')
|
error(f'Failed to run locale-gen on target: {e}')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
(self.target / 'etc/locale.conf').write_text(f'LANG={lang}.{encoding}{modifier}\n')
|
(self.target / 'etc/locale.conf').write_text(f'LANG={lang_value}\n')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_timezone(self, zone: str) -> bool:
|
def set_timezone(self, zone: str) -> bool:
|
||||||
|
|
@ -774,15 +778,6 @@ class Installer:
|
||||||
with open(f'{self.target}/etc/systemd/network/10-{nic.iface}.network', 'a') as netconf:
|
with open(f'{self.target}/etc/systemd/network/10-{nic.iface}.network', 'a') as netconf:
|
||||||
netconf.write(str(conf))
|
netconf.write(str(conf))
|
||||||
|
|
||||||
def systemd_resolved_stub_mode(self) -> None:
|
|
||||||
"""
|
|
||||||
Enable systemd-resolved stub mode by (forcefully) setting a symlink
|
|
||||||
For further details see https://wiki.archlinux.org/title/Systemd-resolved#DNS
|
|
||||||
"""
|
|
||||||
resolv = self.target / 'etc/resolv.conf'
|
|
||||||
resolv.unlink(missing_ok=True)
|
|
||||||
resolv.symlink_to('/run/systemd/resolve/stub-resolv.conf')
|
|
||||||
|
|
||||||
def copy_iso_network_config(self, enable_services: bool = False) -> bool:
|
def copy_iso_network_config(self, enable_services: bool = False) -> bool:
|
||||||
# Copy (if any) iwd password and config files
|
# Copy (if any) iwd password and config files
|
||||||
iwd_dir = LPath('/var/lib/iwd')
|
iwd_dir = LPath('/var/lib/iwd')
|
||||||
|
|
@ -811,7 +806,11 @@ class Installer:
|
||||||
self.pacman.strap('iwd')
|
self.pacman.strap('iwd')
|
||||||
self.enable_service('iwd')
|
self.enable_service('iwd')
|
||||||
|
|
||||||
self.systemd_resolved_stub_mode()
|
# Enable systemd-resolved by (forcefully) setting a symlink
|
||||||
|
# For further details see https://wiki.archlinux.org/title/Systemd-resolved#DNS
|
||||||
|
resolv_config_path = self.target / 'etc/resolv.conf'
|
||||||
|
resolv_config_path.unlink(missing_ok=True)
|
||||||
|
resolv_config_path.symlink_to('/run/systemd/resolve/stub-resolv.conf')
|
||||||
|
|
||||||
# Copy (if any) systemd-networkd config files
|
# Copy (if any) systemd-networkd config files
|
||||||
network_dir = LPath('/etc/systemd/network')
|
network_dir = LPath('/etc/systemd/network')
|
||||||
|
|
@ -1360,7 +1359,7 @@ class Installer:
|
||||||
boot_dir = boot_partition.mountpoint
|
boot_dir = boot_partition.mountpoint
|
||||||
|
|
||||||
add_options = [
|
add_options = [
|
||||||
f'--target={"arm64" if platform.machine() == "aarch64" else platform.machine()}-efi',
|
f'--target={platform.machine()}-efi',
|
||||||
f'--efi-directory={efi_partition.mountpoint}',
|
f'--efi-directory={efi_partition.mountpoint}',
|
||||||
*boot_dir_arg,
|
*boot_dir_arg,
|
||||||
'--bootloader-id=GRUB',
|
'--bootloader-id=GRUB',
|
||||||
|
|
@ -1481,6 +1480,7 @@ class Installer:
|
||||||
|
|
||||||
parent_dev_path = get_parent_device_path(efi_partition.safe_dev_path)
|
parent_dev_path = get_parent_device_path(efi_partition.safe_dev_path)
|
||||||
|
|
||||||
|
try:
|
||||||
efi_dir_path = self.target / efi_partition.mountpoint.relative_to('/') / 'EFI'
|
efi_dir_path = self.target / efi_partition.mountpoint.relative_to('/') / 'EFI'
|
||||||
efi_dir_path_target = efi_partition.mountpoint / 'EFI'
|
efi_dir_path_target = efi_partition.mountpoint / 'EFI'
|
||||||
if bootloader_removable:
|
if bootloader_removable:
|
||||||
|
|
@ -1494,19 +1494,14 @@ class Installer:
|
||||||
|
|
||||||
efi_dir_path.mkdir(parents=True, exist_ok=True)
|
efi_dir_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
efi_binaries: tuple[str, ...]
|
for file in ('BOOTIA32.EFI', 'BOOTX64.EFI'):
|
||||||
if platform.machine() == 'aarch64':
|
|
||||||
efi_binaries = ('BOOTAA64.EFI',)
|
|
||||||
else:
|
|
||||||
efi_binaries = ('BOOTIA32.EFI', 'BOOTX64.EFI')
|
|
||||||
|
|
||||||
try:
|
|
||||||
for file in efi_binaries:
|
|
||||||
(limine_path / file).copy_into(efi_dir_path)
|
(limine_path / file).copy_into(efi_dir_path)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise DiskError(f'Failed to install Limine in {self.target}{efi_partition.mountpoint}: {err}')
|
raise DiskError(f'Failed to install Limine in {self.target}{efi_partition.mountpoint}: {err}')
|
||||||
|
|
||||||
hook_command = ' && '.join(f'/usr/bin/cp /usr/share/limine/{file} {efi_dir_path_target}/' for file in efi_binaries)
|
hook_command = (
|
||||||
|
f'/usr/bin/cp /usr/share/limine/BOOTIA32.EFI {efi_dir_path_target}/ && /usr/bin/cp /usr/share/limine/BOOTX64.EFI {efi_dir_path_target}/'
|
||||||
|
)
|
||||||
|
|
||||||
if not bootloader_removable:
|
if not bootloader_removable:
|
||||||
# Create EFI boot menu entry for Limine.
|
# Create EFI boot menu entry for Limine.
|
||||||
|
|
@ -1517,7 +1512,7 @@ class Installer:
|
||||||
raise OSError(f'Could not open or read /sys/firmware/efi/fw_platform_size to determine EFI bitness: {err}')
|
raise OSError(f'Could not open or read /sys/firmware/efi/fw_platform_size to determine EFI bitness: {err}')
|
||||||
|
|
||||||
if efi_bitness == '64':
|
if efi_bitness == '64':
|
||||||
loader_path = f'\\EFI\\arch-limine\\{"BOOTAA64.EFI" if platform.machine() == "aarch64" else "BOOTX64.EFI"}'
|
loader_path = '\\EFI\\arch-limine\\BOOTX64.EFI'
|
||||||
elif efi_bitness == '32':
|
elif efi_bitness == '32':
|
||||||
loader_path = '\\EFI\\arch-limine\\BOOTIA32.EFI'
|
loader_path = '\\EFI\\arch-limine\\BOOTIA32.EFI'
|
||||||
else:
|
else:
|
||||||
|
|
@ -2136,12 +2131,13 @@ def accessibility_tools_in_use() -> bool:
|
||||||
|
|
||||||
def run_custom_user_commands(commands: list[str], installation: Installer) -> None:
|
def run_custom_user_commands(commands: list[str], installation: Installer) -> None:
|
||||||
for index, command in enumerate(commands):
|
for index, command in enumerate(commands):
|
||||||
script_path = LPath(f'/var/tmp/user-command.{index}.sh')
|
script_path = f'/var/tmp/user-command.{index}.sh'
|
||||||
chroot_path = installation.target / script_path.relative_to_root()
|
chroot_path = f'{installation.target}/{script_path}'
|
||||||
|
|
||||||
info(f'Executing custom command "{command}" ...')
|
info(f'Executing custom command "{command}" ...')
|
||||||
chroot_path.write_text(command)
|
with open(chroot_path, 'w') as user_script:
|
||||||
|
user_script.write(command)
|
||||||
|
|
||||||
SysCommand(f'arch-chroot -S {installation.target} bash {script_path}')
|
SysCommand(f'arch-chroot -S {installation.target} bash {script_path}')
|
||||||
|
|
||||||
chroot_path.unlink()
|
os.unlink(chroot_path)
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,14 @@ async def get_password(
|
||||||
def password_hint(value: str) -> InputInfo | None:
|
def password_hint(value: str) -> InputInfo | None:
|
||||||
if not value:
|
if not value:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
strength = PasswordStrength.strength(value)
|
strength = PasswordStrength.strength(value)
|
||||||
match strength:
|
if strength in (PasswordStrength.VERY_WEAK, PasswordStrength.WEAK):
|
||||||
case PasswordStrength.VERY_WEAK | PasswordStrength.WEAK:
|
|
||||||
return InputInfo(message=tr('Password strength: Weak'), msg_level=MsgLevelType.MsgError)
|
return InputInfo(message=tr('Password strength: Weak'), msg_level=MsgLevelType.MsgError)
|
||||||
case PasswordStrength.MODERATE:
|
elif strength == PasswordStrength.MODERATE:
|
||||||
return InputInfo(message=tr('Password strength: Moderate'), msg_level=MsgLevelType.MsgWarning)
|
return InputInfo(message=tr('Password strength: Moderate'), msg_level=MsgLevelType.MsgWarning)
|
||||||
case PasswordStrength.STRONG:
|
elif strength == PasswordStrength.STRONG:
|
||||||
return InputInfo(message=tr('Password strength: Strong'), msg_level=MsgLevelType.MsgInfo)
|
return InputInfo(message=tr('Password strength: Strong'), msg_level=MsgLevelType.MsgInfo)
|
||||||
|
return None
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
result = await Input(
|
result = await Input(
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@ class MirrorListHandler:
|
||||||
|
|
||||||
for attempt_nr in range(attempts):
|
for attempt_nr in range(attempts):
|
||||||
try:
|
try:
|
||||||
data = fetch_data_from_url(url)
|
mirrorlist = fetch_data_from_url(url)
|
||||||
self._status_mappings = self._parse_remote_mirror_list(data)
|
self._status_mappings = self._parse_remote_mirror_list(mirrorlist)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug(f'Error while fetching mirror list: {e}')
|
debug(f'Error while fetching mirror list: {e}')
|
||||||
|
|
@ -85,9 +85,9 @@ class MirrorListHandler:
|
||||||
# just return as-is without sorting?
|
# just return as-is without sorting?
|
||||||
return region_list
|
return region_list
|
||||||
|
|
||||||
def _parse_remote_mirror_list(self, data: bytes) -> dict[str, list[MirrorStatusEntryV3]]:
|
def _parse_remote_mirror_list(self, mirrorlist: str) -> dict[str, list[MirrorStatusEntryV3]]:
|
||||||
context = {'verbose': self.verbose}
|
context = {'verbose': self.verbose}
|
||||||
mirror_status = MirrorStatusListV3.model_validate_json(data, context=context)
|
mirror_status = MirrorStatusListV3.model_validate_json(mirrorlist, context=context)
|
||||||
|
|
||||||
sorting_placeholder: dict[str, list[MirrorStatusEntryV3]] = {}
|
sorting_placeholder: dict[str, list[MirrorStatusEntryV3]] = {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -805,11 +805,9 @@ class PartitionFlag(PartitionFlagDataMixin, Enum):
|
||||||
|
|
||||||
class PartitionGUID(Enum):
|
class PartitionGUID(Enum):
|
||||||
"""
|
"""
|
||||||
A list of Partition type GUIDs (lsblk -o+PARTTYPE) can be found here:
|
A list of Partition type GUIDs (lsblk -o+PARTTYPE) can be found here: https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
|
||||||
https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_type_GUIDs
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
LINUX_ROOT_AARCH64 = 'B921B045-1DF0-41C3-AF44-4C6F280D3FAE'
|
|
||||||
LINUX_ROOT_X86_64 = '4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709'
|
LINUX_ROOT_X86_64 = '4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -1620,7 +1618,7 @@ class LsblkInfo(BaseModel):
|
||||||
fsver: str | None
|
fsver: str | None
|
||||||
fsavail: int | None
|
fsavail: int | None
|
||||||
fsuse_percentage: str | None = Field(alias='fsuse%')
|
fsuse_percentage: str | None = Field(alias='fsuse%')
|
||||||
type: str
|
type: str | None # may be None for strange behavior with md devices
|
||||||
mountpoint: Path | None
|
mountpoint: Path | None
|
||||||
mountpoints: list[Path]
|
mountpoints: list[Path]
|
||||||
fsroots: list[Path]
|
fsroots: list[Path]
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,8 @@ class MirrorStatusEntryV3(BaseModel):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@field_validator('score', mode='before')
|
@field_validator('score', mode='before')
|
||||||
def validate_score(cls, value: Any) -> Any:
|
def validate_score(cls, value: float) -> int | None:
|
||||||
if isinstance(value, float):
|
if value is not None:
|
||||||
value = round(value)
|
value = round(value)
|
||||||
debug(f' score: {value}')
|
debug(f' score: {value}')
|
||||||
|
|
||||||
|
|
@ -127,8 +127,10 @@ class MirrorStatusListV3(BaseModel):
|
||||||
|
|
||||||
@model_validator(mode='before')
|
@model_validator(mode='before')
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_model(cls, data: Any) -> Any:
|
def check_model(
|
||||||
if isinstance(data, dict):
|
cls,
|
||||||
|
data: dict[str, int | datetime.datetime | list[MirrorStatusEntryV3]],
|
||||||
|
) -> dict[str, int | datetime.datetime | list[MirrorStatusEntryV3]]:
|
||||||
if data.get('version') == 3:
|
if data.get('version') == 3:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,4 +84,6 @@ def _configure_iwd_standalone(installation: Installer) -> None:
|
||||||
""")
|
""")
|
||||||
_ = wired_conf.write_text(wired_conf_content)
|
_ = wired_conf.write_text(wired_conf_content)
|
||||||
|
|
||||||
installation.systemd_resolved_stub_mode()
|
resolv = installation.target / 'etc/resolv.conf'
|
||||||
|
resolv.unlink(missing_ok=True)
|
||||||
|
resolv.symlink_to('/run/systemd/resolve/stub-resolv.conf')
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
from typing import assert_never, override
|
from typing import assert_never, override
|
||||||
|
|
||||||
from archinstall.lib.command import SysCommand
|
from archinstall.lib.command import SysCommand
|
||||||
|
|
@ -6,7 +7,6 @@ from archinstall.lib.exceptions import SysCallError
|
||||||
from archinstall.lib.log import debug
|
from archinstall.lib.log import debug
|
||||||
from archinstall.lib.models.network import WifiConfiguredNetwork, WifiNetwork
|
from archinstall.lib.models.network import WifiConfiguredNetwork, WifiNetwork
|
||||||
from archinstall.lib.network.wpa_supplicant import WpaSupplicantConfig
|
from archinstall.lib.network.wpa_supplicant import WpaSupplicantConfig
|
||||||
from archinstall.lib.networking import SYS_NET
|
|
||||||
from archinstall.lib.translationhandler import tr
|
from archinstall.lib.translationhandler import tr
|
||||||
from archinstall.tui.components import ConfirmationScreen, InputScreen, InstanceRunnable, LoadingScreen, NotifyScreen, TableSelectionScreen, tui
|
from archinstall.tui.components import ConfirmationScreen, InputScreen, InstanceRunnable, LoadingScreen, NotifyScreen, TableSelectionScreen, tui
|
||||||
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
|
||||||
|
|
@ -58,7 +58,7 @@ class WifiHandler(InstanceRunnable[bool]):
|
||||||
async def _enable_supplicant(self, wifi_iface: str) -> bool:
|
async def _enable_supplicant(self, wifi_iface: str) -> bool:
|
||||||
self._wpa_config.load_config()
|
self._wpa_config.load_config()
|
||||||
|
|
||||||
result = self._wpa_cli('status')
|
result = self._wpa_cli('status') # if it it's running it will blow up
|
||||||
|
|
||||||
if result.success:
|
if result.success:
|
||||||
debug('wpa_supplicant already running')
|
debug('wpa_supplicant already running')
|
||||||
|
|
@ -72,11 +72,7 @@ class WifiHandler(InstanceRunnable[bool]):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
SysCommand(f'wpa_supplicant -B -i {wifi_iface} -c {self._wpa_config.config_file}')
|
SysCommand(f'wpa_supplicant -B -i {wifi_iface} -c {self._wpa_config.config_file}')
|
||||||
except SysCallError as err:
|
result = self._wpa_cli('status') # if it it's running it will blow up
|
||||||
debug(f'failed to enable wpa_supplicant: {err}')
|
|
||||||
return False
|
|
||||||
|
|
||||||
result = self._wpa_cli('status')
|
|
||||||
|
|
||||||
if result.success:
|
if result.success:
|
||||||
debug('successfully enabled wpa_supplicant')
|
debug('successfully enabled wpa_supplicant')
|
||||||
|
|
@ -84,10 +80,16 @@ class WifiHandler(InstanceRunnable[bool]):
|
||||||
else:
|
else:
|
||||||
debug(f'failed to enable wpa_supplicant: {result.error}')
|
debug(f'failed to enable wpa_supplicant: {result.error}')
|
||||||
return False
|
return False
|
||||||
|
except SysCallError as err:
|
||||||
|
debug(f'failed to enable wpa_supplicant: {err}')
|
||||||
|
return False
|
||||||
|
|
||||||
def _find_wifi_interface(self) -> str | None:
|
def _find_wifi_interface(self) -> str | None:
|
||||||
for iface in SYS_NET.iterdir():
|
net_path = Path('/sys/class/net')
|
||||||
if (iface / 'wireless').is_dir():
|
|
||||||
|
for iface in net_path.iterdir():
|
||||||
|
maybe_wireless_path = net_path / iface / 'wireless'
|
||||||
|
if maybe_wireless_path.is_dir():
|
||||||
return iface.name
|
return iface.name
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
@ -195,12 +197,6 @@ class WifiHandler(InstanceRunnable[bool]):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = SysCommand(cmd).decode()
|
result = SysCommand(cmd).decode()
|
||||||
except SysCallError as err:
|
|
||||||
debug(f'error running wpa_cli command: {err}')
|
|
||||||
return WpaCliResult(
|
|
||||||
success=False,
|
|
||||||
error=f'Error running wpa_cli command: {err}',
|
|
||||||
)
|
|
||||||
|
|
||||||
if 'FAIL' in result:
|
if 'FAIL' in result:
|
||||||
debug(f'wpa_cli returned FAIL: {result}')
|
debug(f'wpa_cli returned FAIL: {result}')
|
||||||
|
|
@ -210,6 +206,12 @@ class WifiHandler(InstanceRunnable[bool]):
|
||||||
)
|
)
|
||||||
|
|
||||||
return WpaCliResult(success=True, response=result)
|
return WpaCliResult(success=True, response=result)
|
||||||
|
except SysCallError as err:
|
||||||
|
debug(f'error running wpa_cli command: {err}')
|
||||||
|
return WpaCliResult(
|
||||||
|
success=False,
|
||||||
|
error=f'Error running wpa_cli command: {err}',
|
||||||
|
)
|
||||||
|
|
||||||
def _find_network_id(self, ssid: str, iface: str) -> int | None:
|
def _find_network_id(self, ssid: str, iface: str) -> int | None:
|
||||||
result = self._wpa_cli('list_networks', iface)
|
result = self._wpa_cli('list_networks', iface)
|
||||||
|
|
@ -252,9 +254,6 @@ class WifiHandler(InstanceRunnable[bool]):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = self._wpa_cli('scan_results', iface)
|
result = self._wpa_cli('scan_results', iface)
|
||||||
except SysCallError as err:
|
|
||||||
debug('Unable to retrieve wifi results')
|
|
||||||
raise err
|
|
||||||
|
|
||||||
if not result.success:
|
if not result.success:
|
||||||
debug(f'Failed to retrieve scan results: {result.error}')
|
debug(f'Failed to retrieve scan results: {result.error}')
|
||||||
|
|
@ -267,3 +266,6 @@ class WifiHandler(InstanceRunnable[bool]):
|
||||||
networks = WifiNetwork.from_wpa(result.response)
|
networks = WifiNetwork.from_wpa(result.response)
|
||||||
|
|
||||||
return networks
|
return networks
|
||||||
|
except SysCallError as err:
|
||||||
|
debug('Unable to retrieve wifi results')
|
||||||
|
raise err
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@ import socket
|
||||||
import ssl
|
import ssl
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
|
||||||
from types import FrameType, TracebackType
|
from types import FrameType, TracebackType
|
||||||
from typing import Final, Self
|
from typing import Self
|
||||||
from urllib.error import URLError
|
from urllib.error import URLError
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
|
|
@ -17,8 +16,6 @@ from archinstall.lib.exceptions import DownloadTimeout, SysCallError
|
||||||
from archinstall.lib.log import debug, error, info
|
from archinstall.lib.log import debug, error, info
|
||||||
from archinstall.lib.pacman.pacman import Pacman
|
from archinstall.lib.pacman.pacman import Pacman
|
||||||
|
|
||||||
SYS_NET: Final = Path('/sys/class/net')
|
|
||||||
|
|
||||||
|
|
||||||
class DownloadTimer:
|
class DownloadTimer:
|
||||||
"""
|
"""
|
||||||
|
|
@ -108,14 +105,13 @@ def enrich_iface_types(interfaces: list[str]) -> dict[str, str]:
|
||||||
result = {}
|
result = {}
|
||||||
|
|
||||||
for iface in interfaces:
|
for iface in interfaces:
|
||||||
path = SYS_NET / iface
|
if os.path.isdir(f'/sys/class/net/{iface}/bridge/'):
|
||||||
if (path / 'bridge').is_dir():
|
|
||||||
result[iface] = 'BRIDGE'
|
result[iface] = 'BRIDGE'
|
||||||
elif (path / 'tun_flags').is_file():
|
elif os.path.isfile(f'/sys/class/net/{iface}/tun_flags'):
|
||||||
# ethtool -i {iface}
|
# ethtool -i {iface}
|
||||||
result[iface] = 'TUN/TAP'
|
result[iface] = 'TUN/TAP'
|
||||||
elif (path / 'device').is_dir():
|
elif os.path.isdir(f'/sys/class/net/{iface}/device'):
|
||||||
if (path / 'wireless').is_dir():
|
if os.path.isdir(f'/sys/class/net/{iface}/wireless/'):
|
||||||
result[iface] = 'WIRELESS'
|
result[iface] = 'WIRELESS'
|
||||||
else:
|
else:
|
||||||
result[iface] = 'PHYSICAL'
|
result[iface] = 'PHYSICAL'
|
||||||
|
|
@ -125,7 +121,7 @@ def enrich_iface_types(interfaces: list[str]) -> dict[str, str]:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def fetch_data_from_url(url: str, params: dict[str, str] | None = None, timeout: int = 30) -> bytes:
|
def fetch_data_from_url(url: str, params: dict[str, str] | None = None, timeout: int = 30) -> str:
|
||||||
ssl_context = ssl.create_default_context()
|
ssl_context = ssl.create_default_context()
|
||||||
ssl_context.check_hostname = False
|
ssl_context.check_hostname = False
|
||||||
ssl_context.verify_mode = ssl.CERT_NONE
|
ssl_context.verify_mode = ssl.CERT_NONE
|
||||||
|
|
@ -138,7 +134,8 @@ def fetch_data_from_url(url: str, params: dict[str, str] | None = None, timeout:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = urlopen(full_url, context=ssl_context, timeout=timeout)
|
response = urlopen(full_url, context=ssl_context, timeout=timeout)
|
||||||
return response.read()
|
data = response.read().decode('UTF-8')
|
||||||
|
return data
|
||||||
except URLError as e:
|
except URLError as e:
|
||||||
raise ValueError(f'Unable to fetch data from url: {url}\n{e}')
|
raise ValueError(f'Unable to fetch data from url: {url}\n{e}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -258,16 +258,18 @@ class ProfileHandler:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
data = fetch_data_from_url(url)
|
data = fetch_data_from_url(url)
|
||||||
except ValueError:
|
b_data = bytes(data, 'utf-8')
|
||||||
err = tr('Unable to fetch profile from specified url: {}').format(url)
|
|
||||||
error(err)
|
|
||||||
else:
|
|
||||||
with NamedTemporaryFile(delete=False, suffix='.py') as fp:
|
with NamedTemporaryFile(delete=False, suffix='.py') as fp:
|
||||||
fp.write(data)
|
fp.write(b_data)
|
||||||
filepath = Path(fp.name)
|
filepath = Path(fp.name)
|
||||||
|
|
||||||
profiles = self._process_profile_file(filepath)
|
profiles = self._process_profile_file(filepath)
|
||||||
self.remove_custom_profiles(profiles)
|
self.remove_custom_profiles(profiles)
|
||||||
|
self.add_custom_profiles(profiles)
|
||||||
|
except ValueError:
|
||||||
|
err = tr('Unable to fetch profile from specified url: {}').format(url)
|
||||||
|
error(err)
|
||||||
|
|
||||||
def _load_profile_class(self, module: ModuleType) -> list[Profile]:
|
def _load_profile_class(self, module: ModuleType) -> list[Profile]:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -82,14 +82,13 @@ class TranslationHandler:
|
||||||
if not running_from_iso():
|
if not running_from_iso():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
font_fd, font_path = tempfile.mkstemp(prefix='archinstall_font_')
|
font_fd, font_path = tempfile.mkstemp(prefix='archinstall_font_')
|
||||||
cmap_fd, cmap_path = tempfile.mkstemp(prefix='archinstall_cmap_')
|
cmap_fd, cmap_path = tempfile.mkstemp(prefix='archinstall_cmap_')
|
||||||
os.close(font_fd)
|
os.close(font_fd)
|
||||||
os.close(cmap_fd)
|
os.close(cmap_fd)
|
||||||
self._font_backup = Path(font_path)
|
self._font_backup = Path(font_path)
|
||||||
self._cmap_backup = Path(cmap_path)
|
self._cmap_backup = Path(cmap_path)
|
||||||
|
|
||||||
try:
|
|
||||||
SysCommand(['setfont', '-O', str(self._font_backup), '-om', str(self._cmap_backup)])
|
SysCommand(['setfont', '-O', str(self._font_backup), '-om', str(self._cmap_backup)])
|
||||||
except SysCallError as err:
|
except SysCallError as err:
|
||||||
debug(f'Failed to save console font: {err}')
|
debug(f'Failed to save console font: {err}')
|
||||||
|
|
@ -137,8 +136,6 @@ class TranslationHandler:
|
||||||
try:
|
try:
|
||||||
# get a translation for a specific language
|
# get a translation for a specific language
|
||||||
translation = gettext.translation('base', localedir=self._get_locales_dir(), languages=(abbr, lang))
|
translation = gettext.translation('base', localedir=self._get_locales_dir(), languages=(abbr, lang))
|
||||||
except FileNotFoundError as err:
|
|
||||||
raise FileNotFoundError(f"Could not locate language file for '{lang}': {err}")
|
|
||||||
|
|
||||||
# calculate the percentage of total translated text to total number of messages
|
# calculate the percentage of total translated text to total number of messages
|
||||||
if abbr == 'en':
|
if abbr == 'en':
|
||||||
|
|
@ -151,6 +148,8 @@ class TranslationHandler:
|
||||||
|
|
||||||
language = Language(abbr, lang, translation, percent, translated_lang, console_font)
|
language = Language(abbr, lang, translation, percent, translated_lang, console_font)
|
||||||
languages.append(language)
|
languages.append(language)
|
||||||
|
except FileNotFoundError as err:
|
||||||
|
raise FileNotFoundError(f"Could not locate language file for '{lang}': {err}")
|
||||||
|
|
||||||
return languages
|
return languages
|
||||||
|
|
||||||
|
|
@ -278,15 +277,6 @@ def tr(message: str) -> str:
|
||||||
return str(_DeferredTranslation(message))
|
return str(_DeferredTranslation(message))
|
||||||
|
|
||||||
|
|
||||||
def tr_noop(message: str) -> str:
|
|
||||||
"""Mark a string for xgettext extraction without translating it here.
|
|
||||||
|
|
||||||
Use for strings that are translated later from a variable, e.g.
|
|
||||||
binding descriptions passed through tr() at runtime.
|
|
||||||
"""
|
|
||||||
return message
|
|
||||||
|
|
||||||
|
|
||||||
builtins._ = _DeferredTranslation # type: ignore[attr-defined]
|
builtins._ = _DeferredTranslation # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,6 @@ if TYPE_CHECKING:
|
||||||
from _typeshed import DataclassInstance
|
from _typeshed import DataclassInstance
|
||||||
|
|
||||||
|
|
||||||
def _sentence_case(text: str) -> str:
|
|
||||||
# Only capitalize the first letter of the label. The source strings and
|
|
||||||
# their translations are already written with the correct casing for each
|
|
||||||
# language, so title-casing every word is wrong outside English (it turned
|
|
||||||
# "Ім'я хоста" into "Ім'Я Хоста" and "(NTP)" into "(Ntp)").
|
|
||||||
return text[:1].upper() + text[1:]
|
|
||||||
|
|
||||||
|
|
||||||
def as_key_value_pair(
|
def as_key_value_pair(
|
||||||
entries: dict[str, str | list[str] | bool],
|
entries: dict[str, str | list[str] | bool],
|
||||||
ignore_empty: bool = True,
|
ignore_empty: bool = True,
|
||||||
|
|
@ -41,11 +33,28 @@ def as_key_value_pair(
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
value = '\n '.join(str(val) for val in value)
|
value = '\n '.join(str(val) for val in value)
|
||||||
|
|
||||||
table.add_row(_sentence_case(label), f': {value}')
|
table.add_row(label.title(), f': {value}')
|
||||||
|
|
||||||
return table.stringify()
|
return table.stringify()
|
||||||
|
|
||||||
|
|
||||||
|
def as_columns(entries: list[str], cols: int) -> str:
|
||||||
|
"""
|
||||||
|
Will format a list into a given number of columns
|
||||||
|
"""
|
||||||
|
chunks: list[list[str]] = []
|
||||||
|
output = ''
|
||||||
|
|
||||||
|
for i in range(0, len(entries), cols):
|
||||||
|
chunks.append(entries[i : i + cols])
|
||||||
|
|
||||||
|
for row in chunks:
|
||||||
|
out_fmt = '{: <30} ' * len(row)
|
||||||
|
output += out_fmt.format(*row) + '\n'
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
def _get_values(
|
def _get_values(
|
||||||
o: DataclassInstance,
|
o: DataclassInstance,
|
||||||
class_formatter: str | Callable | None = None, # type: ignore[type-arg] # pyright: ignore[reportMissingTypeArgument]
|
class_formatter: str | Callable | None = None, # type: ignore[type-arg] # pyright: ignore[reportMissingTypeArgument]
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import secrets
|
import secrets
|
||||||
import string
|
import string
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from archinstall.lib.pathnames import ARCHISO_MOUNTPOINT
|
from archinstall.lib.pathnames import ARCHISO_MOUNTPOINT
|
||||||
|
from archinstall.lib.utils.format import as_columns
|
||||||
|
|
||||||
|
|
||||||
def timestamp() -> str:
|
def timestamp() -> str:
|
||||||
|
|
@ -23,3 +25,29 @@ def running_from_iso() -> bool:
|
||||||
def generate_password(length: int = 64) -> str:
|
def generate_password(length: int = 64) -> str:
|
||||||
haystack = string.printable # digits, ascii_letters, punctuation (!"#$[] etc) and whitespace
|
haystack = string.printable # digits, ascii_letters, punctuation (!"#$[] etc) and whitespace
|
||||||
return ''.join(secrets.choice(haystack) for _ in range(length))
|
return ''.join(secrets.choice(haystack) for _ in range(length))
|
||||||
|
|
||||||
|
|
||||||
|
def format_cols(items: list[str], header: str | None = None) -> str:
|
||||||
|
if header:
|
||||||
|
text = f'{header}:\n'
|
||||||
|
else:
|
||||||
|
text = ''
|
||||||
|
|
||||||
|
nr_items = len(items)
|
||||||
|
if nr_items <= 4:
|
||||||
|
col = 1
|
||||||
|
elif nr_items <= 8:
|
||||||
|
col = 2
|
||||||
|
elif nr_items <= 12:
|
||||||
|
col = 3
|
||||||
|
else:
|
||||||
|
col = 4
|
||||||
|
|
||||||
|
text += as_columns(items, col)
|
||||||
|
# remove whitespaces on each row
|
||||||
|
text = '\n'.join(t.strip() for t in text.split('\n'))
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_path(path: Path) -> bool:
|
||||||
|
return path.exists() and path.is_dir()
|
||||||
|
|
|
||||||
|
|
@ -58,17 +58,3 @@ msgstr "Wollen sie wirklich abbrechen?"
|
||||||
After the translations have been written, run the script once more `./locales_generator.sh <lang_abbr>` and it will auto-generate the `base.mo` file with the included translations.
|
After the translations have been written, run the script once more `./locales_generator.sh <lang_abbr>` and it will auto-generate the `base.mo` file with the included translations.
|
||||||
|
|
||||||
After that you're all ready to go and enjoy Archinstall in the new language :)
|
After that you're all ready to go and enjoy Archinstall in the new language :)
|
||||||
|
|
||||||
## Key binding descriptions
|
|
||||||
|
|
||||||
`base.po` also contains the key binding descriptions shown in the footer and the help panel, for example `Scroll Up` or `Toggle option`. Translate them like any other string, but keep them short so they still fit in the footer.
|
|
||||||
|
|
||||||
Most of them come from the `textual` library, where `xgettext` cannot see them, so they are listed with `tr_noop` in `archinstall/tui/binding_descriptions.py`, a module that exists only to make them extractable and is never imported at runtime. Archinstall's own descriptions are taken from the third positional argument of `Binding()`, so that argument must never be passed as a keyword.
|
|
||||||
|
|
||||||
The list has to be verified after a `textual` upgrade, or after a new textual widget is introduced in `archinstall/tui/components.py` (the widget also has to be added to `TEXTUAL_CLASSES` in the checker). Nothing in CI checks it, so run the following from the repository root
|
|
||||||
|
|
||||||
```
|
|
||||||
python3 test_tooling/check_binding_descriptions.py
|
|
||||||
```
|
|
||||||
|
|
||||||
It prints every missing or stale entry, and an `OK` line when the list matches the installed `textual`. Fix `binding_descriptions.py` until it passes, then run `./locales_generator.sh <lang_abbr>` again.
|
|
||||||
|
|
|
||||||
|
|
@ -344,10 +344,6 @@ msgstr ""
|
||||||
msgid "Iteration time"
|
msgid "Iteration time"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"No disks were detected. A disk is required to be able to install Arch Linux"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Select disks for the installation"
|
msgid "Select disks for the installation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
@ -1224,192 +1220,12 @@ msgstr ""
|
||||||
msgid "Starting device modifications in "
|
msgid "Starting device modifications in "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Bottom"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Copy selected text"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Cursor down"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Cursor left"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Cursor right"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Cursor up"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Cut selected text"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Delete all to the left"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Delete all to the right"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Delete character left"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Delete character right"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Delete left to start of word"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Delete right to start of word"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Down"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "End"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "First"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Focus Next"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Focus Previous"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Go to end"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Go to start"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Home"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Last"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Move cursor left"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Move cursor left a word"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Move cursor left a word and select"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Move cursor left and select"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Move cursor right a word"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Move cursor right a word and select"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Move cursor right and select"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Move cursor right or accept the completion suggestion"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Page Down"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Page Left"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Page Right"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Page Up"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Page down"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Page up"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Paste text from the clipboard"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Press button"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Quit"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Scroll Down"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Scroll End"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Scroll Home"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Scroll Left"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Scroll Right"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Scroll Up"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Select"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Select all"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Select line end"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Select line start"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Submit"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Toggle option"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Top"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Up"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Reset"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Search"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Toggle"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Confirm"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Focus right"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Focus left"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Ok"
|
msgid "Ok"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Input cannot be empty"
|
msgid "Input cannot be empty"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Show/Hide help"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Yes"
|
msgid "Yes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1423,7 +1423,7 @@ msgid "Yes"
|
||||||
msgstr "ναι"
|
msgstr "ναι"
|
||||||
|
|
||||||
msgid "No"
|
msgid "No"
|
||||||
msgstr "όχι"
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Archinstall help"
|
msgid "Archinstall help"
|
||||||
|
|
@ -1434,7 +1434,7 @@ msgid " (default)"
|
||||||
msgstr "(προκαθορισμένο)"
|
msgstr "(προκαθορισμένο)"
|
||||||
|
|
||||||
msgid "Press Ctrl+h for help"
|
msgid "Press Ctrl+h for help"
|
||||||
msgstr "Πατήστε Ctrl+h για βοήθεια"
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Choose an option to give Sway access to your hardware"
|
msgid "Choose an option to give Sway access to your hardware"
|
||||||
|
|
@ -1621,7 +1621,7 @@ msgid "Select execution mode"
|
||||||
msgstr "Επιλέξτε μία ενέργεια για '{}'"
|
msgstr "Επιλέξτε μία ενέργεια για '{}'"
|
||||||
|
|
||||||
msgid "Press ? for help"
|
msgid "Press ? for help"
|
||||||
msgstr "Πατήστε ? για βοήθεια"
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Choose an option to give Hyprland access to your hardware"
|
msgid "Choose an option to give Hyprland access to your hardware"
|
||||||
|
|
@ -1754,31 +1754,31 @@ msgid "Only ASCII characters are supported"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Show help"
|
msgid "Show help"
|
||||||
msgstr "Εμφάνιση βοήθειας"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Exit help"
|
msgid "Exit help"
|
||||||
msgstr "Έξοδος από τη βοήθεια"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Preview scroll up"
|
msgid "Preview scroll up"
|
||||||
msgstr "Κύλιση προεπισκόπησης πάνω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Preview scroll down"
|
msgid "Preview scroll down"
|
||||||
msgstr "Κύλιση προεπισκόπησης κάτω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Move up"
|
msgid "Move up"
|
||||||
msgstr "Μετακίνηση πάνω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Move down"
|
msgid "Move down"
|
||||||
msgstr "Μετακίνηση κάτω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Move right"
|
msgid "Move right"
|
||||||
msgstr "Μετακίνηση δεξιά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Move left"
|
msgid "Move left"
|
||||||
msgstr "Μετακίνηση αριστερά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Jump to entry"
|
msgid "Jump to entry"
|
||||||
msgstr "Μετάβαση σε καταχώρηση"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Skip selection (if available)"
|
msgid "Skip selection (if available)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -1795,20 +1795,20 @@ msgid "Select on multi select"
|
||||||
msgstr "Επιλέξτε μία ζώνη ώρας"
|
msgstr "Επιλέξτε μία ζώνη ώρας"
|
||||||
|
|
||||||
msgid "Reset"
|
msgid "Reset"
|
||||||
msgstr "Επαναφορά"
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Skip selection menu"
|
msgid "Skip selection menu"
|
||||||
msgstr "Επιλέξτε μία ενέργεια για '{}'"
|
msgstr "Επιλέξτε μία ενέργεια για '{}'"
|
||||||
|
|
||||||
msgid "Start search mode"
|
msgid "Start search mode"
|
||||||
msgstr "Έναρξη λειτουργίας αναζήτησης"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Exit search mode"
|
msgid "Exit search mode"
|
||||||
msgstr "Έξοδος από τη λειτουργία αναζήτησης"
|
msgstr ""
|
||||||
|
|
||||||
msgid "General"
|
msgid "General"
|
||||||
msgstr "Γενικά"
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Navigation"
|
msgid "Navigation"
|
||||||
|
|
@ -1819,115 +1819,115 @@ msgid "Selection"
|
||||||
msgstr "Επιλέξτε διεπαφή προς προσθήκη"
|
msgstr "Επιλέξτε διεπαφή προς προσθήκη"
|
||||||
|
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr "Αναζήτηση"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Down"
|
msgid "Down"
|
||||||
msgstr "Κάτω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Up"
|
msgid "Up"
|
||||||
msgstr "Πάνω"
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Confirm"
|
msgid "Confirm"
|
||||||
msgstr "Επιβεβαίωση και έξοδος"
|
msgstr "Επιβεβαίωση και έξοδος"
|
||||||
|
|
||||||
msgid "Focus right"
|
msgid "Focus right"
|
||||||
msgstr "Εστίαση δεξιά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Focus left"
|
msgid "Focus left"
|
||||||
msgstr "Εστίαση αριστερά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Toggle"
|
msgid "Toggle"
|
||||||
msgstr "Εναλλαγή"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Show/Hide help"
|
msgid "Show/Hide help"
|
||||||
msgstr "Εμφάνιση/Απόκρυψη βοήθειας"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Quit"
|
msgid "Quit"
|
||||||
msgstr "Έξοδος"
|
msgstr ""
|
||||||
|
|
||||||
msgid "First"
|
msgid "First"
|
||||||
msgstr "Πρώτο"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Last"
|
msgid "Last"
|
||||||
msgstr "Τελευταίο"
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr "Επιλέξτε διεπαφή προς προσθήκη"
|
msgstr "Επιλέξτε διεπαφή προς προσθήκη"
|
||||||
|
|
||||||
msgid "Page Up"
|
msgid "Page Up"
|
||||||
msgstr "Σελίδα πάνω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Page Down"
|
msgid "Page Down"
|
||||||
msgstr "Σελίδα κάτω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Page up"
|
msgid "Page up"
|
||||||
msgstr "Σελίδα πάνω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Page down"
|
msgid "Page down"
|
||||||
msgstr "Σελίδα κάτω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Page Left"
|
msgid "Page Left"
|
||||||
msgstr "Σελίδα αριστερά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Page Right"
|
msgid "Page Right"
|
||||||
msgstr "Σελίδα δεξιά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cursor up"
|
msgid "Cursor up"
|
||||||
msgstr "Δρομέας πάνω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cursor down"
|
msgid "Cursor down"
|
||||||
msgstr "Δρομέας κάτω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cursor right"
|
msgid "Cursor right"
|
||||||
msgstr "Δρομέας δεξιά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Cursor left"
|
msgid "Cursor left"
|
||||||
msgstr "Δρομέας αριστερά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Top"
|
msgid "Top"
|
||||||
msgstr "Κορυφή"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Bottom"
|
msgid "Bottom"
|
||||||
msgstr "Βάση"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Home"
|
msgid "Home"
|
||||||
msgstr "Αρχή"
|
msgstr ""
|
||||||
|
|
||||||
msgid "End"
|
msgid "End"
|
||||||
msgstr "Τέλος"
|
msgstr ""
|
||||||
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Toggle option"
|
msgid "Toggle option"
|
||||||
msgstr "Επιλογές υποόγκου"
|
msgstr "Επιλογές υποόγκου"
|
||||||
|
|
||||||
msgid "Scroll Up"
|
msgid "Scroll Up"
|
||||||
msgstr "Κύλιση πάνω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Scroll Down"
|
msgid "Scroll Down"
|
||||||
msgstr "Κύλιση κάτω"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Scroll Left"
|
msgid "Scroll Left"
|
||||||
msgstr "Κύλιση αριστερά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Scroll Right"
|
msgid "Scroll Right"
|
||||||
msgstr "Κύλιση δεξιά"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Scroll Home"
|
msgid "Scroll Home"
|
||||||
msgstr "Κύλιση στην αρχή"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Scroll End"
|
msgid "Scroll End"
|
||||||
msgstr "Κύλιση στο τέλος"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Focus Next"
|
msgid "Focus Next"
|
||||||
msgstr "Εστίαση στο επόμενο"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Focus Previous"
|
msgid "Focus Previous"
|
||||||
msgstr "Εστίαση στο προηγούμενο"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Copy selected text"
|
msgid "Copy selected text"
|
||||||
msgstr "Αντιγραφή επιλεγμένου κειμένου"
|
msgstr ""
|
||||||
|
|
||||||
msgid "labwc needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
|
msgid "labwc needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
@ -2115,7 +2115,7 @@ msgid "Enter wifi password"
|
||||||
msgstr "Εισάγετε κωδικό: "
|
msgstr "Εισάγετε κωδικό: "
|
||||||
|
|
||||||
msgid "Ok"
|
msgid "Ok"
|
||||||
msgstr "Εντάξει"
|
msgstr ""
|
||||||
|
|
||||||
msgid "Removable"
|
msgid "Removable"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -221,27 +221,12 @@ msgstr "Imágenes del núcleo unificadas"
|
||||||
msgid "Install to removable location"
|
msgid "Install to removable location"
|
||||||
msgstr "Instalar a ubicación desmontable"
|
msgstr "Instalar a ubicación desmontable"
|
||||||
|
|
||||||
msgid "Plymouth"
|
|
||||||
msgstr "Plymouth"
|
|
||||||
|
|
||||||
msgid "Will install to /EFI/BOOT/ (removable location, safe default)"
|
msgid "Will install to /EFI/BOOT/ (removable location, safe default)"
|
||||||
msgstr "Instalará a /EFI/BOOT/ (ubicación desmontable, segura por defecto)"
|
msgstr "Instalará a /EFI/BOOT/ (ubicación desmontable, segura por defecto)"
|
||||||
|
|
||||||
msgid "Will install to custom location with NVRAM entry"
|
msgid "Will install to custom location with NVRAM entry"
|
||||||
msgstr "Instalará a ubicación personalizada con entrada NVRAM"
|
msgstr "Instalará a ubicación personalizada con entrada NVRAM"
|
||||||
|
|
||||||
msgid "Plymouth adds a cosmetic boot splash but can cause boot problems on some setups:"
|
|
||||||
msgstr "Plymouth añade una pantalla de inicio cosmética, pero puede causar problemas de arranque en algunas configuraciones:"
|
|
||||||
|
|
||||||
msgid "black screen with the NVIDIA driver"
|
|
||||||
msgstr "pantalla negra con el controlador NVIDIA"
|
|
||||||
|
|
||||||
msgid "hidden password prompt with disk encryption (LUKS)"
|
|
||||||
msgstr "solicitud de contraseña oculta con cifrado de disco (LUKS)"
|
|
||||||
|
|
||||||
msgid "Would you like to enable it?"
|
|
||||||
msgstr "¿Desea habilitarlo?"
|
|
||||||
|
|
||||||
msgid "Would you like to use unified kernel images?"
|
msgid "Would you like to use unified kernel images?"
|
||||||
msgstr "¿Le gustaría utilizar imágenes del núcleo unificadas?"
|
msgstr "¿Le gustaría utilizar imágenes del núcleo unificadas?"
|
||||||
|
|
||||||
|
|
@ -269,9 +254,6 @@ msgstr "Seleccione el gestor de arranque que desea instalar"
|
||||||
msgid "UEFI is not detected and some options are disabled"
|
msgid "UEFI is not detected and some options are disabled"
|
||||||
msgstr "No se detecta UEFI y algunas opciones están desactivadas"
|
msgstr "No se detecta UEFI y algunas opciones están desactivadas"
|
||||||
|
|
||||||
msgid "Select Plymouth theme"
|
|
||||||
msgstr "Seleccionar tema Plymouth"
|
|
||||||
|
|
||||||
msgid "The specified configuration will be applied"
|
msgid "The specified configuration will be applied"
|
||||||
msgstr "Se aplicará la configuración especificada"
|
msgstr "Se aplicará la configuración especificada"
|
||||||
|
|
||||||
|
|
@ -281,6 +263,9 @@ msgstr "¿Quiere continuar?"
|
||||||
msgid "Configuration preview"
|
msgid "Configuration preview"
|
||||||
msgstr "Vista previa de la configuración"
|
msgstr "Vista previa de la configuración"
|
||||||
|
|
||||||
|
msgid "Warning: no network configuration selected. Network will need to be set up manually on the installed system."
|
||||||
|
msgstr "Advertencia: no se ha seleccionado ninguna configuración de red. La red deberá configurarse manualmente en el sistema instalado."
|
||||||
|
|
||||||
msgid "No configuration"
|
msgid "No configuration"
|
||||||
msgstr "Sin configuración"
|
msgstr "Sin configuración"
|
||||||
|
|
||||||
|
|
@ -347,9 +332,6 @@ msgstr "Contraseña"
|
||||||
msgid "Iteration time"
|
msgid "Iteration time"
|
||||||
msgstr "Tiempo de iteración"
|
msgstr "Tiempo de iteración"
|
||||||
|
|
||||||
msgid "No disks were detected. A disk is required to be able to install Arch Linux"
|
|
||||||
msgstr "No se detectaron discos. Se requiere un disco para poder instalar Arch Linux"
|
|
||||||
|
|
||||||
msgid "Select disks for the installation"
|
msgid "Select disks for the installation"
|
||||||
msgstr "Seleccione discos para la instalación"
|
msgstr "Seleccione discos para la instalación"
|
||||||
|
|
||||||
|
|
@ -645,21 +627,16 @@ msgstr "Color"
|
||||||
msgid "Kernel"
|
msgid "Kernel"
|
||||||
msgstr "Núcleo"
|
msgstr "Núcleo"
|
||||||
|
|
||||||
msgid "No network configuration selected. Network will need to be set up manually on the installed system."
|
msgid "Missing configurations:\n"
|
||||||
msgstr "No se ha seleccionado ninguna configuración de red. La red deberá configurarse manualmente en el sistema instalado."
|
msgstr "Configuraciones que faltan:\n"
|
||||||
|
|
||||||
msgid "Missing configurations:"
|
#, python-brace-format
|
||||||
msgstr "Configuraciones faltantes:"
|
msgid "Invalid configuration: {}"
|
||||||
|
msgstr "Configuración no válida: {}"
|
||||||
msgid "Invalid configuration:"
|
|
||||||
msgstr "Configuración no válida:"
|
|
||||||
|
|
||||||
msgid "Ready to install"
|
msgid "Ready to install"
|
||||||
msgstr "Listo para instalar"
|
msgstr "Listo para instalar"
|
||||||
|
|
||||||
msgid "Warnings:"
|
|
||||||
msgstr "Advertencias:"
|
|
||||||
|
|
||||||
msgid "Profiles"
|
msgid "Profiles"
|
||||||
msgstr "Perfiles"
|
msgstr "Perfiles"
|
||||||
|
|
||||||
|
|
@ -881,10 +858,6 @@ msgstr "UKI activado"
|
||||||
msgid "Removable"
|
msgid "Removable"
|
||||||
msgstr "Desmontable"
|
msgstr "Desmontable"
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Plymouth \"{}\""
|
|
||||||
msgstr "Plymouth «{}»"
|
|
||||||
|
|
||||||
msgid "Use a best-effort default partition layout"
|
msgid "Use a best-effort default partition layout"
|
||||||
msgstr "Utlizar un diseño de partición predeterminado de mejor esfuerzo"
|
msgstr "Utlizar un diseño de partición predeterminado de mejor esfuerzo"
|
||||||
|
|
||||||
|
|
@ -992,8 +965,8 @@ msgstr "Grupo de paquetes:"
|
||||||
msgid "Color enabled"
|
msgid "Color enabled"
|
||||||
msgstr "Color activado"
|
msgstr "Color activado"
|
||||||
|
|
||||||
#, fuzzy, python-brace-format
|
#, python-brace-format
|
||||||
msgid "{} graphics driver"
|
msgid "{} grphics driver"
|
||||||
msgstr "{} controlador de gráficos"
|
msgstr "{} controlador de gráficos"
|
||||||
|
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
|
|
@ -1059,15 +1032,15 @@ msgstr "¿Desea conectarse al Wi-Fi?"
|
||||||
msgid "No wifi interface found"
|
msgid "No wifi interface found"
|
||||||
msgstr "No se encontró ninguna interfaz Wi-Fi"
|
msgstr "No se encontró ninguna interfaz Wi-Fi"
|
||||||
|
|
||||||
msgid "No wifi networks found"
|
|
||||||
msgstr "No se encontró redes Wi-Fi"
|
|
||||||
|
|
||||||
msgid "Select wifi network to connect to"
|
msgid "Select wifi network to connect to"
|
||||||
msgstr "Seleccione una red Wi-Fi para conectarse"
|
msgstr "Seleccione una red Wi-Fi para conectarse"
|
||||||
|
|
||||||
msgid "Scanning wifi networks..."
|
msgid "Scanning wifi networks..."
|
||||||
msgstr "Buscando redes Wi-Fi..."
|
msgstr "Buscando redes Wi-Fi..."
|
||||||
|
|
||||||
|
msgid "No wifi networks found"
|
||||||
|
msgstr "No se encontró redes Wi-Fi"
|
||||||
|
|
||||||
msgid "Failed setting up wifi"
|
msgid "Failed setting up wifi"
|
||||||
msgstr "Error al configurar el Wi-Fi"
|
msgstr "Error al configurar el Wi-Fi"
|
||||||
|
|
||||||
|
|
@ -1635,6 +1608,9 @@ msgstr " (predeterminado)"
|
||||||
#~ msgid "Subvolume name :"
|
#~ msgid "Subvolume name :"
|
||||||
#~ msgstr "Nombre del subvolumen :"
|
#~ msgstr "Nombre del subvolumen :"
|
||||||
|
|
||||||
|
#~ msgid "Select a mount point :"
|
||||||
|
#~ msgstr "Seleccione un punto de montaje :"
|
||||||
|
|
||||||
#~ msgid "Select the desired subvolume options "
|
#~ msgid "Select the desired subvolume options "
|
||||||
#~ msgstr "Seleccione las opciones de subvolumen deseadas "
|
#~ msgstr "Seleccione las opciones de subvolumen deseadas "
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
|
@ -230,18 +230,6 @@ msgstr "/EFI/BOOT/(リムーバブルな場所、安全なデフォルト)
|
||||||
msgid "Will install to custom location with NVRAM entry"
|
msgid "Will install to custom location with NVRAM entry"
|
||||||
msgstr "NVRAM エントリーを使用してカスタムした場所にインストール"
|
msgstr "NVRAM エントリーを使用してカスタムした場所にインストール"
|
||||||
|
|
||||||
msgid "Plymouth adds a cosmetic boot splash but can cause boot problems on some setups:"
|
|
||||||
msgstr "Plymouth は表面上のブートスプラッシュを追加しますが、構成によっては起動に問題が生じる可能性があります:"
|
|
||||||
|
|
||||||
msgid "black screen with the NVIDIA driver"
|
|
||||||
msgstr "NVIDIA ドライバーでのブラックスクリーン"
|
|
||||||
|
|
||||||
msgid "hidden password prompt with disk encryption (LUKS)"
|
|
||||||
msgstr "ディスク暗号化(LUKS)使用時の非表示パスワードプロンプト"
|
|
||||||
|
|
||||||
msgid "Would you like to enable it?"
|
|
||||||
msgstr "有効にしますか?"
|
|
||||||
|
|
||||||
msgid "Would you like to use unified kernel images?"
|
msgid "Would you like to use unified kernel images?"
|
||||||
msgstr "Unified カーネルイメージを使用しますか?"
|
msgstr "Unified カーネルイメージを使用しますか?"
|
||||||
|
|
||||||
|
|
@ -347,9 +335,6 @@ msgstr "パスワード"
|
||||||
msgid "Iteration time"
|
msgid "Iteration time"
|
||||||
msgstr "イテレーション時間"
|
msgstr "イテレーション時間"
|
||||||
|
|
||||||
msgid "No disks were detected. A disk is required to be able to install Arch Linux"
|
|
||||||
msgstr "ディスクが検出されませんでした。Arch Linux をインストールするにはディスクが必要です"
|
|
||||||
|
|
||||||
msgid "Select disks for the installation"
|
msgid "Select disks for the installation"
|
||||||
msgstr "インストール用のディスクを選択"
|
msgstr "インストール用のディスクを選択"
|
||||||
|
|
||||||
|
|
@ -1189,197 +1174,17 @@ msgstr "新しいバージョンが利用可能"
|
||||||
msgid "Starting device modifications in "
|
msgid "Starting device modifications in "
|
||||||
msgstr "次の場所でデバイスの変更を開始しています "
|
msgstr "次の場所でデバイスの変更を開始しています "
|
||||||
|
|
||||||
msgid "Bottom"
|
|
||||||
msgstr "一番下へ"
|
|
||||||
|
|
||||||
msgid "Copy selected text"
|
|
||||||
msgstr "選択したテキストをコピー"
|
|
||||||
|
|
||||||
msgid "Cursor down"
|
|
||||||
msgstr "カースルを下へ"
|
|
||||||
|
|
||||||
msgid "Cursor left"
|
|
||||||
msgstr "カーソルを左へ"
|
|
||||||
|
|
||||||
msgid "Cursor right"
|
|
||||||
msgstr "カーソルを右へ"
|
|
||||||
|
|
||||||
msgid "Cursor up"
|
|
||||||
msgstr "カーソルを上へ"
|
|
||||||
|
|
||||||
msgid "Cut selected text"
|
|
||||||
msgstr "選択したテキストを切り取る"
|
|
||||||
|
|
||||||
msgid "Delete all to the left"
|
|
||||||
msgstr "左側をすべて削除"
|
|
||||||
|
|
||||||
msgid "Delete all to the right"
|
|
||||||
msgstr "右側をすべて削除"
|
|
||||||
|
|
||||||
msgid "Delete character left"
|
|
||||||
msgstr "左の文字を削除"
|
|
||||||
|
|
||||||
msgid "Delete character right"
|
|
||||||
msgstr "右の文字を削除"
|
|
||||||
|
|
||||||
msgid "Delete left to start of word"
|
|
||||||
msgstr "単語の先頭まで左側に削除"
|
|
||||||
|
|
||||||
msgid "Delete right to start of word"
|
|
||||||
msgstr "単語の先頭まで右側に削除"
|
|
||||||
|
|
||||||
msgid "Down"
|
|
||||||
msgstr "下へ"
|
|
||||||
|
|
||||||
msgid "End"
|
|
||||||
msgstr "最後へ"
|
|
||||||
|
|
||||||
msgid "First"
|
|
||||||
msgstr "最初"
|
|
||||||
|
|
||||||
msgid "Focus Next"
|
|
||||||
msgstr "次にフォーカス"
|
|
||||||
|
|
||||||
msgid "Focus Previous"
|
|
||||||
msgstr "前にフォーカス"
|
|
||||||
|
|
||||||
msgid "Go to end"
|
|
||||||
msgstr "末尾へ移動"
|
|
||||||
|
|
||||||
msgid "Go to start"
|
|
||||||
msgstr "先頭へ移動"
|
|
||||||
|
|
||||||
msgid "Home"
|
|
||||||
msgstr "ホーム"
|
|
||||||
|
|
||||||
msgid "Last"
|
|
||||||
msgstr "最後"
|
|
||||||
|
|
||||||
msgid "Move cursor left"
|
|
||||||
msgstr "カーソルを左に移動"
|
|
||||||
|
|
||||||
msgid "Move cursor left a word"
|
|
||||||
msgstr "カーソルを1語左に移動"
|
|
||||||
|
|
||||||
msgid "Move cursor left a word and select"
|
|
||||||
msgstr "カーソルを1語左に移動して選択"
|
|
||||||
|
|
||||||
msgid "Move cursor left and select"
|
|
||||||
msgstr "カーソルを左に移動して選択"
|
|
||||||
|
|
||||||
msgid "Move cursor right a word"
|
|
||||||
msgstr "カーソルを1語右に移動"
|
|
||||||
|
|
||||||
msgid "Move cursor right a word and select"
|
|
||||||
msgstr "カーソルを右に1語移動して選択"
|
|
||||||
|
|
||||||
msgid "Move cursor right and select"
|
|
||||||
msgstr "カーソルを右に移動して選択"
|
|
||||||
|
|
||||||
msgid "Move cursor right or accept the completion suggestion"
|
|
||||||
msgstr "カーソルを右に移動するか補完候補を受け入れる"
|
|
||||||
|
|
||||||
msgid "Page Down"
|
|
||||||
msgstr "下のページへ"
|
|
||||||
|
|
||||||
msgid "Page Left"
|
|
||||||
msgstr "左のページへ"
|
|
||||||
|
|
||||||
msgid "Page Right"
|
|
||||||
msgstr "右のページへ"
|
|
||||||
|
|
||||||
msgid "Page Up"
|
|
||||||
msgstr "上のページへ"
|
|
||||||
|
|
||||||
msgid "Page down"
|
|
||||||
msgstr "下のページへ"
|
|
||||||
|
|
||||||
msgid "Page up"
|
|
||||||
msgstr "上のページへ"
|
|
||||||
|
|
||||||
msgid "Paste text from the clipboard"
|
|
||||||
msgstr "クリップボードからテキストを貼り付ける"
|
|
||||||
|
|
||||||
msgid "Press button"
|
|
||||||
msgstr "ボタンを押す"
|
|
||||||
|
|
||||||
msgid "Quit"
|
|
||||||
msgstr "終了"
|
|
||||||
|
|
||||||
msgid "Scroll Down"
|
|
||||||
msgstr "下にスクロール"
|
|
||||||
|
|
||||||
msgid "Scroll End"
|
|
||||||
msgstr "一番下にスクロール"
|
|
||||||
|
|
||||||
msgid "Scroll Home"
|
|
||||||
msgstr "ホームにスクロール"
|
|
||||||
|
|
||||||
msgid "Scroll Left"
|
|
||||||
msgstr "左にスクロール"
|
|
||||||
|
|
||||||
msgid "Scroll Right"
|
|
||||||
msgstr "右にスクロール"
|
|
||||||
|
|
||||||
msgid "Scroll Up"
|
|
||||||
msgstr "上にスクロール"
|
|
||||||
|
|
||||||
msgid "Select"
|
|
||||||
msgstr "選択"
|
|
||||||
|
|
||||||
msgid "Select all"
|
|
||||||
msgstr "すべて選択"
|
|
||||||
|
|
||||||
msgid "Select line end"
|
|
||||||
msgstr "行末を選択"
|
|
||||||
|
|
||||||
msgid "Select line start"
|
|
||||||
msgstr "行頭を選択"
|
|
||||||
|
|
||||||
msgid "Submit"
|
|
||||||
msgstr "決定"
|
|
||||||
|
|
||||||
msgid "Toggle option"
|
|
||||||
msgstr "オプションを切り替え"
|
|
||||||
|
|
||||||
msgid "Top"
|
|
||||||
msgstr "一番上へ"
|
|
||||||
|
|
||||||
msgid "Up"
|
|
||||||
msgstr "上へ"
|
|
||||||
|
|
||||||
msgid "Reset"
|
|
||||||
msgstr "リセット"
|
|
||||||
|
|
||||||
msgid "Search"
|
|
||||||
msgstr "検索"
|
|
||||||
|
|
||||||
msgid "Toggle"
|
|
||||||
msgstr "切り替え"
|
|
||||||
|
|
||||||
msgid "Confirm"
|
|
||||||
msgstr "確認"
|
|
||||||
|
|
||||||
msgid "Focus right"
|
|
||||||
msgstr "右にフォーカス"
|
|
||||||
|
|
||||||
msgid "Focus left"
|
|
||||||
msgstr "左にフォーカス"
|
|
||||||
|
|
||||||
msgid "Ok"
|
msgid "Ok"
|
||||||
msgstr "OK"
|
msgstr "OK"
|
||||||
|
|
||||||
msgid "Input cannot be empty"
|
msgid "Input cannot be empty"
|
||||||
msgstr "入力は空にできません"
|
msgstr "入力は空にできません"
|
||||||
|
|
||||||
msgid "Show/Hide help"
|
|
||||||
msgstr "ヘルプを表示/隠す"
|
|
||||||
|
|
||||||
msgid "Yes"
|
msgid "Yes"
|
||||||
msgstr "はい"
|
msgstr "Yes"
|
||||||
|
|
||||||
msgid "No"
|
msgid "No"
|
||||||
msgstr "いいえ"
|
msgstr "No"
|
||||||
|
|
||||||
msgid " (default)"
|
msgid " (default)"
|
||||||
msgstr " (デフォルト)"
|
msgstr " (デフォルト)"
|
||||||
|
|
@ -1851,6 +1656,9 @@ msgstr " (デフォルト)"
|
||||||
#~ msgid "Manual nic configuration with no auto DHCP requires an IP address"
|
#~ msgid "Manual nic configuration with no auto DHCP requires an IP address"
|
||||||
#~ msgstr "自動 DHCP を使用しない手動 NIC 設定には、IP アドレスが必要です"
|
#~ msgstr "自動 DHCP を使用しない手動 NIC 設定には、IP アドレスが必要です"
|
||||||
|
|
||||||
|
#~ msgid "Select interface to add"
|
||||||
|
#~ msgstr "追加するインターフェースを選択"
|
||||||
|
|
||||||
#~ msgid "Mark/Unmark a partition as compressed (btrfs only)"
|
#~ msgid "Mark/Unmark a partition as compressed (btrfs only)"
|
||||||
#~ msgstr "圧縮するパーティションとしてマーク/マーク解除(Btrfs のみ)"
|
#~ msgstr "圧縮するパーティションとしてマーク/マーク解除(Btrfs のみ)"
|
||||||
|
|
||||||
|
|
@ -2317,6 +2125,9 @@ msgstr " (デフォルト)"
|
||||||
#~ msgid "Root mount directory"
|
#~ msgid "Root mount directory"
|
||||||
#~ msgstr "ルートマウントディレクトリ"
|
#~ msgstr "ルートマウントディレクトリ"
|
||||||
|
|
||||||
|
#~ msgid "Select language"
|
||||||
|
#~ msgstr "言語を選択"
|
||||||
|
|
||||||
#~ msgid "Write additional packages to install (space separated, leave blank to skip)"
|
#~ msgid "Write additional packages to install (space separated, leave blank to skip)"
|
||||||
#~ msgstr "追加でインストールするパッケージを書く(スペースで区切る。無記入でスキップ)"
|
#~ msgstr "追加でインストールするパッケージを書く(スペースで区切る。無記入でスキップ)"
|
||||||
|
|
||||||
|
|
@ -2423,6 +2234,9 @@ msgstr " (デフォルト)"
|
||||||
#~ msgid "Select on multi select"
|
#~ msgid "Select on multi select"
|
||||||
#~ msgstr "複数で選択"
|
#~ msgstr "複数で選択"
|
||||||
|
|
||||||
|
#~ msgid "Reset"
|
||||||
|
#~ msgstr "リセット"
|
||||||
|
|
||||||
#~ msgid "Skip selection menu"
|
#~ msgid "Skip selection menu"
|
||||||
#~ msgstr "選択メニューをスキップ"
|
#~ msgstr "選択メニューをスキップ"
|
||||||
|
|
||||||
|
|
@ -2441,6 +2255,114 @@ msgstr " (デフォルト)"
|
||||||
#~ msgid "Selection"
|
#~ msgid "Selection"
|
||||||
#~ msgstr "選択"
|
#~ msgstr "選択"
|
||||||
|
|
||||||
|
#~ msgid "Search"
|
||||||
|
#~ msgstr "検索"
|
||||||
|
|
||||||
|
#~ msgid "Down"
|
||||||
|
#~ msgstr "下へ"
|
||||||
|
|
||||||
|
#~ msgid "Up"
|
||||||
|
#~ msgstr "上へ"
|
||||||
|
|
||||||
|
#~ msgid "Confirm"
|
||||||
|
#~ msgstr "確認"
|
||||||
|
|
||||||
|
#~ msgid "Focus right"
|
||||||
|
#~ msgstr "右にフォーカス"
|
||||||
|
|
||||||
|
#~ msgid "Focus left"
|
||||||
|
#~ msgstr "左にフォーカス"
|
||||||
|
|
||||||
|
#~ msgid "Toggle"
|
||||||
|
#~ msgstr "切り替え"
|
||||||
|
|
||||||
|
#~ msgid "Show/Hide help"
|
||||||
|
#~ msgstr "ヘルプを表示/隠す"
|
||||||
|
|
||||||
|
#~ msgid "Quit"
|
||||||
|
#~ msgstr "終了"
|
||||||
|
|
||||||
|
#~ msgid "First"
|
||||||
|
#~ msgstr "最初"
|
||||||
|
|
||||||
|
#~ msgid "Last"
|
||||||
|
#~ msgstr "最後"
|
||||||
|
|
||||||
|
#~ msgid "Select"
|
||||||
|
#~ msgstr "選択"
|
||||||
|
|
||||||
|
#~ msgid "Page Up"
|
||||||
|
#~ msgstr "上のページへ"
|
||||||
|
|
||||||
|
#~ msgid "Page Down"
|
||||||
|
#~ msgstr "下のページへ"
|
||||||
|
|
||||||
|
#~ msgid "Page up"
|
||||||
|
#~ msgstr "上のページへ"
|
||||||
|
|
||||||
|
#~ msgid "Page down"
|
||||||
|
#~ msgstr "下のページへ"
|
||||||
|
|
||||||
|
#~ msgid "Page Left"
|
||||||
|
#~ msgstr "左のページへ"
|
||||||
|
|
||||||
|
#~ msgid "Page Right"
|
||||||
|
#~ msgstr "右のページへ"
|
||||||
|
|
||||||
|
#~ msgid "Cursor up"
|
||||||
|
#~ msgstr "カーソルを上へ"
|
||||||
|
|
||||||
|
#~ msgid "Cursor down"
|
||||||
|
#~ msgstr "カースルを下へ"
|
||||||
|
|
||||||
|
#~ msgid "Cursor right"
|
||||||
|
#~ msgstr "カーソルを右へ"
|
||||||
|
|
||||||
|
#~ msgid "Cursor left"
|
||||||
|
#~ msgstr "カーソルを左へ"
|
||||||
|
|
||||||
|
#~ msgid "Top"
|
||||||
|
#~ msgstr "一番上へ"
|
||||||
|
|
||||||
|
#~ msgid "Bottom"
|
||||||
|
#~ msgstr "一番下へ"
|
||||||
|
|
||||||
|
#~ msgid "Home"
|
||||||
|
#~ msgstr "ホーム"
|
||||||
|
|
||||||
|
#~ msgid "End"
|
||||||
|
#~ msgstr "最後へ"
|
||||||
|
|
||||||
|
#~ msgid "Toggle option"
|
||||||
|
#~ msgstr "オプションを切り替え"
|
||||||
|
|
||||||
|
#~ msgid "Scroll Up"
|
||||||
|
#~ msgstr "上にスクロール"
|
||||||
|
|
||||||
|
#~ msgid "Scroll Down"
|
||||||
|
#~ msgstr "下にスクロール"
|
||||||
|
|
||||||
|
#~ msgid "Scroll Left"
|
||||||
|
#~ msgstr "左にスクロール"
|
||||||
|
|
||||||
|
#~ msgid "Scroll Right"
|
||||||
|
#~ msgstr "右にスクロール"
|
||||||
|
|
||||||
|
#~ msgid "Scroll Home"
|
||||||
|
#~ msgstr "ホームにスクロール"
|
||||||
|
|
||||||
|
#~ msgid "Scroll End"
|
||||||
|
#~ msgstr "一番下にスクロール"
|
||||||
|
|
||||||
|
#~ msgid "Focus Next"
|
||||||
|
#~ msgstr "次にフォーカス"
|
||||||
|
|
||||||
|
#~ msgid "Focus Previous"
|
||||||
|
#~ msgstr "前にフォーカス"
|
||||||
|
|
||||||
|
#~ msgid "Copy selected text"
|
||||||
|
#~ msgstr "選択したテキストをコピー"
|
||||||
|
|
||||||
#~ msgid "labwc needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
|
#~ msgid "labwc needs access to your seat (collection of hardware devices i.e. keyboard, mouse, etc)"
|
||||||
#~ msgstr "labwc はお使いの Seat(キーボード、マウスなどのハードウェアデバイス群)にアクセスする必要があります"
|
#~ msgstr "labwc はお使いの Seat(キーボード、マウスなどのハードウェアデバイス群)にアクセスする必要があります"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@
|
||||||
{"abbr": "ne", "lang": "Nepali", "translated_lang": "नेपाली"},
|
{"abbr": "ne", "lang": "Nepali", "translated_lang": "नेपाली"},
|
||||||
{"abbr": "nl", "lang": "Dutch", "translated_lang": "Nederlands"},
|
{"abbr": "nl", "lang": "Dutch", "translated_lang": "Nederlands"},
|
||||||
{"abbr": "nn", "lang": "Norwegian Nynorsk"},
|
{"abbr": "nn", "lang": "Norwegian Nynorsk"},
|
||||||
{"abbr": "nb", "lang": "Norwegian Bokmål", "translated_lang": "Norsk bokmål"},
|
{"abbr": "nb", "lang": "Norwegian Bokmål"},
|
||||||
{"abbr": "no", "lang": "Norwegian"},
|
{"abbr": "no", "lang": "Norwegian"},
|
||||||
{"abbr": "ny", "lang": "Nyanja"},
|
{"abbr": "ny", "lang": "Nyanja"},
|
||||||
{"abbr": "oc", "lang": "Occitan (post 1500)"},
|
{"abbr": "oc", "lang": "Occitan (post 1500)"},
|
||||||
|
|
@ -173,7 +173,7 @@
|
||||||
{"abbr": "ur", "lang": "Urdu", "translated_lang": "اردو"},
|
{"abbr": "ur", "lang": "Urdu", "translated_lang": "اردو"},
|
||||||
{"abbr": "uz", "lang": "Uzbek", "translated_lang": "O'zbek"},
|
{"abbr": "uz", "lang": "Uzbek", "translated_lang": "O'zbek"},
|
||||||
{"abbr": "ve", "lang": "Venda"},
|
{"abbr": "ve", "lang": "Venda"},
|
||||||
{"abbr": "vi", "lang": "Vietnamese", "translated_lang": "Tiếng Việt"},
|
{"abbr": "vi", "lang": "Vietnamese"},
|
||||||
{"abbr": "vo", "lang": "Volapük"},
|
{"abbr": "vo", "lang": "Volapük"},
|
||||||
{"abbr": "wa", "lang": "Walloon"},
|
{"abbr": "wa", "lang": "Walloon"},
|
||||||
{"abbr": "wo", "lang": "Wolof"},
|
{"abbr": "wo", "lang": "Wolof"},
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,8 @@ usage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_pot() {
|
generate_pot() {
|
||||||
# tr - regular translation calls
|
|
||||||
# tr_noop - extraction-only marker for strings translated later from a variable
|
|
||||||
# Binding:3 - the description argument of textual Binding() definitions,
|
|
||||||
# translated at runtime by _translate_bindings(). The description
|
|
||||||
# must be passed positionally - xgettext cannot see keyword arguments.
|
|
||||||
find . -type f -iname '*.py' | sort \
|
find . -type f -iname '*.py' | sort \
|
||||||
| xargs xgettext --no-location --omit-header \
|
| xargs xgettext --no-location --omit-header --keyword='tr' \
|
||||||
--keyword='tr' --keyword='tr_noop' --keyword='Binding:3' \
|
|
||||||
-d base -o locales/base.pot
|
-d base -o locales/base.pot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,9 +61,19 @@ cmd_check_po_syntax() {
|
||||||
echo "All .po files passed syntax check."
|
echo "All .po files passed syntax check."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_check_no_tr_fstring() {
|
||||||
|
echo "Checking for tr(f-string) anti-pattern..."
|
||||||
|
if grep -rnE "tr\(\s*f['\"]" . --include='*.py'; then
|
||||||
|
echo "ERROR: use tr('...{}').format(...) instead of tr(f'...')" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "No tr(f-string) anti-pattern found."
|
||||||
|
}
|
||||||
|
|
||||||
cmd_check() {
|
cmd_check() {
|
||||||
local failed=0
|
local failed=0
|
||||||
cmd_check_po_syntax || failed=1
|
cmd_check_po_syntax || failed=1
|
||||||
|
cmd_check_no_tr_fstring || failed=1
|
||||||
if [ "$failed" -eq 1 ]; then
|
if [ "$failed" -eq 1 ]; then
|
||||||
echo "Some translation checks failed." >&2
|
echo "Some translation checks failed." >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
|
@ -2389,33 +2389,3 @@ msgstr "набір апаратних пристроїв, напр. клавіа
|
||||||
|
|
||||||
msgid "No network configuration selected. Network will need to be set up manually on the installed system."
|
msgid "No network configuration selected. Network will need to be set up manually on the installed system."
|
||||||
msgstr "Конфігурацію мережі не обрано. Мережу доведеться налаштувати вручну на встановленій системі."
|
msgstr "Конфігурацію мережі не обрано. Мережу доведеться налаштувати вручну на встановленій системі."
|
||||||
|
|
||||||
msgid "Plymouth"
|
|
||||||
msgstr "Plymouth"
|
|
||||||
|
|
||||||
msgid ""
|
|
||||||
"Plymouth adds a cosmetic boot splash but can cause boot problems on some "
|
|
||||||
"setups:"
|
|
||||||
msgstr ""
|
|
||||||
"Plymouth додає косметичну заставку завантаження, але на деяких конфігураціях "
|
|
||||||
"може спричинити проблеми із завантаженням:"
|
|
||||||
|
|
||||||
msgid "black screen with the NVIDIA driver"
|
|
||||||
msgstr "чорний екран із драйвером NVIDIA"
|
|
||||||
|
|
||||||
msgid "hidden password prompt with disk encryption (LUKS)"
|
|
||||||
msgstr "прихований запит пароля при шифруванні диска (LUKS)"
|
|
||||||
|
|
||||||
msgid "Would you like to enable it?"
|
|
||||||
msgstr "Увімкнути Plymouth?"
|
|
||||||
|
|
||||||
msgid "Select Plymouth theme"
|
|
||||||
msgstr "Оберіть тему Plymouth"
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "Plymouth \"{}\""
|
|
||||||
msgstr "Plymouth \"{}\""
|
|
||||||
|
|
||||||
#, python-brace-format
|
|
||||||
msgid "{} graphics driver"
|
|
||||||
msgstr "Графічний драйвер {}"
|
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
|
@ -1,77 +0,0 @@
|
||||||
"""Textual's built-in key binding descriptions, listed for xgettext.
|
|
||||||
|
|
||||||
These strings live inside the textual package, so xgettext cannot find them
|
|
||||||
when scanning this code base. At runtime _translate_bindings() in
|
|
||||||
components.py translates every binding description with tr(), including the
|
|
||||||
ones inherited from textual widgets - this module only makes those strings
|
|
||||||
visible to the extraction in locales_generator.sh (via the tr_noop keyword).
|
|
||||||
|
|
||||||
The list covers the MRO-merged bindings of the textual classes used in
|
|
||||||
components.py: _translate_bindings() operates on the merged bindings map, so
|
|
||||||
descriptions inherited from ancestors (e.g. Scroll Up from ScrollView) are
|
|
||||||
translated at runtime too and must be listed here.
|
|
||||||
|
|
||||||
Verify or regenerate the list after a textual upgrade or after introducing
|
|
||||||
a new textual widget in components.py:
|
|
||||||
|
|
||||||
python3 test_tooling/check_binding_descriptions.py
|
|
||||||
"""
|
|
||||||
|
|
||||||
from archinstall.lib.translationhandler import tr_noop
|
|
||||||
|
|
||||||
# textual 8.2.8
|
|
||||||
TEXTUAL_BINDING_DESCRIPTIONS: tuple[str, ...] = (
|
|
||||||
tr_noop('Bottom'),
|
|
||||||
tr_noop('Copy selected text'),
|
|
||||||
tr_noop('Cursor down'),
|
|
||||||
tr_noop('Cursor left'),
|
|
||||||
tr_noop('Cursor right'),
|
|
||||||
tr_noop('Cursor up'),
|
|
||||||
tr_noop('Cut selected text'),
|
|
||||||
tr_noop('Delete all to the left'),
|
|
||||||
tr_noop('Delete all to the right'),
|
|
||||||
tr_noop('Delete character left'),
|
|
||||||
tr_noop('Delete character right'),
|
|
||||||
tr_noop('Delete left to start of word'),
|
|
||||||
tr_noop('Delete right to start of word'),
|
|
||||||
tr_noop('Down'),
|
|
||||||
tr_noop('End'),
|
|
||||||
tr_noop('First'),
|
|
||||||
tr_noop('Focus Next'),
|
|
||||||
tr_noop('Focus Previous'),
|
|
||||||
tr_noop('Go to end'),
|
|
||||||
tr_noop('Go to start'),
|
|
||||||
tr_noop('Home'),
|
|
||||||
tr_noop('Last'),
|
|
||||||
tr_noop('Move cursor left'),
|
|
||||||
tr_noop('Move cursor left a word'),
|
|
||||||
tr_noop('Move cursor left a word and select'),
|
|
||||||
tr_noop('Move cursor left and select'),
|
|
||||||
tr_noop('Move cursor right a word'),
|
|
||||||
tr_noop('Move cursor right a word and select'),
|
|
||||||
tr_noop('Move cursor right and select'),
|
|
||||||
tr_noop('Move cursor right or accept the completion suggestion'),
|
|
||||||
tr_noop('Page Down'),
|
|
||||||
tr_noop('Page Left'),
|
|
||||||
tr_noop('Page Right'),
|
|
||||||
tr_noop('Page Up'),
|
|
||||||
tr_noop('Page down'),
|
|
||||||
tr_noop('Page up'),
|
|
||||||
tr_noop('Paste text from the clipboard'),
|
|
||||||
tr_noop('Press button'),
|
|
||||||
tr_noop('Quit'),
|
|
||||||
tr_noop('Scroll Down'),
|
|
||||||
tr_noop('Scroll End'),
|
|
||||||
tr_noop('Scroll Home'),
|
|
||||||
tr_noop('Scroll Left'),
|
|
||||||
tr_noop('Scroll Right'),
|
|
||||||
tr_noop('Scroll Up'),
|
|
||||||
tr_noop('Select'),
|
|
||||||
tr_noop('Select all'),
|
|
||||||
tr_noop('Select line end'),
|
|
||||||
tr_noop('Select line start'),
|
|
||||||
tr_noop('Submit'),
|
|
||||||
tr_noop('Toggle option'),
|
|
||||||
tr_noop('Top'),
|
|
||||||
tr_noop('Up'),
|
|
||||||
)
|
|
||||||
|
|
@ -150,19 +150,6 @@ class LoadingScreen(BaseScreen[ValueT]):
|
||||||
_ = self.dismiss()
|
_ = self.dismiss()
|
||||||
|
|
||||||
|
|
||||||
class _Input(Input):
|
|
||||||
@override
|
|
||||||
def on_mount(self) -> None:
|
|
||||||
_translate_bindings(self._merged_bindings, self._bindings)
|
|
||||||
|
|
||||||
|
|
||||||
class _Button(Button):
|
|
||||||
# no @override: Button has no on_mount in its MRO,
|
|
||||||
# textual dispatches the handler by naming convention
|
|
||||||
def on_mount(self) -> None:
|
|
||||||
_translate_bindings(self._merged_bindings, self._bindings)
|
|
||||||
|
|
||||||
|
|
||||||
class _OptionList(OptionList):
|
class _OptionList(OptionList):
|
||||||
BINDINGS: ClassVar = [
|
BINDINGS: ClassVar = [
|
||||||
Binding('down', 'cursor_down', 'Down', show=True),
|
Binding('down', 'cursor_down', 'Down', show=True),
|
||||||
|
|
@ -322,7 +309,7 @@ class OptionListScreen(BaseScreen[ValueT]):
|
||||||
yield ScrollableContainer(preview_label)
|
yield ScrollableContainer(preview_label)
|
||||||
|
|
||||||
if self._filter:
|
if self._filter:
|
||||||
yield _Input(placeholder='/filter', id='filter-input')
|
yield Input(placeholder='/filter', id='filter-input')
|
||||||
|
|
||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
|
|
@ -553,7 +540,7 @@ class SelectListScreen(BaseScreen[ValueT]):
|
||||||
yield ScrollableContainer(preview_label)
|
yield ScrollableContainer(preview_label)
|
||||||
|
|
||||||
if self._filter:
|
if self._filter:
|
||||||
yield _Input(placeholder='/filter', id='filter-input')
|
yield Input(placeholder='/filter', id='filter-input')
|
||||||
|
|
||||||
yield Footer()
|
yield Footer()
|
||||||
|
|
||||||
|
|
@ -715,12 +702,12 @@ class ConfirmationScreen(BaseScreen[ValueT]):
|
||||||
with Vertical(classes='content-container'):
|
with Vertical(classes='content-container'):
|
||||||
with Horizontal(classes='buttons-container'):
|
with Horizontal(classes='buttons-container'):
|
||||||
for item in self._group.items:
|
for item in self._group.items:
|
||||||
yield _Button(item.text, id=item.key)
|
yield Button(item.text, id=item.key)
|
||||||
else:
|
else:
|
||||||
with Vertical():
|
with Vertical():
|
||||||
with Horizontal(classes='buttons-container'):
|
with Horizontal(classes='buttons-container'):
|
||||||
for item in self._group.items:
|
for item in self._group.items:
|
||||||
yield _Button(item.text, id=item.key)
|
yield Button(item.text, id=item.key)
|
||||||
|
|
||||||
yield Rule(orientation='horizontal')
|
yield Rule(orientation='horizontal')
|
||||||
if self._preview_header is not None:
|
if self._preview_header is not None:
|
||||||
|
|
@ -861,7 +848,7 @@ class InputScreen(BaseScreen[str]):
|
||||||
|
|
||||||
with Center(classes='container-wrapper'):
|
with Center(classes='container-wrapper'):
|
||||||
with Vertical(classes='input-content'):
|
with Vertical(classes='input-content'):
|
||||||
yield _Input(
|
yield Input(
|
||||||
placeholder=self._placeholder,
|
placeholder=self._placeholder,
|
||||||
password=self._password,
|
password=self._password,
|
||||||
value=self._default_value,
|
value=self._default_value,
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ classifiers = [
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pyparted==3.13.0",
|
"pyparted==3.13.0",
|
||||||
"pydantic==2.13.4",
|
"pydantic==2.13.4",
|
||||||
"cryptography==49.0.0",
|
"cryptography==48.0.1",
|
||||||
"textual==8.2.8",
|
"textual==8.2.7",
|
||||||
"markdown-it-py==4.2.0",
|
"markdown-it-py==4.0.0",
|
||||||
"linkify-it-py==2.1.0",
|
"linkify-it-py==2.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -32,14 +32,14 @@ Documentation = "https://archinstall.readthedocs.io/"
|
||||||
Source = "https://github.com/archlinux/archinstall"
|
Source = "https://github.com/archlinux/archinstall"
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
log = ["systemd_python==236"]
|
log = ["systemd_python==235"]
|
||||||
dev = [
|
dev = [
|
||||||
"mypy==2.3.0",
|
"mypy==2.1.0",
|
||||||
"flake8==7.3.0",
|
"flake8==7.3.0",
|
||||||
"pre-commit==4.6.1",
|
"pre-commit==4.6.0",
|
||||||
"ruff==0.16.0",
|
"ruff==0.15.17",
|
||||||
"pylint==4.0.6",
|
"pylint==4.0.5",
|
||||||
"pytest==9.1.1",
|
"pytest==9.0.3",
|
||||||
"hypothesis>=6.152.4",
|
"hypothesis>=6.152.4",
|
||||||
]
|
]
|
||||||
doc = ["sphinx"]
|
doc = ["sphinx"]
|
||||||
|
|
@ -72,6 +72,7 @@ python_version = "3.14"
|
||||||
files = "."
|
files = "."
|
||||||
exclude = [
|
exclude = [
|
||||||
"^build/",
|
"^build/",
|
||||||
|
"^test_tooling/",
|
||||||
]
|
]
|
||||||
disallow_any_explicit = false
|
disallow_any_explicit = false
|
||||||
disallow_any_expr = false
|
disallow_any_expr = false
|
||||||
|
|
@ -202,6 +203,9 @@ indent-style = "tab"
|
||||||
quote-style = "single"
|
quote-style = "single"
|
||||||
docstring-code-format = true
|
docstring-code-format = true
|
||||||
|
|
||||||
|
[tool.ruff.lint.flake8-tidy-imports]
|
||||||
|
ban-relative-imports = "all"
|
||||||
|
|
||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
select = [
|
select = [
|
||||||
"ASYNC", # flake8-async
|
"ASYNC", # flake8-async
|
||||||
|
|
@ -217,7 +221,6 @@ select = [
|
||||||
"G", # flake8-logging-format
|
"G", # flake8-logging-format
|
||||||
"I", # isort
|
"I", # isort
|
||||||
"ICN", # flake8-import-conventions
|
"ICN", # flake8-import-conventions
|
||||||
"INT", # flake8-gettext
|
|
||||||
"ISC", # flake8-implicit-str-concat
|
"ISC", # flake8-implicit-str-concat
|
||||||
"LOG", # flake8-logging
|
"LOG", # flake8-logging
|
||||||
"PGH", # pygrep-hooks
|
"PGH", # pygrep-hooks
|
||||||
|
|
@ -230,10 +233,10 @@ select = [
|
||||||
"RUF", # Ruff-specific rules
|
"RUF", # Ruff-specific rules
|
||||||
"SLOT", # flake8-slot
|
"SLOT", # flake8-slot
|
||||||
"T10", # flake8-debugger
|
"T10", # flake8-debugger
|
||||||
"TID", # flake8-tidy-imports (Enables TID252)
|
|
||||||
"UP", # pyupgrade
|
"UP", # pyupgrade
|
||||||
"W", # pycodestyle warnings
|
"W", # pycodestyle warnings
|
||||||
"YTT", # flake8-2020
|
"YTT", # flake8-2020
|
||||||
|
"TID", # flake8-tidy-imports (Enables TID252)
|
||||||
]
|
]
|
||||||
|
|
||||||
ignore = [
|
ignore = [
|
||||||
|
|
@ -251,17 +254,9 @@ ignore = [
|
||||||
"RUF005", # collection-literal-concatenation
|
"RUF005", # collection-literal-concatenation
|
||||||
"RUF015", # unnecessary-iterable-allocation-for-first-element
|
"RUF015", # unnecessary-iterable-allocation-for-first-element
|
||||||
"RUF039", # unraw-re-pattern
|
"RUF039", # unraw-re-pattern
|
||||||
|
"RUF067", # non-empty-init-module
|
||||||
"W191", # tab-indentation
|
"W191", # tab-indentation
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.ruff.lint.flake8-gettext]
|
|
||||||
function-names = [
|
|
||||||
"tr",
|
|
||||||
"tr_noop",
|
|
||||||
]
|
|
||||||
|
|
||||||
[tool.ruff.lint.flake8-tidy-imports]
|
|
||||||
ban-relative-imports = "all"
|
|
||||||
|
|
||||||
[tool.ruff.lint.mccabe]
|
[tool.ruff.lint.mccabe]
|
||||||
max-complexity = 40
|
max-complexity = 40
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""Verify archinstall/tui/binding_descriptions.py against the installed textual.
|
|
||||||
|
|
||||||
Run manually after a textual upgrade or after introducing a new textual
|
|
||||||
widget in archinstall/tui/components.py:
|
|
||||||
|
|
||||||
python3 test_tooling/check_binding_descriptions.py
|
|
||||||
|
|
||||||
The script parses the tr_noop() entries from binding_descriptions.py (no
|
|
||||||
archinstall import needed) and compares them with the MRO-merged binding
|
|
||||||
descriptions of the textual classes used in components.py. Exits non-zero
|
|
||||||
on any difference and prints the exact missing/stale entries.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from textual.app import App
|
|
||||||
from textual.screen import Screen
|
|
||||||
from textual.widgets import Button, DataTable, HelpPanel, Input, OptionList, SelectionList
|
|
||||||
|
|
||||||
# Must mirror the textual widget classes used in archinstall/tui/components.py.
|
|
||||||
# Extend when a new widget is introduced there.
|
|
||||||
TEXTUAL_CLASSES = (App, Screen, Button, DataTable, HelpPanel, Input, OptionList, SelectionList)
|
|
||||||
|
|
||||||
MODULE_PATH = Path(__file__).parent.parent / 'archinstall/tui/binding_descriptions.py'
|
|
||||||
|
|
||||||
|
|
||||||
def live_descriptions() -> set[str]:
|
|
||||||
descriptions: set[str] = set()
|
|
||||||
# Walk the full MRO: _translate_bindings() operates on the merged bindings
|
|
||||||
# map, so descriptions inherited from ancestors are translated too.
|
|
||||||
for cls in TEXTUAL_CLASSES:
|
|
||||||
for klass in cls.__mro__:
|
|
||||||
for binding in klass.__dict__.get('BINDINGS', []):
|
|
||||||
description = getattr(binding, 'description', None)
|
|
||||||
if not description and isinstance(binding, tuple) and len(binding) > 2:
|
|
||||||
description = binding[2]
|
|
||||||
if description:
|
|
||||||
descriptions.add(str(description))
|
|
||||||
return descriptions
|
|
||||||
|
|
||||||
|
|
||||||
def listed_descriptions() -> set[str]:
|
|
||||||
content = MODULE_PATH.read_text(encoding='utf-8')
|
|
||||||
return set(re.findall(r"tr_noop\('([^']+)'\)", content))
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
live = live_descriptions()
|
|
||||||
listed = listed_descriptions()
|
|
||||||
|
|
||||||
if live == listed:
|
|
||||||
print(f'OK: {len(listed)} descriptions in sync with installed textual')
|
|
||||||
return 0
|
|
||||||
|
|
||||||
for description in sorted(live - listed):
|
|
||||||
print(f'missing from binding_descriptions.py: {description}')
|
|
||||||
for description in sorted(listed - live):
|
|
||||||
print(f'stale in binding_descriptions.py: {description}')
|
|
||||||
return 1
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main())
|
|
||||||
|
|
@ -11,8 +11,6 @@ import sys
|
||||||
import time
|
import time
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from collections.abc import Iterator
|
from collections.abc import Iterator
|
||||||
from functools import cache
|
|
||||||
from pathlib import Path
|
|
||||||
from select import EPOLLHUP, EPOLLIN, epoll
|
from select import EPOLLHUP, EPOLLIN, epoll
|
||||||
from shutil import which
|
from shutil import which
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
|
|
@ -27,32 +25,25 @@ class ArgumentError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def cpu_count() -> int:
|
def get_master(interface):
|
||||||
count = os.cpu_count()
|
|
||||||
if not count:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
return count - 1
|
|
||||||
|
|
||||||
|
|
||||||
def get_master(interface: str) -> str | None:
|
|
||||||
master_path = pathlib.Path(f'/sys/class/net/{interface}/master')
|
master_path = pathlib.Path(f'/sys/class/net/{interface}/master')
|
||||||
return master_path.readlink().name if master_path.exists() else None
|
return master_path.readlink().name if master_path.exists() else None
|
||||||
|
|
||||||
|
|
||||||
def gray(text: str) -> str:
|
def gray(text):
|
||||||
return f'\033[38;5;246m{text}\033[0m'
|
return f'\033[38;5;246m{text}\033[0m'
|
||||||
|
|
||||||
|
|
||||||
def orange(text: str) -> str:
|
def orange(text):
|
||||||
return f'\033[38;5;208m{text}\033[0m'
|
return f'\033[38;5;208m{text}\033[0m'
|
||||||
|
|
||||||
|
|
||||||
def red(text: str) -> str:
|
def red(text):
|
||||||
return f'\033[31m{text}\033[0m'
|
return f'\033[31m{text}\033[0m'
|
||||||
|
|
||||||
|
|
||||||
harddrives: dict[Path, str] = {}
|
sudo_password = None # Gets populated later
|
||||||
|
harddrives = {}
|
||||||
username = getpass.getuser()
|
username = getpass.getuser()
|
||||||
groupname = grp.getgrgid(os.getgid()).gr_name
|
groupname = grp.getgrgid(os.getgid()).gr_name
|
||||||
|
|
||||||
|
|
@ -80,37 +71,37 @@ hardware = parser.add_argument_group('Hardware', 'General hardware specs for the
|
||||||
hardware.add_argument('--bios', action='store_true', help='Disables EFI (edk2/ovmf) and uses BIOS support instead', default=False)
|
hardware.add_argument('--bios', action='store_true', help='Disables EFI (edk2/ovmf) and uses BIOS support instead', default=False)
|
||||||
hardware.add_argument('--memory', nargs='?', help='Ammount of memory to supply the machine', default=8192)
|
hardware.add_argument('--memory', nargs='?', help='Ammount of memory to supply the machine', default=8192)
|
||||||
hardware.add_argument('--harddrive', action='append', help='Sets up one or more virtio-scsi-pci, size is defined by --harddrive test.qcow2:15G', type=str)
|
hardware.add_argument('--harddrive', action='append', help='Sets up one or more virtio-scsi-pci, size is defined by --harddrive test.qcow2:15G', type=str)
|
||||||
hardware.add_argument('--cpu', help='Sets the number of cores to allocate (default nproc -1)', type=str, default=cpu_count())
|
hardware.add_argument('--cpu', help='Sets the number of cores to allocate (default nproc -1)', type=str, default=os.cpu_count() - 1 if os.cpu_count() else 1)
|
||||||
hardware.add_argument('--resolution', help="Sets Qemu's VGA resolution", type=str, default='1920x1107')
|
hardware.add_argument('--resolution', help="Sets Qemu's VGA resolution", type=str, default='1920x1107')
|
||||||
|
|
||||||
kernel = parser.add_argument_group('Kernel', '--kernel specific arguments')
|
kernel = parser.add_argument_group('Kernel', '--kernel specific arguments')
|
||||||
kernel.add_argument('--initrd', nargs='?', help='Defines which ISO to run (skips build all together)', default=None, type=pathlib.Path)
|
kernel.add_argument('--initrd', nargs='?', help='Defines which ISO to run (skips build all together)', default=None, type=pathlib.Path)
|
||||||
|
|
||||||
cli_args, _ = parser.parse_known_args()
|
args, unknowns = parser.parse_known_args() # pylint: disable=redefined-outer-name
|
||||||
|
|
||||||
if cli_args.bios and cli_args.uki:
|
if args.bios and args.uki:
|
||||||
raise ArgumentError('Cannot boot a --uki image with --bios mode (at least not that I know of).')
|
raise ArgumentError('Cannot boot a --uki image with --bios mode (at least not that I know of).')
|
||||||
|
|
||||||
if cli_args.uki is None and cli_args.kernel is None and cli_args.iso is None and cli_args.harddrive is None:
|
if args.uki is None and args.kernel is None and args.iso is None and args.harddrive is None:
|
||||||
raise ArgumentError('Cannot boot this machine, define at least one of: --uki, --kernel, --iso, --harddrive')
|
raise ArgumentError('Cannot boot this machine, define at least one of: --uki, --kernel, --iso, --harddrive')
|
||||||
|
|
||||||
if cli_args.bridge is None and cli_args.bridge_master:
|
if args.bridge is None and args.bridge_master:
|
||||||
raise ArgumentError('Cannot use --bridge-master without defining --bridge')
|
raise ArgumentError('Cannot use --bridge-master without defining --bridge')
|
||||||
|
|
||||||
if cli_args.bridge is None and cli_args.bridge_mac:
|
if args.bridge is None and args.bridge_mac:
|
||||||
raise ArgumentError('Cannot use --bridge-mac without defining --bridge')
|
raise ArgumentError('Cannot use --bridge-mac without defining --bridge')
|
||||||
elif cli_args.bridge and cli_args.bridge_mac is None:
|
elif args.bridge and args.bridge_mac is None:
|
||||||
cli_args.bridge_mac = '52:54:00:00:00:1'
|
args.bridge_mac = '52:54:00:00:00:1'
|
||||||
|
|
||||||
if cli_args.tap and not cli_args.bridge and get_master(cli_args.tap) is None:
|
if args.tap and not args.bridge and get_master(args.tap) is None:
|
||||||
# We'll allow it, because maybe we're tesing what happens without networking, but the NIC exists.
|
# We'll allow it, because maybe we're tesing what happens without networking, but the NIC exists.
|
||||||
# Or the user has some creative iptables/nftables forwarding.
|
# Or the user has some creative iptables/nftables forwarding.
|
||||||
print(orange('--tap does not have a master, consider adding --bridge or manual set a master using ip-link(8).'))
|
print(orange('--tap does not have a master, consider adding --bridge or manual set a master using ip-link(8).'))
|
||||||
|
|
||||||
if cli_args.tap is None and cli_args.bridge:
|
if args.tap is None and args.bridge:
|
||||||
print(orange("--bridge* arguments will be ignored since there's no --tap defined"))
|
print(orange("--bridge* arguments will be ignored since there's no --tap defined"))
|
||||||
elif cli_args.tap and cli_args.tap_mac is None:
|
elif args.tap and args.tap_mac is None:
|
||||||
cli_args.tap_mac = '52:54:00:00:00:2'
|
args.tap_mac = '52:54:00:00:00:2'
|
||||||
|
|
||||||
|
|
||||||
class SysCallError(Exception):
|
class SysCallError(Exception):
|
||||||
|
|
@ -185,7 +176,7 @@ class SysCommandWorker:
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def __iter__(self, *args: str, **kwargs: dict[str, Any]) -> Iterator[bytes]:
|
def __iter__(self, *args: str, **kwargs: dict[str, Any]) -> Iterator[bytes]: # pylint: disable=redefined-outer-name
|
||||||
last_line = self._trace_log.rfind(b'\n')
|
last_line = self._trace_log.rfind(b'\n')
|
||||||
lines = filter(None, self._trace_log[self._trace_log_pos : last_line].splitlines())
|
lines = filter(None, self._trace_log[self._trace_log_pos : last_line].splitlines())
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
|
@ -343,82 +334,81 @@ class SysCommandWorker:
|
||||||
return self._trace_log.decode(encoding)
|
return self._trace_log.decode(encoding)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
def ensure_sudo():
|
||||||
def get_sudo_password() -> str:
|
global sudo_password # pylint: disable=global-statement
|
||||||
sudo_password = getpass.getpass(f'[sudo] password for {username}: ')
|
|
||||||
if sudo_password == '':
|
if sudo_password is None:
|
||||||
|
if (sudo_password := getpass.getpass(f'[sudo] password for {username}: ')) == '':
|
||||||
raise ValueError('Certain commands need sudo to work and no sudo password was given.')
|
raise ValueError('Certain commands need sudo to work and no sudo password was given.')
|
||||||
|
|
||||||
return sudo_password
|
|
||||||
|
|
||||||
|
def setup_networking():
|
||||||
def setup_networking() -> None:
|
if args.tap:
|
||||||
if cli_args.tap:
|
if pathlib.Path(f'/sys/class/net/{args.tap}').exists() is False:
|
||||||
if pathlib.Path(f'/sys/class/net/{cli_args.tap}').exists() is False:
|
print(gray(f'Creating {args.tap} for user {username} and group {groupname}'))
|
||||||
print(gray(f'Creating {cli_args.tap} for user {username} and group {groupname}'))
|
handle, pw_prompted = SysCommandWorker(f'sudo ip tuntap add dev {args.tap} mode tap user {username} group {groupname}'), False
|
||||||
handle, pw_prompted = SysCommandWorker(f'sudo ip tuntap add dev {cli_args.tap} mode tap user {username} group {groupname}'), False
|
|
||||||
while handle.is_alive():
|
while handle.is_alive():
|
||||||
if b'password for' in handle and pw_prompted is False:
|
if b'password for' in handle and pw_prompted is False:
|
||||||
sudo_pw = get_sudo_password()
|
ensure_sudo()
|
||||||
handle.write(bytes(sudo_pw, 'UTF-8'))
|
handle.write(bytes(sudo_password, 'UTF-8'))
|
||||||
pw_prompted = True
|
pw_prompted = True
|
||||||
|
|
||||||
if cli_args.bridge:
|
if args.bridge:
|
||||||
if pathlib.Path(f'/sys/class/net/{cli_args.bridge}').exists() is False:
|
if pathlib.Path(f'/sys/class/net/{args.bridge}').exists() is False:
|
||||||
print(gray(f'Creating {cli_args.bridge}'))
|
print(gray(f'Creating {args.bridge}'))
|
||||||
handle, pw_prompted = SysCommandWorker(f'sudo ip link add name {cli_args.bridge} type bridge'), False
|
handle, pw_prompted = SysCommandWorker(f'sudo ip link add name {args.bridge} type bridge'), False
|
||||||
while handle.is_alive():
|
while handle.is_alive():
|
||||||
if b'password for' in handle and pw_prompted is False:
|
if b'password for' in handle and pw_prompted is False:
|
||||||
sudo_pw = get_sudo_password()
|
ensure_sudo()
|
||||||
handle.write(bytes(sudo_pw, 'UTF-8'))
|
handle.write(bytes(sudo_password, 'UTF-8'))
|
||||||
pw_prompted = True
|
pw_prompted = True
|
||||||
|
|
||||||
if cli_args.bridge_mac:
|
if args.bridge_mac:
|
||||||
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.bridge} address {cli_args.bridge_mac}'), False
|
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.bridge} address {args.bridge_mac}'), False
|
||||||
print(gray(f'Setting bridge {cli_args.bridge} MAC address to {cli_args.bridge_mac}'))
|
print(gray(f'Setting bridge {args.bridge} MAC address to {args.bridge_mac}'))
|
||||||
while handle.is_alive():
|
while handle.is_alive():
|
||||||
if b'password for' in handle and pw_prompted is False:
|
if b'password for' in handle and pw_prompted is False:
|
||||||
sudo_pw = get_sudo_password()
|
ensure_sudo()
|
||||||
handle.write(bytes(sudo_pw, 'UTF-8'))
|
handle.write(bytes(sudo_password, 'UTF-8'))
|
||||||
pw_prompted = True
|
pw_prompted = True
|
||||||
|
|
||||||
if cli_args.bridge_master and get_master(cli_args.bridge) != cli_args.bridge_master:
|
if args.bridge_master and get_master(args.bridge) != args.bridge_master:
|
||||||
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.bridge_master} master {cli_args.bridge}'), False
|
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.bridge_master} master {args.bridge}'), False
|
||||||
print(gray(f'Setting interface {cli_args.bridge_master} master to {cli_args}'))
|
print(gray(f'Setting interface {args.bridge_master} master to {args.bridge}'))
|
||||||
while handle.is_alive():
|
while handle.is_alive():
|
||||||
if b'password for' in handle and pw_prompted is False:
|
if b'password for' in handle and pw_prompted is False:
|
||||||
sudo_pw = get_sudo_password()
|
ensure_sudo()
|
||||||
handle.write(bytes(sudo_pw, 'UTF-8'))
|
handle.write(bytes(sudo_password, 'UTF-8'))
|
||||||
pw_prompted = True
|
pw_prompted = True
|
||||||
|
|
||||||
print(gray(f'Setting interface {cli_args.tap} master to {cli_args.bridge}'))
|
print(gray(f'Setting interface {args.tap} master to {args.bridge}'))
|
||||||
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.tap} master {cli_args.bridge}'), False
|
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.tap} master {args.bridge}'), False
|
||||||
while handle.is_alive():
|
while handle.is_alive():
|
||||||
if b'password for' in handle and pw_prompted is False:
|
if b'password for' in handle and pw_prompted is False:
|
||||||
sudo_pw = get_sudo_password()
|
ensure_sudo()
|
||||||
handle.write(bytes(sudo_pw, 'UTF-8'))
|
handle.write(bytes(sudo_password, 'UTF-8'))
|
||||||
pw_prompted = True
|
pw_prompted = True
|
||||||
|
|
||||||
print(gray(f'Bringing up bridge {cli_args.bridge}'))
|
print(gray(f'Bringing up bridge {args.bridge}'))
|
||||||
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.bridge} up'), False
|
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.bridge} up'), False
|
||||||
while handle.is_alive():
|
while handle.is_alive():
|
||||||
if b'password for' in handle and pw_prompted is False:
|
if b'password for' in handle and pw_prompted is False:
|
||||||
sudo_pw = get_sudo_password()
|
ensure_sudo()
|
||||||
handle.write(bytes(sudo_pw, 'UTF-8'))
|
handle.write(bytes(sudo_password, 'UTF-8'))
|
||||||
pw_prompted = True
|
pw_prompted = True
|
||||||
|
|
||||||
print(gray(f'Bringing interface {cli_args.tap} up'))
|
print(gray(f'Bringing interface {args.tap} up'))
|
||||||
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {cli_args.tap} up'), False
|
handle, pw_prompted = SysCommandWorker(f'sudo ip link set dev {args.tap} up'), False
|
||||||
while handle.is_alive():
|
while handle.is_alive():
|
||||||
if b'password for' in handle and pw_prompted is False:
|
if b'password for' in handle and pw_prompted is False:
|
||||||
sudo_pw = get_sudo_password()
|
ensure_sudo()
|
||||||
handle.write(bytes(sudo_pw, 'UTF-8'))
|
handle.write(bytes(sudo_password, 'UTF-8'))
|
||||||
pw_prompted = True
|
pw_prompted = True
|
||||||
|
|
||||||
|
|
||||||
def setup_disks() -> None:
|
def setup_disks():
|
||||||
if cli_args.harddrive:
|
if args.harddrive:
|
||||||
for harddrive_arg in cli_args.harddrive:
|
for harddrive_arg in args.harddrive:
|
||||||
path, size = harddrive_arg.split(':')
|
path, size = harddrive_arg.split(':')
|
||||||
path = pathlib.Path(path.strip()).expanduser().resolve().absolute()
|
path = pathlib.Path(path.strip()).expanduser().resolve().absolute()
|
||||||
harddrives[path] = size.strip()
|
harddrives[path] = size.strip()
|
||||||
|
|
@ -435,8 +425,7 @@ def setup_disks() -> None:
|
||||||
setup_networking()
|
setup_networking()
|
||||||
setup_disks()
|
setup_disks()
|
||||||
|
|
||||||
disk_paths_hash = ''
|
if args.uki or args.bios is False:
|
||||||
if cli_args.uki or cli_args.bios is False:
|
|
||||||
disk_paths_hash = hashlib.sha1((''.join(sorted([str(x) for x in harddrives.keys()]))).encode()).hexdigest()
|
disk_paths_hash = hashlib.sha1((''.join(sorted([str(x) for x in harddrives.keys()]))).encode()).hexdigest()
|
||||||
|
|
||||||
shutil.copy2('/usr/share/ovmf/x64/OVMF_CODE.secboot.4m.fd', f'./OVMF_CODE.secboot.4m.fd.{disk_paths_hash}')
|
shutil.copy2('/usr/share/ovmf/x64/OVMF_CODE.secboot.4m.fd', f'./OVMF_CODE.secboot.4m.fd.{disk_paths_hash}')
|
||||||
|
|
@ -450,16 +439,16 @@ qemu += ' -machine q35,accel=kvm'
|
||||||
qemu += ' -object rng-random,filename=/dev/urandom,id=rng0'
|
qemu += ' -object rng-random,filename=/dev/urandom,id=rng0'
|
||||||
qemu += ' -device virtio-rng-pci,rng=rng0'
|
qemu += ' -device virtio-rng-pci,rng=rng0'
|
||||||
qemu += ' -global driver=cfi.pflash01,property=secure,value=on'
|
qemu += ' -global driver=cfi.pflash01,property=secure,value=on'
|
||||||
qemu += f' -smp {cli_args.cpu},sockets=1,dies=1,cores={cli_args.cpu},threads=1'
|
qemu += f' -smp {args.cpu},sockets=1,dies=1,cores={args.cpu},threads=1'
|
||||||
# qemu += f' -vga vga'
|
# qemu += f' -vga vga'
|
||||||
qemu += f' -device VGA,edid=on,xres={cli_args.resolution.split("x")[0]},yres={cli_args.resolution.split("x")[1]}'
|
qemu += f' -device VGA,edid=on,xres={args.resolution.split("x")[0]},yres={args.resolution.split("x")[1]}'
|
||||||
qemu += ' -device intel-iommu,device-iotlb=on,caching-mode=on'
|
qemu += ' -device intel-iommu,device-iotlb=on,caching-mode=on'
|
||||||
qemu += f' -m {cli_args.memory}'
|
qemu += f' -m {args.memory}'
|
||||||
if cli_args.bios is False:
|
if args.bios is False:
|
||||||
qemu += f' -drive if=pflash,format=raw,readonly=on,file=./OVMF_CODE.secboot.4m.fd.{disk_paths_hash}'
|
qemu += f' -drive if=pflash,format=raw,readonly=on,file=./OVMF_CODE.secboot.4m.fd.{disk_paths_hash}'
|
||||||
qemu += f' -drive if=pflash,format=raw,file=./OVMF_VARS.4m.fd.{disk_paths_hash}'
|
qemu += f' -drive if=pflash,format=raw,file=./OVMF_VARS.4m.fd.{disk_paths_hash}'
|
||||||
if cli_args.uki:
|
if args.uki:
|
||||||
qemu += f' -kernel {cli_args.uki}'
|
qemu += f' -kernel {args.uki}'
|
||||||
boot_index += 1
|
boot_index += 1
|
||||||
scsi_index = 0
|
scsi_index = 0
|
||||||
for scsi_index, hdd in enumerate(harddrives.keys()):
|
for scsi_index, hdd in enumerate(harddrives.keys()):
|
||||||
|
|
@ -471,18 +460,18 @@ for scsi_index, hdd in enumerate(harddrives.keys()):
|
||||||
qemu += f' -blockdev \'{{"driver":"file","filename":"{hdd}","aio":"threads","node-name":"libvirt-{scsi_index}-storage","cache":{{"direct":false,"no-flush":false}},"auto-read-only":true,"discard":"unmap"}}\'' # noqa: E501
|
qemu += f' -blockdev \'{{"driver":"file","filename":"{hdd}","aio":"threads","node-name":"libvirt-{scsi_index}-storage","cache":{{"direct":false,"no-flush":false}},"auto-read-only":true,"discard":"unmap"}}\'' # noqa: E501
|
||||||
qemu += f' -blockdev \'{{"node-name":"libvirt-{scsi_index}-format","read-only":false,"discard":"unmap","cache":{{"direct":true,"no-flush":false}},"driver":"qcow2","file":"libvirt-{scsi_index}-storage","backing":null}}\'' # noqa: E501
|
qemu += f' -blockdev \'{{"node-name":"libvirt-{scsi_index}-format","read-only":false,"discard":"unmap","cache":{{"direct":true,"no-flush":false}},"driver":"qcow2","file":"libvirt-{scsi_index}-storage","backing":null}}\'' # noqa: E501
|
||||||
boot_index += 1
|
boot_index += 1
|
||||||
if cli_args.iso:
|
if args.iso:
|
||||||
qemu += f' -device virtio-scsi-pci,bus=pcie.0,id=scsi{scsi_index + 1}'
|
qemu += f' -device virtio-scsi-pci,bus=pcie.0,id=scsi{scsi_index + 1}'
|
||||||
qemu += f' -device scsi-cd,drive=cdrom0,bus=scsi{scsi_index + 1}.0,bootindex={boot_index}'
|
qemu += f' -device scsi-cd,drive=cdrom0,bus=scsi{scsi_index + 1}.0,bootindex={boot_index}'
|
||||||
qemu += f' -drive file={cli_args.iso},media=cdrom,if=none,format=raw,cache=none,id=cdrom0'
|
qemu += f' -drive file={args.iso},media=cdrom,if=none,format=raw,cache=none,id=cdrom0'
|
||||||
boot_index += 1
|
boot_index += 1
|
||||||
|
|
||||||
# if cli_args.vfio:
|
# if args.vfio:
|
||||||
# qemu += f' -drive file={cli_args.vfio},index=2,media=cdrom'
|
# qemu += f' -drive file={args.vfio},index=2,media=cdrom'
|
||||||
|
|
||||||
if cli_args.tap:
|
if args.tap:
|
||||||
qemu += f' -device virtio-net-pci,mac={cli_args.tap_mac},id=network0,netdev=network0.0,status=on,bus=pcie.0'
|
qemu += f' -device virtio-net-pci,mac={args.tap_mac},id=network0,netdev=network0.0,status=on,bus=pcie.0'
|
||||||
qemu += f' -netdev tap,ifname={cli_args.tap},id=network0.0,script=no,downscript=no'
|
qemu += f' -netdev tap,ifname={args.tap},id=network0.0,script=no,downscript=no'
|
||||||
|
|
||||||
print(gray(qemu))
|
print(gray(qemu))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue