Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RegExp to match conventional commit syntax

Tags:

regex

I've been fiddling around this since yesterday.
I just can't seem to match all the possible cases.

I'm trying to come up with a regular expression which matches a Conventional Commit, but which also offers some error recovery functionality.

Current regexp:

(?<type>build)(?<scope>\(.*\)?(?=:))?(?<breaking>!)?(?<subject>:.*)?

Inputs:

build(one)
build(two)!
build(three)!:test
build(example:module)!: test
build: test
build(<> : dda!sd): test
build(:
build

Outputs:

enter image description here

What doesn't work:

  • first two cases
  • the ! isn't captured as breaking in the third and following cases

The sample is at Regex101, https://regex101.com/r/XYC04q/1
And I have other (16) tests here, https://regex101.com/r/sSrvyA/11

Even if you have no time to try and modify it, any comment is appreciated.

like image 915
LppEdd Avatar asked Nov 17 '19 11:11

LppEdd


People also ask

What is a conventional commit?

The Conventional Commits specification is a lightweight convention on top of commit messages. It provides an easy set of rules for creating an explicit commit history; which makes it easier to write automated tools on top of.

What is matching regexp?

The regexp-match-positions function takes a regexp pattern and a text string, and it returns a match if the regexp matches (some part of) the text string, or #f if the regexp did not match the string. A successful match produces a list of index pairs.

What is perf conventional commit?

perf: a commit that improves performance, without functional changes. chore: a catch-all type for any other commits. For instance, if you're implementing a single feature and it makes sense to divide the work into multiple commits, you should mark one commit as feat and the rest as chore .


2 Answers

Doesn't necessarily solve the desire to capture )!: as breaking group but this does seem to follow conventional commits specs 1-5 + 13.

^(?<type>build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|¯\\_\(ツ\)_\/¯)(?<scope>\(\w+\)?((?=:\s)|(?=!:\s)))?(?<breaking>!)?(?<subject>:\s.*)?|^(?<merge>Merge \w+)

https://regex101.com/r/XYC04q/11

Gitlab Push Commit Regex

However, if you are using a platform like GitLab and want to set push rule to commit messages as of v13(?) they are using re2 standards and Golang parser.

Here it is for that Gitlab.

Note that GitLab enables the global flag (?m) which gave me some difficulty. Discussion on gitlab

| Restrict by commit message | Starter 7.10 | Only commit messages that match this regular expression are allowed to be pushed. Leave empty to allow any commit message. Uses multiline mode, which can be disabled using (?-m). |

source doc

## simplified and modified for gitlab's 
^((build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|¯\\_\(ツ\)_/¯)(\(\w+\))?(!)?(: (.*\s*)*))|(Merge (.*\s*)*)|(Initial commit$)

## RE2 compliment but doesn't work on GitLab at this time
^(?P<type>build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|¯\\_\(ツ\)_/¯)(?P<scope>\(\w+\))?(?P<breaking>!)?(?P<subject>:\s.*)?|^(?P<merge>Merge \w+)

https://regex101.com/r/XYC04q/28

GitLab using Terraform Push Rule Regex

Note if you are trying to use Terraform for Gitlab with Regex, note that Terraform parses the string prior to Gitlab requiring some bonus escapes.

resource "gitlab_project_push_rules" "github_flow" {
  project = gitlab_project.project.id

  # Conventional Commits https://www.conventionalcommits.org/en/v1.0.0/
  commit_message_regex = "^((build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|¯\\\\_\\(ツ\\)_/¯)(\\(\\w+\\))?(!)?(: (.*\\s*)*))|(Merge (.*\\s*)*)|(Initial commit$)"
  branch_name_regex    = "(^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|¯\\\\_\\(ツ\\)_\\/¯)\\/[a-z0-9\\-]{1,55}$)|master"

  prevent_secrets = true
}
like image 132
codeangler Avatar answered Oct 04 '22 03:10

codeangler


You have some optional parts for which you could indeed a non capturing group to match either from an opening ( till a closing ) or match only an opening (

(?<type>build)(?<scope>(?:\([^()\r\n]*\)|\()?(?<breaking>!)?)(?<subject>:.*)?
  • (?<type>build) Group type, match build
  • (?<scope> Group scope
    • (?: Non capturing group
      • \([^()\r\n]*\) Match either from opening ( till closing )
      • | or
      • \( Match a single (
    • )? Close non capturing group and make it optional
    • (?<breaking>!)? Optional group breaking
  • ) Close group scope
  • (?<subject>:.*)? Optional group subject

Regex demo

like image 30
The fourth bird Avatar answered Oct 04 '22 02:10

The fourth bird