Skip to content

Commit

Permalink
tools: Setup repo with linters etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
adambirds committed Feb 17, 2022
1 parent 52fcc6a commit 3f17bc4
Show file tree
Hide file tree
Showing 22 changed files with 664 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ venv/
ENV/
env.bak/
venv.bak/
python-discord-insult-bot-venv/

# Spyder project settings
.spyderproject
Expand All @@ -127,3 +128,10 @@ dmypy.json

# Pyre type checker
.pyre/

# node_modules
node_modules/

# Exclude

!tools/lib/
13 changes: 13 additions & 0 deletions .gitlint
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[general]
ignore=title-trailing-punctuation, body-min-length, body-is-missing

extra-path=tools/lib/gitlint-rules.py

[title-match-regex]
regex=^(.+:\ )?[A-Z].+\.$

[title-max-length]
line-length=76

[body-max-line-length]
line-length=76
1 change: 1 addition & 0 deletions .shellcheckrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
disable=SC1091
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "prettier",
"version": "2.5.1",
"description": "[![code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![mypy coverage](https://img.shields.io/badge/mypy-100%25-green.svg)](https://github.com/python/mypy)",
"main": "index.js",
"directories": {
"doc": "docs"
},
"dependencies": {
"prettier": "^2.5.1"
},
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/adambirds/python-discord-insult-bot.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/adambirds/python-discord-insult-bot/issues"
},
"homepage": "https://github.com/adambirds/python-discord-insult-bot#readme"
}
8 changes: 8 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[tool.black]
line-length = 100
target-version = ["py36"]

[tool.isort]
src_paths = [".", "src"]
profile = "black"
line_length = 100
13 changes: 13 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pyyaml

## Zulint

https://github.com/zulip/zulint/archive/6cc46d23906757895e917cc75e231f81f824a31d.zip#egg=zulint==0.0.1

## Linters

black
isort
gitlint
pyflakes
mypy
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pyyaml
Empty file added tools/__init__.py
Empty file.
23 changes: 23 additions & 0 deletions tools/commit-message-lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash

# Lint all commit messages that are newer than upstream/master if running
# locally or the commits in the push or PR if in CircleCI.

# The rules can be found in /.gitlint

