Skip to content

Commit 7cc3232

Browse files
authored
Enhancement (#12)
* Change copyright template * fix pdoc version * fix look for news files * refactored some git helpers * changed API for creating news files * flake8 * fix pdoc version * News files
1 parent 1ce6fd4 commit 7cc3232

23 files changed

+223
-93
lines changed

Pipfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pytest = "*"
1616
pyfakefs = "*"
1717
pytest-cov = "*"
1818
wheel = "*"
19-
continuous-delivery-scripts = {path = "."}
19+
continuous-delivery-scripts = {editable = true, path = "."}
2020
pre-commit = "*"
2121

2222
[pipenv]

azure-pipelines/build-release.yml

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ stages:
3939
Linux_Py_3_8:
4040
python.version: '3.8'
4141
vmImageName: ubuntu-latest
42+
uploadCoverage: "false"
43+
44+
Linux_Py_3_9:
45+
python.version: '3.9'
46+
vmImageName: ubuntu-latest
4247
uploadCoverage: "true"
4348

4449
Windows_Py_3_6:
@@ -112,9 +117,9 @@ stages:
112117

113118
steps:
114119
- task: UsePythonVersion@0
115-
displayName: 'Use Python 3.7'
120+
displayName: 'Use Python 3.9'
116121
inputs:
117-
versionSpec: '3.7'
122+
versionSpec: '3.9'
118123

119124
- template: steps/determine-current-branch.yml
120125

@@ -135,9 +140,9 @@ stages:
135140

136141
steps:
137142
- task: UsePythonVersion@0
138-
displayName: 'Use Python 3.7'
143+
displayName: 'Use Python 3.9'
139144
inputs:
140-
versionSpec: '3.7'
145+
versionSpec: '3.9'
141146

142147
- template: steps/install-development-dependencies.yml
143148

@@ -158,9 +163,9 @@ stages:
158163

159164
steps:
160165
- task: UsePythonVersion@0
161-
displayName: 'Use Python 3.7'
166+
displayName: 'Use Python 3.9'
162167
inputs:
163-
versionSpec: '3.7'
168+
versionSpec: '3.9'
164169

165170
- template: steps/install-development-dependencies.yml
166171

@@ -202,7 +207,7 @@ stages:
202207
steps:
203208
- task: UsePythonVersion@0
204209
inputs:
205-
versionSpec: '3.7'
210+
versionSpec: '3.9'
206211

207212
- template: steps/determine-current-branch.yml
208213

@@ -237,7 +242,7 @@ stages:
237242
steps:
238243
- task: UsePythonVersion@0
239244
inputs:
240-
versionSpec: '3.7'
245+
versionSpec: '3.9'
241246

242247
- template: steps/determine-current-branch.yml
243248

continuous_delivery_scripts/assert_news.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from continuous_delivery_scripts.utils.configuration import configuration, ConfigurationVariable
1414
from continuous_delivery_scripts.utils.git_helpers import ProjectTempClone, LocalProjectRepository, GitWrapper
1515
from continuous_delivery_scripts.utils.logging import log_exception, set_log_level
16+
from continuous_delivery_scripts.utils.news_file import create_news_file
1617

1718
logger = logging.getLogger(__name__)
1819

@@ -92,6 +93,39 @@ def validate_news_files(git: GitWrapper, root_dir: str, news_dir: str) -> None:
9293
validate_news_file(absolute_file_path)
9394

9495

96+
def add_news_files(git: GitWrapper, news_dir: str) -> None:
97+
"""Adds a news file if the branch corresponds to an dependency update.
98+
99+
Args:
100+
git: Instance of GitWrapper.
101+
news_dir: Relative path to news directory.
102+
"""
103+
current_branch = str(git.get_current_branch())
104+
is_dependency_update, groups = git.is_current_branch_of_type(
105+
str(configuration.get_value(ConfigurationVariable.DEPENDENCY_UPDATE_BRANCH_PATTERN))
106+
)
107+
if not is_dependency_update:
108+
raise EnvironmentError(f"Branch {current_branch} must contain a news file.")
109+
if not configuration.get_value(ConfigurationVariable.AUTOGENERATE_NEWS_FILE_ON_DEPENDENCY_UPDATE):
110+
raise EnvironmentError(f"Branch {current_branch} must contain a news file.")
111+
112+
create_news_file(
113+
news_dir,
114+
str(configuration.get_value(ConfigurationVariable.DEPENDENCY_UPDATE_NEWS_MESSAGE)).format(
115+
message=", ".join(groups)
116+
),
117+
configuration.get_value(ConfigurationVariable.DEPENDENCY_UPDATE_NEWS_TYPE),
118+
)
119+
120+
121+
def _commit_news_file(git: GitWrapper, news_dir: str) -> None:
122+
logger.info("Committing news file...")
123+
git.add(news_dir)
124+
git.commit("Adding news file")
125+
git.push()
126+
git.pull()
127+
128+
95129
def main() -> None:
96130
"""Asserts the new PR comprises at least one news file and it adheres to the required standard."""
97131
parser = argparse.ArgumentParser(description="Check correctly formatted news files exist on feature branch.")
@@ -114,7 +148,12 @@ def main() -> None:
114148
validate_news_files(git=git, news_dir=news_dir, root_dir=root_dir)
115149
except Exception as e:
116150
log_exception(logger, e)
117-
sys.exit(1)
151+
try:
152+
add_news_files(git, absolute_news_dir)
153+
_commit_news_file(git, absolute_news_dir)
154+
except Exception as e2:
155+
log_exception(logger, e2)
156+
sys.exit(1)
118157

119158

120159
if __name__ == "__main__":

continuous_delivery_scripts/create_news_file.py

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -8,70 +8,28 @@
88
create-news-file "Fixed a bug" --type bugfix
99
"""
1010
import argparse
11-
import enum
1211
import logging
13-
import pathlib
12+
1413
import sys
15-
from datetime import datetime
1614

1715
from continuous_delivery_scripts.utils.configuration import configuration, ConfigurationVariable
1816
from continuous_delivery_scripts.assert_news import validate_news_file
1917
from continuous_delivery_scripts.utils.logging import log_exception
18+
from continuous_delivery_scripts.utils.news_file import NewsType, create_news_file
2019

2120
logger = logging.getLogger(__name__)
2221

23-
2422
NEWS_DIR = configuration.get_value(ConfigurationVariable.NEWS_DIR)
2523

2624

27-
class NewsType(enum.Enum):
28-
"""Describes the type of news we're writing."""
29-
30-
bugfix = 0
31-
doc = 1
32-
feature = 2
33-
major = 3
34-
misc = 4
35-
removal = 5
36-
37-
38-
def create_news_file(news_text: str, news_type: NewsType) -> pathlib.Path:
39-
"""Facilitates creating a news file, determining it's file name based on the type."""
40-
file_path = determine_news_file_path(news_type)
41-
_write_file(file_path, news_text)
42-
return file_path
43-
44-
45-
def determine_news_file_path(news_type: NewsType) -> pathlib.Path:
46-
"""Returns an available file path for given news type."""
47-
news_file_name = _determine_todays_news_file_name()
48-
news_file_path = pathlib.Path(NEWS_DIR, f"{news_file_name}.{news_type.name}")
49-
inc = 0
50-
while news_file_path.exists():
51-
inc += 1
52-
news_file_path = news_file_path.with_name(f"{news_file_name}{inc:0=2}.{news_type.name}")
53-
return news_file_path
54-
55-
56-
def _write_file(file_path: pathlib.Path, text: str) -> None:
57-
file_path.parent.mkdir(parents=True, exist_ok=True)
58-
if not text.endswith("\n"):
59-
text = f"{text}\n"
60-
file_path.write_text(text)
61-
62-
63-
def _determine_todays_news_file_name() -> str:
64-
return datetime.now().strftime("%Y%m%d%H%M")
65-
66-
6725
def main() -> int:
6826
"""Parses cli arguments and creates a news file."""
6927
parser = argparse.ArgumentParser()
7028
parser.add_argument("news_text", help="Contents of the news file.")
7129
parser.add_argument("--type", help="News type to create.", choices=[t.name for t in NewsType], default="feature")
7230

7331
args = parser.parse_args()
74-
created_file = create_news_file(args.news_text, NewsType[args.type])
32+
created_file = create_news_file(str(NEWS_DIR), args.news_text, NewsType[args.type])
7533

7634
try:
7735
validate_news_file(created_file)

continuous_delivery_scripts/license_files.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,17 @@
2525
ADDITIONAL_EXTENSIONS = ["python=.toml", "c=.go"]
2626

2727

28-
def add_licence_header(verbose_count: int) -> None:
28+
def insert_licence_header(verbose_count: int) -> None:
29+
"""Inserts a copyright notice at the top of every source file of the current project.
30+
31+
Wrapper over the [licenseheaders tool](https://github.com/johann-petrak/licenseheaders).
32+
"""
33+
# copyright (https://github.com/knipknap/copyright) was first considered but
34+
# comprises quite a few bugs and does not seem active anymore.
35+
add_licence_header(verbose_count, Path(configuration.get_value(ConfigurationVariable.PROJECT_ROOT)))
36+
37+
38+
def add_licence_header(verbose_count: int, src: Path) -> None:
2939
"""Puts a copyright notice at the top of every source file.
3040
3141
Wrapper over the [licenseheaders tool](https://github.com/johann-petrak/licenseheaders).
@@ -40,7 +50,7 @@ def add_licence_header(verbose_count: int) -> None:
4050
logger.debug(f"Creates template file in {str(template_file_path)}")
4151
template_file.write(template_string.encode("utf8"))
4252
template_file.close()
43-
copyright_config = get_tool_config(template_file_path)
53+
copyright_config = get_tool_config(template_file_path, src)
4454
_call_licensehearders(copyright_config, verbose_count)
4555

4656

@@ -65,12 +75,12 @@ def _to_copyright_date_string(start: int, current: int) -> str:
6575
return f"{current}" if current == start else f"{start}-{current}"
6676

6777

68-
def get_tool_config(template_file: Path) -> dict:
78+
def get_tool_config(template_file: Path, src: Path) -> dict:
6979
"""Gets the configuration for licenseheaders."""
7080
copyright_dates = _determines_copyright_dates()
7181
return {
7282
"owner": configuration.get_value(ConfigurationVariable.ORGANISATION),
73-
"dir": configuration.get_value(ConfigurationVariable.PROJECT_ROOT),
83+
"dir": src,
7484
"projname": configuration.get_value(ConfigurationVariable.PROJECT_NAME),
7585
"tmpl": str(template_file),
7686
"years": copyright_dates,
@@ -86,7 +96,7 @@ def main() -> int:
8696
args = parser.parse_args()
8797
set_log_level(args.verbose)
8898
try:
89-
add_licence_header(args.verbose)
99+
insert_licence_header(args.verbose)
90100
except Exception as e:
91101
log_exception(logger, e)
92102
return 1

continuous_delivery_scripts/tag_and_release.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,8 @@
1313
from continuous_delivery_scripts.generate_docs import generate_documentation
1414
from continuous_delivery_scripts.generate_news import version_project
1515
from continuous_delivery_scripts.language_specifics import get_language_specifics
16-
from continuous_delivery_scripts.license_files import add_licence_header
17-
from continuous_delivery_scripts.report_third_party_ip import (
18-
generate_spdx_project_reports,
19-
SpdxProject,
20-
)
16+
from continuous_delivery_scripts.license_files import insert_licence_header
17+
from continuous_delivery_scripts.report_third_party_ip import generate_spdx_project_reports, SpdxProject
2118
from continuous_delivery_scripts.utils.configuration import configuration, ConfigurationVariable
2219
from continuous_delivery_scripts.utils.definitions import CommitType
2320
from continuous_delivery_scripts.utils.git_helpers import ProjectTempClone, GitWrapper
@@ -50,7 +47,7 @@ def tag_and_release(mode: CommitType, current_branch: Optional[str] = None) -> N
5047
_update_documentation()
5148
# Adding the licensing summaries in /docs after folder has been cleared and regenerated.
5249
spdx_project = _update_licensing_summary()
53-
add_licence_header(0)
50+
insert_licence_header(0)
5451
_update_repository(mode, is_new_version, version, current_branch)
5552
if is_new_version:
5653
if spdx_project:

continuous_delivery_scripts/utils/configuration.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import toml
1414

1515
from .filesystem_helpers import find_file_in_tree
16+
from .news_file import NewsType
1617

1718
logger = logging.getLogger(__name__)
1819

@@ -49,6 +50,10 @@ class ConfigurationVariable(enum.Enum):
4950
ACCEPTED_THIRD_PARTY_LICENCES = 28
5051
PACKAGES_WITH_CHECKED_LICENCE = 29
5152
PROGRAMMING_LANGUAGE = 30
53+
AUTOGENERATE_NEWS_FILE_ON_DEPENDENCY_UPDATE = 31
54+
DEPENDENCY_UPDATE_BRANCH_PATTERN = 32
55+
DEPENDENCY_UPDATE_NEWS_MESSAGE = 33
56+
DEPENDENCY_UPDATE_NEWS_TYPE = 34
5257

5358
@staticmethod
5459
def choices() -> List[str]:
@@ -145,11 +150,16 @@ class StaticConfig(GenericConfig):
145150
LOGGER_FORMAT = "%(levelname)s: %(message)s"
146151
BOT_USERNAME = "Monty Bot"
147152
BOT_EMAIL = "[email protected]"
148-
ORGANISATION = "Arm"
153+
ORGANISATION = "Arm Limited"
149154
ORGANISATION_EMAIL = "[email protected]"
150155
FILE_LICENCE_IDENTIFIER = "Apache-2.0"
151156
COPYRIGHT_START_DATE = 2020
152157
PROGRAMMING_LANGUAGE = "NoOp"
158+
AWS_BUCKET = "Unknown"
159+
AUTOGENERATE_NEWS_FILE_ON_DEPENDENCY_UPDATE = True
160+
DEPENDENCY_UPDATE_NEWS_MESSAGE = "Dependency upgrade: {message}"
161+
DEPENDENCY_UPDATE_NEWS_TYPE = NewsType.bugfix.name
162+
DEPENDENCY_UPDATE_BRANCH_PATTERN = r"^\s*[Dd]ependabot\/.+\/(?P<DEPENDENCY>.+)"
153163
ACCEPTED_THIRD_PARTY_LICENCES = ["Apache-2.0", "BSD*", "JSON", "MIT", "Python-2.0", "PSF-2.0", "MPL-2.0"]
154164
PACKAGES_WITH_CHECKED_LICENCE: List[str] = []
155165

continuous_delivery_scripts/utils/git_helpers.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,8 @@ def is_release_branch(self, branch_name: Optional[str]) -> bool:
189189
True if the branch is used for `release` code; False otherwise
190190
"""
191191
branch_pattern = configuration.get_value(ConfigurationVariable.RELEASE_BRANCH_PATTERN)
192-
if not branch_pattern or not branch_name:
193-
return False
194-
is_release: Optional[Any] = re.search(branch_pattern, str(branch_name))
195-
return True if is_release else False
192+
is_release, _ = self._is_branch_of_type(branch_name, branch_pattern)
193+
return is_release
196194

197195
def fetch(self) -> None:
198196
"""Fetches latest changes."""
@@ -529,11 +527,23 @@ def _get_remote(self) -> Optional[Any]:
529527

530528
def list_files_added_on_current_branch(self) -> List[str]:
531529
"""Returns a list of files changed against master branch."""
532-
master_branch_commit = self.repo.commit(self.get_master_branch())
530+
master_branch = self.get_master_branch()
531+
beta_branch = self.get_beta_branch()
532+
master_branch_commit = self.repo.commit(master_branch)
533+
beta_branch_commit = self.repo.commit(beta_branch)
533534
current_branch_commit = self.repo.commit(self.get_current_branch())
534-
changes = self.get_changes_list(
535-
self.get_branch_point(master_branch_commit, current_branch_commit), current_branch_commit, change_type="a"
536-
)
535+
# Finding the baseline branch to consider
536+
master_branch_point = self.repo.commit(self.get_branch_point(master_branch_commit, current_branch_commit))
537+
beta_branch_point = self.repo.commit(self.get_branch_point(beta_branch_commit, current_branch_commit))
538+
branch_point = master_branch_point
539+
if not master_branch:
540+
branch_point = beta_branch_point
541+
elif beta_branch and master_branch:
542+
if beta_branch_point.committed_datetime > master_branch_point.committed_datetime:
543+
# The branch point off `beta` is more recent than off `master`.
544+
# Hence, the difference between current and `beta` should be considered.
545+
branch_point = beta_branch_point
546+
changes = self.get_changes_list(branch_point, current_branch_commit, change_type="a")
537547
return changes
538548

539549
def is_current_branch_feature(self) -> bool:
@@ -544,6 +554,18 @@ def is_current_branch_feature(self) -> bool:
544554
is_release = self.is_release_branch(current_branch)
545555
return not (is_master or is_beta or is_release)
546556

557+
def is_current_branch_of_type(self, pattern: str) -> (bool, Optional[List[Any]]):
558+
"""Returns boolean indicating whether the current branch follows the pattern and the list of groups if any."""
559+
return self._is_branch_of_type(self.get_current_branch(), pattern)
560+
561+
def _is_branch_of_type(self, branch_name: Optional[str], pattern: Optional[str]) -> (bool, Optional[List[Any]]):
562+
if not pattern:
563+
return False, None
564+
if not branch_name:
565+
return False, None
566+
match = re.search(pattern, str(branch_name))
567+
return True if match else False, match.groups() if match else None
568+
547569
@property
548570
def uncommitted_changes(self) -> List[Path]:
549571
"""Gets list of uncommitted files.

continuous_delivery_scripts/utils/language_specifics_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def get_language_from_file_name(filename: str) -> str:
2020
return Path(filename).resolve().with_suffix("").name
2121

2222

23-
GENERIC_LICENCE_HEADER_TEMPLATE = """Copyright (C) {date} {author}. All rights reserved.
23+
GENERIC_LICENCE_HEADER_TEMPLATE = """Copyright (C) {date} {author} or its affiliates and Contributors. All rights reserved.
2424
SPDX-License-Identifier: {licence_identifier}
2525
"""
2626

0 commit comments

Comments
 (0)