Files
bind9/dangerfile.py
Petr Špaček 2a9888ca5d Remove milestone check from dangerfile
Milestone is automatically set by 'merged-metadata' CI job to 'Not
released yet' so it does not matter what the original value was.

(cherry picked from commit 73b950dc29)
(cherry picked from commit bed0aea14f)
2024-08-23 10:53:52 +02:00

231 lines
9.8 KiB
Python

############################################################################
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
#
# See the COPYRIGHT file distributed with this work for additional
# information regarding copyright ownership.
############################################################################
import os
import re
import gitlab
# Helper functions and variables
def added_lines(target_branch, paths):
import subprocess
subprocess.check_output(['/usr/bin/git', 'fetch', '--depth', '1', 'origin',
target_branch])
diff = subprocess.check_output(['/usr/bin/git', 'diff', 'FETCH_HEAD..',
'--'] + paths)
added_lines = []
for line in diff.splitlines():
if line.startswith(b'+') and not line.startswith(b'+++'):
added_lines.append(line)
return added_lines
def lines_containing(lines, string):
return [l for l in lines if bytes(string, 'utf-8') in l]
issue_or_mr_id_regex = re.compile(br'\[(GL [#!]|RT #)[0-9]+\]')
release_notes_regex = re.compile(r'doc/(arm|notes)/notes-.*\.(rst|xml)')
modified_files = danger.git.modified_files
mr_labels = danger.gitlab.mr.labels
target_branch = danger.gitlab.mr.target_branch
is_backport = "Backport" in mr_labels or "Backport::Partial" in mr_labels
is_full_backport = is_backport and "Backport::Partial" not in mr_labels
gl = gitlab.Gitlab(
url=f"https://{os.environ['CI_SERVER_HOST']}",
private_token=os.environ["BIND_TEAM_API_TOKEN"],
)
proj = gl.projects.get(os.environ["CI_PROJECT_ID"])
mr = proj.mergerequests.get(os.environ["CI_MERGE_REQUEST_IID"])
###############################################################################
# COMMIT MESSAGES
###############################################################################
#
# - FAIL if any of the following is true for any commit on the MR branch:
#
# * The subject line starts with "fixup!" or "Apply suggestion".
#
# * The subject line contains a trailing dot.
#
# * There is no empty line between the subject line and the log message.
#
# - WARN if any of the following is true for any commit on the MR branch:
#
# * The length of the subject line for a non-merge commit exceeds 72
# characters.
#
# * There is no log message present (i.e. commit only has a subject) and
# the subject line does not contain any of the following strings:
# "fixup!", " CHANGES ", " release note".
#
# * Any line of the log message is longer than 72 characters. This rule is
# not evaluated for:
#
# - lines starting with four spaces, which allows long lines to be
# included in the commit log message by prefixing them with four
# spaces (useful for pasting compiler warnings, static analyzer
# messages, log lines, etc.),
#
# - lines which contain references (i.e. those starting with "[1]",
# "[2]", etc.) which allows e.g. long URLs to be included in the
# commit log message.
fixup_error_logged = False
for commit in danger.git.commits:
message_lines = commit.message.splitlines()
subject = message_lines[0]
if (not fixup_error_logged and
(subject.startswith('fixup!') or
subject.startswith('Apply suggestion'))):
fail('Fixup commits are still present in this merge request. '
'Please squash them before merging.')
fixup_error_logged = True
if len(subject) > 72 and not subject.startswith('Merge branch '):
warn(
f'Subject line for commit {commit.sha} is too long: '
f'```{subject}``` ({len(subject)} > 72 characters).'
)
if subject[-1] == '.':
fail(f'Trailing dot found in the subject of commit {commit.sha}.')
if len(message_lines) > 1 and message_lines[1]:
fail(f'No empty line after subject for commit {commit.sha}.')
if (len(message_lines) < 3 and
'fixup! ' not in subject and
' CHANGES ' not in subject and
' release note' not in subject):
warn(f'Please write a log message for commit {commit.sha}.')
for line in message_lines[2:]:
if (len(line) > 72 and
not line.startswith(' ') and
not re.match(r'\[[0-9]+\]', line)):
warn(
f'Line too long in log message for commit {commit.sha}: '
f'```{line}``` ({len(line)} > 72 characters).'
)
###############################################################################
# BACKPORT & VERSION LABELS
###############################################################################
#
# FAIL if any of the following is true for the merge request:
#
# * The MR is marked as Backport and the number of version labels set is
# different than 1. (For backports, the version label is used for indicating
# its target branch. This is a rather ugly attempt to address a UI
# deficiency - the target branch for each MR is not visible on milestone
# dashboards.)
#
# * The MR is not marked as "Backport" nor any version label is set. (If the
# merge request is not a backport, version labels are used for indicating
# backporting preferences.)
#
# * The Backport MR doesn't have target branch in the merge request title.
#
# * The Backport MR doesn't link to the original MR is its description.
#
# * The original MR linked to from Backport MR hasn't been merged.
BACKPORT_OF_RE = re.compile(
r"Backport\s+of.*(merge_requests/|!)([0-9]+)", flags=re.IGNORECASE
)
backport_desc = BACKPORT_OF_RE.search(danger.gitlab.mr.description)
version_labels = [l for l in mr_labels if l.startswith("v9.")]
if is_backport:
if len(version_labels) != 1:
fail(
"This MR was marked as *Backport*. "
"Please also set exactly one version label (*v9.x*)."
)
else:
mr_title_version = f"[{version_labels[0].replace('.', '_').replace(' (EoL)', '')}]"
if mr_title_version not in danger.gitlab.mr.title:
fail(
"Backport MRs must have their target branch in the "
f"title. Please put `{mr_title_version}` in the MR title."
)
if backport_desc is None:
fail(
"Backport MRs must link to the original MR. Please put "
"`Backport of MR !XXXX` in the MR description."
)
else: # backport MR is linked to original MR
original_mr_id = backport_desc.groups()[1]
original_mr = proj.mergerequests.get(original_mr_id)
if original_mr.state != "merged":
fail(
f"Original MR !{original_mr_id} has not been merged. "
"Please re-run `danger` check once it's merged."
)
else: # check for commit IDs once original MR is merged
original_mr_commits = list(original_mr.commits(all=True))
backport_mr_commits = list(mr.commits(all=True))
for orig_commit in original_mr_commits:
for backport_commit in backport_mr_commits:
if orig_commit.id in backport_commit.message:
break
else:
msg = (
f"Commit {orig_commit.id} from original MR !{original_mr_id} "
"is not referenced in any of the backport commits."
)
if not is_full_backport:
message(msg)
else:
msg += (
" Please use `-x` when cherry-picking to include "
"the full original commit ID. Alternately, use the "
"`Backport::Partial` label if not all original "
"commits are meant to be backported."
)
fail(msg)
if not is_backport and not version_labels:
fail(
"If this merge request is a backport, set the *Backport* label and "
"a single version label (*v9.x*) indicating the target branch. "
"If not, set version labels for all targeted backport branches."
)
###############################################################################
# OTHER LABELS
###############################################################################
#
# WARN if any of the following is true for the merge request:
#
# * The "Review" label is not set. (It may be intentional, but rarely is.)
#
# * The "Review" label is set, but the "LGTM" label is not set. (This aims to
# remind developers about the need to set the latter on merge requests which
# passed review.)
if 'Review' not in mr_labels:
warn('This merge request does not have the *Review* label set. '
'Please set it if you would like the merge request to be reviewed.')
elif 'LGTM (Merge OK)' not in mr_labels:
warn('This merge request is currently in review. '
'It should not be merged until it is marked with the *LGTM* label.')
###############################################################################
# PAIRWISE TESTING
###############################################################################
#
# FAIL if the merge request adds any new ./configure switch without an
# associated annotation used for pairwise testing.
configure_added_lines = added_lines(target_branch, ['configure.ac'])
switches_added = (lines_containing(configure_added_lines, 'AC_ARG_ENABLE') +
lines_containing(configure_added_lines, 'AC_ARG_WITH'))
annotations_added = lines_containing(configure_added_lines, '# [pairwise: ')
if len(switches_added) > len(annotations_added):
fail('This merge request adds at least one new `./configure` switch that '
'is not annotated for pairwise testing purposes.')