• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

git-commit-id / git-commit-id-maven-plugin / #219

04 Dec 2023 07:24PM CUT coverage: 71.134%. Remained the same
#219

push

web-flow
Merge pull request #676 from git-commit-id/dependabot/maven/org.apache.maven.plugins-maven-javadoc-plugin-3.6.3

Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.6.2 to 3.6.3

276 of 388 relevant lines covered (71.13%)

0.71 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.88
/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java
1
/*
2
 * This file is part of git-commit-id-maven-plugin by Konrad 'ktoso' Malawski <konrad.malawski@java.pl>
3
 *
4
 * git-commit-id-maven-plugin 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-maven-plugin 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-maven-plugin.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17

18
package pl.project13.maven.git;
19

20
import java.io.File;
21
import java.nio.charset.Charset;
22
import java.nio.charset.StandardCharsets;
23
import java.text.DateFormat;
24
import java.text.ParseException;
25
import java.text.SimpleDateFormat;
26
import java.util.*;
27
import java.util.function.Supplier;
28

29
import org.apache.maven.execution.MavenSession;
30
import org.apache.maven.plugin.AbstractMojo;
31
import org.apache.maven.plugin.MojoExecution;
32
import org.apache.maven.plugin.MojoExecutionException;
33
import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
34
import org.apache.maven.plugins.annotations.Component;
35
import org.apache.maven.plugins.annotations.LifecyclePhase;
36
import org.apache.maven.plugins.annotations.Mojo;
37
import org.apache.maven.plugins.annotations.Parameter;
38
import org.apache.maven.project.MavenProject;
39
import org.apache.maven.settings.Settings;
40
import org.sonatype.plexus.build.incremental.BuildContext;
41

42
import pl.project13.core.*;
43
import pl.project13.core.git.GitDescribeConfig;
44
import pl.project13.core.log.LogInterface;
45
import pl.project13.core.util.BuildFileChangeListener;
46

47
import javax.annotation.Nonnull;
48
import javax.annotation.Nullable;
49

50
/**
51
 * Puts git build-time information into property files or maven's properties.
52
 *
53
 * @since 1.0
54
 */
