Skip to content

Commit

Permalink
Use the git log to ease release note management.
Browse files Browse the repository at this point in the history
The release notes are generally a direct reflection of the git log.  So, ease the burden of maintaining the release notes by using the git log to determine what needs to be added.

Currently only non-dev items are required to be matched to a git commit but the goal is to account for all commits.

The git history cache is generated from the git log but can be modified to correct typos and match the release notes as they evolve.  The commit hash is used to identify commits that have already been added to the cache.

There's plenty more to do here.  For instance, links to the commits for each release item should be added to the release notes.
  • Loading branch information
dwsteele committed May 22, 2019
1 parent 86482c7 commit ec9622c
Show file tree
Hide file tree
Showing 7 changed files with 11,100 additions and 20 deletions.
20 changes: 17 additions & 3 deletions doc/lib/BackRestDoc/Common/DocRender.pm
Expand Up @@ -167,8 +167,8 @@ sub new
(
__PACKAGE__ . '->new', \@_,
{name => 'strType'},
{name => 'oManifest'},
{name => 'bExe'},
{name => 'oManifest', required => false},
{name => 'bExe', required => false},
{name => 'strRenderOutKey', required => false}
);

Expand Down Expand Up @@ -275,6 +275,20 @@ sub new
);
}

####################################################################################################################################
# Set begin and end values for a tag
####################################################################################################################################
sub tagSet
{
my $self = shift;
my $strTag = shift;
my $strBegin = shift;
my $strEnd = shift;

$oRenderTag->{$self->{strType}}{$strTag}[0] = defined($strBegin) ? $strBegin : '';
$oRenderTag->{$self->{strType}}{$strTag}[1] = defined($strEnd) ? $strEnd : '';
}

####################################################################################################################################
# variableReplace
#
Expand All @@ -284,7 +298,7 @@ sub variableReplace
{
my $self = shift;

return $self->{oManifest}->variableReplace(shift, $self->{strType});
return defined($self->{oManifest}) ? $self->{oManifest}->variableReplace(shift, $self->{strType}) : shift;
}

####################################################################################################################################
Expand Down
194 changes: 190 additions & 4 deletions doc/lib/BackRestDoc/Custom/DocCustomRelease.pm
Expand Up @@ -7,11 +7,15 @@ use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);

use Cwd qw(abs_path);
use Exporter qw(import);
our @EXPORT = qw();
use File::Basename qw(dirname);

use pgBackRest::Common::Log;
use pgBackRest::Common::String;
use pgBackRest::Version;

use pgBackRestBuild::Config::Data;

use BackRestDoc::Common::DocRender;
Expand Down Expand Up @@ -245,6 +249,60 @@ sub contributorTextGet
return $strContributorText;
}

####################################################################################################################################
# Find a commit by subject prefix. Error if the prefix appears more than once.
####################################################################################################################################
sub commitFindSubject
{
my $self = shift;
my $rhyCommit = shift;
my $strSubjectPrefix = shift;
my $bRegExp = shift;

$bRegExp = defined($bRegExp) ? $bRegExp : true;
my $rhResult = undef;

foreach my $rhCommit (@{$rhyCommit})
{
if (($bRegExp && $rhCommit->{subject} =~ /^$strSubjectPrefix/) ||
(!$bRegExp && length($rhCommit->{subject}) >= length($strSubjectPrefix) &&
substr($rhCommit->{subject}, 0, length($strSubjectPrefix)) eq $strSubjectPrefix))
{
if (defined($rhResult))
{
confess &log(ERROR, "subject prefix '${strSubjectPrefix}' already found in commit " . $rhCommit->{commit});
}

$rhResult = $rhCommit;
}
}

return $rhResult;
}

####################################################################################################################################
# Throw an error that includes a list of release commits
####################################################################################################################################
sub commitError
{
my $self = shift;
my $strMessage = shift;
my $rstryCommitRemaining = shift;
my $rhyCommit = shift;

my $strList;

foreach my $strCommit (@{$rstryCommitRemaining})
{
$strList .=
(defined($strList) ? "\n" : '') .
substr($rhyCommit->{$strCommit}{date}, 0, length($rhyCommit->{$strCommit}{date}) - 15) . " $strCommit: " .
$rhyCommit->{$strCommit}{subject};
}

confess &log(ERROR, "${strMessage}:\n${strList}");
}

####################################################################################################################################
# docGet
#
Expand All @@ -257,6 +315,15 @@ sub docGet
# Assign function parameters, defaults, and log debug info
my $strOperation = logDebugParam(__PACKAGE__ . '->docGet');

# Load the git history
my $oStorageDoc = new pgBackRest::Storage::Local(
dirname(abs_path($0)), new pgBackRest::Storage::Posix::Driver({bFileSync => false, bPathSync => false}));
my @hyGitLog = @{(JSON::PP->new()->allow_nonref())->decode(${$oStorageDoc->get("resource/git-history.cache")})};

# Get renderer
my $oRender = new BackRestDoc::Common::DocRender('text');
$oRender->tagSet('backrest', PROJECT_NAME);

# Create the doc
my $oDoc = new BackRestDoc::Common::Doc();
$oDoc->paramSet('title', $self->{oDoc}->paramGet('title'));
Expand All @@ -277,16 +344,85 @@ sub docGet
my $iStableReleaseTotal = 0;
my $iUnsupportedReleaseTotal = 0;

foreach my $oRelease ($self->{oDoc}->nodeGet('release-list')->nodeList('release'))
my @oyRelease = $self->{oDoc}->nodeGet('release-list')->nodeList('release');

