continuous-delivery-scripts 3.5.0__tar.gz → 4.0.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.
Files changed (71) hide show
  1. {continuous_delivery_scripts-3.5.0/continuous_delivery_scripts.egg-info → continuous_delivery_scripts-4.0.0}/PKG-INFO +17 -6
  2. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/README.md +10 -0
  3. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/_version.py +4 -4
  4. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/assert_news.py +13 -5
  5. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/git_helpers.py +6 -3
  6. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/news_file.py +4 -1
  7. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/third_party_licences.py +137 -11
  8. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0/continuous_delivery_scripts.egg-info}/PKG-INFO +17 -6
  9. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts.egg-info/requires.txt +2 -2
  10. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/setup.py +6 -5
  11. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/LICENSE +0 -0
  12. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/MANIFEST.in +0 -0
  13. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/__init__.py +0 -0
  14. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/create_news_file.py +0 -0
  15. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/detect_secrets.py +0 -0
  16. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/generate_docs.py +0 -0
  17. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/generate_news.py +0 -0
  18. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/get_config.py +0 -0
  19. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/get_version.py +0 -0
  20. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/language_specifics.py +0 -0
  21. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/license_files.py +0 -0
  22. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/BASIC.MD +0 -0
  23. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/CI.MD +0 -0
  24. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/GITHUB_ACTIONS.MD +0 -0
  25. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/GOLAND.MD +0 -0
  26. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/NOOP.MD +0 -0
  27. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/PYTHON.MD +0 -0
  28. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/__init__.py +0 -0
  29. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/basic.py +0 -0
  30. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/ci.py +0 -0
  31. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/docker.py +0 -0
  32. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/github_actions.py +0 -0
  33. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/golang.py +0 -0
  34. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/noop.py +0 -0
  35. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/plugins/python.py +0 -0
  36. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/report_third_party_ip.py +0 -0
  37. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/__init__.py +0 -0
  38. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/spdx_dependency.py +0 -0
  39. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/spdx_document.py +0 -0
  40. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/spdx_file.py +0 -0
  41. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/spdx_helpers.py +0 -0
  42. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/spdx_package.py +0 -0
  43. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/spdx_project.py +0 -0
  44. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/spdx_summary.py +0 -0
  45. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/templates/third_party_IP_report.csv.jinja2 +0 -0
  46. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/templates/third_party_IP_report.html.jinja2 +0 -0
  47. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/spdx_report/templates/third_party_IP_report.txt.jinja2 +0 -0
  48. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/tag_and_release.py +0 -0
  49. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/update_secrets_registry.py +0 -0
  50. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/__init__.py +0 -0
  51. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/aws_helpers.py +0 -0
  52. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/configuration.py +0 -0
  53. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/definitions.py +0 -0
  54. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/filesystem_helpers.py +0 -0
  55. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/hash_helpers.py +0 -0
  56. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/language_specifics_base.py +0 -0
  57. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/logging.py +0 -0
  58. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/noop/__init__.py +0 -0
  59. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/noop/package_helpers.py +0 -0
  60. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/package_helpers.py +0 -0
  61. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/python/__init__.py +0 -0
  62. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/python/package_helpers.py +0 -0
  63. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/python/python_helpers.py +0 -0
  64. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/string_helpers.py +0 -0
  65. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts/utils/versioning.py +0 -0
  66. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts.egg-info/SOURCES.txt +0 -0
  67. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts.egg-info/dependency_links.txt +0 -0
  68. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts.egg-info/entry_points.txt +0 -0
  69. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/continuous_delivery_scripts.egg-info/top_level.txt +0 -0
  70. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/pyproject.toml +0 -0
  71. {continuous_delivery_scripts-3.5.0 → continuous_delivery_scripts-4.0.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: continuous-delivery-scripts
3
- Version: 3.5.0
3
+ Version: 4.0.0
4
4
  Summary: Continuous Delivery scripts to increase automation
5
5
  Home-page: https://github.com/ARMmbed/continuous-delivery-scripts
6
6
  Author: CMSIS team
@@ -11,19 +11,20 @@ Classifier: Intended Audience :: Developers
11
11
  Classifier: License :: OSI Approved :: Apache Software License
12
12
  Classifier: Programming Language :: Python
13
13
  Classifier: Programming Language :: Python :: 3 :: Only
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
14
  Classifier: Programming Language :: Python :: 3.10
17
15
  Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
18
19
  Classifier: Topic :: Software Development :: Build Tools
19
- Requires-Python: >=3.8,<4
20
+ Requires-Python: >=3.10,<4
20
21
  Description-Content-Type: text/markdown
21
22
  License-File: LICENSE
22
23
  Requires-Dist: setuptools
23
24
  Requires-Dist: gitpython
24
25
  Requires-Dist: towncrier==22.12.0
25
26
  Requires-Dist: pyautoversion~=1.2.0
26
- Requires-Dist: pdoc3==0.10.0
27
+ Requires-Dist: pdoc3==0.11.6
27
28
  Requires-Dist: toml
28
29
  Requires-Dist: semver~=2.13.0
29
30
  Requires-Dist: python-dotenv
@@ -32,7 +33,7 @@ Requires-Dist: boto3
32
33
  Requires-Dist: detect-secrets[gibberish]==1.5.0
33
34
  Requires-Dist: packaging
34
35
  Requires-Dist: licenseheaders<0.8.9
35
- Requires-Dist: spdx-tools==0.6.1
36
+ Requires-Dist: spdx-tools==0.8.5
36
37
  Requires-Dist: license-expression
37
38
  Requires-Dist: wcmatch
38
39
  Requires-Dist: jellyfish
@@ -141,6 +142,16 @@ Therefore, it installs the following tools which can be used anywhere within a p
141
142
  * `cd-generate-spdx`: Generates SPDX documents for the project (3rd party IP reporting/ OpenChain)
142
143
  * `cd-license-files`: Updates the Licence header and copyright to all the source code files
143
144
 
145
+ For GitHub automation that performs authenticated clone or fetch operations, see [GitHub Token Authentication](#github-token-authentication).
146
+
147
+ ## GitHub Token Authentication
148
+
149
+ When git operations need to retry against GitHub over HTTPS, this project now uses the credential layout `https://x-access-token:<token>@github.com/<owner>/<repo>.git`.
150
+
151
+ This means the token is sent in the password position with `x-access-token` as the username, which is the format GitHub accepts for workflow-generated and other non-OAuth tokens used by automation.
152
+
153
+ Older `https://<token>:x-oauth-basic@github.com/...` URLs were tied to OAuth-style credentials and could reject tokens created dynamically during CI/CD flows.
154
+
144
155
  ## Project Structure
145
156
 
146
157
  The follow described the major aspects of the project structure:
@@ -88,6 +88,16 @@ Therefore, it installs the following tools which can be used anywhere within a p
88
88
  * `cd-generate-spdx`: Generates SPDX documents for the project (3rd party IP reporting/ OpenChain)
89
89
  * `cd-license-files`: Updates the Licence header and copyright to all the source code files
90
90
 
91
+ For GitHub automation that performs authenticated clone or fetch operations, see [GitHub Token Authentication](#github-token-authentication).
92
+
93
+ ## GitHub Token Authentication
94
+
95
+ When git operations need to retry against GitHub over HTTPS, this project now uses the credential layout `https://x-access-token:<token>@github.com/<owner>/<repo>.git`.
96
+
97
+ This means the token is sent in the password position with `x-access-token` as the username, which is the format GitHub accepts for workflow-generated and other non-OAuth tokens used by automation.
98
+
99
+ Older `https://<token>:x-oauth-basic@github.com/...` URLs were tied to OAuth-style credentials and could reject tokens created dynamically during CI/CD flows.
100
+
91
101
  ## Project Structure
92
102
 
93
103
  The follow described the major aspects of the project structure:
@@ -11,8 +11,8 @@ References
11
11
  This file is autogenerated, do not modify by hand.
12
12
  """
13
13
 
14
- __version__ = "3.5.0"
15
- COMMIT = "c8bbe64d754ac6d58403d50c46679ccebc5e1ef8"
16
- MAJOR = 3
17
- MINOR = 5
14
+ __version__ = "4.0.0"
15
+ COMMIT = "88537b61214eccbd719674df43ddf40d985ecd92"
16
+ MAJOR = 4
17
+ MINOR = 0
18
18
  PATCH = 0
@@ -21,6 +21,10 @@ logger = logging.getLogger(__name__)
21
21
  NEWS_FILE_NAME_REGEX = r"^[0-9]+.(misc|doc|removal|bugfix|feature|major)$"
22
22
 
23
23
 
24
+ class MissingNewsFileError(FileNotFoundError):
25
+ """The branch contains no added news file."""
26
+
27
+
24
28
  class NewsFileValidator:
25
29
  """Verifies individual news files."""
26
30
 
@@ -77,10 +81,11 @@ def find_news_files(git: GitWrapper, root_dir: str, news_dir: str) -> List[str]:
77
81
  # If no news files were added, then we check for addition on the branch.
78
82
  # Relies on the fact GitWrapper returns paths that are always relative
79
83
  # to the project root.
80
- added_news_files = [file_path for file_path in files_changed if file_path.startswith(news_dir)]
84
+ news_prefix = news_dir.replace("\\", "/").rstrip("/") + "/"
85
+ added_news_files = [file_path for file_path in files_changed if file_path.startswith(news_prefix)]
81
86
  if len(added_news_files) == 0:
82
87
  files_changed = git.list_files_added_on_current_branch()
83
- added_news_files = [file_path for file_path in files_changed if file_path.startswith(news_dir)]
88
+ added_news_files = [file_path for file_path in files_changed if file_path.startswith(news_prefix)]
84
89
  return [str(pathlib.Path(root_dir, file_path)) for file_path in added_news_files]
85
90
 
86
91
 
@@ -94,7 +99,7 @@ def validate_news_files(git: GitWrapper, root_dir: str, news_dir: str) -> None:
94
99
  """
95
100
  added_news_files = find_news_files(git=git, news_dir=news_dir, root_dir=root_dir)
96
101
  if not added_news_files:
97
- raise FileNotFoundError(f"PR must contain a news file in {news_dir}. See README.md.")
102
+ raise MissingNewsFileError(f"PR must contain a news file in {news_dir}. See README.md.")
98
103
  for absolute_file_path in added_news_files:
99
104
  validate_news_file(absolute_file_path)
100
105
 
@@ -164,8 +169,8 @@ def main() -> None:
164
169
  absolute_news_dir = configuration.get_value(ConfigurationVariable.NEWS_DIR)
165
170
  news_dir = str(pathlib.Path(absolute_news_dir).relative_to(root_dir))
166
171
  try:
167
- validate_news_files(git=git, news_dir=news_dir, root_dir=root_dir)
168
- except Exception as e:
172
+ validate_news_files(git=git, news_dir=news_dir, root_dir=str(git.root))
173
+ except MissingNewsFileError as e:
169
174
  log_exception(logger, e)
170
175
  try:
171
176
  news_file = generate_news_file(git, git.get_corresponding_path(pathlib.Path(news_dir)))
@@ -173,6 +178,9 @@ def main() -> None:
173
178
  except Exception as e2:
174
179
  log_exception(logger, e2)
175
180
  sys.exit(1)
181
+ except Exception as e:
182
+ log_exception(logger, e)
183
+ sys.exit(1)
176
184
 
177
185
 
178
186
  if __name__ == "__main__":
@@ -39,14 +39,17 @@ class GitWrapper:
39
39
  def _git_url_ssh_to_https(self, url: str) -> str:
40
40
  """Changes repository URL to use authorisation token.
41
41
 
42
- Converts the git url to use the GitHub token:
43
- See https://github.blog/2012-09-21-easier-builds-and-deployments-using-git-over-https-and-oauth/
42
+ Converts the git url to use GitHub's token-based HTTPS authentication
43
+ format so workflow-generated tokens can be used for git operations.
44
+
45
+ Reference:
46
+ https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-authentication-to-github#githubs-token-formats
44
47
 
45
48
  Returns:
46
49
  new URL
47
50
  """
48
51
  path = url.split("github.com", 1)[1][1:].strip()
49
- new = "https://{GITHUB_TOKEN}:x-oauth-basic@github.com/%s" % path
52
+ new = "https://x-access-token:{GITHUB_TOKEN}@github.com/%s" % path
50
53
  logger.info("rewriting git url to: %s" % new)
51
54
  return new.format(GITHUB_TOKEN=configuration.get_value(ConfigurationVariable.GIT_TOKEN))
52
55
 
@@ -7,6 +7,7 @@
7
7
  import enum
8
8
  import pathlib
9
9
  import logging
10
+ import time
10
11
  from datetime import datetime
11
12
  from typing import Any, Optional
12
13
 
@@ -57,4 +58,6 @@ def _write_file(file_path: pathlib.Path, text: str) -> None:
57
58
 
58
59
  def determine_basic_new_news_file_name() -> str:
59
60
  """Returns a new news file name."""
60
- return datetime.now().strftime("%Y%m%d%H%M%S")
61
+ now_ns = time.time_ns()
62
+ seconds, nanoseconds = divmod(now_ns, 1_000_000_000)
63
+ return f"{datetime.fromtimestamp(seconds):%Y%m%d%H%M%S}{nanoseconds:09d}"
@@ -9,9 +9,9 @@ import re
9
9
  import json
10
10
  from dataclasses import dataclass
11
11
  from importlib.util import find_spec
12
- from license_expression import Licensing, LicenseExpression, OR
12
+ from license_expression import Licensing, LicenseExpression, OR, get_spdx_licensing
13
13
  from pathlib import Path
14
- from typing import Iterable, cast, Optional, Iterator, List, Pattern, Any
14
+ from typing import Dict, Iterable, cast, Optional, Iterator, List, Pattern, Any, Tuple
15
15
 
16
16
  from continuous_delivery_scripts.utils.configuration import (
17
17
  ConfigurationVariable,
@@ -57,10 +57,101 @@ LICENCE_NON_ACCEPTED_CHARACTERS = r"[^\w\s\.\:\-()]"
57
57
  def _get_spdx_licenses_path() -> Path:
58
58
  spec = find_spec("spdx")
59
59
  if not spec or not spec.origin:
60
- raise ModuleNotFoundError("No module named 'spdx'")
60
+ raise FileNotFoundError("Could not find SPDX licenses.json")
61
61
  return Path(spec.origin).resolve().parent.joinpath("licenses.json")
62
62
 
63
63
 
64
+ FALLBACK_LICENCE_DATA = {
65
+ "0BSD": {
66
+ "reference_number": "319",
67
+ "name": "BSD Zero Clause License",
68
+ },
69
+ "Apache-2.0": {
70
+ "reference_number": "26",
71
+ "name": "Apache License 2.0",
72
+ },
73
+ "GPL-3.0-only": {
74
+ "name": "GNU General Public License v3.0 only",
75
+ },
76
+ "MPL-2.0": {
77
+ "name": "Mozilla Public License 2.0",
78
+ },
79
+ "MIT": {
80
+ "name": "MIT License",
81
+ },
82
+ "PSF-2.0": {
83
+ "name": "Python Software Foundation License 2.0",
84
+ },
85
+ "Python-2.0": {
86
+ "name": "Python License 2.0",
87
+ },
88
+ }
89
+
90
+
91
+ FALLBACK_LICENCE_ALIASES = {
92
+ "0BSD": ["BSD", "BSD License", "BSD Zero Clause License"],
93
+ "Apache-2.0": [
94
+ "Apache",
95
+ "Apache 2",
96
+ "Apache 2.0",
97
+ "Apache License 2",
98
+ "Apache License 2.0",
99
+ "Apache License Version 2",
100
+ "Apache License Version 2.0",
101
+ "Apache License, Version 2",
102
+ "Apache License, Version 2.0",
103
+ "Apache Licence 2",
104
+ "Apache Licence 2.0",
105
+ "Apache Licence Version 2",
106
+ "Apache Licence Version 2.0",
107
+ "Apache Licence, Version 2",
108
+ "Apache Licence, Version 2.0",
109
+ "Apache Software License",
110
+ ],
111
+ "GPL-3.0-only": ["GPL 3", "GPL 3.0", "GPL-3.0", "GPL-3", "GNU GPL 3"],
112
+ "MIT": ["MIT License"],
113
+ "PSF-2.0": ["Python Software Foundation License 2.0"],
114
+ "Python-2.0": ["Python Software Foundation License"],
115
+ }
116
+
117
+
118
+ def _build_fallback_licence(identifier: str, is_deprecated: bool = False) -> Licence:
119
+ metadata = FALLBACK_LICENCE_DATA.get(identifier, {})
120
+ return Licence(
121
+ reference_number=str(metadata.get("reference_number", "")),
122
+ identifier=identifier,
123
+ name=str(metadata.get("name", identifier)),
124
+ is_deprecated=is_deprecated,
125
+ is_osi_approved=bool(metadata.get("is_osi_approved", True)),
126
+ url=f"http://spdx.org/licenses/{identifier}.json",
127
+ reference=f"./{identifier}.html",
128
+ )
129
+
130
+
131
+ def _iter_fallback_licences() -> Iterable[Tuple[Licence, Iterable[str]]]:
132
+ spdx_licensing = get_spdx_licensing()
133
+ seen = set()
134
+ for symbol in spdx_licensing.known_symbols.values():
135
+ identifier = getattr(symbol, "key", None)
136
+ if not identifier or identifier in seen:
137
+ continue
138
+ if identifier.startswith("LicenseRef-") or getattr(symbol, "is_exception", False):
139
+ continue
140
+ seen.add(identifier)
141
+ aliases = list(getattr(symbol, "aliases", ())) + FALLBACK_LICENCE_ALIASES.get(identifier, [])
142
+ yield _build_fallback_licence(identifier, is_deprecated=bool(getattr(symbol, "is_deprecated", False))), aliases
143
+
144
+
145
+ def _normalise_licence_text(text: str) -> str:
146
+ normalised_text = text.strip().lower()
147
+ normalised_text = re.sub(r"osi\s?approved[:]*", "", normalised_text)
148
+ normalised_text = re.sub(r"licen[cs]e", " ", normalised_text)
149
+ normalised_text = re.sub(r"version", " ", normalised_text)
150
+ normalised_text = re.sub(r"[^\w\s]", " ", normalised_text)
151
+ normalised_text = re.sub(r"\s+", " ", normalised_text)
152
+ return normalised_text.strip()
153
+
154
+
64
155
  def _parse_licence_expression(licensing: Licensing, licence_expression: str) -> LicenseExpression:
65
156
  # Removing any unwanted characters so that the expression follows the laws:
66
157
  # > the valid characters are: letters and numbers, underscore, dot, colon or hyphen signs and spaces
@@ -92,11 +183,17 @@ def iter_licenses(licence_info: dict) -> Iterable[Licence]:
92
183
  def _handle_special_licence_entries(cleansed_descriptor: str) -> str:
93
184
  if cleansed_descriptor in ["Python Software Foundation License"]:
94
185
  return "Python"
186
+ if re.fullmatch(r"Python(?:[\w\s\-\.]*)", cleansed_descriptor, re.IGNORECASE):
187
+ return "Python-2.0"
95
188
  if cleansed_descriptor in ["Apache Software License", "Apache", "apache"]:
96
189
  return "Apache-2.0"
97
190
  if cleansed_descriptor in ["LGPL", "UNKNOWN", "Dual License"]:
98
191
  # It is not possible to find which is the actual licence to consider.
99
192
  return UNKNOWN_LICENCE.identifier
193
+ if re.fullmatch(r"Apache(?:\s+(?:Software\s+)?)?(?:Licen[cs]e)?(?:,?\s+Version)?\s*2(?:\.0)?", cleansed_descriptor):
194
+ return "Apache-2.0"
195
+ if re.fullmatch(r"GPL\s*3(?:\.0)?", cleansed_descriptor, re.IGNORECASE):
196
+ return "GPL-3.0-only"
100
197
  return cleansed_descriptor
101
198
 
102
199
 
@@ -122,18 +219,32 @@ class OpenSourceLicences:
122
219
  self._licence_store: Optional[dict] = None
123
220
  self._licence_list: Optional[list] = None
124
221
 
222
+ def _store_licence(self, licence: Licence, aliases: Iterable[str] = ()) -> None:
223
+ if not self._licence_store or self._licence_list is None:
224
+ return
225
+ entries = [licence.identifier, licence.name, *aliases]
226
+ for entry in entries:
227
+ if not entry:
228
+ continue
229
+ self._licence_store[entry] = licence
230
+ self._licence_list.append(entry)
231
+
125
232
  def load(self) -> None:
126
233
  """Loads licence data from internal Json file."""
127
234
  if self._licence_list and self._licence_store:
128
235
  return
129
236
  self._licence_store = {UNKNOWN_LICENCE.identifier: UNKNOWN_LICENCE}
130
237
  self._licence_list = [UNKNOWN_LICENCE.identifier]
131
- with open(_get_spdx_licenses_path(), "r", encoding="utf8") as f:
132
- for licence in iter_licenses(json.load(f)):
133
- self._licence_store[licence.identifier] = licence
134
- self._licence_list.append(licence.identifier)
135
- self._licence_store[licence.name] = licence
136
- self._licence_list.append(licence.name)
238
+ try:
239
+ with open(_get_spdx_licenses_path(), "r", encoding="utf8") as f:
240
+ for licence in iter_licenses(json.load(f)):
241
+ self._store_licence(licence)
242
+ return
243
+ except (FileNotFoundError, ModuleNotFoundError):
244
+ pass
245
+
246
+ for licence, aliases in _iter_fallback_licences():
247
+ self._store_licence(licence, aliases)
137
248
 
138
249
  def get_licences_from_pattern(self, licence_descriptor_pattern: Pattern) -> Optional[List[Licence]]:
139
250
  """Determines all the licences following a certain pattern."""
@@ -153,8 +264,23 @@ class OpenSourceLicences:
153
264
  if not self._licence_store or not self._licence_list or not licence_descriptor:
154
265
  return None
155
266
  cleansed_descriptor = cleanse_licence_descriptor(licence_descriptor)
156
- likelihood, licence = determine_similar_string_from_list(cleansed_descriptor, self._licence_list)
157
- return self._licence_store.get(licence) if likelihood > LICENCE_LIKELIHOOD_THRESHOLD else None
267
+ exact_match = self._licence_store.get(cleansed_descriptor)
268
+ if exact_match:
269
+ return cast(Licence, exact_match)
270
+
271
+ normalised_map: Dict[str, Licence] = {}
272
+ for name in self._licence_list:
273
+ licence = cast(Licence, self._licence_store.get(name))
274
+ if licence:
275
+ normalised_map[_normalise_licence_text(name)] = licence
276
+
277
+ normalised_descriptor = _normalise_licence_text(cleansed_descriptor)
278
+ normalised_exact_match = normalised_map.get(normalised_descriptor)
279
+ if normalised_exact_match:
280
+ return normalised_exact_match
281
+
282
+ likelihood, matched_key = determine_similar_string_from_list(normalised_descriptor, normalised_map.keys())
283
+ return normalised_map.get(matched_key) if likelihood > LICENCE_LIKELIHOOD_THRESHOLD else None
158
284
 
159
285
 
160
286
  OPENSOURCE_LICENCES = OpenSourceLicences()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: continuous-delivery-scripts
3
- Version: 3.5.0
3
+ Version: 4.0.0
4
4
  Summary: Continuous Delivery scripts to increase automation
5
5
  Home-page: https://github.com/ARMmbed/continuous-delivery-scripts
6
6
  Author: CMSIS team
@@ -11,19 +11,20 @@ Classifier: Intended Audience :: Developers
11
11
  Classifier: License :: OSI Approved :: Apache Software License
12
12
  Classifier: Programming Language :: Python
13
13
  Classifier: Programming Language :: Python :: 3 :: Only
14
- Classifier: Programming Language :: Python :: 3.8
15
- Classifier: Programming Language :: Python :: 3.9
16
14
  Classifier: Programming Language :: Python :: 3.10
17
15
  Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
18
19
  Classifier: Topic :: Software Development :: Build Tools
19
- Requires-Python: >=3.8,<4
20
+ Requires-Python: >=3.10,<4
20
21
  Description-Content-Type: text/markdown
21
22
  License-File: LICENSE
22
23
  Requires-Dist: setuptools
23
24
  Requires-Dist: gitpython
24
25
  Requires-Dist: towncrier==22.12.0
25
26
  Requires-Dist: pyautoversion~=1.2.0
26
- Requires-Dist: pdoc3==0.10.0
27
+ Requires-Dist: pdoc3==0.11.6
27
28
  Requires-Dist: toml
28
29
  Requires-Dist: semver~=2.13.0
29
30
  Requires-Dist: python-dotenv
@@ -32,7 +33,7 @@ Requires-Dist: boto3
32
33
  Requires-Dist: detect-secrets[gibberish]==1.5.0
33
34
  Requires-Dist: packaging
34
35
  Requires-Dist: licenseheaders<0.8.9
35
- Requires-Dist: spdx-tools==0.6.1
36
+ Requires-Dist: spdx-tools==0.8.5
36
37
  Requires-Dist: license-expression
37
38
  Requires-Dist: wcmatch
38
39
  Requires-Dist: jellyfish
@@ -141,6 +142,16 @@ Therefore, it installs the following tools which can be used anywhere within a p
141
142
  * `cd-generate-spdx`: Generates SPDX documents for the project (3rd party IP reporting/ OpenChain)
142
143
  * `cd-license-files`: Updates the Licence header and copyright to all the source code files
143
144
 
145
+ For GitHub automation that performs authenticated clone or fetch operations, see [GitHub Token Authentication](#github-token-authentication).
146
+
147
+ ## GitHub Token Authentication
148
+
149
+ When git operations need to retry against GitHub over HTTPS, this project now uses the credential layout `https://x-access-token:<token>@github.com/<owner>/<repo>.git`.
150
+
151
+ This means the token is sent in the password position with `x-access-token` as the username, which is the format GitHub accepts for workflow-generated and other non-OAuth tokens used by automation.
152
+
153
+ Older `https://<token>:x-oauth-basic@github.com/...` URLs were tied to OAuth-style credentials and could reject tokens created dynamically during CI/CD flows.
154
+
144
155
  ## Project Structure
145
156
 
146
157
  The follow described the major aspects of the project structure:
@@ -2,7 +2,7 @@ setuptools
2
2
  gitpython
3
3
  towncrier==22.12.0
4
4
  pyautoversion~=1.2.0
5
- pdoc3==0.10.0
5
+ pdoc3==0.11.6
6
6
  toml
7
7
  semver~=2.13.0
8
8
  python-dotenv
@@ -11,7 +11,7 @@ boto3
11
11
  detect-secrets[gibberish]==1.5.0
12
12
  packaging
13
13
  licenseheaders<0.8.9
14
- spdx-tools==0.6.1
14
+ spdx-tools==0.8.5
15
15
  license-expression
16
16
  wcmatch
17
17
  jellyfish
@@ -30,10 +30,11 @@ setup(
30
30
  "License :: OSI Approved :: Apache Software License",
31
31
  "Programming Language :: Python",
32
32
  "Programming Language :: Python :: 3 :: Only",
33
- "Programming Language :: Python :: 3.8",
34
- "Programming Language :: Python :: 3.9",
35
33
  "Programming Language :: Python :: 3.10",
36
34
  "Programming Language :: Python :: 3.11",
35
+ "Programming Language :: Python :: 3.12",
36
+ "Programming Language :: Python :: 3.13",
37
+ "Programming Language :: Python :: 3.14",
37
38
  "Topic :: Software Development :: Build Tools",
38
39
  ],
39
40
  description="Continuous Delivery scripts to increase automation",
@@ -61,7 +62,7 @@ setup(
61
62
  "towncrier==22.12.0",
62
63
  "pyautoversion~=1.2.0",
63
64
  # FIXME change when https://github.com/pdoc3/pdoc/issues/299 is fixed
64
- "pdoc3==0.10.0",
65
+ "pdoc3==0.11.6",
65
66
  "toml",
66
67
  "semver~=2.13.0",
67
68
  "python-dotenv",
@@ -70,7 +71,7 @@ setup(
70
71
  "detect-secrets[gibberish]==1.5.0",
71
72
  "packaging",
72
73
  "licenseheaders<0.8.9",
73
- "spdx-tools==0.6.1",
74
+ "spdx-tools==0.8.5",
74
75
  "license-expression",
75
76
  "wcmatch",
76
77
  "jellyfish",
@@ -82,7 +83,7 @@ setup(
82
83
  long_description=long_description,
83
84
  name=PROJECT_SLUG,
84
85
  packages=[SOURCE_DIR],
85
- python_requires=">=3.8,<4",
86
+ python_requires=">=3.10,<4",
86
87
  url=f"https://github.com/ARMmbed/{PROJECT_SLUG}",
87
88
  version=__version__,
88
89
  )