github-merge-pr: fix loading .config if symbolic link is used
[maintainer-tools.git] / update_git_source_package.sh
1 #!/usr/bin/env bash
2 # update_git_source_package.sh: (c) 2023 Jo-Philipp Wich <jo@mein.io>
3 # Licensed under the terms of the Apache License, Version 2.0
4
5 MAKEFILE=$1
6 COMMIT=${2:-HEAD}
7 TOPDIR=$3
8
9 [ -n "$MAKEFILE" ] || {
10 cat <<-EOT
11 Usage: $0 <package name or makefile path> [revision] [topdir]
12
13 Update an OpenWrt package Makefile with PKG_SOURCE_PROTO:=git to
14 the given upstream revision (or 'HEAD' if omitted).
15
16 The script either accepts a package name, like "netifd" or
17 an absolute or relative path to a Makefile, e.g.
18 "./package/utils/ucode/Makefile".
19
20 If the revision argument is ommitted, the package is updated
21 to the current HEAD of the remote Git repository.
22
23 By default, this script tries to infer the buildroot directory
24 from the package Makefile path but it may be overridden by
25 passing it as 3rd argument. This is useful in situation where
26 the Makefile to update does not reside within an OpenWrt buildroot.
27
28 On success, the package Makefile is automatically modified and
29 the resulting changes are committed in the currently checked
30 buildroot branch using a standard commit message format.
31 EOT
32
33 exit 1
34 }
35
36 MAKE=$(which gmake) || MAKE=$(which make)
37 FIND=$(which gfind) || FIND=$(which find)
38
39 [ -x "$MAKE" ] || {
40 echo "Unable to locate `make` executable" >&2
41 exit 1
42 }
43
44 [ -x "$FIND" ] || {
45 echo "Unable to locate `find` executable" >&2
46 exit 1
47 }
48
49 [ -e "$MAKEFILE" ] || {
50 MAKEFILE=$("$FIND" "${TOPDIR:-.}/package/" -type f -path "*/$MAKEFILE/Makefile" | head -n1)
51 }
52
53 [ -f "$MAKEFILE" ] || {
54 echo "Usage: $0 <path/to/Makefile> [target commit] [path/to/buildroot/topdir]"
55 exit 1
56 }
57
58 grep -sq BuildPackage "$MAKEFILE" || {
59 echo "The file '$MAKEFILE' does not appear to be an OpenWrt package Makefile." >&2
60 exit 1
61 }
62
63 [ -n "$TOPDIR" ] || {
64 TOPDIR=$(cd "$(dirname "${MAKEFILE%/*}")"; pwd)
65
66 while [ "$TOPDIR" != "/" ]; do
67 TOPDIR=$(dirname "$TOPDIR")
68 [ -f "$TOPDIR/rules.mk" ] && break
69 done
70
71 [ -f "$TOPDIR/rules.mk" ] || {
72 echo "Unable to determine buildroot directory." >&2
73 exit 1
74 }
75 }
76
77 export TOPDIR=$(cd "$TOPDIR"; pwd)
78 export PATH="$TOPDIR/staging_dir/host/bin:$PATH"
79
80 eval $(
81 "$MAKE" --no-print-directory -C "$(dirname "$MAKEFILE")" \
82 var.PKG_NAME \
83 var.PKG_RELEASE \
84 var.PKG_SOURCE_PROTO \
85 var.PKG_SOURCE_URL \
86 var.PKG_SOURCE_DATE \
87 var.PKG_SOURCE_VERSION \
88 var.PKG_MIRROR_HASH
89 )
90
91 case "$PKG_SOURCE_PROTO:$PKG_SOURCE_URL" in
92 git:http://*|git:https://*|git:git://*|git:file:*)
93 :
94 ;;
95 *)
96 echo "Unsupported combination of source protocol ('$PKG_SOURCE_PROTO') and url ('$PKG_SOURCE_URL')." >&2
97 exit 1
98 ;;
99 esac
100
101 TEMP_GIT_DIR=
102
103 for signal in INT TERM EXIT; do
104 trap '
105 [ -d "$TEMP_GIT_DIR" ] && rm -rf "$TEMP_GIT_DIR";
106 git -C "$(dirname "$MAKEFILE")" checkout --quiet "$(basename "$MAKEFILE")"
107 ' $signal
108 done
109
110 TEMP_GIT_DIR=$(mktemp -d) || {
111 echo "Unable to create temporary Git directory." >&2
112 exit 1
113 }
114
115 git clone --bare "$PKG_SOURCE_URL" "$TEMP_GIT_DIR" || {
116 echo "Unable to clone Git repository '$PKG_SOURCE_URL'." >&2
117 exit 1
118 }
119
120 GIT_LOG="$(git -C "$TEMP_GIT_DIR" log \
121 --reverse --no-merges \
122 --abbrev=12 \
123 --format="%h %s" \
124 "$PKG_SOURCE_VERSION..$COMMIT" \
125 )" || {
126 echo "Unable to determine changes from commit '$PKG_SOURCE_VERSION' to '$COMMIT'." >&2
127 exit 1
128 }
129
130 GIT_DATE_COMMIT=$(git -C "$TEMP_GIT_DIR" log \
131 -1 --format='%cd %H' \
132 --date='format:%Y-%m-%d' \
133 "$COMMIT" \
134 ) || {
135 echo "Unable to determine target commit ID and date." >&2
136 exit 1
137 }
138
139 GIT_FIXES="$(
140 IFS=$', \t\n'
141 for issue in $(
142 git -C "$TEMP_GIT_DIR" log \
143 --format="%b" \
144 "$PKG_SOURCE_VERSION..$COMMIT" \
145 | sed -rne 's%^Fixes:(([ ,]*([[:alnum:]_]*#[0-9]+|https?://[^[:space:]]+))+)$%\1%p'
146 ); do
147 case "$issue" in
148 http://*|https://*)
149 echo "$issue"
150 ;;
151 GH#[0-9]*|openwrt#[0-9]*)
152 echo "https://github.com/openwrt/openwrt/issues/${issue#*#}"
153 ;;
154 FS#[0-9]*)
155 echo "https://bugs.openwrt.org/?task_id=${issue#FS#}"
156 ;;
157 [a-zA-Z0-9_]*#[0-9]*)
158 echo "https://github.com/openwrt/${issue%#*}/issues/${issue#*#}"
159 ;;
160 '#'[0-9]*)
161 case "$PKG_SOURCE_URL" in
162 *://github.com/*)
163 repourl=${PKG_SOURCE_URL%/}
164 repourl=${repourl%.git}
165 echo "${repourl}/issues/${issue#\#}"
166 ;;
167 *://git.openwrt.org/project/*)
168 project=${PKG_SOURCE_URL#*://git.openwrt.org/project/}
169 project=${project%.git}
170 echo "https://github.com/openwrt/${project}/issues/${issue#\#}"
171 ;;
172 esac
173 ;;
174 esac
175 done \
176 | sort --version-sort \
177 | uniq \
178 | sed -e 's#^#Fixes: #'
179 )"
180
181 sed -i -r \
182 -e "/PKG_SOURCE_VERSION/s#\<$PKG_SOURCE_VERSION\>#${GIT_DATE_COMMIT#* }#" \
183 -e "/PKG_SOURCE_DATE/s#\<$PKG_SOURCE_DATE\>#${GIT_DATE_COMMIT% *}#" \
184 "$MAKEFILE"
185
186 if [ -n "$PKG_RELEASE" ] && [ "$PKG_RELEASE" != "1" ]; then
187 sed -i -r \
188 -e "/PKG_RELEASE/s#\<$PKG_RELEASE\>#1#" \
189 "$MAKEFILE"
190 fi
191
192 eval $(
193 "$MAKE" --no-print-directory -C "$(dirname "$MAKEFILE")" \
194 var.PKG_SOURCE
195 )
196
197 "$MAKE" -C "$(dirname "$MAKEFILE")" download CONFIG_SRC_TREE_OVERRIDE= || {
198 echo "Unable to download and pack updated Git sources." >&2
199 exit 1
200 }
201
202 DL_HASH=$(sha256sum "$TOPDIR/dl/$PKG_SOURCE") || {
203 echo "Unable to determine archive checksum." >&2
204 exit 1
205 }
206
207 sed -i -r \
208 -e "/PKG_MIRROR_HASH/s#\<$PKG_MIRROR_HASH\>#${DL_HASH%% *}#" \
209 "$MAKEFILE"
210
211 git -C "$(dirname "$MAKEFILE")" commit \
212 --signoff --no-edit \
213 --message "$PKG_NAME: update to Git $COMMIT (${GIT_DATE_COMMIT% *})" \
214 --message "$GIT_LOG" \
215 ${GIT_FIXES:+--message "$GIT_FIXES"} \
216 "$(basename "$MAKEFILE")"
217
218 "$MAKE" --no-print-directory -C "$(dirname "$MAKEFILE")" check || {
219 echo "WARNING: Package check failed for updated Makefile!"
220 exit 1
221 }