summaryrefslogtreecommitdiffstats
path: root/tools/tarsource.sh
blob: eee2a9739b3398ee28418ec05079aa18700f5056 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#!/bin/bash
# 2018 by David Lamparter, placed in the Public Domain

help() {
	cat <<EOF
FRR tarball/dsc helper, intended to run from a git checkout

Usage:
	./tarsource.sh [-dDn] [-i GITPATH] [-o OUTDIR] [-S KEYID]
			[-C COMMIT] [-e EXTRAVERSION] [-z gz|xz]

options:
    -i GITPATH		path to git working tree or bare repository.
			- default: parent directory containing this script
    -o OUTDIR		path to place the generated output files in.
			- default: current directory
    -C COMMIT		build tarball for specified git commit
			- default: current HEAD
    -e EXTRAVERSION	override automatic package extraversion
			- default "-YYYYMMDD-NN-gGGGGGGGGGGGG", but the script
			  autodetects if a release tag is checked out
    -z gz|xz		compression format to use
			- default: xz
    -S KEYID		sign the output with gpg key
    -d			use dirty git tree with local changes
    -D			generate Debian .dsc and .debian.tar.xz too
			(note: output files are moved to parent directory)
    -l			remove Debian auto-build changelog entry
			(always done for releases)
    -V			write version information to config.version and exit
    -n			allow executing from non-git source (NOT RECOMMENDED)
    -h			show this help text

Note(1) that this script tries very hard to generate a deterministic,
reproducible tarball by eliminating timestamps and similar things.  However,
since the tarball includes autoconf/automake files, the versions of these
tools need to be _exactly_ identical to get the same tarball.

Note(2) the debian ".orig" tarball is always identical to the "plain" tarball
generated without the -D option.

Note(3) if you want the tool to identify github PRs, you need to edit your
.git/config to fetch PRs from github like this:

	[remote "origin"]
		url = git@github.com:frrouting/frr.git
		fetch = +refs/heads/*:refs/remotes/origin/*
ADD:		fetch = +refs/pull/*/head:refs/remotes/origin/pull/*
EOF
}

set -e

options=`getopt -o 'hi:o:C:S:e:z:DdnlV' -l help -- "$@"`
debian=false
dirty=false
nongit=false
zip=xz
adjchangelog=false
writeversion=false
extraset=false
set - $options
while test $# -gt 0; do
	arg="$1"; shift; optarg=$1
	case "$arg" in
	-h|--help)	help; exit 0;;
	-d)		dirty=true;;
	-D)		debian=true;;
	-n)		nongit=true;;
	-i)		eval src=$optarg; shift;;
	-C)		eval commit=$optarg; shift;;
	-o)		eval outdir=$optarg; shift;;
	-e)		eval extraver=$optarg; extraset=true; shift;;
	-z)		eval zip=$optarg; shift;;
	-S)		eval keyid=$optarg; shift;;
	-l)		adjchangelog=true;;
	-V)		writeversion=true;;
	--)		break;;
	*)		echo something went wrong with getopt >&2
			exit 1
			;;
	esac
done

cwd="`pwd`"
outdir="${outdir:-$cwd}"

if test -e "$outdir" -a \! -d "$outdir"; then
	echo "output $outdir must be a directory" >&2
	exit 1
elif test \! -d "$outdir"; then
	mkdir -p "$outdir"
fi

cd "$outdir"
outdir="`pwd`"
cd "$cwd"
cd "`dirname $0`/.."
selfdir="`pwd`"
src="${src:-$selfdir}"

if $writeversion; then
	if $nongit; then
		echo "The -V option cannot be used without a git tree" >&2
		exit 1
	fi
	dirty=true
fi

case "$zip" in
gz)	ziptarget=dist-gzip; ziptool="gzip -n -9"; unzip="gzip -k -c";;
xz)	ziptarget=dist-xz;   ziptool="xz -z -e";   unzip="xz -d -k -c";;
*)	echo "unknown compression format $zip" >&2
	exit 1
esac

# always overwrite file ownership in tars
taropt="--owner=root --group=root"

onexit() {
	rv="$?"
	set +e
	test -n "$tmpdir" -a -d "$tmpdir" && rm -rf "$tmpdir"

	if test "$rv" -ne 0; then
		echo -e "\n\033[31;1mfailed\n" >&2
		if test "$dirty" = true; then
			echo please try running the script without the -d option.>&2
		fi
	fi
	exit $rv
}
trap onexit EXIT
tmpdir="`mktemp -d -t frrtar.XXXXXX`"

if test -e "$src/.git"; then
	commit="`git -C \"$src\" rev-parse \"${commit:-HEAD}\"`"

	if $dirty; then
		cd "$src"
		echo -e "\033[31;1mgit: using dirty worktree in $src\033[m" >&2
	else
		echo -e "\033[33;1mgit: preparing a clean clone of $src\033[m"
		branch="${tmpdir##*/}"
		cd "$tmpdir"

		git -C "$src" branch "$branch" "$commit"
		git clone --single-branch -s -b "$branch" "$src" source
		git -C "$src" branch -D "$branch"
		cd source
	fi

	# if we're creating a tarball from git, force the timestamps inside
	# the tar to match the commit date - this makes the tarball itself
	# reproducible
	gitts="`TZ=UTC git show -s --format=%cd --date=local $commit`"
	gitts="`TZ=UTC date -d "$gitts" '+%Y-%m-%dT%H:%M:%SZ'`"
	taropt="--mtime=$gitts $taropt"

	# check if we're on a release tag
	gittag="`git -C \"$src\" describe --tags --match 'frr-*' --first-parent --long $commit`"
	gittag="${gittag%-g*}"
	gittag="${gittag%-*}"

	# if there have been changes to packaging or tests, it's still the
	# same release
	changes="`git diff --name-only "$gittag" $commit | \
		egrep -v '\.git|^m4/|^config|^README|^alpine/|^debian/|^pkgsrc/|^ports/|^redhat/|^snapcraft/|^solaris/|^tests/|^tools/|^gdb/|^docker/|^\.' | \
		wc -l`"
	if test "$changes" -eq 0; then
		adjchangelog=true
		echo "detected release build for tag $gittag" >&2
		$extraset || extraver=""
	elif ! $adjchangelog; then
		gitdate="`TZ=UTC date -d "$gitts" '+%Y%m%d'`"
		gitrev="`git rev-parse --short $commit`"
		dayseq="`git rev-list --since \"${gitts%T*} 00:00:00 +0000\" $commit | wc -l`"
		dayseq="`printf '%02d' $(( $dayseq - 1 ))`"

		$extraset || extraver="-$gitdate-$dayseq-g$gitrev"

		git -C "$src" remote -v | grep fetch | sed -e 's% (fetch)$%%' \
			| egrep -i '\b(git@github\.com:frrouting/frr\.git|https://github\.com/FRRouting/frr\.git)$' \
			| while read remote; do
			remote="${remote%%	*}"

			git -C "$src" var -l | egrep "^remote.$remote.fetch=" \
				| while read fetch; do
				fetch="${fetch#*=}"
				from="${fetch%:*}"
				to="${fetch#*:}"
				if test "$from" = "+refs/pull/*/head"; then
					name="`git -C \"$src\" name-rev --name-only --refs \"$to\" $commit`"
					test "$name" = "undefined" && continue
					realname="${name%~*}"
					realname="${realname%%^*}"
					realname="${realname%%@*}"
					if test "$realname" = "$name"; then
						echo "${name##*/}" > "$tmpdir/.gitpr"
						break
					fi
				fi
			done || true
			test -n "$gitpr" && break
		done || true
		test $extraset = false -a -f "$tmpdir/.gitpr" && extraver="-PR`cat \"$tmpdir/.gitpr\"`$extraver"
	fi

	debsrc="git ls-files debian/"
