commitlint icon indicating copy to clipboard operation
commitlint copied to clipboard

How to use parser headerPattern

Open blackswanny opened this issue 5 years ago • 20 comments

The task is to use commitlint to prevent commits from being commited. I use husky as well when I test it and run echo "bla bla bla" | npx commitlint It doesn't catch anything and shows no errors found

⧗   input: bla bla bla 
✔   found 0 problems, 0 warnings 

Please, help, why it ignores parserOpts.headerPattern ? Also some other notice: I need at least one rule, otherwise commitlint doesn't want to run Do I need to add headerCorrespondence to make it work or I can have headerPattern only? Can I customize report message in case if I headerPattern is not matched?

commitlint.config.js

module.exports = {
  rules: {
    'header-min-length': [2, 'always', 20],
  },
  parserPreset: {
    parserOpts: {
      headerPattern: /^(feat|fix|perf|test|BREAKING CHANGE):.*\[REF-(\d{3,}|N\\A)\] \S+ \S+ \S+ \S+ \S+/,
      headerCorrespondence: ['type', 'scope', 'subject']
    }
  }
};

husky in package.json

  "husky": {
    "hooks": {
      "pre-push": "npm test",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  }

I installed the next

    "@commitlint/cli": "^7.5.2",
    "@commitlint/parse": "^7.5.0",

blackswanny avatar Apr 02 '19 00:04 blackswanny

Hi @blackswanny,

you're right, this is a bit weird.

With your described config I get this output:

echo "bla bla bla" | npx commitlint

⧗   input: bla bla bla
✖   header must not be shorter than 20 characters, current length is 11 [header-min-length]
✖   found 1 problems, 0 warnings
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )

Using this reduced config...

module.exports = {
  parserPreset: {
    parserOpts: {
      headerPattern: /^(feat|fix|perf|test|BREAKING CHANGE):.*\[REF-(\d{3,}|N\\A)\] \S+ \S+ \S+ \S+ \S+/,
    }
  }
};

...output is:

echo "bla bla bla bla bla bla bla" | npx commitlint

⧗   input: bla bla bla bla bla bla bla
✖   Please add rules to your `commitlint.config.js`
    - Getting started guide: https://git.io/fhHij
    - Example config: https://git.io/fhHip [empty-rules]
✖   found 1 problems, 0 warnings
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )

When adding this:

  rules: {
    'header-min-length': [2, 'always', 20],
  },

I get:

echo "bla bla bla bla bla bla bla" | npx commitlint

