1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-03-03 14:52:21 +02:00

More detailed release notes.

Release notes are now broken into sections so that bugs, features, and refactors are clearly delineated.  An "Additional Notes" section has been added for changes to documentation and the test suite that do not affect the core code.
This commit is contained in:
David Steele 2016-05-26 10:34:10 -04:00
parent 0fb8bcbfb7
commit c8d68bcf2d
10 changed files with 1495 additions and 568 deletions

View File

@ -6,7 +6,7 @@ pgBackRest aims to be a simple, reliable backup and restore system that can seam
Instead of relying on traditional backup tools like tar and rsync, pgBackRest implements all backup features internally and uses a custom protocol for communicating with remote systems. Removing reliance on tar and rsync allows for better solutions to database-specific backup challenges. The custom remote protocol allows for more flexibility and limits the types of connections that are required to perform a backup which increases security.
pgBackRest [v1.01](https://github.com/pgbackrest/pgbackrest/releases/tag/release/1.01) is the current stable release.
pgBackRest [v1.01](https://github.com/pgbackrest/pgbackrest/releases/tag/release/1.01) is the current stable release. Release notes are on the [Releases](http://www.pgbackrest.org/release.html) page.
## Features

View File

@ -631,7 +631,7 @@ sub sectionChildProcess
}
}
# Skip children that have already been processed and error on others
elsif ($oChild->nameGet() ne 'title' && $oChild->nameGet() ne 'subtitle' && $oChild->nameGet() ne 'subsubtitle')
elsif ($oChild->nameGet() ne 'title')
{
confess &log(ASSERT, 'unable to process child type ' . $oChild->nameGet());
}

View File

@ -16,7 +16,14 @@ use pgBackRest::Common::Log;
use pgBackRest::Common::String;
use BackRestDoc::Common::DocManifest;
use BackRestDoc::Custom::DocCustomRelease;
####################################################################################################################################
# XML tag/param constants
####################################################################################################################################
use constant XML_SECTION_PARAM_ANCHOR => 'anchor';
push @EXPORT, qw(XML_SECTION_PARAM_ANCHOR);
use constant XML_SECTION_PARAM_ANCHOR_VALUE_NOINHERIT => 'no-inherit';
push @EXPORT, qw(XML_SECTION_PARAM_ANCHOR_VALUE_NOINHERIT);
####################################################################################################################################
# Render tags for various output types
@ -180,6 +187,10 @@ sub new
{
$self->{oReference} =
new BackRestDoc::Common::DocConfig(${$self->{oManifest}->sourceGet('reference')}{doc}, $self);
require BackRestDoc::Custom::DocCustomRelease;
BackRestDoc::Custom::DocCustomRelease->import();
$self->{oRelease} =
new BackRestDoc::Custom::DocCustomRelease(${$self->{oManifest}->sourceGet('release')}{doc}, $self);
}

View File

@ -18,6 +18,40 @@ use pgBackRest::Config::Config;
use pgBackRest::Config::ConfigHelp;
use pgBackRest::FileCommon;
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
####################################################################################################################################
@ -40,6 +74,20 @@ sub 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
(
@ -48,6 +96,114 @@ sub new
);
}
####################################################################################################################################
# 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
#
@ -73,14 +229,61 @@ sub docGet
$oIntroSectionDoc->textSet($self->{oDoc}->nodeGet('intro')->textGet());
# Add each release section
foreach my $oRelease ($self->{oDoc}->nodeGet('changelog')->nodeList('changelog-release'))
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 required release version and date
# Get the release version
my $strVersion = $oRelease->paramGet('version');
# 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'});
$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("Supported Stable Releases");
}
$iStableReleaseTotal++;
}
else
{
if ($iUnsupportedReleaseTotal == 0)
{
$oSection = $oDoc->nodeAdd('section', undef, {id => 'unsupported'});
$oSection->nodeAdd('title')->textSet("Unsupported Releases");
}
$iUnsupportedReleaseTotal++;
}
# Format the date
my $strDate = $oRelease->paramGet('date');
my $strDateOut = "";
# Format the date
my @stryMonth = ('January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December');
@ -91,24 +294,95 @@ sub docGet
if ($strDate =~ /^X/)
{
$strDateOut .= "\n__No Release Date Set__";
$strDateOut .= 'No Release Date Set';
}
else
{
$strDateOut .= $stryMonth[(substr($strDate, 5, 2) - 1)] . ' ' .
$strDateOut .= 'Released ' . $stryMonth[(substr($strDate, 5, 2) - 1)] . ' ' .
(substr($strDate, 8, 2) + 0) . ', ' . substr($strDate, 0, 4);
}
# Add section and titles
my $oSection = $oDoc->nodeAdd('section', undef, {id => $strVersion});
$oSection->nodeAdd('title')->textSet("$strVersion Release");
$oSection->nodeAdd('subtitle')->textSet($oRelease->paramGet('title'));
$oSection->nodeAdd('subsubtitle')->textSet('Released ' . $strDateOut);
my $oReleaseSection = $oSection->nodeAdd('section', undef, {id => $strVersion});
$oReleaseSection->paramSet(XML_SECTION_PARAM_ANCHOR, XML_SECTION_PARAM_ANCHOR_VALUE_NOINHERIT);
# Add release features
foreach my $oReleaseFeature ($oRelease->nodeGet('release-feature-bullet-list')->nodeList('release-feature'))
$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 =
{
$oSection->nodeAdd('p')->textSet($oReleaseFeature->textGet());
&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);
}
}
}
}
}
}

