App-FuguWeb
view release on metacpan or search on metacpan
lib/App/FuguWeb/Render.pm view on Meta::CPAN
return 1 unless @paths;
my $result = Fugu::Process->run(
cmd => [ $self->{mandoc}, '-Tlint', '-W', 'warning', @paths ],
);
return 1 if $result->{success};
$self->{log}->error('mandoc rejected a manual source:');
# mandoc -Tlint writes its diagnostics to standard output, not
# to standard error. A caller that logged only stderr would
# report the failure with no line, no column and no reason.
my $said = join "\n", grep { defined } $result->{stdout},
$result->{stderr}, $result->{error};
$self->{log}->error( '%s', $_ ) for grep { length } split /\n/, $said;
return;
}
# $self->markdown($path):
# Render one Markdown file into an HTML body fragment.
sub markdown ( $self, $path )
{
return $self->_capture( 'lowdown',
lib/App/FuguWeb/Render.pm view on Meta::CPAN
'--release=' . POD_RELEASE,
$path,
],
);
# pod2man reports a malformed directive on standard error and
# still writes the page. The page is what the site needs, so a
# diagnostic is a warning and only empty output is fatal.
unless ( $man->{success} ) {
$self->{log}->warning( 'pod2man on %s: %s', $path, $_ )
for grep { length } split /\n/, $man->{stderr} // '';
}
unless ( length( $man->{stdout} // '' ) ) {
$self->{log}->error( 'pod2man produced nothing for %s', $path );
return;
}
return $self->_capture(
'mandoc',
[ $self->{mandoc}, $self->html_options ],
stdin => $man->{stdout} );
lib/App/FuguWeb/Render.pm view on Meta::CPAN
sub _capture ( $self, $tool, $cmd, %args )
{
my $result = Fugu::Process->run( cmd => $cmd, %args );
unless ( $result->{success} ) {
$self->{log}->error(
'%s failed: %s',
$tool,
$result->{error} // 'exit code ' . $result->{exit_code}
);
$self->{log}->error( '%s', $_ )
for grep { length } split /\n/, $result->{stderr} // '';
return;
}
return $result->{stdout};
}
# _on_path($program):
# Report whether the program can be executed. A path with a
# separator in it is tested as it stands; a bare name is looked
# for in PATH, as the shell would.
t/fuguweb/cli.t view on Meta::CPAN
return $root;
}
# run(@argv):
# Run the CLI with output captured. Return ($exit, $out, $err).
sub run (@argv)
{
my ( $out, $err ) = ( '', '' );
open my $saved_out, '>&', \*STDOUT or die "Cannot save stdout: $!";
open my $saved_err, '>&', \*STDERR or die "Cannot save stderr: $!";
close STDOUT;
close STDERR;
open STDOUT, '>', \$out or die 'Cannot capture stdout';
open STDERR, '>', \$err or die 'Cannot capture stderr';
my $exit = eval { App::FuguWeb::CLI->run(@argv) };
my $died = $@;
close STDOUT;
close STDERR;
open STDOUT, '>&', $saved_out or die "Cannot restore stdout: $!";
open STDERR, '>&', $saved_err or die "Cannot restore stderr: $!";
die $died if $died;
return ( $exit, $out, $err );
}
subtest 'the exit codes are the documented ones' => sub {
is( App::FuguWeb::CLI::EXIT_SUCCESS(), 0, 'success' );
is( App::FuguWeb::CLI::EXIT_ERROR(), 1, 'error' );
is( App::FuguWeb::CLI::EXIT_INVALID_ARGS(), 2, 'invalid arguments' );
t/fuguweb/cli.t view on Meta::CPAN
anchor = manuals
}
RC
'man/bad.1' => ".Sh NAME\n.Nm bad\n",
);
my ( $exit, $out, $err ) = run( '--project', $root, 'build' );
is( $exit, 4, 'a malformed page gives EXIT_RENDER_FAILED' );
# mandoc -Tlint writes its diagnostics to standard output,
# so a caller that logged only stderr would report the
# failure with no line, no column and no reason.
like( $err, qr/mandoc rejected a manual source/,
'the build says what failed' );
like( $err, qr/bad\.1:\d+:\d+/,
'and carries the line and column from mandoc' );
};
}
subtest 'build reports a renderer that is not installed' => sub {
my $root = project();
t/fuguweb/keys.t view on Meta::CPAN
my $out = "$root/out";
ok( site( $config, $out )->build, 'the build succeeds' );
spew( "$out/archive/notes.txt", "mine\n" );
# The build keeps a directory that no build made, and it says
# so. An empty one gets the same report, because the build
# keeps that one too.
my $said = '';
open my $saved, '>&', \*STDERR or die "Cannot save stderr: $!";
close STDERR;
open STDERR, '>', \$said or die 'Cannot capture stderr';
my $again = App::FuguWeb::Site->new(
config => $config,
out => $out,
render => App::FuguWeb::Render->new(
config => $config,
mandoc => '/bin/true',
lowdown => '/bin/true',
),
)->build;
close STDERR;
open STDERR, '>&', $saved or die "Cannot restore stderr: $!";
ok( $again, 'a second build succeeds' );
ok( -e "$out/archive/notes.txt", 'and it keeps the file' );
like( $said, qr{archive/notes\.txt is in the output},
'and it reports the file' );
# An empty directory below the key directory is nobody's
# build, so it stays. The report of it has its own subtest.
mkdir "$out/keys/stale" or die "Cannot make the directory: $!";
ok( site( $config, $out )->build, 'a third build succeeds' );
t/fuguweb/keys.t view on Meta::CPAN
# Run one command of the tool from the project root, with the
# output captured, and return the exit code.
sub cli ( $root, @argv )
{
my ( $out, $err ) = ( '', '' );
my $here = Cwd::getcwd();
chdir $root or die "Cannot chdir to $root: $!";
open my $saved_out, '>&', \*STDOUT or die "Cannot save stdout: $!";
open my $saved_err, '>&', \*STDERR or die "Cannot save stderr: $!";
close STDOUT;
close STDERR;
open STDOUT, '>', \$out or die 'Cannot capture stdout';
open STDERR, '>', \$err or die 'Cannot capture stderr';
my $exit = eval { App::FuguWeb::CLI->run(@argv) };
my $died = $@;
close STDOUT;
close STDERR;
open STDOUT, '>&', $saved_out or die "Cannot restore stdout: $!";
open STDERR, '>&', $saved_err or die "Cannot restore stderr: $!";
chdir $here or die "Cannot chdir back: $!";
die $died if $died;
return ( $exit, $err );
}
subtest 'the clean command removes a key directory' => sub {
my $root = project();
my ( $config, $reason ) = load($root);
t/fuguweb/keys.t view on Meta::CPAN
my $root = project();
my ( $config, $reason ) = load($root);
ok( $config, 'the description loads' ) or diag $reason;
my $out = "$root/out";
ok( site( $config, $out )->build, 'the build succeeds' );
make_path("$out/photos/2024/raw");
my $said = '';
open my $saved, '>&', \*STDERR or die "Cannot save stderr: $!";
close STDERR;
open STDERR, '>', \$said or die 'Cannot capture stderr';
my $again = App::FuguWeb::Site->new(
config => $config,
out => $out,
render => App::FuguWeb::Render->new(
config => $config,
mandoc => '/bin/true',
lowdown => '/bin/true',
),
)->build;
close STDERR;
open STDERR, '>&', $saved or die "Cannot restore stderr: $!";
ok( $again, 'a second build succeeds' );
ok( -d "$out/photos/2024/raw", 'and it keeps the tree' );
like( $said, qr{photos/2024/raw is in the output},
'and it names the tree' );
};
subtest 'the clean refuses a directory of the source' => sub {
# The build renders, so the skip comes before the first
# assertion. A plan that arrives after one is not a plan.
t/fuguweb/rotate.t view on Meta::CPAN
}
# _run(@argv):
# Drive the real command in process, and answer the exit code
# with what it wrote.
sub _run (@argv)
{
my ( $out, $err ) = ( '', '' );
open my $saved_out, '>&', \*STDOUT or die "Cannot save stdout: $!";
open my $saved_err, '>&', \*STDERR or die "Cannot save stderr: $!";
close STDOUT;
close STDERR;
open STDOUT, '>', \$out or die 'Cannot capture stdout';
open STDERR, '>', \$err or die 'Cannot capture stderr';
my $exit = eval { App::FuguWeb::CLI->run(@argv) };
my $died = $@;
close STDOUT;
close STDERR;
open STDOUT, '>&', $saved_out or die "Cannot restore stdout: $!";
open STDERR, '>&', $saved_err or die "Cannot restore stderr: $!";
die $died if $died;
return ( $exit, $out, $err );
}
# _verifies($root, $stem):
# True when the published key of the stem verifies the manifest
# pair of the key directory.
sub _verifies ( $root, $stem )
t/fuguweb/site.t view on Meta::CPAN
is( $site->missing_tool, undef, 'and records no missing tool' );
# The probe is the first step, so the failure names the tool
# that is missing. Without it the build reaches the lint and
# reports that a manual source was rejected, which sends the
# reader to the wrong place.
my $absent = tempdir( CLEANUP => 1 ) . '/out';
my $config = site( $ROOT, $absent )->config;
my $said = '';
open my $saved, '>&', \*STDERR or die "Cannot save stderr: $!";
close STDERR;
open STDERR, '>', \$said or die 'Cannot capture stderr';
my $broken = App::FuguWeb::Site->new(
config => $config,
out => $absent,
render => App::FuguWeb::Render->new(
config => $config,
mandoc => '/nonexistent/mandoc',
),
);
my $failed = $broken->build;
close STDERR;
open STDERR, '>&', $saved or die "Cannot restore stderr: $!";
ok( !$failed, 'a build with no mandoc fails' );
is( $broken->missing_tool, '/nonexistent/mandoc',
'and the build records the missing tool' );
like( $said, qr{/nonexistent/mandoc is not installed},
'and names the tool that is missing' );
unlike( $said, qr/rejected a manual source/,
'and does not blame a manual for it' );
ok( !-e $absent, 'and writes nothing' );
( run in 0.750 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )