1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.plugins.lutecetools.service;
35
36 import java.io.FileNotFoundException;
37 import java.io.IOException;
38 import java.net.InetSocketAddress;
39 import java.net.Proxy;
40 import java.net.SocketAddress;
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.concurrent.ConcurrentHashMap;
45
46 import org.apache.commons.lang.StringUtils;
47 import org.kohsuke.github.GHBranch;
48 import org.kohsuke.github.GHIssueState;
49 import org.kohsuke.github.GHOrganization;
50 import org.kohsuke.github.GHPullRequest;
51 import org.kohsuke.github.GHRepository;
52 import org.kohsuke.github.GitHub;
53 import org.kohsuke.github.GitHubBuilder;
54
55 import fr.paris.lutece.plugins.lutecetools.business.Component;
56 import fr.paris.lutece.portal.service.datastore.DatastoreService;
57 import fr.paris.lutece.portal.service.util.AppLogService;
58 import fr.paris.lutece.portal.service.util.AppPropertiesService;
59
60
61
62
63 public class GitHubService extends AbstractGitPlatformService
64 {
65 private static final String SERVICE_NAME = "GitHub Info filler service registered";
66 private static final String PROPERTY_GITHUB_ACCOUNT_NAME = "lutecetools.github.account.name";
67 private static final String PROPERTY_GITHUB_ACCOUNT_TOKEN = "lutecetools.github.account.token";
68 private static final String PROPERTY_GITHUB_ORGANIZATIONS = "lutecetools.github.organization";
69 private static final String DSKEY_PARENT_POM_VERSION = "lutecetools.site_property.globalPom.version";
70
71 private static final String SITE_INDEX_PATH_PART1 = "/raw/develop/src/site/";
72 private static final String SITE_INDEX_PATH_PART2 = "xdoc/index.xml";
73
74 private static String _strParentPomVersion;
75 private static Map<String, GHRepository> _mapRepositories;
76
77
78
79
80 public GitHubService( )
81 {
82 _strParentPomVersion = DatastoreService.getDataValue( DSKEY_PARENT_POM_VERSION, "3.0.3" );
83 }
84
85
86
87
88 @Override
89 public String getName( )
90 {
91 return SERVICE_NAME;
92 }
93
94
95
96
97 public static void updateGitHubRepositoriesList( )
98 {
99 _mapRepositories = getRepositories( );
100 }
101
102
103
104
105 @Override
106 public void fill( Component component, StringBuilder sbLogs )
107 {
108 String strRepository = getGitHubRepository( component );
109 if ( strRepository == null )
110 {
111 return;
112 }
113 component.set( Component.IS_GIT_REPO, true );
114
115 GHRepository repo = _mapRepositories.get( strRepository );
116
117 try
118 {
119 component.set( GIT_GROUP, repo.getOwner( ).getLogin( ) );
120 component.set( GIT_PLATFORM, getGitPlatform( ) );
121 Map<String, GHBranch> mapBranches = repo.getBranches( );
122 List<String> listBranches = new ArrayList<>( );
123
124 for ( String strBranch : mapBranches.keySet( ) )
125 {
126 listBranches.add( strBranch );
127 }
128
129 component.set( BRANCHES_LIST, listBranches );
130 }
131 catch ( Exception ex )
132 {
133 sbLogs.append( "\n*** ERROR *** Retrieving GitHub infos (branches , readme, ...) for component " )
134 .append( component.getArtifactId( ) ).append( " : " ).append( ex.getMessage( ) );
135 }
136 try
137 {
138 repo.getReadme( );
139 component.set( HAS_README, true );
140 }
141 catch ( Exception e )
142 {
143 if ( e instanceof FileNotFoundException )
144 {
145 component.set( HAS_README, false );
146 }
147 }
148 try
149 {
150 List<GHPullRequest> prs = repo.getPullRequests( GHIssueState.OPEN );
151 component.set( PULL_REQUEST_COUNT, prs.size( ) );
152 long oldest = Long.MAX_VALUE;
153 for ( GHPullRequest pr : prs )
154 {
155 if ( pr.getUpdatedAt( ).getTime( ) < oldest )
156 {
157 oldest = pr.getUpdatedAt( ).getTime( );
158 }
159 }
160 component.set( OLDEST_PULL_REQUEST, oldest );
161 }
162 catch ( IOException e )
163 {
164 sbLogs.append( "\n*** ERROR *** Retreiving Github pull requests for component " )
165 .append( component.getArtifactId( ) ).append( " : " ).append( e.getMessage( ) );
166 }
167 fillGitHubStatus( component );
168 fillGitHubErrors( component );
169
170 fillSiteInfos( component, sbLogs );
171 }
172
173 private static String getGitHubRepository( Component component )
174 {
175 if ( _mapRepositories == null )
176 {
177 _mapRepositories = getRepositories( );
178 }
179 for ( String strRepository : _mapRepositories.keySet( ) )
180 {
181 if ( strRepository.endsWith( component.getArtifactId( ) ) )
182 {
183 return strRepository;
184 }
185 }
186
187 return null;
188
189 }
190
191
192
193
194
195
196 static Map<String, GHRepository> getRepositories( )
197 {
198 String strOrganizations = AppPropertiesService.getProperty( PROPERTY_GITHUB_ORGANIZATIONS );
199
200 String[] organizations = strOrganizations.split( "," );
201
202 Map<String, GHRepository> mapRepositories = new ConcurrentHashMap<>( );
203
204 for ( String strOrganization : organizations )
205 {
206 strOrganization = strOrganization.trim( );
207 try
208 {
209 GitHub github = getGitHub( );
210 GHOrganization organization = github.getOrganization( strOrganization );
211 mapRepositories.putAll( organization.getRepositories( ) );
212 int nSize = organization.getRepositories( ).size( );
213 AppLogService.info( "LuteceTools : GitHub Service initialized - " + nSize
214 + " repositories found for organization " + strOrganization );
215 }
216 catch ( IOException ex )
217 {
218 AppLogService.error( "LuteceTools : Unable to access GitHub repositories", ex );
219 }
220 }
221 return mapRepositories;
222 }
223
224
225
226
227
228
229
230 private static GitHub getGitHub( ) throws IOException
231 {
232 GitHub github;
233
234 String strAccount = AppPropertiesService.getProperty( PROPERTY_GITHUB_ACCOUNT_NAME );
235 String strToken = AppPropertiesService.getProperty( PROPERTY_GITHUB_ACCOUNT_TOKEN );
236 String strProxyHost = AppPropertiesService.getProperty( "httpAccess.proxyHost" );
237 int nProxyPort = AppPropertiesService.getPropertyInt( "httpAccess.proxyPort", 80 );
238 if ( !StringUtils.isEmpty( strProxyHost ) )
239 {
240 GitHubBuilder builder = new GitHubBuilder( );
241 SocketAddress address = new InetSocketAddress( strProxyHost, nProxyPort );
242 Proxy proxy = new Proxy( Proxy.Type.HTTP, address );
243 builder.withProxy( proxy );
244 builder.withOAuthToken( strToken, strAccount );
245 github = builder.build( );
246 AppLogService.info( "LuteceTools : Using httpaccess.properties defined proxy to connect to GitHub." );
247 }
248 else
249 {
250 github = GitHub.connect( strAccount, strToken );
251 }
252
253 return github;
254
255 }
256
257
258
259
260
261
262 private void fillGitHubErrors( Component component )
263 {
264 StringBuilder sbErrors = new StringBuilder( "" );
265
266 if ( Boolean.TRUE.equals( component.getBoolean( Component.IS_GIT_REPO ) ) )
267 {
268 String strScmUrl = component.get( Component.SCM_URL );
269 if ( strScmUrl != null && strScmUrl.contains( "github" ) )
270 {
271 sbErrors.append( "Bad SCM info in the released POM. \n" );
272 }
273
274 String strSnapshotScmUrl = component.get( Component.SNAPSHOT_SCM_URL );
275 if ( strSnapshotScmUrl != null && strSnapshotScmUrl.contains( "github" ) )
276 {
277 sbErrors.append( "Bad SCM info in the snapshot POM. \n" );
278 }
279
280 if ( !_strParentPomVersion.equals( component.get( Component.PARENT_POM_VERSION ) ) )
281 {
282 sbErrors.append( "Bad parent POM in release POM. should be global-pom version " )
283 .append( _strParentPomVersion ).append( '\n' );
284 }
285
286 if ( !_strParentPomVersion.equals( component.get( Component.SNAPSHOT_PARENT_POM_VERSION ) ) )
287 {
288 sbErrors.append( "Bad parent POM in snapshot POM. should be global-pom version " )
289 .append( _strParentPomVersion ).append( '\n' );
290 }
291
292 List listBranches = (List) component.getObject( BRANCHES_LIST );
293 if ( ( listBranches != null ) && ( listBranches.contains( "develop" ) ) )
294 {
295 sbErrors.append( "Branch 'develop' is missing. \n" );
296 }
297 }
298
299 component.set( GIT_REPO_ERRORS, sbErrors.toString( ) );
300 }
301
302
303
304
305
306
307 private void fillGitHubStatus( Component component )
308 {
309 int nStatus = 0;
310
311 if ( Boolean.TRUE.equals( component.getBoolean( Component.IS_GIT_REPO ) ) )
312 {
313 nStatus++;
314 }
315
316 String strScmUrl = component.get( Component.SCM_URL );
317 if ( strScmUrl != null && strScmUrl.contains( "github" ) )
318 {
319 nStatus++;
320 }
321
322 String strSnapshotScmUrl = component.get( Component.SNAPSHOT_SCM_URL );
323 if ( strSnapshotScmUrl != null && strSnapshotScmUrl.contains( "github" ) )
324 {
325 nStatus++;
326 }
327
328 List listBranches = (List) component.getObject( BRANCHES_LIST );
329 if ( ( listBranches != null ) && ( listBranches.contains( "develop" ) ) )
330 {
331 nStatus++;
332 }
333 component.set( GIT_REPO_STATUS, nStatus );
334 }
335
336
337
338
339
340
341 private void fillSiteInfos( Component component, StringBuilder sbLogs )
342 {
343 String strScmUrl = component.get( Component.SCM_URL );
344 if ( strScmUrl != null )
345 {
346 if ( strScmUrl.endsWith( ".git" ) )
347 {
348 strScmUrl = strScmUrl.substring( 0, strScmUrl.length( ) - 4 );
349 }
350
351 String strXdocSiteIndexUrl = strScmUrl + SITE_INDEX_PATH_PART1 + SITE_INDEX_PATH_PART2;
352 SiteInfoService.instance( ).getSiteInfos( component, strXdocSiteIndexUrl, "en", sbLogs );
353
354 strXdocSiteIndexUrl = strScmUrl + SITE_INDEX_PATH_PART1 + "fr/" + SITE_INDEX_PATH_PART2;
355 SiteInfoService.instance( ).getSiteInfos( component, strXdocSiteIndexUrl, "fr", sbLogs );
356
357 }
358 }
359 }