⧗   input: bla bla bla bla bla bla bla
✔   found 0 problems, 0 warnings
    (Need help? -> https://github.com/conventional-changelog/commitlint#what-is-commitlint )

But if the regex is correct I would have expected an error. I guess we need to look into this. What do you think @byCedric ?

escapedcat avatar Apr 02 '19 06:04 escapedcat

I simplified this to be very simple config. The regex below is obviously correct. However, it does not work still

module.exports = {
  rules: {
    'header-min-length': [2, 'always', 2]
  },
  parserPreset: {
    parserOpts: {
      headerPattern: /^feat/,
      headerCorrespondence: ['type']
    }
  }
};

blackswanny avatar Apr 02 '19 23:04 blackswanny

I have been playing with this for a few hours with no avail; wanted to implement a way to at least warn devs that they needed to add the ticket # in the commit message. Upon finding this tool I was eager to try it, but the parserOpts I give are never used. After fiddling with everything and finding this issue, I installed @commitlint/parser besides @commitlint/cli but nothing changed. I have a commitlint.config.js similar to the posted above and also tried with the commitlint example (that encourages you to add it to the package.json but it's missing all the double quotes for JSON format?) and still can commit with any random message.

I'm subscribing particularly to this issue to track and try in a future personal project; I love the idea behind this and am sure it will be great, but the general feel the overall package gives to me is a bit broken (rules being required, documentation and installing modules a bit fuzzy, parsers not being used, the constant 'need help?' message, etc.).

maeriens avatar Apr 26 '19 23:04 maeriens

Hey @maeriens , checking for # in the reference works here for example: https://github.com/sumcumo/node-modules-check/blob/master/commitlint.config.js

If you have questions regarding this you can find me in the commitlint room here: https://devtoolscommunity.herokuapp.com/

escapedcat avatar Apr 27 '19 04:04 escapedcat

ok, i debugged and I know why we think it doesn't work.

  1. The parser takes headerPattern and runs regex.match for commit message. Match results are put into output json under names provided in headerCorrespondence. So headerCorrespondence is a must have or nothing is saved. For instance ['type', 'reference'] https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-commits-parser/lib/parser.js image

  2. Then tools is looping through only rules and try to ask each rule if it fails against that output. If you have no rule, nothing to be caught.

https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/lint/src/index.js image

  1. However, the last thing is that we can check only against existing rules, which all expect exact fields in parser output. For instance rule type-empty expects that parsed.type should be present, which can only happen if we used headerCorrespondence: ['type']. So literally there is no custom check via this parserOpts and it totally sticks to rules of commitlint itself. So I have to use type-empty rule even if I don't have type as my first match in commit message, but something else.

blackswanny avatar Apr 27 '19 06:04 blackswanny

@blackswanny Thanks for the summary! This is essentially a consequence of our decision to leak the commits-parser configuration as is to commitlint users.

I still think it is right to do so to enable some more complicated use cases but we could def. do a better job on explaining that parserOpts are considered "advanced" usage.

I expect most users to specify plugins they want to use (which might enable certain parserOpts) instead of touching the low level options like these directly. Could you outline what you tried to achieve with your parserOpts commitlint rule combination?

marionebl avatar May 31 '19 08:05 marionebl

@marionebl i just wanted every commit to match this regEx.

/^(feat|fix|perf|test|BREAKING CHANGE):.*\[REF-(\d{3,}|N\\A)\] \S+ \S+ \S+ \S+ \S+/

Plugins would solve the issue, but they are not yet released, waiting for it

blackswanny avatar Jun 05 '19 05:06 blackswanny

I have ben following and would like to implement this same functionality on some projects. I have tested the following regex: /^(\w*)(?:\(([JIRA]+-[a-zA-Z0-9_.-]+)\))?\: (.*)$/g

Screen Shot 2019-09-10 at 5 23 14 PM

You can see in the image, the groups are correct which also match the pattern I have in my config file. Screen Shot 2019-09-10 at 5 24 49 PM

and the error I receive

Screen Shot 2019-09-10 at 5 26 56 PM

jasonhodges avatar Sep 10 '19 23:09 jasonhodges

@jasonhodges did you manage to resolve the issue, i've been looking at achieving something similar

nellyk avatar Feb 25 '20 11:02 nellyk

ok, i debugged and I know why we think it doesn't work.

  1. The parser takes headerPattern and runs regex.match for commit message. Match results are put into output json under names provided in headerCorrespondence. So headerCorrespondence is a must have or nothing is saved. For instance ['type', 'reference'] https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-commits-parser/lib/parser.js
image
  1. Then tools is looping through only rules and try to ask each rule if it fails against that output. If you have no rule, nothing to be caught.

https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/lint/src/index.js image

  1. However, the last thing is that we can check only against existing rules, which all expect exact fields in parser output. For instance rule type-empty expects that parsed.type should be present, which can only happen if we used headerCorrespondence: ['type']. So literally there is no custom check via this parserOpts and it totally sticks to rules of commitlint itself. So I have to use type-empty rule even if I don't have type as my first match in commit message, but something else.

@blackswanny Hey, Did you find a workaround for your case? I have the same issue...

vbabenko avatar Feb 26 '20 09:02 vbabenko

I also tried to use a similar regex as @jasonhodges did, with the same result.

If I extend @commitlint/config-conventional on the commitlint.config.js file, I'll get the same 2 errors:

✖   subject may not be empty [subject-empty]
✖   type may not be empty [type-empty]

However, if I remove that extend, the headerPattern is simply ignored, and lets any message go through.

This is the regex I tried: https://regex101.com/r/SF1P42/1

RobCC avatar Feb 28 '20 02:02 RobCC

@marionebl Hi Mario, Thank you for this awasome tool. Is any news/plans for this issue?

vbabenko avatar Feb 29 '20 14:02 vbabenko

i tried to set none of any keys and values in the rules when i use local custom parser in the commitlint.config.js file,the headerPattern can be matched.

const types = [
  'build',    // 构建执行
  'ci',       // CI 相关
  'chore',    // 构建工具相关
  'docs',     // 文档更新
  'feat',     // 新功能
  'fix',      // bug 修复
  'pref',     // 性能优化
  'refactor', // 功能重构
  'revert',   // 回滚操作
  'style',    // 样式变动
  'test'      // 单元测试
];

typeEnum = {
  rules: {
    'type-enum': [2, 'always', types]
  },
  value: () => types
}

// Level,0-2:0表示禁用这条规则、1表示警告、2表示错误。
// Applicable,always|never:always表示使用规则,never表示不使用规则。
// Value:用于这条规则的值。
// https://commitlint.js.org/#/reference-rules
 
module.exports = {
    extends: [
      "@commitlint/config-conventional",
      "@commitlint/parse"
    ],
    rules: {
      // 'body-leading-blank': [1, 'always'],
      // 'footer-leading-blank': [1, 'always'],
      // 'header-max-length': [2, 'always', 72],
      // 'header-min-length': [2, 'always', 20],
      // 'scope-empty': [0, 'never'],
      // 'scope-case': [0],
      // 'subject-full-stop': [0, 'never'],
      // 'subject-case': [0, 'never'],
      // 'type-case': [0, 'always', 'lower-case'],
      // 'type-empty': [0, 'never'],
      // 'type-enum': typeEnum.rules['type-enum']
    },
    parserPreset: {
      parserOpts: {
        headerPattern: /^(build|ci|chore|docs|feat|fix|pref|refactor|revert|style|test)(\(\d+\.\d+\.\d+\)): (\S{20,})$/,
        headerCorrespondence: ['type', 'scope', 'subject']
      }
    }
  };
  • subject less than 20 letter, error, because i set the subject as (\S{20,})
echo "fix(1.12.1): dajkdajjkk" | npx commitlint
⧗   input: fix(1.12.1): dajkdajjkk
✖   subject may not be empty [subject-empty]
✖   type may not be empty [type-empty]

✖   found 2 problems, 0 warnings
ⓘ   Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

then i changed the (\S{20,}) to (\S{5,}), there is no error any more.

so, don't use any rules when use local parser!!!

ArmsZhou avatar Mar 23 '20 10:03 ArmsZhou

Okay, I'm having this problem, and I want to ask, Who requires that 'commitlint.config.js' must exist?


Why is it necessary to add this rule, even though the intention is good, It's like forcing me to drink water. It's disgusting .

PLQin avatar Nov 17 '20 08:11 PLQin

I have the same problem and wanted to ask if there is a solution in the meantime?

FelixSauer avatar Feb 04 '21 10:02 FelixSauer

Here is my demo commit to customize commit lint rules with help from here

xianshenglu avatar Apr 18 '21 12:04 xianshenglu

There is missing the document about parserPreset and parserOpts. :(

mistricky avatar May 27 '21 03:05 mistricky

This guide, by strdr4605 is a great starting point for how to write a custom parser. Cross-references in #3336 as something to reference while documenting parserOpts and headerPattern.

samayer12 avatar Oct 27 '22 22:10 samayer12

I'm still looking for a solution to only configure headerPattern without breaking everything.

baby-gnu avatar Nov 30 '23 14:11 baby-gnu

  "parserPreset": {
    "parserOpts": {
      "headerPattern": "^(.[[A-Z]{1,7}-\\d{1,7}.]) \\s*(build|ci|docs|feat|fix|perf|refactor|test)(?:\\(\\S*(animations|bazel|benchpress|common|compiler|compiler-cli|core|elements|forms|http|language-service|localize|platform-browser|platform-browser-dynamic|platform-server|router|service-worker|upgrade|zone.js|packaging|changelog|docs-infra|migrations|devtools)\\))?\\S* (.+)$",
      "headerCorrespondence": ["ticketReference", "type", "scope", "subject"]
    }
  }

Here is working Regex for angular commit pattern with optional scope group. The problem with yours regex is that you have non-grouping groups and headerCorrespondence can't recognize subject.

pasik89 avatar Dec 29 '23 10:12 pasik89