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 2.488 seconds using v1.01-cache-2.11-cpan-5c0b1e786e0 )