user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
* [PATCH 00/14] Alpine Linux support
@ 2023-12-13  0:50 Eric Wong
  2023-12-13  0:50 ` [PATCH 01/14] t/io: strace is optional on Linux Eric Wong
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

I haven't tested every single possible package combination, but
I think this is enough to get started.

Patch 3 was a WTF moment for me.

Some of these are relevant for other platforms, as well, and
patch 7 decoupling from Inline::C is a good step towards making
our codebase more modular.

Patch 12 is a good fix regardless.

Eric Wong (14):
  t/io: strace is optional on Linux
  tests: account for missing git-http-backend
  t/cindex*: skip --join when join(1) is missing
  tests: attempt compatibility w/ busybox lsof
  lei inspect: drop unneeded strftime import
  treewide: avoid strftime %k for portability
  xap_helper_cxx: decouple from Inline::C
  xap_helper_cxx: support clang w/o `c++' executable
  install: updates for Alpine Linux and apk
  test_common: extract oct_is from search.t
  t/convert-compact: allow S_ISGID bit
  www_coderepo: fix read buffering
  gzip_filter: use OO ->zflush dispatch
  t/lei-import: relax EIO regexp

 install/deps.perl               | 38 +++++++++++++++++++++----
 install/os.perl                 | 12 ++++++--
 lib/PublicInbox/Admin.pm        | 10 ++++++-
 lib/PublicInbox/ExtSearchIdx.pm |  4 +--
 lib/PublicInbox/GzipFilter.pm   |  2 +-
 lib/PublicInbox/Hval.pm         |  6 +++-
 lib/PublicInbox/LeiInspect.pm   |  1 -
 lib/PublicInbox/LeiMirror.pm    |  4 +--
 lib/PublicInbox/LeiXSearch.pm   |  3 +-
 lib/PublicInbox/RepoAtom.pm     |  4 +--
 lib/PublicInbox/TestCommon.pm   | 50 +++++++++++++++++++++++++++++++--
 lib/PublicInbox/WwwCoderepo.pm  |  6 ++--
 lib/PublicInbox/WwwStatic.pm    |  5 ++--
 lib/PublicInbox/XapHelperCxx.pm | 26 +++++++++++------
 t/cindex-join.t                 |  1 +
 t/cindex.t                      | 11 ++++----
 t/clone-coderepo.t              |  1 +
 t/convert-compact.t             | 20 ++++++-------
 t/ds-leak.t                     |  5 +---
 t/httpd-corner.t                | 31 ++++++++------------
 t/httpd.t                       |  1 +
 t/lei-import.t                  |  5 ++--
 t/lei-mirror.t                  |  1 +
 t/nntpd.t                       | 16 +++++------
 t/search.t                      |  5 ----
 t/solver_git.t                  |  8 ++++--
 t/v2reindex.t                   |  5 ++--
 t/www_listing.t                 |  1 +
 t/xap_helper.t                  |  6 ++--
 xt/msgtime_cmp.t                |  2 +-
 30 files changed, 190 insertions(+), 100 deletions(-)

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH 01/14] t/io: strace is optional on Linux
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 02/14] tests: account for missing git-http-backend Eric Wong
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

There are many Linux (GNU or otherwise) which do not have
strace(1) installed.
---
 lib/PublicInbox/TestCommon.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/PublicInbox/TestCommon.pm b/lib/PublicInbox/TestCommon.pm
index 5f123eb4..d587bf10 100644
--- a/lib/PublicInbox/TestCommon.pm
+++ b/lib/PublicInbox/TestCommon.pm
@@ -978,7 +978,7 @@ sub strace (@) {
 		chomp $ps;
 		skip "strace unusable on daemons\n$f is `$ps' (!= 0)" if $ps;
 	}
-	require_cmd('strace', 1);
+	require_cmd('strace', 1) or skip 'strace not available', 1;
 }
 
 sub strace_inject (;$) {

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 02/14] tests: account for missing git-http-backend
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
  2023-12-13  0:50 ` [PATCH 01/14] t/io: strace is optional on Linux Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 03/14] t/cindex*: skip --join when join(1) is missing Eric Wong
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

Alpine Linux ships git-http-backend in the `git-daemon'
package separately from `git', so we must test for its
existence before attempting to test functionality which
depends on it.
---
 lib/PublicInbox/TestCommon.pm | 19 ++++++++++++++++++-
 t/clone-coderepo.t            |  1 +
 t/httpd.t                     |  1 +
 t/lei-mirror.t                |  1 +
 t/www_listing.t               |  1 +
 5 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/lib/PublicInbox/TestCommon.pm b/lib/PublicInbox/TestCommon.pm
index d587bf10..27a758e4 100644
--- a/lib/PublicInbox/TestCommon.pm
+++ b/lib/PublicInbox/TestCommon.pm
@@ -25,7 +25,7 @@ BEGIN {
 		run_script start_script key2sub xsys xsys_e xqx eml_load tick
 		have_xapian_compact json_utf8 setup_public_inboxes create_inbox
 		create_coderepo require_bsd kernel_version check_broken_tmpfs
-		quit_waiter_pipe wait_for_eof
+		quit_waiter_pipe wait_for_eof require_git_http_backend
 		tcp_host_port test_lei lei lei_ok $lei_out $lei_err $lei_opt
 		test_httpd xbail require_cmd is_xdeeply tail_f
 		ignore_inline_c_missing no_pollerfd no_coredump cfg_new
@@ -163,6 +163,23 @@ sub require_git ($;$) {
 		skip("git $req+ required (have $cur_ver), skipping $nr tests")
 }
 
+sub require_git_http_backend (;$) {
+	my ($nr) = @_;
+	state $ok = do {
+		require PublicInbox::Git;
+		my $git = PublicInbox::Git::check_git_exe() or plan
+			skip_all => 'nothing in public-inbox works w/o git';
+		my $rdr = { 1 => \my $out, 2 => \my $err };
+		xsys([$git, qw(http-backend)], undef, $rdr);
+		$out =~ /^Status:/ism;
+	};
+	if (!$ok) {
+		my $msg = "`git http-backend' not available";
+		defined($nr) ? skip $msg, $nr : plan skip_all => $msg;
+	}
+	$ok;
+}
+
 my %IPv6_VERSION = (
 	'Net::NNTP' => 3.00,
 	'Mail::IMAPClient' => 3.40,
diff --git a/t/clone-coderepo.t b/t/clone-coderepo.t
index 0e6b4ac7..c0951941 100644
--- a/t/clone-coderepo.t
+++ b/t/clone-coderepo.t
@@ -8,6 +8,7 @@ use File::Temp;
 use File::Path qw(remove_tree);
 use PublicInbox::SHA qw(sha1_hex);
 require_mods(qw(json Plack::Builder HTTP::Date HTTP::Status));
+require_git_http_backend;
 require_git '1.8.5';
 require_cmd 'curl';
 require_ok 'PublicInbox::LeiMirror';
diff --git a/t/httpd.t b/t/httpd.t
index 0421c7ea..c0fbaa22 100644
--- a/t/httpd.t
+++ b/t/httpd.t
@@ -7,6 +7,7 @@ use PublicInbox::TestCommon;
 use PublicInbox::Eml;
 use Socket qw(IPPROTO_TCP SOL_SOCKET);
 require_mods(qw(Plack::Util Plack::Builder HTTP::Date HTTP::Status));
+require_git_http_backend;
 
 # FIXME: too much setup
 my ($tmpdir, $for_destroy) = tmpdir();
diff --git a/t/lei-mirror.t b/t/lei-mirror.t
index 37c9751b..76041b73 100644
--- a/t/lei-mirror.t
+++ b/t/lei-mirror.t
@@ -5,6 +5,7 @@ use v5.12; use PublicInbox::TestCommon;
 use PublicInbox::Inbox;
 require_mods(qw(-httpd lei DBD::SQLite));
 require_cmd('curl');
+require_git_http_backend;
 use PublicInbox::Spawn qw(which);
 require PublicInbox::Msgmap;
 my $sock = tcp_server();
diff --git a/t/www_listing.t b/t/www_listing.t
index 709dbd05..0a4c79e8 100644
--- a/t/www_listing.t
+++ b/t/www_listing.t
@@ -77,6 +77,7 @@ sub tiny_test {
 
 my $td;
 SKIP: {
+	require_git_http_backend 1;
 	my $err = "$tmpdir/stderr.log";
 	my $out = "$tmpdir/stdout.log";
 	my $alt = "$tmpdir/alt.git";

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 03/14] t/cindex*: skip --join when join(1) is missing
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
  2023-12-13  0:50 ` [PATCH 01/14] t/io: strace is optional on Linux Eric Wong
  2023-12-13  0:50 ` [PATCH 02/14] tests: account for missing git-http-backend Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 04/14] tests: attempt compatibility w/ busybox lsof Eric Wong
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

While join(1) is POSIX, busybox on Alpine 3.19.0 does not
provide its functionality.  So just skip tests for now since
it's too much trouble to provide a workaround for an otherwise
common POSIX command.
---
 t/cindex-join.t |  1 +
 t/cindex.t      | 11 ++++++-----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/t/cindex-join.t b/t/cindex-join.t
index c2e85332..22c67107 100644
--- a/t/cindex-join.t
+++ b/t/cindex-join.t
@@ -11,6 +11,7 @@ use PublicInbox::Config;
 use autodie;
 use File::Spec;
 $ENV{TEST_REMOTE_JOIN} or plan skip_all => 'TEST_REMOTE_JOIN unset';
+require_cmd 'join';
 local $ENV{TAIL_ALL} = $ENV{TAIL_ALL} // 1; # while features are unstable
 require_mods(qw(json Xapian DBD::SQLite +SCM_RIGHTS));
 my @code = qw(https://80x24.org/mwrap-perl.git
diff --git a/t/cindex.t b/t/cindex.t
index 15c860e1..ab4cde7c 100644
--- a/t/cindex.t
+++ b/t/cindex.t
@@ -247,11 +247,12 @@ SKIP: {
 	ok(run_script([qw(-xcpdb --compact), "$tmp/ext"]), 'xcpdb compact');
 };
 
-my $basic = create_inbox 'basic', indexlevel => 'basic', sub {
-	my ($im, $ibx) = @_;
-	$im->add(eml_load('t/plack-qp.eml'));
-};
-{
+SKIP: {
+	require_cmd('join', 1);
+	my $basic = create_inbox 'basic', indexlevel => 'basic', sub {
+		my ($im, $ibx) = @_;
+		$im->add(eml_load('t/plack-qp.eml'));
+	};
 	my $env = { PI_CONFIG => "$tmp/pi_config" };
 	PublicInbox::IO::write_file '>', $env->{PI_CONFIG}, <<EOM;
 [publicinbox "basictest"]

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 04/14] tests: attempt compatibility w/ busybox lsof
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (2 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 03/14] t/cindex*: skip --join when join(1) is missing Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 05/14] lei inspect: drop unneeded strftime import Eric Wong
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

BusyBox lsof(1) ignores the `-p PID' argument and shows
the open files for every process it knows about.  BusyBox
lsof also lacks the `NODE' column of the non-BusyBox
implementation, so we'll rely on /proc/PID/fd/ in those
cases since the deleted file checks are Linux-only and
it's common to have procfs is mounted on /proc on Linux.
---
 lib/PublicInbox/TestCommon.pm | 23 ++++++++++++++++++++++-
 t/ds-leak.t                   |  5 +----
 t/httpd-corner.t              | 31 +++++++++++--------------------
 t/nntpd.t                     | 16 +++++++---------
 t/v2reindex.t                 |  5 ++---
 5 files changed, 43 insertions(+), 37 deletions(-)

diff --git a/lib/PublicInbox/TestCommon.pm b/lib/PublicInbox/TestCommon.pm
index 27a758e4..9c413f43 100644
--- a/lib/PublicInbox/TestCommon.pm
+++ b/lib/PublicInbox/TestCommon.pm
@@ -29,7 +29,7 @@ BEGIN {
 		tcp_host_port test_lei lei lei_ok $lei_out $lei_err $lei_opt
 		test_httpd xbail require_cmd is_xdeeply tail_f
 		ignore_inline_c_missing no_pollerfd no_coredump cfg_new
-		strace strace_inject);
+		strace strace_inject lsof_pid);
 	require Test::More;
 	my @methods = grep(!/\W/, @Test::More::EXPORT);
 	eval(join('', map { "*$_=\\&Test::More::$_;" } @methods));
@@ -951,6 +951,26 @@ sub test_httpd ($$;$$) {
 	}
 };
 
