View Javadoc
1   /*
2    * Copyright (c) 2002-2021, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.releaser.service.github;
35  
36  import java.io.File;
37  import java.io.IOException;
38  import java.util.List;
39  import java.util.Locale;
40  
41  import org.apache.commons.collections.CollectionUtils;
42  import org.apache.commons.lang3.StringUtils;
43  import org.eclipse.jgit.api.Git;
44  import org.eclipse.jgit.api.LogCommand;
45  import org.eclipse.jgit.api.PullResult;
46  import org.eclipse.jgit.api.ResetCommand.ResetType;
47  import org.eclipse.jgit.api.errors.GitAPIException;
48  import org.eclipse.jgit.api.errors.InvalidRemoteException;
49  import org.eclipse.jgit.api.errors.TransportException;
50  import org.eclipse.jgit.internal.storage.file.FileRepository;
51  import org.eclipse.jgit.lib.Ref;
52  import org.eclipse.jgit.revwalk.RevCommit;
53  import org.eclipse.jgit.transport.RefSpec;
54  import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
55  
56  import fr.paris.lutece.plugins.releaser.business.Site;
57  import fr.paris.lutece.plugins.releaser.business.WorkflowReleaseContext;
58  import fr.paris.lutece.plugins.releaser.service.ComponentService;
59  import fr.paris.lutece.plugins.releaser.util.CommandResult;
60  import fr.paris.lutece.plugins.releaser.util.ConstanteUtils;
61  import fr.paris.lutece.plugins.releaser.util.IVCSResourceService;
62  import fr.paris.lutece.plugins.releaser.util.ReleaserUtils;
63  import fr.paris.lutece.plugins.releaser.util.file.FileUtils;
64  import fr.paris.lutece.plugins.releaser.util.github.GitUtils;
65  import fr.paris.lutece.portal.service.util.AppException;
66  
67  // TODO: Auto-generated Javadoc
68  /**
69   * SvnSiteService.
70   */
71  public class GitResourceService implements IVCSResourceService
72  {
73  
74      /**
75       * Fetch the pom.xml content from a repository
76       *
77       * @param site
78       *            the site
79       * @param strGitLogin
80       *            the str git login
81       * @param strGitPwd
82       *            the str git pwd
83       * @return The POM content
84       */
85      public String fetchPom( Site site, String strGitLogin, String strGitPwd )
86      {
87  
88          String strPom = null;
89          CommandResultutil/CommandResult.html#CommandResult">CommandResult commandResult = new CommandResult( );
90          WorkflowReleaseContext context = new WorkflowReleaseContext( );
91          commandResult.setLog( new StringBuffer( ) );
92          context.setCommandResult( commandResult );
93          context.setSite( site );
94          doCheckoutRepository( context, strGitLogin, strGitPwd );
95          strPom = FileUtils.readFile( ReleaserUtils.getLocalPomPath( context ) );
96  
97          return strPom;
98      }
99  
100     /**
101      * Gets the last release found in the SVN repository.
102      *
103      * @param site
104      *            the site
105      * @param strGitLogin
106      *            the str git login
107      * @param strGitPwd
108      *            the str git pwd
109      * @return The version if found otherwise null
110      */
111     public String getLastRelease( Site site, String strGitLogin, String strGitPwd )
112     {
113 
114         WorkflowReleaseContext context = new WorkflowReleaseContext( );
115         context.setSite( site );
116         Git git = GitUtils.getGit( ReleaserUtils.getLocalPath( context ) );
117         List<String> listTags = GitUtils.getTagNameList( git );
118         String strLastRelease = null;
119 
120         if ( !CollectionUtils.isEmpty( listTags ) )
121         {
122             strLastRelease = listTags.get( 0 );
123         }
124 
125         if ( strLastRelease != null && strLastRelease.contains( "-" ) )
126         {
127 
128             String [ ] tabRelease = strLastRelease.split( "-" );
129             strLastRelease = tabRelease [tabRelease.length - 1];
130         }
131         else
132         {
133             strLastRelease = "";
134         }
135 
136         return strLastRelease;
137 
138     }
139 
140     /**
141      * Do checkout repository.
142      *
143      * @param context
144      *            the context
145      * @param strLogin
146      *            the str login
147      * @param strPassword
148      *            the str password
149      * @return the string
150      */
151     @Override
152     public String doCheckoutRepository( WorkflowReleaseContext context, String strLogin, String strPassword )
153     {
154 
155         Git git = null;
156         String strBranchReleaseFrom = null;
157 
158         CommandResult commandResult = context.getCommandResult( );
159         ReleaserUtils.logStartAction( context, " Clone Repository" );
160         String strLocalComponentPath = ReleaserUtils.getLocalPath( context );
161 
162         File file = new File( strLocalComponentPath );
163 
164         if ( file.exists( ) )
165         {
166 
167             commandResult.getLog( ).append( "Local repository " + strLocalComponentPath + " exist\nCleaning Local folder...\n" );
168             if ( !FileUtils.delete( file, commandResult.getLog( ) ) )
169             {
170                 commandResult.setError( commandResult.getLog( ).toString( ) );
171 
172             }
173             commandResult.getLog( ).append( "Local repository has been cleaned\n" );
174         }
175 
176         commandResult.getLog( ).append( "Cloning repository ...\n" );
177         try
178         {
179 
180             // PROGRESS 5%
181             commandResult.setProgressValue( commandResult.getProgressValue( ) + 5 );
182             git = GitUtils.cloneRepo( strLocalComponentPath, context.getReleaserResource( ).getScmUrl( ), commandResult, strLogin, strLogin, strPassword );
183 
184             GitUtils.createLocalBranch( git, GitUtils.MASTER_BRANCH, commandResult );
185 
186             strBranchReleaseFrom = ReleaserUtils.getBranchReleaseFrom( context );
187             if ( !strBranchReleaseFrom.equals( GitUtils.DEFAULT_RELEASE_BRANCH ) )
188             {
189                 GitUtils.createLocalBranch( git, strBranchReleaseFrom, commandResult );
190                 context.setRefBranchReleaseFrom( GitUtils.getRefBranch( git, strBranchReleaseFrom, commandResult ) );
191             }
192             else
193             {
194                 GitUtils.createLocalBranch( git, GitUtils.DEFAULT_RELEASE_BRANCH, commandResult );
195                 context.setRefBranchReleaseFrom( GitUtils.getRefBranch( git, GitUtils.DEFAULT_RELEASE_BRANCH, commandResult ) );
196 
197                 context.setRefBranchRelease( GitUtils.getRefBranch( git, GitUtils.MASTER_BRANCH, commandResult ) );
198             }
199 
200             commandResult.getLog( ).append( "the repository has been successfully cloned.\n" );
201             commandResult.getLog( ).append( "Checkout branch \"" + strBranchReleaseFrom + "\" ...\n" );
202             GitUtils.checkoutRepoBranch( git, strBranchReleaseFrom, commandResult );
203             // PROGRESS 10%
204             commandResult.setProgressValue( commandResult.getProgressValue( ) + 5 );
205 
206             if ( context.getSite( ) == null && context.getComponent( ) != null && ComponentService.getService( )
207                     .isErrorSnapshotComponentInformations( context.getComponent( ), ReleaserUtils.getLocalPomPath( context ) ) )
208             {
209                 ReleaserUtils.addTechnicalError( commandResult, "The cloned component does not match the release informations" );
210 
211             }
212 
213         }
214         catch( AppException e )
215         {
216 
217             if ( e.getCause( ) != null && e.getCause( ) instanceof TransportException )
218             {
219 
220                 ReleaserUtils.addTechnicalError( commandResult, ConstanteUtils.ERROR_TYPE_AUTHENTICATION_ERROR, e );
221             }
222 
223         }
224 
225         finally
226         {
227             if ( git != null )
228             {
229 
230                 git.close( );
231 
232             }
233         }
234 
235         commandResult.getLog( ).append( "Checkout branch develop successfull\n" );
236 
237         ReleaserUtils.logEndAction( context, " Clone Repository" );
238 
239         return ConstanteUtils.CONSTANTE_EMPTY_STRING;
240     }
241 
242     /**
243      * Update develop branch.
244      *
245      * @param context
246      *            the context
247      * @param locale
248      *            the locale
249      * @param strMessage
250      *            the str message
251      */
252     @Override
253     public void updateDevelopBranch( WorkflowReleaseContext context, Locale locale, String strMessage )
254     {
255         updateBranch( context, GitUtils.DEFAULT_RELEASE_BRANCH, locale, strMessage );
256     }
257 
258     /**
259      * Update branch.
260      *
261      * @param context
262      *            the context
263      * @param locale
264      *            the locale
265      * @param strMessage
266      *            the str message
267      */
268     @Override
269     public void updateBranch( WorkflowReleaseContext context, String strBranch, Locale locale, String strMessage )
270     {
271 
272         String strLogin = context.getReleaserUser( ).getCredential( context.getReleaserResource( ).getRepoType( ) ).getLogin( );
273         String strPassword = context.getReleaserUser( ).getCredential( context.getReleaserResource( ).getRepoType( ) ).getPassword( );
274 
275         FileRepository fLocalRepo = null;
276         Git git = null;
277         CommandResult commandResult = context.getCommandResult( );
278         String strLocalComponentPath = ReleaserUtils.getLocalPath( context );
279 
280         try
281         {
282 
283             fLocalRepo = new FileRepository( strLocalComponentPath + "/.git" );
284 
285             git = new Git( fLocalRepo );
286             git.checkout( ).setName( strBranch ).call( );
287             git.add( ).addFilepattern( "." ).setUpdate( true ).call( );
288             git.commit( ).setCommitter( strLogin, strLogin ).setMessage( strMessage ).call( );
289             git.push( ).setCredentialsProvider( new UsernamePasswordCredentialsProvider( strLogin, strPassword ) ).call( );
290         }
291         catch( InvalidRemoteException e )
292         {
293 
294             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
295 
296         }
297         catch( TransportException e )
298         {
299             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
300 
301         }
302         catch( IOException e )
303         {
304             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
305         }
306         catch( GitAPIException e )
307         {
308             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
309         }
310 
311         finally
312         {
313 
314             if ( fLocalRepo != null )
315             {
316 
317                 fLocalRepo.close( );
318 
319             }
320             if ( git != null )
321             {
322 
323                 git.close( );
324 
325             }
326 
327         }
328 
329     }
330 
331     /**
332      * Update master branch.
333      *
334      * @param context
335      *            the context
336      * @param locale
337      *            the locale
338      */
339     @Override
340     public void updateMasterBranch( WorkflowReleaseContext context, Locale locale )
341     {
342 
343         String strLogin = context.getReleaserUser( ).getCredential( context.getReleaserResource( ).getRepoType( ) ).getLogin( );
344         String strPassword = context.getReleaserUser( ).getCredential( context.getReleaserResource( ).getRepoType( ) ).getPassword( );
345 
346         FileRepository fLocalRepo = null;
347         Git git = null;
348         CommandResult commandResult = context.getCommandResult( );
349 
350         String strLocalComponentPath = ReleaserUtils.getLocalPath( context );
351 
352         try
353         {
354 
355             fLocalRepo = new FileRepository( strLocalComponentPath + "/.git" );
356 
357             git = new Git( fLocalRepo );
358             git.checkout( ).setName( GitUtils.DEFAULT_RELEASE_BRANCH ).call( );
359             GitUtils.mergeBack( git, strLogin, strPassword, commandResult );
360 
361         }
362         catch( InvalidRemoteException e )
363         {
364 
365             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
366 
367         }
368         catch( TransportException e )
369         {
370             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
371 
372         }
373         catch( IOException e )
374         {
375             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
376         }
377         catch( GitAPIException e )
378         {
379             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
380         }
381 
382         finally
383         {
384 
385             if ( fLocalRepo != null )
386             {
387 
388                 fLocalRepo.close( );
389 
390             }
391             if ( git != null )
392             {
393 
394                 git.close( );
395 
396             }
397 
398         }
399 
400     }
401 
402     /**
403      * Rollback release.
404      *
405      * @param context
406      *            the context
407      * @param locale
408      *            the locale
409      */
410     @Override
411     public void rollbackRelease( WorkflowReleaseContext context, Locale locale )
412     {
413 
414         ReleaserUtils.logStartAction( context, " Rollback Release prepare" );
415         FileRepository fLocalRepo = null;
416         Git git = null;
417         CommandResult commandResult = context.getCommandResult( );
418         String strLocalComponentPath = ReleaserUtils.getLocalPath( context );
419         String strLogin = context.getReleaserUser( ).getCredential( context.getReleaserResource( ).getRepoType( ) ).getLogin( );
420         String strPassword = context.getReleaserUser( ).getCredential( context.getReleaserResource( ).getRepoType( ) ).getPassword( );
421 
422         try
423         {
424 
425             fLocalRepo = new FileRepository( strLocalComponentPath + "/.git" );
426 
427             git = new Git( fLocalRepo );
428 
429             // RESET commit on release branch from
430             if ( !StringUtils.isEmpty( context.getRefBranchReleaseFrom( ) ) )
431             {
432                 git.checkout( ).setName( ReleaserUtils.getBranchReleaseFrom( context ) ).call( );
433                 git.reset( ).setRef( context.getRefBranchReleaseFrom( ) ).setMode( ResetType.HARD ).call( );
434                 git.push( ).setForce( true ).setCredentialsProvider( new UsernamePasswordCredentialsProvider( strLogin, strPassword ) ).call( );
435 
436             }
437 
438             // Reset Commit on Master (only if the releasing from develop branch)
439             if ( !StringUtils.isEmpty( context.getRefBranchRelease( ) ) && context.getRefBranchReleaseFrom( ).equals( GitUtils.DEFAULT_RELEASE_BRANCH ) )
440             {
441 
442                 git.checkout( ).setName( GitUtils.MASTER_BRANCH ).call( );
443                 git.reset( ).setRef( context.getRefBranchRelease( ) ).setMode( ResetType.HARD ).call( );
444                 git.push( ).setForce( true ).setCredentialsProvider( new UsernamePasswordCredentialsProvider( strLogin, strPassword ) ).call( );
445             }
446 
447             // Delete Tag if exist
448             List<Ref> call = git.tagList( ).call( );
449             String strTagName = context.getReleaserResource( ).getArtifactId( ) + "-" + context.getReleaserResource( ).getTargetVersion( );
450             for ( Ref refTag : call )
451             {
452 
453                 if ( refTag.getName( ).contains( strTagName ) )
454                 {
455 
456                     LogCommand log = git.log( ).setMaxCount( 1 );
457 
458                     Ref peeledRef = git.getRepository( ).peel( refTag );
459                     if ( peeledRef.getPeeledObjectId( ) != null )
460                     {
461                         log.add( peeledRef.getPeeledObjectId( ) );
462                     }
463                     else
464                     {
465                         log.add( refTag.getObjectId( ) );
466                     }
467 
468                     Iterable<RevCommit> logs = log.call( );
469                     for ( RevCommit rev : logs )
470                     {
471                         // Test if the tag was created by the release
472                         if ( !rev.getName( ).equals( context.getRefBranchRelease( ) ) )
473                         {
474 
475                             git.branchDelete( ).setBranchNames( refTag.getName( ) ).setForce( true ).call( );
476                             RefSpec refSpec = new RefSpec( ).setSource( null ).setDestination( refTag.getName( ) );
477                             git.push( ).setRefSpecs( refSpec ).setCredentialsProvider( new UsernamePasswordCredentialsProvider( strLogin, strPassword ) )
478                                     .setRemote( "origin" ).call( );
479                         }
480 
481                     }
482 
483                 }
484 
485             }
486         }
487         catch( InvalidRemoteException e )
488         {
489 
490             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
491 
492         }
493         catch( TransportException e )
494         {
495             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
496 
497         }
498         catch( IOException e )
499         {
500             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
501         }
502         catch( GitAPIException e )
503         {
504             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
505         }
506 
507         finally
508         {
509 
510             if ( fLocalRepo != null )
511             {
512 
513                 fLocalRepo.close( );
514 
515             }
516             if ( git != null )
517             {
518 
519                 git.close( );
520 
521             }
522 
523         }
524         ReleaserUtils.logEndAction( context, " Rollback Release prepare" );
525 
526     }
527 
528     /**
529      * Checkout develop branch.
530      *
531      * @param context
532      *            the context
533      * @param locale
534      *            the locale
535      */
536     @Override
537     public void checkoutDevelopBranch( WorkflowReleaseContext context, Locale locale )
538     {
539         checkoutBranch( context, GitUtils.DEFAULT_RELEASE_BRANCH, locale );
540     }
541 
542     /**
543      * Checkout branch.
544      *
545      * @param context
546      *            the context
547      * @param locale
548      *            the locale
549      */
550     @Override
551     public void checkoutBranch( WorkflowReleaseContext context, String strBranch, Locale locale )
552     {
553         FileRepository fLocalRepo = null;
554         Git git = null;
555         CommandResult commandResult = context.getCommandResult( );
556 
557         String strLocalComponentPath = ReleaserUtils.getLocalPath( context );
558         String strLogin = context.getReleaserUser( ).getCredential( context.getReleaserResource( ).getRepoType( ) ).getLogin( );
559         String strPassword = context.getReleaserUser( ).getCredential( context.getReleaserResource( ).getRepoType( ) ).getPassword( );
560 
561         try
562         {
563 
564             fLocalRepo = new FileRepository( strLocalComponentPath + "/.git" );
565 
566             git = new Git( fLocalRepo );
567             git.checkout( ).setName( strBranch ).call( );
568             PullResult result = GitUtils.pullRepoBranch( git, strBranch, strLogin, strPassword );
569             if ( !result.isSuccessful( ) )
570             {
571                 ReleaserUtils.addTechnicalError( commandResult, "error during checkout " + strBranch + " branch" );
572 
573             }
574 
575         }
576         catch( InvalidRemoteException e )
577         {
578 
579             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
580 
581         }
582         catch( TransportException e )
583         {
584             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
585 
586         }
587         catch( IOException e )
588         {
589             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
590         }
591         catch( GitAPIException e )
592         {
593             ReleaserUtils.addTechnicalError( commandResult, e.getMessage( ), e );
594         }
595 
596         finally
597         {
598 
599             if ( fLocalRepo != null )
600             {
601 
602                 fLocalRepo.close( );
603 
604             }
605             if ( git != null )
606             {
607 
608                 git.close( );
609 
610             }
611 
612         }
613 
614     }
615 
616 }