source: rpmbuild-bot/rpmbuild-bot.sh@ 725

Last change on this file since 725 was 724, checked in by dmik, 9 years ago

rpmbuild-bot: Initial version 1.0.

RPM Build Bot is a major update of the former netlabs-rpmbuild.sh script.

File size: 13.7 KB
Line 
1#!/bin/sh
2
3#
4# rpmbuild-bot.sh: RPM Build Bot version 1.0.
5#
6# Author: Dmitriy Kuminov <coding@dmik.org>
7#
8# This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
9# WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
10#
11# Synopsis
12# --------
13#
14# This script performs a build of RPM pakcages from a given .spec file in
15# controlled environment to guarantee consistent results when building RPMs
16# different machines. It uses a separate file rpmbuild-bot-env.sh located
17# in the same directory to set up the environment and control the build
18# process. The main purpose of this script is to build RPM packages for a
19# specific distribution channel maintaining distribution-siecific rules.
20#
21# Usage
22# -----
23#
24# > rpmbuild-bot.sh SPEC [ upload[=REPO] | test[=MODE] ] [-f]
25#
26# MYAPP is the name of the RPM package spec file (extension is optional,
27# .spec is assumed). The spec file is searched in the SPECS directory of the
28# rpmbuild tree (usually $USER/rpmbuild/SPECS, rpmbuild --eval='%_specdir'
29# will show the exact location). This may be overriden by giving a spec file
30# with a path specification.
31#
32# The second argument defines the command to perfrm. The default command is
33# "build". The following sections will describe each command.
34#
35# Building packages
36# -----------------
37#
38# The "build" is the main command which is used to generate packages for
39# distribution. This command does the following:
40#
41# 1. Build all RPM packages for all architectures specified in
42# $RPMBUILD_BOT_ARCH_LIST. The packages are stored in the RPMS
43# directory of the rpmbuild tree.
44# 2. Build the source RPM. Stored in the SRPMS directory.
45# 3. Create a ZIP package from the RPMs for the architecture specified
46# last in $RPMBUILD_BOT_ARCH_LIST.
47#
48# The build process for each architecture is stored in a log file in the logs
49# directory of the rpmbuild tree (`rpmbuild --eval='%_topdir'/logs`). Creation
50# of the source RPM and ZIP files is also logged, into separate log files.
51#
52# The "build" command will fail if the log directory contains files from a
53# successfull run of another "build" command for this package. This is to
54# prevent overwriting successfully built packages that are not yet uploaded to
55# the distribution repository (see the "upload" command). You should either
56# run the "upload" command or use the -f option to force removal of these
57# log files and the corresponding package files if you are absolutely sure they
58# should be discarded.
59#
60# Doing test builds
61# -----------------
62#
63# The "test" command is used to build packages for one architecture for testing
64# purposes. In this more, neither the source RPM nor the ZIP file is created.
65# Also, a special option is automatically passed to rpmbuild to clear the %dist
66# variable to indicate that this is a local, non-distribution build. Such
67# packages will always be "older" than the corresponding builds with %dist
68# so that they will be automatically updated on the next yum update to the
69# %dist ones. The packages built in "test" mode are NOT intended for
70# distribution, they are meant only for local testing before performing the
71# full build of everything.
72#
73# It is possible to configure which steps of the build to perform using the MODE
74# argument to the "test" command which may take one of the following values:
75#
76# prep Execute the %prep section of the spec file only.
77# build Execute the %build section only (requres %prep to be executed
78# before). May be run multiple times.
79# install Execute the %install sectuion only (requires "prep" and "build"
80# to be executed before). May be run multiple times.
81# pack Create the RPM packages (requires "prep", "build" and "install"
82# to be executed before). Note that this step will also execute
83# the %clean section so that a new "build" + "install" execution is
84# necessary for "pack" to succeed.
85#
86# When no MODE argument is given, all steps are executed in a proper order.
87#
88# The results of the "test" command are stored in a log file in the logs/test
89# directory of the rpmbuild tree. The previous log file, if any, is given a .bak
90# extension (the previous .bak file will be deleted).
91#
92# Uploading packages
93# ------------------
94#
95# The "upload" command will upload the packages built with the "build"
96# command to the official distribution channel configured in
97# rpmbuild-bot-env.sh. The REPO argument specifies one of the configured
98# repositories. When REPO is not given, the default repository is used
99# (usually experimental or similar).
100#
101# Note that the "upload" command needs log files from the "build" command
102# and will fail otherwise.
103#
104# Upon successful completion, the "upload" command will remove all uploaded
105# RPM and ZIP packages and will move all "build" log files to logs/archive.
106#
107# Return value
108# ------------
109#
110# The rpmbuild-bot.sh script will return a zero exit code upon successful
111# completion and non-zero otherwise. The log files should be inspected to
112# check for a reason of the failure.
113#
114
115#
116# Helpers.
117#
118
119run()
120{
121 "$@"
122 local rc=$?
123 if test $rc != 0 ; then
124 echo "ERROR: The following command failed with error code $rc:"
125 echo $@
126 exit $rc
127 fi
128}
129
130log_run()
131{
132 log="$1"
133 shift
134 "$@" >"$log" 2>&1
135 local rc=$?
136 if test $rc != 0 ; then
137 echo "ERROR: The following command failed with error code $rc:"
138 echo $@
139 echo "You will find more information in file '$log'."
140 echo "Here are the last 10 lines of output:"
141 echo ""
142 tail "$log" -n 10
143 exit $rc
144 fi
145}
146
147warn()
148{
149 echo "WARNING: $1"
150}
151
152die()
153{
154 echo "ERROR: $1"
155 exit 1
156}
157
158check_dir_var()
159{
160 eval local val="\$$1"
161 [ -n "$val" ] || die "$1 is empty."
162 [ -d "$val" ] || die "$1 is '$val', not a valid directory."
163}
164
165usage()
166{
167 echo "Usage:"
168 sed -n -e "s/rpmbuild-bot.sh/${0##*/}/g" -e 's/^# > / /p' < "$0"
169 exit 255
170}
171
172build_cmd()
173{
174 # Check settings.
175 test -n "$RPMBUILD_BOT_ARCH_LIST" || die "RPMBUILD_BOT_ARCH_LIST is empty."
176
177 local base_arch="${RPMBUILD_BOT_ARCH_LIST##* }"
178
179 echo "Spec file: $spec_full"
180 echo "Targets: $RPMBUILD_BOT_ARCH_LIST + SRPM + ZIP ($base_arch)"
181
182 if [ -f "$spec_list" ] ; then
183 if [ -z "$force" ] ; then
184 die "File '$spec_list' already exists.
185This file indicates a successful build that was not yet uploaded.
186Either run the '"'upload'"' command to upload the generated RPM and ZIP
187packages to the distribution repository or use the -f option to
188force their removal if you are sure they should be discarded."
189 fi
190
191 echo "Detected successful build in $spec_list, cleaning up due to -f option..."
192 while read f; do
193 echo "Removing $f..."
194 rm -f "$f"
195 done < "$spec_list"
196
197 echo "Removing $log_base.*.log and .list files..."
198 rm -f "$log_base".*.log "$log_base".*.list "$log_base".list
199 fi
200
201 # Generate RPMs.
202 for arch in $RPMBUILD_BOT_ARCH_LIST ; do
203 echo "Creating RPMs for '$arch' target (logging to $log_base.$arch.log)..."
204 log_run "$log_base.$arch.log" rpmbuild.exe --target=$arch -bb "$spec_full"
205 done
206
207 # Generate SRPM.
208 echo "Creating SRPM (logging to $log_base.srpm.log)..."
209 log_run "$log_base.srpm.log" rpmbuild -bs "$spec_full"
210
211 # Find SRPM file name in the log.
212 local src_rpm=`grep "^Wrote: \+.*\.src\.rpm$" "$log_base.srpm.log" | sed -e "s#^Wrote: \+##g"`
213 [ -n "$src_rpm" ] || die "Cannot find .src.rpm file name in '$log_base.srpm.log'."
214
215 # Find pacakge version.
216 local ver_full="${src_rpm%.src.rpm}"
217 local ver_full="${ver_full##*/${spec_name}-}"
218 [ -n "$ver_full" ] || die "Cannot deduce package version from '$src_rpm'."
219
220 # Find all RPM packages for the base arch.
221 local rpms=`grep "^Wrote: \+.*\.\($base_arch\.rpm\|noarch\.rpm\)$" "$log_base.$base_arch.log" | sed -e "s#^Wrote: \+##g"`
222 [ -n "$rpms" ] || die "Cannot find .$base_arch.rpm/.noarch.rpm file names in '$log_base.base_arch.log'."
223
224 local ver_full_zip=`echo $ver_full | tr . _`
225 local zip="$zip_dir/$spec_name-$ver_full_zip.zip"
226
227 # Generate ZIP.
228 echo "Creating ZIP (logging to $log_base.zip.log)..."
229 create_zip()
230 {(
231 run cd "$zip_dir"
232 rm -r "@unixroot" 2> /dev/null
233 for f in "$rpms" ; do
234 echo "Unpacking $f..."
235 run rpm2cpio "$f" | cpio -idm
236 done
237 rm -f "$zip" 2> /dev/null
238 echo "Creating '$zip'..."
239 run zip -mry9 "$zip" "@unixroot"
240 )}
241 log_run "$log_base.zip.log" create_zip
242
243 local ver_list="$log_base.$ver_full.list"
244
245 # Generate list of all generated packages for further reference.
246 echo "Creating list file ($ver_list)..."
247 echo "$src_rpm" > "$ver_list"
248 echo "$zip" >> "$ver_list"
249 # Save base arch RPMs.
250 for f in "$rpms" ; do
251 echo "$f" >> "$ver_list"
252 done
253 # Save other arch RPMs.
254 for arch in ${RPMBUILD_BOT_ARCH_LIST%${base_arch}} ; do
255 rpms=`grep "^Wrote: \+.*\.$arch\.rpm$" "$log_base.$arch.log" | sed -e "s#^Wrote: \+##g"`
256 [ -n "$rpms" ] || die "Cannot find .$arch.rpm file names in '$log_base.arch.log'."
257 for f in "$rpms" ; do
258 echo "$f" >> "$ver_list"
259 done
260 done
261
262 # Everything succeeded. Symlink the list file so that "upload" can find it.
263 run ln -s "${ver_list##*/}" "$log_base.list"
264}
265
266test_cmd()
267{
268 echo "Spec file: $spec_full"
269
270 local base_arch="${RPMBUILD_BOT_ARCH_LIST##* }"
271 local cmds="--define dist%%nil"
272
273 [ -z "$command_arg" ] && command_arg="all"
274
275 case "$command_arg" in
276 all)
277 cmds="$cmds -bb"
278 ;;
279 prep)
280 cmds="$cmds -bp --short-circuit"
281 ;;
282 build)
283 cmds="$cmds -bc --short-circuit"
284 ;;
285 install)
286 cmds="$cmds -bi --short-circuit"
287 ;;
288 pack)
289 cmds="$cmds -bb --short-circuit"
290 ;;
291 *)
292 die "Invalid test build sub-command '$command_arg'."
293 ;;
294 esac
295
296 local log_file="$log_dir/test/$spec_name.log"
297
298 if [ -f "$log_file" ] ; then
299 rm -f "$log_file.bak"
300 run mv "$log_file" "$log_file.bak"
301 fi
302
303 echo "Doing test RPM build for '$base_arch' target (logging to $log_file)..."
304 log_run "$log_file" rpmbuild.exe --target=$base_arch $cmds $spec_full
305
306 # Show the generated RPMs when appropriate.
307 if [ "$command_arg" = "all" -o "$command_arg" = "pack" ] ; then
308 local rpms=`grep "^Wrote: \+.*\.\($base_arch\.rpm\|noarch\.rpm\)$" "$log_file" | sed -e "s#^Wrote: \+##g"`
309 if [ -n "$rpms" ] ; then
310 echo "Successfully generated the following RPMs:"
311 for f in "$rpms" ; do
312 echo "$f"
313 done
314 else
315 warn "Cannot find .$base_arch.rpm/.noarch.rpm file names in '$log_file'."
316 fi
317 fi
318}
319
320upload_cmd()
321{
322 # Check settings.
323 test -n "$RPMBUILD_BOT_UPLOAD_REPO_LIST" || die "RPMBUILD_BOT_UPLOAD_REPO_LIST is empty."
324
325 local repo="$command_arg"
326 [ -z "$repo" ] && repo="${RPMBUILD_BOT_UPLOAD_REPO_LIST%% *}"
327
328 check_dir_var "RPMBUILD_BOT_UPLOAD_${repo}_DIR"
329
330 eval local base="\$RPMBUILD_BOT_UPLOAD_${repo}_DIR"
331
332 [ -f "$spec_list" ] || die \
333"File '$spec_list' is not found.
334You man need to build the packages using the 'build' command."
335
336 while read f; do
337 case "$f" in
338 *.src.rpm)
339 eval local d="$RPMBUILD_BOT_UPLOAD_REPO_LAYOUT_srpm"
340 ;;
341 *.*.rpm)
342 local arch="${f%.rpm}"
343 arch="${arch##*.}"
344 [ -n "$arch" ] || die "No arch spec in file name '$f' in '$spec_list'."
345 eval d="$RPMBUILD_BOT_UPLOAD_REPO_LAYOUT_rpm"
346 ;;
347 *.zip)
348 eval d="$RPMBUILD_BOT_UPLOAD_REPO_LAYOUT_zip"
349 ;;
350 *)
351 die "Unsupported file name '$f' in '$spec_list'."
352 ;;
353 esac
354 [ -d "$d" ] || die "'$d' is not a directory."
355 [ -f "$d/${f##*/}" -a -z "$force" ] && die \
356"File '$d/${f##*/}' already exists.
357Use the -f option to force uploading if you are sure the existing
358packages in the repository should be discarded."
359 echo "Copying $f to $d..."
360 run cp -p "$f" "$d"
361 done < "$spec_list"
362
363 # On success, delete the uploaded packages and archive log files.
364 while read f; do
365 echo "Removing $f..."
366 rm -f "$f"
367 done < "$spec_list"
368
369 # Note: versioned .list file will remain in archive forever (for further reference).
370 echo "Removing old '$spec_name' logs from $log_dir/archive..."
371 rm -f "$log_dir/archive/$spec_name".*.log "$log_dir/archive/$spec_name".list
372 echo "Moving '$spec_name' logs to $log_dir/archive..."
373 run mv "$log_base".*.log "$log_base".*.list "$log_base".list "$log_dir/archive/"
374}
375
376#
377# Main.
378#
379
380# Parse command line.
381while [ -n "$1" ] ; do
382 case "$1" in
383 -*)
384 options="$*"
385 while [ -n "$1" ] ; do
386 case "$1" in
387 -f) force="-f"
388 ;;
389 *) usage
390 ;;
391 esac
392 shift
393 done
394 break
395 ;;
396 *)
397 if [ -z "$spec" ] ; then
398 spec="$1"
399 else
400 command="$1"
401 fi
402 ;;
403 esac
404 shift
405done
406
407[ -n "$spec" ] || usage
408[ -z "$command" ] && command="build"
409
410command_name="${command%=*}"
411command_arg="${command#*=}"
412[ "$command_name" = "$command_arg" ] && command_arg=
413
414case "$command_name" in
415 build|upload|test)
416 ;;
417 *) usage
418 ;;
419esac
420
421# Query all rpmbuild macros in a single run as it may be slow.
422eval `rpmbuild.exe --eval='rpmbuild_dir="%_topdir" ; spec_dir="%_specdir"' | tr '\\\' /`
423
424log_dir="$rpmbuild_dir/logs"
425zip_dir="$rpmbuild_dir/zip"
426
427spec=`echo $spec | tr '\\\' /`
428
429spec_name="${spec##*/}"
430
431if [ "$spec_name" = "$spec" ] ; then
432 # No path information, use SPECS
433 spec_full="$spec_dir/${spec_name%.spec}.spec"
434else
435 # Has some path, use it.
436 spec_full="${spec%.spec}.spec"
437fi
438
439spec_name="${spec_name%.spec}"
440
441[ -f "$spec_full" ] || die "Spec file '$spec_full' is not found."
442
443# Prepare some (non-rpmbuild-standard) directories.
444run mkdir -p "$log_dir"
445run mkdir -p "$log_dir/archive"
446run mkdir -p "$log_dir/test"
447run mkdir -p "$zip_dir"
448
449[ -z "$command" ] && command="build"
450
451command_name="${command%=*}"
452comand_arg="${command#*=}"
453[ "$command_name" = "$command_arg" ] && command_arg=
454
455log_base="$log_dir/$spec_name"
456spec_list="$log_base.list"
457
458echo "Package: $spec_name"
459echo "Command: $command $options"
460
461# Set up the rpmbuild-bot environment.
462. "${0%%.sh}-env.sh"
463
464run eval "${command_name}_cmd"
465
466echo "All done."
467
468exit 0
Note: See TracBrowser for help on using the repository browser.