Welcome to gitflow-linter’s documentation!¶
About¶
gitflow-linter is command line tool written in Python. It checks given repository against provided rules to ensure that Gitflow is respected.
What is Gitflow? Based on Atlassian:
The Gitflow Workflow defines a strict branching model designed around the project release.
[…]
It assigns very specific roles to different branches and defines how and when they should interact. In addition to feature branches, it uses individual branches for preparing, maintaining, and recording releases.
As they wrote: Gitflow is ideally suited for projects that have a scheduled release cycle. It means that Gitflow is not always recommended, but when it is, you’d better stick to the rules!
And this is when gitflow-linter can help ;-)
Quick Start¶
Installation¶
You can install the linter from
pip
pip install gitflow-linter
or the source code
git clone https://github.com/fighterpoul/gitflow_linter.git cd gitflow_linter git checkout 0.1.0 python setup.py install
Usages¶
Usage: gitflow-linter [OPTIONS] GIT_DIRECTORY
Evaluate given repository and check if gitflow is respected
Options:
-s, --settings FILENAME
-o, --output [console|json]
-p, --fetch-prune Linter will refresh the repo before checking
-d, --allow-dirty Linter will ignore the fact that the given repo
is considered dirty
-w, --fatal-warnings Returned code will be 1 anyway, even if there
are warnings but no errors
-F, --date-from [%Y-%m-%d] Issues introduced before this date will be
ignored.
-T, --date-to [%Y-%m-%d] Issues introduced after this date will be
ignored.
--help Show this message and exit.
Standard use case looks pretty simple:
gitflow-linter /path/to/git/repository
Warning
URL to a remote is not supported. Passing https://github.com/fighterpoul/gitflow_linter.git as the argument will fail.
Hint
Run git fetch --prune before to make the repo clean and clear
Settings¶
You can manipulate how linter works by using yaml file with settings.
place the settings file in a root folder of your repo as gitflow_linter.yaml
repo_dir/ |__ .git/ |__ gitflow_linter.yaml |__ other files and folders
or
wherever you want and pass as an option.
gitflow-linter /path/to/git/repository --settings=my_settings.yaml
Example file:
branches: # optional, defines how your gitflow is configured
master: master
develop: develop
features: feature
fixes: bugfix
releases: release
hotfixes: hotfix
others:
- spike
rules: # mandatory, defines what needs to be validated and how
single_master_and_develop:
no_old_development_branches:
max_days_features: 50
no_orphan_branches:
master_must_have_tags:
no_direct_commits_to_protected_branches:
version_names_follow_convention:
version_regex: ^\d+\.\d+(\.\d+)?$ # standard major.minor(.optional patch) convention
dev_branch_names_follow_convention:
name_regex: ^\d+-[\.a-zA-Z0-9_-]+?$ # you may wish to require starting dev branches with eg. ticket numbers
no_dead_releases:
deadline_to_close_release: 30
no_dependant_features:
max_dependant_branches: 0
Rules¶
Rule |
Description |
|---|---|
|
gitflow strongly relies on the fact that there is (1) only one branch for keeping the release history and (2) only one integration branch |
|
having old feature or bugfix branches may create a mess in the repository use |
|
having branches that are out of configured folders (eg. created out of feature/, bugfix/) may be an indicator that you do something wrong and create a mess in the repo |
|
if your master branch contains commits that are not tagged, it probably means that you don’t use master as your releases history keeper |
|
the purposes behind develop and master are different but there is an assumption that at least those two are protected the rule is here to check if it is really the case and both branches does not contain direct commits (commits that were pushed directly) |
|
checks if release branches and tags follow version naming convention the convention must be specified in |
|
sometimes you may wish to have feature and bugfix branch names containing eg. ticket numbers the given convention is checked by providing if you want to provide different conventions for features and bugfixes, use |
|
release branches that are not closed may create a mess in the repository and breaks the master/main branch - releases must be closed as soon as they are deployed to production environment (or just before, depending on your case) since hotfixes are in fact releases started from master instead of develop, the rule will be checked against them as well configure how long releases are supposed to be maintained by using |
|
creating feature/bugfix branches one from another or merging them together before merging to develop may result in ugly issues during code review and merge mistakes creating such a feature/merge is sometimes inevitable, you must configure the limit of such branches by using
|
Severity¶
Each rule (even a custom one handled by a plugin) might be optionally configured by using severity option:
rules:
a_rule:
severity: info
If provided, level of all detected issues for a rule will be changed, according to provided value.
Provided severity value must correspond to one of Level enum cases:
Results¶
By default once job is done a report is printed as a text in a human readable form:
============================================================
Report for git repository: GIT_DIRECTORY
============================================================
Statistics:
main: 1 branch(es)
dev: 1 branch(es)
features: 154 branch(es)
fixes: 77 branch(es)
releases: 5 branch(es)
hotfixes: 3 branch(es)
============================================================
Results:
✅ /* rule from yaml that has been checked */
Quick info on what was checked
❌ /* next rule from yaml */
Quick info
Issues detected:
- Details of an issue, usually with the name of a problematic branch
❌ /* rule with error, eg. bad yaml config */
ERROR!
Issues detected:
- 💀 Cannot be checked because of error: /* reason, eg. missing argument */
You can change it by providing a desired output
gitflow-linter /path/to/git/repository --output=json >> results.json
Then you should see in the console something that contains the same data as above but might be further processed.
Either way, in case of any issues with error severity the exit code will be 1. If repo is all good then 0 is returned. You can change that by providing -w (or --fatal-warnings) flag to return 1 if there are warnings but no errors.
See severity section for more info.
Plugins¶
gitflow-linter is by design open for extensions, you can write a plugin and define your own rules as well as override implementation of existing ones.
Hint
The module that is installed and has name gitflow_{name}_linter will be considered as a plugin.
General principles¶
gitflow-linter is written by using Visitor pattern. Plugin is just a list of visitors that are subscribed to check corresponding rules.
# gitflow_my_awesome_plugin_linter/__init__.py
class MyAwesomeRuleVisitor(gitflow_linter.visitor.BaseVisitor):
@property
def rule(self):
return 'my_awesome_rule_that_can_be_added_into_yaml_file'
@gitflow_linter.visitor.arguments_checker('my_awesome_rule_argument')
def visit(self, repo: gitflow_linter.repository.Repository, args, **kwargs) -> gitflow_linter.report.Section:
# visit repository and return a ``Section`` with results of inspection
# you can read ``Gitflow`` options by using self.gitflow (eg. name of branches): self.gitflow.develop
# you can read your rule settings by using kwargs['my_awesome_rule_argument']
def visitors(gitflow: Gitflow) -> List[gitflow_linter.visitor.BaseVisitor]:
return [MyAwesomeRuleVisitor(gitflow=gitflow)]
Example visitor will be ran if yaml settings file contains the rule:
rules:
my_awesome_rule_that_can_be_added_into_yaml_file:
my_awesome_rule_argument: 20
Hint
If your plugin’s visitor returns an existing, pre-configured rule, it will be ran instead of default visitor. This is how you can override default behaviour.
To verify if a plugin is properly installed and recognized you can run gitflow-linter-plugins
Available gitflow-linter plugins:
- gitflow_authors_linter handles following rules:
* no_multiple_open_features_per_author
Example¶
Exploring official plugin gitflow_authors_linter seems like a good starting point.
API Reference¶
From a plugin perspective, those are the most important classes:
- class gitflow_linter.repository.Repository(repo: git.repo.base.Repo, gitflow: gitflow_linter.rules.Gitflow, should_fetch=False, allow_dirty=False)¶
- raw_query(query: callable, predicate: Optional[callable] = None, map_line: Optional[callable] = None)¶
Let you run raw queries on GitPython’s git object
- Parameters
query – callable where you can run raw query, eg.
lambda git: git.log('master')predicate – optional callable where you can decide if given line should be included, eg.
lambda commit: commit.startwith('Merged'). All lines are included if predicate is not given.map_line – optional callable where you can map line to other object, eg. when query returns list of name of branches, you can map them to branch objects:
lambda line: repo.branch(line)
- Returns
list of lines returned by query that matches optional predicate, eg.
["sha-of-commit1", "sha-of-commit2", ...]
- unique_commits_for_branch(branch: git.refs.remote.RemoteReference, force_including_head=True) → set¶
Returns set of unique commits for the given branch. Only commits that appear specifically on the branch will be returned. If a commit is included in the given branch and any other branch, it won’t be taken into consideration.
- Parameters
branch – the commits specific to the branch will be returned
force_including_head – the flag will force including head commit of the branch. If a second branch has been started from branch passed as param, the flag set to True will ensure, that at least the one head commit will be returned, otherwise second/child branch will “consume” all commits and none will be considered as unique for the given branch.
- Returns
set of unique commits for branch passed as the
- class gitflow_linter.rules.Gitflow(settings: dict)¶
Contains all settings related to branches from YAML file. It extends
AttributeDict, so settings may be accessed like properties:gitflow.featuresIt will contain custom settings if you add them in YAML file as a child of
branchesnode.
- class gitflow_linter.visitor.BaseVisitor(gitflow: gitflow_linter.rules.Gitflow)¶
Abstract class describing how gitflow-linter works. A visitor must provide a rule that it is supposed to verify. The linter will let the visitor visit a repository only if user wants to check the repository against the rule. Plugins can override default visitors by returning the same rule as a visitor they wish override.
- abstract property rule: str¶
- Returns
Rule from YAML file that is checked by the visitor
- abstract visit(repo: gitflow_linter.repository.Repository, *args, **kwargs) → gitflow_linter.report.Section¶
Verifies the
repository- checks ifself.ruleis respected- Parameters
repo – Tiny wrapper for GitPython’s repository
args –
kwargs – arguments from YAML file
- Returns
Sectionwith results
- class gitflow_linter.report.Section(rule: str, title: str, issues=None)¶
Represents repository verification done for a single rule. Results are represented by list of
Issues.- append(issue: gitflow_linter.report.Issue)¶
Adds new issue detected
- Parameters
issue – New issue detected
- Returns
- class gitflow_linter.report.Issue(level: gitflow_linter.report.Level, description: str, obj: Optional[git.refs.reference.Reference] = None)¶
- classmethod error(description: str, obj: Optional[git.refs.reference.Reference] = None)¶
Creates an
Issuewith ERROR severity for related git object
- classmethod info(description: str, obj: Optional[git.refs.reference.Reference] = None)¶
Creates an
Issuewith INFO severity for related git object
- classmethod warning(description: str, obj: Optional[git.refs.reference.Reference] = None)¶
Creates an
Issuewith WARNING severity for related git object