Merge pull request #1030 from AladW/build-conf-fallback
[aurutils.git] / lib / aur-fetch
blobc18a251017748dc7882bc76a6800b420faac5d68
1 #!/bin/bash
2 # aur-fetch - retrieve build files from the AUR
3 [[ -v AUR_DEBUG ]] && set -o xtrace
4 shopt -s extglob
5 argv0=fetch
6 XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
7 XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
8 AUR_FETCH_USE_MIRROR=${AUR_FETCH_USE_MIRROR:-0}
9 AUR_LOCATION=${AUR_LOCATION:-https://aur.archlinux.org}
10 PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[1]}(): }'
12 # Author information for merge commits
13 export GIT_AUTHOR_NAME=aurutils
14 export GIT_AUTHOR_EMAIL=aurutils@localhost
15 export GIT_COMMITTER_NAME=aurutils
16 export GIT_COMMITTER_EMAIL=aurutils@localhost
17 export GIT_HTTP_USER_AGENT=aurutils
19 # Placeholder for repositories without commits
20 git_empty_object=$(git hash-object -t tree /dev/null)
22 # default options
23 existing=0 recurse=0 discard=0 sync=fetch
25 # XXX: races with multiple fetch instances
26 results() {
27 local mode=$1 prev=$2 current=$3 path=$4 dest=$5
29 if [[ -w $dest ]]; then
30 printf >> "$dest" '%s:%s:%s:file://%s\n' "$mode" "$prev" "$current" "$path"
34 sync_package_config() {
35 case $(git config --get --type bool aurutils.rebase) in
36 true)
37 printf >&2 '%s: aurutils.rebase is set for %s\n' "$argv0" "$1"
38 printf '%s' rebase ;;
40 printf '%s' merge ;;
41 esac
44 usage() {
45 cat <<! | base64 -d
46 ICAgICAgICAgICAgIC4tLX5+LF9fCjotLi4uLiwtLS0tLS0tYH5+Jy5fLicKIGAtLCwsICAs
47 XyAgICAgIDsnflUnCiAgXywtJyAsJ2AtX187ICctLS4KIChfLyd+fiAgICAgICcnJycoOwoK
49 printf >&2 'usage: %s [-Sefr] [--rebase|--reset|--merge] [--] pkgname...\n' "$argv0"
50 exit 1
53 # option handling
54 source /usr/share/makepkg/util/parseopts.sh
56 opt_short='efrS'
57 opt_long=('auto' 'merge' 'reset' 'rebase' 'discard' 'existing' 'results:' 'ff'
58 'ff-only' 'no-ff' 'no-commit' 'recurse')
59 opt_hidden=('dump-options' 'sync:')
61 if ! parseopts "$opt_short" "${opt_long[@]}" "${opt_hidden[@]}" -- "$@"; then
62 usage
64 set -- "${OPTRET[@]}"
66 unset rebase_args merge_args results_file
67 while true; do
68 case "$1" in
69 # fetch options
70 -S|--auto)
71 sync=auto ;;
72 -f|--discard)
73 discard=1 ;;
74 -e|--existing)
75 existing=1 ;;
76 --merge)
77 sync=merge ;;
78 --rebase)
79 sync=rebase ;;
80 --reset)
81 sync=reset ;;
82 --results)
83 shift; results_file=$(realpath -- "$1") ;;
84 # git options
85 --ff)
86 merge_args+=(-ff) ;;
87 --ff-only)
88 merge_args+=(--ff-only) ;;
89 --no-commit)
90 merge_args+=(--no-commit) ;;
91 --no-ff)
92 merge_args+=(--no-ff); rebase_args+=(--no-ff) ;;
93 # Compatibility options
94 --sync)
95 shift; sync=$1 ;;
96 -r|--recurse)
97 recurse=1 ;;
98 --dump-options)
99 printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
100 printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
101 exit ;;
102 --) shift; break ;;
103 esac
104 shift
105 done
107 # Default to only allowing fast-forward merges (as git-pull)
108 if (( ! ${#merge_args[@]} )); then
109 merge_args=(--ff-only)
112 # option validation
113 if [[ $sync == !(auto|merge|rebase|reset|fetch) ]]; then
114 printf >&2 '%s: invalid --sync mode\n' "$argv0"
115 exit 1
118 if (( ! $# )); then
119 printf >&2 '%s: no arguments given\n' "$argv0"
120 exit 1
123 # XXX: race with concurrent processes
124 if [[ -v results_file ]]; then
125 : >"$results_file" || exit 1 # truncate file
128 # Save stdin/depends in array (2 passes)
129 if (( recurse )); then
130 mapfile -t packages < <(aur depends --pkgbase "$@")
131 wait "$!" || exit
133 elif (( $# == 1 )) && [[ $1 == "-" || $1 == "/dev/stdin" ]]; then
134 mapfile -t packages
135 else
136 packages=("$@")
137 set --
140 # Update revisions in local AUR mirror
141 declare -A local_clones
143 # With an AUR mirror, updates are retrieved in two steps. First, updates to the
144 # mirror are synchronized with `git-fetch`. Secondly, local clones of the miror
145 # are created and are updated with `git-fetch` and `git-merge` as usual.
146 if (( AUR_FETCH_USE_MIRROR )); then
147 while IFS=':' read -r pkg head; do
148 printf "Cloning into '%s'\n" "$pkg"
149 git -C "$pkg" --no-pager log --pretty=reference -1
151 if [[ -v results_file ]]; then
152 results 'clone' "$git_empty_object" "$head" "$PWD/$pkg" "$results_file"
154 local_clones[$pkg]=$head
155 done < <(
156 aur fetch--mirror --lclone "${packages[@]}"
158 wait "$!" || exit
161 # Main loop
162 for pkg in "${packages[@]}"; do
163 unset -f git
165 # Local clone by fetch--mirror
166 if [[ ${local_clones[$pkg]} ]]; then
167 continue
169 # Verify if the repository is hosted on AUR (#959)
170 elif (( existing )) && ! git ls-remote --exit-code "$AUR_LOCATION/$pkg" >/dev/null; then
171 printf >&2 '%s: warning: package %s is not in AUR, skipping\n' "$argv0" "$pkg"
172 continue
174 # Clone package if not existing
175 elif [[ ! -d $pkg/.git ]]; then
176 git clone "$AUR_LOCATION/$pkg" || exit 1
178 head=$(git -C "$pkg" rev-parse --verify HEAD)
179 [[ $head ]] && git -C "$pkg" --no-pager log --pretty=reference -1
181 if [[ -v results_file ]]; then
182 results 'clone' "$git_empty_object" "${head:-$git_empty_object}" "$PWD/$pkg" "$results_file"
185 # Update existing git repository
186 else
187 # Per-package lock
188 exec {fd}< "$pkg"/.git
189 flock --wait 5 "$fd" || exit 1
191 # Avoid issues with filesystem boundaries (#274)
192 git() { command git -C "$pkg" "$@"; }
194 # Retrieve per-package configuration (aurutils.rebase, #1007)
195 if [[ $sync == 'auto' ]]; then
196 sync_pkg=$(sync_package_config "$pkg")
197 else
198 sync_pkg=$sync
201 # Retrieve new upstream commits
202 git fetch origin || exit
204 # Store original HEAD for --results output
205 orig_head=$(git rev-parse --verify --quiet HEAD)
206 orig_head=${orig_head:-$git_empty_object}
208 # Set correct branch for rebasing local clones
209 if (( AUR_FETCH_USE_MIRROR )); then
210 upstream=origin/$pkg # master -> origin/$pkg
211 else
212 upstream=origin/master # master -> origin/master
215 # Merge in new history
216 case $sync_pkg in
217 rebase)
218 dest='HEAD'
219 if (( discard )); then
220 # reset worktree
221 git checkout ./
223 git rebase "${rebase_args[@]}" "$upstream" ;;
224 merge)
225 dest='HEAD'
226 if (( discard )); then
227 # reset worktree if new commits are available
228 git merge-base --is-ancestor 'master@{u}' HEAD || git checkout ./
230 git merge "${merge_args[@]}" "$upstream" ;;
231 reset)
232 dest='master@{u}'
233 git reset --hard 'master@{u}' ;;
234 fetch)
235 dest='master@{u}'
237 esac || {
238 printf >&2 '%s: failed to %s %s\n' "$argv0" "$sync_pkg" "$pkg"
239 exit 1
241 head=$(git rev-parse --verify "$dest")
243 if [[ -v results_file ]]; then
244 results "$sync_pkg" "$orig_head" "$head" "$PWD/$pkg" "$results_file"
246 exec {fd}<&- # release lock
247 fi >&2 # print all git output to stderr
248 done
250 # vim: set et sw=4 sts=4 ft=sh: