From ca642b0dad8c5ee1ec9d172eae534dacd79d9410 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Sun, 27 Jul 2014 12:30:00 -0400 Subject: initial commit --- .gitignore | 2 + Makefile | 49 ++++++++++++ README.md | 28 +++++++ asp.in | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ man/asp.1.txt | 92 +++++++++++++++++++++ package.inc.sh | 148 ++++++++++++++++++++++++++++++++++ remote.inc.sh | 50 ++++++++++++ 7 files changed, 615 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 asp.in create mode 100644 man/asp.1.txt create mode 100644 package.inc.sh create mode 100644 remote.inc.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e930cc4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +asp +asp.1 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..18f6786 --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +PACKAGE_NAME = asp +VER=0 + +PREFIX = /usr/local + +BINPROGS = \ + asp + +MANPAGES = \ + man/asp.1 + +INCLUDES = \ + package.inc.sh \ + remote.inc.sh + +all: $(BINPROGS) $(MANPAGES) + +V_GEN = $(_v_GEN_$(V)) +_v_GEN_ = $(_v_GEN_0) +_v_GEN_0 = @echo " GEN " $@; + +edit = $(V_GEN) m4 -P $@.in >$@ && chmod go-w,+x $@ + +%: %.in $(INCLUDES) + $(edit) + +doc: $(MANPAGES) +man/%: man/%.txt Makefile + a2x -d manpage \ + -f manpage \ + -a manversion=$(VERSION) \ + -a manmanual="$(PACKAGE_NAME) manual" $< + +clean: + $(RM) $(BINPROGS) $(MANPAGES) + +install: all + install -dm755 $(DESTDIR)$(PREFIX)/bin + install -m755 $(BINPROGS) $(DESTDIR)$(PREFIX)/bin + +uninstall: + for f in $(BINPROGS); do $(RM) $(DESTDIR)$(PREFIX)/bin/$$f; done + $(RM) $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_archinstallscripts + +dist: + git archive --format=tar --prefix=$(PACKAGE_NAME)-$(VER)/ v$(VER) | gzip -9 > $(PACKAGE_NAME)-$(VER).tar.gz + gpg --detach-sign --use-agent $(PACKAGE_NAME)-$(VER).tar.gz + +.PHONY: all clean install uninstall dist diff --git a/README.md b/README.md new file mode 100644 index 0000000..262e58b --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +# asp + +`asp` is a tool to manage the build source files used to create Arch Linux +packages. It attempts to replace the `abs` tool, offering more up to date +sources (via the svntogit repositories) and uses a sparse checkout model to +conserve diskspace. This probably won't be interesting to users who want a +full checkout (for whatever reason that may be). + +# Setup + +None! Though, it should be noted that the **ASPROOT** environment variable +will control where `asp` keeps its locally tracked packages. By default, this +is `$HOME/asp`. + +# Examples + +Get the source files for some packages: + +~~~ +asp export pacman testing/systemd extra/pkgfile +~~~ + +List the repositories a package has been pushed to: + +~~~ +asp list-repos pacman +~~~ + diff --git a/asp.in b/asp.in new file mode 100644 index 0000000..5393823 --- /dev/null +++ b/asp.in @@ -0,0 +1,246 @@ +#!/bin/bash + +ARCH_GIT_REPOS=(packages community) + +OPT_ARCH=$(uname -m) +OPT_FORCE=0 +: ${ASPROOT:=$HOME/asp} + +m4_include(remote.inc.sh) +m4_include(package.inc.sh) + +log_meta() { + printf "$1 $2\n" "${@:3}" +} + +log_error() { + log_meta 'error:' "$@" >&2 +} + +log_fatal() { + log_error "$@" + exit 1 +} + +log_warning() { + log_meta 'warning:' "$@" >&2 +} + +log_info() { + log_meta '==>' "$@" +} + +map() { + local map_r=0 + for _ in "${@:2}"; do + "$1" "$_" || (( $# > 255 ? map_r=1 : ++r )) + done + return $map_r +} + +usage() { + cat< diff --git a/package.inc.sh b/package.inc.sh new file mode 100644 index 0000000..09af4b1 --- /dev/null +++ b/package.inc.sh @@ -0,0 +1,148 @@ +package_init() { + local pkgname=$1 + local do_update=1 + + if [[ $1 = -n ]]; then + do_update=0 + shift + pkgname=$1 + fi + + package_find_remote "$pkgname" "$2" || return 1 + + (( do_update )) || return 0 + + if ! remote_is_tracking "${!2}" "$pkgname"; then + package_update "$pkgname" "${!2}" || return 1 + fi +} + +package_update() { + local pkgname=$1 remote=$2 + + git fetch "$remote" "packages/$pkgname" +} + +package_find_remote() { + local pkgname=$1 out=$2 + + # fastpath, checks local caches only + for r in "${ARCH_GIT_REPOS[@]}"; do + if remote_is_tracking "$r" "$pkgname"; then + printf -v "$out" %s "$r" + return 0 + fi + done + + # slowpath, needs to talk to the remote + for r in "${ARCH_GIT_REPOS[@]}"; do + if remote_has_package "$r" "$pkgname"; then + printf -v "$out" %s "$r" + return 0 + fi + done + + log_error 'unknown package: %s' "$pkgname" + + return 1 +} + +package_log() { + local pkgname=$1 method=$2 remote + + package_init "$pkgname" remote || return + + "_package_$method" "$pkgname" "$remote" +} + +package_export() { + local pkgname=$1 remote repo arch + local mode objtype objid path + + if [[ $pkgname = */* ]]; then + IFS=/ read -r repo pkgname <<<"$pkgname" + fi + + package_init "$pkgname" remote || return 1 + + # support $repo/$pkgname syntax + if [[ $repo ]]; then + # TODO: add an --arch flag + subtree=repos/$repo-$OPT_ARCH + else + subtree=trunk + fi + + if [[ -z $(git ls-tree "$remote/packages/$pkgname" "$subtree/") ]]; then + if [[ $repo ]]; then + log_error "package '%s' not found in repo '%s-%s'" "$pkgname" "$repo" "$OPT_ARCH" + return 1 + else + log_error "package '%s' has no trunk directory!" "$pkgname" + return 1 + fi + fi + + if (( ! OPT_FORCE )); then + mkdir "$startdir/$pkgname" || return 1 + fi + + log_info 'exporting %s:%s' "$pkgname" "$subtree" + git archive --format=tar "$remote/packages/$pkgname" "$subtree/" | + bsdtar -C "$startdir" -s ",^$subtree/,$pkgname/," -xf - "$subtree/" +} + +package_get_repos_with_arch() { + local pkgname=$1 remote=$2 + local objtype path arch repo + + while read _ objtype _ path; do + [[ $objtype = tree ]] || continue + IFS=- read repo arch <<<"${path#repos/}" + printf '%s %s\n' "$repo" "$arch" + done < <(git ls-tree "$remote/packages/$pkgname" repos/) +} + +package_get_arches() { + local pkgname=$1 remote arch + declare -A arches + + package_init "$pkgname" remote || return 1 + + while read _ arch; do + arches["$arch"]=1 + done < <(package_get_repos_with_arch "$pkgname" "$remote") + + printf '%s\n' "${!arches[@]}" +} + +package_get_repos() { + local pkgname=$1 remote repo + declare -A repos + + package_init "$pkgname" remote || return 1 + + while read repo _; do + repos["$repo"]=1 + done < <(package_get_repos_with_arch "$pkgname" "$remote") + + printf '%s\n' "${!repos[@]}" +} + +_package_shortlog() { + local pkgname=$1 remote=$2 + + git log --pretty=oneline "$remote/packages/$pkgname" +} + +_package_difflog() { + local pkgname=$1 remote=$2 + + git log -p "$remote/packages/$pkgname" +} + +_package_log() { + local pkgname=$1 remote=$2 + + git log "$remote/packages/$pkgname" +} diff --git a/remote.inc.sh b/remote.inc.sh new file mode 100644 index 0000000..b125419 --- /dev/null +++ b/remote.inc.sh @@ -0,0 +1,50 @@ +remote_get_all_refs() { + local remote=$1 + + mapfile -t "$2" < <(git ls-remote "$remote" 'refs/heads/packages/*' | + awk '{ sub(/refs\/heads\//, "", $2); print $2 }') +} + +remote_has_package() { + local remote=$1 pkgname=$2 + + [[ $(git ls-remote "$remote" "$pkgname") ]] +} + +remote_is_tracking() { + local repo=$1 pkgname=$2 + + git rev-parse "$repo/packages/$pkgname" &>/dev/null +} + +remote_get_tracked_refs() { + local remote=$1 + + mapfile -t "$2" < <(git branch --remote 2>/dev/null | + awk -F'( +|/)' -v "remote=$1" \ + '$2 == remote && $3 == "packages" { print "packages/" $4 }') +} + +remote_update_refs() { + local remote=$1 refspecs=("${@:2}") + + git fetch "$remote" "${refspecs[@]}" +} + +remote_update() { + local remote=$1 refspecs + + remote_get_tracked_refs "$remote" refspecs + + # refuse to update everything + # TODO: allow this with a flag + [[ -z $refspecs ]] && return 0 + + remote_update_refs "$remote" "${refspecs[@]}" +} + +remote_get_url() { + local remote=$1 + + git ls-remote --get-url "$remote" +} -- cgit v1.2.3-70-g09d2