UPDATE: 2023-May-16, I can confirm apt-manage is the safest most consistent way I found to manage apt sources with keys properly.
I've rewritten the answer, removing my personal script (you can view it in the revisions history if you'd like) to focus on the command and not my personal code.
All of the answers here are great, and I've learned a lot from them.
Per @mesterlion comments, as of this time there is a single tool that does all of this, and does it correctly, properly maintained by the Pop!_OS team.
That tool is apt-manage which is part of RepoLib module which introduced the key sub-command in version 2.
So in Pop!_OS 22.04, installation will look like this:
sudo apt install --yes apt-manage
The upstream projects (Ubuntu and Debian) are missing this package, and for me the following installs it successfully on Ubuntu:
cd ~ # recommended
VER=2.0.0
PKG="python3-repolib_${VER}_all.deb"
URL="https://github.com/pop-os/repolib/releases/download/${VER}/${PKG}"
mkdir tmp && pushd tmp >/dev/null \
&& sudo apt update \
&& sudo apt install --yes python3-gnupg python3-debian \
&& curl -LO "${URL}" \
&& sudo apt install "./${PKG}" \
&& rm debian-binary control.tar.xz data.tar.xz control.tar.zst data.tar.zst \
popd >/dev/null \
&& apt-manage --help
For older Debian distros the zst compression is not part of dpkg, and so repackaging the deb is required:
### Replace the 'apt install' command above with:
sudo apt install --yes \
debhelper dh-python python3-all python3-setuptools \
python3-gnupg python3-debian zstd
### Place the following right after: && curl -LO "${URL}" \
&& mv "./${PKG}" "./${PKG}.tmp" \
&& ar x "./${PKG}.tmp" \
&& zstd -d < control.tar.zst | xz > control.tar.xz \
&& zstd -d < data.tar.zst | xz > data.tar.xz \
&& ar -m -c -a sdsd "./${PKG}" debian-binary control.tar.xz data.tar.xz \
### Continue with: && sudo apt install "./${PKG}" \
For the project's build instrucitons - see Installation section of the git-repo's README.md.
Example 1
An example of a set of apt-manage calls from my system:
# Adding PPA
sudo apt-manage add --terse --format=sources ppa:ppa:alessandro-strada/ppa
# Adding APT source and assinging a key
sudo apt-manage add --terse --format=sources --name Microsoft --identifier packages-microsoft-com 'deb [arch=amd64] https://packages.microsoft.com/ubuntu/22.04/prod jammy main'
# Assigning a key via URL
sudo apt-manage key packages-microsoft-com --url=https://packages.microsoft.com/keys/microsoft.asc
# R-Project needed some additional love - because they do something 'different':
sudo apt-manage add --terse --format=sources --name R-Project --identifier cloud-r-project-org 'deb [arch=amd64] https://cloud.r-project.org/bin/linux/ubuntu jammy-cran40/ ""_""'
# Specific to R-Project, as one of the values are empty and the command line can' pass it effectively.
sudo sed -i /etc/apt/sources.list.d/cloud-r-project-org.sources -Ee 's/""_""//g'
# Assigning a key via fingerprint
sudo apt-manage key cloud-r-project-org --fingerprint=E298A3A825C0D65DFD57CBB651716619E084DAB9
# After all were added, run update.
sudo apt update
Example 2: Initialize docker on a fresh Ubuntu compute instance
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get update \
&& apt-get install --yes --no-install-recommends \
apt-transport-https ca-certificates \
wget gnupg software-properties-common lsb-release \
curl jq bash-completion
has() { command -v "$1" > /dev/null; }
# Try differnt ways of installing apt-manage
# First let's hope there is package ready for installation
has apt-manage || apt-get install --yes apt-manage || true
# If it still doesn't exist, let's get it from repo
if ! has apt-manage; then
FILENAME=python3-repolib_2.0.0_all.deb
URL="https://github.com/pop-os/repolib/releases/download/2.0.0/$FILENAME"
if [ ! -e "/tmp/$FILENAME" ]; then
apt-get install --yes python3-gnupg python3-debian
curl -Lo "/tmp/$FILENAME" "$URL"
fi
apt-get install "/tmp/$FILENAME"
fi
# If the repo isn't part of existing apt sources, add it
REPO_URL="https://download.docker.com/linux/ubuntu"
APT_NAME="$(
apt-manage list -a \
| grep -B4 "${REPO_URL}" | head -1 \
| sed -Ee 's/^\W*([^:]+):$/\1/'
)"
if [[ -n "${APT_NAME}" ]]; then
>&2 printf '%s\n' \
'' \
"$(apt-manage list "${APT_NAME}")" \
'' \
"Docker repository found [${APT_NAME}]. Skipping."
else
apt-manage add --identifier download-docker-com-pqb --name Docker \
--terse --format=sources \
"$( printf "deb [arch=%s] %s %s %s" \
"$(dpkg --print-architecture)" \
"${REPO_URL}" \
"$(lsb_release --codename --short)" \
"stable"
)"
apt-manage key download-docker-com-pqb \
--url=https://download.docker.com/linux/ubuntu/gpg
fi