for (my $iReleaseIdx = 0; $iReleaseIdx < @oyRelease; $iReleaseIdx++)
{
# Get the release version
my $oRelease = $oyRelease[$iReleaseIdx];

# Get the release version and dev flag
my $strVersion = $oRelease->paramGet('version');
my $bReleaseDev = $strVersion =~ /dev$/ ? true : false;

# Get a list of commits that apply to this release
my @rhyReleaseCommit;
my $rhReleaseCommitRemaining;
my @stryReleaseCommitRemaining;
my $bReleaseCheckCommit = false;

if ($strVersion ge '2.01')
{
# Should commits in the release be checked?
$bReleaseCheckCommit = !$bReleaseDev ? true : false;

# Get the begin commit
my $rhReleaseCommitBegin = $self->commitFindSubject(\@hyGitLog, "Begin v${strVersion} development\\.");
my $strReleaseCommitBegin = defined($rhReleaseCommitBegin) ? $rhReleaseCommitBegin->{commit} : undef;

# Get the end commit of the last release
my $strReleaseLastVersion = $oyRelease[$iReleaseIdx + 1]->paramGet('version');
my $rhReleaseLastCommitEnd = $self->commitFindSubject(\@hyGitLog, "v${strReleaseLastVersion}\\: .+");

if (!defined($rhReleaseLastCommitEnd))
{
confess &log(ERROR, "release ${strReleaseLastVersion} must have an end commit");
}

my $strReleaseLastCommitEnd = $rhReleaseLastCommitEnd->{commit};

# Get the end commit
my $rhReleaseCommitEnd = $self->commitFindSubject(\@hyGitLog, "v${strVersion}\\: .+");
my $strReleaseCommitEnd = defined($rhReleaseCommitEnd) ? $rhReleaseCommitEnd->{commit} : undef;

if ($bReleaseCheckCommit && !defined($rhReleaseCommitEnd) && $iReleaseIdx != 0)
{
confess &log(ERROR, "release ${strVersion} must have an end commit");
}

# Make a list of commits for this release
while ($hyGitLog[0]->{commit} ne $strReleaseLastCommitEnd)
{
# Don't add begin/end commits to the list since they are already accounted for
if ((defined($strReleaseCommitEnd) && $hyGitLog[0]->{commit} eq $strReleaseCommitEnd) ||
(defined($strReleaseCommitBegin) && $hyGitLog[0]->{commit} eq $strReleaseCommitBegin))
{
shift(@hyGitLog);
}
# Else add the commit to this releases' list
else
{
push(@stryReleaseCommitRemaining, $hyGitLog[0]->{commit});
push(@rhyReleaseCommit, $hyGitLog[0]);

$rhReleaseCommitRemaining->{$hyGitLog[0]->{commit}}{date} = $hyGitLog[0]->{date};
$rhReleaseCommitRemaining->{$hyGitLog[0]->{commit}}{subject} = $hyGitLog[0]->{subject};

shift(@hyGitLog);
}
}

# At least one commit is required for non-dev releases
if ($bReleaseCheckCommit && @stryReleaseCommitRemaining == 0)
{
confess &log(ERROR, "no commits found for release ${strVersion}");
}
}

# Display versions in TOC?
my $bTOC = true;

# Create a release section
if ($strVersion =~ /dev$/)
if ($bReleaseDev)
{
if ($iDevReleaseTotal > 1)
{
Expand Down Expand Up @@ -354,7 +490,7 @@ sub docGet
$oReleaseSection->paramSet(XML_SECTION_PARAM_ANCHOR, XML_SECTION_PARAM_ANCHOR_VALUE_NOINHERIT);

$oReleaseSection->nodeAdd('title')->textSet(
"v${strVersion} " . ($strVersion =~ /dev$/ ? '' : 'Release ') . 'Notes');
"v${strVersion} " . ($bReleaseDev ? '' : 'Release ') . 'Notes');

$oReleaseSection->nodeAdd('subtitle')->textSet($oRelease->paramGet('title'));
$oReleaseSection->nodeAdd('subsubtitle')->textSet($strDateOut);
Expand Down Expand Up @@ -420,6 +556,49 @@ sub docGet
my @rhyReleaseItemP = $oReleaseFeature->nodeList('p');
my $oReleaseItemText = $rhyReleaseItemP[0]->textGet();

# Check release item commits
if ($bReleaseCheckCommit && $strItemType ne XML_RELEASE_DEVELOPMENT_LIST)
{
my @oyCommit = $oReleaseFeature->nodeList('commit', false);

# If no commits found then try to use the description as the commit subject
if (@oyCommit == 0)
{
my $strSubject = $oRender->processText($oReleaseItemText);
my $rhCommit = $self->commitFindSubject(\@rhyReleaseCommit, $strSubject, false);

if (!defined($rhCommit))
{
$self->commitError(
"unable to find commit or no subject match for release ${strVersion} item" .
" '${strSubject}'",
\@stryReleaseCommitRemaining, $rhReleaseCommitRemaining);

my $strCommit = $rhCommit->{commit};
@stryReleaseCommitRemaining = grep(!/$strCommit/, @stryReleaseCommitRemaining);
}
}

# Check the rest of the commits to ensure they exist
foreach my $oCommit (@oyCommit)
{
my $strSubject = $oCommit->paramGet('subject');
my $rhCommit = $self->commitFindSubject(\@rhyReleaseCommit, $strSubject, false);

if (defined($rhCommit))
{
my $strCommit = $rhCommit->{commit};
@stryReleaseCommitRemaining = grep(!/$strCommit/, @stryReleaseCommitRemaining);
}
else
{
$self->commitError(
"unable to find release ${strVersion} commit subject '${strSubject}' in list",
\@stryReleaseCommitRemaining, $rhReleaseCommitRemaining);
}
}
}

# Append the rest of the text
if (@rhyReleaseItemP > 1)
{
Expand Down Expand Up @@ -451,6 +630,13 @@ sub docGet
}
}
}

# Error if there are commits left over
# if ($bReleaseCheckCommit && @stryReleaseCommitRemaining != 0)
# {
# $self->commitError(
# "unassigned commits for release ${strVersion}", \@stryReleaseCommitRemaining, $rhReleaseCommitRemaining);
# }
}

# Return from function and log return values if any
Expand Down

0 comments on commit ec9622c

Please sign in to comment.