Skip to content
This repository has been archived by the owner on Jan 18, 2021. It is now read-only.

Commit

Permalink
Merge pull request #11 from mockito/concise-release-notes
Browse files Browse the repository at this point in the history
Progress on concise release notes
  • Loading branch information
mockitoguy committed Feb 18, 2017
2 parents fca9291 + 9f6800e commit 1cdc8ed
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 32 deletions.
Expand Up @@ -38,7 +38,7 @@ class GitNotesBuilder implements NotesBuilder {
public String buildNotes(String version, String fromRevision, String toRevision, final Map<String, String> labels) {
LOG.info("Getting release notes between {} and {}", fromRevision, toRevision);

ContributionsProvider contributionsProvider = Vcs.getGitProvider(Exec.getProcessRunner(workDir));
ContributionsProvider contributionsProvider = Vcs.getContributionsProvider(Exec.getProcessRunner(workDir));
ContributionSet contributions = contributionsProvider.getContributionsBetween(fromRevision, toRevision);

ImprovementsProvider improvementsProvider = Improvements.getGitHubProvider(authTokenEnvVar);
Expand Down
@@ -1,12 +1,9 @@
package org.mockito.release.notes.format;

import org.mockito.release.notes.internal.DateFormat;
import org.mockito.release.notes.model.Improvement;
import org.mockito.release.notes.model.ReleaseNotesData;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

class ConciseFormatter implements MultiReleaseNotesFormatter {

private final String introductionText;
Expand All @@ -18,7 +15,7 @@ public ConciseFormatter(String introductionText) {
public String formatReleaseNotes(Iterable<ReleaseNotesData> data) {
StringBuilder sb = new StringBuilder(introductionText);
for (ReleaseNotesData d : data) {
sb.append("### ").append(d.getVersion()).append(" - ").append(formatDate(d.getDate()))
sb.append("### ").append(d.getVersion()).append(" - ").append(DateFormat.formatDate(d.getDate()))
.append("\n\n");

for (Improvement i : d.getImprovements()) {
Expand All @@ -31,11 +28,4 @@ public String formatReleaseNotes(Iterable<ReleaseNotesData> data) {

return sb.toString();
}

private static String formatDate(Date date) {
//TODO SF reuse and unify
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm");
f.setTimeZone(TimeZone.getTimeZone("UTC"));
return f.format(date);
}
}
@@ -1,12 +1,12 @@
package org.mockito.release.notes.format;

import org.mockito.release.notes.internal.DateFormat;
import org.mockito.release.notes.model.Contribution;
import org.mockito.release.notes.model.ContributionSet;
import org.mockito.release.notes.model.Improvement;
import org.mockito.release.notes.model.ReleaseNotesData;
import org.mockito.release.util.MultiMap;

import java.text.SimpleDateFormat;
import java.util.*;

/**
Expand Down Expand Up @@ -83,9 +83,7 @@ private String format(ContributionSet contributions) {
}

public String formatVersion(ReleaseNotesData data) {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm z");
f.setTimeZone(TimeZone.getTimeZone("UTC"));
String now = f.format(data.getDate());
String now = DateFormat.formatDate(data.getDate());

return "### " + data.getVersion() + " (" + now + ")" + "\n\n"
+ format(data.getContributions()) + "\n"
Expand Down
Expand Up @@ -6,28 +6,27 @@
import org.mockito.release.notes.model.Improvement;
import org.mockito.release.notes.model.ReleaseNotesData;
import org.mockito.release.notes.vcs.ContributionsProvider;
import org.mockito.release.notes.vcs.ReleaseDateProvider;

import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.*;

class DefaultReleaseNotesGenerator implements ReleaseNotesGenerator {

private final ContributionsProvider contributionsProvider;
private final ImprovementsProvider improvementsProvider;
private final ReleaseDateProvider releaseDateProvider;

DefaultReleaseNotesGenerator(ContributionsProvider contributionsProvider, ImprovementsProvider improvementsProvider) {
DefaultReleaseNotesGenerator(ContributionsProvider contributionsProvider, ImprovementsProvider improvementsProvider, ReleaseDateProvider releaseDateProvider) {
this.contributionsProvider = contributionsProvider;
this.improvementsProvider = improvementsProvider;
this.releaseDateProvider = releaseDateProvider;
}

public Collection<ReleaseNotesData> generateReleaseNotes(Collection<String> targetVersions, String tagPrefix,
Collection<String> gitHubLabels, boolean onlyPullRequests) {
List<ReleaseNotesData> out = new LinkedList<ReleaseNotesData>();

//TODO SF use this to get the tag date: 2.6.7
//git log --tags --simplify-by-decoration --pretty="format:%ai %d"
Map<String, Date> releaseDates = releaseDateProvider.getReleaseDates(targetVersions, tagPrefix);

String to = null;
for (String v : targetVersions) {
Expand All @@ -40,9 +39,9 @@ public Collection<ReleaseNotesData> generateReleaseNotes(Collection<String> targ

ContributionSet contributions = contributionsProvider.getContributionsBetween(fromRev, toRev);
Collection<Improvement> improvements = improvementsProvider.getImprovements(contributions, gitHubLabels, onlyPullRequests);
out.add(new DefaultReleaseNotesData(to, new Date(), contributions, improvements));
out.add(new DefaultReleaseNotesData(to, releaseDates.get(to), contributions, improvements));

//next round
//next version
to = v;
}

Expand Down
@@ -1,18 +1,22 @@
package org.mockito.release.notes.generator;

import org.mockito.release.exec.Exec;
import org.mockito.release.exec.ProcessRunner;
import org.mockito.release.notes.improvements.Improvements;
import org.mockito.release.notes.improvements.ImprovementsProvider;
import org.mockito.release.notes.vcs.ContributionsProvider;
import org.mockito.release.notes.vcs.ReleaseDateProvider;
import org.mockito.release.notes.vcs.Vcs;

import java.io.File;

public class ReleaseNotesGenerators {

public static ReleaseNotesGenerator releaseNotesGenerator(File workDir, String authToken) {
ContributionsProvider contributionsProvider = Vcs.getGitProvider(Exec.getProcessRunner(workDir));
ProcessRunner processRunner = Exec.getProcessRunner(workDir);
ContributionsProvider contributionsProvider = Vcs.getContributionsProvider(processRunner);
ImprovementsProvider improvementsProvider = Improvements.getGitHubProvider(authToken);
return new DefaultReleaseNotesGenerator(contributionsProvider, improvementsProvider);
ReleaseDateProvider releaseDateProvider = Vcs.getReleaseDateProvider(processRunner);
return new DefaultReleaseNotesGenerator(contributionsProvider, improvementsProvider, releaseDateProvider);
}
}
34 changes: 34 additions & 0 deletions src/main/groovy/org/mockito/release/notes/internal/DateFormat.java
@@ -0,0 +1,34 @@
package org.mockito.release.notes.internal;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

/**
* Date parsing and formatting utilities
*/
public class DateFormat {

/**
* Parses date in iso format, e.g. "yyyy-MM-dd HH:mm:ss Z"
*/
public static Date parseDate(String date) {
String pattern = "yyyy-MM-dd HH:mm:ss Z";
SimpleDateFormat format = new SimpleDateFormat(pattern);
try {
return format.parse(date.trim());
} catch (ParseException e) {
throw new RuntimeException("Problems parsing date: [" + date + "]. Required format is: [" + pattern + "].", e);
}
}

/**
* Parses date in iso format, e.g. "yyyy-MM-dd HH:mm:ss Z"
*/
public static String formatDate(Date date) {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm");
f.setTimeZone(TimeZone.getTimeZone("UTC"));
return f.format(date);
}
}
@@ -0,0 +1,33 @@
package org.mockito.release.notes.vcs;

import org.mockito.release.exec.ProcessRunner;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static org.mockito.release.notes.internal.DateFormat.parseDate;

class DefaultReleaseDateProvider implements ReleaseDateProvider {

private final ProcessRunner runner;

DefaultReleaseDateProvider(ProcessRunner runner) {
this.runner = runner;
}

@Override
public Map<String, Date> getReleaseDates(Collection<String> versions, String tagPrefix) {
Map<String, Date> out = new HashMap<String, Date>();
for (String version: versions) {
String tag = tagPrefix + version;
String date = runner.run("git", "log", "--pretty=%ad", "--date=iso", tag, "-n", "1");
//example output returned by running git command: 2017-01-29 08:14:09 -0800
Date d = parseDate(date.trim());
out.put(version, d);
}

return out;
}
}
@@ -0,0 +1,20 @@
package org.mockito.release.notes.vcs;

import java.util.Collection;
import java.util.Date;
import java.util.Map;

/**
* Provides release dates for versions
*/
public interface ReleaseDateProvider {

/**
* Provides release dates for given versions.
*
* @param versions for example: 1.2.0, 1.1.0, 1.0.0
* @param tagPrefix optional tag prefix, adding it to the version String should create vcs addressable revision, tag.
* Typically it is "v" or empty String if no tag prefix is used.
*/
Map<String, Date> getReleaseDates(Collection<String> versions, String tagPrefix);
}
11 changes: 9 additions & 2 deletions src/main/groovy/org/mockito/release/notes/vcs/Vcs.java
Expand Up @@ -10,7 +10,14 @@ public class Vcs {
/**
* Provides means to get contributions.
*/
public static ContributionsProvider getGitProvider(ProcessRunner runner) {
public static ContributionsProvider getContributionsProvider(ProcessRunner runner) {
return new GitContributionsProvider(new GitLogProvider(runner), new IgnoreCiSkip());
}
}

/**
* Provides means to get release dates
*/
public static ReleaseDateProvider getReleaseDateProvider(ProcessRunner runner) {
return new DefaultReleaseDateProvider(runner);
}
}
Expand Up @@ -7,6 +7,9 @@ import spock.lang.Specification

class ConciseFormatterTest extends Specification {

/*
TODO SF add more useful information to the concise report (like number of contributions)
*/
def "formats notes"() {
def c = Stub(ContributionSet)
def i1 = [new DefaultImprovement(100, "Fixed issue", "http://issues/100", ["bugfix"], true),
Expand Down
Expand Up @@ -129,7 +129,7 @@ class DefaultFormatterTest extends Specification {
def is = [new DefaultImprovement(100, "Fix bug x", "http://issues/100", ["bug"], true)]
def contributions = new DefaultContributionSet({false} as Predicate).add(new GitCommit("a", "a", "m"))
when: def notes = f.formatVersion(new DefaultReleaseNotesData("2.0.1", date, contributions, is))
then: notes == """### 2.0.1 (2017-01-04 23:00 UTC)
then: notes == """### 2.0.1 (2017-01-04 23:00)
* Authors: 1
* Commits: 1
Expand Down
Expand Up @@ -5,23 +5,28 @@ import org.mockito.release.notes.improvements.ImprovementsProvider
import org.mockito.release.notes.model.ContributionSet
import org.mockito.release.notes.model.Improvement
import org.mockito.release.notes.vcs.ContributionsProvider
import org.mockito.release.notes.vcs.ReleaseDateProvider
import spock.lang.Ignore
import spock.lang.Specification

class DefaultReleaseNotesGeneratorTest extends Specification {

def contributionsProvider = Mock(ContributionsProvider)
def improvementsProvider = Mock(ImprovementsProvider)
def gen = new DefaultReleaseNotesGenerator(contributionsProvider, improvementsProvider)
def releaseDateProvider = Stub(ReleaseDateProvider)
def gen = new DefaultReleaseNotesGenerator(contributionsProvider, improvementsProvider, releaseDateProvider)

def "generates release notes"() {
def c1 = Stub(ContributionSet), c2 = Stub(ContributionSet)
def i1 = [Stub(Improvement)], i2 = [Stub(Improvement)]
def date1 = new Date(1487000000000), date2 = new Date(1488000000000)

when:
def notes = gen.generateReleaseNotes(["1.2.0", "1.1.0", "1.0.0"], "v", ["bugfix"], true)

then:
releaseDateProvider.getReleaseDates(["1.2.0", "1.1.0", "1.0.0"], "v") >> ["1.2.0": date1, "1.1.0": date2]

1 * contributionsProvider.getContributionsBetween("v1.1.0", "v1.2.0") >> c1
1 * contributionsProvider.getContributionsBetween("v1.0.0", "v1.1.0") >> c2
0 * contributionsProvider._
Expand All @@ -32,7 +37,9 @@ class DefaultReleaseNotesGeneratorTest extends Specification {

notes.size() == 2
notes[0].version == "1.2.0"
notes[0].date == date1
notes[1].version == "1.1.0"
notes[1].date == date2
}

def "generates single release notes with no tag prefix"() {
Expand Down
@@ -0,0 +1,23 @@
package org.mockito.release.notes.internal

import spock.lang.Specification

class DateFormatTest extends Specification {

def "parses date"() {
def date = DateFormat.parseDate("2017-01-29 08:14:09 -0800")

expect:
//Ensure that the TZ is correct
DateFormat.formatDate(date) == "2017-01-29 16:14"
}

def "throws meaningful exception when date cannot be parsed"() {
when:
DateFormat.parseDate("2017-01- 08:14:09 -0800")

then:
def ex = thrown(RuntimeException)
ex.message.contains("2017-01- 08:14:09 -0800")
}
}
@@ -0,0 +1,29 @@
package org.mockito.release.notes.vcs

import org.mockito.release.exec.ProcessRunner
import org.mockito.release.notes.internal.DateFormat
import spock.lang.Specification

class DefaultReleaseDateProviderTest extends Specification {

def runner = Mock(ProcessRunner)
def provider = new DefaultReleaseDateProvider(runner)

def "no versions"() {
expect:
provider.getReleaseDates([], "v").isEmpty()
}

def "provides release dates"() {
runner.run("git", "log", "--pretty=%ad", "--date=iso", "v1.0.0", "-n", "1") >> "\n2017-01-29 08:14:09 -0800\n"
runner.run("git", "log", "--pretty=%ad", "--date=iso", "v2.0.0", "-n", "1") >> "\n2017-01-30 10:14:09 -0400\n"

when:
def dates = provider.getReleaseDates(["1.0.0", "2.0.0"], "v")

then:
dates.size() == 2
DateFormat.formatDate(dates["1.0.0"]) == "2017-01-29 16:14"
DateFormat.formatDate(dates["2.0.0"]) == "2017-01-30 14:14"
}
}

0 comments on commit 1cdc8ed

Please sign in to comment.