else
	if $nongit; then
		echo -e "\033[31;1mWARNING: this script should be executed from a git tree\033[m" >&2
	else
		echo -e "\033[31;1mERROR: this script should be executed from a git tree\033[m" >&2
		exit 1
	fi
	debsrc="echo debian"
fi

if $writeversion; then
	pkgver="`egrep ^AC_INIT configure.ac`"
	pkgver="${pkgver#*,}"
	pkgver="${pkgver%,*}"
	pkgver="`echo $pkgver`" # strip whitespace
	pkgver="${pkgver#[}"
	pkgver="${pkgver%]}"

	echo -e "\033[32;1mwriting version ID \033[36;1mfrr-$pkgver$extraver\033[m"

	cat > config.version <<EOF
# config.version override by tarsource.sh
EXTRAVERSION="$extraver"
DIST_PACKAGE_VERSION="$pkgver$extraver"
gitts="$gitts"
taropt="$taropt"
EOF
	sed -e "s%@VERSION@%$pkgver$extraver%" \
		< changelog-auto.in \
		> changelog-auto
	exit 0
fi

echo -e "\033[33;1mpreparing source tree\033[m"

# config.version will also overwrite gitts and taropt when tarsource.sh
# was used to write the config.version file before - but configure will
# overwrite config.version down below!
if test -f config.version; then
	# never executed for clean git build
	. ./config.version
	if $nongit; then
		$extraset || extraver="$EXTRAVERSION"
	fi