+# TODO: support fstat(1) on OpenBSD, lsof already works on FreeBSD + Linux
+# don't use this for deleted file checks, we only check that on Linux atm
+# and we can readlink /proc/PID/fd/* directly
+sub lsof_pid ($;$) {
+	my ($pid, $rdr) = @_;
+	state $lsof = require_cmd('lsof', 1);
+	$lsof or skip 'lsof missing/broken', 1;
+	my @out = xqx([$lsof, '-p', $pid], undef, $rdr);
+	if ($?) {
+		undef $lsof;
+		skip "lsof -p PID broken \$?=$?", 1;
+	}
+	my @cols = split ' ', $out[0];
+	if (($cols[7] // '') eq 'NODE') { # normal lsof
+		@out;
+	} else { # busybox lsof ignores -p, so we DIY it
+		grep /\b$pid\b/, @out;
+	}
+}
+
 sub no_pollerfd ($) {
 	my ($pid) = @_;
 	my ($re, @cmd);
@@ -966,6 +986,7 @@ sub no_pollerfd ($) {
 		my @of = xqx(\@cmd, {}, {2 => \(my $e)});
 		my $err = $?;
 		skip "$bin broken? (\$?=$err) ($e)", 1 if $err;
+		@of = grep /\b$pid\b/, @of; # busybox lsof ignores -p
 		is(grep(/$re/, @of), 0, "no $re FDs") or diag explain(\@of);
 	}
 }
diff --git a/t/ds-leak.t b/t/ds-leak.t
index 179997eb..f39985e0 100644
--- a/t/ds-leak.t
+++ b/t/ds-leak.t
@@ -29,11 +29,8 @@ if ('close-on-exec for epoll and kqueue') {
 	is($l, undef, 'cloexec works and sleep(1) is running');
 
 	SKIP: {
-		my $lsof = require_cmd('lsof', 1) or skip 'lsof missing', 1;
 		my $rdr = { 2 => \(my $null) };
-		my @of = grep(/$evfd_re/, xqx([$lsof, '-p', $pid], {}, $rdr));
-		my $err = $?;
-		skip "lsof broken ? (\$?=$err)", 1 if $err;
+		my @of = grep /$evfd_re/, lsof_pid $pid, $rdr;
 		is_deeply(\@of, [], 'no FDs leaked to subprocess');
 	};
 	if (defined $pid) {
diff --git a/t/httpd-corner.t b/t/httpd-corner.t
index da1c24b9..35c88600 100644
--- a/t/httpd-corner.t
+++ b/t/httpd-corner.t
@@ -639,30 +639,21 @@ SKIP: {
 };
 
 SKIP: {
-	skip 'only testing lsof(8) output on Linux', 1 if $^O ne 'linux';
-	my $lsof = require_cmd('lsof', 1) or skip 'no lsof in PATH', 1;
-	my $null_in = '';
-	my $rdr = { 2 => \(my $null_err), 0 => \$null_in };
-	my @lsof = xqx([$lsof, '-p', $td->{pid}], undef, $rdr);
-	my $d = [ grep(/\(deleted\)/, @lsof) ];
-	is_deeply($d, [], 'no lingering deleted inputs') or diag explain($d);
+	skip 'only testing /proc/PID/fd on Linux', 1 if $^O ne 'linux';
+	my $fd_dir = "/proc/$td->{pid}/fd";
+	-d $fd_dir or skip '/proc/$PID/fd missing', 1;
+	my @child = grep defined, map readlink, glob "$fd_dir/*";
+	my @d = grep /\(deleted\)/, @child;
+	is_deeply(\@d, [], 'no lingering deleted inputs') or diag explain(\@d);
 
 	# filter out pipes inherited from the parent
-	my @this = xqx([$lsof, '-p', $$], undef, $rdr);
-	my $bad;
-	my $extract_inodes = sub {
-		map {;
-			my @f = split(' ', $_);
-			my $inode = $f[-2];
-			$bad = $_ if $inode !~ /\A[0-9]+\z/;
-			$inode => 1;
-		} grep (/\bpipe\b/, @_);
-	};
-	my %child = $extract_inodes->(@lsof);
+	my @this = grep defined, map readlink, glob "/proc/$$/fd/*";
+	my $extract_inodes = sub { map { $_ => 1 } grep /\bpipe\b/, @_ };
+	my %child = $extract_inodes->(@child);
 	my %parent = $extract_inodes->(@this);
-	skip("inode not in expected format: $bad", 1) if defined($bad);
 	delete @child{(keys %parent)};
-	is_deeply([], [keys %child], 'no extra pipes with -W0');
+	is_deeply([], [keys %child], 'no extra pipes with -W0') or
+		diag explain([child => \%child, parent => \%parent]);
 };
 
 # ensure compatibility with other PSGI servers
diff --git a/t/nntpd.t b/t/nntpd.t
index ffe0fd8c..0f3ef596 100644
--- a/t/nntpd.t
+++ b/t/nntpd.t
@@ -14,7 +14,6 @@ use PublicInbox::DS;
 my $version = $ENV{PI_TEST_VERSION} || 1;
 require_git('2.6') if $version == 2;
 use_ok 'PublicInbox::Msgmap';
-my $lsof = require_cmd('lsof', 1);
 my $fast_idle = eval { require Linux::Inotify2; 1 } //
 		eval { require IO::KQueue; 1 };
 
@@ -332,9 +331,7 @@ Date: Fri, 02 Oct 1993 00:00:00 +0000
 				($ENV{TEST_RUN_MODE} // 2)) {
 			skip 'Xapian.pm pre-loaded (by xt/check-run.t?)', 1;
 		}
-		$lsof or skip 'lsof missing', 1;
-		my @of = xqx([$lsof, '-p', $td->{pid}], undef, $noerr);
-		skip('lsof broken', 1) if (!scalar(@of) || $?);
+		my @of = lsof_pid $td->{pid}, $noerr;
 		my @xap = grep m!\bXapian\b!, @of;
 		is_deeply(\@xap, [], 'Xapian not loaded in nntpd') or
 			diag explain(\@of);
@@ -364,12 +361,13 @@ Date: Fri, 02 Oct 1993 00:00:00 +0000
 		tick($fast_idle ? 0.1 : 2.1);
 		$art = $n->article($ex->header('Message-ID'));
 		ok($art, 'new article retrieved after compact');
-		$lsof or skip 'lsof missing', 1;
-		($^O =~ /\A(?:linux)\z/) or
+		$^O eq 'linux' or
 			skip "lsof /(deleted)/ check untested on $^O", 1;
-		my @lsof = xqx([$lsof, '-p', $td->{pid}], undef, $noerr);
-		my $d = [ grep(/\(deleted\)/, grep(!/batch-command\.err/, @lsof)) ];
-		is_deeply($d, [], 'no deleted files') or diag explain($d);
+		my $fd = "/proc/$td->{pid}/fd";
+		-d $fd or skip '/proc/PID/fd missing', 1;
+		my @of = map readlink, glob "$fd/*";
+		my @d = grep /\(deleted\)/, grep !/batch-command\.err/, @of;
+		is_deeply(\@d, [], 'no deleted files') or diag explain(\@d);
 	};
 	SKIP: { test_watch($tmpdir, $host_port, $group) };
 	{
diff --git a/t/v2reindex.t b/t/v2reindex.t
index 406c0517..8c49e154 100644
--- a/t/v2reindex.t
+++ b/t/v2reindex.t
@@ -1,4 +1,4 @@
-# Copyright (C) 2018-2021 all contributors <meta@public-inbox.org>
+# Copyright (C) all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 use strict; use v5.10.1; use PublicInbox::TestCommon;
 use PublicInbox::Eml;
@@ -549,9 +549,8 @@ is($err, '', 'no errors from --xapian-only');
 undef $for_destroy;
 SKIP: {
 	skip 'only testing lsof(8) output on Linux', 1 if $^O ne 'linux';
-	my $lsof = require_cmd('lsof', 1) or skip 'no lsof in PATH', 1;
 	my $rdr = { 2 => \(my $null_err) };
-	my @d = grep(m!/xap[0-9]+/!, xqx([$lsof, '-p', $$], undef, $rdr));
+	my @d = grep m!/xap[0-9]+/!, lsof_pid $$, $rdr;
 	is_deeply(\@d, [], 'no deleted index files') or diag explain(\@d);
 }
 done_testing();

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 05/14] lei inspect: drop unneeded strftime import
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (3 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 04/14] tests: attempt compatibility w/ busybox lsof Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 06/14] treewide: avoid strftime %k for portability Eric Wong
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

`lei inspect' uses the `iso8601' sub from LeiOverview.
---
 lib/PublicInbox/LeiInspect.pm | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lib/PublicInbox/LeiInspect.pm b/lib/PublicInbox/LeiInspect.pm
index 88d7949c..576ab2c7 100644
--- a/lib/PublicInbox/LeiInspect.pm
+++ b/lib/PublicInbox/LeiInspect.pm
@@ -12,7 +12,6 @@ use parent qw(PublicInbox::IPC);
 use PublicInbox::Config;
 use PublicInbox::MID qw(mids);
 use PublicInbox::NetReader qw(imap_uri nntp_uri);
-use POSIX qw(strftime);
 use PublicInbox::LeiOverview;
 *iso8601 = \&PublicInbox::LeiOverview::iso8601;
 

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 06/14] treewide: avoid strftime %k for portability
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (4 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 05/14] lei inspect: drop unneeded strftime import Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 07/14] xap_helper_cxx: decouple from Inline::C Eric Wong
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

The musl strftime(3) implementation on AlpineLinux 3.19.0
doesn't support `%k' and `%k' isn't in POSIX, either.  So we
fall back to using the `sprintf' perlop in the user-facing UI
since leading zeroes require needless overhead for my eyes and
brain to parse in the time.
---
 lib/PublicInbox/Admin.pm        | 10 +++++++++-
 lib/PublicInbox/ExtSearchIdx.pm |  4 ++--
 lib/PublicInbox/Hval.pm         |  6 +++++-
 lib/PublicInbox/LeiMirror.pm    |  4 ++--
 lib/PublicInbox/LeiXSearch.pm   |  3 ++-
 lib/PublicInbox/WwwStatic.pm    |  5 ++---
 xt/msgtime_cmp.t                |  2 +-
 7 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/lib/PublicInbox/Admin.pm b/lib/PublicInbox/Admin.pm
index cc9d2171..a1b1fc07 100644
--- a/lib/PublicInbox/Admin.pm
+++ b/lib/PublicInbox/Admin.pm
@@ -6,7 +6,7 @@
 package PublicInbox::Admin;
 use v5.12;
 use parent qw(Exporter);
-our @EXPORT_OK = qw(setup_signals);
+our @EXPORT_OK = qw(setup_signals fmt_localtime);
 use PublicInbox::Config;
 use PublicInbox::Inbox;
 use PublicInbox::Spawn qw(run_qx);
@@ -381,4 +381,12 @@ sub do_chdir ($) {
 	}
 }
 
