ansys-pre-commit-hooks 0.7.1__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/LICENSE +1 -1
- {ansys_pre_commit_hooks-0.7.1/src/ansys_pre_commit_hooks.egg-info → ansys_pre_commit_hooks-0.8.0}/PKG-INFO +8 -8
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/pyproject.toml +25 -19
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/setup.py +7 -7
- ansys_pre_commit_hooks-0.8.0/src/ansys/pre_commit_hooks/VERSION +1 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/__init__.py +1 -1
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/add_license_headers.py +135 -42
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/assets/LICENSES/Apache-2.0.txt +1 -1
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/assets/LICENSES/MIT.txt +1 -1
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/tech_review.py +3 -3
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/templates/AUTHORS +1 -1
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/templates/LICENSE +1 -1
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0/src/ansys_pre_commit_hooks.egg-info}/PKG-INFO +8 -8
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys_pre_commit_hooks.egg-info/requires.txt +3 -3
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/tests/test_add_license_headers.py +209 -8
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/tests/test_metadata.py +1 -1
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/tests/test_tech_review.py +4 -4
- ansys_pre_commit_hooks-0.7.1/src/ansys/pre_commit_hooks/VERSION +0 -1
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/MANIFEST.in +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/README.rst +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/setup.cfg +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/assets/.reuse/templates/ansys.jinja2 +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/assets/licenses.json +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/templates/CODE_OF_CONDUCT.md +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/templates/CONTRIBUTING.md +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/templates/CONTRIBUTORS.md +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/templates/README.md +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/templates/README.rst +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys/pre_commit_hooks/templates/dependabot.yml +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys_pre_commit_hooks.egg-info/SOURCES.txt +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys_pre_commit_hooks.egg-info/dependency_links.txt +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys_pre_commit_hooks.egg-info/entry_points.txt +0 -0
- {ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/src/ansys_pre_commit_hooks.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2023 - 2026
|
|
3
|
+
Copyright (c) 2023 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
6
|
this software and associated documentation files (the "Software"), to deal in
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ansys-pre-commit-hooks
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Home-page: https://github.com/ansys/pre-commit-hooks
|
|
5
|
-
Author: ANSYS, Inc.
|
|
6
|
-
Author-email: "ANSYS, Inc." <pyansys
|
|
7
|
-
Maintainer: ANSYS, Inc.
|
|
8
|
-
Maintainer-email: "ANSYS, Inc." <pyansys
|
|
5
|
+
Author: Synopsys, Inc. and ANSYS, Inc.
|
|
6
|
+
Author-email: "Synopsys, Inc. and ANSYS, Inc." <pyansys-core@synopsys.com>
|
|
7
|
+
Maintainer: Synopsys, Inc. and ANSYS, Inc.
|
|
8
|
+
Maintainer-email: "Synopsys, Inc. and ANSYS, Inc." <pyansys-core@synopsys.com>
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
Project-URL: Source, https://github.com/ansys/pre-commit-hooks/
|
|
11
11
|
Project-URL: Issues, https://github.com/ansys/pre-commit-hooks/issues
|
|
@@ -24,15 +24,15 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
24
24
|
Requires-Python: >=3.10,<4
|
|
25
25
|
Description-Content-Type: text/x-rst
|
|
26
26
|
License-File: LICENSE
|
|
27
|
-
Requires-Dist: GitPython==3.1.
|
|
27
|
+
Requires-Dist: GitPython==3.1.50
|
|
28
28
|
Requires-Dist: importlib-metadata==9.0.0
|
|
29
29
|
Requires-Dist: Jinja2==3.1.6
|
|
30
30
|
Requires-Dist: reuse==6.2.0
|
|
31
|
-
Requires-Dist: requests==2.
|
|
31
|
+
Requires-Dist: requests==2.34.2
|
|
32
32
|
Requires-Dist: semver==3.0.4
|
|
33
33
|
Requires-Dist: toml==0.10.2
|
|
34
34
|
Provides-Extra: doc
|
|
35
|
-
Requires-Dist: ansys-sphinx-theme[autoapi]==1.
|
|
35
|
+
Requires-Dist: ansys-sphinx-theme[autoapi]==1.8.2; extra == "doc"
|
|
36
36
|
Requires-Dist: numpydoc==1.10.0; extra == "doc"
|
|
37
37
|
Requires-Dist: sphinx==8.2.3; extra == "doc"
|
|
38
38
|
Requires-Dist: sphinx-autodoc-typehints==3.1.0; extra == "doc"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[build-system]
|
|
2
2
|
requires = [
|
|
3
3
|
"setuptools>=65.5.1",
|
|
4
|
-
"GitPython==3.1.
|
|
4
|
+
"GitPython==3.1.50",
|
|
5
5
|
"Jinja2==3.1.6",
|
|
6
6
|
"reuse==6.2.0",
|
|
7
|
-
"requests==2.
|
|
7
|
+
"requests==2.34.2",
|
|
8
8
|
"semver==3.0.4",
|
|
9
9
|
"toml==0.10.2",
|
|
10
10
|
"wheel",
|
|
@@ -17,14 +17,14 @@ dynamic = ["version", "classifiers", "description", "readme", "scripts"]
|
|
|
17
17
|
requires-python = ">=3.10,<4"
|
|
18
18
|
license = "MIT"
|
|
19
19
|
license-files = ["LICENSE"]
|
|
20
|
-
authors = [{ name = "ANSYS, Inc.", email = "pyansys
|
|
21
|
-
maintainers = [{ name = "ANSYS, Inc.", email = "pyansys
|
|
20
|
+
authors = [{ name = "Synopsys, Inc. and ANSYS, Inc.", email = "pyansys-core@synopsys.com" }]
|
|
21
|
+
maintainers = [{ name = "Synopsys, Inc. and ANSYS, Inc.", email = "pyansys-core@synopsys.com" }]
|
|
22
22
|
dependencies = [
|
|
23
|
-
"GitPython==3.1.
|
|
23
|
+
"GitPython==3.1.50",
|
|
24
24
|
"importlib-metadata==9.0.0",
|
|
25
25
|
"Jinja2==3.1.6",
|
|
26
26
|
"reuse==6.2.0",
|
|
27
|
-
"requests==2.
|
|
27
|
+
"requests==2.34.2",
|
|
28
28
|
"semver==3.0.4",
|
|
29
29
|
"toml==0.10.2",
|
|
30
30
|
]
|
|
@@ -47,7 +47,7 @@ classifiers = {file = """
|
|
|
47
47
|
|
|
48
48
|
[project.optional-dependencies]
|
|
49
49
|
doc = [
|
|
50
|
-
"ansys-sphinx-theme[autoapi]==1.
|
|
50
|
+
"ansys-sphinx-theme[autoapi]==1.8.2",
|
|
51
51
|
"numpydoc==1.10.0",
|
|
52
52
|
"sphinx==8.2.3",
|
|
53
53
|
"sphinx-autodoc-typehints==3.1.0",
|
|
@@ -105,13 +105,13 @@ title_format = "`{version} <https://github.com/ansys/pre-commit-hooks/releases/t
|
|
|
105
105
|
issue_format = "`#{issue} <https://github.com/ansys/pre-commit-hooks/pull/{issue}>`_"
|
|
106
106
|
|
|
107
107
|
[[tool.towncrier.type]]
|
|
108
|
-
directory = "
|
|
109
|
-
name = "
|
|
108
|
+
directory = "breaking"
|
|
109
|
+
name = "Breaking"
|
|
110
110
|
showcontent = true
|
|
111
111
|
|
|
112
112
|
[[tool.towncrier.type]]
|
|
113
|
-
directory = "
|
|
114
|
-
name = "
|
|
113
|
+
directory = "added"
|
|
114
|
+
name = "Added"
|
|
115
115
|
showcontent = true
|
|
116
116
|
|
|
117
117
|
[[tool.towncrier.type]]
|
|
@@ -120,26 +120,32 @@ name = "Fixed"
|
|
|
120
120
|
showcontent = true
|
|
121
121
|
|
|
122
122
|
[[tool.towncrier.type]]
|
|
123
|
-
directory = "
|
|
124
|
-
name = "
|
|
123
|
+
directory = "documentation"
|
|
124
|
+
name = "Documentation"
|
|
125
125
|
showcontent = true
|
|
126
126
|
|
|
127
127
|
[[tool.towncrier.type]]
|
|
128
|
-
directory = "
|
|
129
|
-
name = "
|
|
128
|
+
directory = "dependencies"
|
|
129
|
+
name = "Dependencies"
|
|
130
130
|
showcontent = true
|
|
131
131
|
|
|
132
132
|
[[tool.towncrier.type]]
|
|
133
|
-
directory = "
|
|
134
|
-
name = "
|
|
133
|
+
directory = "maintenance"
|
|
134
|
+
name = "Maintenance"
|
|
135
135
|
showcontent = true
|
|
136
136
|
|
|
137
137
|
[[tool.towncrier.type]]
|
|
138
|
-
directory = "
|
|
139
|
-
name = "
|
|
138
|
+
directory = "miscellaneous"
|
|
139
|
+
name = "Miscellaneous"
|
|
140
140
|
showcontent = true
|
|
141
141
|
|
|
142
142
|
[[tool.towncrier.type]]
|
|
143
143
|
directory = "test"
|
|
144
144
|
name = "Test"
|
|
145
145
|
showcontent = true
|
|
146
|
+
|
|
147
|
+
[[tool.towncrier.type]]
|
|
148
|
+
directory = "changed"
|
|
149
|
+
name = "Changed"
|
|
150
|
+
showcontent = true
|
|
151
|
+
|
|
@@ -22,10 +22,10 @@ setup(
|
|
|
22
22
|
description=description,
|
|
23
23
|
long_description=long_description,
|
|
24
24
|
license="MIT",
|
|
25
|
-
author="ANSYS, Inc.",
|
|
26
|
-
author_email="pyansys
|
|
27
|
-
maintainer="ANSYS, Inc.",
|
|
28
|
-
maintainer_email="pyansys
|
|
25
|
+
author="Synopsys, Inc. and ANSYS, Inc.",
|
|
26
|
+
author_email="pyansys-core@synopsys.com",
|
|
27
|
+
maintainer="Synopsys, Inc. and ANSYS, Inc.",
|
|
28
|
+
maintainer_email="pyansys-core@synopsys.com",
|
|
29
29
|
classifiers=[
|
|
30
30
|
"Development Status :: 4 - Beta",
|
|
31
31
|
"Intended Audience :: Science/Research",
|
|
@@ -39,17 +39,17 @@ setup(
|
|
|
39
39
|
url="https://github.com/ansys/pre-commit-hooks",
|
|
40
40
|
python_requires=">=3.10,<4",
|
|
41
41
|
install_requires=[
|
|
42
|
-
"GitPython==3.1.
|
|
42
|
+
"GitPython==3.1.50",
|
|
43
43
|
"importlib-metadata==9.0.0",
|
|
44
44
|
"Jinja2==3.1.6",
|
|
45
45
|
"reuse==6.2.0",
|
|
46
|
-
"requests==2.
|
|
46
|
+
"requests==2.34.2",
|
|
47
47
|
"semver==3.0.4",
|
|
48
48
|
"toml==0.10.2",
|
|
49
49
|
],
|
|
50
50
|
extras_require={
|
|
51
51
|
"doc": [
|
|
52
|
-
"ansys-sphinx-theme[autoapi]==1.
|
|
52
|
+
"ansys-sphinx-theme[autoapi]==1.8.2",
|
|
53
53
|
"numpydoc==1.10.0",
|
|
54
54
|
"sphinx==8.2.3",
|
|
55
55
|
"sphinx-autodoc-typehints==3.1.0",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.8.0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (C) 2023 - 2026
|
|
1
|
+
# Copyright (C) 2023 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
#
|
|
4
4
|
#
|
|
@@ -29,6 +29,8 @@ A license header consists of the Ansys copyright statement and licensing informa
|
|
|
29
29
|
import argparse
|
|
30
30
|
from datetime import date as dt
|
|
31
31
|
import filecmp
|
|
32
|
+
import io
|
|
33
|
+
from logging import Logger
|
|
32
34
|
from pathlib import Path
|
|
33
35
|
import re
|
|
34
36
|
import shutil
|
|
@@ -36,16 +38,76 @@ import sys
|
|
|
36
38
|
from tempfile import NamedTemporaryFile
|
|
37
39
|
from typing import IO, Union
|
|
38
40
|
|
|
41
|
+
logger = Logger(__name__)
|
|
42
|
+
|
|
39
43
|
DEFAULT_TEMPLATE = "ansys"
|
|
40
44
|
"""Default template to use for license headers."""
|
|
41
|
-
DEFAULT_COPYRIGHT = "
|
|
45
|
+
DEFAULT_COPYRIGHT = "Synopsys, Inc. and ANSYS, Inc. All rights reserved."
|
|
42
46
|
"""Default copyright line for license headers."""
|
|
43
47
|
DEFAULT_LICENSE = "MIT"
|
|
44
48
|
"""Default license for headers."""
|
|
45
49
|
DEFAULT_START_YEAR = dt.today().year
|
|
46
|
-
"""
|
|
50
|
+
"""Fallback start year used when git history is unavailable."""
|
|
47
51
|
YEAR_REGEX = r"(\d{4}) - (\d{4})|\d{4}"
|
|
48
52
|
"""Year regex to match year or year range in files."""
|
|
53
|
+
HEADER_PEEK_BYTES = 1024
|
|
54
|
+
"""Number of bytes read from the top of a file to check for an existing license header.
|
|
55
|
+
|
|
56
|
+
License headers always appear at the very start of a file and are at most a few hundred
|
|
57
|
+
bytes, so reading only this many bytes avoids loading the full file for every checked file."""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_start_year_from_git(git_repo) -> int:
|
|
61
|
+
"""Return the year of the first (oldest) commit in the git repository.
|
|
62
|
+
|
|
63
|
+
Uses ``git log --reverse`` to find the oldest commit and extracts its year.
|
|
64
|
+
When the repository is a shallow clone (e.g. ``fetch-depth: 1`` in CI/CD),
|
|
65
|
+
attempts ``git fetch --unshallow`` to retrieve the full history before
|
|
66
|
+
reading the first commit year. If unshallowing fails (no network access,
|
|
67
|
+
no remote configured, etc.) the function continues with the limited history
|
|
68
|
+
that is locally available.
|
|
69
|
+
Falls back to :data:`DEFAULT_START_YEAR` (the current year) when the
|
|
70
|
+
repository has no commits yet.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
git_repo: git.Repo
|
|
75
|
+
The git repository object.
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
int
|
|
80
|
+
The four-digit year of the first commit, or the current year if the
|
|
81
|
+
repository has no commits.
|
|
82
|
+
"""
|
|
83
|
+
# Detect shallow clones produced by CI/CD checkouts with fetch-depth: 1.
|
|
84
|
+
# In that case git log --reverse only sees the single fetched commit, not
|
|
85
|
+
# the true first commit in history.
|
|
86
|
+
try:
|
|
87
|
+
is_shallow = git_repo.git.rev_parse("--is-shallow-repository").strip() == "true"
|
|
88
|
+
except Exception:
|
|
89
|
+
# Older git versions (<2.15) do not support --is-shallow-repository;
|
|
90
|
+
# fall back to checking for the presence of the .git/shallow file.
|
|
91
|
+
is_shallow = (Path(git_repo.git_dir) / "shallow").is_file()
|
|
92
|
+
|
|
93
|
+
if is_shallow:
|
|
94
|
+
# Attempt to retrieve the full history so the real first commit is visible.
|
|
95
|
+
try:
|
|
96
|
+
git_repo.git.fetch("--unshallow")
|
|
97
|
+
except Exception as e:
|
|
98
|
+
# Unshallow can fail when there is no network, no remote is configured,
|
|
99
|
+
# or the server does not support it. Continue with whatever history is
|
|
100
|
+
# available locally.
|
|
101
|
+
logger.debug(f"Failure during unshallow: {e}")
|
|
102
|
+
logger.debug("Continuing with limited history available in shallow clone.")
|
|
103
|
+
|
|
104
|
+
# --reverse makes the oldest commit appear first; %ad is the author date
|
|
105
|
+
first_year_str = (
|
|
106
|
+
git_repo.git.log("--reverse", "--format=%ad", "--date=format:%Y").split("\n")[0].strip()
|
|
107
|
+
)
|
|
108
|
+
if first_year_str and first_year_str.isdigit():
|
|
109
|
+
return int(first_year_str)
|
|
110
|
+
return DEFAULT_START_YEAR
|
|
49
111
|
|
|
50
112
|
|
|
51
113
|
def set_lint_args(parser: argparse.ArgumentParser) -> argparse.Namespace:
|
|
@@ -89,8 +151,11 @@ def set_lint_args(parser: argparse.ArgumentParser) -> argparse.Namespace:
|
|
|
89
151
|
parser.add_argument(
|
|
90
152
|
"--start_year",
|
|
91
153
|
type=str,
|
|
92
|
-
help=
|
|
93
|
-
|
|
154
|
+
help=(
|
|
155
|
+
"Start year for copyright line in headers. "
|
|
156
|
+
"When omitted the year of the first git commit is used automatically."
|
|
157
|
+
),
|
|
158
|
+
default=None,
|
|
94
159
|
)
|
|
95
160
|
# Ignore license check by default is False when action='store_true'
|
|
96
161
|
parser.add_argument("--ignore_license_check", action="store_true")
|
|
@@ -201,22 +266,31 @@ def update_license_file(
|
|
|
201
266
|
template_parent_dir = hook_loc / "assets" / "LICENSES"
|
|
202
267
|
generate_license_file(template_parent_dir, year_span, repo_license_path, license)
|
|
203
268
|
else:
|
|
204
|
-
# The year regex to match the year or year range in the LICENSE file
|
|
205
|
-
year_regex = r"(\d{4}) - (\d{4})|\d{4}"
|
|
206
|
-
|
|
207
269
|
content = existing_content
|
|
208
|
-
#
|
|
209
|
-
|
|
210
|
-
#
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
270
|
+
# Match the year (or year range) specifically in the Copyright line.
|
|
271
|
+
# Searching for the first year in the file is incorrect for licenses like
|
|
272
|
+
# Apache-2.0 whose boilerplate contains "January 2004" before the copyright
|
|
273
|
+
# notice, which would otherwise be overwritten with the current year.
|
|
274
|
+
copyright_year_regex = r"(Copyright(?:\s+\(c\))?\s+)((?:\d{4}\s+-\s+)?\d{4})"
|
|
275
|
+
year_range_match = re.search(copyright_year_regex, content)
|
|
276
|
+
|
|
277
|
+
if year_range_match:
|
|
278
|
+
# Get the group from the year_range_match
|
|
279
|
+
year_range = year_range_match.group(2)
|
|
280
|
+
|
|
281
|
+
# Replace the current year span with the updated year span
|
|
282
|
+
if year_range != year_span:
|
|
283
|
+
# Update the year span in the LICENSE file. "1" is the max number of replacements
|
|
284
|
+
content = re.sub(copyright_year_regex, rf"\g<1>{year_span}", content, 1)
|
|
285
|
+
|
|
286
|
+
with repo_license_path.open(encoding="utf-8", newline="", mode="w") as file:
|
|
287
|
+
file.write(content)
|
|
288
|
+
else:
|
|
289
|
+
# No year found in the Copyright line (e.g. "Copyright [yyyy]" placeholder
|
|
290
|
+
# in a stock Apache-2.0 LICENSE). Regenerate the file from the template.
|
|
291
|
+
hook_loc = Path(__file__).parent.resolve()
|
|
292
|
+
template_parent_dir = hook_loc / "assets" / "LICENSES"
|
|
293
|
+
generate_license_file(template_parent_dir, year_span, repo_license_path, license)
|
|
220
294
|
|
|
221
295
|
# If the file changed, print a message that the LICENSE file was changed
|
|
222
296
|
if not check_same_content(temp_file, repo_license_path):
|
|
@@ -361,7 +435,8 @@ def _has_current_header(file_path: str, copyright: list, years: str) -> bool:
|
|
|
361
435
|
file_path: str
|
|
362
436
|
Path to the file to check.
|
|
363
437
|
copyright: list
|
|
364
|
-
List containing the copyright string. For example,
|
|
438
|
+
List containing the copyright string. For example,
|
|
439
|
+
["Synopsys, Inc. and ANSYS, Inc. All rights reserved."].
|
|
365
440
|
years: str
|
|
366
441
|
The expected year span. For example, "2023 - 2026" or "2026".
|
|
367
442
|
|
|
@@ -373,7 +448,7 @@ def _has_current_header(file_path: str, copyright: list, years: str) -> bool:
|
|
|
373
448
|
"""
|
|
374
449
|
try:
|
|
375
450
|
with Path(file_path).open(encoding="utf-8", errors="ignore") as f:
|
|
376
|
-
head = f.read(
|
|
451
|
+
head = f.read(HEADER_PEEK_BYTES)
|
|
377
452
|
except OSError:
|
|
378
453
|
return False
|
|
379
454
|
|
|
@@ -400,7 +475,7 @@ def _file_has_license(file_path: str, license: str) -> bool:
|
|
|
400
475
|
"""
|
|
401
476
|
try:
|
|
402
477
|
with Path(file_path).open(encoding="utf-8", errors="ignore") as f:
|
|
403
|
-
head = f.read(
|
|
478
|
+
head = f.read(HEADER_PEEK_BYTES)
|
|
404
479
|
except OSError:
|
|
405
480
|
return False
|
|
406
481
|
|
|
@@ -501,13 +576,20 @@ def non_recursive_file_check(
|
|
|
501
576
|
add_header(copyright, license, years, file, template, commented, sys.stdout)
|
|
502
577
|
else:
|
|
503
578
|
# Check whether the existing SPDX-License-Identifier differs from the
|
|
504
|
-
# requested license
|
|
505
|
-
#
|
|
506
|
-
|
|
579
|
+
# requested license, or whether the copyright holder phrase has changed.
|
|
580
|
+
# In either case, strip the old header and write a fresh one so that the
|
|
581
|
+
# old lines are fully replaced rather than merged by REUSE's annotate.
|
|
582
|
+
try:
|
|
583
|
+
with Path(file).open(encoding="utf-8", errors="ignore") as _f:
|
|
584
|
+
_head = _f.read(HEADER_PEEK_BYTES)
|
|
585
|
+
except OSError:
|
|
586
|
+
_head = ""
|
|
587
|
+
copyright_holder_changed = copyright[0] not in _head
|
|
588
|
+
if not existing_license_matches or copyright_holder_changed:
|
|
507
589
|
before_hook = NamedTemporaryFile(mode="w", delete=False).name
|
|
508
590
|
shutil.copyfile(file, before_hook)
|
|
509
591
|
_strip_reuse_header(file)
|
|
510
|
-
add_header(copyright, license, years, file, template, commented,
|
|
592
|
+
add_header(copyright, license, years, file, template, commented, io.StringIO())
|
|
511
593
|
if not check_same_content(before_hook, file):
|
|
512
594
|
changed_headers = 1
|
|
513
595
|
print(f"Successfully changed header of {file}")
|
|
@@ -575,7 +657,8 @@ def update_header(
|
|
|
575
657
|
file: str
|
|
576
658
|
The file whose header is being updated.
|
|
577
659
|
copyright: str
|
|
578
|
-
The copyright string of the header. For example,
|
|
660
|
+
The copyright string of the header. For example,
|
|
661
|
+
"Synopsys, Inc. and ANSYS, Inc. All rights reserved."
|
|
579
662
|
license: str
|
|
580
663
|
The license of the header. For example, "MIT".
|
|
581
664
|
years: str
|
|
@@ -662,7 +745,7 @@ def add_header(
|
|
|
662
745
|
----------
|
|
663
746
|
copyright: str
|
|
664
747
|
The copyright line for the license header. For example,
|
|
665
|
-
"
|
|
748
|
+
"Synopsys, Inc. and ANSYS, Inc. All rights reserved."
|
|
666
749
|
license: str
|
|
667
750
|
The license for the license header. For example, "MIT".
|
|
668
751
|
years: str
|
|
@@ -673,7 +756,7 @@ def add_header(
|
|
|
673
756
|
The template to use for the license header. For example, "ansys.jinja2".
|
|
674
757
|
commented: bool
|
|
675
758
|
Whether the template is commented or not.
|
|
676
|
-
|
|
759
|
+
out: Union[NamedTemporaryFile, IO[str]]
|
|
677
760
|
Temporary file to capture the stdout of the add_header_to_file() function or ``sys.stdout``.
|
|
678
761
|
"""
|
|
679
762
|
from reuse.cli.annotate import add_header_to_file, get_comment_style, get_reuse_info
|
|
@@ -932,17 +1015,6 @@ def main():
|
|
|
932
1015
|
# Set changed_headers to zero by default
|
|
933
1016
|
changed_headers = 0
|
|
934
1017
|
|
|
935
|
-
# Check start_year is valid
|
|
936
|
-
if str(args.start_year).isdigit():
|
|
937
|
-
# Check the start year is not later than the current year
|
|
938
|
-
if int(args.start_year) > dt.today().year:
|
|
939
|
-
raise Exception("Please provide a start year less than or equal to the current year.")
|
|
940
|
-
# Check the start year isn't earlier than when computers were created :)
|
|
941
|
-
elif int(args.start_year) < 1942:
|
|
942
|
-
raise Exception("Please provide a start year greater than or equal to 1942.")
|
|
943
|
-
else:
|
|
944
|
-
raise Exception("Please ensure the start year is a number.")
|
|
945
|
-
|
|
946
1018
|
import git
|
|
947
1019
|
|
|
948
1020
|
# Get root directory of the git repository.
|
|
@@ -950,6 +1022,27 @@ def main():
|
|
|
950
1022
|
# Get the root of the git repository and fix the line separators
|
|
951
1023
|
git_root = Path(git_repo.git.rev_parse("--show-toplevel"))
|
|
952
1024
|
|
|
1025
|
+
# Determine the start year.
|
|
1026
|
+
# When --start_year is explicitly provided, validate and use that value.
|
|
1027
|
+
# Otherwise, auto-detect from the year of the first git commit so that the
|
|
1028
|
+
# original copyright year is never accidentally erased.
|
|
1029
|
+
if args.start_year is not None:
|
|
1030
|
+
if str(args.start_year).isdigit():
|
|
1031
|
+
# Check the start year is not later than the current year
|
|
1032
|
+
if int(args.start_year) > dt.today().year:
|
|
1033
|
+
raise Exception(
|
|
1034
|
+
"Please provide a start year less than or equal to the current year."
|
|
1035
|
+
)
|
|
1036
|
+
# Check the start year isn't earlier than when computers were created :)
|
|
1037
|
+
elif int(args.start_year) < 1942:
|
|
1038
|
+
raise Exception("Please provide a start year greater than or equal to 1942.")
|
|
1039
|
+
else:
|
|
1040
|
+
raise Exception("Please ensure the start year is a number.")
|
|
1041
|
+
start_year = int(args.start_year)
|
|
1042
|
+
else:
|
|
1043
|
+
# Auto-detect: use the year of the repository's first commit
|
|
1044
|
+
start_year = get_start_year_from_git(git_repo)
|
|
1045
|
+
|
|
953
1046
|
# Create dictionary containing the committed files, custom copyright,
|
|
954
1047
|
# template, license, changed_headers, year, and git_repo
|
|
955
1048
|
values = {
|
|
@@ -957,7 +1050,7 @@ def main():
|
|
|
957
1050
|
"copyright": args.custom_copyright,
|
|
958
1051
|
"template": args.custom_template,
|
|
959
1052
|
"license": args.custom_license,
|
|
960
|
-
"start_year":
|
|
1053
|
+
"start_year": start_year,
|
|
961
1054
|
"current_year": dt.today().year,
|
|
962
1055
|
"git_repo": git_repo,
|
|
963
1056
|
}
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
same "printed page" as the copyright notice for easier
|
|
188
188
|
identification within third-party archives.
|
|
189
189
|
|
|
190
|
-
Copyright {{ year_span }}
|
|
190
|
+
Copyright {{ year_span }} Synopsys, Inc. and ANSYS, Inc. All rights reserved.
|
|
191
191
|
|
|
192
192
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
193
|
you may not use this file except in compliance with the License.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) {{ year_span }}
|
|
3
|
+
Copyright (c) {{ year_span }} Synopsys, Inc. and ANSYS, Inc. All rights reserved.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
6
|
this software and associated documentation files (the "Software"), to deal in
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (C) 2023 - 2026
|
|
1
|
+
# Copyright (C) 2023 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
#
|
|
4
4
|
#
|
|
@@ -37,10 +37,10 @@ HOOK_PATH = pathlib.Path(__file__).parent.resolve()
|
|
|
37
37
|
LICENSES_JSON = HOOK_PATH / "assets" / "licenses.json"
|
|
38
38
|
"""JSON file containing licenses information."""
|
|
39
39
|
|
|
40
|
-
DEFAULT_AUTHOR_MAINT_NAME = "ANSYS, Inc."
|
|
40
|
+
DEFAULT_AUTHOR_MAINT_NAME = "Synopsys, Inc. and ANSYS, Inc."
|
|
41
41
|
"""Default name of project authors and maintainers."""
|
|
42
42
|
|
|
43
|
-
DEFAULT_AUTHOR_MAINT_EMAIL = "pyansys
|
|
43
|
+
DEFAULT_AUTHOR_MAINT_EMAIL = "pyansys-core@synopsys.com"
|
|
44
44
|
"""Default email of project authors and maintainers."""
|
|
45
45
|
|
|
46
46
|
DEFAULT_START_YEAR = dt.today().year
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) {{ year_span }}
|
|
3
|
+
Copyright (c) {{ year_span }} Synopsys, Inc. and ANSYS, Inc. All rights reserved.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
6
|
this software and associated documentation files (the "Software"), to deal in
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ansys-pre-commit-hooks
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Home-page: https://github.com/ansys/pre-commit-hooks
|
|
5
|
-
Author: ANSYS, Inc.
|
|
6
|
-
Author-email: "ANSYS, Inc." <pyansys
|
|
7
|
-
Maintainer: ANSYS, Inc.
|
|
8
|
-
Maintainer-email: "ANSYS, Inc." <pyansys
|
|
5
|
+
Author: Synopsys, Inc. and ANSYS, Inc.
|
|
6
|
+
Author-email: "Synopsys, Inc. and ANSYS, Inc." <pyansys-core@synopsys.com>
|
|
7
|
+
Maintainer: Synopsys, Inc. and ANSYS, Inc.
|
|
8
|
+
Maintainer-email: "Synopsys, Inc. and ANSYS, Inc." <pyansys-core@synopsys.com>
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
Project-URL: Source, https://github.com/ansys/pre-commit-hooks/
|
|
11
11
|
Project-URL: Issues, https://github.com/ansys/pre-commit-hooks/issues
|
|
@@ -24,15 +24,15 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
24
24
|
Requires-Python: >=3.10,<4
|
|
25
25
|
Description-Content-Type: text/x-rst
|
|
26
26
|
License-File: LICENSE
|
|
27
|
-
Requires-Dist: GitPython==3.1.
|
|
27
|
+
Requires-Dist: GitPython==3.1.50
|
|
28
28
|
Requires-Dist: importlib-metadata==9.0.0
|
|
29
29
|
Requires-Dist: Jinja2==3.1.6
|
|
30
30
|
Requires-Dist: reuse==6.2.0
|
|
31
|
-
Requires-Dist: requests==2.
|
|
31
|
+
Requires-Dist: requests==2.34.2
|
|
32
32
|
Requires-Dist: semver==3.0.4
|
|
33
33
|
Requires-Dist: toml==0.10.2
|
|
34
34
|
Provides-Extra: doc
|
|
35
|
-
Requires-Dist: ansys-sphinx-theme[autoapi]==1.
|
|
35
|
+
Requires-Dist: ansys-sphinx-theme[autoapi]==1.8.2; extra == "doc"
|
|
36
36
|
Requires-Dist: numpydoc==1.10.0; extra == "doc"
|
|
37
37
|
Requires-Dist: sphinx==8.2.3; extra == "doc"
|
|
38
38
|
Requires-Dist: sphinx-autodoc-typehints==3.1.0; extra == "doc"
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
GitPython==3.1.
|
|
1
|
+
GitPython==3.1.50
|
|
2
2
|
importlib-metadata==9.0.0
|
|
3
3
|
Jinja2==3.1.6
|
|
4
4
|
reuse==6.2.0
|
|
5
|
-
requests==2.
|
|
5
|
+
requests==2.34.2
|
|
6
6
|
semver==3.0.4
|
|
7
7
|
toml==0.10.2
|
|
8
8
|
|
|
9
9
|
[doc]
|
|
10
|
-
ansys-sphinx-theme[autoapi]==1.
|
|
10
|
+
ansys-sphinx-theme[autoapi]==1.8.2
|
|
11
11
|
numpydoc==1.10.0
|
|
12
12
|
sphinx==8.2.3
|
|
13
13
|
sphinx-autodoc-typehints==3.1.0
|
{ansys_pre_commit_hooks-0.7.1 → ansys_pre_commit_hooks-0.8.0}/tests/test_add_license_headers.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (C) 2023 - 2026
|
|
1
|
+
# Copyright (C) 2023 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
#
|
|
4
4
|
#
|
|
@@ -37,7 +37,7 @@ import ansys.pre_commit_hooks.add_license_headers as hook
|
|
|
37
37
|
git_repo = git.Repo(Path.cwd(), search_parent_directories=True)
|
|
38
38
|
REPO_PATH = git_repo.git.rev_parse("--show-toplevel")
|
|
39
39
|
START_YEAR = "2023"
|
|
40
|
-
DEFAULT_COPYRIGHT = "
|
|
40
|
+
DEFAULT_COPYRIGHT = "Synopsys, Inc. and ANSYS, Inc. All rights reserved."
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def set_up_repo(tmp_path, template_path, template_name, license_path, license_name):
|
|
@@ -129,7 +129,7 @@ def check_ansys_header(file_name):
|
|
|
129
129
|
for line in file:
|
|
130
130
|
count += 1
|
|
131
131
|
if count == 1:
|
|
132
|
-
assert "
|
|
132
|
+
assert "Synopsys, Inc. and ANSYS, Inc." in line
|
|
133
133
|
if count == 2:
|
|
134
134
|
assert "MIT" in line
|
|
135
135
|
if count == 5:
|
|
@@ -189,7 +189,7 @@ def test_start_year_same_as_current(tmp_path: pytest.TempPathFactory):
|
|
|
189
189
|
count += 1
|
|
190
190
|
# Assert the copyright line's time range is from 2023 to the current year
|
|
191
191
|
if count == 1:
|
|
192
|
-
assert f"Copyright (C) {dt.today().year} ANSYS, Inc." in line
|
|
192
|
+
assert f"Copyright (C) {dt.today().year} Synopsys, Inc. and ANSYS, Inc." in line
|
|
193
193
|
if count > 1:
|
|
194
194
|
break
|
|
195
195
|
|
|
@@ -356,7 +356,7 @@ def test_no_license_check(tmp_path: pytest.TempPathFactory):
|
|
|
356
356
|
count += 1
|
|
357
357
|
# Assert that only the copyright line is in the file
|
|
358
358
|
if count == 1:
|
|
359
|
-
assert "
|
|
359
|
+
assert "Synopsys, Inc. and ANSYS, Inc." in line
|
|
360
360
|
if count == 2:
|
|
361
361
|
assert "MIT" not in line
|
|
362
362
|
if count == 5:
|
|
@@ -735,6 +735,81 @@ def test_license_year_update(tmp_path: pytest.TempPathFactory):
|
|
|
735
735
|
os.chdir(REPO_PATH)
|
|
736
736
|
|
|
737
737
|
|
|
738
|
+
@pytest.mark.add_license_headers
|
|
739
|
+
def test_get_start_year_from_git_new_repo(tmp_path: pytest.TempPathFactory):
|
|
740
|
+
"""Test that get_start_year_from_git returns the current year for a brand-new repo."""
|
|
741
|
+
os.chdir(tmp_path)
|
|
742
|
+
repo = init_repo(tmp_path)
|
|
743
|
+
year = hook.get_start_year_from_git(repo)
|
|
744
|
+
assert year == dt.today().year
|
|
745
|
+
os.chdir(REPO_PATH)
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
@pytest.mark.add_license_headers
|
|
749
|
+
def test_get_start_year_from_git_detects_first_commit_year(tmp_path: pytest.TempPathFactory):
|
|
750
|
+
"""Test that get_start_year_from_git returns the year of the *first* commit.
|
|
751
|
+
|
|
752
|
+
Creates a repo with a backdated initial commit so the result is deterministic
|
|
753
|
+
regardless of when the test suite is run.
|
|
754
|
+
"""
|
|
755
|
+
from datetime import datetime
|
|
756
|
+
|
|
757
|
+
os.chdir(tmp_path)
|
|
758
|
+
git.Repo.init(tmp_path)
|
|
759
|
+
repo = git.Repo(tmp_path)
|
|
760
|
+
|
|
761
|
+
# Back-dated initial commit — gitpython requires timezone-aware datetimes
|
|
762
|
+
from datetime import timezone
|
|
763
|
+
|
|
764
|
+
old_date = datetime(2020, 6, 15, tzinfo=timezone.utc)
|
|
765
|
+
readme = tmp_path / "README.md"
|
|
766
|
+
readme.write_text("initial")
|
|
767
|
+
repo.index.add([str(readme)])
|
|
768
|
+
repo.index.commit("initial commit", author_date=old_date, commit_date=old_date)
|
|
769
|
+
|
|
770
|
+
# A more recent commit — must not affect the result
|
|
771
|
+
readme.write_text("updated")
|
|
772
|
+
repo.index.add([str(readme)])
|
|
773
|
+
repo.index.commit("recent commit")
|
|
774
|
+
|
|
775
|
+
year = hook.get_start_year_from_git(repo)
|
|
776
|
+
assert year == 2020
|
|
777
|
+
|
|
778
|
+
os.chdir(REPO_PATH)
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
@pytest.mark.add_license_headers
|
|
782
|
+
def test_start_year_autodetected_from_git(tmp_path: pytest.TempPathFactory):
|
|
783
|
+
"""End-to-end test: when --start_year is omitted the hook uses the first commit year.
|
|
784
|
+
|
|
785
|
+
A fresh repo is initialised so the first commit year equals the current year.
|
|
786
|
+
Running the hook without ``--start_year`` should therefore produce a copyright
|
|
787
|
+
line that contains only the current year (no range).
|
|
788
|
+
"""
|
|
789
|
+
template_name = "ansys.jinja2"
|
|
790
|
+
license_name = "MIT.txt"
|
|
791
|
+
template_path = Path(REPO_PATH) / ".reuse" / "templates" / template_name
|
|
792
|
+
license_path = Path(REPO_PATH) / "LICENSES" / license_name
|
|
793
|
+
|
|
794
|
+
os.chdir(tmp_path)
|
|
795
|
+
repo, tmp_file = set_up_repo(tmp_path, template_path, template_name, license_path, license_name)
|
|
796
|
+
|
|
797
|
+
# Run the hook with NO --start_year argument
|
|
798
|
+
assert add_argv_run(repo, tmp_file, [tmp_file]) == 1
|
|
799
|
+
|
|
800
|
+
with open(tmp_file, "r") as f:
|
|
801
|
+
first_line = f.readline()
|
|
802
|
+
|
|
803
|
+
# New repo: first commit is in the current year, so the copyright should
|
|
804
|
+
# contain only the current year (no multi-year range).
|
|
805
|
+
assert (
|
|
806
|
+
f"Copyright (C) {dt.today().year} Synopsys, Inc. and ANSYS, Inc. All rights reserved."
|
|
807
|
+
in first_line
|
|
808
|
+
)
|
|
809
|
+
|
|
810
|
+
os.chdir(REPO_PATH)
|
|
811
|
+
|
|
812
|
+
|
|
738
813
|
@pytest.mark.add_license_headers
|
|
739
814
|
def test_date_update(tmp_path: pytest.TempPathFactory):
|
|
740
815
|
"""Test the date is correctly updated in the license header."""
|
|
@@ -755,11 +830,17 @@ def test_date_update(tmp_path: pytest.TempPathFactory):
|
|
|
755
830
|
years = ["2022", "2023", str(dt.today().year)]
|
|
756
831
|
|
|
757
832
|
# Check the copyright line has "2023 - {current_year}", "2022 - {current_year}"
|
|
758
|
-
# and "{current_year}"
|
|
833
|
+
# and "{current_year}" (contained within the preserved range)
|
|
759
834
|
for year in years:
|
|
760
835
|
custom_args = [tmp_file, f"--start_year={year}"]
|
|
761
836
|
# Git add tmp_file and run hook with custom arguments
|
|
762
|
-
|
|
837
|
+
result = add_argv_run(repo, tmp_file, custom_args)
|
|
838
|
+
# When the start year differs from the current year the hook must update the
|
|
839
|
+
# header (return code 1). When start_year equals the current year and files
|
|
840
|
+
# already have the current year as their end year (set during earlier
|
|
841
|
+
# iterations), the hook preserves the existing range and returns 0.
|
|
842
|
+
if str(year) != str(dt.today().year):
|
|
843
|
+
assert result == 1
|
|
763
844
|
# Check the license year is correctly updated
|
|
764
845
|
check_license_year(tmp_file, DEFAULT_COPYRIGHT, year, str(dt.today().year))
|
|
765
846
|
# Add file with updated header
|
|
@@ -868,7 +949,7 @@ def check_apache_header(file_name):
|
|
|
868
949
|
"""Check file contains all Apache-2.0 copyright and license header components."""
|
|
869
950
|
with open(file_name, "r", encoding="utf8") as file:
|
|
870
951
|
content = file.read()
|
|
871
|
-
assert "
|
|
952
|
+
assert "Synopsys, Inc. and ANSYS, Inc." in content
|
|
872
953
|
assert "Apache-2.0" in content
|
|
873
954
|
assert "Apache License, Version 2.0" in content
|
|
874
955
|
assert "http://www.apache.org/licenses/LICENSE-2.0" in content
|
|
@@ -950,6 +1031,46 @@ def test_mit_header_replaced_with_apache(tmp_path: pytest.TempPathFactory):
|
|
|
950
1031
|
os.chdir(REPO_PATH)
|
|
951
1032
|
|
|
952
1033
|
|
|
1034
|
+
@pytest.mark.add_license_headers
|
|
1035
|
+
def test_no_duplicate_prints_on_license_switch(
|
|
1036
|
+
tmp_path: pytest.TempPathFactory, capsys: pytest.CaptureFixture
|
|
1037
|
+
):
|
|
1038
|
+
"""Test that switching from MIT to Apache-2.0 prints each changed file exactly once."""
|
|
1039
|
+
template_name = "ansys.jinja2"
|
|
1040
|
+
license_name = "Apache-2.0.txt"
|
|
1041
|
+
template_path = Path(REPO_PATH) / ".reuse" / "templates" / template_name
|
|
1042
|
+
license_path = (
|
|
1043
|
+
Path(REPO_PATH)
|
|
1044
|
+
/ "src"
|
|
1045
|
+
/ "ansys"
|
|
1046
|
+
/ "pre_commit_hooks"
|
|
1047
|
+
/ "assets"
|
|
1048
|
+
/ "LICENSES"
|
|
1049
|
+
/ license_name
|
|
1050
|
+
)
|
|
1051
|
+
|
|
1052
|
+
repo, tmp_file = set_up_repo(tmp_path, template_path, template_name, license_path, license_name)
|
|
1053
|
+
|
|
1054
|
+
# First run: add the MIT header
|
|
1055
|
+
add_argv_run(repo, tmp_file, [tmp_file])
|
|
1056
|
+
repo.index.add([tmp_file])
|
|
1057
|
+
capsys.readouterr() # discard output from the MIT-header run
|
|
1058
|
+
|
|
1059
|
+
# Second run: switch to Apache-2.0 — triggers the license-switch branch
|
|
1060
|
+
add_argv_run(repo, tmp_file, [tmp_file, "--custom_license=Apache-2.0"])
|
|
1061
|
+
|
|
1062
|
+
captured = capsys.readouterr()
|
|
1063
|
+
file_lines = [
|
|
1064
|
+
line for line in captured.out.splitlines() if "Successfully changed header" in line
|
|
1065
|
+
]
|
|
1066
|
+
assert len(file_lines) == 1, (
|
|
1067
|
+
f"Expected 'Successfully changed header' to appear exactly once, "
|
|
1068
|
+
f"but got {len(file_lines)} time(s):\n{captured.out}"
|
|
1069
|
+
)
|
|
1070
|
+
|
|
1071
|
+
os.chdir(REPO_PATH)
|
|
1072
|
+
|
|
1073
|
+
|
|
953
1074
|
@pytest.mark.add_license_headers
|
|
954
1075
|
def test_mit_license_file_replaced_with_apache(tmp_path: pytest.TempPathFactory):
|
|
955
1076
|
"""Test that LICENSE file is regenerated as Apache-2.0 when it's the custom license."""
|
|
@@ -991,6 +1112,34 @@ def test_mit_license_file_replaced_with_apache(tmp_path: pytest.TempPathFactory)
|
|
|
991
1112
|
os.chdir(REPO_PATH)
|
|
992
1113
|
|
|
993
1114
|
|
|
1115
|
+
@pytest.mark.add_license_headers
|
|
1116
|
+
def test_apache_license_year_update_preserves_boilerplate(tmp_path):
|
|
1117
|
+
"""Test that updating the year in an Apache-2.0 LICENSE does not corrupt the boilerplate.
|
|
1118
|
+
|
|
1119
|
+
Regression test: the 'January 2004' text in the Apache boilerplate header must not
|
|
1120
|
+
be replaced with the current year when the copyright year is updated.
|
|
1121
|
+
"""
|
|
1122
|
+
hook_loc = Path(REPO_PATH) / "src" / "ansys" / "pre_commit_hooks"
|
|
1123
|
+
template_dir = hook_loc / "assets" / "LICENSES"
|
|
1124
|
+
license_file = tmp_path / "LICENSE"
|
|
1125
|
+
|
|
1126
|
+
# Generate an Apache-2.0 LICENSE file with a fixed start year
|
|
1127
|
+
hook.generate_license_file(template_dir, "2023", license_file, "Apache-2.0")
|
|
1128
|
+
|
|
1129
|
+
initial_content = license_file.read_text(encoding="utf-8")
|
|
1130
|
+
assert "January 2004" in initial_content
|
|
1131
|
+
assert "Copyright 2023 Synopsys, Inc. and ANSYS, Inc." in initial_content
|
|
1132
|
+
|
|
1133
|
+
# Simulate a year-span update (e.g., a new calendar year)
|
|
1134
|
+
hook.update_license_file(license_file, "2023 - 2026", "Apache-2.0")
|
|
1135
|
+
|
|
1136
|
+
updated_content = license_file.read_text(encoding="utf-8")
|
|
1137
|
+
assert (
|
|
1138
|
+
"January 2004" in updated_content
|
|
1139
|
+
), "The Apache boilerplate 'January 2004' must not be overwritten by the year update"
|
|
1140
|
+
assert "Copyright 2023 - 2026 Synopsys, Inc. and ANSYS, Inc." in updated_content
|
|
1141
|
+
|
|
1142
|
+
|
|
994
1143
|
@pytest.mark.add_license_headers
|
|
995
1144
|
def test_line_endings(tmp_path: pytest.TempPathFactory):
|
|
996
1145
|
"""Test line endings remain the same before and after running the hook."""
|
|
@@ -1023,3 +1172,55 @@ def test_line_endings(tmp_path: pytest.TempPathFactory):
|
|
|
1023
1172
|
assert license_line_endings_before == get_line_endings(tmp_license)
|
|
1024
1173
|
|
|
1025
1174
|
os.chdir(REPO_PATH)
|
|
1175
|
+
|
|
1176
|
+
|
|
1177
|
+
@pytest.mark.add_license_headers
|
|
1178
|
+
def test_copyright_holder_change_replaces_not_duplicates(tmp_path: pytest.TempPathFactory):
|
|
1179
|
+
"""Test that changing the copyright holder replaces the old line instead of adding a new one.
|
|
1180
|
+
|
|
1181
|
+
Regression test: when a file already has a valid header with the *old* copyright phrase
|
|
1182
|
+
(e.g. 'ANSYS, Inc. and/or its affiliates.') and the hook is re-run with a new phrase
|
|
1183
|
+
(e.g. 'Synopsys, Inc. and ANSYS, Inc. All rights reserved.'), the header must be
|
|
1184
|
+
fully replaced — not duplicated — so only one copyright line exists in the file.
|
|
1185
|
+
"""
|
|
1186
|
+
template_name = "ansys.jinja2"
|
|
1187
|
+
license_name = "MIT.txt"
|
|
1188
|
+
template_path = Path(REPO_PATH) / ".reuse" / "templates" / template_name
|
|
1189
|
+
license_path = Path(REPO_PATH) / "LICENSES" / license_name
|
|
1190
|
+
|
|
1191
|
+
repo, tmp_file = set_up_repo(tmp_path, template_path, template_name, license_path, license_name)
|
|
1192
|
+
|
|
1193
|
+
old_copyright = "ANSYS, Inc. and/or its affiliates."
|
|
1194
|
+
new_copyright = "Synopsys, Inc. and ANSYS, Inc. All rights reserved."
|
|
1195
|
+
|
|
1196
|
+
# First run: add header with the *old* copyright phrase
|
|
1197
|
+
old_args = [tmp_file, f"--custom_copyright={old_copyright}"]
|
|
1198
|
+
assert add_argv_run(repo, tmp_file, old_args) == 1
|
|
1199
|
+
repo.index.add([tmp_file])
|
|
1200
|
+
|
|
1201
|
+
with open(tmp_file, "r", encoding="utf-8") as f:
|
|
1202
|
+
content_after_first_run = f.read()
|
|
1203
|
+
assert old_copyright in content_after_first_run
|
|
1204
|
+
assert new_copyright not in content_after_first_run
|
|
1205
|
+
|
|
1206
|
+
# Second run: re-run with the new copyright phrase (default)
|
|
1207
|
+
new_args = [tmp_file, f"--custom_copyright={new_copyright}"]
|
|
1208
|
+
assert add_argv_run(repo, tmp_file, new_args) == 1
|
|
1209
|
+
|
|
1210
|
+
with open(tmp_file, "r", encoding="utf-8") as f:
|
|
1211
|
+
content_after_second_run = f.read()
|
|
1212
|
+
|
|
1213
|
+
# The new phrase must be present
|
|
1214
|
+
assert new_copyright in content_after_second_run
|
|
1215
|
+
# The old phrase must be gone
|
|
1216
|
+
assert old_copyright not in content_after_second_run
|
|
1217
|
+
# Exactly one copyright line (no duplicates)
|
|
1218
|
+
copyright_line_count = sum(
|
|
1219
|
+
1 for line in content_after_second_run.splitlines() if "Copyright" in line
|
|
1220
|
+
)
|
|
1221
|
+
assert copyright_line_count == 1, (
|
|
1222
|
+
f"Expected exactly 1 copyright line, found {copyright_line_count}:\n"
|
|
1223
|
+
f"{content_after_second_run}"
|
|
1224
|
+
)
|
|
1225
|
+
|
|
1226
|
+
os.chdir(REPO_PATH)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (C) 2023 - 2026
|
|
1
|
+
# Copyright (C) 2023 - 2026 Synopsys, Inc. and ANSYS, Inc. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: MIT
|
|
3
3
|
#
|
|
4
4
|
#
|
|
@@ -96,8 +96,8 @@ def run_main(custom_args):
|
|
|
96
96
|
@pytest.mark.tech_review
|
|
97
97
|
def test_pyproject_toml(tmp_path: pytest.TempPathFactory):
|
|
98
98
|
"""Test pyproject.toml retrieves all information."""
|
|
99
|
-
author_maint_name = "ANSYS, Inc."
|
|
100
|
-
author_maint_email = "pyansys
|
|
99
|
+
author_maint_name = "Synopsys, Inc. and ANSYS, Inc."
|
|
100
|
+
author_maint_email = "pyansys-core@synopsys.com"
|
|
101
101
|
is_compliant = True
|
|
102
102
|
non_compliant_name = False
|
|
103
103
|
|
|
@@ -282,7 +282,7 @@ def test_bad_author_maint_name_email(tmp_path: pytest.TempPathFactory, capsys):
|
|
|
282
282
|
setup_repo(tmp_path)
|
|
283
283
|
|
|
284
284
|
# Remove name and email from author and maintainer in pyproject.toml
|
|
285
|
-
search = '{name = "ANSYS, Inc.", email = "pyansys
|
|
285
|
+
search = '{name = "Synopsys, Inc. and ANSYS, Inc.", email = "pyansys-core@synopsys.com"},'
|
|
286
286
|
replace = "{},"
|
|
287
287
|
replace_line(tmp_path, "pyproject.toml", search, replace)
|
|
288
288
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.7.1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|