Skip to content

Commit

Permalink
Merge pull request #626 from diffplug/feat/maven-licenseyear-githistory
Browse files Browse the repository at this point in the history
Add maven support for git-based copyright headers
  • Loading branch information
nedtwigg authored Jun 30, 2020
2 parents 41ed32d + f46feff commit ba3c126
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public String format(String raw) {
}
}

private static final String spotlessSetLicenseHeaderYearsFromGitHistory = "spotlessSetLicenseHeaderYearsFromGitHistory";
public static final String spotlessSetLicenseHeaderYearsFromGitHistory = "spotlessSetLicenseHeaderYearsFromGitHistory";

public static final String FLAG_SET_LICENSE_HEADER_YEARS_FROM_GIT_HISTORY() {
return spotlessSetLicenseHeaderYearsFromGitHistory;
Expand Down
1 change: 1 addition & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
### Added
* You can now ratchet a project's style by limiting Spotless only to files which have changed since a given [git reference](https://javadoc.io/static/org.eclipse.jgit/org.eclipse.jgit/5.6.1.202002131546-r/org/eclipse/jgit/lib/Repository.html#resolve-java.lang.String-), e.g. `ratchetFrom 'origin/main'`. ([#590](https://github.com/diffplug/spotless/pull/590))
* Huge speed improvement for multi-module projects thanks to improved cross-project classloader caching ([#571](https://github.com/diffplug/spotless/pull/571), fixes [#559](https://github.com/diffplug/spotless/issues/559)).
* If you specify `-DspotlessSetLicenseHeaderYearsFromGitHistory=true`, Spotless will perform an expensive search through git history to determine the oldest and newest commits for each file, and uses that to determine license header years. ([#626](https://github.com/diffplug/spotless/pull/626))
* `prettier` will now autodetect the parser (and formatter) to use based on the filename, unless you override this using `config` or `configFile` with the option `parser` or `filepath` ([#620](https://github.com/diffplug/spotless/pull/620)).

## [1.31.3] - 2020-06-17
Expand Down
30 changes: 30 additions & 0 deletions plugin-maven/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,36 @@ Unlike Eclipse, Spotless WTP ignores per default external URIs in schema locatio
external entities. To allow the access of external URIs, set the property `resolveExternalURI`
to true.

<a name="license-header"></a>

## License header

Spotless can inject a license header into your files, including populating an accurate copyright header from today's date or from git history.

```xml
<format> <!-- or java, scala, kotlin, etc. -->
<licenseHeader>
<!-- Specify either content or file, but not both -->
<content>/* Licensed under Apache-2.0 */</content>
<file>${basedir}/license-header</file>
<!-- content until first occurrence of the delimiter regex will be interpreted as header section -->
<!-- for language-specific formats (java, etc.) this is optional, and the regex is provided automatically. -->
<delimiter>#</delimiter>
</licenseHeader>
```

If the license header (specified with `content` or `file`) contains `$YEAR` or `$today.year`, then that token will be replaced with the current 4-digit year. For example, if Spotless is launched in 2020, then `/* Licensed under Apache-2.0 $YEAR. */` will produce `/* Licensed under Apache-2.0 2020. */`

Once a file's license header has a valid year, whether it is a year (`2020`) or a year range (`2017-2020`), it will not be changed. If you want the date to be updated when it changes, enable the [`ratchetFrom` functionality](#ratchet), and the year will be automatically set to today's year according to the following table (assuming the current year is 2020):

* No license header -> `2020`
* `2017` -> `2017-2020`
* `2017-2019` -> `2017-2020`

### Retroactively populating year range from git history

If your project has not been rigorous with copyright headers, and you'd like to use git history to repair this retroactively, you can do so with `-DspotlessSetLicenseHeaderYearsFromGitHistory=true`. When run in this mode, Spotless will do an expensive search through git history for each file, and set the copyright header based on the oldest and youngest commits for that file. This is intended to be a one-off sort of thing.

<a name="invisible"></a>

## Line endings and encodings (invisible stuff)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.diffplug.spotless.Formatter;
import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.Provisioner;
import com.diffplug.spotless.generic.LicenseHeaderStep;
import com.diffplug.spotless.maven.cpp.Cpp;
import com.diffplug.spotless.maven.generic.Format;
import com.diffplug.spotless.maven.generic.LicenseHeader;
Expand Down Expand Up @@ -120,6 +121,9 @@ public abstract class AbstractSpotlessMojo extends AbstractMojo {
@Parameter(property = "spotlessFiles")
private String filePatterns;

@Parameter(property = LicenseHeaderStep.spotlessSetLicenseHeaderYearsFromGitHistory)
private String setLicenseHeaderYearsFromGitHistory;

protected abstract void process(Iterable<File> files, Formatter formatter) throws MojoExecutionException;

@Override
Expand Down Expand Up @@ -189,7 +193,7 @@ private FormatterConfig getFormatterConfig() {
Provisioner provisioner = MavenProvisioner.create(resolver);
List<FormatterStepFactory> formatterStepFactories = getFormatterStepFactories();
FileLocator fileLocator = getFileLocator();
return new FormatterConfig(baseDir, encoding, lineEndings, Optional.ofNullable(ratchetFrom), provisioner, fileLocator, formatterStepFactories);
return new FormatterConfig(baseDir, encoding, lineEndings, Optional.ofNullable(ratchetFrom), provisioner, fileLocator, formatterStepFactories, Optional.ofNullable(setLicenseHeaderYearsFromGitHistory));
}

private FileLocator getFileLocator() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,17 @@ public class FormatterConfig {
private final Provisioner provisioner;
private final FileLocator fileLocator;
private final List<FormatterStepFactory> globalStepFactories;
private final Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory;

public FormatterConfig(File baseDir, String encoding, LineEnding lineEndings, Optional<String> ratchetFrom, Provisioner provisioner,
FileLocator fileLocator, List<FormatterStepFactory> globalStepFactories) {
FileLocator fileLocator, List<FormatterStepFactory> globalStepFactories, Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory) {
this.encoding = encoding;
this.lineEndings = lineEndings;
this.ratchetFrom = ratchetFrom;
this.provisioner = provisioner;
this.fileLocator = fileLocator;
this.globalStepFactories = globalStepFactories;
this.spotlessSetLicenseHeaderYearsFromGitHistory = spotlessSetLicenseHeaderYearsFromGitHistory;
}

public String getEncoding() {
Expand All @@ -63,6 +65,10 @@ public List<FormatterStepFactory> getGlobalStepFactories() {
return unmodifiableList(globalStepFactories);
}

public Optional<String> getSpotlessSetLicenseHeaderYearsFromGitHistory() {
return spotlessSetLicenseHeaderYearsFromGitHistory;
}

public FileLocator getFileLocator() {
return fileLocator;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Optional<String> ratchetFrom(FormatterConfig config) {
}

private FormatterStepConfig stepConfig(Charset encoding, FormatterConfig config) {
return new FormatterStepConfig(encoding, licenseHeaderDelimiter(), config.getProvisioner(), config.getFileLocator());
return new FormatterStepConfig(encoding, licenseHeaderDelimiter(), ratchetFrom(config), config.getProvisioner(), config.getFileLocator(), config.getSpotlessSetLicenseHeaderYearsFromGitHistory());
}

private static List<FormatterStepFactory> gatherStepFactories(List<FormatterStepFactory> allGlobal, List<FormatterStepFactory> allConfigured) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016 DiffPlug
* Copyright 2016-2020 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,21 +16,26 @@
package com.diffplug.spotless.maven;

import java.nio.charset.Charset;
import java.util.Optional;

import com.diffplug.spotless.Provisioner;

public class FormatterStepConfig {

private final Charset encoding;
private final String licenseHeaderDelimiter;
private final Optional<String> ratchetFrom;
private final Provisioner provisioner;
private final FileLocator fileLocator;
private final Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory;

public FormatterStepConfig(Charset encoding, String licenseHeaderDelimiter, Provisioner provisioner, FileLocator fileLocator) {
public FormatterStepConfig(Charset encoding, String licenseHeaderDelimiter, Optional<String> ratchetFrom, Provisioner provisioner, FileLocator fileLocator, Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory) {
this.encoding = encoding;
this.licenseHeaderDelimiter = licenseHeaderDelimiter;
this.ratchetFrom = ratchetFrom;
this.provisioner = provisioner;
this.fileLocator = fileLocator;
this.spotlessSetLicenseHeaderYearsFromGitHistory = spotlessSetLicenseHeaderYearsFromGitHistory;
}

public Charset getEncoding() {
Expand All @@ -41,11 +46,19 @@ public String getLicenseHeaderDelimiter() {
return licenseHeaderDelimiter;
}

public Optional<String> getRatchetFrom() {
return ratchetFrom;
}

public Provisioner getProvisioner() {
return provisioner;
}

public FileLocator getFileLocator() {
return fileLocator;
}

public Optional<String> spotlessSetLicenseHeaderYearsFromGitHistory() {
return spotlessSetLicenseHeaderYearsFromGitHistory;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016 DiffPlug
* Copyright 2016-2020 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,9 +16,12 @@
package com.diffplug.spotless.maven.generic;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

import org.apache.maven.plugins.annotations.Parameter;

import com.diffplug.spotless.FormatterFunc;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.generic.LicenseHeaderStep;
import com.diffplug.spotless.maven.FormatterStepConfig;
Expand All @@ -41,24 +44,44 @@ public final FormatterStep newFormatterStep(FormatterStepConfig config) {
if (delimiterString == null) {
throw new IllegalArgumentException("You need to specify 'delimiter'.");
}

if (file != null ^ content != null) {
FormatterStep step = file != null
? createStepFromFile(config, delimiterString)
: createStepFromContent(delimiterString);
FormatterStep unfiltered;
if ("true".equals(config.spotlessSetLicenseHeaderYearsFromGitHistory().orElse(""))) {
unfiltered = FormatterStep.createNeverUpToDateLazy(LicenseHeaderStep.name(), () -> {
boolean updateYear = false; // doesn't matter
LicenseHeaderStep step = new LicenseHeaderStep(readFileOrContent(config), delimiterString, LicenseHeaderStep.defaultYearDelimiter(), updateYear);
return new FormatterFunc() {
@Override
public String apply(String input, File source) throws Exception {
return step.setLicenseHeaderYearsFromGitHistory(input, source);
}

return step.filterByFile(LicenseHeaderStep.unsupportedJvmFilesFilter());
@Override
public String apply(String input) throws Exception {
throw new UnsupportedOperationException();
}
};
});
} else {
unfiltered = FormatterStep.createLazy(LicenseHeaderStep.name(), () -> {
// by default, we should update the year if the user is using ratchetFrom
boolean updateYear = config.getRatchetFrom().isPresent();
String header = readFileOrContent(config);
return new LicenseHeaderStep(header, delimiterString, LicenseHeaderStep.defaultYearDelimiter(), updateYear);
}, step -> step::format);
}
return unfiltered.filterByFile(LicenseHeaderStep.unsupportedJvmFilesFilter());
} else {
throw new IllegalArgumentException("Must specify exactly one of 'file' or 'content'.");
}
}

private FormatterStep createStepFromFile(FormatterStepConfig config, String delimiterString) {
File licenseHeaderFile = config.getFileLocator().locateFile(file);
return LicenseHeaderStep.createFromFile(licenseHeaderFile, config.getEncoding(), delimiterString);
}

private FormatterStep createStepFromContent(String delimiterString) {
return LicenseHeaderStep.createFromHeader(content, delimiterString);
private String readFileOrContent(FormatterStepConfig config) throws IOException {
if (content != null) {
return content;
} else {
byte[] raw = Files.readAllBytes(config.getFileLocator().locateFile(file).toPath());
return new String(raw, config.getEncoding());
}
}
}

0 comments on commit ba3c126

Please sign in to comment.