+sub fmt_localtime ($) {
+	require POSIX;
+	my @lt = localtime $_[0];
+	my (undef, $M, $H, $d, $m, $Y) = @lt;
+	sprintf('%u-%02u-%02u % 2u:%02u ', $Y + 1900, $m + 1, $d, $H, $M)
+		.POSIX::strftime('%z', @lt);
+}
+
 1;
diff --git a/lib/PublicInbox/ExtSearchIdx.pm b/lib/PublicInbox/ExtSearchIdx.pm
index 7b7436ea..53078124 100644
--- a/lib/PublicInbox/ExtSearchIdx.pm
+++ b/lib/PublicInbox/ExtSearchIdx.pm
@@ -20,7 +20,6 @@ use parent qw(PublicInbox::ExtSearch PublicInbox::Umask PublicInbox::Lock);
 use Carp qw(croak carp);
 use Scalar::Util qw(blessed);
 use Sys::Hostname qw(hostname);
-use POSIX qw(strftime);
 use File::Glob qw(bsd_glob GLOB_NOSORT);
 use PublicInbox::MultiGit;
 use PublicInbox::Search;
@@ -34,6 +33,7 @@ use PublicInbox::ContentHash qw(content_hash);
 use PublicInbox::Eml;
 use PublicInbox::DS qw(now add_timer);
 use DBI qw(:sql_types); # SQL_BLOB
+use PublicInbox::Admin qw(fmt_localtime);
 
 sub new {
 	my (undef, $dir, $opt) = @_;
@@ -749,7 +749,7 @@ sub eidxq_lock_acquire ($) {
 		return $locked if $locked eq $cur;
 	}
 	my ($pid, $time, $euid, $ident) = split(/-/, $cur, 4);
-	my $t = strftime('%Y-%m-%d %k:%M %z', localtime($time));
+	my $t = fmt_localtime($time);
 	local $self->{current_info} = 'eidxq';
 	if ($euid == $> && $ident eq host_ident) {
 		kill(0, $pid) and warn <<EOM and return;
diff --git a/lib/PublicInbox/Hval.pm b/lib/PublicInbox/Hval.pm
index b804254a..963dbb71 100644
--- a/lib/PublicInbox/Hval.pm
+++ b/lib/PublicInbox/Hval.pm
@@ -145,7 +145,11 @@ sub to_attr ($) {
 sub ts2str ($) { strftime('%Y%m%d%H%M%S', gmtime($_[0])) };
 
 # human-friendly format
-sub fmt_ts ($) { strftime('%Y-%m-%d %k:%M', gmtime($_[0])) }
+sub fmt_ts ($) {
+	# strftime %k is not portable and leading zeros in %H slow me down
+	my (undef, $M, $H, $d, $m, $Y) = gmtime $_[0];
+	sprintf '%u-%02u-%02u % 2u:%02u', $Y + 1900, $m + 1, $d, $H, $M;
+}
 
 sub utf8_maybe ($) {
 	utf8::decode($_[0]);
diff --git a/lib/PublicInbox/LeiMirror.pm b/lib/PublicInbox/LeiMirror.pm
index e048a807..0c77a8b5 100644
--- a/lib/PublicInbox/LeiMirror.pm
+++ b/lib/PublicInbox/LeiMirror.pm
@@ -21,6 +21,7 @@ use PublicInbox::LeiCurl;
 use PublicInbox::OnDestroy;
 use PublicInbox::SHA qw(sha256_hex sha_all);
 use POSIX qw(strftime);
+use PublicInbox::Admin qw(fmt_localtime);
 use autodie qw(chdir chmod close open pipe readlink
 		seek symlink sysopen sysseek truncate unlink);
 
@@ -1232,8 +1233,7 @@ EOM
 	# set by clone_v2_prep/-I/--exclude
 	my $mis = delete $self->{chg}->{fp_mismatch};
 	if ($mis) {
-		my $t = (stat($ft))[9];
-		$t = strftime('%F %k:%M:%S %z', localtime($t));
+		my $t = fmt_localtime((stat($ft))[9]);
 		warn <<EOM;
 W: Fingerprints for the following repositories do not match
 W: $mf_url @ $t:
diff --git a/lib/PublicInbox/LeiXSearch.pm b/lib/PublicInbox/LeiXSearch.pm
index cee3ad07..fc95d401 100644
--- a/lib/PublicInbox/LeiXSearch.pm
+++ b/lib/PublicInbox/LeiXSearch.pm
@@ -298,8 +298,9 @@ sub fudge_qstr_time ($$$) {
 		$rft = $diff;
 	}
 	$lr -= ($rft || (48 * 60 * 60));
+	require PublicInbox::Admin;
 	$lei->qerr("# $uri limiting to ".
-		strftime('%Y-%m-%d %k:%M %z', localtime($lr)). ' and newer');
+		PublicInbox::Admin::fmt_localtime($lr).' and newer');
 	# this should really be rt: (received-time), but no stable
 	# public-inbox releases support it, yet.
 	my $dt = 'dt:'.strftime('%Y%m%d%H%M%S', gmtime($lr)).'..';
diff --git a/lib/PublicInbox/WwwStatic.pm b/lib/PublicInbox/WwwStatic.pm
index 1c1a3d38..d8902193 100644
--- a/lib/PublicInbox/WwwStatic.pm
+++ b/lib/PublicInbox/WwwStatic.pm
@@ -12,13 +12,12 @@ use strict;
 use v5.10.1;
 use parent qw(Exporter);
 use Fcntl qw(SEEK_SET O_RDONLY O_NONBLOCK);
-use POSIX qw(strftime);
 use HTTP::Date qw(time2str);
 use HTTP::Status qw(status_message);
 use Errno qw(EACCES ENOTDIR ENOENT);
 use URI::Escape qw(uri_escape_utf8);
 use PublicInbox::GzipFilter qw(gzf_maybe);
-use PublicInbox::Hval qw(ascii_html);
+use PublicInbox::Hval qw(ascii_html fmt_ts);
 use Plack::MIME;
 our @EXPORT_OK = qw(@NO_CACHE r path_info_raw);
 
@@ -299,7 +298,7 @@ sub dir_response ($$$) {
 		$pad = 1 if $pad <= 0;
 		$entry = qq(\n<a\nhref="$href">$name</a>) .
 				(' ' x $pad) .
-				strftime('%Y-%m-%d %k:%M', gmtime($mtime)) .
+				fmt_ts($mtime) .
 				sprintf('% 8s', $hsize);
 	}
 
diff --git a/xt/msgtime_cmp.t b/xt/msgtime_cmp.t
index a7ef5245..c63f785e 100644
--- a/xt/msgtime_cmp.t
+++ b/xt/msgtime_cmp.t
@@ -36,7 +36,7 @@ sub quiet_is_deeply ($$$$$) {
 			($old->[0] != $cur->[0]) ||
 			($old->[1] != $cur->[1]))) {
 		for ($cur, $old) {
-			$_->[2] = strftime('%Y-%m-%d %k:%M:%S', gmtime($_->[0]))
+			$_->[2] = strftime('%F %T', gmtime($_->[0]))
 		}
 		is_deeply($cur, $old, "$func $oid");
 		diag('got: ', explain($cur));

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 07/14] xap_helper_cxx: decouple from Inline::C
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (5 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 06/14] treewide: avoid strftime %k for portability Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 08/14] xap_helper_cxx: support clang w/o `c++' executable Eric Wong
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

We don't actually need Inline::C support to build a standalone
executable implemented in C++.
---
 lib/PublicInbox/XapHelperCxx.pm | 11 ++++++-----
 t/xap_helper.t                  |  6 ++++--
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/lib/PublicInbox/XapHelperCxx.pm b/lib/PublicInbox/XapHelperCxx.pm
index 17f988ee..8e95ff42 100644
--- a/lib/PublicInbox/XapHelperCxx.pm
+++ b/lib/PublicInbox/XapHelperCxx.pm
@@ -16,8 +16,9 @@ use autodie;
 my $cxx = which($ENV{CXX} // 'c++');
 my $dir = substr("$cxx-$Config{archname}", 1); # drop leading '/'
 $dir =~ tr!/!-!;
-$ENV{PERL_INLINE_DIRECTORY} // die('BUG: PERL_INLINE_DIRECTORY unset');
-substr($dir, 0, 0) = "$ENV{PERL_INLINE_DIRECTORY}/";
+my $idir = ($ENV{XDG_CACHE_HOME} //
+	(($ENV{HOME} // die('HOME unset')).'/.cache')).'/public-inbox/jaot';
+substr($dir, 0, 0) = "$idir/";
 my $bin = "$dir/xap_helper";
 my ($srcpfx) = (__FILE__ =~ m!\A(.+/)[^/]+\z!);
 my @srcs = map { $srcpfx.$_ } qw(xh_mset.h xh_cidx.h xap_helper.h);
@@ -54,9 +55,9 @@ sub needs_rebuild () {
 }
 
 sub build () {
-	if (!-d $dir && !CORE::mkdir($dir)) {
-		my $err = $!;
-		die "mkdir($dir): $err" if !-d $dir;
+	if (!-d $dir) {
+		require File::Path;
+		File::Path::make_path($dir);
 	}
 	require PublicInbox::CodeSearch;
 	require PublicInbox::Lock;
diff --git a/t/xap_helper.t b/t/xap_helper.t
index be010c75..0f474608 100644
--- a/t/xap_helper.t
+++ b/t/xap_helper.t
@@ -149,8 +149,10 @@ unless ($ENV{TEST_XH_CXX_ONLY}) {
 	no_pollerfd($ar->{pid});
 }
 SKIP: {
-	require PublicInbox::XapHelperCxx;
-	my $cmd = eval { PublicInbox::XapHelperCxx::cmd() };
+	my $cmd = eval {
+		require PublicInbox::XapHelperCxx;
+		PublicInbox::XapHelperCxx::cmd();
+	};
 	skip "XapHelperCxx build: $@", 1 if $@;
 
 	@NO_CXX = $ENV{TEST_XH_CXX_ONLY} ? (0) : (0, 1);

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 08/14] xap_helper_cxx: support clang w/o `c++' executable
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (6 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 07/14] xap_helper_cxx: decouple from Inline::C Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 09/14] install: updates for Alpine Linux and apk Eric Wong
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

This makes the C++ build work on Alpine Linux (tested 3.19.0)
without having to install g++ to get the `c++' executable.
I've tested this change with and without g++ on Alpine so it'll
continue to work if a user decides to install g++.

This should continue to work if the Xapian package on Alpine is
changed to link against libc++ instead of libstdc++, since we
only add `-lstdc++' as a fallback.  For reference, Xapian is
already linked against libc++ and not libstdc++ on FreeBSD 13.x
---
 lib/PublicInbox/XapHelperCxx.pm | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/lib/PublicInbox/XapHelperCxx.pm b/lib/PublicInbox/XapHelperCxx.pm
index 8e95ff42..6bd165b8 100644
--- a/lib/PublicInbox/XapHelperCxx.pm
+++ b/lib/PublicInbox/XapHelperCxx.pm
@@ -7,13 +7,13 @@
 # The resulting executable is not linked to Perl in any way.
 package PublicInbox::XapHelperCxx;
 use v5.12;
-use PublicInbox::Spawn qw(run_die run_qx which);
+use PublicInbox::Spawn qw(run_die run_qx run_wait which);
 use PublicInbox::IO qw(try_cat write_file);
 use PublicInbox::Search;
 use Fcntl qw(SEEK_SET);
 use Config;
 use autodie;
-my $cxx = which($ENV{CXX} // 'c++');
+my $cxx = which($ENV{CXX} // 'c++') // which('clang') // die 'no C++ compiler';
 my $dir = substr("$cxx-$Config{archname}", 1); # drop leading '/'
 $dir =~ tr!/!-!;
 my $idir = ($ENV{XDG_CACHE_HOME} //
@@ -81,7 +81,16 @@ sub build () {
 	my @xflags = split(' ', "$fl $xflags"); # ' ' awk-mode eats leading WS
 	my @cflags = ('-I', $srcpfx, grep(!/\A-(?:Wl|l|L)/, @xflags));
 	run_die([$cxx, '-o', "$dir/$prog.o", '-c', "$dir/$prog.cpp", @cflags]);
-	run_die([$cxx, '-o', "$dir/$prog.tmp", "$dir/$prog.o", @xflags]);
+
+	# xapian on Alpine Linux (tested 3.19.0) is linked against libstdc++,
+	# and clang needs to be told to use it (rather than libc++):
+	my @try = rindex($cxx, 'clang') >= 0 ? qw(-lstdc++) : ();
+	my @cmd = ($cxx, '-o', "$dir/$prog.tmp", "$dir/$prog.o", @xflags);
+	while (run_wait(\@cmd) and @try) {
+		warn("# attempting to link again with $try[0]...\n");
+		push(@cmd, shift(@try));
+	}
+	die "# @cmd failed: \$?=$?" if $?;
 	unlink "$dir/$prog.cpp", "$dir/$prog.o";
 	write_file '>', "$dir/XFLAGS.tmp", $xflags, "\n";
 	write_file '>', "$dir/xap_modversion.tmp", $xap_modversion, "\n";

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 09/14] install: updates for Alpine Linux and apk
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (7 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 08/14] xap_helper_cxx: support clang w/o `c++' executable Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 10/14] test_common: extract oct_is from search.t Eric Wong
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

Somewhat surprising that BSD::Resource hasn't been packaged for
Alpine, but otherwise pretty straightforward mapping with some
dependencies filled in manually.
---
 install/deps.perl | 38 +++++++++++++++++++++++++++++++++-----
 install/os.perl   | 12 +++++++++---
 2 files changed, 42 insertions(+), 8 deletions(-)

diff --git a/install/deps.perl b/install/deps.perl
index 3fed4f10..6563c3ce 100755
--- a/install/deps.perl
+++ b/install/deps.perl
@@ -130,33 +130,48 @@ my $non_auto = { # git and perl (+autodie) are essential
 		pkg_add => [ qw(curl p5-Time-TimeDate git) ],
 	},
 	perl => {
+		apk => [ qw(perl perl-utils) ],
 		pkg => 'perl5',
 		pkgin => 'perl',
 		pkg_add => [], # Perl is part of OpenBSD base
 	},
 	# optional stuff:
+	'BSD::Resource' => {
+		apk => [], # not packaged for Alpine 3.19
+	},
 	'Date::Parse' => {
+		apk => 'perl-timedate',
 		deb => 'libtimedate-perl',
 		pkg => 'p5-TimeDate',
 		rpm => 'perl-TimeDate',
 		pkg_add => 'p5-Time-TimeDate',
 	},
 	'Inline::C' => {
+		apk => [ qw(perl-inline-c perl-dev) ],
 		pkg_add => 'p5-Inline', # tested OpenBSD 7.3
 		rpm => 'perl-Inline', # for CentOS 7.x, at least
 	},
 	'DBD::SQLite' => { deb => 'libdbd-sqlite3-perl' },
+	'Plack::Middleware::ReverseProxy' => {
+		apk => [], # not packaged for Alpine 3.19.0
+	},
 	'Plack::Test' => {
+		apk => 'perl-plack',
 		deb => 'libplack-perl',
 		pkg => 'p5-Plack',
 	},
+	'Plack::Test::ExternalServer' => {
+		apk => [], # not packaged for Alpine 3.19.0
+	},
 	'Xapian' => {
+		apk => 'xapian-bindings-perl',
 		deb => 'libsearch-xapian-perl',
 		pkg => 'p5-Xapian',
 		pkg_add => 'xapian-bindings-perl',
 		rpm => [], # xapian14-bindings-perl in 3rd-party repo
 	},
 	'highlight.pm' => {
+		apk => [],
 		deb => 'libhighlight-perl',
 		pkg => [],
 		pkgin => 'p5-highlight',
@@ -171,6 +186,7 @@ my $non_auto = { # git and perl (+autodie) are essential
 
 	# some distros have both sqlite 2 and 3, we've only ever used 3
 	'libsqlite3' => {
+		apk => [], # handled by apk w/ perl-dbd-sqlite
 		pkg => 'sqlite3',
 		rpm => [], # `sqlite' is not removable due to yum/systemd
 		deb => [], # libsqlite3-0, but no need to specify
@@ -184,22 +200,26 @@ my $non_auto = { # git and perl (+autodie) are essential
 		rpm => 'xapian-core',
 	},
 	'libxapian-dev' => {
+		apk => 'xapian-core-dev',
 		pkg => 'xapian-core',
 		pkgin => 'xapian',
 		rpm => 'xapian-core-devel',
 	},
 	'pkg-config' => {
+		apk => [], # handled by apk w/ xapian-core-dev
 		pkg_add => [], # part of the OpenBSD base system
 		pkg => 'pkgconf', # pkg-config is a symlink to pkgconf
 		pkgin => 'pkg-config',
 	},
 	'sqlite3' => { # this is just the executable binary on deb
+		apk => 'sqlite',
 		rpm => [], # `sqlite' is not removable due to yum/systemd
 	},
 
 	# we call xapian-compact(1) in public-inbox-compact(1) and
 	# xapian-delve(1) in public-inbox-cindex(1)
 	'xapian-tools' => {
+		apk => 'xapian-core',
 		pkg => 'xapian-core',
 		pkgin => 'xapian',
 		rpm => 'xapian-core', # ???
@@ -207,6 +227,7 @@ my $non_auto = { # git and perl (+autodie) are essential
 
 	# OS-specific
 	'IO::KQueue' => {
+		apk => [],
 		deb => [],
 		rpm => [],
 	},
@@ -226,6 +247,7 @@ for (qw(autodie Digest::SHA ExtUtils::MakeMaker IO::Compress Sys::Syslog
 		deb => 'perl', # libperl5.XX, but the XX varies
 		pkg => 'perl5',
 		pkg_add => [], # perl is in the OpenBSD base system
+		apk => 'perl',
 		pkgin => 'perl',
 		rpm => "perl-$rpm",
 	};
@@ -240,8 +262,9 @@ if ($pkg_fmt =~ /\A(?:pkg_add|pkgin)\z/) {
 }
 
 my %inst_check = ( # subs which return true if a package is intalled
-	pkg => sub { system(qw(pkg info -q), $_[0]) == 0 },
+	apk => sub { system(qw(apk info -q -e), $_[0]) == 0 },
 	deb => sub { system("dpkg -s $_[0] >/dev/null 2>&1") == 0 },
+	pkg => sub { system(qw(pkg info -q), $_[0]) == 0 },
 	pkg_add => sub { system(qw(pkg_info -q -e), "$_[0]->=0") == 0 },
 	pkgin => sub { system(qw(pkg_info -q -e), $_[0]) == 0 },
 	rpm => sub { system("rpm -qs $_[0] >/dev/null 2>&1") == 0 },
@@ -290,7 +313,10 @@ my (%add, %rm); # uniquify lists
 (@pkg_remove || @pkg_install) or warn "# no packages to install nor remove\n";
 
 # OS-specific cleanups appreciated
-if ($pkg_fmt eq 'deb') {
+if ($pkg_fmt eq 'apk') {
+	root('apk', 'add', @pkg_install) if @pkg_install;
+	root('apk', 'del', @pkg_remove) if @pkg_remove;
+} elsif ($pkg_fmt eq 'deb') {
 	my @apt_opt = qw(-o APT::Install-Recommends=false
 			-o APT::Install-Suggests=false);
 	push @apt_opt, '-y' if $opt->{yes};
@@ -340,10 +366,12 @@ sub pkg2ospkg {
 
 	# check common Perl module name patterns:
 	if ($pkg =~ /::/ || $pkg =~ /\A[A-Z]/) {
-		if ($fmt eq 'deb') {
+		if ($fmt eq 'apk') {
+			$pkg =~ s/::/-/g;
+			return "perl-\L$pkg"
+		} elsif ($fmt eq 'deb') {
 			$pkg =~ s/::/-/g;
-			$pkg =~ tr/A-Z/a-z/;
-			return "lib$pkg-perl";
+			return "lib\L$pkg-perl";
 		} elsif ($fmt eq 'rpm') {
 			$pkg =~ s/::/-/g;
 			return "perl-$pkg"
diff --git a/install/os.perl b/install/os.perl
index bf5c55c2..00edbadf 100644
--- a/install/os.perl
+++ b/install/os.perl
@@ -50,8 +50,13 @@ EOM
 	die "$^O unsupported";
 }
 $VERSION_ID //= 0; # numeric? could be 'sid', actually...
-my %MIN_VER = (freebsd => v11, openbsd => v7.3, netbsd => v9.3,
-	dragonfly => v6.4);
+my %MIN_VER = ( # likely older versions work for many of these...
+	alpine => v3.19,
+	dragonfly => v6.4,
+	freebsd => v11,
+	netbsd => v9.3,
+	openbsd => v7.3,
+);
 
 if (defined(my $min_ver = $MIN_VER{$^O})) {
 	my $vid = $VERSION_ID;
@@ -64,7 +69,8 @@ EOM
 }
 
 sub pkg_fmt () {
-	if ($ID =~ /\A(?:freebsd|dragonfly)\z/) { 'pkg' }
+	if ($ID eq 'alpine') { 'apk' }
+	elsif ($ID =~ /\A(?:freebsd|dragonfly)\z/) { 'pkg' }
 	# *shrug*, as long as the (Net|Open)BSD names don't conflict w/ FreeBSD
 	elsif ($ID eq 'netbsd') { 'pkgin' }
 	elsif ($ID eq 'openbsd') { 'pkg_add' }

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 10/14] test_common: extract oct_is from search.t
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (8 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 09/14] install: updates for Alpine Linux and apk Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 11/14] t/convert-compact: allow S_ISGID bit Eric Wong
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

And use it in convert-compact.t  This gives us nicer errors for
debugging a problem I noticed on Alpine Linux (tested 3.19.0)
---
 lib/PublicInbox/TestCommon.pm |  8 +++++++-
 t/convert-compact.t           | 18 +++++++++---------
 t/search.t                    |  5 -----
 3 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/lib/PublicInbox/TestCommon.pm b/lib/PublicInbox/TestCommon.pm
index 9c413f43..22c50675 100644
--- a/lib/PublicInbox/TestCommon.pm
+++ b/lib/PublicInbox/TestCommon.pm
@@ -29,7 +29,7 @@ BEGIN {
 		tcp_host_port test_lei lei lei_ok $lei_out $lei_err $lei_opt
 		test_httpd xbail require_cmd is_xdeeply tail_f
 		ignore_inline_c_missing no_pollerfd no_coredump cfg_new
-		strace strace_inject lsof_pid);
+		strace strace_inject lsof_pid oct_is);
 	require Test::More;
 	my @methods = grep(!/\W/, @Test::More::EXPORT);
 	eval(join('', map { "*$_=\\&Test::More::$_;" } @methods));
@@ -1033,6 +1033,12 @@ sub strace_inject (;$) {
 	$cmd
 }
 
+sub oct_is ($$$) {
+	my ($got, $exp, $msg) = @_;
+	@_ = (sprintf('0%03o', $got), sprintf('0%03o', $exp), $msg);
+	goto &is; # tail recursion to get lineno from callers on failure
+}
+
 package PublicInbox::TestCommon::InboxWakeup;
 use strict;
 sub on_inbox_unlock { ${$_[0]}->($_[1]) }
diff --git a/t/convert-compact.t b/t/convert-compact.t
index 1ac3517d..c7b7f77e 100644
--- a/t/convert-compact.t
+++ b/t/convert-compact.t
@@ -35,14 +35,14 @@ EOF
 	$im->add($eml) or BAIL_OUT '->add';
 };
 umask(077) or BAIL_OUT "umask: $!";
-is(((stat("$ibx->{inboxdir}/public-inbox"))[2]) & 07777, 0755,
+oct_is(((stat("$ibx->{inboxdir}/public-inbox"))[2]) & 07777, 0755,
 	'sharedRepository respected for v1');
-is(((stat("$ibx->{inboxdir}/public-inbox/msgmap.sqlite3"))[2]) & 07777, 0644,
-	'sharedRepository respected for v1 msgmap');
+oct_is(((stat("$ibx->{inboxdir}/public-inbox/msgmap.sqlite3"))[2]) & 07777,
+	0644, 'sharedRepository respected for v1 msgmap');
 my @xdir = glob("$ibx->{inboxdir}/public-inbox/xap*/*");
 foreach (@xdir) {
 	my @st = stat($_);
-	is($st[2] & 07777, -f _ ? 0644 : 0755,
+	oct_is($st[2] & 07777, -f _ ? 0644 : 0755,
 		'sharedRepository respected on file after convert');
 }
 
@@ -55,7 +55,7 @@ ok(run_script($cmd, undef, $rdr), 'v1 compact works');
 
 @xdir = glob("$ibx->{inboxdir}/public-inbox/xap*");
 is(scalar(@xdir), 1, 'got one xapian directory after compact');
-is(((stat($xdir[0]))[2]) & 07777, 0755,
+oct_is(((stat($xdir[0]))[2]) & 07777, 0755,
 	'sharedRepository respected on v1 compact');
 
 my $hwm = do {
@@ -73,7 +73,7 @@ ok(run_script($cmd, undef, $rdr), 'convert works');
 @xdir = glob("$tmpdir/x/v2/xap*/*");
 foreach (@xdir) {
 	my @st = stat($_);
-	is($st[2] & 07777, -f _ ? 0644 : 0755,
+	oct_is($st[2] & 07777, -f _ ? 0644 : 0755,
 		'sharedRepository respected after convert');
 }
 
@@ -87,17 +87,17 @@ is($ibx->mm->num_highwater, $hwm, 'highwater mark unchanged in v2 inbox');
 @xdir = glob("$tmpdir/x/v2/xap*/*");
 foreach (@xdir) {
 	my @st = stat($_);
-	is($st[2] & 07777, -f _ ? 0644 : 0755,
+	oct_is($st[2] & 07777, -f _ ? 0644 : 0755,
 		'sharedRepository respected after v2 compact');
 }
-is(((stat("$tmpdir/x/v2/msgmap.sqlite3"))[2]) & 07777, 0644,
+oct_is(((stat("$tmpdir/x/v2/msgmap.sqlite3"))[2]) & 07777, 0644,
 	'sharedRepository respected for v2 msgmap');
 
 @xdir = (glob("$tmpdir/x/v2/git/*.git/objects/*/*"),
 	 glob("$tmpdir/x/v2/git/*.git/objects/pack/*"));
 foreach (@xdir) {
 	my @st = stat($_);
-	is($st[2] & 07777, -f _ ? 0444 : 0755,
+	oct_is($st[2] & 07777, -f _ ? 0444 : 0755,
 		'sharedRepository respected after v2 compact');
 }
 my $msgs = $ibx->over->recent({limit => 1000});
diff --git a/t/search.t b/t/search.t
index 282ae586..9fda6694 100644
--- a/t/search.t
+++ b/t/search.t
@@ -34,11 +34,6 @@ my $rw_commit = sub {
 	$ibx->search->reopen;
 };
 
-sub oct_is ($$$) {
-	my ($got, $exp, $msg) = @_;
-	is(sprintf('0%03o', $got), sprintf('0%03o', $exp), $msg);
-}
-
 {
 	# git repository perms
 	use_ok 'PublicInbox::Umask';

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 11/14] t/convert-compact: allow S_ISGID bit
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (9 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 10/14] test_common: extract oct_is from search.t Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 12/14] www_coderepo: fix read buffering Eric Wong
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

My user home directory on Alpine has S_ISGID set on it and every
subdirectory inherits it.  This includes my work tree and the
t/data-gen/* subdirectories.  So just ignore the presence (or
non-presence) of the S_ISGID bit on directories descended from
the cached t/data-gen/* directories.

Now, public-inbox-convert may want to preserve S_ISGID on the
newly-created v2 inbox, but that's a separate discussion.
---
 t/convert-compact.t | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/t/convert-compact.t b/t/convert-compact.t
index c7b7f77e..b123f17b 100644
--- a/t/convert-compact.t
+++ b/t/convert-compact.t
@@ -35,14 +35,14 @@ EOF
 	$im->add($eml) or BAIL_OUT '->add';
 };
 umask(077) or BAIL_OUT "umask: $!";
-oct_is(((stat("$ibx->{inboxdir}/public-inbox"))[2]) & 07777, 0755,
+oct_is(((stat("$ibx->{inboxdir}/public-inbox"))[2]) & 05777, 0755,
 	'sharedRepository respected for v1');
-oct_is(((stat("$ibx->{inboxdir}/public-inbox/msgmap.sqlite3"))[2]) & 07777,
+oct_is(((stat("$ibx->{inboxdir}/public-inbox/msgmap.sqlite3"))[2]) & 05777,
 	0644, 'sharedRepository respected for v1 msgmap');
 my @xdir = glob("$ibx->{inboxdir}/public-inbox/xap*/*");
 foreach (@xdir) {
 	my @st = stat($_);
-	oct_is($st[2] & 07777, -f _ ? 0644 : 0755,
+	oct_is($st[2] & 05777, -f _ ? 0644 : 0755,
 		'sharedRepository respected on file after convert');
 }
 
@@ -55,7 +55,7 @@ ok(run_script($cmd, undef, $rdr), 'v1 compact works');
 
 @xdir = glob("$ibx->{inboxdir}/public-inbox/xap*");
 is(scalar(@xdir), 1, 'got one xapian directory after compact');
-oct_is(((stat($xdir[0]))[2]) & 07777, 0755,
+oct_is(((stat($xdir[0]))[2]) & 05777, 0755,
 	'sharedRepository respected on v1 compact');
 
 my $hwm = do {
@@ -71,7 +71,7 @@ ok(run_script($cmd, undef, $rdr), 'convert --no-index works');
 $cmd = [ '-convert', $ibx->{inboxdir}, "$tmpdir/x/v2" ];
 ok(run_script($cmd, undef, $rdr), 'convert works');
 @xdir = glob("$tmpdir/x/v2/xap*/*");
-foreach (@xdir) {
+for (@xdir) { # TODO: should public-inbox-convert preserve S_ISGID bit?
 	my @st = stat($_);
 	oct_is($st[2] & 07777, -f _ ? 0644 : 0755,
 		'sharedRepository respected after convert');

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 12/14] www_coderepo: fix read buffering
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (10 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 11/14] t/convert-compact: allow S_ISGID bit Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 13/14] gzip_filter: use OO ->zflush dispatch Eric Wong
  2023-12-13  0:50 ` [PATCH 14/14] t/lei-import: relax EIO regexp Eric Wong
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

Our read buffering only worked well with the stdout buffering on
glibc and *BSD libc, but not musl.  When reading the stdout of
git(1), we are likely to get smaller buffers and require more
reads on musl-based systems (tested Alpine Linux 3.19.0).

Thus we must prevent ->translate from being called with an empty
argument list (denoting EOF).  We'll also avoid some local
variable assignments while at it and favor the non-OO ->zflush
dispatch inside RepoAtom and WwwCoderepo subclasses.
---
 lib/PublicInbox/RepoAtom.pm    | 4 ++--
 lib/PublicInbox/WwwCoderepo.pm | 6 +++---
 t/solver_git.t                 | 8 +++++---
 3 files changed, 10 insertions(+), 8 deletions(-)

diff --git a/lib/PublicInbox/RepoAtom.pm b/lib/PublicInbox/RepoAtom.pm
index c1649d0a..ab0f2fcc 100644
--- a/lib/PublicInbox/RepoAtom.pm
+++ b/lib/PublicInbox/RepoAtom.pm
@@ -43,7 +43,7 @@ sub zflush { $_[0]->SUPER::zflush('</feed>') }
 # called by GzipFilter->write or GetlineResponse->getline
 sub translate {
 	my $self = shift;
-	my $rec = $_[0] // return $self->zflush; # getline
+	$_[0] // return zflush($self); # getline caller
 	my @out;
 	my $lbuf = delete($self->{lbuf}) // shift;
 	$lbuf .= shift while @_;
@@ -87,7 +87,7 @@ xmlns="http://www.w3.org/1999/xhtml"><pre style="white-space:pre-wrap">
 	}
 	$self->{lbuf} = $lbuf;
 	chomp @out;
-	$self->SUPER::translate(@out);
+	@out ? $self->SUPER::translate(@out) : ''; # not EOF, yet
 }
 
 # $REPO/tags.atom endpoint
diff --git a/lib/PublicInbox/WwwCoderepo.pm b/lib/PublicInbox/WwwCoderepo.pm
index d1354af5..3814f719 100644
--- a/lib/PublicInbox/WwwCoderepo.pm
+++ b/lib/PublicInbox/WwwCoderepo.pm
@@ -273,7 +273,7 @@ sub zflush { $_[0]->SUPER::zflush('</pre>', $_[0]->_html_end) }
 # called by GzipFilter->write or GetlineResponse->getline
 sub translate {
 	my $ctx = shift;
-	my $rec = $_[0] // return zflush($ctx); # getline
+	$_[0] // return zflush($ctx); # getline caller
 	my @out;
 	my $fbuf = delete($ctx->{fbuf}) // shift;
 	$fbuf .= shift while @_;
@@ -290,8 +290,8 @@ sub translate {
 						$snap_pfx, @snap_fmt);
 		}
 	}
-	$ctx->{fbuf} = $fbuf;
-	$ctx->SUPER::translate(@out);
+	$ctx->{fbuf} = $fbuf; # may be incomplete
+	@out ? $ctx->SUPER::translate(@out) : ''; # not EOF, yet
 }
 
 sub _refs_parse_hdr { # {parse_hdr} for Qspawn
diff --git a/t/solver_git.t b/t/solver_git.t
index ab8aba15..db672904 100644
--- a/t/solver_git.t
+++ b/t/solver_git.t
@@ -405,14 +405,16 @@ EOF
 		is($res->code, 200, 'Atom feed');
 		SKIP: {
 			require_mods('XML::TreePP', 1);
-			my $t = XML::TreePP->new->parse($res->content);
+			my $t = eval { XML::TreePP->new->parse($res->content) }
+				or diag explain($res);
 			is(scalar @{$t->{feed}->{entry}}, 50,
-				'got 50 entries');
+				'got 50 entries') or diag explain([$t, $res]);
 
 			$res = $cb->(GET('/public-inbox/atom/COPYING'));
 			is($res->code, 200, 'file Atom feed');
 			$t = XML::TreePP->new->parse($res->content);
-			ok($t->{feed}->{entry}, 'got entry');
+			ok($t->{feed}->{entry}, 'got entry') or
+				diag explain([ $t, $res ]);
 
 			$res = $cb->(GET('/public-inbox/atom/README.md'));
 			is($res->code, 404, '404 on missing file Atom feed');

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 13/14] gzip_filter: use OO ->zflush dispatch
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (11 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 12/14] www_coderepo: fix read buffering Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  2023-12-13  0:50 ` [PATCH 14/14] t/lei-import: relax EIO regexp Eric Wong
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

While it's not in a code path intended WwwCoderepo and RepoAtom,
those classes provide their own ->zflush, this can future-proof
our code against future subclasses at a minor performance cost.
---
 lib/PublicInbox/GzipFilter.pm | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/PublicInbox/GzipFilter.pm b/lib/PublicInbox/GzipFilter.pm
index fc471ea2..8b630f25 100644
--- a/lib/PublicInbox/GzipFilter.pm
+++ b/lib/PublicInbox/GzipFilter.pm
@@ -106,7 +106,7 @@ sub translate {
 		zmore($self, @_);
 		length($self->{zbuf}) >= 8192 ? delete($self->{zbuf}) : '';
 	} else { # undef == EOF
-		zflush($self);
+		$self->zflush;
 	}
 }
 

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH 14/14] t/lei-import: relax EIO regexp
  2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
                   ` (12 preceding siblings ...)
  2023-12-13  0:50 ` [PATCH 13/14] gzip_filter: use OO ->zflush dispatch Eric Wong
@ 2023-12-13  0:50 ` Eric Wong
  13 siblings, 0 replies; 15+ messages in thread
From: Eric Wong @ 2023-12-13  0:50 UTC (permalink / raw)
  To: meta

musl uses "I/O error" while glibc uses "Input/output error"
I wish something like strerrorname_np(3) were portable
and built into Perl so we could just match on /EIO/.
---
 t/lei-import.t | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/t/lei-import.t b/t/lei-import.t
index b4446b56..89eb1492 100644
--- a/t/lei-import.t
+++ b/t/lei-import.t
@@ -172,12 +172,13 @@ SKIP: {
 	tick; # wait for strace to attach
 	ok(!lei(qw(import -F eml t/plack-qp.eml)),
 		'-F eml import fails on pathname error injection');
-	like($lei_err, qr!error reading t/plack-qp\.eml: .*Input/output error!,
+	my $IO = '[Ii](?:nput)?/[Oo](?:utput)?';
+	like($lei_err, qr!error reading t/plack-qp\.eml: .*?$IO error!,
 		'EIO noted in stderr');
 	open $fh, '<', 't/plack-qp.eml';
 	ok(!lei(qw(import -F eml -), undef, { %$lei_opt, 0 => $fh }),
 		'-F eml import fails on stdin error injection');
-	like($lei_err, qr!error reading .*?: .*Input/output error!,
+	like($lei_err, qr!error reading .*?: .*?$IO error!,
 		'EIO noted in stderr');
 }
 

^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2023-12-13  0:50 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-13  0:50 [PATCH 00/14] Alpine Linux support Eric Wong
2023-12-13  0:50 ` [PATCH 01/14] t/io: strace is optional on Linux Eric Wong
2023-12-13  0:50 ` [PATCH 02/14] tests: account for missing git-http-backend Eric Wong
2023-12-13  0:50 ` [PATCH 03/14] t/cindex*: skip --join when join(1) is missing Eric Wong
2023-12-13  0:50 ` [PATCH 04/14] tests: attempt compatibility w/ busybox lsof Eric Wong
2023-12-13  0:50 ` [PATCH 05/14] lei inspect: drop unneeded strftime import Eric Wong
2023-12-13  0:50 ` [PATCH 06/14] treewide: avoid strftime %k for portability Eric Wong
2023-12-13  0:50 ` [PATCH 07/14] xap_helper_cxx: decouple from Inline::C Eric Wong
2023-12-13  0:50 ` [PATCH 08/14] xap_helper_cxx: support clang w/o `c++' executable Eric Wong
2023-12-13  0:50 ` [PATCH 09/14] install: updates for Alpine Linux and apk Eric Wong
2023-12-13  0:50 ` [PATCH 10/14] test_common: extract oct_is from search.t Eric Wong
2023-12-13  0:50 ` [PATCH 11/14] t/convert-compact: allow S_ISGID bit Eric Wong
2023-12-13  0:50 ` [PATCH 12/14] www_coderepo: fix read buffering Eric Wong
2023-12-13  0:50 ` [PATCH 13/14] gzip_filter: use OO ->zflush dispatch Eric Wong
2023-12-13  0:50 ` [PATCH 14/14] t/lei-import: relax EIO regexp Eric Wong

Code repositories for project(s) associated with this public inbox

	https://80x24.org/public-inbox.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).