View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugins.changes.announcement;
20  
21  import javax.inject.Inject;
22  
23  import java.io.File;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.OutputStreamWriter;
27  import java.io.Writer;
28  import java.nio.charset.Charset;
29  import java.util.ArrayList;
30  import java.util.Collections;
31  import java.util.List;
32  import java.util.Map;
33  
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugins.annotations.Mojo;
36  import org.apache.maven.plugins.annotations.Parameter;
37  import org.apache.maven.plugins.changes.ChangesXML;
38  import org.apache.maven.plugins.changes.IssueAdapter;
39  import org.apache.maven.plugins.changes.ProjectUtils;
40  import org.apache.maven.plugins.changes.ReleaseUtils;
41  import org.apache.maven.plugins.changes.github.GitHubDownloader;
42  import org.apache.maven.plugins.changes.github.GitHubIssueManagementSystem;
43  import org.apache.maven.plugins.changes.issues.Issue;
44  import org.apache.maven.plugins.changes.issues.IssueManagementSystem;
45  import org.apache.maven.plugins.changes.issues.IssueUtils;
46  import org.apache.maven.plugins.changes.jira.JIRAIssueManagementSystem;
47  import org.apache.maven.plugins.changes.jira.RestJiraDownloader;
48  import org.apache.maven.plugins.changes.model.Release;
49  import org.apache.maven.plugins.changes.trac.TracDownloader;
50  import org.apache.maven.plugins.changes.trac.TracIssueManagmentSystem;
51  import org.apache.maven.project.MavenProject;
52  import org.apache.maven.settings.Settings;
53  import org.apache.maven.settings.crypto.SettingsDecrypter;
54  import org.apache.velocity.Template;
55  import org.apache.velocity.app.VelocityEngine;
56  import org.apache.velocity.context.Context;
57  import org.apache.velocity.exception.ResourceNotFoundException;
58  import org.apache.velocity.exception.VelocityException;
59  import org.apache.velocity.tools.ToolManager;
60  import org.codehaus.plexus.velocity.VelocityComponent;
61  
62  /**
63   * Goal which generates an announcement from the announcement template.
64   *
65   * @author aramirez@exist.com
66   * @version $Id$
67   * @since 2.0-beta-2
68   */
69  @Mojo(name = "announcement-generate", threadSafe = true)
70  public class AnnouncementMojo extends AbstractAnnouncementMojo {
71      private static final String CHANGES_XML = "changes.xml";
72  
73      private static final String JIRA = "JIRA";
74  
75      private static final String TRAC = "Trac";
76  
77      private static final String GIT_HUB = "GitHub";
78  
79      /**
80       * The name of the file which will contain the generated announcement. If no value is specified, the plugin will use
81       * the name of the template.
82       *
83       * @since 2.4
84       */
85      @Parameter(property = "changes.announcementFile")
86      private String announcementFile;
87  
88      /**
89       * Map of custom parameters for the announcement. This Map will be passed to the template.
90       *
91       * @since 2.1
92       */
93      @Parameter
94      private Map<Object, Object> announceParameters;
95  
96      /**
97       */
98      @Parameter(property = "project.artifactId", readonly = true)
99      private String artifactId;
100 
101     /**
102      * Name of the team that develops the artifact. This parameter will be passed to the template.
103      */
104     @Parameter(property = "changes.developmentTeam", defaultValue = "${project.name} team", required = true)
105     private String developmentTeam;
106 
107     /**
108      * The name of the artifact to be used in the announcement.
109      */
110     @Parameter(property = "changes.finalName", defaultValue = "${project.build.finalName}", required = true)
111     private String finalName;
112 
113     /**
114      */
115     @Parameter(property = "project.groupId", readonly = true)
116     private String groupId;
117 
118     /**
119      * Short description or introduction of the released artifact. This parameter will be passed to the template.
120      */
121     @Parameter(defaultValue = "${project.description}")
122     private String introduction;
123 
124     /**
125      * A list of issue management systems to fetch releases from. This parameter replaces the parameters
126      * <code>generateJiraAnnouncement</code> and <code>jiraMerge</code>.
127      * <p>
128      * Valid values are: <code>changes.xml</code> and <code>JIRA</code>.
129      * </p>
130      * <strong>Note:</strong> Only one issue management system that is configured in
131      * &lt;project&gt;/&lt;issueManagement&gt; can be used. This currently means that you can combine a changes.xml file
132      * with one other issue management system.
133      *
134      * @since 2.4
135      */
136     @Parameter
137     private List<String> issueManagementSystems;
138 
139     /**
140      * Maps issues types to action types for grouping issues in announcements. If issue types are not defined for a
141      * action type then the default issue type will be applied.
142      * <p>
143      * Valid action types: <code>add</code>, <code>fix</code> and <code>update</code>.
144      * </p>
145      *
146      * @since 2.6
147      */
148     @Parameter
149     private Map<String, String> issueTypes;
150 
151     /**
152      * Directory where the announcement file will be generated.
153      *
154      * @since 2.10
155      */
156     @Parameter(defaultValue = "${project.build.directory}/announcement", required = true)
157     private File announcementDirectory;
158 
159     /**
160      * Packaging structure for the artifact.
161      */
162     @Parameter(property = "project.packaging", readonly = true)
163     private String packaging;
164 
165     /**
166      * The Maven Project.
167      */
168     @Parameter(defaultValue = "${project}", readonly = true, required = true)
169     private MavenProject project;
170 
171     /**
172      * The Velocity template used to format the announcement.
173      */
174     @Parameter(property = "changes.template", defaultValue = "announcement.vm", required = true)
175     private String template;
176 
177     /**
178      * Directory that contains the template.
179      * <p>
180      * <b>Note:</b> This directory must be a subdirectory of
181      * <code>/src/main/resources/ or current project base directory</code>.
182      * </p>
183      */
184     // CHECKSTYLE_OFF: LineLength
185     @Parameter(
186             property = "changes.templateDirectory",
187             defaultValue = "org/apache/maven/plugins/changes/announcement",
188             required = true)
189     private String templateDirectory;
190     // CHECKSTYLE_ON: LineLength
191 
192     /**
193      * The template encoding.
194      *
195      * @since 2.1
196      */
197     @Parameter(property = "changes.templateEncoding", defaultValue = "${project.build.sourceEncoding}")
198     private String templateEncoding;
199 
200     /**
201      * Obsolete, since REST queries always use JQL.
202      *
203      * @since 2.10
204      * @deprecated ignored; remove from your configs
205      */
206     @Deprecated
207     @Parameter(property = "changes.useJql", defaultValue = "false")
208     private boolean useJql;
209 
210     /**
211      * Distribution URL of the artifact. This parameter will be passed to the template.
212      */
213     @Parameter(property = "project.url")
214     private String url;
215 
216     /**
217      * URL where the artifact can be downloaded. If not specified, no URL is used. This parameter will be passed to the
218      * template.
219      */
220     @Parameter
221     private String urlDownload;
222 
223     /**
224      * Version of the artifact.
225      */
226     @Parameter(property = "changes.version", defaultValue = "${project.version}", required = true)
227     private String version;
228 
229     /**
230      * The path of the changes.xml file.
231      */
232     @Parameter(defaultValue = "${basedir}/src/changes/changes.xml")
233     private File xmlPath;
234 
235     // =======================================//
236     // JIRA-Announcement Needed Parameters //
237     // =======================================//
238 
239     /**
240      * Defines the filter parameters to restrict which issues are retrieved from JIRA. The filter parameter uses the
241      * same format of url parameters that is used in a JIRA search.
242      *
243      * @since 2.4
244      */
245     @Parameter
246     private String filter;
247 
248     /**
249      * Defines the JIRA password for authentication into a private JIRA installation.
250      *
251      * @since 2.1
252      */
253     @Parameter(property = "changes.jiraPassword")
254     private String jiraPassword;
255 
256     /**
257      * Defines the JIRA username for authentication into a private JIRA installation.
258      *
259      * @since 2.1
260      */
261     @Parameter(property = "changes.jiraUser")
262     private String jiraUser;
263 
264     /**
265      * The settings.xml server id to be used for authentication into a private JIRA installation.
266      *
267      * @since 3.0.0
268      */
269     @Parameter(property = "changes.jiraServerId")
270     private String jiraServerId;
271 
272     /**
273      * The maximum number of issues to fetch from JIRA.
274      */
275     @Parameter(property = "changes.maxEntries", defaultValue = "25", required = true)
276     private int maxEntries;
277 
278     /**
279      * If you only want to show issues from JIRA for the current version in the report. The current version being used is
280      * <code>${project.version}</code> minus any "-SNAPSHOT" suffix.
281      *
282      * @since 3.0.0
283      */
284     @Parameter(defaultValue = "false")
285     private boolean onlyCurrentVersion;
286 
287     /**
288      * Include issues from JIRA with these resolution ids. Multiple resolution ids can be specified as a comma separated
289      * list of ids.
290      * <p>
291      * <b>Note:</b> In versions 2.0-beta-3 and earlier this parameter was called "resolutionId".
292      * </p>
293      */
294     @Parameter(property = "changes.resolutionIds", defaultValue = "Fixed")
295     private String resolutionIds;
296 
297     /**
298      * Settings XML configuration.
299      */
300     @Parameter(defaultValue = "${settings}", readonly = true, required = true)
301     private Settings settings;
302 
303     /**
304      * Include issues from JIRA with these status ids. Multiple status ids can be specified as a comma separated list of
305      * ids.
306      */
307     @Parameter(property = "changes.statusIds", defaultValue = "Closed")
308     private String statusIds;
309 
310     /**
311      * Defines the http user for basic authentication into the JIRA webserver.
312      *
313      * @since 2.4
314      * @deprecated use {@link #jiraUser} or {@link #jiraServerId}
315      */
316     @Deprecated
317     @Parameter(property = "changes.webUser")
318     private String webUser;
319 
320     /**
321      * Defines the http password for basic authentication into the JIRA webserver.
322      *
323      * @since 2.4
324      * @deprecated use {@link #jiraPassword} or {@link #jiraServerId}
325      */
326     @Deprecated
327     @Parameter(property = "changes.webPassword")
328     private String webPassword;
329 
330     /**
331      * The prefix used when naming versions in JIRA.
332      * <p>
333      * If you have a project in JIRA with several components that have different release cycles, it is an often used
334      * pattern to prefix the version with the name of the component, e.g. maven-filtering-1.0 etc. To fetch issues from
335      * JIRA for a release of the "maven-filtering" component you would need to set this parameter to "maven-filtering-".
336      * </p>
337      *
338      * @since 2.5
339      */
340     @Parameter(property = "changes.versionPrefix")
341     private String versionPrefix;
342 
343     /**
344      * Defines the connection timeout in milliseconds when accessing JIRA's REST-API.
345      * <p>
346      * Might help when you have a lot of different resolutions in your JIRA instance.
347      * </p>
348      *
349      * @since 2.11
350      */
351     @Parameter(property = "changes.jiraConnectionTimeout", defaultValue = "36000")
352     private int jiraConnectionTimeout;
353 
354     /**
355      * Defines the receive timeout in milliseconds when accessing JIRA's REST-API.
356      * <p>
357      * Might help when you have a lot of different resolutions in your JIRA instance.
358      * </p>
359      *
360      * @since 2.11
361      */
362     @Parameter(property = "changes.jiraReceiveTimout", defaultValue = "32000")
363     private int jiraReceiveTimout;
364 
365     // =======================================//
366     // Trac Parameters //
367     // =======================================//
368 
369     /**
370      * Defines the Trac password for authentication into a private Trac installation.
371      *
372      * @since 2.4
373      */
374     @Parameter(property = "changes.tracPassword")
375     private String tracPassword;
376 
377     /**
378      * Defines the Trac query for searching for tickets.
379      *
380      * @since 2.4
381      */
382     @Parameter(defaultValue = "order=id")
383     private String tracQuery;
384 
385     /**
386      * Defines the Trac username for authentication into a private Trac installation.
387      *
388      * @since 2.4
389      */
390     @Parameter(property = "changes.tracUser")
391     private String tracUser;
392 
393     // =======================================//
394     // Github Parameters //
395     // =======================================//
396 
397     /**
398      * The settings.xml server id to be used to authenticate into GitHub Api.
399      * <br>
400      * Since 3.x - only password item is used as authentication token with {@code Authorization: Bearer YOUR-TOKEN}
401      * <a href="https://docs.github.com/en/rest/authentication/authenticating-to-the-rest-api">Authenticating to the REST API</a>
402      *
403      * @since 2.12
404      */
405     @Parameter(defaultValue = "github")
406     private String githubAPIServerId;
407 
408     /**
409      * Boolean which says if we should include open github issues in the announcement.
410      */
411     @Parameter(defaultValue = "false")
412     private boolean includeOpenIssues;
413 
414     private ChangesXML xml;
415 
416     /**
417      * Velocity Component.
418      */
419     private VelocityComponent velocity;
420 
421     /**
422      * Component used to decrypt server information.
423      */
424     private final SettingsDecrypter settingsDecrypter;
425 
426     @Inject
427     public AnnouncementMojo(VelocityComponent velocity, SettingsDecrypter settingsDecrypter) {
428         this.velocity = velocity;
429         this.settingsDecrypter = settingsDecrypter;
430     }
431 
432     // =======================================//
433     // announcement-generate execution //
434     // =======================================//
435 
436     /**
437      * Generate the template
438      *
439      * @throws MojoExecutionException in case of errors
440      */
441     public void execute() throws MojoExecutionException {
442         // Run only at the execution root
443         if (runOnlyAtExecutionRoot && !isThisTheExecutionRoot()) {
444             getLog().info("Skipping the announcement generation in this project because it's not the Execution Root");
445         } else {
446             if (issueManagementSystems == null) {
447                 issueManagementSystems = new ArrayList<>();
448             }
449 
450             if (issueManagementSystems.isEmpty()) {
451                 issueManagementSystems.add(CHANGES_XML);
452             }
453 
454             // Fetch releases from the configured issue management systems
455             List<Release> releases = null;
456             if (issueManagementSystems.contains(CHANGES_XML)) {
457                 if (getXmlPath().exists()) {
458                     ChangesXML changesXML = new ChangesXML(getXmlPath(), getLog());
459                     List<Release> changesReleases = changesXML.getReleaseList();
460                     releases = ReleaseUtils.mergeReleases(null, changesReleases);
461                     getLog().info("Including issues from file " + getXmlPath() + " in announcement...");
462                 } else {
463                     getLog().warn("changes.xml file " + getXmlPath().getAbsolutePath() + " does not exist.");
464                 }
465             }
466 
467             if (issueManagementSystems.contains(JIRA)) {
468                 String message = ProjectUtils.validateIssueManagement(project, JIRA, "JIRA announcement");
469                 if (message == null) {
470                     List<Release> jiraReleases = getJiraReleases();
471                     releases = ReleaseUtils.mergeReleases(releases, jiraReleases);
472                     getLog().info("Including issues from JIRA in announcement...");
473                 } else {
474                     throw new MojoExecutionException(
475                             "Something is wrong with the Issue Management section. " + message);
476                 }
477             }
478 
479             if (issueManagementSystems.contains(TRAC)) {
480                 getLog().warn(
481                                 "Trac integration is prepared for removal in next major version due to lack of maintainers");
482                 String message = ProjectUtils.validateIssueManagement(project, TRAC, "Trac announcement");
483                 if (message == null) {
484                     List<Release> tracReleases = getTracReleases();
485                     releases = ReleaseUtils.mergeReleases(releases, tracReleases);
486                     getLog().info("Including issues from Trac in announcement...");
487                 } else {
488                     throw new MojoExecutionException(
489                             "Something is wrong with the Issue Management section. " + message);
490                 }
491             }
492 
493             if (issueManagementSystems.contains(GIT_HUB)) {
494                 String message = ProjectUtils.validateIssueManagement(project, GIT_HUB, "GitHub announcement");
495                 if (message == null) {
496                     List<Release> gitHubReleases = getGitHubReleases();
497                     releases = ReleaseUtils.mergeReleases(releases, gitHubReleases);
498                     getLog().info("Including issues from GitHub in announcement...");
499                 } else {
500                     throw new MojoExecutionException(
501                             "Something is wrong with the Issue Management section. " + message);
502                 }
503             }
504 
505             // @todo Add more issue management systems here.
506 
507             // Follow these steps:
508             // 1. Add a constant for the name of the issue management system
509             // 2. Add the @parameters needed to configure the issue management system
510             // 3. Add a protected List get<IMSname>Releases() method that retrieves a list of releases
511             // 4. Merge those releases into the "releases" variable
512             // For help with these steps, you can have a look at how this has been done for JIRA or Trac
513 
514             // Generate the report
515             if (releases == null || releases.isEmpty()) {
516                 throw new MojoExecutionException(
517                         "No releases found in any of the " + "configured issue management systems.");
518             } else {
519                 doGenerate(releases);
520             }
521         }
522     }
523 
524     /**
525      * Add the parameters to velocity context
526      *
527      * @param releases A <code>List</code> of <code>Release</code>s
528      * @throws MojoExecutionException in case of errors
529      */
530     public void doGenerate(List<Release> releases) throws MojoExecutionException {
531         String version = (versionPrefix == null ? "" : versionPrefix) + getVersion();
532 
533         getLog().debug("Generating announcement for version [" + version + "]. Found these releases: "
534                 + ReleaseUtils.toString(releases));
535 
536         doGenerate(releases, ReleaseUtils.getLatestRelease(releases, version));
537     }
538 
539     protected void doGenerate(List<Release> releases, Release release) throws MojoExecutionException {
540         try {
541             ToolManager toolManager = new ToolManager(true);
542             Context context = toolManager.createContext();
543 
544             if (getIntroduction() == null || getIntroduction().equals("")) {
545                 setIntroduction(getUrl());
546             }
547 
548             context.put("releases", releases);
549 
550             context.put("groupId", getGroupId());
551 
552             context.put("artifactId", getArtifactId());
553 
554             context.put("version", getVersion());
555 
556             context.put("packaging", getPackaging());
557 
558             context.put("url", getUrl());
559 
560             context.put("release", release);
561 
562             context.put("introduction", getIntroduction());
563 
564             context.put("developmentTeam", getDevelopmentTeam());
565 
566             context.put("finalName", getFinalName());
567 
568             context.put("urlDownload", getUrlDownload());
569 
570             context.put("project", project);
571 
572             if (announceParameters == null) {
573                 // empty Map to prevent NPE in velocity execution
574                 context.put("announceParameters", Collections.emptyMap());
575             } else {
576                 context.put("announceParameters", announceParameters);
577             }
578 
579             processTemplate(context, announcementDirectory, template, announcementFile);
580         } catch (ResourceNotFoundException rnfe) {
581             throw new MojoExecutionException("Resource not found.", rnfe);
582         } catch (VelocityException ve) {
583             throw new MojoExecutionException(ve.toString(), ve);
584         }
585     }
586 
587     /**
588      * Create the velocity template.
589      *
590      * @param context velocity context that has the parameter values
591      * @param outputDirectory directory where the file will be generated
592      * @param template velocity template which will the context be merged
593      * @param announcementFile the file name of the generated announcement
594      * @throws VelocityException in case of error processing the Velocty template
595      * @throws MojoExecutionException in case of errors
596      */
597     public void processTemplate(Context context, File outputDirectory, String template, String announcementFile)
598             throws VelocityException, MojoExecutionException {
599 
600         // Use the name of the template as a default value
601         if (announcementFile == null || announcementFile.isEmpty()) {
602             announcementFile = template;
603         }
604 
605         if (!outputDirectory.exists()) {
606             if (!outputDirectory.mkdirs()) {
607                 throw new MojoExecutionException("Failed to create directory " + outputDirectory);
608             }
609         }
610 
611         File f = new File(outputDirectory, announcementFile);
612 
613         VelocityEngine engine = velocity.getEngine();
614 
615         engine.setApplicationAttribute("baseDirectory", basedir);
616 
617         if (templateEncoding == null || templateEncoding.isEmpty()) {
618             templateEncoding = Charset.defaultCharset().name();
619             getLog().warn("File encoding has not been set, using platform encoding " + templateEncoding
620                     + "; build is platform dependent!");
621         }
622         try (Writer writer = new OutputStreamWriter(new FileOutputStream(f), templateEncoding)) {
623             Template velocityTemplate = engine.getTemplate(templateDirectory + "/" + template, templateEncoding);
624             velocityTemplate.merge(context, writer);
625             getLog().info("Created template " + f);
626         } catch (ResourceNotFoundException ex) {
627             throw new ResourceNotFoundException(
628                     "Template not found. ( " + templateDirectory + "/" + template + " )", ex);
629         } catch (VelocityException ve) {
630             throw ve;
631         } catch (RuntimeException | IOException e) {
632             throw new MojoExecutionException(e.toString(), e);
633         }
634     }
635 
636     protected List<Release> getJiraReleases() throws MojoExecutionException {
637         RestJiraDownloader jiraDownloader = new RestJiraDownloader();
638 
639         jiraDownloader.setLog(getLog());
640 
641         jiraDownloader.setStatusIds(statusIds);
642 
643         jiraDownloader.setResolutionIds(resolutionIds);
644 
645         jiraDownloader.setMavenProject(project);
646 
647         jiraDownloader.setSettings(settings);
648         jiraDownloader.setSettingsDecrypter(settingsDecrypter);
649 
650         jiraDownloader.setNbEntries(maxEntries);
651         jiraDownloader.setOnlyCurrentVersion(onlyCurrentVersion);
652         jiraDownloader.setVersionPrefix(versionPrefix);
653 
654         jiraDownloader.setFilter(filter);
655 
656         jiraDownloader.setJiraServerId(jiraServerId);
657         if (jiraUser != null) {
658             jiraDownloader.setJiraUser(jiraUser);
659             jiraDownloader.setJiraPassword(jiraPassword);
660         } else if (webUser != null) {
661             jiraDownloader.setJiraUser(webUser);
662             jiraDownloader.setJiraPassword(webPassword);
663         }
664 
665         jiraDownloader.setConnectionTimeout(jiraConnectionTimeout);
666 
667         jiraDownloader.setReceiveTimout(jiraReceiveTimout);
668 
669         try {
670             jiraDownloader.doExecute();
671 
672             List<Issue> issueList = jiraDownloader.getIssueList();
673 
674             if (versionPrefix != null && !versionPrefix.isEmpty()) {
675                 int originalNumberOfIssues = issueList.size();
676                 issueList = IssueUtils.filterIssuesWithVersionPrefix(issueList, versionPrefix);
677                 getLog().debug("Filtered out " + issueList.size() + " issues of " + originalNumberOfIssues
678                         + " that matched the versionPrefix '" + versionPrefix + "'.");
679             }
680 
681             if (onlyCurrentVersion) {
682                 String version = (versionPrefix == null ? "" : versionPrefix) + project.getVersion();
683                 issueList = IssueUtils.getIssuesForVersion(issueList, version);
684                 getLog().debug("The JIRA Report will contain issues only for the current version.");
685             }
686 
687             return getReleases(issueList, new JIRAIssueManagementSystem());
688         } catch (Exception e) {
689             throw new MojoExecutionException("Failed to extract issues from JIRA.", e);
690         }
691     }
692 
693     private List<Release> getReleases(List<Issue> issues, IssueManagementSystem ims) throws MojoExecutionException {
694         if (issueTypes != null) {
695             ims.applyConfiguration(issueTypes);
696         }
697         if (issues.isEmpty()) {
698             return Collections.emptyList();
699         } else {
700             IssueAdapter adapter = new IssueAdapter(ims);
701             return adapter.getReleases(issues);
702         }
703     }
704 
705     protected List<Release> getTracReleases() throws MojoExecutionException {
706         TracDownloader issueDownloader = new TracDownloader();
707 
708         issueDownloader.setProject(project);
709 
710         issueDownloader.setQuery(tracQuery);
711 
712         issueDownloader.setTracPassword(tracPassword);
713 
714         issueDownloader.setTracUser(tracUser);
715 
716         try {
717             return getReleases(issueDownloader.getIssueList(), new TracIssueManagmentSystem());
718         } catch (Exception e) {
719             throw new MojoExecutionException("Failed to extract issues from Trac.", e);
720         }
721     }
722 
723     protected List<Release> getGitHubReleases() throws MojoExecutionException {
724         try {
725             GitHubDownloader issueDownloader = new GitHubDownloader(project, includeOpenIssues, true);
726 
727             issueDownloader.configureAuthentication(settingsDecrypter, githubAPIServerId, settings, getLog());
728 
729             return getReleases(issueDownloader.getIssueList(), new GitHubIssueManagementSystem());
730         } catch (Exception e) {
731             throw new MojoExecutionException("Failed to extract issues from GitHub.", e);
732         }
733     }
734 
735     /*
736      * accessors
737      */
738 
739     public String getArtifactId() {
740         return artifactId;
741     }
742 
743     public void setArtifactId(String artifactId) {
744         this.artifactId = artifactId;
745     }
746 
747     public String getDevelopmentTeam() {
748         return developmentTeam;
749     }
750 
751     public void setDevelopmentTeam(String developmentTeam) {
752         this.developmentTeam = developmentTeam;
753     }
754 
755     public String getFinalName() {
756         return finalName;
757     }
758 
759     public void setFinalName(String finalName) {
760         this.finalName = finalName;
761     }
762 
763     public String getGroupId() {
764         return groupId;
765     }
766 
767     public void setGroupId(String groupId) {
768         this.groupId = groupId;
769     }
770 
771     public String getIntroduction() {
772         return introduction;
773     }
774 
775     public void setIntroduction(String introduction) {
776         this.introduction = introduction;
777     }
778 
779     public void setIssueTypes(Map<String, String> issueTypes) {
780         this.issueTypes = issueTypes;
781     }
782 
783     public Map<String, String> getIssueTypes() {
784         return issueTypes;
785     }
786 
787     public File getAnnouncementDirectory() {
788         return announcementDirectory;
789     }
790 
791     public void setAnnouncementDirectory(File announcementDirectory) {
792         this.announcementDirectory = announcementDirectory;
793     }
794 
795     public String getPackaging() {
796         return packaging;
797     }
798 
799     public void setPackaging(String packaging) {
800         this.packaging = packaging;
801     }
802 
803     public String getUrl() {
804         return url;
805     }
806 
807     public void setUrl(String url) {
808         this.url = url;
809     }
810 
811     public String getUrlDownload() {
812         return urlDownload;
813     }
814 
815     public void setUrlDownload(String urlDownload) {
816         this.urlDownload = urlDownload;
817     }
818 
819     public String getVersion() {
820         return version;
821     }
822 
823     public void setVersion(String version) {
824         this.version = version;
825     }
826 
827     public ChangesXML getXml() {
828         return xml;
829     }
830 
831     public void setXml(ChangesXML xml) {
832         this.xml = xml;
833     }
834 
835     public File getXmlPath() {
836         return xmlPath;
837     }
838 
839     public void setXmlPath(File xmlPath) {
840         this.xmlPath = xmlPath;
841     }
842 }