ajoberstar / reckon

Infer a project's version from your Git repository.
Apache License 2.0
187 stars 28 forks source link

Configure the default scope for Reckon #98

Closed michelzanini closed 6 years ago

michelzanini commented 6 years ago

Hi,

I am using snapshotFromProp() and when I run ./gradlew build with no arguments on a project with no Git tags I get:

Reckoned version: 0.1.0-SNAPSHOT

Is it possible to configure the default scope to be patch so then if I run with no parameters I get instead:

Reckoned version: 0.0.1-SNAPSHOT

I know I can send this parameter to accomplish this -Preckon.scope=patch but I wonder if its possible to configure the default when no parameter is sent.

Thanks.

ajoberstar commented 6 years ago

Currently, the default is not configurable. I've tended to minimize configurability to keep the codebase simpler, but I'm curious about this. Does your general workflow include more patch releases than minors? Or is it more of a safety behavior: don't release a minor if it might just have a bug fix in there?

michelzanini commented 6 years ago

We tend to work with JAR libraries where its more common for us to release a patch than a minor version. I implemented a simple logic to detect if any commits since the last tag have "#major" or "#minor" on the commit message. If this is present, I am using the corresponding scope. But when there is not commit with #major / #minor I wanted to be patch by default...

Anyway, congratulations on this plugin, its very simple and nice. Hopefully we will get a 1.0.0 release soon. Thanks!

ajoberstar commented 6 years ago

Do you have a snippet of what you do to detect major/minor from the commit messages? I'm curious since it seems potentially related to #72, where I want to expose some more direct flexibility for determining the scope/stage without a property having to be set.

The use case makes sense, but I'm wondering if just having a better way to set the value via a detection method like your commit message one would replace a need for a custom default value.

On 1.0.0, unfortunately, I don't think it's there yet, though resolving things like #72 will get it closer.

michelzanini commented 6 years ago

I have this logic on a Gradle plugin of my own.

This part here guess the scope unless the scope is already defined via command line:

if (!project.hasProperty('reckon.scope')) {
  project.ext['reckon.scope'] = findVersionScope(project)
}

Find version scope function looks like this:

protected String findVersionScope(Project project) {
    Grgit git = (Grgit) project.findProperty("grgit");
    if (git == null) {
      throw new IllegalStateException("Cannot find Grgit instance. Reckon and Grgit plugins need to be applied.")
    }

    GitService gitService = new GitService(git)
    gitService.findVersionScopeBasedOnPreviousCommits()
}

Which uses this class:

class GitService {

  Grgit git

  Pattern commitMajorRegex
  Pattern commitMinorRegex

  GitService(Grgit git) {
    this.git = git

    this.commitMajorRegex = Pattern.compile("(?s)(.*\\s+|)(#major)(\\s+.*|)")
    this.commitMinorRegex = Pattern.compile("(?s)(.*\\s+|)(#minor)(\\s+.*|)")
  }

  String findVersionScopeBasedOnPreviousCommits() {
    def commits = commitsSinceLastTagOrAllCommits()

    Commit majorCommit = commits.find {
      hasMajorScopeForCommit(it)
    }

    if (majorCommit) {
      return 'major'
    }

    Commit minorCommit = commits.find {
      hasMinorScopeForCommit(it)
    }

    if (minorCommit) {
      return 'minor'
    }

    return 'patch'
  }

  List<Commit> commitsSinceLastTagOrAllCommits() {
    List<Tag> tags = git.tag.list()

    if (tags.isEmpty()) {
      git.log()
    } else {
      git.log {
        range(tags.last(), 'HEAD')
      }
    }
  }

  boolean hasMajorScopeForCommit(Commit commit) {
    hasScopeForCommit(commit, commitMajorRegex)
  }

  boolean hasMinorScopeForCommit(Commit commit) {
    hasScopeForCommit(commit, commitMinorRegex)
  }

  private boolean hasScopeForCommit(Commit commit, Pattern regex) {
    commit?.fullMessage ? regex.matcher(commit.fullMessage).matches() : false
  }

}
ajoberstar commented 6 years ago

Thanks for posting that. I'll keep noodling on supporting config for the default. I'd also like to make the commit list available somehow from reckon, so that you don't have to go grab it yourself.

michelzanini commented 6 years ago

No problem, I will close this issue as I could get this done as described.

I did find another thing that is bothering me a bit, maybe I will create another separated issue.

I have some projects with tags that are a bit odd, like 'master-RELEASE-1.0.0'. I would like to identify those as '1.0.0', if possible. I have seen a bit of code where it allow to support removing the 'v' before the version, like 'v1.0.0' is identified as '1.0.0'. Maybe we could adapt this to "ignore" whatever comes before the 'major.minor.patch' ? Anyway, just a suggestion, if you think this is useful, I could try a Pull Request.

Thanks.

ajoberstar commented 6 years ago

On that note about different tag formats, it's worth noting #54. While I'm not the fondest of the multi-project use case, to resolve it you would end up supporting some customization of how tags are parsed and rendered. I think that would cover your use case as well.

michelzanini commented 6 years ago

I have created PR #99 exposing the tag selector function, we can discuss further over there. Thanks.