fi
if test \! -f configure; then
	# always executed for clean git build
	./bootstrap.sh
fi
if test "$EXTRAVERSION" != "$extraver" -o \! -f config.status; then
	# always executed for clean git build
	# options don't matter really - we just want to make a dist tarball
	./configure --with-pkg-extra-version=$extraver
fi

. ./config.version
PACKAGE_VERSION="$DIST_PACKAGE_VERSION"

echo -e "\033[33;1mpacking up \033[36;1mfrr-$PACKAGE_VERSION\033[m"

make GZIP_ENV="-n9" am__tar="tar -chof - $taropt \"\$\$tardir\"" $ziptarget
mv frr-${PACKAGE_VERSION}.tar.$zip "$outdir" || true
lsfiles="frr-${PACKAGE_VERSION}.tar.$zip"

if $debian; then
	mkdir -p "$tmpdir/debian/source"
	cat debian/changelog > "$tmpdir/debian/changelog"
	if $adjchangelog; then
		if grep -q 'autoconf changelog entry' debian/changelog; then
			tail -n +9 debian/changelog > "$tmpdir/debian/changelog"
		fi
	fi
	echo '3.0 (quilt)' > "$tmpdir/debian/source/format"
	DEBVER="`dpkg-parsechangelog -l\"$tmpdir/debian/changelog\" -SVersion`"

	eval $debsrc | tar -cho $taropt \
		--exclude-vcs --exclude debian/source/format \
		--exclude debian/changelog \
		--exclude debian/changelog-auto \
		--exclude debian/changelog-auto.in \
		--exclude debian/subdir.am \
		-T - -f ../frr_${DEBVER}.debian.tar
	# add specially prepared files from above
	tar -uf ../frr_${DEBVER}.debian.tar $taropt -C "$tmpdir" debian/source/format debian/changelog

	test -f ../frr_${DEBVER}.debian.tar.$zip && rm -f ../frr_${DEBVER}.debian.tar.$zip
	$ziptool ../frr_${DEBVER}.debian.tar

	# pack up debian files proper
	ln -s "$outdir/frr-${PACKAGE_VERSION}.tar.$zip" ../frr_${PACKAGE_VERSION}.orig.tar.$zip
	dpkg-source -l"$tmpdir/debian/changelog" \
		--format='3.0 (custom)' --target-format='3.0 (quilt)' \
		-b . frr_${PACKAGE_VERSION}.orig.tar.$zip frr_${DEBVER}.debian.tar.$zip

	mv ../frr_${DEBVER}.dsc "$outdir" || true
	mv ../frr_${DEBVER}.debian.tar.$zip "$outdir" || true
	if test -h ../frr_${PACKAGE_VERSION}.orig.tar.$zip; then
		rm ../frr_${PACKAGE_VERSION}.orig.tar.$zip || true
	fi
	ln -s frr-${PACKAGE_VERSION}.tar.$zip "$outdir/frr_${PACKAGE_VERSION}.orig.tar.$zip" || true

	cd "$outdir"
	test -n "$keyid" && debsign -k "$keyid" "frr_${DEBVER}.dsc"

	lsfiles="$lsfiles \
		frr_${DEBVER}.dsc \
		frr_${DEBVER}.debian.tar.$zip \
		frr_${PACKAGE_VERSION}.orig.tar.$zip"
fi

cd "$outdir"
if test -n "$keyid"; then
	$unzip frr-${PACKAGE_VERSION}.tar.$zip > frr-${PACKAGE_VERSION}.tar
	test -f frr-${PACKAGE_VERSION}.tar.asc && rm frr-${PACKAGE_VERSION}.tar.asc
	if gpg -a --detach-sign -u "$keyid" frr-${PACKAGE_VERSION}.tar; then
		lsfiles="$lsfiles frr-${PACKAGE_VERSION}.tar.asc"
	fi
	rm frr-${PACKAGE_VERSION}.tar
fi

echo -e "\n\033[32;1mdone: \033[36;1mfrr-$PACKAGE_VERSION\033[m\n"
ls -l $lsfiles