aboutsummaryrefslogtreecommitdiff
path: root/container
diff options
context:
space:
mode:
Diffstat (limited to 'container')
-rw-r--r--container/Dockerfile30
-rwxr-xr-xcontainer/bin/buildpackage.sh29
-rwxr-xr-xcontainer/bin/getpackage.py159
-rw-r--r--container/sudoers2
4 files changed, 220 insertions, 0 deletions
diff --git a/container/Dockerfile b/container/Dockerfile
new file mode 100644
index 0000000..9decea7
--- /dev/null
+++ b/container/Dockerfile
@@ -0,0 +1,30 @@
+FROM archlinux:base-devel
+
+ENV PACKAGE_NAME=""
+ENV PACKAGER="John Doe <John.Doe@example.com>"
+ENV ARCH="x86_64"
+ENV FORCE_REBUILD="no"
+ENV CHOWN=""
+
+RUN pacman --noconfirm -Syu
+RUN pacman --noconfirm -S git sudo
+
+RUN groupadd sudo
+RUN useradd --groups sudo --home-dir /workdir --create-home --user-group --system aurbuilder
+COPY --chown=root:root sudoers /etc/sudoers
+
+
+RUN mkdir /pkgout
+VOLUME /pkgout
+
+RUN mkdir /pkgdest
+RUN chown aurbuilder:aurbuilder /pkgdest
+
+RUN pacman --noconfirm -S python python-requests
+
+RUN mkdir -p /opt/aurbuilder
+
+COPY --chown=root:root bin/ /opt/aurbuilder
+ENV PATH="${PATH}:/opt/aurbuilder"
+
+CMD buildpackage.sh
diff --git a/container/bin/buildpackage.sh b/container/bin/buildpackage.sh
new file mode 100755
index 0000000..a1dd8c3
--- /dev/null
+++ b/container/bin/buildpackage.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+echo "BUILDING: $PACKAGE_NAME"
+echo "PACKAGER: $PACKAGER"
+
+INTERMED_DEST="/pkgdest"
+
+cd /workdir || exit 1
+
+while read -r NAME BASE VER _; do
+ echo "$NAME $VER for $ARCH"
+ OUTNAME="$NAME-$VER-$ARCH.pkg"
+
+ # Only build when needed
+ if [ ! "$FORCE_REBUILD" = "yes" ] && compgen -G "/pkgout/${OUTNAME}*"; then
+ echo "$NAME $VER ($ARCH) is already built. Skipping."
+ continue
+ fi
+
+ sudo --user=aurbuilder \
+ git clone "https://aur.archlinux.org/$BASE.git" "$BASE"
+ cd "$BASE" || exit 1
+
+ sudo -u aurbuilder CARCH="$ARCH" PACKAGER="$PACKAGER" PKGDEST="$INTERMED_DEST" \
+ makepkg --force --syncdeps --noconfirm --install
+
+ cp --no-preserve=ownership "$INTERMED_DEST/"*.pkg.tar.zst /pkgout
+ #test -n "$CHOWN" && sudo chown "$CHOWN" "/pkgout/${OUTNAME}"*
+done <<< "$(getpackage.py "$PACKAGE_NAME")"
diff --git a/container/bin/getpackage.py b/container/bin/getpackage.py
new file mode 100755
index 0000000..0091dd6
--- /dev/null
+++ b/container/bin/getpackage.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+# Create a list of package names required to build supplied AUR package.
+# packages present in the official repos will be ignored.
+
+# relevant docs:
+# AUR API:
+# https://wiki.archlinux.org/title/Aurweb_RPC_interface
+# Package API:
+# https://wiki.archlinux.org/title/Official_repositories_web_interface
+
+import os
+import sys
+import requests
+import json
+
+#from packaging import version
+#version.parse
+
+def eprint(*args, **kwargs):
+ print(*args, file=sys.stderr, **kwargs)
+
+
+# TODO we should actually use version limits instead
+def sanitize_name(_name):
+ _name = _name.replace('<','=')
+ _name = _name.replace('>','=')
+
+ return _name.split('=')[0]
+
+
+# TODO: Don't just take the first one. prioritize maybe Votes, version, git, bin, ...
+def aur_search_package(_name):
+ _name = sanitize_name(_name)
+
+ data = requests.get(f'https://aur.archlinux.org/rpc/?v=5&type=search&by=name&arg={_name}').json()
+
+ for r in data['results']:
+ p = aur_get_package(r['Name'])
+ if p is None:
+ continue
+
+ if 'Provides' in p:
+ for prov in p['Provides']:
+ if sanitize_name(prov) == _name:
+ return p
+
+ return None
+
+
+def aur_get_package(_name):
+ _name = sanitize_name(_name)
+
+ data = requests.get(f'https://aur.archlinux.org/rpc/?v=5&type=info&arg={_name}').json()
+
+ if len(data['results']) != 1:
+ return aur_search_package(_name)
+
+ return data['results'][0]
+
+
+def repo_search_package(_name):
+ _name = sanitize_name(_name)
+
+ data = requests.get(f'https://archlinux.org/packages/search/json/?q={_name}').json()
+ for r in data['results']:
+ p = repo_get_package(r['pkgname'], False)
+ if p is None:
+ continue
+
+ if 'provides' in p:
+ for prov in p['provides']:
+ if sanitize_name(prov) == _name:
+ return p
+
+ return None
+
+
+def repo_get_package(_name, _search=True):
+ _name = sanitize_name(_name)
+
+ data = requests.get(f'https://archlinux.org/packages/search/json/?name={_name}').json()
+
+ if len(data['results']) != 1:
+ if _search:
+ return repo_search_package(_name)
+ else:
+ return None
+
+ return data['results'][0]
+
+
+def build_aur_dependencies(_pkgname):
+ _pkgname = sanitize_name(_pkgname)
+
+ ret = []
+
+ maybe_deps = aur_get_package(_pkgname)
+ if maybe_deps is None:
+ eprint(f'WARNING: {_pkgname} Was not found in AUR!')
+ return []
+ elif 'Depends' not in maybe_deps:
+ return []
+
+ deps = maybe_deps['Depends']
+
+ for dep in deps:
+ # if package exists in official repo
+ if repo_get_package(dep) is not None:
+ continue
+
+ # detect depency cycles
+ if dep in ret:
+ continue
+
+ ret.append(dep)
+ ret.insert(0,dep)
+ ret = build_aur_dependencies(dep) + ret
+
+ return ret
+
+if len(sys.argv) <= 1:
+ eprint('CRITICAL: No package name provided.')
+ exit(1)
+
+PKG=sys.argv[1]
+
+# name: version
+TO_BUILD={}
+
+maybe_base_pkg_info = aur_get_package(PKG)
+if maybe_base_pkg_info is None:
+ eprint(f'CRITICAL: Package {PKG} not found in AUR')
+ exit(1)
+base_pkg_info = maybe_base_pkg_info
+
+aur_deps = build_aur_dependencies(PKG)
+
+# We rely on the rigth order here.
+# The dependency must be before the package that depends on it,
+# otherwise makepkg will fail
+for dep in aur_deps:
+ pkg = aur_get_package(dep)
+
+ if pkg is None:
+ eprint(f'WARNING: Dependency {dep} Unmet!')
+ continue
+
+ TO_BUILD[pkg['Name']] = {
+ 'version':pkg['Version'],
+ 'base':pkg['PackageBase']
+ }
+
+TO_BUILD[base_pkg_info['Name']] = {
+ 'version':base_pkg_info['Version'],
+ 'base':base_pkg_info['PackageBase']
+ }
+
+for e in TO_BUILD:
+ print(f'{e}\t{TO_BUILD[e]["base"]}\t{TO_BUILD[e]["version"]}')
diff --git a/container/sudoers b/container/sudoers
new file mode 100644
index 0000000..d023346
--- /dev/null
+++ b/container/sudoers
@@ -0,0 +1,2 @@
+root ALL=(ALL) ALL
+%sudo ALL=(ALL) NOPASSWD: ALL