7 changed files with 1812 additions and 1 deletions
@ -1,3 +1,57 @@ |
|||||||
# customize_debian_iso |
# customize_debian_iso |
||||||
|
|
||||||
Build an iso with all your deb favorite package and you personnal files in it. With dependencies. |
Build an iso with all your deb favorite package and you personnal files in it. With dependencies. |
||||||
|
|
||||||
|
Those scripts are intended to customise an existing debian installer iso. |
||||||
|
|
||||||
|
The initial idea is to anticipate the end of i386 architecture ( bookworm is the last release to include this architecture ) and be able to reinstall i386 hosts in the future with the last existing release even in future (retroxxxx). |
||||||
|
Those servers will no more have security fixes, so a local usage is recommanded. |
||||||
|
|
||||||
|
The goals are : |
||||||
|
|
||||||
|
- to be able to install a debian on a specific arch in full offline mode |
||||||
|
- to store everything you need and will need without network on a cd or usb disk |
||||||
|
- to have all package dependencies included |
||||||
|
- to include package you want |
||||||
|
- to exclude package tags you don't want |
||||||
|
- to exclude package you don't want |
||||||
|
|
||||||
|
You may also include : |
||||||
|
|
||||||
|
- recommended packages of a package |
||||||
|
- suggested packages of a package |
||||||
|
- suggestions from popcon |
||||||
|
|
||||||
|
It doesn't need admin rights but apt should be configured properly. |
||||||
|
If you want to include a package in contrib, contrib must be available in sources.list. |
||||||
|
|
||||||
|
You need to execute it on the same architecture as the target. |
||||||
|
You need to execute it on the same debian version as the target. |
||||||
|
|
||||||
|
It can be executed on an existing host or in a virtual machine. |
||||||
|
|
||||||
|
Copy all files on your home directory. |
||||||
|
Adjust wanted.txt, excluded.txt and exclude_tags.txt to your needs. |
||||||
|
|
||||||
|
Execute customize_iso.sh with the iso as argument. |
||||||
|
|
||||||
|
customize_iso.sh : |
||||||
|
|
||||||
|
- mount iso as temp |
||||||
|
- select and download packages |
||||||
|
- prepare a custom preseed |
||||||
|
- copy the downloaded packages |
||||||
|
- generate the custom iso you can write on a disk or a usb key |
||||||
|
|
||||||
|
You can adjust it tou your needs. |
||||||
|
|
||||||
|
$ customize_iso.sh debian-12.12.0-i386-netinst.iso |
||||||
|
|
||||||
|
download_myfiles.py : |
||||||
|
|
||||||
|
- load existing package of the original iso |
||||||
|
- download wanted packages |
||||||
|
- ask to include recommanded, suggested and popcon packages |
||||||
|
- simulate an apt cache to integrate all dependencies |
||||||
|
|
||||||
|
The custom iso include scripts to use local-apt-repository package after installation for a good apt experience. |
||||||
|
|||||||
@ -0,0 +1,273 @@ |
|||||||
|
#!/bin/bash |
||||||
|
|
||||||
|
release=${1%-netinst.iso} |
||||||
|
execution_directory=$(pwd) |
||||||
|
release_name=${release}-netinst |
||||||
|
newrelease=${release}-custom |
||||||
|
myfiles=${HOME}/myfiles |
||||||
|
wantedfile=${execution_directory}/wanted.txt |
||||||
|
excludedfile=${execution_directory}/excluded.txt |
||||||
|
|
||||||
|
# global parameters |
||||||
|
original_iso=${execution_directory}/${release_name}.iso |
||||||
|
newrelease_iso=${execution_directory}/${newrelease}.iso |
||||||
|
|
||||||
|
cd "${execution_directory}" || exit 1 |
||||||
|
|
||||||
|
temp_file=/tmp/tmp_iso_directory.txt |
||||||
|
if [ -e ${temp_file} ] |
||||||
|
then |
||||||
|
temp_dir=$(cat ${temp_file}) |
||||||
|
else |
||||||
|
temp_dir=$(mktemp -d) |
||||||
|
echo "${temp_dir}" > ${temp_file} |
||||||
|
fi |
||||||
|
|
||||||
|
if [ ! -e "${original_iso}" ] |
||||||
|
then |
||||||
|
echo "image not found : ${original_iso}" |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
package_list='xorriso isolinux python3-apt apt-utils python3-requests rsync wget' |
||||||
|
# shellcheck disable=SC2086 |
||||||
|
if apt-get -s install ${package_list} |grep ^Inst |
||||||
|
then |
||||||
|
echo "sudo apt install ${package_list} !!!" |
||||||
|
exit 0 |
||||||
|
fi |
||||||
|
|
||||||
|
ask() { |
||||||
|
USER_ANSWER="?"; |
||||||
|
while [ "$USER_ANSWER" != "n" ] && [ "$USER_ANSWER" != "y" ] ; do |
||||||
|
echo -n "$1 ? [y/n] " |
||||||
|
read -r -n 1 USER_ANSWER |
||||||
|
echo ""; |
||||||
|
done |
||||||
|
if [ "$USER_ANSWER" == "y" ] ; then |
||||||
|
return 0 |
||||||
|
else |
||||||
|
return 1 |
||||||
|
fi |
||||||
|
} |
||||||
|
|
||||||
|
if [ ! -e "${wantedfile}" ] |
||||||
|
then |
||||||
|
cat > "${wantedfile}" <<EOF |
||||||
|
local-apt-repository |
||||||
|
rsync |
||||||
|
EOF |
||||||
|
fi |
||||||
|
|
||||||
|
if [ ! -e "${excludedfile}" ] |
||||||
|
then |
||||||
|
cat > "${excludedfile}" <<EOF |
||||||
|
gdm3 |
||||||
|
chromium |
||||||
|
firefox |
||||||
|
EOF |
||||||
|
fi |
||||||
|
|
||||||
|
echo "##### extracting iso" |
||||||
|
if [ ! -e "${temp_dir}"/md5sum.txt ] |
||||||
|
then |
||||||
|
echo "extracting iso to ${temp_dir}" |
||||||
|
xorriso -osirrox on -indev "${original_iso}" -extract / "${temp_dir}" |
||||||
|
chmod -R +w "${temp_dir}" |
||||||
|
fi |
||||||
|
|
||||||
|
echo "##### myfiles generation" |
||||||
|
if ask "populating myfiles with download_myfiles.py ?" |
||||||
|
then |
||||||
|
mkdir -p ~/myfiles |
||||||
|
if ! python3 download_myfiles.py --isodir "${temp_dir}" |
||||||
|
then |
||||||
|
echo "myfiles generation failure !!!" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
echo "##### myfiles generation" |
||||||
|
if ask "populating myfiles with custom files ?" |
||||||
|
then |
||||||
|
mkdir -p ~/myfiles/zips |
||||||
|
if [ ! -e ~/myfiles/zips/gnirehtet-java-v2.5.1.zip ] |
||||||
|
then |
||||||
|
cd ~/myfiles/zips/ && wget https://github.com/Genymobile/gnirehtet/releases/download/v2.5.1/gnirehtet-java-v2.5.1.zip |
||||||
|
fi |
||||||
|
if [ ! -e ~/myfiles/zips/dvb-firmware-osmc.zip ] |
||||||
|
then |
||||||
|
cd ~/myfiles/zips/ && wget https://github.com/osmc/dvb-firmware-osmc/archive/refs/heads/master.zip --output-document=dvb-firmware-osmc.zip |
||||||
|
fi |
||||||
|
if [ ! -e ~/myfiles/zips/alsa-firmware-1.2.4.tar.bz2 ] |
||||||
|
then |
||||||
|
cd ~/myfiles/zips/ && wget https://www.alsa-project.org/files/files/pub/firmware/alsa-firmware-1.2.4.tar.bz2 |
||||||
|
fi |
||||||
|
if [ ! -e ~/myfiles/zips/libdvdcss-1.4.3.tar.bz2 ] |
||||||
|
then |
||||||
|
cd ~/myfiles/zips/ && wget https://download.videolan.org/pub/libdvdcss/1.4.3/libdvdcss-1.4.3.tar.bz2 |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
#https://gitlab.com/Dark-Sky/rpi-linux-udl/-/blob/master/firmware/dvb-usb-dibusb-6.0.0.8.fw |
||||||
|
#https://github.com/osmc/dvb-firmware-osmc/blob/master/dvb-usb-dibusb-6.0.0.8.fw |
||||||
|
#https://github.com/osmc/dvb-firmware-osmc/archive/refs/heads/master.zip |
||||||
|
|
||||||
|
|
||||||
|
echo "generate preseed.cfg" |
||||||
|
cat > /tmp/preseed.cfg <<"EOF" |
||||||
|
#_preseed_V1 |
||||||
|
### Locale configuration |
||||||
|
d-i debian-installer/language string fr |
||||||
|
d-i debian-installer/country string FR |
||||||
|
d-i debian-installer/locale select fr_FR.UTF-8 |
||||||
|
d-i keyboard-configuration/xkb-keymap select fr(latin9) |
||||||
|
#d-i keyboard-configuration/toggle select No toggling |
||||||
|
d-i keyboard-configuration/layoutcode string fr |
||||||
|
d-i keyboard-configuration/modelcode string pc105 |
||||||
|
d-i keyboard-configuration/variantcode string latin9 |
||||||
|
### Network configuration |
||||||
|
d-i netcfg/enable boolean false |
||||||
|
### account configuration |
||||||
|
#d-i passwd/root-password-crypted password [$6$tYi0kNPyvi45IWqW$vOvUycJFPqZhg.N.g7wNZ96UriyvrxoumTuWpRD0OoRp47iuqGfvICxNrsVE0IRHaTs.KkU/btWqJYdetRlpE1] |
||||||
|
#d-i passwd/user-fullname string Cyril CONSTANTIN |
||||||
|
#d-i passwd/username string constcyr |
||||||
|
#d-i passwd/user-password-crypted password [$6$tYi0kNPyvi45IWqW$vOvUycJFPqZhg.N.g7wNZ96UriyvrxoumTuWpRD0OoRp47iuqGfvICxNrsVE0IRHaTs.KkU/btWqJYdetRlpE1] |
||||||
|
#d-i passwd/user-uid string 1000 |
||||||
|
#d-i passwd/user-default-groups string audio cdrom video |
||||||
|
### Clock and time zone setup |
||||||
|
d-i clock-setup/utc boolean true |
||||||
|
d-i time/zone string Europe/Paris |
||||||
|
# Controls whether to use NTP to set the clock during the install |
||||||
|
#d-i clock-setup/ntp boolean true |
||||||
|
# NTP server to use. The default is almost always fine here. |
||||||
|
#d-i clock-setup/ntp-server string ntp.example.com |
||||||
|
### Partitioning |
||||||
|
### Base system installation |
||||||
|
### Apt setup |
||||||
|
d-i apt-setup/use_mirror boolean false |
||||||
|
apt-cdrom-setup apt-setup/cdrom/set-next boolean false |
||||||
|
### Package selection |
||||||
|
popularity-contest popularity-contest/participate boolean false |
||||||
|
EOF |
||||||
|
|
||||||
|
cat > "${myfiles}"/libdvdcss2.README <<"EOF" |
||||||
|
libdvd-pkg: Downloading orig source... |
||||||
|
I: libdvdcss_1.4.3 |
||||||
|
/usr/bin/wget --tries=3 --timeout=40 --read-timeout=40 --continue -O libdvdcss_1.4.3.orig.tar.bz2 \ |
||||||
|
https://download.videolan.org/pub/libdvdcss/1.4.3/libdvdcss-1.4.3.tar.bz2 \ |
||||||
|
|| /usr/bin/uscan --noconf --verbose --rename --destdir=/usr/src/libdvd-pkg --check-dirname-level=0 --force-download --download-current-version /usr/share/libdvd-pkg/debian |
||||||
|
--2026-01-02 21:06:57-- https://download.videolan.org/pub/libdvdcss/1.4.3/libdvdcss-1.4.3.tar.bz2 |
||||||
|
Résolution de download.videolan.org (download.videolan.org)… 213.36.253.2, 2a01:e0d:1:3:58bf:fa02:c0de:5 |
||||||
|
Connexion à download.videolan.org (download.videolan.org)|213.36.253.2|:443… connecté. |
||||||
|
requête HTTP transmise, en attente de la réponse… 200 OK |
||||||
|
Taille: 388404 (379K) [application/octet-stream] |
||||||
|
Sauvegarde en: «libdvdcss_1.4.3.orig.tar.bz2» |
||||||
|
libdvdcss_1.4.3.orig.tar.bz2 100%[==============================================================>] 379,30K 725KB/s ds 0,5s |
||||||
|
2026-01-02 21:06:58 (725 KB/s) — «libdvdcss_1.4.3.orig.tar.bz2» sauvegardé [388404/388404] |
||||||
|
EOF |
||||||
|
|
||||||
|
cat > "${myfiles}"/cs46xx.README <<"EOF" |
||||||
|
# not included |
||||||
|
apt install alsa-utils |
||||||
|
tar xvf alsa-firmware-1.2.4.tar.bz2 |
||||||
|
cd alsa-firmware-1.2.4/cs46xx |
||||||
|
mkdir /lib/firmware/cs46xx |
||||||
|
cp ba1 cwc4630 cwcasync cwcbinhack cwcdma cwcsnoop /lib/firmware/cs46xx |
||||||
|
modprobe -r snd-cs46xx |
||||||
|
modprobe snd-cs46xx |
||||||
|
adduser yourusername audio |
||||||
|
alsamixer : demute |
||||||
|
aplay /usr/share/sounds/alsa/Noise.wav |
||||||
|
speaker-test -t sine -f 440 -c 2 |
||||||
|
speaker-test -t wav -c 2 |
||||||
|
EOF |
||||||
|
|
||||||
|
cat > "${myfiles}"/local-apt-repository.sh <<"EOF" |
||||||
|
#!/bin/bash |
||||||
|
help() |
||||||
|
{ |
||||||
|
echo "rsync and local-apt-repository packages are mandatory" |
||||||
|
} |
||||||
|
if [[ -n $(find "$(pwd)" -name "rsync*deb") ]] |
||||||
|
then |
||||||
|
find "$(pwd)" -name "rsync*deb" -exec dpkg -i {} \; |
||||||
|
else |
||||||
|
help |
||||||
|
fi |
||||||
|
if [[ -n $(find "$(pwd)" -name "local-apt-repository*deb") ]] |
||||||
|
then |
||||||
|
find "$(pwd)" -name "local-apt-repository*deb" -exec dpkg -i {} \; |
||||||
|
else |
||||||
|
help |
||||||
|
fi |
||||||
|
mkdir /srv/local-apt-repository |
||||||
|
mkdir /srv/zips |
||||||
|
rsync --recursive "$(pwd)"/pool /srv/local-apt-repository/ |
||||||
|
rsync --recursive "$(pwd)"/firmware /srv/local-apt-repository/ |
||||||
|
rsync --recursive "$(pwd)"/zips/ /srv/zips |
||||||
|
apt update |
||||||
|
EOF |
||||||
|
|
||||||
|
cat > "${myfiles}"/README.custom <<"EOF" |
||||||
|
You can add package under <cdrom> or <disk> with dpkg e.g. |
||||||
|
# find /mnt/cdrom0 -name "rsync*.deb" -exec dpkg -i {} \; |
||||||
|
But you have to install dependencies one by one as the same method. |
||||||
|
|
||||||
|
The most convenient is to execute : |
||||||
|
# mount <installation_device> |
||||||
|
# cd <installation_device> |
||||||
|
# bash local-apt-repository.sh |
||||||
|
|
||||||
|
It copy all packages into /srv/local-apt-repository and populate apt with all package |
||||||
|
Check you have enough space on /srv before (size of the disk/iso). |
||||||
|
|
||||||
|
Start the systemd service |
||||||
|
# systemctl start local-apt-repository.service |
||||||
|
Check after one or two minutes with |
||||||
|
# apt list |
||||||
|
And you can install them |
||||||
|
# apt install tmux |
||||||
|
EOF |
||||||
|
|
||||||
|
if ask "customize installer (add preseed and modify installation option) ?" |
||||||
|
then |
||||||
|
chmod -R +w "${temp_dir}" |
||||||
|
cp -v /tmp/preseed.cfg "${temp_dir}"/preseed.cfg |
||||||
|
# sed -e "s#quiet#priority=high locale=fr_FR.UTF-8 keymap=fr file=/cdrom/preseed.cfg#" ${temp_dir}/isolinux/txt.cfg |
||||||
|
# sed -i -e "s#quiet#priority=high locale=fr_FR.UTF-8 keymap=fr file=/cdrom/preseed.cfg#" ${temp_dir}/isolinux/txt.cfg |
||||||
|
sed -e "s#quiet#file=/cdrom/preseed.cfg#" "${temp_dir}"/isolinux/txt.cfg |
||||||
|
sed -i -e "s#quiet#file=/cdrom/preseed.cfg#" "${temp_dir}"/isolinux/txt.cfg |
||||||
|
fi |
||||||
|
|
||||||
|
rsync --dry-run --verbose --recursive "${myfiles}"/ "${temp_dir}" |
||||||
|
if ask "copy additionnal packages ?" |
||||||
|
then |
||||||
|
rsync --verbose --recursive "${myfiles}"/ "${temp_dir}" |
||||||
|
# echo "Generate Release file" |
||||||
|
# apt-ftparchive \ |
||||||
|
# -o "APT::FTPArchive::Release::Origin=local-apt-repository" \ |
||||||
|
# -o "APT::FTPArchive::Release::Description=Local APT repository" \ |
||||||
|
# release ${custom_dir} > ${custom_dir}/Release |
||||||
|
fi |
||||||
|
|
||||||
|
# Final steps |
||||||
|
# Calculate md5 and generate iso |
||||||
|
|
||||||
|
echo "calculate md5sum.txt" |
||||||
|
chmod +w "${temp_dir}"/md5sum.txt |
||||||
|
cd "${temp_dir}" && find . -follow -type f ! -name "md5sum.txt" -print0 | xargs -0 md5sum > md5sum.txt |
||||||
|
chmod -R -w "${temp_dir}" |
||||||
|
|
||||||
|
cd "${HOME}" || exit 1 |
||||||
|
|
||||||
|
if ask "generate final iso ?" |
||||||
|
then |
||||||
|
xorriso -as mkisofs -o "${newrelease_iso}" \ |
||||||
|
-isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin \ |
||||||
|
-c isolinux/boot.cat -b isolinux/isolinux.bin -no-emul-boot \ |
||||||
|
-boot-load-size 4 -boot-info-table "${temp_dir}" |
||||||
|
fi |
||||||
|
|
||||||
|
echo "=== End of script ===" |
||||||
|
exit 0 |
||||||
@ -0,0 +1,706 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
# |
||||||
|
# SPECIFICATIONS |
||||||
|
# |
||||||
|
# lire une liste de packages requis |
||||||
|
# lire les entrées popcon by_installed |
||||||
|
# lire une liste de packages non souhaités |
||||||
|
# rechercher dans le cache les packages souhaités |
||||||
|
# rechercher leurs dépendances (DEPENDS PREDEPENDS) |
||||||
|
# exclure les packages avec dépendances non souhaités |
||||||
|
# télécharger chaque "deb" en local |
||||||
|
# calculer la taille approximative de l'image avec les nouveaux packages |
||||||
|
# en fonction de la taille, réitérer dans la base popcon pour identifier des packages intéressants |
||||||
|
# les proposer interactivement à l'ajout |
||||||
|
|
||||||
|
import apt # |
||||||
|
import sys # |
||||||
|
import pprint # |
||||||
|
import requests |
||||||
|
import logging.config |
||||||
|
import logging # log ! |
||||||
|
import os # open |
||||||
|
import fnmatch # find debs |
||||||
|
import re # read lines |
||||||
|
import gzip # jigdo files are compressed |
||||||
|
from collections.abc import MutableMapping, Iterator, Iterable |
||||||
|
from typing import Any |
||||||
|
|
||||||
|
# Source - https://stackoverflow.com/a |
||||||
|
# Posted by Sridhar Ratnakumar, modified by community. See post 'Timeline' for change history |
||||||
|
# Retrieved 2025-11-13, License - CC BY-SA 4.0 |
||||||
|
def sizeof_fmt(num, suffix="B"): |
||||||
|
for unit in ("", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"): |
||||||
|
if abs(num) < 1024.0: |
||||||
|
return f"{num:3.1f}{unit}{suffix}" |
||||||
|
num /= 1024.0 |
||||||
|
return f"{num:.1f}Yi{suffix}" |
||||||
|
|
||||||
|
class WantedList: |
||||||
|
"""Iterator for looping over lines in a file.""" |
||||||
|
def __init__(self, path: str): |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
self.logger.setLevel(logging.INFO) |
||||||
|
self.wanted_list = [] |
||||||
|
if os.path.exists(path): |
||||||
|
for line in open(path): |
||||||
|
self.logger.debug('WantedList: ADD {}'.format(line.strip())) |
||||||
|
self.wanted_list.append(line.strip()) |
||||||
|
else: |
||||||
|
self.logger.warning('WantedList: {} does not exist'.format(path)) |
||||||
|
self.wanted_list.reverse() |
||||||
|
self.index = len(self.wanted_list) |
||||||
|
self.logger.info("WantedList: Initiated") |
||||||
|
|
||||||
|
def __iter__(self): |
||||||
|
return self |
||||||
|
|
||||||
|
def __next__(self): |
||||||
|
if self.index == 0: |
||||||
|
raise StopIteration |
||||||
|
self.index = self.index - 1 |
||||||
|
return self.wanted_list[self.index] |
||||||
|
|
||||||
|
class ExcludedList: |
||||||
|
"""Iterator for looping over lines in a file.""" |
||||||
|
def __init__(self, path: str): |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
self.logger.setLevel(logging.INFO) |
||||||
|
self.excluded_list = [] |
||||||
|
if os.path.exists(path): |
||||||
|
for line in open(path): |
||||||
|
self.logger.debug('ExcludedList: ADD {}'.format(line.strip())) |
||||||
|
self.excluded_list.append(line.strip()) |
||||||
|
else: |
||||||
|
self.logger.warning('ExcludedList: {} does not exist'.format(path)) |
||||||
|
self.index = len(self.excluded_list) |
||||||
|
self.excluded_list.reverse() |
||||||
|
self.logger.info("ExcludedList: Initiated") |
||||||
|
|
||||||
|
def __iter__(self): |
||||||
|
return self |
||||||
|
|
||||||
|
def __next__(self): |
||||||
|
if self.index == 0: |
||||||
|
raise StopIteration |
||||||
|
self.index = self.index - 1 |
||||||
|
return self.excluded_list[self.index] |
||||||
|
|
||||||
|
class ExcludedTags: |
||||||
|
"""Iterator for looping over lines in a file.""" |
||||||
|
def __init__(self, path: str): |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
self.logger.setLevel(logging.DEBUG) |
||||||
|
self.excluded_tags = [] |
||||||
|
if os.path.exists(path): |
||||||
|
for line in open(path): |
||||||
|
self.logger.debug('ExcludedTags: ADD {}'.format(line.strip())) |
||||||
|
self.excluded_tags.append(line.strip()) |
||||||
|
else: |
||||||
|
self.logger.warning('ExcludedTags: {} does not exist'.format(path)) |
||||||
|
self.index = len(self.excluded_tags) |
||||||
|
self.logger.info("Excluded tags initiated: {}".format(self.excluded_tags)) |
||||||
|
|
||||||
|
def __iter__(self): |
||||||
|
return self |
||||||
|
|
||||||
|
def __next__(self): |
||||||
|
if self.index == 0: |
||||||
|
raise StopIteration |
||||||
|
self.index = self.index - 1 |
||||||
|
return self.excluded_tags[self.index] |
||||||
|
|
||||||
|
class PopConList: |
||||||
|
"""Iterator for looping over lines in a file.""" |
||||||
|
def __init__(self): |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
#self.logger.setLevel(logging.INFO) |
||||||
|
self.popconrecordspath = '/tmp/myfiles_popcon.txt' |
||||||
|
popcon_url = 'https://popcon.debian.org/by_inst.gz' |
||||||
|
path = '/tmp/by_inst.gz' |
||||||
|
rank = 0 |
||||||
|
headers_regex = re.compile(r'^[#].*$|^[-]+$') |
||||||
|
stat_regex = re.compile(r'^(?P<rank>[0-9]+)[ ]+(?P<name>[a-zA-Z0-9_.+-]+)[ ]+[0-9]+[ ]+[0-9]+[ ]+[0-9]+[ ]+[0-9]+[ ]+[0-9]+[ ]+.*$') |
||||||
|
if not os.path.exists(path): |
||||||
|
r = requests.get(popcon_url) |
||||||
|
with open(path,'wb') as f: |
||||||
|
f.write(r.content) |
||||||
|
for bbytes in gzip.open(path,'r'): |
||||||
|
line = bbytes.decode('utf-8').strip() |
||||||
|
stat = re.match(stat_regex,line) |
||||||
|
if stat: |
||||||
|
if rank < int(stat.groupdict()['rank']): |
||||||
|
self.logger.debug("PopConList: ADD {} from {}".format(stat.groupdict()['name'],line)) |
||||||
|
with open(self.popconrecordspath, 'a') as prp: |
||||||
|
prp.write('{}\n'.format(stat.groupdict()['name'])) |
||||||
|
rank += 1 |
||||||
|
else: |
||||||
|
self.logger.warning("rank {} already exist : {} :".format(rank,stat.groupdict()['rank'])) |
||||||
|
pass |
||||||
|
else: |
||||||
|
headers = re.match(headers_regex,line) |
||||||
|
if headers: |
||||||
|
self.logger.debug("PopConList: SKIP {}".format(line)) |
||||||
|
else: |
||||||
|
self.logger.warning("PopConList: ERROR {}".format(line)) |
||||||
|
self.logger.info("PopConList: Initiated") |
||||||
|
|
||||||
|
def __iter__(self): |
||||||
|
return self |
||||||
|
|
||||||
|
def __next__(self): |
||||||
|
if self.index == 0: |
||||||
|
raise StopIteration |
||||||
|
self.index = self.index - 1 |
||||||
|
return self.popcon_list[self.index] |
||||||
|
|
||||||
|
class OriginalPackageSource: |
||||||
|
def __init__(self, path: str): |
||||||
|
pass |
||||||
|
|
||||||
|
class JigdoFile(OriginalPackageSource): |
||||||
|
"""Read original jigdo""" |
||||||
|
def __init__(self, path: str): |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
self.logger.setLevel(logging.INFO) |
||||||
|
path_regex = re.compile(r'^(?P<checksum>[A-Za-z0-9_-]+)[=](?P<Origin>[A-Za-z0-9]+)[:](?P<path>[A-Za-z0-9+~/._-]+deb)$') |
||||||
|
package_regex = re.compile(r'(?P<directory>[A-Za-z0-9/+.-]+)[/](?P<Package>[a-z0-9+.-]+)[_](?P<Version>[a-z0-9.+-~]+)[_](?P<Architecture>i386|amd64|armhf|all)[.](?P<Format>deb|udeb)') |
||||||
|
debianMirror = 'http://deb.debian.org/debian/' |
||||||
|
nonusMirror = 'http://debian.proxad.net/debian-non-US/' |
||||||
|
self.packages = {} |
||||||
|
for bbytes in gzip.open(path,'r'): |
||||||
|
line = bbytes.decode('utf-8').strip() |
||||||
|
self.logger.debug("JigdoFile: read line : {}".format(line)) |
||||||
|
path = re.match(path_regex,line) |
||||||
|
if path: |
||||||
|
self.logger.debug("JigdoFile: found {}".format(path.groupdict())) |
||||||
|
self.logger.debug("JigdoFile: DUPLICATE line : {}".format(line)) |
||||||
|
package = re.match(package_regex,path.groupdict()['path']) |
||||||
|
if package: |
||||||
|
self.logger.debug("JigdoFile: found {}".format(package.groupdict())) |
||||||
|
if package.groupdict()['Format'] == 'deb': |
||||||
|
self.logger.debug("JigdoFile: PACKAGE add : {}".format(package.groupdict()['Package'])) |
||||||
|
self.packages[package.groupdict()['Package']] = package.groupdict() |
||||||
|
else: |
||||||
|
self.logger.warning("JigdoFile: Package not found {}".format(path)) |
||||||
|
self.logger.debug("JigdoFile: {} packages registered".format(len(self.packages))) |
||||||
|
|
||||||
|
class IsoDir(OriginalPackageSource): |
||||||
|
"""Read original extracted iso""" |
||||||
|
def __init__(self, path: str, created: bool = False): |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
#self.logger.setLevel(logging.INFO) |
||||||
|
self.logger.setLevel(logging.DEBUG) |
||||||
|
self.packages = {} |
||||||
|
self.created = created |
||||||
|
self.path = path |
||||||
|
package_regex = re.compile(r'(?P<directory>[A-Za-z0-9/+.-]+)[/](?P<Package>[a-z0-9+.-]+)[_](?P<Version>[a-z0-9.+-~]+)[_](?P<Architecture>i386|amd64|armhf|all)[.](?P<Format>deb|udeb)') |
||||||
|
for filepath in self.__find_files_by_pattern('*.deb', path): |
||||||
|
self.logger.debug("IsoDir: read file path : {}".format(filepath)) |
||||||
|
package = re.match(package_regex,filepath) |
||||||
|
if package: |
||||||
|
self.logger.debug("IsoDir: found package : {}".format(package.groupdict())) |
||||||
|
if package.groupdict()['Format'] == 'deb': |
||||||
|
self.logger.debug("IsoDir: PACKAGE add : {}".format(package.groupdict()['Package'])) |
||||||
|
self.packages[package.groupdict()['Package']] = package.groupdict() |
||||||
|
else: |
||||||
|
self.logger.warning("IsoDir: Package not found {}".format(path)) |
||||||
|
self.logger.info("IsoDir: {} packages registered".format(len(self.packages))) |
||||||
|
self.logger.info('IsoDir: Initiated') |
||||||
|
|
||||||
|
def __find_files_by_pattern(self, pattern: str, path: str) -> list[str]: |
||||||
|
""" __find_files_by_pattern('*.txt', '/path/to/dir') """ |
||||||
|
result = [] |
||||||
|
for root, dirs, files in os.walk(path): |
||||||
|
for name in files: |
||||||
|
if fnmatch.fnmatch(name, pattern): |
||||||
|
result.append(os.path.join(root, name)) |
||||||
|
self.logger.debug("Check if path {} was created or not : {}".format(self.path, self.created)) |
||||||
|
if len(result) < 1 and not self.created: |
||||||
|
self.logger.critical("Unable to find asked files !!!") |
||||||
|
sys.exit(1) |
||||||
|
return result |
||||||
|
|
||||||
|
class LocalFiles(IsoDir): |
||||||
|
|
||||||
|
def __init__(self, path: str, created : bool = True): |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
self.logger.setLevel(logging.DEBUG) |
||||||
|
self.path=os.path.expanduser(path) |
||||||
|
self.created = created |
||||||
|
if not os.path.exists(self.path): |
||||||
|
os.mkdir(self.path) |
||||||
|
self.logger.debug("Check if path {} was created or not : {}".format(self.path, self.created)) |
||||||
|
super().__init__(self.path, self.created) |
||||||
|
|
||||||
|
def add(self, key: str, record: str): |
||||||
|
self.packages[key]=record |
||||||
|
|
||||||
|
class JRecords(MutableMapping[Any, Any]): |
||||||
|
"""Store a list of Record from package |
||||||
|
Under the form |
||||||
|
<name>_<version>_<arch> : apt.package.Record |
||||||
|
""" |
||||||
|
|
||||||
|
def __init__(self) -> None: |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
self.logger.setLevel(logging.INFO) |
||||||
|
self._rec = {} |
||||||
|
self._list = [] |
||||||
|
self.logger.info('JRecords: Initiated') |
||||||
|
|
||||||
|
def __hash__(self) -> int: |
||||||
|
return hash(self._rec) |
||||||
|
|
||||||
|
def __str__(self) -> str: |
||||||
|
return str(self._rec) |
||||||
|
|
||||||
|
def __getitem__(self, key: str) -> str: |
||||||
|
return self._rec[key] |
||||||
|
|
||||||
|
def __setitem__(self, key: str, value): |
||||||
|
self._rec[key] = value |
||||||
|
self._list.append(re.sub(r'([a-z0-9+.-]+)[_]([a-z0-9.+-~]+)[_](i386|amd64|armhf|all)',r'\1',key)) |
||||||
|
|
||||||
|
def __delitem__(self, key: str): |
||||||
|
del self_rec[key] |
||||||
|
|
||||||
|
def __contains__(self, key: object) -> bool: |
||||||
|
return key in self._rec or key in self._list |
||||||
|
|
||||||
|
def __iter__(self) -> Iterator[str]: |
||||||
|
return iter(self._rec.keys()) |
||||||
|
|
||||||
|
def iteritems(self) -> Iterable[tuple[object, str]]: |
||||||
|
"""An iterator over the (key, value) items of the record.""" |
||||||
|
for key in self._rec.keys(): |
||||||
|
yield key, self._rec[key] |
||||||
|
|
||||||
|
def get(self, key: str, default: object = None) -> object: |
||||||
|
"""Return record[key] if key in record, else *default*. |
||||||
|
|
||||||
|
The parameter *default* must be either a string or None. |
||||||
|
""" |
||||||
|
return self._rec.get(key, default) |
||||||
|
|
||||||
|
def has_key(self, key: str) -> bool: |
||||||
|
"""deprecated form of ``key in x``.""" |
||||||
|
return key in self._rec or key in self._list |
||||||
|
|
||||||
|
def __len__(self) -> int: |
||||||
|
return len(self._rec) |
||||||
|
|
||||||
|
class Controller: |
||||||
|
|
||||||
|
def __init__(self, aptcache, jrecords, localfiles, originalpackagesource, maxsize: int, excludedlist: [str], excludedtags: [str], interactive: bool) -> None: |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
self.logger.setLevel(logging.DEBUG) |
||||||
|
self.aptcache = aptcache |
||||||
|
self.jrecords = jrecords |
||||||
|
self.originalpackagesource = originalpackagesource |
||||||
|
self.localfiles = localfiles |
||||||
|
self.excludedlist = excludedlist |
||||||
|
self.excludedtags = excludedtags |
||||||
|
self.excludedrecordspath = '/tmp/myfiles_excluded.txt' |
||||||
|
self.recommended = set([]) |
||||||
|
self.suggested = set([]) |
||||||
|
self.suggestedrecordspath = '/tmp/myfiles_suggested.txt' |
||||||
|
self.recommendedrecordspath = '/tmp/myfiles_recommended.txt' |
||||||
|
self.addedrecordspath = '/tmp/myfiles_added.txt' |
||||||
|
self.popconrecordspath = '/tmp/myfiles_popcon.txt' |
||||||
|
self.maxsize = maxsize |
||||||
|
self.interactive = interactive |
||||||
|
self.size = 0 |
||||||
|
self.tags_regex = re.compile(r'(?P<tag>[a-z0-9:, -]+)') |
||||||
|
self.recordkey_regex = re.compile(r'(?P<recordkey>[a-z0-9()<>=|., -]+)') |
||||||
|
self.package_regex = re.compile(r'[ ]*(?P<package>[a-z0-9.-]+)[ ]*') |
||||||
|
self.want_exit = False |
||||||
|
for name,version in ( originalpackagesource.packages | localfiles.packages ).items(): |
||||||
|
self.logger.debug("Controller: recording {}_{}_{}.{} ".format(name,version['Version'],version['Architecture'],version['Format'])) |
||||||
|
record = self.aptcache.record(name) |
||||||
|
key = "{}_{}_{}".format(name,version['Version'],version['Architecture']) |
||||||
|
if not key in self.jrecords: |
||||||
|
self.jrecords[key] = record |
||||||
|
self.logger.debug("Controller: RECORDED {}.deb ({})".format(key,record['Size'])) |
||||||
|
self.size += int(record["Size"]) |
||||||
|
if not name in self.recommended: |
||||||
|
self.__record_recommend(record) |
||||||
|
self.recommended.add(name) |
||||||
|
if not name in self.suggested and not name in self.recommended: |
||||||
|
self.__record_suggest(record) |
||||||
|
self.suggested.add(name) |
||||||
|
else: |
||||||
|
self.logger.warning("Controller: Already registered") |
||||||
|
for name in self.excludedlist: |
||||||
|
key = "{}_{}_{}".format(name,'0.0.0','all') |
||||||
|
self.jrecords[key] = "" |
||||||
|
self.logger.info("Controller: EXCLUDE fixed package {}".format(key)) |
||||||
|
if os.path.exists(self.excludedrecordspath): |
||||||
|
for line in open(self.excludedrecordspath,'r'): |
||||||
|
key = "{}_{}_{}".format(line.strip(),'0.0.0','all') |
||||||
|
self.jrecords[key] = "" |
||||||
|
self.logger.info("Controller: EXCLUDE recorded package {}".format(key)) |
||||||
|
self.logger.debug("Controller: original size is {}".format(self.size)) |
||||||
|
print("Original size is {}".format(sizeof_fmt(self.size))) |
||||||
|
self.logger.debug("Controller: Initiated") |
||||||
|
|
||||||
|
def clean_exit(self) -> bool: |
||||||
|
return self.want_exit |
||||||
|
|
||||||
|
def __add(self, name: str) -> bool: |
||||||
|
self.logger.debug("Add package {}".format(name)) |
||||||
|
if self.size > self.maxsize: |
||||||
|
self.logger.warning("Controller: {} > {}".format(self.size,self.maxsize)) |
||||||
|
if self.interactive: |
||||||
|
print("Taille maximum d'image désirée atteinte: {} > {}".format(self.size,self.maxsize)) |
||||||
|
return False |
||||||
|
else: |
||||||
|
candidates = self.aptcache.records([],name) |
||||||
|
self.logger.debug("Controller: candidates: {}".format(candidates)) |
||||||
|
for candidate in candidates: |
||||||
|
record = self.aptcache.record(candidate) |
||||||
|
self.logger.debug("Controller: record: {}".format(record)) |
||||||
|
key = "{}_{}_{}".format(record['Package'],record['Version'],record['Architecture']) |
||||||
|
self.logger.debug("Controller: recording {} ".format(key)) |
||||||
|
self.localfiles.add(key,record) |
||||||
|
self.logger.debug("Controller: RECORDED {}.deb ({})".format(key,record['Size'])) |
||||||
|
self.size += int(record["Size"]) |
||||||
|
if not name in self.recommended: |
||||||
|
self.__record_recommend(record) |
||||||
|
self.recommended.add(name) |
||||||
|
if not name in self.suggested and not name in self.recommended: |
||||||
|
self.__record_suggest(record) |
||||||
|
self.suggested.add(name) |
||||||
|
return True |
||||||
|
|
||||||
|
def __exclude_tags(self, record) -> bool: |
||||||
|
self.logger.debug("call: __exclude_tags(record)") |
||||||
|
if 'Tag' in record: |
||||||
|
self.logger.debug("__exclude_tags:: tags in record : {}".format(record['Tag'])) |
||||||
|
tags = re.match(self.tags_regex,record['Tag'].replace('\n','').replace(' ','')) |
||||||
|
self.logger.debug("__exclude_tags:: match tags_regex: {} ({})".format(tags,type(tags))) |
||||||
|
if tags: |
||||||
|
for tag in re.split(r',', tags.groupdict()['tag']): |
||||||
|
self.logger.debug("Check if exclude tag : {}".format(tag)) |
||||||
|
if tag == "" or tag == " ": |
||||||
|
continue |
||||||
|
elif tag in self.excludedtags.excluded_tags: |
||||||
|
self.logger.debug("Package has excluded tags") |
||||||
|
if self.interactive: |
||||||
|
print("Tag excluded in package {} : {} ({})".format(record['Package'],tag,record['Description'])) |
||||||
|
pass |
||||||
|
return True |
||||||
|
else: |
||||||
|
self.logger.warning("__exclude_records_tags:: no regex match of record['Tag']") |
||||||
|
pass |
||||||
|
else: |
||||||
|
self.logger.debug("No tag in record") |
||||||
|
return False |
||||||
|
|
||||||
|
def finalsize(self, size:str) -> str: |
||||||
|
return "{}".format(sizeof_fmt(self.size + int(size))) |
||||||
|
|
||||||
|
def recordadded(self,name: str): |
||||||
|
with open(self.addedrecordspath,'a') as added: |
||||||
|
self.logger.debug('AddedList: ADD {}'.format(name)) |
||||||
|
added.write('{}\n'.format(name)) |
||||||
|
|
||||||
|
def exclude(self, name: str): |
||||||
|
key = "{}_{}_{}".format(name,'0.0.0','all') |
||||||
|
self.jrecords[key] = "" |
||||||
|
with open(self.excludedrecordspath,'a') as excluded: |
||||||
|
self.logger.debug('ExcludedList: ADD {}'.format(name)) |
||||||
|
excluded.write('{}\n'.format(name)) |
||||||
|
|
||||||
|
def add_wanted(self, name: str): |
||||||
|
return self.__add(name) |
||||||
|
|
||||||
|
def __record(self, record, key: str, path: str): |
||||||
|
self.logger.debug('__record(key={},record={})'.format(key,record)) |
||||||
|
if key in record: |
||||||
|
matching = re.match(self.recordkey_regex,record[key]) |
||||||
|
self.logger.debug('__record: matching={}'.format(matching)) |
||||||
|
if matching: |
||||||
|
for comma in re.split(r',', matching.groupdict()['recordkey']): |
||||||
|
self.logger.debug('__record::comma={}'.format(comma)) |
||||||
|
for pipe in re.split(r'\|', comma): |
||||||
|
self.logger.debug('__record::pipe={}'.format(pipe)) |
||||||
|
package = re.match(self.package_regex, pipe) |
||||||
|
self.logger.debug('__record::package={}'.format(package)) |
||||||
|
if package: |
||||||
|
self.logger.info('__record::ADD {} : {} ({})'.format(key,package,type(package))) |
||||||
|
name = package.groupdict()['package'] |
||||||
|
self.logger.debug('__record::WRITE {} : {} ({})'.format(key,name,type(name))) |
||||||
|
with open(path, 'a') as srp: |
||||||
|
srp.write('{}\n'.format(name)) |
||||||
|
if self.interactive: |
||||||
|
print("Add {} package for later : {}".format(key,name)) |
||||||
|
else: |
||||||
|
self.logger.debug("__record::record has no {} section".format(key)) |
||||||
|
self.logger.debug("__record::record.keys() = {}".format(list(record.keys()))) |
||||||
|
pass |
||||||
|
|
||||||
|
def __record_recommend(self, record): |
||||||
|
self.__record(record=record, key='Recommends', path=self.recommendedrecordspath) |
||||||
|
|
||||||
|
def __record_suggest(self, record): |
||||||
|
self.__record(record=record, key='Suggests', path=self.suggestedrecordspath) |
||||||
|
|
||||||
|
def __add_package_from_path(self, key: str, path: str): |
||||||
|
self.logger.setLevel(logging.DEBUG) |
||||||
|
recorded = True |
||||||
|
package_list = set([]) |
||||||
|
while recorded: |
||||||
|
recorded = False |
||||||
|
if os.path.exists(path): |
||||||
|
for name in open(path,'r'): |
||||||
|
package_list.add(name.strip()) |
||||||
|
for name in package_list: |
||||||
|
self.logger.debug("__add_package_from_path::{}".format(key)) |
||||||
|
if name in self.jrecords: |
||||||
|
self.logger.debug("Package {} already registered".format(name)) |
||||||
|
continue |
||||||
|
try: |
||||||
|
record = self.aptcache.record(name) |
||||||
|
self.logger.debug("__add_package_from_path::{} {} with record {}".format(key, name, record)) |
||||||
|
except KeyError as ke: |
||||||
|
self.logger.debug("Package {} does not exist".format(name)) |
||||||
|
if self.interactive: |
||||||
|
print("Package {} does not exist".format(name)) |
||||||
|
continue |
||||||
|
if self.__exclude_tags(record): |
||||||
|
self.exclude(name) |
||||||
|
continue |
||||||
|
if self.interactive: |
||||||
|
print("record: {}".format(record)) |
||||||
|
answer = input("Add {} package: {} ({} -> {})? y/1 (yes) | n/0 (no) l/. (later) | q (quit)".format(key,name,record['Size'],self.finalsize(record['Size']))) |
||||||
|
if answer == 'y' or answer == '1': |
||||||
|
self.__add(name) |
||||||
|
self.recordadded(name) |
||||||
|
recorded = True |
||||||
|
self.__record(record=record, key=key, path=path) |
||||||
|
elif answer == 'n' or answer == '0': |
||||||
|
self.exclude(name) |
||||||
|
continue |
||||||
|
elif answer == 'l' or answer == '.': |
||||||
|
continue |
||||||
|
elif answer == 'q': |
||||||
|
recorded = False |
||||||
|
self.want_exit = True |
||||||
|
break |
||||||
|
|
||||||
|
def add_popcon(self): |
||||||
|
self.__add_package_from_path(key='Popcon', path=self.popconrecordspath) |
||||||
|
|
||||||
|
def add_recommend(self): |
||||||
|
self.__add_package_from_path(key='Recommends', path=self.recommendedrecordspath) |
||||||
|
|
||||||
|
def add_suggest(self): |
||||||
|
self.__add_package_from_path(key='Suggests', path=self.suggestedrecordspath) |
||||||
|
|
||||||
|
class AptCache: |
||||||
|
def __init__(self, jrecords, myfiles, interactive) -> None: |
||||||
|
self.logger = logging.getLogger(__name__) |
||||||
|
self.logger.setLevel(logging.INFO) |
||||||
|
self.cache = apt.Cache() |
||||||
|
self.jrecords = jrecords |
||||||
|
self.myfiles = os.path.expanduser(myfiles) |
||||||
|
self.excludedrecordspath = '/tmp/myfiles_excluded.txt' |
||||||
|
self.interactive = interactive |
||||||
|
self.logger.info('AptCache initiated') |
||||||
|
|
||||||
|
def fetch_source(self, version): |
||||||
|
self.logger.debug('AptCache: fetch_source {}'.format(version)) |
||||||
|
record = version.record |
||||||
|
destfile = '{}/{}'.format(self.myfiles,record['Filename']) |
||||||
|
package_regex = re.compile(r'(?P<directory>[A-Za-z0-9/+.-]+)[/](?P<Package>[a-z0-9+.-]+)[_](?P<Version>[a-z0-9.+-~]+)[_](?P<Architecture>i386|amd64|armhf|all)[.](?P<Format>deb)') |
||||||
|
directory = re.match(package_regex,record['Filename']) |
||||||
|
destdir = '{}/{}'.format(self.myfiles,directory.groupdict()['directory']) |
||||||
|
os.makedirs(destdir, exist_ok=True) |
||||||
|
key = "{}_{}_{}".format(record['Package'],record['Version'],record['Architecture']) |
||||||
|
if not os.path.exists(destfile): |
||||||
|
self.logger.debug("JRecord : download source: {}".format(version)) |
||||||
|
version.fetch_source(destdir=destdir, unpack=False) |
||||||
|
else: |
||||||
|
self.logger.debug("JRecord : already downloaded source : {}".format(version)) |
||||||
|
|
||||||
|
def package(self, name: str): |
||||||
|
self.logger.debug("AptCache: ask package for {}".format(name)) |
||||||
|
return self.cache[name] |
||||||
|
|
||||||
|
def version(self, name: str): |
||||||
|
self.logger.debug("AptCache: ask version for {}".format(name)) |
||||||
|
return self.cache[name].candidate |
||||||
|
|
||||||
|
def record(self, name: str): |
||||||
|
self.logger.debug("AptCache: ask record for {}".format(name)) |
||||||
|
return self.cache[name].candidate.record |
||||||
|
|
||||||
|
def records(self, candidates: [], name: str): |
||||||
|
self.logger.debug("AptCache: ask record with dependencies for {}".format(name)) |
||||||
|
self.logger.debug("AptCache: start with candidates: {}".format(candidates)) |
||||||
|
if name in self.jrecords: |
||||||
|
self.logger.debug("AptCache: already exist: {}".format(name)) |
||||||
|
pass |
||||||
|
else: |
||||||
|
self.logger.debug("AptCache: new record: {}".format(name)) |
||||||
|
if self.cache.is_virtual_package(name): |
||||||
|
self.logger.debug("AptCache: virtualpackage: {}".format(name)) |
||||||
|
names = [] |
||||||
|
for providingpackage in self.cache.get_providing_packages(name): |
||||||
|
self.logger.debug("AptCache: add providing_package: {}".format(providingpackage.name)) |
||||||
|
names.append(providingpackage.name) |
||||||
|
else: |
||||||
|
names = [name] |
||||||
|
self.logger.debug("AptCache: building package list: {}".format(names)) |
||||||
|
for name in names: |
||||||
|
try: |
||||||
|
package = self.package(name) |
||||||
|
except KeyError as ke: |
||||||
|
if interactive: |
||||||
|
print("Package {} does not exist".format(name)) |
||||||
|
answer = input("Package {} does not exist ... Ignore ? y / 1 (yes) / n / 0 (no)".format(name)) |
||||||
|
if answer == 'y' or answer == '1': |
||||||
|
self.jrecords[name] = "" |
||||||
|
with open(self.excludedrecordspath,'a') as excluded: |
||||||
|
self.logger.debug('ExcludedList: ADD {}'.format(name)) |
||||||
|
excluded.write('{}\n'.format(name)) |
||||||
|
continue |
||||||
|
else: |
||||||
|
return [] |
||||||
|
else: |
||||||
|
return [] |
||||||
|
version = package.candidate |
||||||
|
record = version.record |
||||||
|
destfile = '{}/{}'.format(self.myfiles,record['Filename']) |
||||||
|
package_regex = re.compile(r'(?P<directory>[A-Za-z0-9/+.-]+)[/](?P<Package>[a-z0-9+.-]+)[_](?P<Version>[a-z0-9.+-~]+)[_](?P<Architecture>i386|amd64|armhf|all)[.](?P<Format>deb)') |
||||||
|
directory = re.match(package_regex,record['Filename']) |
||||||
|
destdir = '{}/{}'.format(self.myfiles,directory.groupdict()['directory']) |
||||||
|
os.makedirs(destdir, exist_ok=True) |
||||||
|
key = "{}_{}_{}".format(record['Package'],record['Version'],record['Architecture']) |
||||||
|
self.logger.debug("JRecord : new package: {} ({})=> {}".format(key,destdir,record)) |
||||||
|
self.jrecords[key] = record |
||||||
|
if not os.path.exists(destfile): |
||||||
|
self.logger.debug("JRecord : download package: {}".format(version)) |
||||||
|
version.fetch_binary(destdir) |
||||||
|
else: |
||||||
|
self.logger.debug("JRecord : already downloaded package: {}".format(version)) |
||||||
|
pass |
||||||
|
candidates.append(name) |
||||||
|
if 'Depends' in record: |
||||||
|
self.logger.debug("AptCache: dependencies: {}".format(record['Depends'])) |
||||||
|
pass |
||||||
|
for dependency in package.candidate.dependencies: |
||||||
|
for or_dependency in dependency.or_dependencies: |
||||||
|
self.logger.debug("AptCache: or_dependency: {}".format(or_dependency)) |
||||||
|
precandidate_name = or_dependency.name |
||||||
|
self.logger.debug("AptCache: precandidate: {}".format(precandidate_name)) |
||||||
|
candidate_name = re.sub(r'(?P<name>[a-z0-9+.-]+)[:]*.*',r'\g<name>',precandidate_name) |
||||||
|
self.logger.debug("AptCache: candidate: {}".format(candidate_name)) |
||||||
|
#subpackage = self.package(candidate_name) |
||||||
|
#candidate_record = subpackage.candidate.record |
||||||
|
#self.logger.debug("AptCache: candidate_record: {}".format(candidate_record)) |
||||||
|
candidates.extend(self.records([],candidate_name)) |
||||||
|
self.logger.debug("AptCache: return candidates: {}".format(candidates)) |
||||||
|
return candidates |
||||||
|
|
||||||
|
def display_package(self, name: str) -> None: |
||||||
|
pkg = self.cache[name] |
||||||
|
print("Name: %s " % pkg.name) |
||||||
|
print("ID: %s " % pkg.id) |
||||||
|
print("Priority (Candidate): %s " % pkg.candidate.priority) |
||||||
|
print("Priority (Installed): %s " % pkg.installed.priority) |
||||||
|
print("Installed: %s " % pkg.installed.version) |
||||||
|
print("Candidate: %s " % pkg.candidate.version) |
||||||
|
print("CandidateDownloadable: %s" % pkg.candidate.downloadable) |
||||||
|
print("CandidateOrigins: %s" % pkg.candidate.origins) |
||||||
|
print("SourcePkg: %s " % pkg.candidate.source_name) |
||||||
|
print("Summary: %s" % pkg.candidate.summary) |
||||||
|
print("Description (formatted) :\n%s" % pkg.candidate.description) |
||||||
|
print("Description (unformatted):\n%s" % pkg.candidate.raw_description) |
||||||
|
print("InstalledSize: %s " % pkg.candidate.installed_size) |
||||||
|
print("PackageSize: %s " % pkg.candidate.size) |
||||||
|
print("Dependencies: %s" % pkg.installed.dependencies) |
||||||
|
print("Recommends: %s" % pkg.installed.recommends) |
||||||
|
for dep in pkg.candidate.dependencies: |
||||||
|
print( |
||||||
|
",".join( |
||||||
|
f"{o.name} ({o.version}) ({o.relation}) ({o.pre_depend})" |
||||||
|
for o in dep.or_dependencies |
||||||
|
) |
||||||
|
) |
||||||
|
print("arch: %s" % pkg.candidate.architecture) |
||||||
|
print("homepage: %s" % pkg.candidate.homepage) |
||||||
|
print("rec: ", pkg.candidate.record) |
||||||
|
|
||||||
|
if __name__ == u'__main__': |
||||||
|
''' |
||||||
|
import apt |
||||||
|
c = apt.Cache() |
||||||
|
p = c['apt-file'] |
||||||
|
v = p.candidate |
||||||
|
r = v.record |
||||||
|
''' |
||||||
|
import argparse |
||||||
|
parser = argparse.ArgumentParser(description="A CLI tool to generate custom jigdo files") |
||||||
|
parser.add_argument("-e","--excluded",default='excluded.txt',help="File containing packages to exclude") |
||||||
|
parser.add_argument("-f","--files",default='~/myfiles',help="Directory added to custom original image") |
||||||
|
parser.add_argument("-i","--isodir",help="Original path of xorriso extracted iso file") |
||||||
|
parser.add_argument("-j","--jigdofile",help="Original path of jigdo file to customize. The cd netinst jigdo is recommended.") |
||||||
|
parser.add_argument("-m","--maxsize",type=int,default=3500000000,help="Fix a limit to iso size") |
||||||
|
parser.add_argument("-p","--popcon",action='store_true',help="Suggest packages from popcon statistics") |
||||||
|
parser.add_argument("-q","--quiet",action='store_true',help="Do not prompt, do not ask") |
||||||
|
parser.add_argument("-r","--recommend",action='store_true',help="Suggest packages from recommended records") |
||||||
|
parser.add_argument("-s","--suggest",action='store_true',help="Suggest packages from suggested records") |
||||||
|
parser.add_argument("-t","--excludedtags",default='excluded_tags.txt',help="Exclude package with specific tags") |
||||||
|
parser.add_argument("-w","--wanted",default='wanted.txt',help="File containing packages to include") |
||||||
|
args = parser.parse_args() |
||||||
|
if os.path.exists('/tmp/jigdo.log'): |
||||||
|
os.remove('/tmp/jigdo.log') |
||||||
|
#logging.config.dictConfig(logging_config) |
||||||
|
logging.basicConfig(filename='/tmp/myfiles.log',level=logging.DEBUG) |
||||||
|
logger=logging.getLogger(__name__) |
||||||
|
interactive = not args.quiet |
||||||
|
jrecords = JRecords() |
||||||
|
myfiles = LocalFiles(args.files) |
||||||
|
aptcache = AptCache(jrecords,args.files,interactive) |
||||||
|
if args.isodir: |
||||||
|
originalpackagesource = IsoDir(args.isodir) |
||||||
|
elif args.jigdofile: |
||||||
|
originalpackagesource = JigdoFile(args.jigdofile,args.label) |
||||||
|
else: |
||||||
|
raise SyntaxError('You have to specify a minimal source (isodir or jigdofile)') |
||||||
|
wantedlist = WantedList(args.wanted) |
||||||
|
excludedlist = ExcludedList(args.excluded) |
||||||
|
excludedtags = ExcludedTags(args.excludedtags) |
||||||
|
controller = Controller(aptcache=aptcache, jrecords=jrecords, originalpackagesource=originalpackagesource, localfiles=myfiles, maxsize=args.maxsize, excludedlist=excludedlist, excludedtags=excludedtags, interactive=interactive) |
||||||
|
for wanted in iter(wantedlist): |
||||||
|
if not controller.add_wanted(wanted): |
||||||
|
break |
||||||
|
if interactive: |
||||||
|
print('Wanted Packages added successfully ...') |
||||||
|
recommend = False |
||||||
|
suggest = False |
||||||
|
popcon = False |
||||||
|
if not args.recommend and not controller.clean_exit(): |
||||||
|
answer = input("Check for adding recommended package ? (y/N) (1/0)") |
||||||
|
if answer == 'y' or answer == '1': |
||||||
|
recommend = True |
||||||
|
if args.recommend or recommend and not controller.clean_exit(): |
||||||
|
controller.add_recommend() |
||||||
|
if not args.suggest and not controller.clean_exit(): |
||||||
|
answer = input("Check for adding suggested package ? (y/N)") |
||||||
|
if answer == 'y' or answer == '1': |
||||||
|
suggest = True |
||||||
|
if args.suggest or suggest and not controller.clean_exit(): |
||||||
|
controller.add_suggest() |
||||||
|
if not args.popcon and not controller.clean_exit(): |
||||||
|
answer = input("Check for adding popcon package ? (y/N)") |
||||||
|
if answer == 'y' or answer == '1': |
||||||
|
popcon = True |
||||||
|
if args.popcon or popcon and not controller.clean_exit(): |
||||||
|
print('Checking popcon suggestions ...') |
||||||
|
popconlist = PopConList() |
||||||
|
controller.add_popcon() |
||||||
|
|
||||||
@ -0,0 +1,33 @@ |
|||||||
|
gdm3 |
||||||
|
chromium |
||||||
|
www-browser |
||||||
|
firefox |
||||||
|
firefox-esr |
||||||
|
apt-listchanges |
||||||
|
debian-reference-ja |
||||||
|
debian-reference-id |
||||||
|
debian-reference-zh-tw |
||||||
|
nvidia-opencl-icd |
||||||
|
adwaita-icon-theme |
||||||
|
debian-reference-it |
||||||
|
debian-reference-zh-cn |
||||||
|
libclipboard-perl |
||||||
|
debian-reference-es |
||||||
|
glyrc |
||||||
|
brltty-x11 |
||||||
|
gcc-multilib |
||||||
|
libosra-dev |
||||||
|
libgm2-17-mips64el-cross |
||||||
|
getdp-sparskit |
||||||
|
libyuv-utils |
||||||
|
librust-strsim-0.9+default-dev |
||||||
|
librust-strsim-0.8+default-dev |
||||||
|
librust-strsim-0.7+default-developers |
||||||
|
exim4-daemon-custom |
||||||
|
postfix |
||||||
|
init-d-script |
||||||
|
iproute |
||||||
|
openssl-provider-legacy |
||||||
|
w3m-ssl |
||||||
|
w3mee |
||||||
|
dbus-system-bus |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
interface::graphical |
||||||
|
interface::x11 |
||||||
|
suite::gnome |
||||||
|
uitoolkit::gtk |
||||||
|
x11::application |
||||||
|
culture::japanese |
||||||
|
uitoolkit::gtk |
||||||
|
culture::portuguese |
||||||
|
culture::italian |
||||||
|
x11::font |
||||||
|
culture::hungarian |
||||||
@ -0,0 +1,451 @@ |
|||||||
|
abcde |
||||||
|
abs-guide |
||||||
|
acpi-fakekey |
||||||
|
acpi-support |
||||||
|
adb |
||||||
|
aldo |
||||||
|
alsa-utils |
||||||
|
anacron |
||||||
|
android-sdk-platform-tools-common |
||||||
|
apachetop |
||||||
|
apache2 |
||||||
|
apache2-doc |
||||||
|
apt-doc |
||||||
|
aptitude |
||||||
|
arpalert |
||||||
|
arpwatch |
||||||
|
aspell-en |
||||||
|
aspell-fr |
||||||
|
atop |
||||||
|
autoconf |
||||||
|
automake |
||||||
|
bash-completion |
||||||
|
bash-doc |
||||||
|
beep |
||||||
|
bettercap |
||||||
|
binfmt-support |
||||||
|
bison |
||||||
|
bison-doc |
||||||
|
bluemon |
||||||
|
bluetooth |
||||||
|
bluez-alsa-utils |
||||||
|
bluez-firmware |
||||||
|
bluez-hcidump |
||||||
|
bluez-meshd |
||||||
|
bluez-obexd |
||||||
|
bluez-source |
||||||
|
bluez-test-scripts |
||||||
|
bluez-test-tools |
||||||
|
bluez-tools |
||||||
|
bmon |
||||||
|
bookworm |
||||||
|
bpfmon |
||||||
|
btop |
||||||
|
btscanner |
||||||
|
build-essential |
||||||
|
busybox |
||||||
|
busybox-static |
||||||
|
bzip2-doc |
||||||
|
bwm-ng |
||||||
|
bzr |
||||||
|
ca-certificates |
||||||
|
calibre |
||||||
|
care |
||||||
|
cbm |
||||||
|
cdrkit-doc |
||||||
|
cgoban |
||||||
|
chrony |
||||||
|
clang-tools-19 |
||||||
|
connman |
||||||
|
connman-dev |
||||||
|
connman-vpn |
||||||
|
conserver-client |
||||||
|
conserver-server |
||||||
|
console-common |
||||||
|
console-data |
||||||
|
convlit |
||||||
|
cpipe |
||||||
|
cpp-12-doc |
||||||
|
cpustat |
||||||
|
cw |
||||||
|
cwcp |
||||||
|
cwdaemon |
||||||
|
dablin |
||||||
|
dbeacon |
||||||
|
ddccontrol |
||||||
|
debconf-utils |
||||||
|
debian-faq |
||||||
|
debian-handbook |
||||||
|
debian-installer |
||||||
|
debian-installer-12-netboot-amd64 |
||||||
|
debian-installer-12-netboot-armhf |
||||||
|
debian-installer-12-netboot-i386 |
||||||
|
debian-kernel-handbook |
||||||
|
debian-policy |
||||||
|
debian-reference |
||||||
|
debian-reference-fr |
||||||
|
debtags |
||||||
|
default-jdk-headless |
||||||
|
detox |
||||||
|
developers-reference |
||||||
|
developers-reference-fr |
||||||
|
dict |
||||||
|
dictd |
||||||
|
dict-wn |
||||||
|
dillo |
||||||
|
direvent |
||||||
|
direwolf |
||||||
|
dlocate |
||||||
|
dmidecode |
||||||
|
dnsmasq |
||||||
|
dnsmasq-base |
||||||
|
dns-root-data |
||||||
|
doc-debian |
||||||
|
dosfstools |
||||||
|
dpkg |
||||||
|
dtv-scan-tables |
||||||
|
dvb-apps |
||||||
|
dvb-tools |
||||||
|
dvblast |
||||||
|
dvbpsi-utils |
||||||
|
dvbstream |
||||||
|
dvbstreamer |
||||||
|
dvbtune |
||||||
|
ebook2cw |
||||||
|
ed |
||||||
|
emacs-common-non-dfsg |
||||||
|
emacs-nox |
||||||
|
epub-utils |
||||||
|
ethstatus |
||||||
|
ethtool |
||||||
|
exfatprogs |
||||||
|
exim4-doc-html |
||||||
|
fakeroot |
||||||
|
fancontrol |
||||||
|
farpd |
||||||
|
fbcat |
||||||
|
fbi |
||||||
|
fbreader |
||||||
|
fdisk |
||||||
|
feh |
||||||
|
fim |
||||||
|
firewalld |
||||||
|
firmware-atheros |
||||||
|
firmware-iwlwifi |
||||||
|
firmware-misc-nonfree |
||||||
|
firmware-realtek |
||||||
|
firmware-realtek-rtl8723cs-bt |
||||||
|
flashrom |
||||||
|
flex |
||||||
|
flex-doc |
||||||
|
flim |
||||||
|
forkstat |
||||||
|
fortran95-compiler |
||||||
|
fspy |
||||||
|
gawk |
||||||
|
gcc-12-doc |
||||||
|
gcc-doc |
||||||
|
gdb |
||||||
|
gdb-doc |
||||||
|
gdbserver |
||||||
|
genisoimage |
||||||
|
getstream |
||||||
|
gfortran |
||||||
|
gfortran-12-doc |
||||||
|
gfortran-12-multilib |
||||||
|
gimp |
||||||
|
gimp-help-en |
||||||
|
git |
||||||
|
git-doc |
||||||
|
git-svn |
||||||
|
gitweb |
||||||
|
glibc-doc |
||||||
|
gnugo |
||||||
|
gnupg |
||||||
|
go-bluetooth |
||||||
|
gparted |
||||||
|
gpicview |
||||||
|
gpm |
||||||
|
gpsd |
||||||
|
gpsd-tools |
||||||
|
gqrx-sdr |
||||||
|
groff-base |
||||||
|
grub-common |
||||||
|
grub-pc |
||||||
|
grub-pc-bin |
||||||
|
grub2-common |
||||||
|
gsasl-common |
||||||
|
hyphen-fr |
||||||
|
horst |
||||||
|
htop |
||||||
|
hwinfo |
||||||
|
i2c-tools |
||||||
|
icewm |
||||||
|
installation-guide-i386 |
||||||
|
ioping |
||||||
|
iotop |
||||||
|
iotop-c |
||||||
|
ipband |
||||||
|
iperf3 |
||||||
|
iproute2-doc |
||||||
|
ipset |
||||||
|
iptotal |
||||||
|
iptraf-ng |
||||||
|
ir-keytable |
||||||
|
iwd |
||||||
|
kannel-extras |
||||||
|
kicad |
||||||
|
kicad-doc-fr |
||||||
|
kmon |
||||||
|
kpcli |
||||||
|
krb5-doc |
||||||
|
libalgorithm-diff-xs-perl |
||||||
|
libalgorithm-merge-perl |
||||||
|
libapache2-mod-ruwsgi |
||||||
|
libasound2-plugin-bluez |
||||||
|
libauthen-ntlm-perl |
||||||
|
libauthen-sasl-perl |
||||||
|
libbluetooth3 |
||||||
|
libbluetooth-dev |
||||||
|
libbtbb1 |
||||||
|
libbtbb-dev |
||||||
|
libcapture-tiny-perl |
||||||
|
libcgi-fast-perl |
||||||
|
libclang-rt-14-dev |
||||||
|
libclang-rt-16-dev |
||||||
|
libclang-rt-19-dev |
||||||
|
libcwiid1 |
||||||
|
libdata-dump-perl |
||||||
|
libdata-password-perl |
||||||
|
libdigest-sha-perl |
||||||
|
libdvdcss2 |
||||||
|
libfcgi-bin |
||||||
|
libfftw3-dev |
||||||
|
libfile-fcntllock-perl |
||||||
|
libfuture-asyncawait-perl |
||||||
|
libhtml-format-perl |
||||||
|
libhtml-form-perl |
||||||
|
libhttp-daemon-perl |
||||||
|
libio-socket-inet6-perl |
||||||
|
libio-socket-socks-perl |
||||||
|
libiw-dev |
||||||
|
libjs-leaflet.markercluster |
||||||
|
libjs-sizzle |
||||||
|
libltdl-dev |
||||||
|
libmailtools-perl |
||||||
|
libmath-random-isaac-perl |
||||||
|
libmath-random-isaac-xs-perl |
||||||
|
libmbim-utils |
||||||
|
libmojo-server-fastcgi-perl |
||||||
|
libncurses-dev |
||||||
|
libnet-idn-encode-perl |
||||||
|
libnet-libidn-perl |
||||||
|
libnss-myhostname |
||||||
|
libnss-mymachines |
||||||
|
libnss-nisplus |
||||||
|
libnss-resolve |
||||||
|
libparted-dev |
||||||
|
libqmi-utils |
||||||
|
libreoffice |
||||||
|
libreoffice-help-fr |
||||||
|
libreoffice-l10n-fr |
||||||
|
librole-tiny-perl |
||||||
|
librust-clap-2-dev |
||||||
|
librust-proc-macro2-dev |
||||||
|
libssl-doc |
||||||
|
libtest-hexstring-perl |
||||||
|
libtool |
||||||
|
libtool-doc |
||||||
|
libxml-sax-expat-perl |
||||||
|
libyaml-libyaml-perl |
||||||
|
libyaml-syck-perl |
||||||
|
libwrap0 |
||||||
|
links2 |
||||||
|
linux-doc |
||||||
|
linux-image-686-pae |
||||||
|
linux-libc-dev |
||||||
|
linux-source |
||||||
|
lirc |
||||||
|
llvm-13-dev |
||||||
|
llvm-14-dev |
||||||
|
llvm-15-dev |
||||||
|
llvm-16-dev |
||||||
|
llvm-19-dev |
||||||
|
lm-sensors |
||||||
|
local-apt-repository |
||||||
|
locate |
||||||
|
logrotate |
||||||
|
low-memory-monitor |
||||||
|
low-memory-monitor-doc |
||||||
|
lshw |
||||||
|
lsof |
||||||
|
lynx |
||||||
|
m4 |
||||||
|
m4-doc |
||||||
|
mailcap |
||||||
|
mailutils |
||||||
|
manpages |
||||||
|
manpages-fr |
||||||
|
mc |
||||||
|
memtest86+ |
||||||
|
mighttpd2 |
||||||
|
modemmanager |
||||||
|
modemmanager-dev |
||||||
|
modemmanager-doc |
||||||
|
morse |
||||||
|
morse2ascii |
||||||
|
mumudvb |
||||||
|
mupdf |
||||||
|
mutt |
||||||
|
mythes-fr |
||||||
|
mwc |
||||||
|
ncdu |
||||||
|
ncftp |
||||||
|
ncurses-examples |
||||||
|
ncurses-term |
||||||
|
netcat-openbsd |
||||||
|
netplug |
||||||
|
netproc |
||||||
|
nfs-common |
||||||
|
nfstrace |
||||||
|
nfstrace-doc |
||||||
|
nftables |
||||||
|
nginx |
||||||
|
nginx-doc |
||||||
|
nginx-full |
||||||
|
nginx-light |
||||||
|
nload |
||||||
|
node-jquery |
||||||
|
ntfs-3g |
||||||
|
nyx |
||||||
|
obexfs |
||||||
|
obexftp |
||||||
|
obfs4proxy |
||||||
|
ofono |
||||||
|
onionprobe |
||||||
|
oomd |
||||||
|
openssh-server |
||||||
|
openssl |
||||||
|
openssl-provider-legacy |
||||||
|
packit |
||||||
|
pagemon |
||||||
|
parted |
||||||
|
pcapfix |
||||||
|
pcp |
||||||
|
pgpainless-cli |
||||||
|
pkg-config |
||||||
|
pmount |
||||||
|
polkitd-pkla |
||||||
|
powermgmt-base |
||||||
|
ppp |
||||||
|
pulseaudio |
||||||
|
pxelinux |
||||||
|
python3.11-doc |
||||||
|
python3-bleak |
||||||
|
python3-bluez |
||||||
|
python3-brotli |
||||||
|
python3-cap-ng |
||||||
|
python3-crccheck |
||||||
|
python3-cryptography |
||||||
|
python3-doc |
||||||
|
python3-fastimport |
||||||
|
python3-github |
||||||
|
python3-gpg |
||||||
|
python3-invoke |
||||||
|
python3-json-pointer |
||||||
|
python3-keyring |
||||||
|
python3-launchpadlib |
||||||
|
python3-openssl |
||||||
|
python3-paramiko |
||||||
|
python3-requests-oauthlib |
||||||
|
python3-socks |
||||||
|
python-bleak-doc |
||||||
|
python-openssl-doc |
||||||
|
python-requests-doc |
||||||
|
python3-rfc3987 |
||||||
|
python3-uno |
||||||
|
python3-uritemplate |
||||||
|
python3-webcolors |
||||||
|
qjoypad |
||||||
|
read-edid |
||||||
|
readline-doc |
||||||
|
rfkill |
||||||
|
rsync |
||||||
|
rtl-sdr |
||||||
|
runit-helper |
||||||
|
s-tui |
||||||
|
screen |
||||||
|
sidedoor |
||||||
|
slurm |
||||||
|
speex-doc |
||||||
|
sq |
||||||
|
sqop |
||||||
|
ssl-cert |
||||||
|
ssldump |
||||||
|
stockfish |
||||||
|
strace |
||||||
|
sudo |
||||||
|
supercat |
||||||
|
sxiv |
||||||
|
syslinux-common |
||||||
|
systemd-boot |
||||||
|
systemd-container |
||||||
|
systemd-resolved |
||||||
|
tar-doc |
||||||
|
tcpdump |
||||||
|
testdisk |
||||||
|
texinfo |
||||||
|
tftpd-hpa |
||||||
|
tlp-rdw |
||||||
|
tmux |
||||||
|
tor |
||||||
|
tor-geoipdb |
||||||
|
torsocks |
||||||
|
traceroute |
||||||
|
tsdecrypt |
||||||
|
tshark |
||||||
|
tvoe |
||||||
|
udisks2 |
||||||
|
unzip |
||||||
|
usb.ids |
||||||
|
usb-modeswitch |
||||||
|
ussp-push-dbg |
||||||
|
uvccapture |
||||||
|
uwsgi-core |
||||||
|
viewnior |
||||||
|
vim-doc |
||||||
|
vim-nox |
||||||
|
vlc |
||||||
|
w-scan |
||||||
|
w-scan-cpp |
||||||
|
w3m |
||||||
|
w3m-el |
||||||
|
w3m-img |
||||||
|
wajig |
||||||
|
wamerican |
||||||
|
welle.io |
||||||
|
wfrench |
||||||
|
wget |
||||||
|
wireless-regdb |
||||||
|
wireless-tools |
||||||
|
wireshark |
||||||
|
wireshark-doc |
||||||
|
wl |
||||||
|
wl-beta |
||||||
|
wodim |
||||||
|
wpasupplicant |
||||||
|
wvdial |
||||||
|
x11-apps |
||||||
|
xbitmaps |
||||||
|
xboard |
||||||
|
xchm |
||||||
|
xfsdump |
||||||
|
xinit |
||||||
|
xl2tpd |
||||||
|
xli |
||||||
|
xloadimage |
||||||
|
xpdf |
||||||
|
xorriso |
||||||
|
xz-utils |
||||||
|
zip |
||||||
|
zvbi |
||||||
@ -0,0 +1,283 @@ |
|||||||
|
abs-guide |
||||||
|
acpi-fakekey |
||||||
|
acpi-support |
||||||
|
alsa-utils |
||||||
|
anacron |
||||||
|
apt-doc |
||||||
|
autoconf |
||||||
|
automake |
||||||
|
bash-completion |
||||||
|
bash-doc |
||||||
|
beep |
||||||
|
bettercap |
||||||
|
binfmt-support |
||||||
|
bison |
||||||
|
bison-doc |
||||||
|
bluemon |
||||||
|
bluetooth |
||||||
|
bluez-alsa-utils |
||||||
|
bluez-firmware |
||||||
|
bluez-hcidump |
||||||
|
bluez-meshd |
||||||
|
bluez-obexd |
||||||
|
bluez-source |
||||||
|
bluez-test-scripts |
||||||
|
bluez-test-tools |
||||||
|
bluez-tools |
||||||
|
bmon |
||||||
|
bpfmon |
||||||
|
bsdextrautils |
||||||
|
btop |
||||||
|
btscanner |
||||||
|
build-essential |
||||||
|
busybox |
||||||
|
busybox-static |
||||||
|
bzip2-doc |
||||||
|
bwm-ng |
||||||
|
ca-certificates |
||||||
|
care |
||||||
|
cbm |
||||||
|
cdrkit-doc |
||||||
|
chrony |
||||||
|
clang-tools-19 |
||||||
|
connman |
||||||
|
connman-dev |
||||||
|
connman-vpn |
||||||
|
conserver-client |
||||||
|
conserver-server |
||||||
|
console-common |
||||||
|
console-data |
||||||
|
convlit |
||||||
|
cpipe |
||||||
|
cpp-12-doc |
||||||
|
cpustat |
||||||
|
debconf-utils |
||||||
|
debian-faq |
||||||
|
debian-handbook |
||||||
|
debian-installer |
||||||
|
debian-installer-12-netboot-amd64 |
||||||
|
debian-installer-12-netboot-armhf |
||||||
|
debian-installer-12-netboot-i386 |
||||||
|
debian-kernel-handbook |
||||||
|
debian-policy |
||||||
|
debian-reference |
||||||
|
debian-reference-fr |
||||||
|
debtags |
||||||
|
default-jdk-headless |
||||||
|
detox |
||||||
|
developers-reference |
||||||
|
developers-reference-fr |
||||||
|
direvent |
||||||
|
dlocate |
||||||
|
dmidecode |
||||||
|
dnsmasq |
||||||
|
dnsmasq-base |
||||||
|
dns-root-data |
||||||
|
doc-debian |
||||||
|
dosfstools |
||||||
|
dpkg |
||||||
|
ed |
||||||
|
emacs-common-non-dfsg |
||||||
|
emacs-nox |
||||||
|
ethstatus |
||||||
|
ethtool |
||||||
|
exfatprogs |
||||||
|
fakeroot |
||||||
|
fancontrol |
||||||
|
farpd |
||||||
|
fbcat |
||||||
|
fbi |
||||||
|
fdisk |
||||||
|
firewalld |
||||||
|
firmware-atheros |
||||||
|
firmware-iwlwifi |
||||||
|
firmware-misc-nonfree |
||||||
|
firmware-realtek |
||||||
|
firmware-realtek-rtl8723cs-bt |
||||||
|
flashrom |
||||||
|
flex |
||||||
|
flex-doc |
||||||
|
forkstat |
||||||
|
fortran95-compiler |
||||||
|
fspy |
||||||
|
gawk |
||||||
|
gcc-12-doc |
||||||
|
gcc-doc |
||||||
|
gdb |
||||||
|
gdb-doc |
||||||
|
gdbserver |
||||||
|
genisoimage |
||||||
|
gfortran |
||||||
|
gfortran-12-doc |
||||||
|
gfortran-12-multilib |
||||||
|
git |
||||||
|
git-doc |
||||||
|
git-svn |
||||||
|
glibc-doc |
||||||
|
gnupg |
||||||
|
gpm |
||||||
|
groff-base |
||||||
|
grub-common |
||||||
|
grub-pc |
||||||
|
grub-pc-bin |
||||||
|
grub2-common |
||||||
|
gsasl-common |
||||||
|
horst |
||||||
|
htop |
||||||
|
hwinfo |
||||||
|
i2c-tools |
||||||
|
installation-guide-i386 |
||||||
|
ioping |
||||||
|
iotop |
||||||
|
iotop-c |
||||||
|
ipband |
||||||
|
iperf3 |
||||||
|
iproute2 |
||||||
|
iproute2-doc |
||||||
|
ipset |
||||||
|
iptotal |
||||||
|
iptraf-ng |
||||||
|
ir-keytable |
||||||
|
iwd |
||||||
|
kpcli |
||||||
|
krb5-doc |
||||||
|
linux-doc |
||||||
|
linux-image-686-pae |
||||||
|
linux-libc-dev |
||||||
|
linux-source |
||||||
|
lirc |
||||||
|
llvm-13-dev |
||||||
|
llvm-14-dev |
||||||
|
llvm-15-dev |
||||||
|
llvm-16-dev |
||||||
|
llvm-19-dev |
||||||
|
lm-sensors |
||||||
|
local-apt-repository |
||||||
|
locate |
||||||
|
logrotate |
||||||
|
low-memory-monitor |
||||||
|
low-memory-monitor-doc |
||||||
|
lshw |
||||||
|
lsof |
||||||
|
lynx |
||||||
|
m4 |
||||||
|
m4-doc |
||||||
|
mailcap |
||||||
|
mailutils |
||||||
|
manpages |
||||||
|
manpages-fr |
||||||
|
mc |
||||||
|
memtest86+ |
||||||
|
modemmanager |
||||||
|
modemmanager-dev |
||||||
|
modemmanager-doc |
||||||
|
mutt |
||||||
|
ncdu |
||||||
|
ncftp |
||||||
|
ncurses-examples |
||||||
|
ncurses-term |
||||||
|
netcat-openbsd |
||||||
|
netplug |
||||||
|
netproc |
||||||
|
nfs-common |
||||||
|
nfstrace |
||||||
|
nfstrace-doc |
||||||
|
nftables |
||||||
|
nload |
||||||
|
ntfs-3g |
||||||
|
nyx |
||||||
|
obexfs |
||||||
|
obexftp |
||||||
|
obfs4proxy |
||||||
|
onionprobe |
||||||
|
oomd |
||||||
|
openssh-server |
||||||
|
openssl |
||||||
|
openssl-provider-legacy |
||||||
|
packit |
||||||
|
pagemon |
||||||
|
parted |
||||||
|
pcapfix |
||||||
|
pmount |
||||||
|
polkitd-pkla |
||||||
|
powermgmt-base |
||||||
|
ppp |
||||||
|
pulseaudio |
||||||
|
pxelinux |
||||||
|
python3.11-doc |
||||||
|
python3-bleak |
||||||
|
python3-bluez |
||||||
|
python3-brotli |
||||||
|
python3-cap-ng |
||||||
|
python3-crccheck |
||||||
|
python3-cryptography |
||||||
|
python3-doc |
||||||
|
python3-fastimport |
||||||
|
python3-github |
||||||
|
python3-gpg |
||||||
|
python3-invoke |
||||||
|
python3-json-pointer |
||||||
|
python3-keyring |
||||||
|
python3-launchpadlib |
||||||
|
python3-openssl |
||||||
|
python3-paramiko |
||||||
|
python3-requests-oauthlib |
||||||
|
python3-socks |
||||||
|
python-bleak-doc |
||||||
|
python-openssl-doc |
||||||
|
python-requests-doc |
||||||
|
python3-rfc3987 |
||||||
|
python3-uritemplate |
||||||
|
python3-webcolors |
||||||
|
qemu-guest-agent |
||||||
|
read-edid |
||||||
|
readline-doc |
||||||
|
rfkill |
||||||
|
rsync |
||||||
|
runit-helper |
||||||
|
s-tui |
||||||
|
screen |
||||||
|
sidedoor |
||||||
|
slurm |
||||||
|
sq |
||||||
|
sqop |
||||||
|
ssl-cert |
||||||
|
ssldump |
||||||
|
strace |
||||||
|
sudo |
||||||
|
supercat |
||||||
|
syslinux-common |
||||||
|
systemd-boot |
||||||
|
systemd-container |
||||||
|
systemd-resolved |
||||||
|
tar-doc |
||||||
|
tcpdump |
||||||
|
testdisk |
||||||
|
texinfo |
||||||
|
tftpd-hpa |
||||||
|
tmux |
||||||
|
tor |
||||||
|
tor-geoipdb |
||||||
|
torsocks |
||||||
|
traceroute |
||||||
|
tshark |
||||||
|
udisks2 |
||||||
|
unzip |
||||||
|
usb.ids |
||||||
|
usb-modeswitch |
||||||
|
ussp-push-dbg |
||||||
|
uvccapture |
||||||
|
vim-doc |
||||||
|
vim-nox |
||||||
|
w3m |
||||||
|
w3m-el |
||||||
|
wajig |
||||||
|
wget |
||||||
|
wireless-regdb |
||||||
|
wireless-tools |
||||||
|
wodim |
||||||
|
wpasupplicant |
||||||
|
wvdial |
||||||
|
xorriso |
||||||
|
xz-utils |
||||||
|
zip |
||||||
Loading…
Reference in new issue