Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-ports-kde
Path: blob/main/Tools/scripts/chkversion.pl
16462 views
1
#!/usr/bin/env perl
2
#
3
# Copyright (c) 2004 Oliver Eikemeier. All rights reserved.
4
#
5
# Redistribution and use in source and binary forms, with or without
6
# modification, are permitted provided that the following conditions are
7
# met:
8
#
9
# 1. Redistributions of source code must retain the above copyright notice
10
# this list of conditions and the following disclaimer.
11
#
12
# 2. Redistributions in binary form must reproduce the above copyright
13
# notice, this list of conditions and the following disclaimer in the
14
# documentation and/or other materials provided with the distribution.
15
#
16
# 3. Neither the name of the author nor the names of its contributors may be
17
# used to endorse or promote products derived from this software without
18
# specific prior written permission.
19
#
20
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
#
31
# MAINTAINER= [email protected]
32
#
33
# PORTVERSION and PKGORIGIN auditing script
34
#
35
# This scripts compares version numbers with previously known ones, and
36
# checks ports for a correct PKGORIGIN. It is primarily intended to be run
37
# from a (non-root) cron job.
38
#
39
# If you just call it with no preparation, it will compare all port versions
40
# with their INDEX entries and complain if they have gone backwards. You need
41
# You need an old INDEX for this, of course. An up-to-date INDEX will accomplish
42
# nothing.
43
#
44
# To use the script as intended, do the following (assuming you want to
45
# run the script as user `ports'):
46
#
47
# install port sysutils/pkg_install-devel (optional)
48
# mkdir -p /var/db/chkversion
49
# touch /var/db/chkversion/VERSIONS
50
# chown -R ports /var/db/chkversion
51
# and enter something like
52
#
53
# BLAME=yes (git specific)
54
# ALLPORTS=yes
55
# [email protected]
56
# [email protected]
57
# 0 */2 * * * /usr/ports/Tools/scripts/chkversion.pl
58
#
59
# into `crontab -u ports -e', or run the script by hand if you can spare the time.
60
#
61
# If the environment variable BLAME is set and the ports tree is checked
62
# out by git, every entry is listed with a record of the last git commit.
63
#
64
65
use v5.20;
66
use strict;
67
use warnings;
68
69
use feature qw(signatures);
70
no warnings qw(experimental::signatures);
71
72
use Cwd 'abs_path';
73
use File::Find;
74
use List::Util qw(first);
75
use POSIX;
76
77
my $portsdir = $ENV{PORTSDIR} // '/usr/ports';
78
my $versiondir = $ENV{VERSIONDIR} // '/var/db/chkversion';
79
my $blame = exists $ENV{BLAME};
80
my $allports = exists $ENV{ALLPORTS};
81
82
my $watch_re = $ENV{WATCH_REGEX} // '';
83
my $watchm_re = $ENV{WATCHM_REGEX} // '';
84
my $returnpath = $ENV{RETURNPATH} // '';
85
my $h_from = $ENV{HEADER_FROM} // $ENV{USER} . '@' . ($ENV{HOST} // `/bin/hostname`);
86
my $h_replyto = $ENV{HEADER_REPLYTO} // $h_from;
87
my $rcpt_watch = $ENV{RCPT_WATCH} // '';
88
my $rcpt_watchm = $ENV{RCPT_WATCHM} // '';
89
my $rcpt_orig = $ENV{RCPT_ORIGIN} // '';
90
my $rcpt_vers = $ENV{RCPT_VERSION} // '';
91
my $cc_author = exists $ENV{CC_AUTHOR};
92
my $cc_mntnr = exists $ENV{CC_MAINTAINER};
93
94
my $make = '/usr/bin/make';
95
my $git = '/usr/local/bin/git';
96
my $sendmail = '/usr/sbin/sendmail';
97
my $pkg = first { -x $_ } ($ENV{PKG} // '', '/usr/local/sbin/pkg', '/usr/sbin/pkg');
98
99
$watch_re =~ s/ /|/g;
100
$watchm_re =~ s/ /|/g;
101
102
-d $portsdir or die "Can't find ports tree at $portsdir.\n";
103
$portsdir = abs_path($portsdir);
104
105
my $versionfile = "$versiondir/VERSIONS";
106
my $useindex = !-w $versiondir;
107
108
my $starttime = strftime "%a %b %e %G %k:%M:%S %Z", localtime;
109
110
# @output_lines = readfrom(dir, cmd, arg1, arg2, ...)
111
sub readfrom($dir, @cmd) {
112
my $CHILD;
113
if (!open $CHILD, '-|') {
114
open STDERR, '>', '/dev/null';
115
chdir $dir if $dir;
116
exec @cmd;
117
die;
118
}
119
my @childout = <$CHILD>;
120
close $CHILD;
121
122
map chomp, @childout;
123
124
return wantarray ? @childout : $childout[0];
125
}
126
127
for (qw(ARCH OPSYS OSREL OSVERSION UID)) {
128
my @cachedenv = readfrom($portsdir, $make, "-V$_");
129
$ENV{$_} = $cachedenv[0];
130
}
131
132
# These map a 2-dir path (editors/vim) to variables set in
133
# that port's Makefile
134
my %pkgname;
135
my %pkgorigin;
136
my %masterdir;
137
my %pkgmntnr;
138
139
sub wanted() {
140
return unless -d;
141
142
# Skip directories we shouldn't descend into
143
# if (/^.git$/
144
if (/^\.git$/
145
|| $File::Find::name =~ m"^$portsdir/(?:Mk|Templates|Tools|distfiles|packages)$"os
146
|| $File::Find::name =~ m"^$portsdir/[^/]+/pkg$"os)
147
{
148
$File::Find::prune = 1;
149
}
150
elsif ($File::Find::name =~ m"^$portsdir/([^/]+/[^/]+)$"os) {
151
$File::Find::prune = 1;
152
if (-f "$File::Find::name/Makefile") {
153
my @makevar = readfrom $File::Find::name, $make, qw(-VPKGORIGIN -VPKGNAME -VMAINTAINER -VMASTERDIR);
154
155
# $1 is the current 2-dir path
156
if ($#makevar == 3 && $makevar[1]) {
157
# %pkgorigin is the list of dirs that gets monitored. Only monitor a
158
# path if it matches the PKGORIGIN.
159
$pkgorigin{$1} = $makevar[0] if $1 ne $makevar[0];
160
$pkgname{$1} = $makevar[1];
161
$pkgmntnr{$1} = $makevar[2];
162
$masterdir{$1} = $makevar[3];
163
}
164
}
165
}
166
}
167
168
if ($allports) {
169
find(\&wanted, $portsdir);
170
}
171
else {
172
my @categories = split ' ' => readfrom($portsdir, $make, '-VSUBDIR');
173
174
for my $category (@categories) {
175
next unless -f "$portsdir/$category/Makefile";
176
my @ports = split ' ' => readfrom("$portsdir/$category", $make, '-VSUBDIR');
177
for (map "$category/$_", @ports) {
178
next unless -f "$portsdir/$_/Makefile";
179
180
my @makevar = readfrom "$portsdir/$_", $make, qw(-VPKGORIGIN -VPKGNAME -VMAINTAINER -VMASTERDIR);
181
182
next if $#makevar != 3 || ! $makevar[1];
183
$pkgorigin{$_} = $makevar[0] if $_ ne $makevar[0];
184
$pkgname{$_} = $makevar[1];
185
$pkgmntnr{$_} = $makevar[2];
186
$masterdir{$_} = $makevar[3];
187
}
188
}
189
}
190
191
my %backwards;
192
my %watched;
193
my %watchedm;
194
195
if ($useindex) {
196
my $indexname = readfrom $portsdir, $make, '-VINDEXFILE';
197
$versionfile = "$portsdir/$indexname";
198
}
199
200
# Read in the old (expected) values
201
open my $VERSIONS, '<', $versionfile;
202
while (<$VERSIONS>) {
203
chomp;
204
next if /^(#|$)/;
205
206
# These are the old (expected) values
207
my ($origin, $version, $maintainer);
208
209
if ($useindex) {
210
($origin, $version, $maintainer) = (split '|')[1,0,5];
211
# Only keep the 2-dir path (editors/vim)
212
$origin =~ s,^.*/([^/]+/[^/]+)/?$,$1,;
213
}
214
else {
215
($origin, $version, $maintainer) = split /\t/;
216
}
217
if (defined $pkgname{$origin}) {
218
my $newversion = $pkgname{$origin};
219
my $oldversion = $version;
220
221
$newversion =~ s/^.*-//;
222
$oldversion =~ s/^.*-//;
223
224
# If the two values differ, use `pkg version` to find which one is bigger
225
my $result = $newversion eq $oldversion ? '='
226
: readfrom '', $pkg, 'version', '-t', $newversion, $oldversion;
227
228
$watched{$origin} = "$version -> $pkgname{$origin}"
229
if ($watch_re && $result ne '=' && $origin =~ /^(?:$watch_re)$/o);
230
231
$watchedm{$origin} = "(was <$maintainer>) $version -> $pkgname{$origin}"
232
if ($watchm_re && $maintainer && $pkgmntnr{$origin}
233
&& $maintainer ne $pkgmntnr{$origin} && $origin =~ /^(?:$watchm_re)$/o);
234
235
if ($result eq '<') {
236
$backwards{$origin} = "$pkgname{$origin} < $version";
237
$pkgname{$origin} = $version;
238
}
239
}
240
elsif ($origin) {
241
$pkgname{$origin} = $version;
242
$pkgmntnr{$origin} = $maintainer;
243
}
244
}
245
close $VERSIONS;
246
247
if (!$useindex) {
248
rename $versionfile, "$versionfile.bak";
249
250
open my $VERSIONS, '>', $versionfile;
251
for (sort keys %pkgname) {
252
print $VERSIONS "$_\t$pkgname{$_}\t$pkgmntnr{$_}\n";
253
}
254
close $VERSIONS;
255
}
256
257
my %revision;
258
259
# Parses the $FreeBSD$ line to return revision, date, author
260
sub parsemakefile($portdir) {
261
open my $MAKEFILE, '<', "$portdir/Makefile";
262
while (<$MAKEFILE>) {
263
if (m/^# \$FreeBSD: [^ ]+ (?<rev>\d{6}) (?<date>\d{4}-\d\d-\d\d) [\d:]+Z (?<author>\w+) \$$/) {
264
close $MAKEFILE;
265
return ($+{rev}, $+{date}, $+{author});
266
}
267
}
268
close $MAKEFILE;
269
}
270
271
sub getauthors($ports) {
272
my %author;
273
for my $origin (keys %{$ports}) {
274
if (!$revision{$origin}) {
275
my ($r, $d, $a) = parsemakefile "$portsdir/$origin";
276
push @{$revision{$origin}}, $r;
277
push @{$author{$origin}}, $a;
278
279
if ($masterdir{$origin} ne "$portsdir/$origin") {
280
($r, $d, $a) = parsemakefile $masterdir{$origin};
281
push @{$revision{$origin}}, $r;
282
push @{$author{$origin}}, $a;
283
}
284
}
285
286
}
287
288
return %author;
289
}
290
291
# Gets the Makefile log starting from the last known rev for a port
292
sub printlog($fh, $portdir, $rev) {
293
if ($blame && -d "$portsdir/.git") {
294
my @log = readfrom $portdir, $git, 'log', "${rev}^..HEAD", 'Makefile';
295
print $fh " | $_\n" for @log;
296
}
297
}
298
299
# Git version:
300
# sub printlog($fh, $portdir, $rev) {
301
# }
302
303
sub blame($fh, $ports) {
304
if (%{$ports}) {
305
for my $origin (sort keys %{$ports}) {
306
print $fh "- *$origin* <$pkgmntnr{$origin}>: $ports->{$origin}\n";
307
printlog $fh, "$portsdir/$origin", $revision{$origin}[0];
308
if ($masterdir{$origin} ne "$portsdir/$origin") {
309
my $master = $masterdir{$origin};
310
$master =~ s|^$portsdir/||o;
311
while ($master =~ s!(^|/)[^/]+/\.\.(?:/|$)!$1!) {}
312
print $fh " (master: $master)\n";
313
printlog $fh, $masterdir{$origin}, $revision{$origin}[1];
314
}
315
print $fh "\n";
316
}
317
print $fh "\n";
318
}
319
}
320
321
sub template($from, $rcpt, $replyto, $starttime, $ports) {
322
my $portlist = join ', ' => sort keys %{$ports};
323
substr($portlist, 32) = '...'
324
if length $portlist > 35;
325
326
my %cclist;
327
my %author = getauthors $ports;
328
329
if ($cc_author) {
330
for (map @{$author{$_} ? $author{$_} : []}, keys %{$ports}) {
331
$cclist{"$_\@FreeBSD.org"} = 1
332
if $_;
333
}
334
}
335
if ($cc_mntnr) {
336
for (map $pkgmntnr{$_}, keys %{$ports}) {
337
$cclist{$_} = 1
338
if $_;
339
}
340
}
341
my $cc = join ', ' => sort keys %cclist;
342
343
my $header = '';
344
while (<main::DATA>) {
345
last if /^\.\n?$/;
346
$header .= $_
347
=~ s/%%FROM%%/$from/ogr
348
=~ s/%%RCPT%%/$rcpt/ogr
349
=~ s/%%CC%%/$cc/ogr
350
=~ s/%%REPLYTO%%/$replyto/ogr
351
=~ s/%%SUBJECT%%/$portlist/ogr
352
=~ s/%%STARTTIME%%/$starttime/ogr;
353
}
354
return $header;
355
}
356
357
sub mail($template, $rcpt, $ports) {
358
if (%{$ports}) {
359
# If the RCPT_* variables are empty, just print the mail to STDOUT
360
if ($rcpt) {
361
my $MAIL;
362
if (!open $MAIL, '|-') {
363
exec $sendmail, qw(-oi -t -f), $returnpath;
364
die;
365
}
366
print $MAIL $template;
367
blame $MAIL, $ports;
368
close $MAIL;
369
} else {
370
$template =~ s/^.*?\n\n//os;
371
print $template;
372
blame *STDOUT, $ports;
373
}
374
}
375
}
376
377
my $tmpl;
378
379
$tmpl = template $h_from, $rcpt_orig, $h_replyto, $starttime, \%pkgorigin;
380
mail $tmpl, $rcpt_orig, \%pkgorigin;
381
382
$tmpl = template $h_from, $rcpt_vers, $h_replyto, $starttime, \%backwards;
383
mail $tmpl, $rcpt_vers, \%backwards;
384
385
$tmpl = template $h_from, $rcpt_watch, $h_replyto, $starttime, \%watched;
386
mail $tmpl, $rcpt_watch, \%watched;
387
388
$tmpl = template $h_from, $rcpt_watch, $h_replyto, $starttime, \%watchedm;
389
mail $tmpl, $rcpt_watchm, \%watchedm;
390
391
exit((%pkgorigin || %backwards) ? 1 : 0);
392
393
__END__
394
From: %%FROM%%
395
To: %%RCPT%%
396
CC: %%CC%%
397
Reply-To: %%REPLYTO%%
398
Subject: Ports with a broken PKGORIGIN: %%SUBJECT%%
399
X-FreeBSD-Chkversion: PKGORIGIN
400
401
** The following ports have an incorrect PKGORIGIN **
402
403
PKGORIGIN connects packaged or installed ports to the directory they
404
originated from. This is essential for tools like pkg_version or
405
portupgrade to work correctly. Wrong PKGORIGINs are often caused by a
406
wrong order of CATEGORIES after a repocopy.
407
408
Please fix any errors as soon as possible.
409
410
The ports tree was updated at %%STARTTIME%%.
411
412
.
413
From: %%FROM%%
414
To: %%RCPT%%
415
CC: %%CC%%
416
Reply-To: %%REPLYTO%%
417
Subject: Ports with version numbers going backwards: %%SUBJECT%%
418
X-FreeBSD-Chkversion: backwards
419
420
** The following ports have a version number that sorts before a previous one **
421
422
For many package tools to work correctly, it is of utmost importance that
423
version numbers of a port form a monotonic increasing sequence over time.
424
Refer to the FreeBSD Porter's Handbook, 'Package Naming Conventions' for
425
more information. Tools that won't work include pkg_version, portupgrade
426
and portaudit. A common error is an accidental deletion of PORTEPOCH.
427
428
Please fix any errors as soon as possible.
429
430
The ports tree was updated at %%STARTTIME%%.
431
432
.
433
From: %%FROM%%
434
To: %%RCPT%%
435
Reply-To: %%REPLYTO%%
436
Subject: Version changes in your watched ports: %%SUBJECT%%
437
X-FreeBSD-Chkversion: vwatch
438
439
** The following ports have changed version numbers **
440
441
You have requested to be notified of version changes in the following
442
ports:
443
444
The ports tree was updated at %%STARTTIME%%.
445
446
.
447
From: %%FROM%%
448
To: %%RCPT%%
449
Reply-To: %%REPLYTO%%
450
Subject: Maintainer changes in your watched ports: %%SUBJECT%%
451
X-FreeBSD-Chkversion: mwatch
452
453
** The following ports have changed maintainers **
454
455
You have requested to be notified of maintainer changes in the following
456
ports:
457
458
The ports tree was updated at %%STARTTIME%%.
459
460
.
461
462