if [[ "
$(git remote -v)
" =~ '
'([^[:space:]]*)[[:space:]]*(https://github\.com/|ssh://git@github\.com/|git@github\.com:)adambirds/python-discord-insult-bot(\.git|/)?\ \(fetch\)'
' ]]; then
range="${BASH_REMATCH[1]}/main..HEAD"
else
range="upstream/main..HEAD"
fi

commits=$(git log "$range" | wc -l)
if [ "$commits" -gt 0 ]; then
# Only run gitlint with non-empty commit lists, to avoid a printed
# warning.
gitlint --commits "$range"
fi
15 changes: 15 additions & 0 deletions tools/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash

# This hook runs gitlint on your commit message.

# Do not invoke gitlint if commit message is empty
if grep -q '^[^#]' "$1"; then
lint_cmd="tools/lint --only=gitlint"
if
! eval "$lint_cmd" <"$1"
then
echo "WARNING: Your commit message does not match python-discord-insult-bot's style guide."
fi
fi

exit 0
Empty file added tools/lib/__init__.py
Empty file.
52 changes: 52 additions & 0 deletions tools/lib/custom_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from typing import List

from zulint.custom_rules import Rule, RuleList

trailing_whitespace_rule: "Rule" = {
"pattern": r"\s+$",
"strip": "\n",
"description": "Fix trailing whitespace",
}
whitespace_rules: List["Rule"] = [
# This linter should be first since bash_rules depends on it.
trailing_whitespace_rule,
]

markdown_whitespace_rules: List["Rule"] = [
*(rule for rule in whitespace_rules if rule["pattern"] != r"\s+$"),
# Two spaces trailing a line with other content is okay--it's a Markdown line break.
# This rule finds one space trailing a non-space, three or more trailing spaces, and
# spaces on an empty line.
{
"pattern": r"((?<!\s)\s$)|(\s\s\s+$)|(^\s+$)",
"strip": "\n",
"description": "Fix trailing whitespace",
},
{
"pattern": "^#+[A-Za-z0-9]",
"strip": "\n",
"description": "Missing space after # in heading",
"good_lines": ["### some heading", "# another heading"],
"bad_lines": ["###some heading", "#another heading"],
},
]

markdown_rules = RuleList(
langs=["md"],
rules=[
*markdown_whitespace_rules,
{
"pattern": r"\[(?P<url>[^\]]+)\]\((?P=url)\)",
"description": "Linkified Markdown URLs should use cleaner <http://example.com> syntax.",
},
{
"pattern": r"\][(][^#h]",
"description": "Use absolute links from docs served by GitHub",
},
],
max_length=120,
)

non_py_rules = [
markdown_rules,
]
110 changes: 110 additions & 0 deletions tools/lib/gitlint-rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
from typing import List

from gitlint.git import GitCommit
from gitlint.rules import CommitMessageTitle, LineRule, RuleViolation

# Word list from https://github.com/m1foley/fit-commit
# Copyright (c) 2015 Mike Foley
# License: MIT
# Ref: fit_commit/validators/tense.rb
TENSE_DATA = [
(["adds", "adding", "added"], "add"),
(["allows", "allowing", "allowed"], "allow"),
(["amends", "amending", "amended"], "amend"),
(["bumps", "bumping", "bumped"], "bump"),
(["calculates", "calculating", "calculated"], "calculate"),
(["changes", "changing", "changed"], "change"),
(["cleans", "cleaning", "cleaned"], "clean"),
(["commits", "committing", "committed"], "commit"),
(["corrects", "correcting", "corrected"], "correct"),
(["creates", "creating", "created"], "create"),
(["darkens", "darkening", "darkened"], "darken"),
(["disables", "disabling", "disabled"], "disable"),
(["displays", "displaying", "displayed"], "display"),
(["documents", "documenting", "documented"], "document"),
(["drys", "drying", "dryed"], "dry"),
(["ends", "ending", "ended"], "end"),
(["enforces", "enforcing", "enforced"], "enforce"),
(["enqueues", "enqueuing", "enqueued"], "enqueue"),
(["extracts", "extracting", "extracted"], "extract"),
(["finishes", "finishing", "finished"], "finish"),
(["fixes", "fixing", "fixed"], "fix"),
(["formats", "formatting", "formatted"], "format"),
(["guards", "guarding", "guarded"], "guard"),
(["handles", "handling", "handled"], "handle"),
(["hides", "hiding", "hid"], "hide"),
(["increases", "increasing", "increased"], "increase"),
(["ignores", "ignoring", "ignored"], "ignore"),
(["implements", "implementing", "implemented"], "implement"),
(["improves", "improving", "improved"], "improve"),
(["keeps", "keeping", "kept"], "keep"),
(["kills", "killing", "killed"], "kill"),
(["makes", "making", "made"], "make"),
(["merges", "merging", "merged"], "merge"),
(["moves", "moving", "moved"], "move"),
(["permits", "permitting", "permitted"], "permit"),
(["prevents", "preventing", "prevented"], "prevent"),
(["pushes", "pushing", "pushed"], "push"),
(["rebases", "rebasing", "rebased"], "rebase"),
(["refactors", "refactoring", "refactored"], "refactor"),
(["removes", "removing", "removed"], "remove"),
(["renames", "renaming", "renamed"], "rename"),
(["reorders", "reordering", "reordered"], "reorder"),
(["replaces", "replacing", "replaced"], "replace"),
(["requires", "requiring", "required"], "require"),
(["restores", "restoring", "restored"], "restore"),
(["sends", "sending", "sent"], "send"),
(["sets", "setting"], "set"),
(["separates", "separating", "separated"], "separate"),
(["shows", "showing", "showed"], "show"),
(["simplifies", "simplifying", "simplified"], "simplify"),
(["skips", "skipping", "skipped"], "skip"),
(["sorts", "sorting"], "sort"),
(["speeds", "speeding", "sped"], "speed"),
(["starts", "starting", "started"], "start"),
(["supports", "supporting", "supported"], "support"),
(["takes", "taking", "took"], "take"),
(["testing", "tested"], "test"), # "tests" excluded to reduce false negatives
(["truncates", "truncating", "truncated"], "truncate"),
(["updates", "updating", "updated"], "update"),
(["uses", "using", "used"], "use"),
]

TENSE_CORRECTIONS = {word: imperative for words, imperative in TENSE_DATA for word in words}


class ImperativeMood(LineRule):
"""This rule will enforce that the commit message title uses imperative
mood. This is done by checking if the first word is in `WORD_SET`, if so
show the word in the correct mood."""

name = "title-imperative-mood"
id = "Z1"
target = CommitMessageTitle

error_msg = (
"The first word in commit title should be in imperative mood "
'("{word}" -> "{imperative}"): "{title}"'
)

def validate(self, line: str, commit: GitCommit) -> List[RuleViolation]:
violations = []

# Ignore the section tag (ie `<section tag>: <message body>.`)
words = line.split(": ", 1)[-1].split()
first_word = words[0].lower()

if first_word in TENSE_CORRECTIONS:
imperative = TENSE_CORRECTIONS[first_word]
violation = RuleViolation(
self.id,
self.error_msg.format(
word=first_word,
imperative=imperative,
title=commit.message.title,
),
)

violations.append(violation)

return violations
Loading

0 comments on commit 3f17bc4

Please sign in to comment.