7 changed files with 1812 additions and 1 deletions
@ -1,3 +1,57 @@
@@ -1,3 +1,57 @@
|
||||
# customize_debian_iso |
||||
|
||||
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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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