View Javadoc
1   /*
2    * Copyright (c) 2016 ingenieux Labs
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package br.com.ingenieux.mojo.beanstalk.bundle;
18  
19  import com.amazonaws.services.elasticbeanstalk.model.ApplicationVersionDescription;
20  import com.amazonaws.services.elasticbeanstalk.model.DescribeApplicationVersionsRequest;
21  import com.amazonaws.services.elasticbeanstalk.model.DescribeApplicationVersionsResult;
22  
23  import org.apache.maven.plugins.annotations.Mojo;
24  import org.apache.maven.plugins.annotations.Parameter;
25  import org.apache.maven.project.MavenProject;
26  import org.eclipse.jgit.api.AddCommand;
27  import org.eclipse.jgit.api.Git;
28  import org.eclipse.jgit.api.PushCommand;
29  import org.eclipse.jgit.api.Status;
30  import org.eclipse.jgit.lib.ObjectId;
31  import org.eclipse.jgit.lib.Ref;
32  import org.eclipse.jgit.lib.Repository;
33  import org.eclipse.jgit.lib.RepositoryBuilder;
34  import org.eclipse.jgit.lib.TextProgressMonitor;
35  import org.eclipse.jgit.transport.PushResult;
36  import org.eclipse.jgit.transport.RefSpec;
37  
38  import java.io.File;
39  import java.util.Date;
40  
41  import br.com.ingenieux.mojo.beanstalk.AbstractNeedsEnvironmentMojo;
42  
43  import static java.lang.String.format;
44  
45  /**
46   * Uploads a packed war file to Amazon S3 for further Deployment.
47   *
48   * @since 0.2.8
49   */
50  @Mojo(name = "fast-deploy")
51  public class FastDeployMojo extends AbstractNeedsEnvironmentMojo {
52  
53    /**
54     * Artifact to Deploy
55     */
56    @Parameter(defaultValue = "${project.build.directory}/${project.build.finalName}")
57    File sourceDirectory;
58  
59    /**
60     * Git Staging Dir (should not be under target/)
61     */
62    @Parameter(property = "beanstalk.stagingDirectory", defaultValue = "${project.basedir}/tmp-git-deployment-staging")
63    File stagingDirectory;
64  
65    /**
66     * Use Staging Directory?
67     */
68    @Parameter(property = "beanstalk.useStagingDirectory", defaultValue = "false")
69    boolean useStagingDirectory = false;
70  
71    @Parameter(defaultValue = "${project}")
72    MavenProject project;
73  
74    /**
75     * Version Description
76     */
77    @Parameter(property = "beanstalk.versionDescription", defaultValue = "Update from fast-deploy")
78    String versionDescription;
79  
80    /**
81     * Skip Environment Update?
82     */
83    @Parameter(property = "beanstalk.skipEnvironmentUpdate", defaultValue = "false")
84    boolean skipEnvironmentUpdate = false;
85  
86    /**
87     * Silent Upload?
88     */
89    @Parameter(property = "beanstalk.silentUpload", defaultValue = "false")
90    boolean silentUpload = false;
91  
92    @Override
93    protected void configure() {
94      try {
95        super.configure();
96      } catch (Exception exc) {
97        //getLog().warn(exc);
98      }
99    }
100 
101   @Override
102   protected Object executeInternal() throws Exception {
103     Git git = getGitRepo();
104     String versionLabel = null;
105 
106     String commitId = null;
107 
108     Ref masterRef = git.getRepository().getRef("master");
109     if (null != masterRef) {
110       commitId = ObjectId.toString(masterRef.getObjectId());
111     }
112 
113     Status status = git.status().call();
114 
115     boolean pushAhead = false;
116 
117     if (null != commitId && status.isClean()) {
118       versionLabel = lookupVersionLabelForCommitId(commitId);
119 
120       if (null == versionLabel) {
121         getLog().info("No Changes. However, we've didn't get something close in AWS Elastic Beanstalk and we're pushing ahead");
122         pushAhead = true;
123       } else {
124         getLog().info("No Changes. However, we've got something close in AWS Elastic Beanstalk and we're continuing");
125 
126         project.getProperties().put("beanstalk.versionLabel", versionLabel);
127 
128         return null;
129       }
130     }
131 
132     if (!pushAhead) {
133       // Asks for Existing Files to get added
134       git.add().setUpdate(true).addFilepattern(".").call();
135 
136       // Now as for any new files (untracked)
137 
138       AddCommand addCommand = git.add();
139 
140       if (!status.getUntracked().isEmpty()) {
141         for (String s : status.getUntracked()) {
142           getLog().info("Adding file " + s);
143           addCommand.addFilepattern(s);
144         }
145 
146         addCommand.call();
147       }
148 
149       git.commit().setAll(true).setMessage(versionDescription).call();
150 
151       masterRef = git.getRepository().getRef("master");
152 
153       commitId = ObjectId.toString(masterRef.getObjectId());
154     }
155 
156     String environmentName = null;
157 
158     /*
159      * Builds the remote push URL
160      */
161     if (null != curEnv && !skipEnvironmentUpdate) {
162       environmentName = curEnv.getEnvironmentName();
163     }
164 
165     String remote = getRemoteUrl(commitId, environmentName);
166 
167     getLog().info("Using remote: " + remote);
168 
169     /*
170      * Does the Push
171      */
172     {
173       PushCommand cmd =
174           git. //
175               push();
176 
177       if (!silentUpload) {
178         cmd.setProgressMonitor(new TextProgressMonitor());
179       }
180 
181       Iterable<PushResult> pushResults = null;
182       try {
183         pushResults =
184             cmd.setRefSpecs(new RefSpec("HEAD:refs/heads/master"))
185                 . //
186                 setForce(true)
187                 . //
188                 setRemote(remote)
189                 . //
190                 call();
191       } catch (Exception exc) {
192         // Ignore
193         getLog().debug("(Actually Expected) Exception", exc);
194       }
195 
196       /*
197        * I wish someday it could work... :(
198        */
199       if (null != pushResults) {
200         for (PushResult pushResult : pushResults) {
201           getLog().debug(" * " + pushResult.getMessages());
202         }
203       }
204     }
205 
206     versionLabel = lookupVersionLabelForCommitId(commitId);
207 
208     if (null != versionLabel) {
209       getLog().info("Deployed version " + versionLabel);
210 
211       project.getProperties().put("beanstalk.versionLabel", versionLabel);
212     } else {
213       getLog().warn("No version found. Ignoring.");
214     }
215 
216     return null;
217   }
218 
219   protected String getRemoteUrl(String commitId, String environmentName) throws org.apache.maven.plugin.MojoFailureException {
220     return new RequestSigner(getAWSCredentials(), applicationName, regionName, commitId, environmentName, new Date()).getPushUrl();
221   }
222 
223   protected String lookupVersionLabelForCommitId(String commitId) throws Exception {
224     String versionLabel = null;
225     String prefixToLookup = format("git-%s-", commitId);
226 
227     DescribeApplicationVersionsResult describeApplicationVersions =
228         getService().describeApplicationVersions(new DescribeApplicationVersionsRequest().withApplicationName(applicationName));
229 
230     for (ApplicationVersionDescription avd : describeApplicationVersions.getApplicationVersions()) {
231       if (avd.getVersionLabel().startsWith(prefixToLookup)) {
232         versionLabel = avd.getVersionLabel();
233         break;
234       }
235     }
236 
237     return versionLabel;
238   }
239 
240   protected Git getGitRepo() throws Exception {
241     Git git = null;
242 
243     if (!useStagingDirectory) {
244       File gitRepo = new File(sourceDirectory, ".git");
245 
246       if (!gitRepo.exists()) {
247         git = Git.init().setDirectory(sourceDirectory).call();
248       } else {
249         git = Git.open(gitRepo);
250       }
251     } else {
252       File gitRepo = stagingDirectory;
253       Repository r = null;
254 
255       RepositoryBuilder b = new RepositoryBuilder().setGitDir(stagingDirectory).setWorkTree(sourceDirectory);
256 
257       if (!gitRepo.exists()) {
258         gitRepo.getParentFile().mkdirs();
259 
260         r = b.build();
261 
262         r.create();
263       } else {
264         r = b.build();
265       }
266 
267       git = Git.wrap(r);
268     }
269 
270     return git;
271   }
272 }