update_git_source_package: add fixed issues to commit message
[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
78
79 eval $(
80 "$MAKE" --no-print-directory -C "$(dirname "$MAKEFILE")" \
81 var.PKG_NAME \
82 var.PKG_RELEASE \
83 var.PKG_SOURCE_PROTO \
84 var.PKG_SOURCE_URL \
85 var.PKG_SOURCE_DATE \
86 var.PKG_SOURCE_VERSION \
87 var.PKG_MIRROR_HASH
88 )
89
90 case "$PKG_SOURCE_PROTO:$PKG_SOURCE_URL" in
91 git:http://*|git:https://*|git:git://*|git:file:*)
92 :
93 ;;
94 *)
95 echo "Unsupported combination of source protocol ('$PKG_SOURCE_PROTO') and url ('$PKG_SOURCE_URL')." >&2
96 exit 1
97 ;;
98 esac
99
100 TEMP_GIT_DIR=
101
102 for signal in INT TERM EXIT; do
103 trap '
104 [ -d "$TEMP_GIT_DIR" ] && rm -rf "$TEMP_GIT_DIR";
105 git -C "$(dirname "$MAKEFILE")" checkout --quiet "$(basename "$MAKEFILE")"
106 ' $signal
107 done
108
109 TEMP_GIT_DIR=$(mktemp -d) || {
110 echo "Unable to create temporary Git directory." >&2
111 exit 1
112 }
113
114 git clone --bare "$PKG_SOURCE_URL" "$TEMP_GIT_DIR" || {
115 echo "Unable to clone Git repository '$PKG_SOURCE_URL'." >&2
116 exit 1
117 }
118
119 GIT_LOG="$(git -C "$TEMP_GIT_DIR" log \
120 --reverse --no-merges \
121 --abbrev=12 \
122 --format="%h %s" \
123 "$PKG_SOURCE_VERSION..$COMMIT" \
124 )" || {
125 echo "Unable to determine changes from commit '$PKG_SOURCE_VERSION' to '$COMMIT'." >&2
126 exit 1
127 }
128
129 GIT_DATE_COMMIT=$(git -C "$TEMP_GIT_DIR" log \
130 -1 --format='%cd %H' \
131 --date='format:%Y-%m-%d' \
132 "$COMMIT" \
133 ) || {
134 echo "Unable to determine target commit ID and date." >&2
135 exit 1
136 }
137
138 GIT_FIXES="$(
139 IFS=$', \t\n'
140 for issue in $(
141 git -C "$TEMP_GIT_DIR" log \
142 --format="%b" \
143 "$PKG_SOURCE_VERSION..$COMMIT" \
144 | sed -rne 's%^Fixes:(([ ,]*([[:alnum:]_]*#[0-9]+|https?://[^[:space:]]+))+)$%\1%p'
145 ); do
146 case "$issue" in
147 http://*|https://*)
148 echo "$issue"
149 ;;
150 GH#[0-9]*|openwrt#[0-9]*)
151 echo "https://github.com/openwrt/openwrt/issues/${issue#*#}"
152 ;;
153 FS#[0-9]*)
154 echo "https://bugs.openwrt.org/?task_id=${issue#FS#}"
155 ;;
156 [a-zA-Z0-9_]*#[0-9]*)
157 echo "https://github.com/openwrt/${issue%#*}/issues/${issue#*#}"
158 ;;
159 '#'[0-9]*)
160 case "$PKG_SOURCE_URL" in
161 *://github.com/*)
162 echo "${PKG_SOURCE_URL%/}/issues/${issue#\#}"
163 ;;
164 *://git.openwrt.org/project/*)
165 project=${PKG_SOURCE_URL#*://git.openwrt.org/project/}
166 project=${project%.git}
167 echo "https://github.com/openwrt/${project}/issues/${issue#\#}"
168 ;;
169 esac
170 ;;
171 esac
172 done \
173 | sort --version-sort \
174 | uniq \
175 | sed -e 's#^#Fixes: #'
176 )"
177
178 sed -i -r \
179 -e "/PKG_SOURCE_VERSION/s#\<$PKG_SOURCE_VERSION\>#${GIT_DATE_COMMIT#* }#" \
180 -e "/PKG_SOURCE_DATE/s#\<$PKG_SOURCE_DATE\>#${GIT_DATE_COMMIT% *}#" \
181 "$MAKEFILE"
182
183 if [ -n "$PKG_RELEASE" ] && [ "$PKG_RELEASE" != "1" ]; then
184 sed -i -r \
185 -e "/PKG_RELEASE/s#\<$PKG_RELEASE\>#1#" \
186 "$MAKEFILE"
187 fi
188
189 eval $(
190 "$MAKE" --no-print-directory -C "$(dirname "$MAKEFILE")" \
191 var.PKG_SOURCE
192 )
193
194 "$MAKE" -C "$(dirname "$MAKEFILE")" download || {
195 echo "Unable to download and pack updated Git sources." >&2
196 exit 1
197 }
198
199 DL_HASH=$(sha256sum "$TOPDIR/dl/$PKG_SOURCE") || {
200 echo "Unable to determine archive checksum." >&2
201 exit 1
202 }
203
204 sed -i -r \
205 -e "/PKG_MIRROR_HASH/s#\<$PKG_MIRROR_HASH\>#${DL_HASH%% *}#" \
206 "$MAKEFILE"
207
208 git -C "$(dirname "$MAKEFILE")" commit \
209 --signoff --no-edit \
210 --message "$PKG_NAME: update to Git $COMMIT (${GIT_DATE_COMMIT% *})" \
211 --message "$GIT_LOG" \
212 ${GIT_FIXES:+--message "$GIT_FIXES"} \
213 "$(basename "$MAKEFILE")"
214
215 "$MAKE" --no-print-directory -C "$(dirname "$MAKEFILE")" check || {
216 echo "WARNING: Package check failed for updated Makefile!"
217 exit 1
218 }