|
| 1 | +/* |
| 2 | + * This file is part of git-commit-id-plugin-core by Konrad 'ktoso' Malawski <konrad.malawski@java.pl> |
| 3 | + * |
| 4 | + * git-commit-id-plugin-core is free software: you can redistribute it and/or modify |
| 5 | + * it under the terms of the GNU Lesser General Public License as published by |
| 6 | + * the Free Software Foundation, either version 3 of the License, or |
| 7 | + * (at your option) any later version. |
| 8 | + * |
| 9 | + * git-commit-id-plugin-core is distributed in the hope that it will be useful, |
| 10 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | + * GNU General Public License for more details. |
| 13 | + * |
| 14 | + * You should have received a copy of the GNU Lesser General Public License |
| 15 | + * along with git-commit-id-plugin-core. If not, see <http://www.gnu.org/licenses/>. |
| 16 | + */ |
| 17 | + |
| 18 | +package pl.project13.core; |
| 19 | + |
| 20 | +import pl.project13.core.cibuild.BuildServerDataProvider; |
| 21 | +import pl.project13.core.git.GitDescribeConfig; |
| 22 | +import pl.project13.core.log.LogInterface; |
| 23 | +import pl.project13.core.util.BuildFileChangeListener; |
| 24 | + |
| 25 | +import javax.annotation.Nonnull; |
| 26 | +import javax.annotation.Nullable; |
| 27 | +import java.io.File; |
| 28 | +import java.nio.charset.Charset; |
| 29 | +import java.text.SimpleDateFormat; |
| 30 | +import java.util.*; |
| 31 | +import java.util.function.Supplier; |
| 32 | + |
| 33 | +public class GitCommitIdPlugin { |
| 34 | + public interface Callback { |
| 35 | + |
| 36 | + /** |
| 37 | + * @return Supplier that provides the version of the project that is currently evaluated. |
| 38 | + * Used to determine {@link GitCommitPropertyConstant#BUILD_VERSION}. |
| 39 | + */ |
| 40 | + Supplier<String> supplyProjectVersion(); |
| 41 | + |
| 42 | + /** |
| 43 | + * @return Logging Interface |
| 44 | + */ |
| 45 | + @Nonnull |
| 46 | + LogInterface getLogInterface(); |
| 47 | + |
| 48 | + /** |
| 49 | + * @return The date format to be used for any dates exported by this plugin. |
| 50 | + * It should be a valid {@link SimpleDateFormat} string. |
| 51 | + */ |
| 52 | + @Nonnull |
| 53 | + String getDateFormat(); |
| 54 | + |
| 55 | + /** |
| 56 | + * <p>The timezone used in the date format of dates exported by this plugin. |
| 57 | + * It should be a valid Timezone string such as {@code 'America/Los_Angeles'}, {@code 'GMT+10'} or {@code 'PST'}.</p> |
| 58 | + * |
| 59 | + * <p>Try to avoid three-letter time zone IDs because the same abbreviation is often used for multiple time zones. |
| 60 | + * Please review <a href="https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html">https://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html</a> for more information on this issue.</p> |
| 61 | + * |
| 62 | + * @return The timezone used in the date format of dates exported by this plugin. |
| 63 | + */ |
| 64 | + @Nonnull |
| 65 | + String getDateFormatTimeZone(); |
| 66 | + |
| 67 | + /** |
| 68 | + * The prefix to expose the properties on. For example {@code 'git'} would allow you to access {@code ${git.branch}}. |
| 69 | + * |
| 70 | + * @return The prefix to expose the properties on. |
| 71 | + */ |
| 72 | + @Nonnull |
| 73 | + String getPrefixDot(); |
| 74 | + |
| 75 | + /** |
| 76 | + * <p>List of properties to exclude from the resulting file. |
| 77 | + * May be useful when you want to hide {@code 'git.remote.origin.url'} (maybe because it contains your repo password?) |
| 78 | + * or the email of the committer.</p> |
| 79 | + * |
| 80 | + * <p>Supports wildcards: you can write {@code 'git.commit.user.*'} to exclude both the {@code 'name'} |
| 81 | + * as well as {@code 'email'} properties from being emitted into the resulting files.</p> |
| 82 | + * |
| 83 | + * <p><b>Note:</b> The strings here are Java regular expressions: {@code '.*'} is a wildcard, not plain {@code '*'}.</p> |
| 84 | + * |
| 85 | + * @return List of properties to exclude |
| 86 | + */ |
| 87 | + List<String> getExcludeProperties(); |
| 88 | + |
| 89 | + /** |
| 90 | + * <p>List of properties to include into the resulting file. Only properties specified here will be included. |
| 91 | + * This list will be overruled by the {@code 'excludeProperties'}.</p> |
| 92 | + * |
| 93 | + * <p>Supports wildcards: you can write {@code 'git.commit.user.*'} to include both the {@code 'name'} |
| 94 | + * as well as {@code 'email'} properties into the resulting files.</p> |
| 95 | + * |
| 96 | + * <p><b>Note:</b> The strings here are Java regular expressions: {@code '.*'} is a wildcard, not plain {@code '*'}.</p> |
| 97 | + * |
| 98 | + * @return List of properties to include |
| 99 | + */ |
| 100 | + List<String> getIncludeOnlyProperties(); |
| 101 | + |
| 102 | + /** |
| 103 | + * Timestamp for reproducible output archive entries |
| 104 | + * (https://maven.apache.org/guides/mini/guide-reproducible-builds.html). |
| 105 | + * The value from <code>${project.build.outputTimestamp}</code> is either formatted as ISO 8601 |
| 106 | + * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like |
| 107 | + * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>. |
| 108 | + * |
| 109 | + * @throws GitCommitIdExecutionException In case the user provided time stamp is invalid a GitCommitIdExecutionException is thrown |
| 110 | + * @return Timestamp for reproducible output archive entries. |
| 111 | + */ |
| 112 | + @Nullable |
| 113 | + Date getReproducibleBuildOutputTimestamp() throws GitCommitIdExecutionException; |
| 114 | + |
| 115 | + /** |
| 116 | + * Set this to {@code 'true'} to use native Git executable to fetch information about the repository. |
| 117 | + * It is in most cases faster but requires a git executable to be installed in system. |
| 118 | + * By default the plugin will use jGit implementation as a source of information about the repository. |
| 119 | + * |
| 120 | + * @return Controls if this plugin should use the native Git executable. |
| 121 | + */ |
| 122 | + boolean useNativeGit(); |
| 123 | + |
| 124 | + /** |
| 125 | + * Allow to specify a timeout (in milliseconds) for fetching information with the native Git executable. |
| 126 | + * Note that {@code useNativeGit} needs to be set to {@code 'true'} to use native Git executable. |
| 127 | + * |
| 128 | + * @return A timeout (in milliseconds) for fetching information with the native Git executable. |
| 129 | + */ |
| 130 | + long getNativeGitTimeoutInMs(); |
| 131 | + |
| 132 | + /** |
| 133 | + * <p>Minimum length of {@code 'git.commit.id.abbrev'} property. |
| 134 | + * Value must be from 2 to 40 (inclusive), other values will result in an exception.</p> |
| 135 | + * |
| 136 | + * <p>An abbreviated commit is a shorter version of commit id. However, it is guaranteed to be unique. |
| 137 | + * To keep this contract, the plugin may decide to print an abbreviated version |
| 138 | + * that is longer than the value specified here.</p> |
| 139 | + * |
| 140 | + * <p><b>Example:</b> You have a very big repository, yet you set this value to 2. It's very probable that you'll end up |
| 141 | + * getting a 4 or 7 char long abbrev version of the commit id. If your repository, on the other hand, |
| 142 | + * has just 4 commits, you'll probably get a 2 char long abbreviation.</p> |
| 143 | + * |
| 144 | + * @return Minimum length of {@code 'git.commit.id.abbrev'} property. |
| 145 | + */ |
| 146 | + int getAbbrevLength(); |
| 147 | + |
| 148 | + /** |
| 149 | + * Configuration for the {@code 'git-describe'} command. |
| 150 | + * You can modify the dirty marker, abbrev length and other options here. |
| 151 | + * |
| 152 | + * @return Configuration for the {@code 'git-describe'} command. |
| 153 | + */ |
| 154 | + GitDescribeConfig getGitDescribe(); |
| 155 | + |
| 156 | + /** |
| 157 | + * <p>The mode of {@code 'git.commit.id'} property generation.</p> |
| 158 | + * |
| 159 | + * {@code 'git.commit.id'} property name is incompatible with json export |
| 160 | + * (see <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/122">issue #122</a>). |
| 161 | + * This property allows one either to preserve backward compatibility or to enable fully valid json export: |
| 162 | + * |
| 163 | + * <ol> |
| 164 | + * <li>{@code 'flat'} (default) generates the property {@code 'git.commit.id'}, preserving backwards compatibility.</li> |
| 165 | + * <li>{@code 'full'} generates the property {@code 'git.commit.id.full'}, enabling fully valid json object export.</li> |
| 166 | + * </ol> |
| 167 | + * |
| 168 | + * <p><b>Note:</b> Depending on your plugin configuration you obviously can choose the `prefix` of your properties |
| 169 | + * by setting it accordingly in the plugin's configuration. As a result this is therefore only an illustration |
| 170 | + * what the switch means when the 'prefix' is set to it's default value.</p> |
| 171 | + * <p><b>Note:</b> If you set the value to something that's not equal to {@code 'flat'} or {@code 'full'} (ignoring the case) |
| 172 | + * the plugin will output a warning and will fallback to the default {@code 'flat'} mode.</p> |
| 173 | + * |
| 174 | + * @return The mode of {@code 'git.commit.id'} property generation. |
| 175 | + */ |
| 176 | + CommitIdGenerationMode getCommitIdGenerationMode(); |
| 177 | + |
| 178 | + /** |
| 179 | + * Use branch name from build environment. Set to {@code 'false'} to use JGit/GIT to get current branch name. |
| 180 | + * Useful when using the JGitflow maven plugin. |
| 181 | + * Note: If not using "Check out to specific local branch' and setting this to false may result in getting |
| 182 | + * detached head state and therefore a commit id as branch name. |
| 183 | + * |
| 184 | + * @return Controls if the branch name from build environment should be used. |
| 185 | + */ |
| 186 | + boolean getUseBranchNameFromBuildEnvironment(); |
| 187 | + |
| 188 | + /** |
| 189 | + * Controls whether the git plugin tries to access remote repos to fetch latest information |
| 190 | + * or only use local information. |
| 191 | + * |
| 192 | + * :warning: Before version 5.X.X the default was set to {@code false} causing the plugin to operate |
| 193 | + * in online-mode by default. |
| 194 | + * |
| 195 | + * @return Controls whether the git plugin tries to access remote repos to fetch latest information. |
| 196 | + */ |
| 197 | + boolean isOffline(); |
| 198 | + |
| 199 | + /** |
| 200 | + * Allow to tell the plugin what commit should be used as reference to generate the properties from. |
| 201 | + * By default this property is simply set to <p>HEAD</p> which should reference to the latest commit in your repository. |
| 202 | + * |
| 203 | + * In general this property can be set to something generic like <p>HEAD^1</p> or point to a branch or tag-name. |
| 204 | + * To support any kind or use-case this configuration can also be set to an entire commit-hash or it's abbreviated version. |
| 205 | + * |
| 206 | + * A use-case for this feature can be found in https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/338. |
| 207 | + * |
| 208 | + * Please note that for security purposes not all references might be allowed as configuration. |
| 209 | + * If you have a specific use-case that is currently not white listed feel free to file an issue. |
| 210 | + * |
| 211 | + * @return Tell the plugin what commit should be used as reference to generate the properties from. |
| 212 | + */ |
| 213 | + String getEvaluateOnCommit(); |
| 214 | + |
| 215 | + /** |
| 216 | + * @return The root directory of the repository we want to check. |
| 217 | + */ |
| 218 | + File getDotGitDirectory(); |
| 219 | + |
| 220 | + /** |
| 221 | + * Set this to {@code 'true'} to generate a {@code 'git.properties'} file. |
| 222 | + * By default plugin only adds properties to maven project properties. |
| 223 | + * |
| 224 | + * @return Control if the plugin should generate a {@code 'git.properties'} file. |
| 225 | + */ |
| 226 | + boolean shouldGenerateGitPropertiesFile(); |
| 227 | + |
| 228 | + /** |
| 229 | + * Callback when the plugin wants to publish a set of properties. |
| 230 | + * @param properties The properties the plugin want's to publish to the user. |
| 231 | + */ |
| 232 | + void performPublishToAllSystemEnvironments(Properties properties); |
| 233 | + |
| 234 | + /** |
| 235 | + * Callback when the plugin wants to perform the properties replacement. |
| 236 | + * @param properties The current set of properties. |
| 237 | + */ |
| 238 | + void performPropertiesReplacement(Properties properties); |
| 239 | + |
| 240 | + /** |
| 241 | + * @return The output format of the generated properties file. Can either be "properties" or "json". |
| 242 | + */ |
| 243 | + // TODO: Why is this a String and not an enum? |
| 244 | + String getPropertiesOutputFormat(); |
| 245 | + |
| 246 | + /** |
| 247 | + * @return The BuildFileChangeListener that will be called when an output file of the plugin has changed |
| 248 | + */ |
| 249 | + BuildFileChangeListener getBuildFileChangeListener(); |
| 250 | + |
| 251 | + /** |
| 252 | + * @return The project name |
| 253 | + */ |
| 254 | + String getProjectName(); |
| 255 | + |
| 256 | + /** |
| 257 | + * @return The project base dir |
| 258 | + */ |
| 259 | + File getProjectBaseDir(); |
| 260 | + |
| 261 | + /** |
| 262 | + * @return The optional name of the properties filename where properties should be dumped into |
| 263 | + */ |
| 264 | + String getGenerateGitPropertiesFilename(); |
| 265 | + |
| 266 | + /** |
| 267 | + * @return The Charset in which format the properties should be dumped (e.g. 'UTF-8') |
| 268 | + */ |
| 269 | + Charset getPropertiesSourceCharset(); |
| 270 | + |
| 271 | + boolean shouldPropertiesEscapeUnicode(); |
| 272 | + } |
| 273 | + |
| 274 | + public static void runPlugin(@Nonnull Callback cb, @Nullable Properties contextProperties) throws GitCommitIdExecutionException { |
| 275 | + PropertiesFilterer propertiesFilterer = new PropertiesFilterer(cb.getLogInterface()); |
| 276 | + |
| 277 | + // The properties we store our data in and then expose them. |
| 278 | + Properties properties = contextProperties == null |
| 279 | + ? new Properties() |
| 280 | + : contextProperties; |
| 281 | + |
| 282 | + loadGitData(cb, properties); |
| 283 | + loadBuildData(cb, properties); |
| 284 | + // first round of publication and filtering |
| 285 | + // (we need to make variables available for the ParameterExpressionEvaluator) |
| 286 | + propertiesFilterer.filter(properties, cb.getIncludeOnlyProperties(), cb.getPrefixDot()); |
| 287 | + propertiesFilterer.filterNot(properties, cb.getExcludeProperties(), cb.getPrefixDot()); |
| 288 | + cb.performPublishToAllSystemEnvironments(properties); |
| 289 | + |
| 290 | + cb.performPropertiesReplacement(properties); |
| 291 | + if (cb.shouldGenerateGitPropertiesFile()) { |
| 292 | + new PropertiesFileGenerator( |
| 293 | + cb.getLogInterface(), |
| 294 | + cb.getBuildFileChangeListener(), |
| 295 | + cb.getPropertiesOutputFormat(), |
| 296 | + cb.getPrefixDot(), |
| 297 | + cb.getProjectName() |
| 298 | + ).maybeGeneratePropertiesFile( |
| 299 | + properties, |
| 300 | + cb.getProjectBaseDir(), |
| 301 | + cb.getGenerateGitPropertiesFilename(), |
| 302 | + cb.getPropertiesSourceCharset(), |
| 303 | + cb.shouldPropertiesEscapeUnicode() |
| 304 | + ); |
| 305 | + } |
| 306 | + |
| 307 | + // publish properties again since we might have new properties gained by the replacement |
| 308 | + cb.performPublishToAllSystemEnvironments(properties); |
| 309 | + } |
| 310 | + |
| 311 | + protected static void loadBuildData(@Nonnull Callback cb, @Nonnull Properties properties) throws GitCommitIdExecutionException { |
| 312 | + Map<String, Supplier<String>> additionalProperties = Collections.singletonMap( |
| 313 | + GitCommitPropertyConstant.BUILD_VERSION, cb.supplyProjectVersion()); |
| 314 | + BuildServerDataProvider buildServerDataProvider = BuildServerDataProvider.getBuildServerProvider( |
| 315 | + System.getenv(), cb.getLogInterface()); |
| 316 | + buildServerDataProvider |
| 317 | + .setDateFormat(cb.getDateFormat()) |
| 318 | + .setDateFormatTimeZone(cb.getDateFormatTimeZone()) |
| 319 | + .setAdditionalProperties(additionalProperties) |
| 320 | + .setPrefixDot(cb.getPrefixDot()) |
| 321 | + .setExcludeProperties(cb.getExcludeProperties()) |
| 322 | + .setIncludeOnlyProperties(cb.getIncludeOnlyProperties()); |
| 323 | + buildServerDataProvider.loadBuildData(properties, cb.getReproducibleBuildOutputTimestamp()); |
| 324 | + } |
| 325 | + |
| 326 | + protected static void loadGitData(@Nonnull Callback cb, @Nonnull Properties properties) throws GitCommitIdExecutionException { |
| 327 | + if (cb.useNativeGit()) { |
| 328 | + loadGitDataWithNativeGit(cb, properties); |
| 329 | + } else { |
| 330 | + loadGitDataWithJGit(cb, properties); |
| 331 | + } |
| 332 | + } |
| 333 | + |
| 334 | + private static void loadGitDataWithNativeGit(@Nonnull Callback cb, @Nonnull Properties properties) throws GitCommitIdExecutionException { |
| 335 | + GitDataProvider nativeGitProvider = NativeGitProvider |
| 336 | + .on(cb.getDotGitDirectory().getParentFile(), cb.getNativeGitTimeoutInMs(), cb.getLogInterface()) |
| 337 | + .setPrefixDot(cb.getPrefixDot()) |
| 338 | + .setAbbrevLength(cb.getAbbrevLength()) |
| 339 | + .setDateFormat(cb.getDateFormat()) |
| 340 | + .setDateFormatTimeZone(cb.getDateFormatTimeZone()) |
| 341 | + .setGitDescribe(cb.getGitDescribe()) |
| 342 | + .setCommitIdGenerationMode(cb.getCommitIdGenerationMode()) |
| 343 | + .setUseBranchNameFromBuildEnvironment(cb.getUseBranchNameFromBuildEnvironment()) |
| 344 | + .setExcludeProperties(cb.getExcludeProperties()) |
| 345 | + .setIncludeOnlyProperties(cb.getIncludeOnlyProperties()) |
| 346 | + .setOffline(cb.isOffline()); |
| 347 | + |
| 348 | + nativeGitProvider.loadGitData(cb.getEvaluateOnCommit(), properties); |
| 349 | + } |
| 350 | + |
| 351 | + private static void loadGitDataWithJGit(@Nonnull Callback cb, @Nonnull Properties properties) throws GitCommitIdExecutionException { |
| 352 | + GitDataProvider jGitProvider = JGitProvider |
| 353 | + .on(cb.getDotGitDirectory(), cb.getLogInterface()) |
| 354 | + .setPrefixDot(cb.getPrefixDot()) |
| 355 | + .setAbbrevLength(cb.getAbbrevLength()) |
| 356 | + .setDateFormat(cb.getDateFormat()) |
| 357 | + .setDateFormatTimeZone(cb.getDateFormatTimeZone()) |
| 358 | + .setGitDescribe(cb.getGitDescribe()) |
| 359 | + .setCommitIdGenerationMode(cb.getCommitIdGenerationMode()) |
| 360 | + .setUseBranchNameFromBuildEnvironment(cb.getUseBranchNameFromBuildEnvironment()) |
| 361 | + .setExcludeProperties(cb.getExcludeProperties()) |
| 362 | + .setIncludeOnlyProperties(cb.getIncludeOnlyProperties()) |
| 363 | + .setOffline(cb.isOffline()); |
| 364 | + |
| 365 | + jGitProvider.loadGitData(cb.getEvaluateOnCommit(), properties); |
| 366 | + } |
| 367 | +} |
0 commit comments