View File

@ -21,6 +21,7 @@ use pgBackRest::Common::String;
use pgBackRest::Config::ConfigHelp;
use BackRestDoc::Common::DocManifest;
use BackRestDoc::Common::DocRender;
use BackRestDoc::Html::DocHtmlBuilder;
use BackRestDoc::Html::DocHtmlElement;
@ -209,7 +210,10 @@ sub sectionProcess
}
# Working variables
$strAnchor = (defined($strAnchor) ? "${strAnchor}/" : '') . $oSection->paramGet('id');
$strAnchor =
($oSection->paramTest(XML_SECTION_PARAM_ANCHOR, XML_SECTION_PARAM_ANCHOR_VALUE_NOINHERIT) ? '' :
(defined($strAnchor) ? "${strAnchor}/" : '')) .
$oSection->paramGet('id');
# Create the section toc element
my $oSectionTocElement = new BackRestDoc::Html::DocHtmlElement(HTML_DIV, "section${iDepth}-toc");
@ -234,21 +238,6 @@ sub sectionProcess
addNew(HTML_A, undef,
{strContent => $strSectionTitle, strRef => "#${strAnchor}"});
# Add subtitle and subsubtitle if they exist
if ($oSection->nodeTest('subtitle'))
{
$oSectionElement->
addNew(HTML_DIV, "section${iDepth}-subtitle",
{strContent => $self->processText($oSection->nodeGet('subtitle')->textGet())});
}
if ($oSection->nodeTest('subsubtitle'))
{
$oSectionElement->
addNew(HTML_DIV, "section${iDepth}-subsubtitle",
{strContent => $self->processText($oSection->nodeGet('subsubtitle')->textGet())});
}
# Add the section intro if it exists
if (defined($oSection->textGet(false)))
{
@ -409,6 +398,20 @@ sub sectionProcess
$oList->addNew(HTML_LI, 'list-unordered', {strContent => $self->processText($oListItem->textGet())});
}
}
# Add a subtitle
elsif ($oChild->nameGet() eq 'subtitle')
{
$oSectionBodyElement->
addNew(HTML_DIV, "section${iDepth}-subtitle",
{strContent => $self->processText($oChild->textGet())});
}
# Add a subsubtitle
elsif ($oChild->nameGet() eq 'subsubtitle')
{
$oSectionBodyElement->
addNew(HTML_DIV, "section${iDepth}-subsubtitle",
{strContent => $self->processText($oChild->textGet())});
}
# Add a subsection
elsif ($oChild->nameGet() eq 'section')
{

View File

@ -191,6 +191,7 @@ Section
{
font-weight: bold;
font-size: 16pt;
margin-top: .5em;
}
.section1-subsubtitle, .section2-subsubtitle, .section3-subsubtitle
@ -200,6 +201,16 @@ Section
margin-top: 0px;
}
.section1-subtitle
{
font-size: 16pt;
}
.section1-subsubtitle
{
font-size: 14pt;
}
.section2
{
margin-top: 1em;
@ -212,6 +223,16 @@ Section
font-size: 16pt;
}
.section2-subtitle, .section2-subtitle
{
font-size: 14pt;
}
.section2-subsubtitle, .section3-subsubtitle
{
font-size: 12pt;
}
.section3
{
margin-left: 1em;

View File

@ -1,4 +1,5 @@
<!ELEMENT doc ((description, intro, changelog)|(config, operation)|(description, variable-list?, cleanup?, section+))>
<!ELEMENT doc ((description, intro, contributor-list, release-list)|(config, operation)|
(description, variable-list?, cleanup?, section+))>
<!ATTLIST doc title CDATA "">
<!ATTLIST doc subtitle CDATA "">
<!ATTLIST doc toc CDATA "y">
@ -117,16 +118,40 @@
<!ELEMENT allow (#PCDATA)>
<!ELEMENT example (#PCDATA)>
<!ELEMENT changelog (text?, changelog-release+)>
<!ELEMENT contributor-list (contributor+)>
<!ELEMENT changelog-release (text?, release-feature-bullet-list)>
<!ATTLIST changelog-release date CDATA #REQUIRED>
<!ATTLIST changelog-release version CDATA #REQUIRED>
<!ATTLIST changelog-release title CDATA #REQUIRED>
<!ELEMENT contributor (contributor-name-display, contributor-id*)>
<!ATTLIST contributor id CDATA #REQUIRED>
<!ELEMENT release-feature-bullet-list (release-feature+)>
<!ELEMENT contributor-name-display (#PCDATA)>
<!ELEMENT contributor-id (#PCDATA)>
<!ATTLIST contributor-id type CDATA #REQUIRED>
<!ELEMENT release-feature (text)>
<!ELEMENT release-list (release+)>
<!ELEMENT release (release-core-list?, release-doc-list?, release-test-list?)>
<!ATTLIST release date CDATA #REQUIRED>
<!ATTLIST release version CDATA #REQUIRED>
<!ATTLIST release title CDATA #REQUIRED>
<!ELEMENT release-core-list (p?, release-bug-list?, release-feature-list?, release-refactor-list?)>
<!ELEMENT release-test-list (p?, release-bug-list?, release-feature-list?, release-refactor-list?)>
<!ELEMENT release-doc-list (p?, release-bug-list?, release-feature-list?, release-refactor-list?)>
<!ELEMENT release-bug-list (release-item+)>
<!ELEMENT release-feature-list (release-item+)>
<!ELEMENT release-refactor-list (release-item+)>
<!ELEMENT release-item (release-item-contributor-list?, p)*>
<!ELEMENT release-item-contributor-list (release-item-ideator*, release-item-contributor*, release-item-reviewer*)>
<!ELEMENT release-item-ideator (#PCDATA)>
<!ATTLIST release-item-ideator id CDATA #REQUIRED>
<!ELEMENT release-item-contributor (#PCDATA)>
<!ATTLIST release-item-contributor id CDATA #REQUIRED>
<!ELEMENT release-item-reviewer (#PCDATA)>
<!ATTLIST release-item-reviewer id CDATA #REQUIRED>
<!ELEMENT contribute (text)>
<!ATTLIST contribute title CDATA #REQUIRED>

View File

@ -32,7 +32,7 @@
<p>Instead of relying on traditional backup tools like tar and rsync, <backrest/> implements all backup features internally and uses a custom protocol for communicating with remote systems. Removing reliance on tar and rsync allows for better solutions to database-specific backup challenges. The custom remote protocol allows for more flexibility and limits the types of connections that are required to perform a backup which increases security.</p>
<p><backrest/> <link url="{[github-url-base]}/releases/tag/release/{[version]}">v{[version]}</link> is the current stable release.</p>
<p><backrest/> <link url="{[github-url-base]}/releases/tag/release/{[version]}">v{[version]}</link> is the current stable release. Release notes are on the <link page="{[backrest-page-release]}">Releases</link> page.</p>
</section>
<section id="features">

File diff suppressed because it is too large Load Diff

View File

@ -328,9 +328,21 @@ eval
my $strReleaseFile = dirname(dirname($0)) . '/doc/xml/release.xml';
my $oReleaseDoc = new BackRestDoc::Common::Doc($strReleaseFile);
foreach my $oRelease ($oReleaseDoc->nodeGet('changelog')->nodeList('changelog-release'))
foreach my $oRelease ($oReleaseDoc->nodeGet('release-list')->nodeList('release'))
{
if ($oRelease->paramGet('version') ne BACKREST_VERSION)
my $strVersion = $oRelease->paramGet('version');
if ($strVersion =~ /dev$/)
{
if ($oRelease->nodeTest('release-core-list'))
{
confess "dev release ${strVersion} must match the program version when core changes have been made";
}
next;
}
if ($strVersion ne BACKREST_VERSION)
{
confess 'unable to find version ' . BACKREST_VERSION . " as the most recent release in ${strReleaseFile}";
}