mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-14 10:13:05 +02:00
443 lines
17 KiB
Perl
443 lines
17 KiB
Perl
####################################################################################################################################
|
|
# DOC RELEASE MODULE
|
|
####################################################################################################################################
|
|
package BackRestDoc::Custom::DocCustomRelease;
|
|
|
|
use strict;
|
|
use warnings FATAL => qw(all);
|
|
use Carp qw(confess);
|
|
|
|
use Exporter qw(import);
|
|
our @EXPORT = qw();
|
|
|
|
use pgBackRest::Common::Log;
|
|
use pgBackRest::Common::String;
|
|
use pgBackRest::Config::Config;
|
|
use pgBackRest::Config::ConfigHelp;
|
|
|
|
use BackRestDoc::Common::DocRender;
|
|
|
|
####################################################################################################################################
|
|
# XML node constants
|
|
####################################################################################################################################
|
|
use constant XML_PARAM_ID => 'id';
|
|
|
|
use constant XML_CONTRIBUTOR_LIST => 'contributor-list';
|
|
use constant XML_CONTRIBUTOR => 'contributor';
|
|
use constant XML_CONTRIBUTOR_NAME_DISPLAY => 'contributor-name-display';
|
|
|
|
use constant XML_RELEASE_CORE_LIST => 'release-core-list';
|
|
use constant XML_RELEASE_DOC_LIST => 'release-doc-list';
|
|
use constant XML_RELEASE_TEST_LIST => 'release-test-list';
|
|
|
|
use constant XML_RELEASE_BUG_LIST => 'release-bug-list';
|
|
use constant XML_RELEASE_FEATURE_LIST => 'release-feature-list';
|
|
use constant XML_RELEASE_REFACTOR_LIST => 'release-refactor-list';
|
|
|
|
use constant XML_RELEASE_ITEM_CONTRIBUTOR_LIST => 'release-item-contributor-list';
|
|
|
|
use constant XML_RELEASE_ITEM_CONTRIBUTOR => 'release-item-contributor';
|
|
use constant XML_RELEASE_ITEM_IDEATOR => 'release-item-ideator';
|
|
use constant XML_RELEASE_ITEM_REVIEWER => 'release-item-reviewer';
|
|
|
|
####################################################################################################################################
|
|
# Contributor text constants
|
|
####################################################################################################################################
|
|
use constant TEXT_CONTRIBUTED => 'Contributed';
|
|
use constant TEXT_FIXED => 'Fixed';
|
|
use constant TEXT_FOUND => 'Reported';
|
|
use constant TEXT_REVIEWED => 'Reviewed';
|
|
use constant TEXT_SUGGESTED => 'Suggested';
|
|
|
|
####################################################################################################################################
|
|
# CONSTRUCTOR
|
|
####################################################################################################################################
|
|
sub new
|
|
{
|
|
my $class = shift; # Class name
|
|
|
|
# Create the class hash
|
|
my $self = {};
|
|
bless $self, $class;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
(
|
|
my $strOperation,
|
|
$self->{oDoc}
|
|
) =
|
|
logDebugParam
|
|
(
|
|
__PACKAGE__ . '->new', \@_,
|
|
{name => 'oDoc'}
|
|
);
|
|
|
|
# Get contributor list
|
|
foreach my $oContributor ($self->{oDoc}->nodeGet(XML_CONTRIBUTOR_LIST)->nodeList(XML_CONTRIBUTOR))
|
|
{
|
|
my $strContributorId = $oContributor->paramGet(XML_PARAM_ID);
|
|
|
|
if (!defined($self->{hContributor}))
|
|
{
|
|
$self->{hContributor} = {};
|
|
$self->{strContributorDefault} = $strContributorId;
|
|
}
|
|
|
|
${$self->{hContributor}}{$strContributorId}{name} = $oContributor->fieldGet(XML_CONTRIBUTOR_NAME_DISPLAY);
|
|
}
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'self', value => $self}
|
|
);
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# currentStableVersion
|
|
#
|
|
# Return the current stable version.
|
|
####################################################################################################################################
|
|
sub currentStableVersion
|
|
{
|
|
my $self = shift;
|
|
|
|
my $oDoc = $self->{oDoc};
|
|
|
|
foreach my $oRelease ($oDoc->nodeGet('release-list')->nodeList('release'))
|
|
{
|
|
my $strVersion = $oRelease->paramGet('version');
|
|
|
|
if ($strVersion !~ /dev$/)
|
|
{
|
|
return $strVersion;
|
|
}
|
|
}
|
|
|
|
confess &log(ERROR, "unable to find non-development version");
|
|
}
|
|
|
|
|
|
####################################################################################################################################
|
|
# releaseLast
|
|
#
|
|
# Get the last release.
|
|
####################################################################################################################################
|
|
sub releaseLast
|
|
{
|
|
my $self = shift;
|
|
|
|
my $oDoc = $self->{oDoc};
|
|
|
|
foreach my $oRelease ($oDoc->nodeGet('release-list')->nodeList('release'))
|
|
{
|
|
return $oRelease;
|
|
}
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# contributorTextGet
|
|
#
|
|
# Get a list of contributors for an item in text format.
|
|
####################################################################################################################################
|
|
sub contributorTextGet
|
|
{
|
|
my $self = shift;
|
|
my $oReleaseItem = shift;
|
|
my $strItemType = shift;
|
|
|
|
my $strContributorText;
|
|
my $hItemContributorType = {};
|
|
|
|
# Create a the list of contributors
|
|
foreach my $strContributorType (XML_RELEASE_ITEM_IDEATOR, XML_RELEASE_ITEM_CONTRIBUTOR, XML_RELEASE_ITEM_REVIEWER)
|
|
{
|
|
my $stryItemContributor = [];
|
|
|
|
if ($oReleaseItem->nodeTest(XML_RELEASE_ITEM_CONTRIBUTOR_LIST))
|
|
{
|
|
foreach my $oContributor ($oReleaseItem->nodeGet(XML_RELEASE_ITEM_CONTRIBUTOR_LIST)->
|
|
nodeList($strContributorType, false))
|
|
{
|
|
push @{$stryItemContributor}, $oContributor->paramGet(XML_PARAM_ID);
|
|
}
|
|
}
|
|
|
|
if (@$stryItemContributor == 0 && $strContributorType eq XML_RELEASE_ITEM_CONTRIBUTOR)
|
|
{
|
|
push @{$stryItemContributor}, $self->{strContributorDefault}
|
|
}
|
|
|
|
$$hItemContributorType{$strContributorType} = $stryItemContributor;
|
|
}
|
|
|
|
# Error if a reviewer is also a contributor
|
|
foreach my $strReviewer (@{$$hItemContributorType{&XML_RELEASE_ITEM_REVIEWER}})
|
|
{
|
|
foreach my $strContributor (@{$$hItemContributorType{&XML_RELEASE_ITEM_CONTRIBUTOR}})
|
|
{
|
|
if ($strReviewer eq $strContributor)
|
|
{
|
|
confess &log(ERROR, "${strReviewer} cannot be both a contributor and a reviewer");
|
|
}
|
|
}
|
|
}
|
|
|
|
# Error if the ideator list is the same as the contributor list
|
|
if (join(',', @{$$hItemContributorType{&XML_RELEASE_ITEM_IDEATOR}}) eq
|
|
join(',', @{$$hItemContributorType{&XML_RELEASE_ITEM_CONTRIBUTOR}}))
|
|
{
|
|
confess &log(ERROR, 'cannot have same contributor and ideator list: ' .
|
|
join(', ', @{$$hItemContributorType{&XML_RELEASE_ITEM_CONTRIBUTOR}}));
|
|
}
|
|
|
|
# Remove the default user if they are the only one in a group (to prevent the entire page from being splattered with one name)
|
|
foreach my $strContributorType (XML_RELEASE_ITEM_IDEATOR, XML_RELEASE_ITEM_CONTRIBUTOR, XML_RELEASE_ITEM_REVIEWER)
|
|
{
|
|
if (@{$$hItemContributorType{$strContributorType}} == 1 &&
|
|
@{$$hItemContributorType{$strContributorType}}[0] eq $self->{strContributorDefault})
|
|
{
|
|
$$hItemContributorType{$strContributorType} = [];
|
|
}
|
|
}
|
|
|
|
# Render the string
|
|
foreach my $strContributorType (XML_RELEASE_ITEM_CONTRIBUTOR, XML_RELEASE_ITEM_REVIEWER, XML_RELEASE_ITEM_IDEATOR)
|
|
{
|
|
my $stryItemContributor = $$hItemContributorType{$strContributorType};
|
|
my $strContributorTypeText;
|
|
|
|
foreach my $strContributor (@{$stryItemContributor})
|
|
{
|
|
my $hContributor = ${$self->{hContributor}}{$strContributor};
|
|
|
|
if (!defined($hContributor))
|
|
{
|
|
confess &log(ERROR, "contributor ${strContributor} does not exist");
|
|
}
|
|
|
|
$strContributorTypeText .= (defined($strContributorTypeText) ? ', ' : '') . $$hContributor{name};
|
|
}
|
|
|
|
if (defined($strContributorTypeText))
|
|
{
|
|
$strContributorTypeText = ' by ' . $strContributorTypeText . '.';
|
|
|
|
if ($strContributorType eq XML_RELEASE_ITEM_CONTRIBUTOR)
|
|
{
|
|
$strContributorTypeText = ($strItemType eq 'bug' ? TEXT_FIXED : TEXT_CONTRIBUTED) . $strContributorTypeText;
|
|
}
|
|
elsif ($strContributorType eq XML_RELEASE_ITEM_IDEATOR)
|
|
{
|
|
$strContributorTypeText = ($strItemType eq 'bug' ? TEXT_FOUND : TEXT_SUGGESTED) . $strContributorTypeText;
|
|
}
|
|
elsif ($strContributorType eq XML_RELEASE_ITEM_REVIEWER)
|
|
{
|
|
$strContributorTypeText = TEXT_REVIEWED . $strContributorTypeText;
|
|
}
|
|
|
|
$strContributorText .= (defined($strContributorText) ? ' ' : '') . $strContributorTypeText;
|
|
}
|
|
}
|
|
|
|
return $strContributorText;
|
|
}
|
|
|
|
####################################################################################################################################
|
|
# docGet
|
|
#
|
|
# Get the xml for release.
|
|
####################################################################################################################################
|
|
sub docGet
|
|
{
|
|
my $self = shift;
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
my $strOperation = logDebugParam(__PACKAGE__ . '->docGet');
|
|
|
|
# Create the doc
|
|
my $oDoc = new BackRestDoc::Common::Doc();
|
|
$oDoc->paramSet('title', $self->{oDoc}->paramGet('title'));
|
|
$oDoc->paramSet('toc-number', $self->{oDoc}->paramGet('toc-number'));
|
|
|
|
# Set the description for use as a meta tag
|
|
$oDoc->fieldSet('description', $self->{oDoc}->fieldGet('description'));
|
|
|
|
# Add the introduction
|
|
my $oIntroSectionDoc = $oDoc->nodeAdd('section', undef, {id => 'introduction'});
|
|
$oIntroSectionDoc->nodeAdd('title')->textSet('Introduction');
|
|
$oIntroSectionDoc->textSet($self->{oDoc}->nodeGet('intro')->textGet());
|
|
|
|
# Add each release section
|
|
my $oSection;
|
|
my $iDevReleaseTotal = 0;
|
|
my $iCurrentReleaseTotal = 0;
|
|
my $iStableReleaseTotal = 0;
|
|
my $iUnsupportedReleaseTotal = 0;
|
|
|
|
foreach my $oRelease ($self->{oDoc}->nodeGet('release-list')->nodeList('release'))
|
|
{
|
|
# Get the release version
|
|
my $strVersion = $oRelease->paramGet('version');
|
|
|
|
# Display versions in TOC?
|
|
my $bTOC = true;
|
|
|
|
# Create a release section
|
|
if ($strVersion =~ /dev$/)
|
|
{
|
|
if ($iDevReleaseTotal > 1)
|
|
{
|
|
confess &log(ERROR, 'only one development release is allowed');
|
|
}
|
|
|
|
$oSection = $oDoc->nodeAdd('section', undef, {id => 'development', keyword => 'dev'});
|
|
$oSection->nodeAdd('title')->textSet("Development Notes");
|
|
|
|
$iDevReleaseTotal++;
|
|
}
|
|
elsif ($iCurrentReleaseTotal == 0)
|
|
{
|
|
$oSection = $oDoc->nodeAdd('section', undef, {id => 'current'});
|
|
$oSection->nodeAdd('title')->textSet("Current Stable Release");
|
|
$iCurrentReleaseTotal++;
|
|
}
|
|
elsif ($strVersion ge '1.00')
|
|
{
|
|
if ($iStableReleaseTotal == 0)
|
|
{
|
|
$oSection = $oDoc->nodeAdd('section', undef, {id => 'supported'});
|
|
$oSection->nodeAdd('title')->textSet("Stable Releases");
|
|
}
|
|
|
|
$iStableReleaseTotal++;
|
|
$bTOC = false;
|
|
}
|
|
else
|
|
{
|
|
if ($iUnsupportedReleaseTotal == 0)
|
|
{
|
|
$oSection = $oDoc->nodeAdd('section', undef, {id => 'unsupported'});
|
|
$oSection->nodeAdd('title')->textSet("Pre-Stable Releases");
|
|
}
|
|
|
|
$iUnsupportedReleaseTotal++;
|
|
$bTOC = false;
|
|
}
|
|
|
|
# Format the date
|
|
my $strDate = $oRelease->paramGet('date');
|
|
my $strDateOut = "";
|
|
|
|
my @stryMonth = ('January', 'February', 'March', 'April', 'May', 'June',
|
|
'July', 'August', 'September', 'October', 'November', 'December');
|
|
|
|
if ($strDate =~ /^X/)
|
|
{
|
|
$strDateOut .= 'No Release Date Set';
|
|
}
|
|
else
|
|
{
|
|
if ($strDate !~ /^(XXXX-XX-XX)|([0-9]{4}-[0-9]{2}-[0-9]{2})$/)
|
|
{
|
|
confess &log(ASSERT, "invalid date ${strDate} for release {$strVersion}");
|
|
}
|
|
|
|
$strDateOut .= 'Released ' . $stryMonth[(substr($strDate, 5, 2) - 1)] . ' ' .
|
|
(substr($strDate, 8, 2) + 0) . ', ' . substr($strDate, 0, 4);
|
|
}
|
|
|
|
# Add section and titles
|
|
my $oReleaseSection = $oSection->nodeAdd('section', undef, {id => $strVersion, toc => !$bTOC ? 'n' : undef});
|
|
$oReleaseSection->paramSet(XML_SECTION_PARAM_ANCHOR, XML_SECTION_PARAM_ANCHOR_VALUE_NOINHERIT);
|
|
|
|
$oReleaseSection->nodeAdd('title')->textSet(
|
|
"v${strVersion} " . ($strVersion =~ /dev$/ ? '' : 'Release ') . 'Notes');
|
|
|
|
$oReleaseSection->nodeAdd('subtitle')->textSet($oRelease->paramGet('title'));
|
|
$oReleaseSection->nodeAdd('subsubtitle')->textSet($strDateOut);
|
|
|
|
# Add release sections
|
|
my $bAdditionalNotes = false;
|
|
|
|
my $hSectionType =
|
|
{
|
|
&XML_RELEASE_CORE_LIST => {title => 'Core', type => 'core'},
|
|
&XML_RELEASE_DOC_LIST => {title => 'Documentation', type => 'doc'},
|
|
&XML_RELEASE_TEST_LIST => {title => 'Test Suite', type => 'test'},
|
|
};
|
|
|
|
foreach my $strSectionType (XML_RELEASE_CORE_LIST, XML_RELEASE_DOC_LIST, XML_RELEASE_TEST_LIST)
|
|
{
|
|
if ($oRelease->nodeTest($strSectionType))
|
|
{
|
|
# Create subsections for any release section other than core. This breaks up the release items.
|
|
my $oSubSection = $oReleaseSection;
|
|
|
|
if ($strSectionType ne XML_RELEASE_CORE_LIST && !$bAdditionalNotes)
|
|
{
|
|
$oReleaseSection->nodeAdd('subtitle')->textSet("Additional Notes");
|
|
$bAdditionalNotes = true;
|
|
}
|
|
|
|
# Add release note if present
|
|
if ($oRelease->nodeGet($strSectionType)->nodeTest('p'))
|
|
{
|
|
$oSubSection->nodeAdd('p')->textSet($oRelease->nodeGet($strSectionType)->nodeGet('p')->textGet());
|
|
}
|
|
|
|
# Add release item types
|
|
my $hItemType =
|
|
{
|
|
&XML_RELEASE_BUG_LIST => {title => 'Bug Fixes', type => 'bug'},
|
|
&XML_RELEASE_FEATURE_LIST => {title => 'Features', type=> 'feature'},
|
|
&XML_RELEASE_REFACTOR_LIST => {title => 'Refactoring', type=> 'refactor'},
|
|
};
|
|
|
|
foreach my $strItemType (XML_RELEASE_BUG_LIST, XML_RELEASE_FEATURE_LIST, XML_RELEASE_REFACTOR_LIST)
|
|
{
|
|
if ($oRelease->nodeGet($strSectionType)->nodeTest($strItemType))
|
|
{
|
|
my $strTypeText =
|
|
($strSectionType eq XML_RELEASE_CORE_LIST ? '' : $$hSectionType{$strSectionType}{title}) . ' ' .
|
|
$$hItemType{$strItemType}{title} . ':';
|
|
|
|
$oSubSection->
|
|
nodeAdd('p')->textSet(
|
|
{name => 'text', children=> [{name => 'b', value => $strTypeText}]});
|
|
|
|
my $oList = $oSubSection->nodeAdd('list');
|
|
|
|
# Add release items
|
|
foreach my $oReleaseFeature ($oRelease->nodeGet($strSectionType)->
|
|
nodeGet($strItemType)->nodeList('release-item'))
|
|
{
|
|
my $oReleaseItemText = $oReleaseFeature->nodeGet('p')->textGet();
|
|
my $strContributorText = $self->contributorTextGet($oReleaseFeature, $$hItemType{$strItemType}{type});
|
|
|
|
if (defined($strContributorText))
|
|
{
|
|
push(@{$oReleaseItemText->{oDoc}{children}}, ' (');
|
|
push(@{$oReleaseItemText->{oDoc}{children}},
|
|
{name => 'i', value => $strContributorText});
|
|
push(@{$oReleaseItemText->{oDoc}{children}}, ')');
|
|
}
|
|
|
|
$oList->nodeAdd('list-item')->textSet($oReleaseItemText);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Return from function and log return values if any
|
|
return logDebugReturn
|
|
(
|
|
$strOperation,
|
|
{name => 'oDoc', value => $oDoc}
|
|
);
|
|
}
|
|
|
|
1;
|