Anatomia, maintainer scripts, dpkg-deb, debhelper, dpkg-buildpackage e repository APT firmati. Tutto quello che serve per costruire un .deb nel 2026 senza ricorrere a Snap.
"Mando il software in produzione con un tar.gz e uno script di install che chiede sudo." — il modo peggiore di distribuire software su Debian, senza eccezioni.
Un archivio di metadati e file da copiare. Inventato nel 1995, sopravvive a tutto.
Un .deb è un archivio Unix ar (non tar!) che contiene esattamente tre file: la versione del formato, i metadati (control), e i file da copiare sul filesystem (data). Punto.
L'estensione è .deb, il MIME type è application/vnd.debian.binary-package. Il formato è stato disegnato da Bdale Garbee e Ian Murdock nel 1995 (sì, esiste da prima di JavaScript) e è rimasto sostanzialmente identico da allora — quando un formato funziona, non si tocca.
Debian, Ubuntu, Mint, Pop!_OS, Devuan, Kali, Raspberry Pi OS, Proxmox, ...
RHEL, Fedora, openSUSE, Rocky, Alma. Stesso scopo, formato diverso.
"Universali", pesano 200 MB per un editor di testo, montano squashfs ad ogni avvio.
Un .deb è una scatola IKEA: dentro trovi i pezzi (i file da copiare), il foglietto delle istruzioni (i maintainer scripts), e l'etichetta sul cartone (il control file). Il sistema operativo è il vostro garage: apt apre la scatola, segue le istruzioni, e i pezzi finiscono al posto giusto. Senza il .deb avete un mucchio di assi e tre viti, il che è quello che ./configure && make && sudo make install vi lascia da gestire a mano.
apt sa: risolvere dipendenze, fare upgrade atomici, riconoscere conffile modificati, fare downgrade, rimuovere pulito, gestire alternative. Tutto questo gratis, da 30 anni. Voi dovreste reimplementarlo? Davvero?
Tre file dentro un archivio ar. Niente di più.
Vi mostro l'interno di un pacchetto qualsiasi:
$ ar t nginx_1.24.0-2_amd64.deb
debian-binary
control.tar.xz
data.tar.xz
# Estrazione completa per ispezione
$ mkdir extract && cd extract
$ ar x ../nginx_1.24.0-2_amd64.deb
$ ls -la
# debian-binary control.tar.xz data.tar.xz
$ cat debian-binary
2.0
# È un file di testo con DUE byte significativi: "2.0\n"
| Membro | Cosa contiene | Format |
|---|---|---|
debian-binary | Versione del formato. Sempre 2.0 da decenni. | testo, 4 byte |
control.tar.xz | File control + maintainer scripts + md5sums + conffiles | tar compresso (gz/xz/zst) |
data.tar.xz | I file veri da installare, con percorsi assoluti relativi a / | tar compresso (gz/xz/zst) |
debian-binary deve essere il primo membro dell'archivio ar. Se lo metti in fondo, alcune implementazioni di dpkg e parser custom rifiutano il pacchetto. dpkg-deb --build lo gestisce per te — rilevante solo se costruisci a mano con ar.
control.tar.xzdata.tar.xzI percorsi sono relativi alla root / — dpkg esegue l'estrazione a /. Se nel tar metti ./usr/bin/myapp finisce in /usr/bin/myapp.
control e le dipendenzeIl documento d'identità del pacchetto. Dove dichiari chi sei, cosa ti serve, con cosa litighi.
control è un file di testo con campi Chiave: valore, ispirato al formato delle email. I campi multi-riga indentano la riga successiva con uno spazio (oppure un . singolo per indicare paragrafo vuoto nelle descrizioni).
Package: myapp
Version: 1.2.3-1
Architecture: amd64
Maintainer: Tuo Nome <[email protected]>
Installed-Size: 4567
Depends: python3 (>= 3.10), nginx (>= 1.18), libssl3
Recommends: postgresql-client
Suggests: redis-server
Conflicts: myapp-legacy
Replaces: myapp-legacy
Provides: webapp-runtime
Section: web
Priority: optional
Homepage: https://myapp.example.com
Description: Web dashboard per qualcosa di utile
Riga lunga con un paragrafo iniziale che riassume il pacchetto.
.
Secondo paragrafo (il punto su una riga indica riga vuota).
Caratteri di continuazione sempre con uno spazio iniziale.
| Campo | Note |
|---|---|
Package | Nome del pacchetto. Lowercase, niente spazi, alfanumerico + -+. |
Version | Versione (vedi sezione 11) |
Architecture | amd64, arm64, i386, armhf, all (no-arch), any |
Maintainer | Nome <email>. Anche se sei solo, scrivilo: chi debugga vuole sapere chi chiamare |
Description | Una riga short + righe extended indentate |
| Campo | Significato | Quando usarlo |
|---|---|---|
Depends | Hard requirement | Se manca, apt rifiuta install |
Pre-Depends | Deve essere configurato prima dell'unpack | Solo per cose come libc6 |
Recommends | "Quasi sempre vuoi anche questo" | Installato di default, rimovibile |
Suggests | "Potrebbe interessarti" | Solo menzionato |
Enhances | "Migliora questo altro pacchetto" | Inverso di Suggests |
Conflicts | Non puoi avere entrambi installati | Apt sceglie cosa rimuovere |
Breaks | Conflicts ma upgrade-friendly | "<= versione X di X" rotta |
Replaces | "Sono io il successore di X" | Permette di sovrascrivere file |
Provides | Pacchetto virtuale | Es. mail-transport-agent |
(>> 1.0) strettamente maggiore, (>= 1.0) maggiore o uguale, (= 1.0) esatto, (<< 2.0) strettamente minore, (<= 2.0) minore o uguale. Combinabili con virgole (AND) e pipe (OR): nginx | apache2 (>= 2.4).
Section: categoria archivistica. Esempi: admin, devel, net, web, utils, libs, python, doc, misc. Se distribuisci fuori dall'archivio Debian ufficiale, è quasi cosmetico.
Priority: required (es. libc6, non puoi rimuoverlo), important, standard (parte dell'install minimale), optional (default per i tuoi pacchetti), extra (deprecato, usa optional).
Essential: yes: il pacchetto non si può rimuovere senza --force-remove-essential. Riservato a libc6, bash, coreutils. Non usarlo per app vostre, mai.
Dove vanno i tuoi file: la convenzione che lintian ti farà rispettare anche se non vuoi.
Ogni file in un .deb deve andare al posto giusto, secondo il Filesystem Hierarchy Standard. Mettere un binario in /opt/myapp/bin/ è tecnicamente legale, ma lintian ti dirà che fai schifo, e i package reviewer ti rispediranno il pacchetto.
| Path | Per cosa |
|---|---|
/usr/bin/ | Eseguibili per utenti normali |
/usr/sbin/ | Eseguibili per admin (richiedono root) |
/usr/lib/<pkg>/ | Librerie private del tuo pacchetto, supporto runtime |
/usr/lib/<arch-triplet>/ | Librerie condivise multi-arch (es. x86_64-linux-gnu) |
/usr/share/<pkg>/ | Dati arch-independent (template, asset, traduzioni) |
/usr/share/doc/<pkg>/ | Obbligatorio: copyright + changelog.Debian.gz |
/usr/share/man/man[1-9]/ | Man page (gzippate, lintian si arrabbia se non lo sono) |
/etc/<pkg>/ | Configurazioni utente. Da listare in conffiles |
/var/lib/<pkg>/ | Stato persistente che il pacchetto modifica |
/var/log/<pkg>/ | Log (rotation via /etc/logrotate.d/) |
/var/cache/<pkg>/ | Cache rigenerabili (purgable senza perdita dati) |
/srv/ | Dati di servizio "site-specific" (web roots, mail, ...) |
/lib/systemd/system/ | Unit files systemd |
/etc/systemd/system/ | NO. Quello è per overrides admin. |
/opt/<pkg>/ | Solo per software third-party non-distribuito (Chrome, Slack, ...) |
/usr/bin o /usr/sbin/usr/share/<pkg>//etc/<pkg>/ + conffiles/var/lib/<pkg>//lib/systemd/system//usr/share/doc/<pkg>//opt/<pkg>/ "perché così è isolato"/etc/systemd/system/ (è per gli admin)/root/ o /home/.git/, __pycache__/, node_modules/ dentro il .debchangelog non gzippato (lintian errore: changelog-not-compressed)lintian mypkg.deb — ti elenca tutto ciò che hai sbagliato vs. Debian Policy. Da E: (errori) a W: (warning) a I: (info). Punta a zero errori; i warning li valuti caso per caso.
I quattro script che dpkg chiama nei momenti giusti. Devono essere idempotenti e gestire tutti i casi.
Ogni operazione su un pacchetto chiama gli script in ordine prevedibile. Per un'installazione fresca:
preinst installpostinst configurePer un upgrade:
prerm upgrade <new-version> (sul vecchio)preinst upgrade <old-version> (sul nuovo)postrm upgrade <new-version> (sul vecchio)postinst configure <old-version> (sul nuovo)Per remove / purge:
prerm removepostrm remove (per remove)postrm purge (per purge: rimuove anche conffile e dati)#!/bin/sh
set -e
case "$1" in
configure)
# 1. Crea utente di sistema (se non esiste)
if ! getent passwd myapp >/dev/null; then
adduser --system --group \
--home /var/lib/myapp \
--no-create-home \
--shell /usr/sbin/nologin \
myapp
fi
# 2. Crea directory di stato con i permessi giusti
install -d -m 0750 -o myapp -g myapp /var/lib/myapp
install -d -m 0750 -o myapp -g myapp /var/log/myapp
# 3. Genera secret se non esiste (idempotente!)
if [ ! -f /etc/myapp/secret ]; then
head -c 32 /dev/urandom | base64 > /etc/myapp/secret
chmod 0640 /etc/myapp/secret
chown root:myapp /etc/myapp/secret
fi
# 4. systemd: enable + (re)start
deb-systemd-helper enable myapp.service >/dev/null || true
if [ -d /run/systemd/system ]; then
systemctl daemon-reload >/dev/null || true
deb-systemd-invoke restart myapp.service >/dev/null || true
fi
;;
abort-upgrade|abort-remove|abort-deconfigure)
# Niente da fare; un altro script gestirà il rollback
;;
*)
echo "postinst chiamato con argomento ignoto: $1" >&2
exit 1
;;
esac
exit 0
#!/bin/sh
set -e
case "$1" in
purge)
# Solo a purge: rimuovi conffile, dati, utente
rm -rf /var/lib/myapp /var/log/myapp /etc/myapp
if getent passwd myapp >/dev/null; then
deluser --quiet myapp 2>/dev/null || true
fi
deb-systemd-helper purge myapp.service >/dev/null || true
;;
remove)
# Stop senza purge: lascia conffile e dati
deb-systemd-helper mask myapp.service >/dev/null || true
;;
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
;;
*)
echo "postrm chiamato con argomento ignoto: $1" >&2
exit 1
;;
esac
exit 0
adduser deve essere preceduto da if ! getent passwd; install -d è sicuro per directory che esistono già; systemctl deve essere wrappato con || true nel caso non sia in esecuzione (chroot, container).
chmod 0755) e iniziare con shebang (#!/bin/sh o #!/bin/bash). set -e sempre — un errore non gestito deve far fallire l'install/upgrade, non procedere silenziosamente.
/etc/I file listati in DEBIAN/conffiles ricevono trattamento speciale:
dpkg calcola l'md5 al primo installpurge, vengono rimossi (a remove normale, restano)Il file conffiles elenca un path assoluto per riga:
/etc/myapp/config.yaml
/etc/myapp/logging.conf
/etc/logrotate.d/myapp
/etc/cron.daily/myapp-cleanup
Costruire un .deb senza debhelper, partendo da una directory. Per capire cosa succede sotto.
Il modo "Debian-canonico" (debhelper, sezione 07) è più corretto e portatile, ma quando vuoi capire cosa fa conviene partire da dpkg-deb --build: ti mostra la corrispondenza diretta tra una directory tree e un .deb, senza magia.
Va benissimo anche per: pacchetti privati interni, build script bash custom, tooling cross-platform, distribuzione di binari precompilati Go/Rust senza source.
DEBIAN/controlPackage: myapp
Version: 1.0.0-1
Section: web
Priority: optional
Architecture: amd64
Depends: python3 (>= 3.10), python3-flask
Maintainer: Selif Dev <[email protected]>
Description: Web app per fare cose
Una piccola web app Flask che serve come esempio.
Non fa niente di utile, ma fa qualcosa.
$ dpkg-deb --build --root-owner-group myapp_1.0.0
dpkg-deb: building package 'myapp' in 'myapp_1.0.0.deb'.
# Convenzione di naming:
$ mv myapp_1.0.0.deb myapp_1.0.0-1_amd64.deb
--root-owner-group è cruciale. Senza, ogni file dentro il .deb ha owner = il tuo utente (es. selif:selif) e a install diventerà quello (errore di sicurezza). Con il flag, tutto è root:root — che è quello che vuoi sempre per i binari.
# Lista file (con permessi e owner)
$ dpkg-deb -c myapp_1.0.0-1_amd64.deb
# Mostra il control
$ dpkg-deb -I myapp_1.0.0-1_amd64.deb
# Estrai data in /tmp/x
$ dpkg-deb -x myapp_1.0.0-1_amd64.deb /tmp/x
# Estrai control in /tmp/c
$ dpkg-deb -e myapp_1.0.0-1_amd64.deb /tmp/c
# Lint check (richiede "apt install lintian")
$ lintian myapp_1.0.0-1_amd64.deb
# Install + test
$ sudo apt install ./myapp_1.0.0-1_amd64.deb
# NB: "./" obbligatorio per dirgli "file locale, non repo"
xz. Per pacchetti grandi puoi forzare zstd (decompressione molto più veloce) con dpkg-deb -Zzstd --build .... Per max compatibility: -Zgzip (formato anni '90, supportato ovunque, decompressione lenta).
dh — il modo DebianQuando vuoi un pacchetto "vero", quello che potrebbe finire nell'archivio Debian.
Con dpkg-deb tu costruisci una directory tree finale a mano. Con debhelper dichiari cosa vuoi nel pacchetto e dh ricostruisce tutto a partire dal source: compila, installa, comprime, gzippa changelog, byte-compile Python, registra man page, configura systemd, e così via.
È il modo standard di Debian. Più ripido all'inizio, infinitamente meno faticoso quando il pacchetto cresce.
debian/debian/control (source-style)Source: myapp
Section: web
Priority: optional
Maintainer: Selif Dev <[email protected]>
Build-Depends: debhelper-compat (= 13), dh-python, python3-all
Standards-Version: 4.6.2
Homepage: https://myapp.example.com
Rules-Requires-Root: no
Package: myapp
Architecture: all
Depends: ${misc:Depends}, ${python3:Depends}, python3-flask
Description: Web app per fare cose
Una piccola web app Flask...
Nota: nei sostituti automatici (${misc:Depends}, ${python3:Depends}) dh inietta in build-time le dipendenze rilevate — non scrivere a mano libc6 o version constraint.
debian/rules minimale#!/usr/bin/make -f
# Tutto il vero lavoro lo fa dh, queste 3 righe sono il pacchetto completo.
%:
dh $@
Sì, sono 3 righe. dh $@ espande nelle 30+ chiamate giuste a dh_install, dh_compress, dh_strip, dh_systemd_enable, dh_python3, dh_md5sums, dh_builddeb, ... a seconda dei build-deps che hai dichiarato.
Quando ti serve override, lo fai per singolo step:
#!/usr/bin/make -f
%:
dh $@ --with python3 --buildsystem=pybuild
override_dh_auto_test:
# Salta i test in build (tipico se servono rete/db)
override_dh_install:
dh_install
# Crea directory di stato vuota
install -d -m 0750 debian/myapp/var/lib/myapp
debian/installLista sorgente destinazione, una mappatura per riga. Quello che vuoi nel .deb finale, partendo dalla source tree.
myapp.py usr/lib/myapp/
bin/myapp usr/bin/
config.yaml etc/myapp/
templates/ usr/share/myapp/
debian/ sono auto-rilevati: debian/<pkg>.service → va in /lib/systemd/system/; debian/<pkg>.1 → man page in sezione 1; debian/<pkg>.cron.daily → /etc/cron.daily/<pkg>; debian/<pkg>.logrotate → /etc/logrotate.d/<pkg>. Niente boilerplate.
Il formato è rigido. Sgarrare significa dpkg-buildpackage che si rifiuta di costruire.
myapp (1.2.0-1) unstable; urgency=medium
* Nuova feature: export PDF dei report.
* Bugfix: timeout del client SMTP elevato a 30s.
* Refactoring del modulo auth.
-- Selif Dev <[email protected]> Wed, 30 Apr 2026 14:23:01 +0200
myapp (1.1.5-1) unstable; urgency=high
* Security fix: CVE-2026-12345 (XSS nella pagina /search).
-- Selif Dev <[email protected]> Mon, 22 Apr 2026 09:00:00 +0200
nome (versione) suite; urgency=...; corpo con bullet che iniziano con * (due spazi + asterisco + spazio); riga firma -- Nome <email> Data RFC2822 (un solo spazio iniziale, due spazi tra email e data). Sgarrare significa errore. Usa dch o git-dch per generare entry — il formato lo fa lui.
# Prima entry (crea il file)
$ dch --create --package myapp --newversion 1.0.0-1 \
"Initial release."
# Nuova versione (incrementa Debian revision)
$ dch -i "Bugfix nel modulo X."
# Nuova versione upstream
$ dch -v 1.2.0-1 "New upstream release"
# Marca pronta per release (cambia "UNRELEASED" in "unstable")
$ dch -r ""
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: myapp
Upstream-Contact: Selif Dev <[email protected]>
Source: https://git.example.com/myapp
Files: *
Copyright: 2026 Selif Dev <[email protected]>
License: MIT
Files: debian/*
Copyright: 2026 Selif Dev <[email protected]>
License: MIT
License: MIT
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
...
Una sola riga, scegli tra:
3.0 (native) — il pacchetto nasce per Debian, non c'è un upstream separato. Tipico per i tuoi tool interni.3.0 (quilt) — pacchetti che hanno un upstream esterno (es. nginx, redis) e si applicano patch su quello. Standard per archivio Debian.Vecchio modo: file debian/compat con un numero (es. 13). Modo moderno: dichiara debhelper-compat (= 13) in Build-Depends dentro debian/control. Mai entrambi.
Compat 13 è il default ragionevole su Debian 12+ (bookworm) e Debian 13 (trixie).
Da source con debian/ a .deb finito. Locale, in chroot, in CI.
# Prerequisiti: una volta sola
$ sudo apt install build-essential debhelper devscripts dh-make lintian
# Dentro la directory source (con debian/ presente)
$ cd myapp-1.0.0/
# Installa build-deps dichiarate in debian/control
$ sudo apt build-dep .
# equivalente a: leggi Build-Depends, apt install ognuno
# Build (binary only, senza firma)
$ dpkg-buildpackage -us -uc -b
# -us: don't sign source
# -uc: don't sign .changes
# -b: binary only (no source tarball)
# Output: tutto nella parent directory
$ ls ../
myapp-1.0.0/
myapp_1.0.0-1_amd64.deb # <-- ecco il tuo .deb
myapp_1.0.0-1_amd64.buildinfo
myapp_1.0.0-1_amd64.changes
lintian dopo build. $ lintian ../myapp_1.0.0-1_amd64.deb ti elenca tutto ciò che viola la Debian Policy. Per release interne tollera i W:, mai gli E:.
pbuilder)Il problema della build "locale" è che dipende dallo stato della tua macchina — pacchetti installati, env vars, versioni. pbuilder (o sbuild) crea un chroot Debian minimale, installa solo i build-deps dichiarati, fa la build dentro, restituisce il .deb. Così sai che il pacchetto si costruisce davvero da zero su una macchina pulita.
$ sudo apt install pbuilder
# Setup: crea base chroot (una volta per distro target)
$ sudo pbuilder create --distribution trixie \
--basetgz /var/cache/pbuilder/trixie-base.tgz
# Build di un pacchetto (a partire dal .dsc generato da dpkg-buildpackage -S)
$ dpkg-buildpackage -S -us -uc # source package → .dsc
$ sudo pbuilder build ../myapp_1.0.0-1.dsc \
--basetgz /var/cache/pbuilder/trixie-base.tgz
# Output in /var/cache/pbuilder/result/
$ ls /var/cache/pbuilder/result/
myapp_1.0.0-1_amd64.deb
myapp_1.0.0-1_amd64.changes
name: build-deb
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
distro: [bookworm, trixie]
container: debian:${{ matrix.distro }}
steps:
- uses: actions/checkout@v4
- name: Install build prereqs
run: |
apt update
apt install -y build-essential debhelper devscripts lintian
apt build-dep -y .
- name: Build .deb
run: dpkg-buildpackage -us -uc -b
- name: Lintian check
run: lintian ../*.deb || true
- uses: actions/upload-artifact@v3
with:
name: deb-${{ matrix.distro }}
path: ../*.deb
piuparts)piuparts = "package installation, upgrading and removal testing suite". Esegue install → remove → install → upgrade → purge in un chroot fresco e cattura ogni anomalia (file lasciati in giro a purge, errori in script, file non posseduti).
$ sudo apt install piuparts
$ sudo piuparts -d trixie myapp_1.0.0-1_amd64.deb
# Esegue install/remove/install/purge ciclo completo
# PASS = nessuna sorpresa
Per distribuire i tuoi .deb con apt install <pkg>, non con curl | bash.
Un sito statico con una struttura precisa di file e una firma GPG. Niente daemon, niente database. Può vivere su Cloudflare Pages, GitHub Pages, S3, MinIO, Nginx static, Apache — qualsiasi cosa serva file via HTTP/HTTPS.
reprepro$ sudo apt install reprepro
$ mkdir -p ~/myrepo/conf
$ cat > ~/myrepo/conf/distributions <<EOF
Origin: Selif
Label: Selif Repo
Codename: trixie
Suite: stable
Architectures: amd64 arm64 source
Components: main
Description: Selif personal package repository
SignWith: ABCDEF1234567890 # GPG key ID
EOF
# Aggiungi un .deb al repo
$ cd ~/myrepo
$ reprepro includedeb trixie /path/to/myapp_1.0.0-1_amd64.deb
# Lista pacchetti
$ reprepro list trixie
# Rimuovi
$ reprepro remove trixie myapp
A questo punto ~/myrepo/dists/ e ~/myrepo/pool/ sono pronti. Pubblicali con qualsiasi static hosting (rsync su VPS, push a Cloudflare Pages, sync a S3).
$ gpg --batch --quick-gen-key "Selif Repo <[email protected]>" rsa4096
$ gpg --list-keys
# Cerca la riga "pub": ABCDEF1234567890... <-- key ID
# Esporta la chiave pubblica (questa la pubblichi)
$ gpg --armor --export ABCDEF1234567890 > selif-repo.gpg
# È un file di testo, lo metti su https://repo.example.com/gpg
# 1. Scarica la chiave pubblica
$ sudo install -d -m 0755 /etc/apt/keyrings
$ curl -fsSL https://repo.example.com/gpg \
| sudo tee /etc/apt/keyrings/selif.asc > /dev/null
# 2. Aggiungi la sources list (sintassi nuova: signed-by inline)
$ echo "deb [signed-by=/etc/apt/keyrings/selif.asc] https://repo.example.com/debian trixie main" \
| sudo tee /etc/apt/sources.list.d/selif.list
# 3. Aggiorna e installa
$ sudo apt update
$ sudo apt install myapp
apt-key add. Il vecchio modo (apt-key add - < gpg.key) è deprecato dal 2022 e rimosso da Debian 12. La chiave deve essere in /etc/apt/keyrings/ o /etc/apt/trusted.gpg.d/, e referenziata con signed-by= nella sources list. Le guide vecchie su Stack Overflow ti faranno installare repo non firmati.
Repo APT/RPM/PyPI/Docker. Free tier piccolo, paid scalabile.
Storico, multi-format. Piano gratis per progetti open source.
Hostare i .deb come release asset; non è un repo APT vero, l'utente fa wget && dpkg -i.
Il formato versione di Debian. Più ricco di SemVer, con regole sottili che è meglio conoscere.
Una versione Debian ha forma:
[epoch:]upstream_version[-debian_revision]
2026.04 → 1.0.0 richiede 1:1.0.0 per essere considerata più recente)1.2.3)1.2.3-1, 1.2.3-2, 1.2.3-3)| Versione | Cosa significa |
|---|---|
1.0.0-1 | Prima release Debian del software 1.0.0 |
1.0.0-2 | Stesso codice, packaging cambiato (es. fix in postinst) |
1.0.1-1 | Nuova upstream, primo packaging |
2:1.0.0-1 | Epoch 2 (versione "logicamente posteriore" anche se il numero è minore) |
1.0.0~rc1-1 | Pre-release: ~ significa "minore di" |
1.0.0+dfsg1-1 | Upstream ripulito da file non-DFSG (es. binari proprietari rimossi) |
1.0.0~git20260430.abcdef-1 | Snapshot git tra release ufficiali |
1.0.0-1ubuntu1 | Convenzione Ubuntu: derivato da Debian 1.0.0-1, prima patch Ubuntu |
~ è magica. Comparando versioni, ~ è sempre minore di qualunque altro carattere, persino della stringa vuota. Quindi 1.0.0~rc1 < 1.0.0~rc2 < 1.0.0. Senza tilde, 1.0.0rc1 > 1.0.0 (il rc1 è > vuoto), che inverte la semantica: gli RC sembrerebbero "successivi" alla release. La tilde risolve esattamente questo.
$ dpkg --compare-versions 1.0.0~rc1 lt 1.0.0 && echo yes
yes
$ dpkg --compare-versions 1.0.0-1 lt 1.0.0-2 && echo yes
yes
$ dpkg --compare-versions 2:0.5 gt 1:99.9 && echo yes
yes
# Epoch 2 batte epoch 1 anche se i numeri parlano contrario
$ dpkg --compare-versions 1.0.0+dfsg1-1 gt 1.0.0-1 && echo yes
yes
Quando aggiorni un pacchetto e un file in conffiles è cambiato sia upstream che dall'utente, dpkg presenta un prompt:
Configuration file '/etc/myapp/config.yaml'
==> Modified (by you or by a script) since installation.
==> Package distributor has shipped an updated version.
What would you like to do about it ? Your options are:
Y or I : install the package maintainer's version
N or O : keep your currently-installed version
D : show the differences between the versions
Z : start a shell to examine the situation
The default action is to keep your current version.
*** config.yaml (Y/I/N/O/D/Z) [default=N] ?
Per non avere prompt in script automatici (Ansible, cloud-init):
$ sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \
-o Dpkg::Options::="--force-confold"
# ^ tieni la versione locale
# Alternative:
# --force-confnew → sovrascrivi con la nuova maintainer
# --force-confdef → usa default (rispetta scelte già fatte)
I comandi che ti serviranno davvero, in una pagina da tenere aperta.
$ dpkg-deb -I pkg.deb # control + info
$ dpkg-deb -c pkg.deb # lista file con permessi
$ dpkg-deb -e pkg.deb /tmp/ctrl/ # estrai control + scripts
$ dpkg-deb -x pkg.deb /tmp/data/ # estrai i file (data)
$ ar t pkg.deb # i 3 membri ar
$ ar x pkg.deb # estrai i 3 membri
$ dpkg -l # tutti i pacchetti
$ dpkg -l nginx # info su uno specifico
$ dpkg -L nginx # tutti i file installati da nginx
$ dpkg -s nginx # status + control
$ dpkg -S /etc/nginx/nginx.conf # quale pkg possiede questo file?
$ dpkg --verify nginx # confronta md5sums vs filesystem
$ apt-cache depends nginx # di cosa nginx ha bisogno
$ apt-cache rdepends nginx # chi dipende da nginx
$ apt-cache policy nginx # versione installata + candidate + sorgenti
$ apt-mark hold nginx # blocca upgrade automatici
$ apt-mark unhold nginx # sblocca
$ apt-mark showhold # lista pacchetti held
$ mkdir -p mypkg/DEBIAN mypkg/usr/bin
$ cat > mypkg/DEBIAN/control <<EOF
Package: mypkg
Version: 1.0.0-1
Section: utils
Priority: optional
Architecture: all
Maintainer: Me <[email protected]>
Description: Test package
EOF
$ cp myapp mypkg/usr/bin/
$ chmod 0755 mypkg/usr/bin/myapp
$ dpkg-deb --build --root-owner-group mypkg
$ mv mypkg.deb mypkg_1.0.0-1_all.deb
$ sudo apt install ./mypkg_1.0.0-1_all.deb
$ sudo apt install build-essential debhelper devscripts dh-make lintian
$ cd mypackage-1.0.0/
# Genera scheletro debian/ (interattivo)
$ dh_make --native --single --packagename mypackage_1.0.0
# Edita debian/{control,changelog,rules,copyright}
$ sudo apt build-dep .
$ dpkg-buildpackage -us -uc -b
$ lintian ../mypackage_1.0.0-1_amd64.deb
$ lintian pkg.deb # style/policy check
$ sudo piuparts -d trixie pkg.deb # install/remove/upgrade in chroot
# Install di test in container Docker
$ docker run --rm -v $PWD:/p debian:trixie \
sh -c "apt update && apt install -y /p/pkg.deb && /p/test.sh"
$ sudo apt install reprepro
$ mkdir -p ~/myrepo/conf
$ cat > ~/myrepo/conf/distributions <<EOF
Origin: Me
Label: My Repo
Codename: trixie
Architectures: amd64 arm64 source
Components: main
SignWith: ABCDEF1234567890
EOF
$ cd ~/myrepo
$ reprepro includedeb trixie /path/to/pkg.deb
$ reprepro list trixie
# Ora ~/myrepo/dists e ~/myrepo/pool sono pronti
# Pubblica con: rsync -a ~/myrepo/ user@host:/var/www/repo/
$ sudo DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \
-o Dpkg::Options::="--force-confold" \
-o Dpkg::Options::="--force-confdef"
apt-key add).
.deb a fleet), arx · nomina · missus (esempi reali di pacchetti .deb per panel web).