Browse Source


Ryan McGuire 7 years ago
  1. 3
  2. 132
  3. 211

.gitignore vendored

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@


@ -0,0 +1,132 @@ @@ -0,0 +1,132 @@
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.
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
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:
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:
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.


@ -0,0 +1,211 @@ @@ -0,0 +1,211 @@
BASEDIR=$(cd $(dirname $0); pwd)
if [ `whoami` == 'root' ]; then
echo "Must not be run as root."
exit 1;
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 | tar xz
exe cd cower
exe makechrootpkg -r $CHROOT -l root -- -i
exe rm -rf $TMP_BUILD
echo "## Root chroot build complete."
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
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
if [ ! -d $pkgdir ]; then
echo "Package $package_name not found in $pkgdir"
echo "Try running: package_add $package_name"
return 1
if [ ! -f $pkgdir/PKGBUILD ]; then
echo "Cannot find PKGBUILD in $pkgdir"
return 1
if (arch-nspawn $CHROOT/$repo_name pacman -Q $package_name > /dev/null 2>&1); then
echo "Package $package_name already built"
# 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
# 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
set -e
cd $(dirname $1)
for pkg in "$@"; do
gpg --detach-sign --use-agent $pkg
find_deps() {
# Inspect package srcinfo and pull out the dependencies that we maintain in src
set -e
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
add() {
# Add packages and their AUR dependencies to src
set -e
if [ "$#" -lt 1 ]; then
echo "Must specify package(s) to add"
return 1
if [ ! -d $BASEDIR/src ]; then
mkdir $BASEDIR/src
for pkg in "$@"; do
arch-nspawn $CHROOT/root --bind=$BASEDIR/src:/src cower -q -t /src -d -d $pkg
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
arch-nspawn $CHROOT/$1 pacman -Qm
setup() {
mkdir -p $BASEDIR/src
clean() {
set -e
if [ "$#" -lt 1 ]; then
echo "Must specify repository names to clean"
return 1
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
if [ -d $BASEDIR/$repo ]; then
exe rm -rf $BASEDIR/$repo
build() {
set -e
if [ "$#" -lt 1 ]; then
echo "Must specify repository name to build (and optional package list to include)"
return 1
mkdir -p $BASEDIR/$repo_name
if [ "$#" -gt 0 ]; then
# Build only requested packages
for pkg in "$@"; do
package_build $repo_name $pkg
echo "## All requested packages built"
# Build all packages:
cd $BASEDIR/src
find -type d | sed 's/\.\///' | tail -n +2 | while read pkg; do
package_build $repo_name $pkg
echo "## All packages built"
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;
sign_packages *.pkg.tar.xz
repo-add $repo_name.db.tar.gz *.pkg.tar.xz
sign_packages $repo_name.db
if [ "$#" -gt 0 ]; then
echo "Must specify a command, eg: add, build, list, clean"
exit 1