diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..9bab40d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,64 @@ +# Pre-commit hooks to run tests and ensure code is cleaned. +# See https://pre-commit.com for more information +--- +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.1.6 + hooks: + - id: ruff + args: ["--fix"] + - repo: https://github.com/asottile/pyupgrade + rev: v3.15.0 + hooks: + - id: pyupgrade + args: ["--keep-percent-format", "--py37-plus"] + - repo: https://github.com/psf/black + rev: 23.11.0 + hooks: + - id: black + args: ["--target-version", "py37", "--line-length", "100"] + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + args: ["--profile", "black", "--line-length", "100"] + - repo: https://github.com/PyCQA/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + args: ["--max-line-length=100"] + - repo: https://github.com/codespell-project/codespell + rev: v2.2.2 + hooks: + - id: codespell + args: + - --ignore-words-list=exten + - --skip="./.*,*.csv,*.json,*.ini,*.subject,*.txt,*.html,*.log,*.conf" + - --quiet-level=2 + - --ignore-regex=.*codespell-ignore$ + # - --write-changes # Uncomment to write changes + exclude_types: [csv, json] + - repo: https://github.com/adrienverge/yamllint + rev: v1.32.0 + hooks: + - id: yamllint + ignore: .github/ + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.7.1 + hooks: + - id: prettier + args: ["--print-width", "100"] + - repo: local + hooks: + - id: pylint + name: pylint + entry: pylint + language: system + types: [python] + require_serial: true + - repo: https://github.com/PyCQA/bandit + rev: 1.7.5 + hooks: + - id: bandit + args: [--skip, "B101", --recursive] +minimum_pre_commit_version: 3.2.0 diff --git a/.woodpecker.yml b/.woodpecker.yml index 2edfc19..ea5c29e 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,3 +1,4 @@ +--- clone: git: image: woodpeckerci/plugin-git @@ -25,7 +26,7 @@ pipeline: - echo "$GPG_KEY"|base64 -d|gpg --import - ./build.sh --quiet - rm -fr dist/gitdch-* - secrets: [ maintainer_name, maintainer_email, gpg_key, debian_codename ] + secrets: [maintainer_name, maintainer_email, gpg_key, debian_codename] publish-dryrun: group: publish diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 0000000..937d3d5 --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,7 @@ +--- +extends: default + +rules: + line-length: + max: 100 + level: warning diff --git a/README.md b/README.md index 914bc3a..7aaf6f5 100644 --- a/README.md +++ b/README.md @@ -8,29 +8,34 @@ numbered using ''git describe --always --tags'' command. # Installation ## Dependencies + ``` apt install git python3-git ``` ## Using git + ``` git clone https://gogs.zionetrix.net/bn8/gitdch.git /usr/local/src/gitdch ln -s /usr/local/src/gitdch/gitdch /usr/local/bin/gitdch ``` ## Using wget + ``` wget -O /usr/local/bin/gitdch https://gogs.zionetrix.net/bn8/gitdch/raw/master/gitdch chmod +x /usr/local/bin/gitdch ``` ## Using curl + ``` curl -o /usr/local/bin/gitdch https://gogs.zionetrix.net/bn8/gitdch/raw/master/gitdch chmod +x /usr/local/bin/gitdch ``` # Usage + ``` usage: gitdch [-h] [-d] [-v] [-w] [-l LOGFILE] [-q] [-p GIT_PATH] [-o OUTPUT] [-A] [-n PACKAGE_NAME] [-V VERSION] [--version-suffix VERSION_SUFFIX] @@ -54,12 +59,12 @@ optional arguments: -o OUTPUT, --output OUTPUT Generated Debian changelog output path (default: stdout) -A, --append Append mode: if the output changelog file already exists, - append generated changelog lines at the begining of the file + append generated changelog lines at the beginning of the file (optional, default: overwriting the file) -n PACKAGE_NAME, --package-name PACKAGE_NAME Package name -V VERSION, --version VERSION - Currrent version (default: autodetected using git describe + Current version (default: autodetected using git describe --always --tags) --version-suffix VERSION_SUFFIX Suffix for autodetected version @@ -100,4 +105,3 @@ This program is free software; you can redistribute it and/or modify it under th This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - diff --git a/build.sh b/build.sh index 5cbcdb3..d3ecfba 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,7 @@ sed -i "s/^VERSION *=.*$/VERSION = '$VERSION'/" $BDIR/gitdch if [ -z "$DEBIAN_CODENAME" ] then - echo "Retreive debian codename using lsb_release..." + echo "Retrieve debian codename using lsb_release..." DEBIAN_CODENAME=$( lsb_release -c -s ) else echo "Use debian codename from environment ($DEBIAN_CODENAME)" diff --git a/gitdch b/gitdch index f8fe2f2..35162bb 100755 --- a/gitdch +++ b/gitdch @@ -12,196 +12,166 @@ import textwrap import git from git.exc import GitCommandError -VERSION = '0.0' -DEFAULT_GIT_PATCH = './' -DEFAULT_CODE_NAME = 'unstable' -DEFAULT_URGENCY = 'medium' +VERSION = "0.0" +DEFAULT_GIT_PATCH = "./" +DEFAULT_CODE_NAME = "unstable" +DEFAULT_URGENCY = "medium" -parser = argparse.ArgumentParser( - description='{0} (version: {1})'.format(__doc__, VERSION) +parser = argparse.ArgumentParser(description=f"{__doc__} (version: {VERSION})") + +parser.add_argument("-d", "--debug", action="store_true", help="Show debug messages") + +parser.add_argument("-v", "--verbose", action="store_true", help="Show verbose messages") + +parser.add_argument("-w", "--warning", action="store_true", help="Show warning messages") + +parser.add_argument( + "-l", "--log-file", action="store", type=str, dest="logfile", help="Log file path" ) parser.add_argument( - '-d', '--debug', - action='store_true', - help='Show debug messages' + "-q", + "--quiet", + action="store_true", + help="Quiet mode: do not log on console (only if log file is provided)", ) parser.add_argument( - '-v', '--verbose', - action='store_true', - help='Show verbose messages' -) - -parser.add_argument( - '-w', '--warning', - action='store_true', - help='Show warning messages' -) - -parser.add_argument( - '-l', - '--log-file', - action="store", + "-p", + "--path", type=str, - dest="logfile", - help="Log file path" + dest="git_path", + help="Git repository path (default: %s)" % DEFAULT_GIT_PATCH, + default=DEFAULT_GIT_PATCH, ) parser.add_argument( - '-q', '--quiet', - action='store_true', - help='Quiet mode: do not log on console (only if log file is provided)' -) - -parser.add_argument( - '-p', - '--path', + "-o", + "--output", type=str, - dest='git_path', - help='Git repository path (default: %s)' % DEFAULT_GIT_PATCH, - default=DEFAULT_GIT_PATCH + dest="output", + help="Generated Debian changelog output path (default: stdout)", ) parser.add_argument( - '-o', - '--output', - type=str, - dest='output', - help='Generated Debian changelog output path (default: stdout)', -) - -parser.add_argument( - '-A', - '--append', - action='store_true', - dest='append', + "-A", + "--append", + action="store_true", + dest="append", help=( - 'Append mode: if the output changelog file already exists, append ' - 'generated changelog lines at the begining of the file (optional, ' - 'default: overwriting the file)' - ) -) - -parser.add_argument( - '-n', - '--package-name', - type=str, - dest='package_name', - help='Package name' -) - -parser.add_argument( - '-V', - '--version', - type=str, - dest='version', - help=( - 'Currrent version (default: autodetected using git describe ' - '--always --tags)') -) - -parser.add_argument( - '--version-suffix', - type=str, - dest='version_suffix', - help='Suffix for autodetected version' -) - -parser.add_argument( - '-c', - '--code-name', - type=str, - dest='code_name', - help='Debian code name (default: %s)' % DEFAULT_CODE_NAME, - default=DEFAULT_CODE_NAME -) - -parser.add_argument( - '-u', - '--urgency', - type=str, - dest='urgency', - help='Package urgency (default: %s)' % DEFAULT_URGENCY, - default=DEFAULT_URGENCY -) - -parser.add_argument( - '-N', - '--maintainer-name', - type=str, - dest='maintainer_name', - help='Maintainer name (default: last commit author name)' -) - -parser.add_argument( - '-E', - '--maintainer-email', - type=str, - dest='maintainer_email', - help='Maintainer email (default: last commit author email)' -) - -parser.add_argument( - '-R', - '--release-notes', - type=str, - dest='release_notes', - help='Specify an optional Markdown release notes output path' -) - -parser.add_argument( - '--revision', - type=str, - dest='revision', - help=( - 'Specify the revision to use to generate the changelog (see ' - 'git-rev-parse for viable options, optional, default: generate the ' - 'changelog with all commits of the current branch) ' + "Append mode: if the output changelog file already exists, append " + "generated changelog lines at the beginning of the file (optional, " + "default: overwriting the file)" ), - default=None +) + +parser.add_argument("-n", "--package-name", type=str, dest="package_name", help="Package name") + +parser.add_argument( + "-V", + "--version", + type=str, + dest="version", + help=("Current version (default: autodetected using git describe " "--always --tags)"), ) parser.add_argument( - '-C', '--clean-tags-regex', - action='append', - type=re.compile, - dest='clean_tags_regex', + "--version-suffix", type=str, dest="version_suffix", help="Suffix for autodetected version" +) + +parser.add_argument( + "-c", + "--code-name", + type=str, + dest="code_name", + help="Debian code name (default: %s)" % DEFAULT_CODE_NAME, + default=DEFAULT_CODE_NAME, +) + +parser.add_argument( + "-u", + "--urgency", + type=str, + dest="urgency", + help="Package urgency (default: %s)" % DEFAULT_URGENCY, + default=DEFAULT_URGENCY, +) + +parser.add_argument( + "-N", + "--maintainer-name", + type=str, + dest="maintainer_name", + help="Maintainer name (default: last commit author name)", +) + +parser.add_argument( + "-E", + "--maintainer-email", + type=str, + dest="maintainer_email", + help="Maintainer email (default: last commit author email)", +) + +parser.add_argument( + "-R", + "--release-notes", + type=str, + dest="release_notes", + help="Specify an optional Markdown release notes output path", +) + +parser.add_argument( + "--revision", + type=str, + dest="revision", help=( - 'Clean tags regex: you could specify regex to clean tag names when ' + "Specify the revision to use to generate the changelog (see " + "git-rev-parse for viable options, optional, default: generate the " + "changelog with all commits of the current branch) " + ), + default=None, +) + +parser.add_argument( + "-C", + "--clean-tags-regex", + action="append", + type=re.compile, + dest="clean_tags_regex", + help=( + "Clean tags regex: you could specify regex to clean tag names when " 'computing package versions. For instance, to drop a "-eeXXX" suffix ' 'of tag names, specify -C "\\-ee[0-9]{3}$" (optional, multiple regex ' - 'allowed)' + "allowed)" ), - default=[] + default=[], ) parser.add_argument( - '-x', '--exclude', - action='append', + "-x", + "--exclude", + action="append", type=re.compile, - dest='exclude', + dest="exclude", help=( - 'Commit exclusion regex: you could specify regex to exclude some ' - 'commits from generated changelog entries. For instance, to exclude ' + "Commit exclusion regex: you could specify regex to exclude some " + "commits from generated changelog entries. For instance, to exclude " 'commits with message starting with "CI: ", specify -x "^CI: " ' - '(optional, multiple regex allowed)' + "(optional, multiple regex allowed)" ), - default=[] + default=[], ) options = parser.parse_args() if not options.package_name: - parser.error( - 'You must provide package name using -n/--package-name parameter') + parser.error("You must provide package name using -n/--package-name parameter") # Initialize log log = logging.getLogger() logformat = logging.Formatter( - '%(asctime)s - {} - %(levelname)s : %(message)s'.format( - os.path.basename(sys.argv[0]) - ) + f"%(asctime)s - {os.path.basename(sys.argv[0])} - %(levelname)s : %(message)s" ) # Set root logger to DEBUG (filtering done by handlers) log.setLevel(logging.DEBUG) @@ -230,68 +200,63 @@ repo = git.Repo(options.git_path) def clean_deb_version(version_name): - """ Clean debian version name """ - version_name = re.sub('^[^0-9]*', '', version_name) + """Clean debian version name""" + version_name = re.sub("^[^0-9]*", "", version_name) for clean_regex in options.clean_tags_regex: - version_name = clean_regex.sub('', version_name) + version_name = clean_regex.sub("", version_name) if options.version_suffix: version_name += options.version_suffix return version_name if not options.version: - log.info('Detect current version from git tags & commits') - options.version = clean_deb_version( - repo.git.describe('--always', '--tags') - ) - log.info('Currrent version detected: %s', options.version) + log.info("Detect current version from git tags & commits") + options.version = clean_deb_version(repo.git.describe("--always", "--tags")) + log.info("Current version detected: %s", options.version) if options.output and options.append and not options.revision: log.info( - 'Append mode enabled but no revision specify, try to detect it from ' - 'last modification of the changelog file') + "Append mode enabled but no revision specify, try to detect it from " + "last modification of the changelog file" + ) try: last_change_commit = next(repo.iter_commits(paths=options.output)) # pylint: disable=consider-using-f-string - options.revision = '{0}..HEAD'.format(last_change_commit) + options.revision = f"{last_change_commit}..HEAD" log.info( 'Last change commit of the output file is "%s": use revision "%s"', - last_change_commit, options.revision) + last_change_commit, + options.revision, + ) except StopIteration: log.warning( - 'Fail to auto-detect last change commit of changelog file: it ' - 'seem not tracked. Continue without revision.') + "Fail to auto-detect last change commit of changelog file: it " + "seem not tracked. Continue without revision." + ) except GitCommandError: log.warning( "Fail to auto-detect last change commit of changelog file. May " "be it's outside of the git repository. Continue without " - "revision.") + "revision." + ) # Reset repo object of to avoid BrokenPipeError repo = git.Repo(options.git_path) -log.info('Generate changelog from git commits') +log.info("Generate changelog from git commits") versions = [] -tag_commits = dict( - (tag.commit.binsha, tag) - for tag in repo.tags -) +tag_commits = {tag.commit.binsha: tag for tag in repo.tags} def add_version(): - """ Add version info """ + """Add version info""" global messages # pylint: disable=global-statement if not version_commit: return if not messages: - messages = ['Release version {0}'.format(version)] - log.info('Add version %s:\n - %s', version, '\n - '.join(messages)) - versions.append({ - 'name': version, - 'tag': tag, - 'commit': version_commit, - 'messages': messages - }) + messages = [f"Release version {version}"] + log.info("Add version %s:\n - %s", version, "\n - ".join(messages)) + versions.append({"name": version, "tag": tag, "commit": version_commit, "messages": messages}) tag = None @@ -299,10 +264,10 @@ version_commit = None version = options.version messages = [] for commit in repo.iter_commits(rev=options.revision): - log.debug('Commit %s (%s)', commit, commit.summary) + log.debug("Commit %s (%s)", commit, commit.summary) if commit.binsha in tag_commits: new_tag = tag_commits[commit.binsha] - log.debug('Reach new tag %s', new_tag) + log.debug("Reach new tag %s", new_tag) add_version() @@ -310,7 +275,7 @@ for commit in repo.iter_commits(rev=options.revision): version = clean_deb_version(tag.name) version_commit = commit messages = [] - log.debug('Iter commits for version %s', version) + log.debug("Iter commits for version %s", version) if version_commit is None: version_commit = commit excluded = False @@ -318,76 +283,65 @@ for commit in repo.iter_commits(rev=options.revision): if regex.search(commit.summary): excluded = True log.debug( - 'Exclude commit %s ("%s", match with "%s")', - commit, commit.summary, regex.pattern) + 'Exclude commit %s ("%s", match with "%s")', commit, commit.summary, regex.pattern + ) if not excluded: messages.append(commit.summary) add_version() -log.info('%d versions found', len(versions)) +log.info("%d versions found", len(versions)) changelog_lines = [] for version in versions: # pylint: disable=consider-using-f-string changelog_lines.append( - '{package} ({version}) {code_name}; urgency={urgency}\n\n'.format( + "{package} ({version}) {code_name}; urgency={urgency}\n\n".format( package=options.package_name, - version=version['name'], + version=version["name"], code_name=options.code_name, - urgency=options.urgency + urgency=options.urgency, ) ) - for message in version['messages']: - for idx, line in enumerate( - textwrap.wrap(message, 76, break_long_words=True) - ): + for message in version["messages"]: + for idx, line in enumerate(textwrap.wrap(message, 76, break_long_words=True)): # pylint: disable=consider-using-f-string - changelog_lines.append( - '{0}{1}\n'.format( - ' * ' if not idx else ' ', - line - ) - ) + changelog_lines.append("{}{}\n".format(" * " if not idx else " ", line)) # pylint: disable=consider-using-f-string changelog_lines.append( "\n -- {name} <{email}> {date}\n\n".format( name=( - options.maintainer_name if options.maintainer_name - else version['commit'].author.name), + options.maintainer_name + if options.maintainer_name + else version["commit"].author.name + ), email=( - options.maintainer_email if options.maintainer_email - else version['commit'].author.email), - date=version['commit'].committed_datetime.strftime( - "%a, %d %b %Y %H:%M:%S %z") + options.maintainer_email + if options.maintainer_email + else version["commit"].author.email + ), + date=version["commit"].committed_datetime.strftime("%a, %d %b %Y %H:%M:%S %z"), ) ) if options.output: - log.info('Write generated Debian changelog in file %s', options.output) + log.info("Write generated Debian changelog in file %s", options.output) if options.append and os.path.exists(options.output): - with open(options.output, 'r', encoding='utf8') as fd: + with open(options.output, encoding="utf8") as fd: changelog_lines += [""] changelog_lines += fd.readlines() - with open(options.output, 'w', encoding='utf8') as fd: + with open(options.output, "w", encoding="utf8") as fd: fd.writelines(changelog_lines) else: - print(''.join(changelog_lines)) + print("".join(changelog_lines)) if options.release_notes: - log.info('Generate Markdown release notes') - release_notes_lines = ['# Changelog:\n\n'] + log.info("Generate Markdown release notes") + release_notes_lines = ["# Changelog:\n\n"] if versions: - release_notes_lines.extend([ - '* {0}\n'.format(message) - for message in versions[0]['messages'] - ]) + release_notes_lines.extend([f"* {message}\n" for message in versions[0]["messages"]]) else: - release_notes_lines.extend([ - '* Release version {0}\n'.format(options.version) - ]) - log.info( - 'Write generated Markdown release notes in file %s', - options.release_notes) - with open(options.release_notes, 'w', encoding='utf8') as fd: + release_notes_lines.extend([f"* Release version {options.version}\n"]) + log.info("Write generated Markdown release notes in file %s", options.release_notes) + with open(options.release_notes, "w", encoding="utf8") as fd: fd.writelines(release_notes_lines)