1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package com.hazendaz.maven.makeself;
19
20 import java.io.BufferedInputStream;
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.InputStreamReader;
26 import java.nio.charset.StandardCharsets;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.StandardCopyOption;
30 import java.util.Arrays;
31 import java.util.List;
32
33 import javax.inject.Inject;
34
35 import org.apache.commons.compress.archivers.ArchiveEntry;
36 import org.apache.commons.compress.archivers.ArchiveInputStream;
37 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
38 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
39 import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
40 import org.apache.maven.plugin.AbstractMojo;
41 import org.apache.maven.plugin.MojoFailureException;
42 import org.apache.maven.plugins.annotations.Parameter;
43 import org.eclipse.aether.RepositorySystem;
44 import org.eclipse.aether.RepositorySystemSession;
45 import org.eclipse.aether.artifact.Artifact;
46 import org.eclipse.aether.artifact.DefaultArtifact;
47 import org.eclipse.aether.repository.RemoteRepository;
48 import org.eclipse.aether.resolution.ArtifactRequest;
49 import org.eclipse.aether.resolution.ArtifactResolutionException;
50 import org.eclipse.aether.resolution.ArtifactResult;
51
52
53
54
55 public abstract class AbstractGitMojo extends AbstractMojo {
56
57
58 static final boolean WINDOWS = System.getProperty("os.name").startsWith("Windows");
59
60
61 static final String GIT_USER_BIN = "/usr/bin/";
62
63
64
65
66
67
68
69 protected boolean isWindows() {
70 return WINDOWS;
71 }
72
73
74
75
76
77 @Parameter(defaultValue = "", property = "gitPath")
78 protected String gitPath;
79
80
81 @Inject
82 protected RepositorySystem repositorySystem;
83
84
85 @Parameter(defaultValue = "${repositorySystemSession}", readonly = true, required = true)
86 protected RepositorySystemSession repoSession;
87
88
89 @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
90 protected List<RemoteRepository> remoteRepositories;
91
92
93 protected PortableGit portableGit;
94
95
96
97
98
99
100
101 protected void checkGitSetup() throws MojoFailureException {
102
103 this.portableGit = new PortableGit(this.getLog());
104
105
106 this.extractPortableGit();
107 }
108
109
110
111
112
113
114
115 protected void extractPortableGit() throws MojoFailureException {
116 final String location = this.repoSession.getLocalRepository().getBasedir() + File.separator
117 + this.portableGit.getName() + File.separator + this.portableGit.getVersion();
118 if (Files.exists(Path.of(location))) {
119 this.getLog().debug("Existing 'PortableGit' folder found at " + location);
120 this.gitPath = location + AbstractGitMojo.GIT_USER_BIN;
121 return;
122 }
123
124 this.getLog().info("Loading portable git");
125 final Artifact artifact = new DefaultArtifact(this.portableGit.getGroupId(), this.portableGit.getArtifactId(),
126 this.portableGit.getClassifier(), this.portableGit.getExtension(), this.portableGit.getVersion());
127 final ArtifactRequest artifactRequest = new ArtifactRequest().setRepositories(this.remoteRepositories)
128 .setArtifact(artifact);
129 ArtifactResult resolutionResult = null;
130 try {
131 resolutionResult = this.repositorySystem.resolveArtifact(this.repoSession, artifactRequest);
132 if (!resolutionResult.isResolved()) {
133 throw new MojoFailureException("Unable to resolve artifact: " + artifact.getGroupId() + ":"
134 + artifact.getArtifactId() + ":" + artifact.getVersion() + ":" + artifact.getClassifier() + ":"
135 + artifact.getExtension());
136 }
137 } catch (final ArtifactResolutionException e) {
138 throw new MojoFailureException(
139 "Unable to resolve artifact: " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
140 + artifact.getVersion() + ":" + artifact.getClassifier() + ":" + artifact.getExtension());
141 }
142 this.installGit(resolutionResult.getArtifact(), location);
143 }
144
145
146
147
148
149
150
151
152
153 protected void installGit(final Artifact artifact, final String location) {
154 Path currentFile = null;
155
156
157
158 try (InputStream inputStream = Files.newInputStream(artifact.getFile().toPath());
159 InputStream bufferedStream = new BufferedInputStream(inputStream);
160 InputStream gzipStream = new GzipCompressorInputStream(bufferedStream);
161 ArchiveInputStream<TarArchiveEntry> tarStream = new TarArchiveInputStream(gzipStream)) {
162 ArchiveEntry entry;
163 final String directory = this.repoSession.getLocalRepository().getBasedir() + File.separator
164 + this.portableGit.getName();
165 while ((entry = tarStream.getNextEntry()) != null) {
166 if (entry.isDirectory()) {
167 continue;
168 }
169 currentFile = Path.of(directory, entry.getName());
170 if (!currentFile.normalize().startsWith(directory)) {
171 throw new IOException("Bad zip entry, possible directory traversal");
172 }
173 final Path parent = currentFile.getParent();
174 if (!Files.exists(parent)) {
175 Files.createDirectories(parent);
176 }
177 this.getLog().debug("Current file: " + currentFile.getFileName());
178 Files.copy(tarStream, currentFile, StandardCopyOption.REPLACE_EXISTING);
179 }
180 } catch (final IOException e) {
181 this.getLog().error("", e);
182 }
183
184 try {
185 if (currentFile != null) {
186
187 this.getLog().debug("Extract Portable Git");
188 this.runInstaller(Arrays.asList(currentFile.toString(), "-y", "-o", location));
189 this.gitPath = location + AbstractGitMojo.GIT_USER_BIN;
190 }
191 } catch (final IOException e) {
192 this.getLog().error("", e);
193 } catch (final InterruptedException e) {
194 this.getLog().error("", e);
195
196 Thread.currentThread().interrupt();
197 }
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211 protected void runInstaller(final List<String> command) throws IOException, InterruptedException {
212 this.getLog().debug("Execution commands: " + command);
213
214 final ProcessBuilder processBuilder = new ProcessBuilder(command);
215 processBuilder.redirectErrorStream(true);
216
217 final Process process = processBuilder.start();
218 try (BufferedReader reader = new BufferedReader(
219 new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
220 String line = "";
221 while ((line = reader.readLine()) != null) {
222 this.getLog().info(line);
223 }
224 this.getLog().info("");
225 }
226
227 final int status = process.waitFor();
228 if (status > 0) {
229 this.getLog().error(String.join(" ", "Process failed with error status:", String.valueOf(status)));
230 }
231 }
232
233 }