Skip to content

Commit 9d7eaa9

Browse files
authored
release notes maven plugin (for Jersey) (#4290)
Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
1 parent 43adb96 commit 9d7eaa9

File tree

6 files changed

+586
-0
lines changed

6 files changed

+586
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[//]: # " Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. "
2+
[//]: # " "
3+
[//]: # " This program and the accompanying materials are made available under the "
4+
[//]: # " terms of the Eclipse Public License v. 2.0, which is available at "
5+
[//]: # " http://www.eclipse.org/legal/epl-2.0. "
6+
[//]: # " "
7+
[//]: # " This Source Code may also be made available under the following Secondary "
8+
[//]: # " Licenses when the conditions for such availability set forth in the "
9+
[//]: # " Eclipse Public License v. 2.0 are satisfied: GNU General Public License, "
10+
[//]: # " version 2 with the GNU Classpath Exception, which is available at "
11+
[//]: # " https://www.gnu.org/software/classpath/license.html. "
12+
[//]: # " "
13+
[//]: # " SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 "
14+
15+
### Release Notes maven plugin
16+
17+
Runs for post-site mvn target, generates release notes for given template file,
18+
stores generated release notes in target/release-notes/{versionNumber}.html file (path of output file can be changed)
19+
20+
### Input parameters:
21+
22+
- releaseVersion - (String) - version to be used everywhere where it occurs. Like file name, replacement tag, release tag etc.
23+
supplied in for of '2.29.1'
24+
- githubApiUrl - (String) - short relative path to github api to which release notes shall be published (like eclipse-ee4j/jersey)
25+
- githubLogin - (String) - login of github user used to publish release notes to GitHub. Not used in dry run or not publish to GitHub modes.
26+
- githubToken - (String) - token of github user while two factor authentication is used.
27+
Used to publish release notes to GitHub. Not used in dry run or not publish to GitHub modes.
28+
- githubPassword - (String) - password of github user while simple authentication is used.
29+
Used to publish release notes to GitHub. Not used in dry run or not publish to GitHub modes.
30+
- publishToGithub - (Boolean) - whether or not publish generated release notes directly to GitHub (false by default)
31+
- dryRun - (Boolean) - whether to modify anything (false) or not (true). True by default
32+
- templateFilePath - (String) - template HTML file which is used to generate HTML release notes page
33+
- releaseDate - (String) - date of release (like 10-11-2019 or 10-NOV-2019 or any valid date format)
34+
- releaseNotesFilePath - (String) - path for output file to be stored (default target/relese-notes/)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
5+
6+
This program and the accompanying materials are made available under the
7+
terms of the Eclipse Public License v. 2.0, which is available at
8+
http://www.eclipse.org/legal/epl-2.0.
9+
10+
This Source Code may also be made available under the following Secondary
11+
Licenses when the conditions for such availability set forth in the
12+
Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
13+
version 2 with the GNU Classpath Exception, which is available at
14+
https://www.gnu.org/software/classpath/license.html.
15+
16+
SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
17+
18+
-->
19+
20+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
22+
<modelVersion>4.0.0</modelVersion>
23+
24+
<parent>
25+
<groupId>org.eclipse.ee4j</groupId>
26+
<artifactId>project</artifactId>
27+
<version>1.0.5</version>
28+
</parent>
29+
30+
<groupId>org.glassfish.jersey.tools.plugins</groupId>
31+
<artifactId>jersey-release-notes-maven-plugin</artifactId>
32+
<packaging>maven-plugin</packaging>
33+
<version>1.0.1</version>
34+
<name>jersey-release-notes-maven-plugin</name>
35+
36+
<description>
37+
Publishes release notes for Jersey to GitHub
38+
</description>
39+
40+
<dependencies>
41+
<dependency>
42+
<groupId>org.apache.maven</groupId>
43+
<artifactId>maven-plugin-api</artifactId>
44+
<version>${maven.version}</version>
45+
</dependency>
46+
<dependency>
47+
<groupId>org.apache.maven</groupId>
48+
<artifactId>maven-core</artifactId>
49+
<version>${maven.version}</version>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.apache.maven</groupId>
53+
<artifactId>maven-artifact</artifactId>
54+
<version>${maven.version}</version>
55+
</dependency>
56+
<dependency>
57+
<groupId>org.apache.maven.plugin-tools</groupId>
58+
<artifactId>maven-plugin-annotations</artifactId>
59+
<version>3.6.0</version>
60+
<scope>provided</scope>
61+
</dependency>
62+
<dependency>
63+
<groupId>org.kohsuke</groupId>
64+
<artifactId>github-api</artifactId>
65+
<version>1.95</version>
66+
</dependency>
67+
<dependency>
68+
<groupId>org.apache.maven.plugin-testing</groupId>
69+
<artifactId>maven-plugin-testing-harness</artifactId>
70+
<version>3.3.0</version>
71+
<scope>test</scope>
72+
</dependency>
73+
74+
<dependency>
75+
<groupId>commons-io</groupId>
76+
<artifactId>commons-io</artifactId>
77+
<version>2.6</version>
78+
</dependency>
79+
<dependency>
80+
<groupId>org.apache.maven</groupId>
81+
<artifactId>maven-compat</artifactId>
82+
<version>3.6.0</version>
83+
<scope>test</scope>
84+
</dependency>
85+
<dependency>
86+
<groupId>junit</groupId>
87+
<artifactId>junit</artifactId>
88+
<version>4.12</version>
89+
<scope>test</scope>
90+
</dependency>
91+
92+
</dependencies>
93+
94+
<build>
95+
<plugins>
96+
<plugin>
97+
<groupId>org.apache.maven.plugins</groupId>
98+
<artifactId>maven-plugin-plugin</artifactId>
99+
<version>3.6.0</version>
100+
<configuration>
101+
<skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
102+
</configuration>
103+
<executions>
104+
<execution>
105+
<id>default-descriptor</id>
106+
<goals>
107+
<goal>descriptor</goal>
108+
</goals>
109+
<phase>process-classes</phase>
110+
</execution>
111+
</executions>
112+
</plugin>
113+
<plugin>
114+
<groupId>org.apache.maven.plugins</groupId>
115+
<artifactId>maven-compiler-plugin</artifactId>
116+
<version>3.1</version>
117+
<inherited>true</inherited>
118+
<configuration>
119+
<source>${java.version}</source>
120+
<target>${java.version}</target>
121+
<showWarnings>false</showWarnings>
122+
<fork>false</fork>
123+
</configuration>
124+
</plugin>
125+
</plugins>
126+
</build>
127+
128+
<properties>
129+
<java.version>1.8</java.version>
130+
<maven.version>3.6.2</maven.version>
131+
</properties>
132+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
* Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package org.glassfish.jersey.tools.plugins.releasenotes;
18+
19+
import org.apache.maven.plugin.AbstractMojo;
20+
import org.apache.maven.plugin.MojoExecutionException;
21+
import org.apache.maven.plugin.MojoFailureException;
22+
import org.apache.maven.plugin.logging.Log;
23+
import org.apache.maven.plugins.annotations.LifecyclePhase;
24+
import org.apache.maven.plugins.annotations.Mojo;
25+
import org.apache.maven.plugins.annotations.Parameter;
26+
import org.kohsuke.github.GHIssue;
27+
import org.kohsuke.github.GHIssueState;
28+
import org.kohsuke.github.GHMilestone;
29+
import org.kohsuke.github.GHRepository;
30+
import org.kohsuke.github.GitHub;
31+
import org.kohsuke.github.PagedIterable;
32+
33+
import java.io.IOException;
34+
import java.nio.charset.Charset;
35+
import java.nio.file.Files;
36+
import java.nio.file.Paths;
37+
import java.util.ArrayList;
38+
import java.util.List;
39+
40+
@Mojo(name = "release-notes", defaultPhase = LifecyclePhase.POST_SITE)
41+
public class ReleaseNotesMojo extends AbstractMojo {
42+
43+
/**
44+
* Version for release notes to be used for generated name and content of release notes
45+
* IMPORTANT - version shall be the same as milestone at GitHub issue tracker
46+
*/
47+
@Parameter(required = true, defaultValue = "2.29.1", property = "releaseVersion")
48+
private String releaseVersion;
49+
50+
/**
51+
* Short path to GitHub issue tracker (like eclipse-ee4j/jersey). Not full url.
52+
*/
53+
@Parameter(required = true, defaultValue = "eclipse-ee4j/jersey", property = "githubApiUrl")
54+
private String githubApiUrl;
55+
56+
/**
57+
* Login to GitHub (related to githubApiPath).
58+
* if DRY_RUN or !publishToGitHub is used the property may be NULL
59+
*/
60+
@Parameter(property = "githubLogin")
61+
private String githubLogin;
62+
63+
/**
64+
* Token for login (if two factor auth is used, empty otherwise)
65+
* if DRY_RUN or !publishToGitHub is used the property may be NULL
66+
*/
67+
@Parameter(property = "githubToken")
68+
private String githubToken;
69+
70+
/**
71+
* GitHub password (if simple auth is used, empty otherwise)
72+
* if DRY_RUN or !publishToGitHub is used the property may be NULL
73+
*/
74+
@Parameter(property = "githubPassword")
75+
private String githubPassword;
76+
77+
/**
78+
* Flag which allows direct publishing of notes to github. (RELEASE TAG)
79+
* Is FALSE by default
80+
*/
81+
@Parameter(required = true, defaultValue = "false", property = "publishToGithub")
82+
private Boolean publishToGithub;
83+
84+
/**
85+
* Dry run mode - no publishing, no creating of release notes file.
86+
* TRUE by default
87+
*/
88+
@Parameter(required = true, defaultValue = "true", property = "dryRun")
89+
private Boolean dryRun;
90+
91+
/**
92+
* Path to HTML template which shall be used for substitution of values to generate release notes
93+
*/
94+
@Parameter(required = true, property = "templateFilePath")
95+
private String templateFilePath;
96+
97+
/**
98+
* Date of release - only used inside HTML file which is generated by template
99+
*/
100+
@Parameter(required = true, property = "releaseDate")
101+
private String releaseDate;
102+
103+
/**
104+
* Path to store generated release notes. The name of file is [releaseVersion].html.
105+
* Default is target/release-notes/
106+
*/
107+
@Parameter(required = true, defaultValue = "target/release-notes", property = "releaseNotesFilePath")
108+
private String releaseNotesFilePath;
109+
110+
private static final String RELEASE_DATE_PATTERN = "@RELEASE_DATE@";
111+
private static final String LATEST_VERSION_PATTERN = "@LATEST_VERSION@";
112+
113+
public void execute() throws MojoExecutionException, MojoFailureException {
114+
validateParameters();
115+
try {
116+
final GitHub github = (dryRun || !publishToGithub) ? GitHub.connectAnonymously() :
117+
(githubPassword != null ? GitHub.connectUsingPassword(githubLogin, githubPassword) :
118+
GitHub.connect(githubLogin, githubToken));
119+
final GHRepository repository = github.getRepository(githubApiUrl);
120+
final PagedIterable<GHMilestone> milestones = repository.listMilestones(GHIssueState.ALL);
121+
for (final GHMilestone milestone : milestones) {
122+
if (releaseVersion.equalsIgnoreCase(milestone.getTitle())) {
123+
getLog().info(String.format("Milestone found for release version: %s", releaseVersion));
124+
processMilestone(releaseVersion, milestone, repository, publishToGithub, dryRun, templateFilePath,
125+
releaseDate, releaseNotesFilePath, getLog());
126+
}
127+
}
128+
} catch (IOException e) {
129+
throw new MojoExecutionException(e.getMessage(), e);
130+
}
131+
}
132+
133+
private static void processMilestone(String releaseVersion, GHMilestone milestone,
134+
GHRepository repository, Boolean publishToGithub,
135+
Boolean dryRun, String templateFilePath, String releaseDate,
136+
String releaseNotesFilePath, Log log
137+
) throws MojoExecutionException {
138+
try {
139+
final String releaseNotes = prepareReleaseNotes(milestone, repository);
140+
log.info("Prepared release notes:");
141+
log.info(releaseNotes);
142+
if (Boolean.TRUE.equals(publishToGithub) && Boolean.FALSE.equals(dryRun)) {
143+
log.info("Publishing release notes to GitHub");
144+
publishReleaseNotes(releaseNotes, releaseVersion, repository);
145+
} else {
146+
log.info("Publishing to GitHub is disabled, skipping");
147+
}
148+
storeReleaseNotes(releaseNotes, templateFilePath, releaseVersion,
149+
releaseDate, releaseNotesFilePath, dryRun, log);
150+
} catch (IOException e) {
151+
throw new MojoExecutionException(e.getMessage(), e);
152+
}
153+
}
154+
155+
private static String prepareReleaseNotes(GHMilestone milestone, GHRepository repository) throws IOException {
156+
final StringBuffer releaseNotes = new StringBuffer();
157+
final String pullRequestFormat = "<li>[<a href='%s'>Pull %d</a>] - %s</li>\n";
158+
final String issueFormat = "<li>[<a href='%s'>Issue %d</a>] - %s</li>\n";
159+
// final List<String> releaseNotesLines = new ArrayList<>();
160+
final List<GHIssue> issues = repository.getIssues(GHIssueState.CLOSED, milestone);
161+
issues.stream().map(issue -> String.format(issue.isPullRequest() ? pullRequestFormat : issueFormat,
162+
issue.getHtmlUrl(), issue.getNumber(), issue.getTitle())).sorted().forEach(releaseNotes::append);
163+
// releaseNotesLines.stream().sorted().forEach(releaseNotes::append);
164+
return releaseNotes.toString();
165+
}
166+
167+
private static void publishReleaseNotes(String releaseNotes, String releaseVersion, GHRepository repository) throws IOException {
168+
repository.createRelease(releaseVersion).name(releaseVersion).body(releaseNotes).create();
169+
}
170+
171+
private static void storeReleaseNotes(String releaseNotes, String templateFilePath,
172+
String releaseVersion, String releaseDate,
173+
String releaseNotesFilePath,
174+
Boolean dryRun, Log log) throws IOException {
175+
if (Files.notExists(Paths.get(templateFilePath))) {
176+
log.warn(String.format("There is no source template file at the given location:%s", templateFilePath));
177+
return;
178+
}
179+
final List<String> notesLines = new ArrayList<>();
180+
final List<String> lines = Files.readAllLines(Paths.get(templateFilePath), Charset.defaultCharset());
181+
for (final String line : lines) {
182+
if (line.contains(RELEASE_DATE_PATTERN)) {
183+
notesLines.add(line.replace(RELEASE_DATE_PATTERN, releaseDate));
184+
} else if (line.contains(LATEST_VERSION_PATTERN)) {
185+
notesLines.add(line.replace(LATEST_VERSION_PATTERN, releaseVersion));
186+
} else if (line.contains("<h2>Previous releases</h2>")) {
187+
notesLines.add("<h2>Pull requests and issues</h2>\n<ul>");
188+
notesLines.add(releaseNotes);
189+
notesLines.add("</ul>");
190+
notesLines.add(line);
191+
} else if (line.contains("<ul>")) {
192+
notesLines.add(line);
193+
notesLines.add(String.format(" <li><a href=\"%s.html\">Jersey %s Release Notes</a></li>", releaseVersion, releaseVersion));
194+
} else {
195+
notesLines.add(line);
196+
}
197+
}
198+
if (Boolean.FALSE.equals(dryRun)) {
199+
log.info(String.format("Storing release notes to file %s/%s.html", releaseNotesFilePath, releaseVersion));
200+
Files.createDirectories(Paths.get(releaseNotesFilePath));
201+
Files.write(Paths.get(String.format("%s/%s.html", releaseNotesFilePath, releaseVersion)), notesLines, Charset.defaultCharset());
202+
} else {
203+
log.info("Prepared release notes are not stored to file due to dryRun mode");
204+
log.info(String.format("File path to store release notes is: %s/%s.html", releaseNotesFilePath, releaseVersion));
205+
}
206+
}
207+
208+
private void validateParameters() throws MojoFailureException {
209+
if (releaseVersion == null) {
210+
throw new MojoFailureException("releaseVersion shall be provided");
211+
}
212+
if (Boolean.FALSE.equals(dryRun) || Boolean.TRUE.equals(publishToGithub)) {
213+
if (githubLogin == null) {
214+
throw new MojoFailureException("githubLogin shall be provided");
215+
}
216+
if (githubPassword == null && githubToken == null) {
217+
throw new MojoFailureException("Either githubPassword or githubToken shall be provided");
218+
}
219+
}
220+
}
221+
}

0 commit comments

Comments
 (0)