55
@Mojo(name = "revision", defaultPhase = LifecyclePhase.INITIALIZE, threadSafe = true)
56
public class GitCommitIdMojo extends AbstractMojo {
1✔
57
  private static final String CONTEXT_KEY = GitCommitIdMojo.class.getName() + ".properties";
1✔
58

59
  // ============================================================================================================
60
  // Parameter injected by maven itself can't be configured in the pom.xml!
61

62
  /**
63
   * This parameter can't be configured in the {@code pom.xml}
64
   * it represents the Maven Project that will be injected by maven itself.
65
   */
66
  @Parameter(defaultValue = "${project}", readonly = true, required = true)
67
  MavenProject project;
68

69
  /**
70
   * This parameter can't be configured in the {@code pom.xml}
71
   * it represents the list of projects in the reactor that will be injected by maven itself.
72
   */
73
  @Parameter(defaultValue = "${reactorProjects}", readonly = true, required = true)
74
  List<MavenProject> reactorProjects;
75

76
  /**
77
   * This parameter can't be configured in the {@code pom.xml}
78
   * it represents the Mojo Execution that will be injected by maven itself.
79
   */
80
  @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
81
  MojoExecution mojoExecution;
82

83
  /**
84
   * This parameter can't be configured in the {@code pom.xml}
85
   * it represents the Maven Session Object that will be injected by maven itself.
86
   */
87
  @Parameter(defaultValue = "${session}", readonly = true, required = true)
88
  MavenSession session;
89

90
  /**
91
   * This parameter can't be configured in the {@code pom.xml}
92
   * it represents the Maven settings that will be injected by maven itself.
93
   */
94
  @Parameter(defaultValue = "${settings}", readonly = true, required = true)
95
  Settings settings;
96

97
  // ============================================================================================================
98
  // Parameters that can be configured in the pom.xml
99

100
  /**
101
   * Configuration to tell the git-commit-id-maven-plugin if the plugin should
102
   * inject the git properties into all reactor projects not just the current one.
103
   *
104
   * <p>The property is set to {@code false} by default to prevent the overriding of
105
   * properties that may be unrelated to the project. If you need to expose your git properties
106
   * to another maven module (e.g. maven-antrun-plugin) you need to set it to {@code true}.</p>
107
   *
108
   * <p>Inject git properties into all reactor projects, not just the current one
109
   * may slow down the build and you don't always need this feature.</p>
110
   *
111
   * <p>For details about why you might want to skip this, read this issue:
112
   * <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/pull/65">pull #65</a></p>
113
   *
114
   * <p>Example:
115
   * <pre>
116
   * {@code
117
   *     <injectAllReactorProjects>false</injectAllReactorProjects>
118
   * }
119
   * </pre>
120
   * </p>
121
   *
122
   * @since 2.1.4
123
   */
124
  @Parameter(defaultValue = "false")
125
  boolean injectAllReactorProjects;
126

127
  /**
128
   * Configuration to tell the git-commit-id-maven-plugin to print
129
   * some more verbose information during the build
130
   * (e.g. a summary of all collected properties when it's done).
131
   *
132
   * <p>By default this option is disabled (set to {@code false})</p>
133
   *
134
   * <p>Note, If enabled (set to {@code true}) the plugin may
135
   * print information you deem sensible, so be extra cautious when you share those.</p>
136
   *
137
   * <p>Example:
138
   * <pre>
139
   * {@code
140
   *     <verbose>false</verbose>
141
   * }
142
   * </pre>
143
   * </p>
144
   */
145
  @Parameter(defaultValue = "false")
146
  boolean verbose;
147

148
  /**
149
   * Configuration to tell the git-commit-id-maven-plugin to <b>not</b> run in
150
   * a pom packaged project (e.g. {@code <packaging>pom</packaging>}).
151
   *
152
   * <p>By default 'pom' packaged projects will be skipped (to {@code true})</p>
153
   *
154
   * <p>You may want to set this to {@code false}, if the plugin
155
   * should also run inside a pom packaged project.
156
   * Most projects won't need to override this property.
157
   * For an use-case for this kind of behaviour see:
158
   *
159
   * <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/21">Issue 21</a>
160
   * </p>
161
   *
162
   * <p>Note: The plugin might not execute at all, if you also set {@code <runOnlyOnce>true</runOnlyOnce>}</p>
163
   *
164
   * <p>Example:
165
   * <pre>
166
   * {@code
167
   *     <skipPoms>true</skipPoms>
168
   * }
169
   * </pre>
170
   * </p>
171
   */
172
  @Parameter(defaultValue = "true")
173
  boolean skipPoms;
174

175
  /**
176
   * Configuration to tell the git-commit-id-maven-plugin to
177
   * generate a {@code 'git.properties'} file.
178
   * By default the plugin will not <b>not</b> generate such a file (set to {@code false}),
179
   * and only adds properties to maven project properties.
180
   *
181
   * <p>Set this to {@code 'true'} if you want an easy way to expose your git information
182
   * into your final artifact (jar, war, ...), which will generate a properties file (with filled out values)
183
   * that can be configured to end up in the final artifact.
184
   * Refer to the configuration of {@link #generateGitPropertiesFilename}`
185
   * that helps you setup that final path.</p>
186
   *
187
   * <p>Such generated property file, can normally be read using
188
   * <pre>
189
   *     new Properties().load(...)
190
   * </pre>
191
   * during runtime.</p>
192
   *
193
   * <p>Note:
194
   * When writing the {@code git.properties} file the value *git.build.time* will only be updated
195
   * when things in the commit information have changed. If you only change a bit of your code
196
   * and rebuild/rerun you will see an older timestamp that you may have expected. Essentially
197
   * the functional meaning becomes **The latest build time when the git information was written
198
   * to the git.properties file**.
199
   * The reason why this was done can be found in
200
   * [issue 151](https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/151).</p>
201
   *
202
   * <p>Example:
203
   * <pre>
204
   * {@code
205
   *     <generateGitPropertiesFile>true</generateGitPropertiesFile>
206
   * }
207
   * </pre>
208
   * </p>
209
   */
210
  @Parameter(defaultValue = "false")
211
  boolean generateGitPropertiesFile;
212

213
  /**
214
   * Configuration to tell the git-commit-id-maven-plugin about the
215
   * location where you want to generate a {@code 'git.properties'} file.
216
   *
217
   * <p>By default the file would be generated under
218
   * {@code ${project.build.outputDirectory}/git.properties}, but you would need to
219
   * set {@link #generateGitPropertiesFile} to {@code true} first to "activate" the generation of this file.
220
   * You can also choose the format of the generated properties by specifying it under {@link #format}.</p>
221
   *
222
   * <p>The path can be relative to {@code ${project.basedir}} (e.g. {@code target/classes/git.properties}) or
223
   * can be a full path (e.g. {@code ${project.build.outputDirectory}/git.properties}).</p>
224
   *
225
   * <p>Note: If you plan to set the generateGitPropertiesFilename-Path to a location where usually
226
   * the source-files comes from (e.g. {@code src/main/resources}) and experience that your IDE
227
   * (e.g. eclipse) invokes "Maven Project Builder" once every second, the chances that you
228
   * are using an IDE where the src-folder is a watched folder for files that are <b>only</b>
229
   * edited by humans is pretty high.
230
   * <br>
231
   * For further information refer to the manual for your
232
   * specific IDE and check the workflow of "incremental project builders".
233
   * <br>
234
   * In order to fix this problem we recommend to set the generateGitPropertiesFilename-Path
235
   * to a target folder (e.g. {@code ${project.build.outputDirectory}}) since this is
236
   * the place where all derived/generated resources should go.
237
   * <br>
238
   * With plugin version 3.0.0 we introduced a smarter way to counter that issue, but that might not
239
   * be supported by your IDE.
240
   * See: <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/pull/385">pull 385</a>
241
   * for further information</p>
242
   *
243
   * <p>Example:
244
   * <pre>
245
   * {@code
246
   *     <generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
247
   * }
248
   * </pre>
249
   * </p>
250
   *
251
   */
252
  @Parameter(defaultValue = "${project.build.outputDirectory}/git.properties")
253
  String generateGitPropertiesFilename;
254

255
  /**
256
   * Controls whether special characters in the properties
257
   * within the {@link #generateGitPropertiesFilename} should be unicode escaped.
258
   * By default properties are escaped (e.g. \\u6E2C\\u8A66\\u4E2D\\u6587).
259
   * If you write commit messages in chinese and want to extract the message
260
   * without any additional conversion from the generated properties
261
   * you may want to set this to {@code false}.
262
   *
263
   * <p>See <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/590">issue 590</a>
264
   * for further details.</p>
265
   *
266
   * <p>Example:
267
   * <pre>
268
   * {@code
269
   *    <generateGitPropertiesFileWithEscapedUnicode>true</generateGitPropertiesFileWithEscapedUnicode>
270
   * }
271
   * </pre>
272
   * </p>
273
   *
274
   * @since 6.0.0
275
   */
276
  @Parameter(defaultValue = "true")
277
  boolean generateGitPropertiesFileWithEscapedUnicode;
278

279
  /**
280
   * Configuration to tell the git-commit-id-maven-plugin about the
281
   * root directory of the git repository we want to check.
282
   * By default uses {@code ${project.basedir}/.git} will most probably be
283
   * ok for single module projects, in other cases please use `../` to get higher up
284
   * in the dir tree (e.g. {@code ${project.basedir}/../.git}).
285
   *
286
   * <p>Example:
287
   * <pre>
288
   * {@code
289
   *    <dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
290
   * }
291
   * </pre>
292
   * </p>
293
   */
294
  @Parameter(defaultValue = "${project.basedir}/.git")
295
  File dotGitDirectory;
296

297
  /**
298
   * Configuration for the {@code 'git-describe'} command.
299
   * You can modify the dirty marker, abbrev length and other options here.
300
   * The following `gitDescribe` configuration below is optional and can be leveraged as a
301
   * really powerful versioning helper. If you are not familiar with
302
   * <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin#git-describe-&#45;-short-intro-to-an-awesome-command">git-describe</a>
303
   * it is highly recommended to go through this part of the documentation.
304
   *
305
   * <p>More advanced users can most likely skip the explanations in this section, as it just explains the
306
   * same options that git provides.
307
   * As a side note this plugin tries to be 1-to-1 compatible with git's plain output, even
308
   * though the describe functionality has been reimplemented manually using JGit (you don't
309
   * have to have a git executable to use the plugin).</p>
310
   *
311
   * For further information refer to <a href="https://git-scm.com/docs/git-describe">this</a>.
312
   *
313
   * <p>Example:
314
   *     <pre>
315
   *     {@code
316
   *         <gitDescribe>
317
   *             <!--
318
   *             Default (optional):
319
   *             false
320
   *
321
   *             Explanation:
322
   *             When you don't want to use `git-describe` information in your build, you can set this
323
   *             to `true` to avoid to calculate it.
324
   *             -->
325
   *             <skip>false</skip>
326
   *
327
   *             <!--
328
   *             Default (optional):
329
   *             true
330
   *
331
   *             Explanation:
332
   *             In some cases no tag can be found `near` this commit (e.g. usually when performing a
333
   *             shallow clone). If this is set to `true` and no tag was found, this property will
334
   *             fallback to the commit's id instead (when `true` this property will not become empty).
335
   *             Set this to `true` when you *always* want to return something meaningful in the
336
   *             describe property.
337
   *             -->
338
   *             <always>true</always>
339
   *
340
   *             <!--
341
   *             Default (optional):
342
   *             7
343
   *
344
   *             Explanation:
345
   *             In the describe output, the object id of the hash is always abbreviated to N letters
346
   *             (by default 7).
347
   *
348
   *             The typical describe output you'll see therefore is: `v2.1.0-1-gf5cd254`, where `-1-`
349
   *             means the number of commits away from the mentioned tag and the `-gf5cd254` part means
350
   *             the first 7 chars of the current commit's id `f5cd254`.
351
   *             Setting *abbrev* to `0` has the effect of hiding the "distance from tag" and
352
   *             "object id" parts of the output, so you end up with just the "nearest tag" (that is,
353
   *             instead `tag-12-gaaaaaaa` with `abbrev = 0` you'd get `tag`).
354
   *
355
   *             **Please note that the `g` prefix is included to notify you that it's a commit id,
356
   *             it is NOT part of the commit's object id** - *this is default git behaviour, so we're
357
   *             doing the same*.
358
   *
359
   *             You can set this to any value between 0 and 40 (inclusive). `0` carries the special
360
   *             meaning (checkout the [git describe documentation](docs/git-describe.md) for the
361
   *             special case abbrev = 0).
362
   *             Maximum value is `40`, because of max SHA-1 length.
363
   *             -->
364
   *             <abbrev>7</abbrev>
365
   *
366
   *             <!--
367
   *             Default (optional):
368
   *             -dirty
369
   *
370
   *             Explanation:
371
   *             When you run describe on a repository that's in "dirty state" (has uncommitted
372
   *             changes), the describe output will contain an additional suffix, such as "-devel"
373
   *             in this example: `v3.5-3-g2222222-devel`. This configuration allows you to alter
374
   *             that additional suffix and gets appended to describe, while the repo is in
375
   *             "dirty state". You can configure that suffix to be anything you want, "-DEV" being
376
   *             a nice example. The "-" sign should be included in the configuration parameter, as it
377
   *             will not be added automatically. If in doubt run `git describe &#45;-dirty=-my_thing`
378
   *             to see how the end result will look like.
379
   *             -->
380
   *             <dirty>-dirty</dirty>
381
   *
382
   *             <!--
383
   *             Default (optional):
384
   *             * (include all tags)
385
   *
386
   *             Explanation:
387
   *             Git describe may contain information to tag names. Set this configuration to only
388
   *             consider tags matching the given pattern.
389
   *             This can be used to avoid leaking private tags from the repository.
390
   *             -->
391
   *             <match>*</match>
392
   *
393
   *             <!--
394
   *             Default (optional):
395
   *             false
396
   *
397
   *             Explanation:
398
   *             When you run git-describe it only looks only for *annotated tags* by default.
399
   *             If you wish to consider *lightweight tags* in your describe as well you would need
400
   *             to switch this to `true`.
401
   *
402
   *             The difference between *annotated tags* and *lightweight tags* is outlined in more
403
   *             depth here: https://github.com/git-commit-id/git-commit-id-maven-plugin/#git-describe-and-a-small-gotcha-with-tags
404
   *             -->
405
   *             <tags>false</tags>
406
   *
407
   *             <!--
408
   *             Default (optional):
409
   *             false
410
   *
411
   *             Explanation:
412
   *             git-describe, by default, returns just the tag name, if the current commit is tagged.
413
   *             Set this option to `true` to force it to format the output using the typical describe
414
   *             format ("$tag_name-$commits_from_tag-g$commit_id-maybe_dirty"), even if "on" a tag.
415
   *
416
   *             An example would be: `tagname-0-gc0ffebabe` - notice that the distance from the tag is
417
   *             0 here, if you don't use **forceLongFormat** mode, the describe for such commit would
418
   *             look like this: `tagname`.
419
   *             -->
420
   *             <forceLongFormat>false</forceLongFormat>
421
   *         </gitDescribe>
422
   *     }
423
   *     </pre>
424
   * </p>
425
   *
426
   * @since 2.1.0
427
   */
428
  @Parameter
429
  GitDescribeConfig gitDescribe;
430

431
  /**
432
   * <p>Minimum length of {@code 'git.commit.id.abbrev'} property.
433
   * Value must be from 2 to 40 (inclusive), other values will result in an exception.</p>
434
   *
435
   * <p>Defaults to `7`</p>
436
   *
437
   * <p>An abbreviated commit is a shorter version of commit id. However, it is guaranteed to be unique.
438
   * To keep this contract, the plugin may decide to print an abbreviated version
439
   * that is longer than the value specified here.</p>
440
   *
441
   * <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
442
   * getting a 4 or 7 char long abbrev version of the commit id. If your repository, on the other hand,
443
   * has just 4 commits, you'll probably get a 2 char long abbreviation.</p>
444
   *
445
   * <p>Example:
446
   * <pre>
447
   * {@code
448
   *    <abbrevLength>7</abbrevLength>
449
   * }
450
   * </pre>
451
   * </p>
452
   *
453
   * @since 2.0.4
454
   */
455
  @Parameter(defaultValue = "7")
456
  int abbrevLength;
457

458
  /**
459
   * Denotes the format to save properties of the properties file that can be configured with
460
   * {@link #generateGitPropertiesFilename}.
461
   *
462
   * <p>Valid options are encoded in {@link CommitIdPropertiesOutputFormat}
463
   * and currently would allow "properties" (default) and "json".
464
   * Future option like yml, toml, ... might be supported at some point.</p>
465
   *
466
   * <p>Note:
467
   * If you set this to "json", you might also should checkout the documentation about
468
   * {@link #commitIdGenerationMode} and may want to set
469
   * {@code <commitIdGenerationMode>full</commitIdGenerationMode>}.
470
   * </p>
471
   *
472
   * <p>Example:
473
   * <pre>
474
   * {@code
475
   *    <format>properties</format>
476
   * }
477
   * </pre>
478
   * </p>
479
   */
480
  @Parameter(defaultValue = "properties")
481
  String format;
482

483
  /**
484
   * Not settable by any configuration in the {@code pom.xml}.
485
   * For internal use only (represents the {@link #format} the user has set as enum.
486
   */
487
  private CommitIdPropertiesOutputFormat commitIdPropertiesOutputFormat;
488

489
  /**
490
   * Configuration to tell the git-commit-id-maven-plugin about the
491
   * property that will be used as the "namespace" prefix for all exposed/generated properties.
492
   * An example the plugin may generate the property `${configured-prefix}.commit.id`.
493
   * Such behaviour can be used to generate properties for multiple git repositories (see
494
   * <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/137#issuecomment-418144756">issue 173</a>
495
   * for a full example).
496
   *
497
   * <p>By default is set to {@code 'git'} that for example would allow you to access {@code ${git.branch}}</p>
498
   *
499
   * <p>Example:
500
   * <pre>
501
   * {@code
502
   *    <prefix>git</prefix>
503
   * }
504
   * </pre>
505
   * </p>
506
   */
507
  @Parameter(defaultValue = "git")
508
  String prefix;
509

510
  /**
511
   * This date format will be used to format the time of any exposed/generated property
512
   * that represents dates or times exported by this plugin (e.g. {@code git.commit.time}, {@code git.build.time}).
513
   * It should be a valid {@link SimpleDateFormat} string.
514
   *
515
   * <p>
516
   * The current dateFormat is set to match maven's default {@code yyyy-MM-dd'T'HH:mm:ssZ}.
517
   * Please note that in previous versions (2.2.0 - 2.2.2) the default dateFormat was set to:
518
   * {@code dd.MM.yyyy '@' HH:mm:ss z}. However the {@code RFC 822 time zone} seems to give a more
519
   * reliable option in parsing the date and it's being used in maven as default.</p>
520
   *
521
   * <p>Example:
522
   * <pre>
523
   * {@code
524
   *    <dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat>
525
   * }
526
   * </pre>
527
   * </p>
528
   *
529
   * @since 2.2.0
530
   */
531
  @Parameter(defaultValue = "yyyy-MM-dd'T'HH:mm:ssZ")
532
  String dateFormat;
533

534
  /**
535
   * <p>The timezone used in the {@link #dateFormat} of dates exported by this plugin (e.g. {@code git.commit.time}, {@code git.build.time}).
536
   * It should be a valid Timezone string such as {@code 'America/Los_Angeles'}, {@code 'GMT+10'} or {@code 'PST'}.</p>
537
   *
538
   * <p>As a general warning try to avoid three-letter time zone IDs because the same abbreviation are often used for multiple time zones.
539
   * 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>
540
   * for more information on this issue.</p>
541
   *
542
   * <p>The default value we'll use the timezone use the timezone that's shipped with java
543
   * ({@code java.util.TimeZone.getDefault().getID()}).
544
   * <b>Note</b>: If you plan to set the java's timezone by using
545
   * {@code MAVEN_OPTS=-Duser.timezone=UTC mvn clean package},
546
   * {@code mvn clean package -Duser.timezone=UTC},
547
   * or any other configuration keep in mind that this option will override those settings and
548
   * will not take other configurations into account!</p>
549
   *
550
   * <p>Example:
551
   * <pre>
552
   * {@code
553
   *     <dateFormatTimeZone>${user.timezone}</dateFormatTimeZone>
554
   * }
555
   * </pre>
556
   * </p>
557
   *
558
   * @since 2.2.0
559
   */
560
  @Parameter
561
  String dateFormatTimeZone;
562

563
  /**
564
   * Specify whether the plugin should fail when a {@code '.git'} directory cannot be found.
565
   * When set to {@code false} and no {@code .git} directory is found the plugin will skip execution.
566
   *
567
   * <p>Defaults to {@code true}, so a missing {@code '.git'} directory is treated as error
568
   * and should cause a failure in your build.</p>
569
   *
570
   * <p>Example:
571
   * <pre>
572
   * {@code
573
   *     <failOnNoGitDirectory>true</failOnNoGitDirectory>
574
   * }
575
   * </pre>
576
   * </p>
577
   *
578
   * @since 2.0.4
579
   */
580
  @Parameter(defaultValue = "true")
581
  boolean failOnNoGitDirectory;
582

583
  /**
584
   * <p>Set this to {@code false} to continue the build even if unable to get enough data for a complete run.
585
   * This may be useful during CI builds if the CI server does weird things to the repository.</p>
586
   *
587
   * <p>Setting this value to {@code false} causes the plugin to gracefully tell you "I did my best"
588
   * and abort its execution if unable to obtain git meta data - yet the build will continue to run without failing.</p>
589
   *
590
   * <p>By default the plugin will fail the build (set to {@code true}) if unable to obtain enough data for a complete
591
   * run.</p>
592
   *
593
   * <p>See <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/63">issue #63</a>
594
   * for a rationale behind this flag.</p>
595
   *
596
   * <p>Example:
597
   * <pre>
598
   * {@code
599
   *     <failOnUnableToExtractRepoInfo>true</failOnUnableToExtractRepoInfo>
600
   * }
601
   * </pre>
602
   * </p>
603
   *
604
   * @since 2.1.5
605
   */
606
  @Parameter(defaultValue = "true")
607
  boolean failOnUnableToExtractRepoInfo;
608

609
  /**
610
   * This plugin ships with custom {@code jgit} implementation that is being used to obtain all relevant information.
611
   * If set to {@code true} the plugin will use native git executable instead of the custom {@code jgit} implementation
612
   * to fetch information about the repository.
613
   * Of course if set to {@code true} will require a git executable to be installed in system.
614
   *
615
   * <p>Although setting this to {@code true} (use the native git executable)
616
   * should usually give your build some performance boost, it may randomly
617
   * break if you upgrade your git version and it decides to print information in a different
618
   * format suddenly.</p>
619
   *
620
   * <p>By default the plugin will use {@code jgit} implementation as a source of information about the repository.
621
   * As rule of thumb, keep using the default {@code jgit} implementation (set to {@code false})
622
   * until you notice performance problems within your build (usually when you have *hundreds* of maven modules).</p>
623
   *
624
   * <p>With plugin version *3.0.2* you can also control it using the commandline option
625
   * {@code -Dmaven.gitcommitid.nativegit=true}. See {@link #useNativeGitViaCommandLine}</p>
626
   *
627
   * <p>Example:
628
   * <pre>
629
   * {@code
630
   *     <useNativeGit>true</useNativeGit>
631
   * }
632
   * </pre>
633
   * </p>
634
   *
635
   * @since 2.1.9
636
   */
637
  @Parameter(defaultValue = "false")
638
  boolean useNativeGit;
639

640
  /**
641
   * Option to be used in command-line to override the value of {@link #useNativeGit} specified in
642
   * the pom.xml, or its default value if it's not set explicitly.
643
   *
644
   * <p>NOTE / WARNING:
645
   * Do *NOT* set this property inside the configuration of your plugin.
646
   * Please read <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/315">issue 315</a>
647
   * to find out why.</p>
648
   *
649
   * <p>Example:
650
   * <pre>
651
   * {@code
652
   *     mvn clean package -Dmaven.gitcommitid.nativegit=true
653
   * }
654
   * </pre>
655
   * </p>
656
   *
657
   * @since 3.0.2
658
   */
659
  @Parameter(property = "maven.gitcommitid.nativegit", defaultValue = "false")
660
  boolean useNativeGitViaCommandLine;
661

662
  /**
663
   * When set to {@code true} the plugin execution will completely skip.
664
   * This is useful for e.g. profile activated plugin invocations or to use properties to
665
   * enable / disable pom features.
666
   *
667
   * <p>By default the execution is not skipped (set to {@code false})</p>
668
   *
669
   * <p>With version *2.2.3* you can also skip the plugin by using the commandline option
670
   * {@code -Dmaven.gitcommitid.skip=true}. See {@link #skipViaCommandLine}</p>
671
   *
672
   * <p>Example:
673
   * <pre>
674
   * {@code
675
   *     <skip>false</skip>
676
   * }
677
   * </pre>
678
   * </p>
679
   *
680
   * @since 2.1.8
681
   */
682
  @Parameter(defaultValue = "false")
683
  boolean skip;
684

685
  /**
686
   * Option to be used in command-line to override the value of {@link #skip} specified in
687
   * the pom.xml, or its default value if it's not set explicitly.
688
   * Set this to {@code true} to skip plugin execution via commandline.
689
   *
690
   * <p>NOTE / WARNING:
691
   * Do *NOT* set this property inside the configuration of your plugin.
692
   * Please read <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/315">issue 315</a>
693
   * to find out why.</p>
694
   *
695
   * <p>Example:
696
   * <pre>
697
   * {@code
698
   *     mvn clean package -Dmaven.gitcommitid.skip=true
699
   * }
700
   * </pre>
701
   * </p>
702
   *
703
   * @since 2.2.4
704
   */
705
  @Parameter(property = "maven.gitcommitid.skip", defaultValue = "false")
706
  private boolean skipViaCommandLine;
707

708
  /**
709
   * Use with caution!
710
   *
711
   * <p>Set this to {@code true} to only run once in a multi-module build.
712
   * This means that the plugins effects will only execute once for the first project in the execution graph.
713
   * If {@code skipPoms} is set to {@code true} (default) the plugin will run for the first
714
   * non pom project in the execution graph (as listed in the reactor build order).
715
   * This probably won't "do the right thing" if your project has more than one git repository.</p>
716
   *
717
   * <p>Defaults to {@code false}, so the plugin may get executed multiple times in a reactor build!</p>
718
   *
719
   * <p>Important: If you're using {@link #generateGitPropertiesFile}, setting {@code runOnlyOnce} will make
720
   * the plugin only generate the file in the project build directory which is the first one
721
   * based on the execution graph (!).</p>
722
   *
723
   * <p>Important: Please note that the git-commit-id-maven-plugin also has an option to skip pom
724
   * project ({@code <packaging>pom</packaging>}). If you plan to use the {@code runOnlyOnce} option
725
   * alongside with an aggregator pom you may want to set {@code <skipPoms>false</skipPoms>}.
726
   * Refer to {@link #skipPoms} for more information</p>
727
   *
728
   * <p>For multi-module build you might also want to set {@link #injectAllReactorProjects} to make
729
   * the {@code git.*} maven properties available in all modules.</p>
730
   *
731
   * <p>Note:
732
   * Prior to version 4.0.0 the plugin was simply using the execute once applied for the parent
733
   * project (which might have skipped execution if the parent project was a pom project).</p>
734
   *
735
   * <p>Example:
736
   * <pre>
737
   * {@code
738
   *     <runOnlyOnce>true</runOnlyOnce>
739
   * }
740
   * </pre>
741
   * </p>
742
   *
743
   * @since 2.1.12
744
   */
745
  @Parameter(defaultValue = "false")
746
  boolean runOnlyOnce;
747

748
  /**
749
   * Can be used to exclude certain properties from being emitted (e.g. filter out properties
750
   * that you *don't* want to expose). May be useful when you want to hide
751
   * {@code git.build.user.email} (maybe because you don't want to expose your eMail?),
752
   * or the email of the committer?
753
   *
754
   * <p>Each value may be globbing, that is, you can write {@code git.commit.user.*} to
755
   * exclude both the {@code name}, as well as {@code email} properties from being emitted.
756
   *
757
   * <p>Please note that the strings here are Java regexes ({@code .*} is globbing,
758
   * not plain {@code *}).
759
   * If you have a very long list of exclusions you may want to
760
   * use {@link #includeOnlyProperties}.
761
   *
762
   * <p>This feature was implemented in response to <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/91">issue 91</a>,
763
   * so if you're curious about the use-case, check that issue.</p>
764
   *
765
   * <p>Prior to version 3.0.0 the plugin used the 'naive' approach to ask for all properties
766
   * and then apply filtering. However, with the growing numbers of properties each property
767
   * eat more and more of execution time that will be filtered out afterwards.
768
   * With 3.0.0 this behaviour was readjusted to a 'selective running' approach whereby the
769
   * plugin will not even try to get the property when excluded. Such behaviour can result in
770
   * an overall reduced execution time of the plugin
771
   * (see <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/408">issue 408</a> for details).</p>
772
   *
773
   * <p>Defaults to the empty list (= no properties are excluded).
774
   *
775
   * <p>Example:
776
   * <pre>
777
   * {@code
778
   *     <excludeProperties>
779
   *          <excludeProperty>git.user.*</excludeProperty>
780
   *     </excludeProperties>
781
   * }
782
   * </pre>
783
   * </p>
784
   *
785
   * @since 2.1.9
786
   */
787
  @Parameter
788
  List<String> excludeProperties;
789

790
  /**
791
   * Can be used to include only certain properties into the emission (e.g. include only
792
   * properties that you <b>want</b> to expose). This feature was implemented to avoid big exclude
793
   * properties tag when we only want very few specific properties.
794
   *
795
   * <p>The inclusion rules, will be overruled by the {@link #excludeProperties} rules
796
   * (e.g. you can write an inclusion rule that applies for multiple
797
   * properties and then exclude a subset of them).
798
   * You can therefor can be a bit broader in the inclusion rules and
799
   * exclude more sensitive ones  in the {@link #excludeProperties} rules.
800
   *
801
   * <p>Each value may be globbing, that is, you can write {@code git.commit.user.*} to
802
   * exclude both the {@code name}, as well as {@code email} properties from being emitted.
803
   *
804
   * <p>Please note that the strings here are Java regexes ({@code .*} is globbing,
805
   * not plain {@code *}).
806
   * If you have a short list of exclusions you may want to
807
   * use {@link #excludeProperties}.
808
   *
809
   * <p>Prior to version 3.0.0 the plugin used the 'naive' approach to ask for all properties
810
   * and then apply filtering. However, with the growing numbers of properties each property
811
   * eat more and more of execution time that will be filtered out afterwards.
812
   * With 3.0.0 this behaviour was readjusted to a 'selective running' approach whereby the
813
   * plugin will not even try to get the property when excluded. Such behaviour can result in
814
   * an overall reduced execution time of the plugin
815
   * (see <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/408">issue 408</a> for details).</p>
816
   *
817
   * <p>Defaults to the empty list (= no properties are excluded).</p>
818
   *
819
   * <p>Example:
820
   * <pre>
821
   * {@code
822
   *     <includeOnlyProperties>
823
   *         <includeOnlyProperty>^git.commit.id.full$</includeOnlyProperty>
824
   *     </includeOnlyProperties>
825
   * }
826
   * </pre>
827
   * </p>
828
   *
829
   * @since 2.1.14
830
   */
831
  @Parameter
832
  List<String> includeOnlyProperties;
833

834
  /**
835
   * The option can be used to tell the plugin how it should generate the {@code 'git.commit.id'} property.
836
   * Due to some naming issues when exporting the properties as an json-object
837
   * (<a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/122">issue 122</a>) we needed to
838
   * make it possible to export all properties as a valid json-object.
839
   *
840
   * <p>Due to the fact that this is one of the major properties the plugin is exporting we
841
   * just don't want to change the exporting mechanism and somehow throw the backwards
842
   * compatibility away.
843
   * We rather provide a convenient switch where you can choose if you
844
   * would like the properties as they always had been, or if you rather need to support
845
   * full json-object compatibility.</p>
846
   *
847
   * <p>In the case you need to fully support json-object we unfortunately need to change the
848
   * {@code 'git.commit.id'} property from {@code 'git.commit.id'} to {@code 'git.commit.id.full'} in the exporting
849
   * mechanism to allow the generation of a fully valid json object.</p>
850
   *
851
   * <p>Currently, the switch allows two different options:
852
   * <ol>
853
   *     <li>
854
   *         By default this property is set to {@code 'flat'} and will generate the formerly known
855
   *         property {@code 'git.commit.id'} as it was in the previous versions of the plugin.
856
   *         Keeping it to {@code 'flat'} by default preserve backwards compatibility and does not require further
857
   *         adjustments by the end user.
858
   *     </li>
859
   *     <li>
860
   *         If you set this switch to {@code 'full'} the plugin will export the formerly known property
861
   *         {@code 'git.commit.id'} as {@code 'git.commit.id.full'} and therefore will generate a fully valid
862
   *         json object in the exporting mechanism.
863
   *     </li>
864
   * </ol></p>
865
   *
866
   * <p><b>Note:</b> If you set the value to something that's not equal to {@code 'flat'} or {@code 'full'} (ignoring the case)
867
   * the plugin will output a warning and will fallback to the default {@code 'flat'} mode.</p>
868
   *
869
   *
870
   * <p>Example:
871
   * <pre>
872
   * {@code
873
   *     <commitIdGenerationMode>flat</commitIdGenerationMode>
874
   * }
875
   * </pre>
876
   * </p>
877
   *
878
   * @since 2.2.0
879
   */
880
  @Parameter(defaultValue = "flat")
881
  String commitIdGenerationMode;
882

883
  /**
884
   * Not settable by any configuration in the {@code pom.xml}.
885
   * For internal use only (represents the {@link #commitIdGenerationMode} the user has set as enum.
886
   */
887
  private CommitIdGenerationMode commitIdGenerationModeEnum;
888

889
  /**
890
   * Can be used to replace certain characters or strings using regular expressions within the
891
   * exposed properties.
892
   * Replacements can be performed using regular expressions and on a configuration level
893
   * it can be defined whether the replacement should affect all properties or just a single one.
894
   *
895
   * Please note that the replacement will only be applied to properties that are being generated by the plugin.
896
   * If you want to replace properties that are being generated by other plugins you may want to use the
897
   * maven-replacer-plugin or any other alternative.
898
   *
899
   * Replacements can be configured with a {@code replacementProperty}.
900
   * A {@code replacementProperty} can have a {@code property}` and a {@code regex}-tag.
901
   * If the {@code replacementProperty} configuration has a {@code property}-tag the
902
   * replacement will only be performed on that specific property
903
   * (e.g. {@code <property>git.branch</property>} will only be performed on {@code git.branch}).
904
   *
905
   * In case this specific element is not defined or left empty the replacement will be
906
   * performed <b>on all generated properties</b>.
907
   *
908
   * The optional {@code regex}-tag can either be {@code true} to perform a replacement with regular
909
   * expressions or {@code false} to perform a replacement with java's string.replace-function.
910
   *
911
   * By default the replacement will be performed with regular expressions ({@code true}).
912
   * Furthermore each {@code replacementProperty} need to be configured with a {@code token} and a {@code value}.
913
   * The {@code token} can be seen as the needle and the {@code value} as the text to be written over any
914
   * found tokens. If using regular expressions the value can reference grouped regex matches
915
   * by using $1, $2, etc.
916
   *
917
   * Since 2.2.4 the plugin allows to define a even more sophisticated ruleset and allows to
918
   * set an {@code propertyOutputSuffix} within each {@code replacementProperty}.
919
   * If this option is empty the original property will be overwritten (default behaviour in 2.2.3).
920
   * However when this configuration is set to {@code something} and a user wants to modify the
921
   * {@code git.branch} property the plugin will keep {@code git.branch} as the original one (w/o modifications)
922
   * but also will be creating a new {@code git.branch.something} property with the requested replacement.
923
   *
924
   * Furthermore with 2.2.4 the plugin allows to perform certain types of string manipulation
925
   * either before or after the evaluation of the replacement.
926
   * With this feature a user can currently easily manipulate the case (e.g. lower case VS upper case)
927
   * of the input/output property.
928
   * This behaviour can be achieved by defining a list of {@code transformationRules} for
929
   * the property where those rules should take effect.
930
   * Each {@code transformationRule} consist of two required fields {@code apply} and {@code action}.
931
   * The {@code apply}-tag controls when the rule should be applied and can be set to {@code BEFORE}
932
   * to have the rule being applied before or it can be set to {@code AFTER} to have the
933
   * rule being applied after the replacement.
934
   * The {@code action}-tag determines the string conversion rule that should be applied.
935
   * Currently supported is {@code LOWER_CASE} and {@code UPPER_CASE}.
936
   * Potential candidates in the feature are {@code CAPITALIZATION} and {@code INVERT_CASE}
937
   * (open a ticket if you need them...).
938
   *
939
   *  Since 4.0.1 the plugin allows to define a {@code forceValueEvaluation}-switch which forces the
940
   *  plugin to evaluate the given value on <b>every</b> project.
941
   *
942
   *  This might come handy if <b>every</b> project needs a unique value and a user wants to
943
   *  project specific variables like {@code ${project.artifactId}}.
944
   *  Be advised that this essentially means that the plugin <b>must</b> run for every child-project of a
945
   *  reactor build and thus might cause some overhead (the git properties should be cached).
946
   *  For a use-case refer to <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/457">issue 457</a>
947
   *
948
   * <p>Defaults to the empty list / not set (= no properties are being replaced by default)</p>
949
   *
950
   * <p>Example:
951
   * <pre>
952
   * {@code
953
   *     <replacementProperties>
954
   *         <!--
955
   *             example:
956
   *             apply replacement only to the specific property git.branch and replace '/' with '-'
957
   *             see also https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/138
958
   *         -->
959
   *         <replacementProperty>
960
   *             <property>git.branch</property>
961
   *             <propertyOutputSuffix>something</propertyOutputSuffix>
962
   *             <token>^([^\/]*)\/([^\/]*)$</token>
963
   *             <value>$1-$2</value>
964
   *             <regex>true</regex>
965
   *             <forceValueEvaluation>false</forceValueEvaluation>
966
   *             <transformationRules>
967
   *                 <transformationRule>
968
   *                     <apply>BEFORE</apply>
969
   *                     <action>UPPER_CASE</action>
970
   *                 </transformationRule>
971
   *                 <transformationRule>
972
   *                     <apply>AFTER</apply>
973
   *                     <action>LOWER_CASE</action>
974
   *                 </transformationRule>
975
   *             </transformationRules>
976
   *         </replacementProperty>
977
   *     </replacementProperties>
978
   * }
979
   * </pre>
980
   * </p>
981
   *
982
   * @since 2.2.3
983
   */
984
  @Parameter
985
  List<ReplacementProperty> replacementProperties;
986

987
  /**
988
   * Allow to tell the plugin what commit should be used as reference to
989
   * generate the properties from.
990
   *
991
   * <p>In general this property can be set to something generic like {@code HEAD^1} or point to a
992
   * branch or tag-name. To support any kind or use-case this configuration can also be set
993
   * to an entire commit-hash or it's abbreviated version.</p>
994
   *
995
   * <p>A use-case for this feature can be found in
996
   * <a href="https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/338">here</a>.</p>
997
   *
998
   * <p>Please note that for security purposes not all references might
999
   * be allowed as configuration. If you have a specific use-case that is currently
1000
   * not white listed feel free to file an issue.</p>
1001
   *
1002
   * <p>By default this property is simply set to {@code HEAD} which should reference to the latest
1003
   * commit in your repository.</p>
1004
   *
1005
   * <p>Example:
1006
   * <pre>
1007
   * {@code
1008
   *     <evaluateOnCommit>HEAD</evaluateOnCommit>
1009
   * }
1010
   * </pre>
1011
   * </p>
1012
   *
1013
   * @since 2.2.4
1014
   */
1015
  @Parameter(defaultValue = "HEAD")
1016
  String evaluateOnCommit;
1017

1018
  /**
1019
   * Allow to specify a timeout (in milliseconds) for fetching information with the native
1020
   * Git executable. This option might come in handy in cases where fetching information
1021
   * about the repository with the native Git executable does not terminate.
1022
   *
1023
   * <p>Note: This option will only be taken into consideration when using the native git
1024
   * executable ({@link #useNativeGit} is set to {@code true}).
1025
   *
1026
   * <p>By default this timeout is set to 30000 (30 seconds).
1027
   *
1028
   * <p>Example:
1029
   * <pre>
1030
   * {@code
1031
   *     <nativeGitTimeoutInMs>30000</nativeGitTimeoutInMs>
1032
   * }
1033
   * </pre>
1034
   * </p>
1035
   *
1036
   * @since 3.0.0
1037
   */
1038
  @Parameter(defaultValue = "30000")
1039
  long nativeGitTimeoutInMs;
1040

1041
  /**
1042
   * When set to {@code true} this plugin will try to use the branch name from build environment.
1043
   * Set to {@code false} to use JGit/GIT to get current branch name which can be useful
1044
   * when using the JGitflow maven plugin.
1045
   * See https://github.com/git-commit-id/git-commit-id-maven-plugin/issues/24#issuecomment-203285398
1046
   *
1047
   * Note: If not using "Check out to specific local branch' and setting this to false may result in getting
1048
   * detached head state and therefore a commit id as branch name.
1049
   *
1050
   * <p>By default this is set to {@code true}.</p>
1051
   *
1052
   * <p>Example:
1053
   * <pre>
1054
   * {@code
1055
   *     <useBranchNameFromBuildEnvironment>true</useBranchNameFromBuildEnvironment>
1056
   * }
1057
   * </pre>
1058
   * </p>
1059
   *
1060
   * @since 3.0.0
1061
   */
1062
  @Parameter(defaultValue = "true")
1063
  boolean useBranchNameFromBuildEnvironment;
1064

1065
  /**
1066
   * Controls if this plugin should expose the generated properties into {@code System.properties}
1067
   * When set to {@code true} this plugin will try to expose the generated properties into
1068
   * {@code System.getProperties()}. Set to {@code false} to avoid this exposure.
1069
   *
1070
   * Note that parameters provided via command-line (e.g. {@code -Dgit.commit.id=value}) still
1071
   * have precedence.
1072
   *
1073
   * <p>By default this is set to {@code true}.</p>
1074
   *
1075
   * <p>Example:
1076
   * <pre>
1077
   * {@code
1078
   *     <injectIntoSysProperties>true</injectIntoSysProperties>
1079
   * }
1080
   * </pre>
1081
   * </p>
1082
   *
1083
   * @since 3.0.0
1084
   */
1085
  @Parameter(defaultValue = "true")
1086
  boolean injectIntoSysProperties;
1087

1088
  /**
1089
   * The plugin can generate certain properties that represents the count of commits
1090
   * that your local branch is ahead or behind in perspective to the remote branch.
1091
   *
1092
   * <p>When your branch is "ahead" it means your local branch has committed changes that are not
1093
   * pushed yet to the remote branch.
1094
   * When your branch is "behind" it means there are commits in the remote branch that are not yet
1095
   * integrated into your local branch.</p>
1096
   *
1097
   * <p>This configuration allows you to control if the plugin should somewhat ensure
1098
   * that such properties are more accurate. More accurate means that the plugin will perform a
1099
   * {@code git fetch} before the properties are calculated.
1100
   * Certainly a {@code git fetch} is an operation that may alter your local git repository
1101
   * and thus the plugin will operate not perform such operation (offline is set to {@code true}).
1102
   * If you however desire more accurate properties you may want to set this to {@code false}.</p>
1103
   *
1104
   * <p>Before version 5.X.X the default was set to {@code false} causing the plugin to operate
1105
   * in online-mode by default. Now the default is set to {@code true} (offline-mode) so the plugin might generate
1106
   * inaccurate {@code git.local.branch.ahead} and {@code git.local.branch.behind} branch information.
1107
   *
1108
   * <p>Example:
1109
   * <pre>
1110
   * {@code
1111
   *     <offline>true</offline>
1112
   * }
1113
   * </pre>
1114
   * </p>
1115
   *
1116
   * @since 3.0.1
1117
   */
1118
  @Parameter(defaultValue = "true")
1119
  boolean offline;
1120

1121
  /**
1122
   * Timestamp for reproducible output archive entries
1123
   * (https://maven.apache.org/guides/mini/guide-reproducible-builds.html).
1124
   * The value from <code>${project.build.outputTimestamp}</code> is either formatted as ISO 8601
1125
   * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like
1126
   * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>.
1127
   *
1128
   * @since 4.0.2
1129
   */
1130
  @Parameter(defaultValue = "${project.build.outputTimestamp}")
1131
  private String projectBuildOutputTimestamp;
1132

1133
  // This is now the end of parameters that can be configured in the pom.xml
1134
  // Happy hacking!
1135
  // ============================================================================================================
1136

1137
  /**
1138
   * Injected {@link BuildContext} to recognize incremental builds.
1139
   */
1140
  @Component
1141
  private BuildContext buildContext;
1142

1143
  /**
1144
   * Charset to read-write project sources.
1145
   */
1146
  private Charset sourceCharset = StandardCharsets.UTF_8;
1✔
1147

1148
  /**
1149
   * This method is used to mock the system environment in testing.
1150
   *
1151
   * @return unmodifiable string map view of the current system environment {@link System#getenv}.
1152
   */
1153
  protected Map<String, String> getCustomSystemEnv() {
1154
    return System.getenv();
1✔
1155
  }
1156

1157
  @Override
1158
  public void execute() throws MojoExecutionException {
1159
    LogInterface log = new LogInterface() {
1✔
1160
      @Override
1161
      public void debug(String msg) {
1162
        if (verbose) {
1✔
1163
          getLog().debug(msg);
×
1164
        }
1165
      }
1✔
1166

1167
      @Override
1168
      public void info(String msg) {
1169
        if (verbose) {
1✔
1170
          getLog().info(msg);
×
1171
        }
1172
      }
1✔
1173

1174
      @Override
1175
      public void warn(String msg) {
1176
        if (verbose) {
1✔
1177
          getLog().warn(msg);
×
1178
        }
1179
      }
1✔
1180

1181
      @Override
1182
      public void error(String msg) {
1183
        // TODO: Should we truly only report errors when verbose = true?
1184
        if (verbose) {
×
1185
          getLog().error(msg);
×
1186
        }
1187
      }
×
1188

1189
      @Override
1190
      public void error(String msg, Throwable t) {
1191
        // TODO: Should we truly only report errors when verbose = true?
1192
        if (verbose) {
1✔
1193
          getLog().error(msg, t);
×
1194
        }
1195
      }
1✔
1196
    };
1197

1198

1199
    try {
1200
      // Skip mojo execution on incremental builds.
1201
      if (buildContext != null && buildContext.isIncremental()) {
1✔
1202
        // Except if properties file is missing at all
1203
        if (!generateGitPropertiesFile ||
×
1204
                PropertiesFileGenerator.craftPropertiesOutputFile(
×
1205
                        project.getBasedir(), new File(generateGitPropertiesFilename)).exists()) {
×
1206
          return;
×
1207
        }
1208
      }
1209

1210
      // read source encoding from project properties for those who still doesn't use UTF-8
1211
      String sourceEncoding = project.getProperties().getProperty("project.build.sourceEncoding");
1✔
1212
      if (null != sourceEncoding) {
1✔
1213
        sourceCharset = Charset.forName(sourceEncoding);
×
1214
      } else {
1215
        sourceCharset = Charset.defaultCharset();
1✔
1216
      }
1217

1218
      if (skip || skipViaCommandLine) {
1✔
1219
        log.info("skip is enabled, skipping execution!");
1✔
1220
        return;
1✔
1221
      }
1222

1223
      if (runOnlyOnce) {
1✔
1224
        List<MavenProject> sortedProjects =
×
1225
                Optional.ofNullable(session.getProjectDependencyGraph())
×
1226
                        .map(graph -> graph.getSortedProjects())
×
1227
                        .orElseGet(() -> {
×
1228
                          log.warn("Maven's dependency graph is null. Assuming project is the only one executed.");
×
1229
                          return Collections.singletonList(session.getCurrentProject());
×
1230
                        });
1231
        MavenProject firstProject = sortedProjects.stream()
×
1232
                // skipPoms == true => find first project that is not pom project
1233
                .filter(p -> {
×
1234
                  if (skipPoms) {
×
1235
                    return !isPomProject(p);
×
1236
                  } else {
1237
                    return true;
×
1238
                  }
1239
                })
1240
                .findFirst()
×
1241
                .orElse(session.getCurrentProject());
×
1242

1243
        log.info("Current project: '" + session.getCurrentProject().getName() +
×
1244
                "', first project to execute based on dependency graph: '" + firstProject.getName() + "'");
×
1245

1246
        if (!session.getCurrentProject().equals(firstProject)) {
×
1247
          log.info("runOnlyOnce is enabled and this project is not the first project (perhaps skipPoms is configured?), skipping execution!");
×
1248
          return;
×
1249
        }
1250
      }
1251

1252
      if (isPomProject(project) && skipPoms) {
1✔
1253
        log.info("isPomProject is true and skipPoms is true, return");
1✔
1254
        return;
1✔
1255
      }
1256

1257
      dotGitDirectory = lookupGitDirectory();
1✔
1258
      if (failOnNoGitDirectory && !directoryExists(dotGitDirectory)) {
1✔
1259
        throw new GitCommitIdExecutionException(".git directory is not found! Please specify a valid [dotGitDirectory] in your pom.xml");
1✔
1260
      }
1261

1262
      if (gitDescribe == null) {
1✔
1263
        gitDescribe = new GitDescribeConfig();
1✔
1264
      }
1265

1266
      if (dotGitDirectory != null) {
1✔
1267
        log.info("dotGitDirectory '" + dotGitDirectory.getAbsolutePath() + "'");
1✔
1268
      } else {
1269
        log.info("dotGitDirectory is null, aborting execution!");
1✔
1270
        return;
1✔
1271
      }
1272

1273
      try {
1274
        commitIdGenerationModeEnum = CommitIdGenerationMode.valueOf(commitIdGenerationMode.toUpperCase());
1✔
1275
      } catch (IllegalArgumentException e) {
×
1276
        log.warn("Detected wrong setting for 'commitIdGenerationMode'. Falling back to default 'flat' mode!");
×
1277
        commitIdGenerationModeEnum = CommitIdGenerationMode.FLAT;
×
1278
      }
1✔
1279

1280
      try {
1281
        commitIdPropertiesOutputFormat = CommitIdPropertiesOutputFormat.valueOf(format.toUpperCase());
1✔
1282
      } catch (IllegalArgumentException e) {
×
1283
        log.warn("Detected wrong setting for 'format'. Falling back to default 'properties' mode!");
×
1284
        commitIdPropertiesOutputFormat = CommitIdPropertiesOutputFormat.PROPERTIES;
×
1285
      }
1✔
1286

1287
      final GitCommitIdPlugin.Callback cb = new GitCommitIdPlugin.Callback() {
1✔
1288
        @Override
1289
        public Map<String, String> getSystemEnv() {
1290
          return getCustomSystemEnv();
1✔
1291
        }
1292

1293
        @Override
1294
        public Supplier<String> supplyProjectVersion() {
1295
          return () -> project.getVersion();
1✔
1296
        }
1297

1298
        @Nonnull
1299
        @Override
1300
        public LogInterface getLogInterface() {
1301
          return log;
1✔
1302
        }
1303

1304
        @Nonnull
1305
        @Override
1306
        public String getDateFormat() {
1307
          return dateFormat;
1✔
1308
        }
1309

1310
        @Nonnull
1311
        @Override
1312
        public String getDateFormatTimeZone() {
1313
          return dateFormatTimeZone;
1✔
1314
        }
1315

1316
        @Nonnull
1317
        @Override
1318
        public String getPrefixDot() {
1319
          String trimmedPrefix = prefix.trim();
1✔
1320
          return trimmedPrefix.equals("") ? "" : trimmedPrefix + ".";
1✔
1321
        }
1322

1323
        @Override
1324
        public List<String> getExcludeProperties() {
1325
          return excludeProperties;
1✔
1326
        }
1327

1328
        @Override
1329
        public List<String> getIncludeOnlyProperties() {
1330
          return includeOnlyProperties;
1✔
1331
        }
1332

1333
        @Nullable
1334
        @Override
1335
        public Date getReproducibleBuildOutputTimestamp() throws GitCommitIdExecutionException {
1336
          return parseOutputTimestamp(projectBuildOutputTimestamp);
1✔
1337
        }
1338

1339
        @Override
1340
        public boolean useNativeGit() {
1341
          return useNativeGit || useNativeGitViaCommandLine;
1✔
1342
        }
1343

1344
        @Override
1345
        public long getNativeGitTimeoutInMs() {
1346
          return nativeGitTimeoutInMs;
1✔
1347
        }
1348

1349
        @Override
1350
        public int getAbbrevLength() {
1351
          return abbrevLength;
1✔
1352
        }
1353

1354
        @Override
1355
        public GitDescribeConfig getGitDescribe() {
1356
          return gitDescribe;
1✔
1357
        }
1358

1359
        @Override
1360
        public CommitIdGenerationMode getCommitIdGenerationMode() {
1361
          return commitIdGenerationModeEnum;
1✔
1362
        }
1363

1364
        @Override
1365
        public boolean getUseBranchNameFromBuildEnvironment() {
1366
          return useBranchNameFromBuildEnvironment;
1✔
1367
        }
1368

1369
        @Override
1370
        public boolean isOffline() {
1371
          return offline || settings.isOffline();
1✔
1372
        }
1373

1374
        @Override
1375
        public String getEvaluateOnCommit() {
1376
          return evaluateOnCommit;
1✔
1377
        }
1378

1379
        @Override
1380
        public File getDotGitDirectory() {
1381
          return dotGitDirectory;
1✔
1382
        }
1383

1384
        @Override
1385
        public boolean shouldGenerateGitPropertiesFile() {
1386
          return generateGitPropertiesFile;
1✔
1387
        }
1388

1389
        @Override
1390
        public void performPublishToAllSystemEnvironments(Properties properties) {
1391
          publishToAllSystemEnvironments(getLogInterface(), properties);
1✔
1392
        }
1✔
1393

1394
        @Override
1395
        public void performPropertiesReplacement(Properties properties) {
1396
          PropertiesReplacer propertiesReplacer = new PropertiesReplacer(
1✔
1397
                  log, new PluginParameterExpressionEvaluator(session, mojoExecution));
1398
          propertiesReplacer.performReplacement(properties, replacementProperties);
1✔
1399

1400
          logProperties(getLogInterface(), properties);
1✔
1401
        }
1✔
1402

1403
        @Override
1404
        public CommitIdPropertiesOutputFormat getPropertiesOutputFormat() {
1405
          return commitIdPropertiesOutputFormat;
1✔
1406
        }
1407

1408
        @Override
1409
        public BuildFileChangeListener getBuildFileChangeListener() {
1410
          return file -> {
1✔
1411
            // this should only be null in our tests
1412
            if (buildContext != null) {
1✔
1413
              buildContext.refresh(file);
×
1414
            }
1415
          };
1✔
1416
        }
1417

1418
        @Override
1419
        public String getProjectName() {
1420
          return project.getName();
1✔
1421
        }
1422

1423
        @Override
1424
        public File getProjectBaseDir() {
1425
          return project.getBasedir();
1✔
1426
        }
1427

1428
        @Override
1429
        public File getGenerateGitPropertiesFile() {
1430
          return new File(generateGitPropertiesFilename);
1✔
1431
        }
1432

1433
        @Override
1434
        public Charset getPropertiesSourceCharset() {
1435
          return sourceCharset;
1✔
1436
        }
1437

1438
        @Override
1439
        public boolean shouldPropertiesEscapeUnicode() {
1440
          return generateGitPropertiesFileWithEscapedUnicode;
1✔
1441
        }
1442
      };
1443

1444
      Properties properties = null;
1✔
1445
      // check if properties have already been injected
1446
      Properties contextProperties = getContextProperties(project);
1✔
1447
      boolean alreadyInjected = injectAllReactorProjects && contextProperties != null;
1✔
1448
      if (alreadyInjected) {
1✔
1449
        log.info("injectAllReactorProjects is enabled - attempting to use the already computed values");
1✔
1450
        properties = contextProperties;
1✔
1451
      }
1452

1453
      GitCommitIdPlugin.runPlugin(cb, properties);
1✔
1454
    } catch (GitCommitIdExecutionException e) {
1✔
1455
      throw new MojoExecutionException(e.getMessage(), e);
1✔
1456
    }
1✔
1457
  }
1✔
1458

1459
  private void publishToAllSystemEnvironments(LogInterface log, Properties propertiesToPublish) {
1460
    publishPropertiesInto(propertiesToPublish, project.getProperties());
1✔
1461
    // some plugins rely on the user properties (e.g. flatten-maven-plugin)
1462
    publishPropertiesInto(propertiesToPublish, session.getUserProperties());
1✔
1463

1464
    if (injectAllReactorProjects) {
1✔
1465
      appendPropertiesToReactorProjects(log, propertiesToPublish);
1✔
1466
    }
1467

1468
    if (injectIntoSysProperties) {
1✔
1469
      publishPropertiesInto(propertiesToPublish, System.getProperties());
×
1470
      publishPropertiesInto(propertiesToPublish, session.getSystemProperties());
×
1471
      publishPropertiesInto(propertiesToPublish, session.getRequest().getSystemProperties());
×
1472
    }
1473
  }
1✔
1474

1475
  @Nullable
1476
  private Properties getContextProperties(MavenProject project) {
1477
    Object stored = project.getContextValue(CONTEXT_KEY);
1✔
1478
    if (stored instanceof Properties) {
1✔
1479
      return (Properties)stored;
1✔
1480
    }
1481
    return null;
1✔
1482
  }
1483

1484
  /**
1485
   * Parse output timestamp configured for Reproducible Builds' archive entries
1486
   * (https://maven.apache.org/guides/mini/guide-reproducible-builds.html).
1487
   * The value from <code>${project.build.outputTimestamp}</code> is either formatted as ISO 8601
1488
   * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like
1489
   * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>.
1490
   *
1491
   * Inspired by https://github.com/apache/maven-archiver/blob/a3103d99396cd8d3440b907ef932a33563225265/src/main/java/org/apache/maven/archiver/MavenArchiver.java#L765
1492
   *
1493
   * @param outputTimestamp the value of <code>${project.build.outputTimestamp}</code> (may be <code>null</code>)
1494
   * @return the parsed timestamp, may be <code>null</code> if <code>null</code> input or input contains only 1
1495
   *         character
1496
   */
1497
  private Date parseOutputTimestamp(String outputTimestamp) throws GitCommitIdExecutionException {
1498
    if (outputTimestamp != null && !outputTimestamp.trim().isEmpty() && outputTimestamp.chars().allMatch(Character::isDigit)) {
1✔
1499
      return new Date(Long.parseLong(outputTimestamp) * 1000);
×
1500
    }
1501

1502
    if ((outputTimestamp == null) || (outputTimestamp.length() < 2)) {
1✔
1503
      // no timestamp configured
1504
      return null;
1✔
1505
    }
1506

1507
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
×
1508
    try {
1509
      return df.parse(outputTimestamp);
×
1510
    } catch (ParseException pe) {
×
1511
      throw new GitCommitIdExecutionException(
×
1512
              "Invalid 'project.build.outputTimestamp' value '" + outputTimestamp + "'",
1513
              pe);
1514
    }
1515
  }
1516

1517
  private void publishPropertiesInto(Properties propertiesToPublish, Properties propertiesTarget) {
1518
    for (String propertyName : propertiesToPublish.stringPropertyNames()) {
1✔
1519
      propertiesTarget.setProperty(propertyName, propertiesToPublish.getProperty(propertyName));
1✔
1520
    }
1✔
1521
  }
1✔
1522

1523
  private void appendPropertiesToReactorProjects(LogInterface log, Properties propertiesToPublish) {
1524
    for (MavenProject mavenProject : reactorProjects) {
1✔
1525
      log.debug("Adding properties to project: '" + mavenProject.getName() + "'");
1✔
1526

1527
      publishPropertiesInto(propertiesToPublish, mavenProject.getProperties());
1✔
1528
      mavenProject.setContextValue(CONTEXT_KEY, propertiesToPublish);
1✔
1529
    }
1✔
1530
    log.info("Added properties to '" + reactorProjects.size() + "' projects");
1✔
1531
  }
1✔
1532

1533
  /**
1534
   * Find the git directory of the currently used project.
1535
   * If it's not already specified, this method will try to find it.
1536
   *
1537
   * @return the File representation of the .git directory
1538
   */
1539
  private File lookupGitDirectory() throws GitCommitIdExecutionException {
1540
    return new GitDirLocator(project, reactorProjects).lookupGitDirectory(dotGitDirectory);
1✔
1541
  }
1542

1543
  private void logProperties(LogInterface log, Properties propertiesToPublish) {
1544
    for (String propertyName : propertiesToPublish.stringPropertyNames()) {
1✔
1545
      log.info("including property '" + propertyName + "' in results");
1✔
1546
    }
1✔
1547
  }
1✔
1548

1549
  private boolean isPomProject(@Nonnull MavenProject project) {
1550
    return project.getPackaging().equalsIgnoreCase("pom");
1✔
1551
  }
1552

1553
  private boolean directoryExists(@Nullable File fileLocation) {
1554
    return fileLocation != null && fileLocation.exists() && fileLocation.isDirectory();
1✔
1555
  }
1556
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc