From 0bb0b0294edc3d73fb94600a064ce4ab323c7229 Mon Sep 17 00:00:00 2001 From: Ryan McGuire Date: Thu, 14 Apr 2016 22:23:30 -0400 Subject: [PATCH] Initial --- .gitignore | 3 + README.md | 132 +++++++++++++++++++++++++++++++++ arch-ppa | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 arch-ppa diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa4f96d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +chroot +*~ +pkg diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf5c466 --- /dev/null +++ b/README.md @@ -0,0 +1,132 @@ +arch-ppa +======== + +arch-ppa is a tool that easily creates and maintains your own Arch +package repositories. Kind of like the Personal Package Archives (PPA) +that Ubuntu has, but way easier. + +The Arch User Repository (AUR) is convenient, has tons of software, is +generally awesome, but is inherently insecure. Anyone can upload +anything they want to the AUR. This is why I don't like to use AUR +helpers like `yaourt` or `pacaur`. Using the AUR with a helper +requires you to be diligent in reviewing the PKGBUILDs it downloads, +in order to make sure it doesn't include things like viruses or +trojans, or downloading from a weird URL. + +I wanted a way to maintain my own repository of PKGBUILDs, downloaded +from the AUR, that I have manually verified. Building and installing +packages built from those pre-verified PKGBUILDs resolves the +insecurity of the AUR in my mind. This gives me the full power of the +AUR, but allows me to automate my package installs in a way that I +never felt comfortable with before. Seriously, why does pacaur have a +`--noconfirm` option? That's scary. + +The packages this tool builds can be hosted as a regular arch +repository, which you put into your `/etc/pacman.conf`. The added +convenience here is that although the packages came from the AUR, your +clients install it through regular-old pacman. + + +Usage +----- + +Clone this repo somewhere. Everything will be self contained in this +directory wherever you put it. + +arch-ppa should not be run as root, but the user that does run it does +need sudo privileges as the underlying devtools need it. + +Run setup: + + ./arch-ppa setup + +The setup installs a few dependencies like `devtools` and `git`. It +also creates a chroot directory which is a container that will be used +to build packages in a completely clean environment using +`systemd-nspawn`. + +Add packages from the AUR: + + ./arch-ppa add cower curlbomb pasystray + +This downloads PKGBUILDs from the AUR for the listed packages: cower, +curlbomb, pasystray, as well as all of their AUR dependencies, and +placed into the `src` directory. You can put PKGBUILDs from other +sources in the src directory too; they don't have to be from the +AUR. Note that any PKGBUILD that lists a dependency of another +package, that is not found in one of the arch repositories, needs to +have it's own PKGBUILD in the `src` directory too. (The `add` command +does this for you automatically, thanks to `cower -d -d`) + +Build everything: + + ./arch-ppa clean ryan + ./arch-ppa build ryan + +The build process operates on a single repository, in this example +called `ryan`. You can maintain several repositories, each containing +different sets of packages. Just make sure to give each repository a +unique name. + +The clean process removes the repository directory containing all the +built packages. It also deletes the chroot for the repository from the +`chroot` directory. + +The build process creates a new package repository called `ryan` (or +whatever you called yours.) It finds PKGBUILD files in the `src` +directory and figures out the dependency chain and builds all the +packages in the correct order. Additionally, you can specify +individual package names after the repository name if you only wish to +build certain packages. If you do specify package names, make sure to +include all dependencies, as they will not be included otherwise. + +The repository directory can be listed in your /etc/pacman.conf like this: + + [ryan] + Server = file:///home/ryan/git/arch-ppa/ryan + SigLevel = Required TrustedOnly + +This is the full path to the ryan repository just created. Run `pacman +-Sy` and you should see pacman synchronize with the new repository +name. Alternatively, upload the directory to a webserver and share it +with all your friends. + +The SigLevel option specifies how pacman should trust our +repository. `Required TruestedOnly` is a strict rule that the key must +be in the local pacman keyring and be assigned a trust level. Pacman +will usually download the key without a problem, but you will still +need to locally sign the key to trust it. + +Mini gpg tutorial +----------------- +View your key information: + + gpg -K + +This should output something like this: + + /home/ryan/.gnupg/pubring.kbx + ----------------------------- + sec rsa2048/4BAACCF8 2016-04-15 [SC] + uid [ultimate] test guy + ssb rsa2048/C22BDAA5 2016-04-15 [E] + +My public key ID is 4BAACCF8. Always omit the part before the +slash. If it didn't output any key information at all, this means you +don't have a key yet. Create one and follow the prompts: + + gpg --gen-key + +Send your public key to the keyserver (replace with your ID): + + gpg --send-keys 4BAACCF8 + +On each machine you will use your package repository, run the +following to import the key and to locally sign (assign trust) the +key (again, replace with your key ID): + + sudo pacman-key -r 4BAACCF8 + sudo pacman-key --lsign-key 4BAACCF8 + +If you don't sign the key, pacman will complain that your packages are +not trusted. diff --git a/arch-ppa b/arch-ppa new file mode 100755 index 0000000..20d1b36 --- /dev/null +++ b/arch-ppa @@ -0,0 +1,211 @@ +BASEDIR=$(cd $(dirname $0); pwd) +CHROOT=$BASEDIR/chroot + +if [ `whoami` == 'root' ]; then + echo "Must not be run as root." + exit 1; +fi + +exe() { echo "\$ $@" ; "$@" ; } + +create_chroot() { + ( + set -e + if [ ! -d $CHROOT ]; then + echo "## Creating arch chroot ..." + exe mkdir $CHROOT + exe mkarchroot $CHROOT/root base-devel + # install cower: + TMP_BUILD=`mktemp -d` + exe cd $TMP_BUILD + curl https://aur.archlinux.org/cgit/aur.git/snapshot/cower.tar.gz | tar xz + exe cd cower + exe makechrootpkg -r $CHROOT -l root -- -i + exe rm -rf $TMP_BUILD + echo "## Root chroot build complete." + fi + ) +} + +install_system_deps() { + ( + set -e + pkg_dep() { + if !(pacman -Q $1 > /dev/null 2>&1); then + echo "Installing $1..." + exe sudo pacman -S --noconfirm $1 + fi + } + pkg_dep devtools + pkg_dep git + ) +} + +package_build() { + # Build a package in a container. $1=container $2=package + # PKGBUILD must already exist in src/ + # If package has dependencies we also maintain in src/, recursively build those first. + ( + set -e + if [ "$#" -ne 2 ]; then + echo "package_build takes two args: repo_name and package_name" + return 1 + fi + repo_name=$1 + package_name=$2 + setup + pkgdir=$BASEDIR/src/$package_name + if [ ! -d $pkgdir ]; then + echo "Package $package_name not found in $pkgdir" + echo "Try running: package_add $package_name" + return 1 + fi + if [ ! -f $pkgdir/PKGBUILD ]; then + echo "Cannot find PKGBUILD in $pkgdir" + return 1 + fi + if (arch-nspawn $CHROOT/$repo_name pacman -Q $package_name > /dev/null 2>&1); then + echo "Package $package_name already built" + return + fi + # Find and build dependencies: + find_deps $package_name | while read dep; do + # Build the dep if we haven't already yet: + if !(arch-nspawn $CHROOT/$repo_name pacman -Q $dep > /dev/null 2>&1); then + package_build $repo_name $dep + fi + done + # Build package: + cd $pkgdir + rm -f *.pkg.tar.xz + mkdir -p $BASEDIR/$repo_name + exe makechrootpkg -r $CHROOT -l $repo_name -- -i + exe mv *.pkg.tar.xz $BASEDIR/$repo_name + ) +} + +sign_packages() { + if [ "$#" -lt 1 ]; then + echo "sign_packages requires specifying the list of packages to sign" + return 1 + fi + ( + set -e + cd $(dirname $1) + for pkg in "$@"; do + gpg --detach-sign --use-agent $pkg + done + ) +} + +find_deps() { + # Inspect package srcinfo and pull out the dependencies that we maintain in src + ( + set -e + setup + cat $BASEDIR/src/$1/.SRCINFO | sed -nr 's/^\W*depends = ([-a-zA-Z0-9]+).*$/\1/p' | while read dep; do + if [ -d $BASEDIR/src/$dep ]; then + echo $dep + fi + done + ) +} + +add() { + # Add packages and their AUR dependencies to src + ( + set -e + if [ "$#" -lt 1 ]; then + echo "Must specify package(s) to add" + return 1 + fi + setup + if [ ! -d $BASEDIR/src ]; then + mkdir $BASEDIR/src + fi + for pkg in "$@"; do + arch-nspawn $CHROOT/root --bind=$BASEDIR/src:/src cower -q -t /src -d -d $pkg + done + sudo chown $USER -R $BASEDIR/src + echo "## All requested packages added" + ) +} + +list() { + if [ "$#" -ne 1 ]; then + echo "Must specify repository name to list" + return 1 + fi + setup + arch-nspawn $CHROOT/$1 pacman -Qm +} + +setup() { + mkdir -p $BASEDIR/src + install_system_deps + create_chroot +} + +clean() { + ( + set -e + if [ "$#" -lt 1 ]; then + echo "Must specify repository names to clean" + return 1 + fi + for repo in "$@"; do + # Clean repository and chroot + if [ -d $BASEDIR/chroot/$repo ]; then + exe sudo rm -rf $BASEDIR/chroot/$repo $BASEDIR/chroot/$repo.lock + fi + if [ -d $BASEDIR/$repo ]; then + exe rm -rf $BASEDIR/$repo + fi + done + ) +} + +build() { + ( + set -e + if [ "$#" -lt 1 ]; then + echo "Must specify repository name to build (and optional package list to include)" + return 1 + fi + setup + repo_name=$1 + mkdir -p $BASEDIR/$repo_name + shift + if [ "$#" -gt 0 ]; then + # Build only requested packages + for pkg in "$@"; do + package_build $repo_name $pkg + done + echo "## All requested packages built" + else + # Build all packages: + cd $BASEDIR/src + find -type d | sed 's/\.\///' | tail -n +2 | while read pkg; do + package_build $repo_name $pkg + done + echo "## All packages built" + fi + echo "## Updating repository database" + cd $BASEDIR/$repo_name + if [ `ls *.pkg.tar.xz 2> /dev/null | wc -l` -lt 1 ]; then + echo "No packages found in $BASEDIR/$repo_name" + return 1; + fi + sign_packages *.pkg.tar.xz + repo-add $repo_name.db.tar.gz *.pkg.tar.xz + sign_packages $repo_name.db + ) +} + + +if [ "$#" -gt 0 ]; then + $* +else + echo "Must specify a command, eg: add, build, list, clean" + exit 1 +fi