continuous-delivery-scripts 4.1.0__tar.gz → 4.2.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 (140) hide show
  1. {continuous_delivery_scripts-4.1.0/continuous_delivery_scripts.egg-info → continuous_delivery_scripts-4.2.0}/PKG-INFO +1 -1
  2. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/_version.py +3 -3
  3. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/generate_docs.py +5 -2
  4. continuous_delivery_scripts-4.2.0/continuous_delivery_scripts/plugins/GOLAND.MD +52 -0
  5. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/golang.py +146 -20
  6. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_document.py +2 -2
  7. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_helpers.py +2 -1
  8. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_package.py +7 -3
  9. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_project.py +17 -7
  10. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_summary.py +67 -13
  11. continuous_delivery_scripts-4.2.0/continuous_delivery_scripts/spdx_report/templates/third_party_IP_report.html.jinja2 +289 -0
  12. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/tag_and_release.py +13 -4
  13. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/configuration.py +3 -0
  14. continuous_delivery_scripts-4.2.0/continuous_delivery_scripts/utils/package_helpers.py +280 -0
  15. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0/continuous_delivery_scripts.egg-info}/PKG-INFO +1 -1
  16. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/pyproject.toml +1 -0
  17. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/setup.py +3 -2
  18. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/GitPython.spdx +8 -8
  19. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Incremental.spdx +2 -2
  20. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Jinja2.spdx +2 -2
  21. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Mako.spdx +2 -2
  22. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Markdown.spdx +2 -2
  23. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/MarkupSafe.spdx +2 -2
  24. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/PyYAML.spdx +2 -2
  25. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Pygments.spdx +2 -2
  26. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/SecretStorage.spdx +2 -2
  27. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/boolean.py.spdx +2 -2
  28. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/boto3.spdx +5 -5
  29. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/botocore.spdx +5 -5
  30. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/bracex.spdx +2 -2
  31. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/certifi.spdx +2 -2
  32. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/cffi.spdx +2 -2
  33. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/charset-normalizer.spdx +5 -5
  34. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/click-default-group.spdx +2 -2
  35. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/click.spdx +2 -2
  36. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/continuous-delivery-scripts.spdx +85 -85
  37. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/cryptography.spdx +5 -5
  38. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/detect-secrets.spdx +2 -2
  39. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/docutils.spdx +2 -2
  40. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/gibberish-detector.spdx +2 -2
  41. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/gitdb.spdx +2 -2
  42. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/id.spdx +2 -2
  43. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/idna.spdx +2 -2
  44. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jaraco.classes.spdx +2 -2
  45. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jaraco.context.spdx +2 -2
  46. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jaraco.functools.spdx +2 -2
  47. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jeepney.spdx +2 -2
  48. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jellyfish.spdx +2 -2
  49. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jmespath.spdx +2 -2
  50. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/keyring.spdx +2 -2
  51. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/license-expression.spdx +2 -2
  52. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/licenseheaders.spdx +2 -2
  53. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/markdown-it-py.spdx +2 -2
  54. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/mdurl.spdx +2 -2
  55. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/more-itertools.spdx +2 -2
  56. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/nh3.spdx +2 -2
  57. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/packaging.spdx +2 -2
  58. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/pdoc3.spdx +2 -2
  59. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/ply.spdx +2 -2
  60. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/pyautoversion.spdx +2 -2
  61. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/pycparser.spdx +2 -2
  62. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/pyparsing.spdx +2 -2
  63. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/python-dateutil.spdx +2 -2
  64. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/python-dotenv.spdx +2 -2
  65. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/rdflib.spdx +2 -2
  66. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/readme_renderer.spdx +3 -3
  67. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/regex.spdx +2 -2
  68. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/requests-toolbelt.spdx +2 -2
  69. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/requests.spdx +2 -2
  70. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/rfc3986.spdx +2 -2
  71. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/rich.spdx +2 -2
  72. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/s3transfer.spdx +2 -2
  73. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/semver.spdx +2 -2
  74. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/setuptools.spdx +2 -2
  75. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/six.spdx +2 -2
  76. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/smmap.spdx +2 -2
  77. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/spdx-tools.spdx +2 -2
  78. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/toml.spdx +2 -2
  79. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/towncrier.spdx +2 -2
  80. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/twine.spdx +2 -2
  81. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/urllib3.spdx +2 -2
  82. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/wcmatch.spdx +2 -2
  83. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/xmltodict.spdx +2 -2
  84. continuous_delivery_scripts-4.1.0/continuous_delivery_scripts/plugins/GOLAND.MD +0 -12
  85. continuous_delivery_scripts-4.1.0/continuous_delivery_scripts/spdx_report/templates/third_party_IP_report.html.jinja2 +0 -148
  86. continuous_delivery_scripts-4.1.0/continuous_delivery_scripts/utils/package_helpers.py +0 -194
  87. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/LICENSE +0 -0
  88. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/MANIFEST.in +0 -0
  89. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/README.md +0 -0
  90. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/__init__.py +0 -0
  91. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/assert_news.py +0 -0
  92. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/create_news_file.py +0 -0
  93. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/detect_secrets.py +0 -0
  94. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/generate_news.py +0 -0
  95. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/get_config.py +0 -0
  96. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/get_version.py +0 -0
  97. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/language_specifics.py +0 -0
  98. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/license_files.py +0 -0
  99. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/BASIC.MD +0 -0
  100. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/CI.MD +0 -0
  101. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/GITHUB_ACTIONS.MD +0 -0
  102. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/NOOP.MD +0 -0
  103. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/PYTHON.MD +0 -0
  104. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/__init__.py +0 -0
  105. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/basic.py +0 -0
  106. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/ci.py +0 -0
  107. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/docker.py +0 -0
  108. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/github_actions.py +0 -0
  109. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/noop.py +0 -0
  110. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/python.py +0 -0
  111. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/report_third_party_ip.py +0 -0
  112. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/__init__.py +0 -0
  113. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_dependency.py +0 -0
  114. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_file.py +0 -0
  115. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/templates/third_party_IP_report.csv.jinja2 +0 -0
  116. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/templates/third_party_IP_report.txt.jinja2 +0 -0
  117. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/update_secrets_registry.py +0 -0
  118. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/__init__.py +0 -0
  119. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/aws_helpers.py +0 -0
  120. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/definitions.py +0 -0
  121. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/filesystem_helpers.py +0 -0
  122. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/git_helpers.py +0 -0
  123. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/hash_helpers.py +0 -0
  124. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/language_specifics_base.py +0 -0
  125. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/logging.py +0 -0
  126. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/news_file.py +0 -0
  127. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/noop/__init__.py +0 -0
  128. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/noop/package_helpers.py +0 -0
  129. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/python/__init__.py +0 -0
  130. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/python/package_helpers.py +0 -0
  131. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/python/python_helpers.py +0 -0
  132. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/string_helpers.py +0 -0
  133. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/third_party_licences.py +0 -0
  134. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/versioning.py +0 -0
  135. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/SOURCES.txt +0 -0
  136. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/dependency_links.txt +0 -0
  137. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/entry_points.txt +0 -0
  138. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/requires.txt +0 -0
  139. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/top_level.txt +0 -0
  140. {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: continuous-delivery-scripts
3
- Version: 4.1.0
3
+ Version: 4.2.0
4
4
  Summary: Git-based CI/CD tools for releases, changelogs, SPDX SBOMs and licence reporting
5
5
  Home-page: https://github.com/ARMmbed/continuous-delivery-scripts
6
6
  Author: Arm
@@ -11,8 +11,8 @@ References
11
11
  This file is autogenerated, do not modify by hand.
12
12
  """
13
13
 
14
- __version__ = "4.1.0"
15
- COMMIT = "298fb1cfdf2636976e5ea78d65207793279edcca"
14
+ __version__ = "4.2.0"
15
+ COMMIT = "757dd096956394de1185aa8f7bb169dc0d0fc982"
16
16
  MAJOR = 4
17
- MINOR = 1
17
+ MINOR = 2
18
18
  PATCH = 0
@@ -93,7 +93,7 @@ def _guide_settings() -> tuple[Path, Path, Path, str] | None:
93
93
  def _render_guide(source: Path, destination: Path, project_name: str, nav_prefix: str, has_api_index: bool) -> str:
94
94
  """Render one Markdown guide to an HTML page and return its title."""
95
95
  text = source.read_text(encoding="utf8")
96
- title = text.splitlines()[0].removeprefix("# ").strip()
96
+ title = next((line[2:].strip() for line in text.splitlines() if line.startswith("# ")), source.stem)
97
97
  body: str = markdown.markdown(text, extensions=["fenced_code", "tables"])
98
98
  body = re.sub(r'href="([a-z0-9-]+)\.md(#[^"]*)?"', _html_guide_link, body)
99
99
  description = f"{title} — {project_name}."
@@ -124,10 +124,13 @@ def _write_guide_index(
124
124
  ) -> None:
125
125
  """Write the project-specific landing page for the published guides."""
126
126
  guide_prefix = guide_folder.as_posix()
127
+ licence = configuration.get_value_or_default(ConfigurationVariable.FILE_LICENCE_IDENTIFIER, None)
128
+ licence_details = f"<p>Project licence: {escape(str(licence))}</p>" if licence else ""
127
129
  overview = (
128
130
  f"<h1>{escape(project_name)}</h1>"
129
131
  f"<p>Task guides for {escape(project_name)}, with setup, examples and project workflows.</p>"
130
- f'<p><a href="{escape(guide_prefix)}/index.html">All task guides</a>'
132
+ + licence_details
133
+ + f'<p><a href="{escape(guide_prefix)}/index.html">All task guides</a>'
131
134
  + (' · <a href="api.html">API reference</a>' if has_api_index else "")
132
135
  + "</p><ul>"
133
136
  + "".join(links)
@@ -0,0 +1,52 @@
1
+ # Golang plugin
2
+
3
+ This plugin is used for golang projects when `PROGRAMMING_LANGUAGE` is set to `golang`.
4
+
5
+ In order to work, `go > 1.18` is required.
6
+
7
+ ## Release
8
+ The plugin leverages [goreleaser](https://goreleaser.com/) to package and release projects.
9
+ Therefore, a `.goreleaser.yml` file must be present in the go source directory i.e. where the mod file lies.
10
+
11
+ ## Documentation
12
+ The plugin uses [doc2go](https://abhinav.github.io/doc2go) to generate the code reference documentation.
13
+
14
+ ## SPDX and third-party IP reporting
15
+
16
+ The Go plugin uses [go-licenses v2](https://github.com/google/go-licenses) to
17
+ discover the licences of packages imported by Go modules. It checks for a
18
+ usable `go-licenses` binary on `PATH` before installing
19
+ `github.com/google/go-licenses/v2@latest` with `go install`. The Go toolchain
20
+ must be available; an installed binary can also be located under `GOBIN` or
21
+ the first `GOPATH` entry's `bin` directory.
22
+
23
+ Set `PROGRAMMING_LANGUAGE = "golang"`, `PROJECT_NAME`, `PROJECT_ROOT` and
24
+ `SOURCE_DIR` in `[ProjectConfig]`. Put a `go.mod` under the source or project
25
+ root; `go.work` modules and nested Go modules are also considered. Supply
26
+ `FILE_LICENCE_IDENTIFIER` for the project licence and set `PROJECT_UUID`
27
+ and `[spdx]` namespace fields as explained in the
28
+ [SPDX configuration guide](../../DEVELOPMENT.md#spdx-document-identity).
29
+
30
+ With the project and its Go dependencies available, run:
31
+
32
+ ```bash
33
+ mkdir -p licensing
34
+ cd-generate-spdx --output-dir licensing
35
+ ```
36
+
37
+ For each module, the plugin runs `go-licenses report ./...` with a template
38
+ to collect dependency package names, versions, licence URLs and SPDX licence
39
+ names. The [go-licenses custom-template documentation](https://github.com/google/go-licenses/blob/master/README.md#reports-with-custom-templates)
40
+ defines the `Name`, `Version`, `LicenseURL` and `LicenseName` fields used by the
41
+ plugin; the default CSV report has no `Version` field. The metadata then enters
42
+ the **shared** SPDX writer, HTML/CSV/text/JSON
43
+ third-party IP report and accepted-licence policy used by other plugins.
44
+ To include the shared licence summary in release documentation, set
45
+ `GENERATE_LICENSING_SUMMARY_ON_RELEASE = true` in `[ProjectConfig]` (the default
46
+ is `false`).
47
+ The Go tool's own `check` policy is not used; configure
48
+ `ACCEPTED_THIRD_PARTY_LICENCES` and documented exceptions in
49
+ `PACKAGES_WITH_CHECKED_LICENCE` in `pyproject.toml` instead. Unknown licences
50
+ remain unknown so that the shared audit can flag them. The report reflects
51
+ dependencies built for the current Go environment and excludes test-only
52
+ imports unless the Go tool is invoked separately with `--include_tests`.
@@ -4,19 +4,23 @@
4
4
  #
5
5
  """Plugin for Golang projects."""
6
6
 
7
+ import csv
8
+ import io
7
9
  import json
8
10
  import logging
9
11
  import os
10
12
  import shutil
11
13
  from pathlib import Path
12
14
  from subprocess import check_call, check_output
13
- from typing import TYPE_CHECKING, Optional, List, Dict, MutableMapping
15
+ from tempfile import TemporaryDirectory
16
+ from typing import Optional, List, Dict, MutableMapping
14
17
 
18
+ from continuous_delivery_scripts.spdx_report.spdx_project import SpdxProject
15
19
  from continuous_delivery_scripts.utils.configuration import (
16
20
  configuration,
17
21
  ConfigurationVariable,
18
22
  )
19
- from continuous_delivery_scripts.utils.definitions import CommitType
23
+ from continuous_delivery_scripts.utils.definitions import CommitType, UNKNOWN
20
24
  from continuous_delivery_scripts.utils.git_helpers import (
21
25
  LocalProjectRepository,
22
26
  GitWrapper,
@@ -25,9 +29,12 @@ from continuous_delivery_scripts.utils.language_specifics_base import (
25
29
  BaseLanguage,
26
30
  get_language_from_file_name,
27
31
  )
28
-
29
- if TYPE_CHECKING:
30
- from continuous_delivery_scripts.spdx_report.spdx_project import SpdxProject
32
+ from continuous_delivery_scripts.utils.package_helpers import (
33
+ LicenceSource,
34
+ PackageMetadata,
35
+ ProjectMetadata,
36
+ ProjectMetadataFetcher,
37
+ )
31
38
 
32
39
  logger = logging.getLogger(__name__)
33
40
 
@@ -37,6 +44,9 @@ ENVVAR_GORELEASER_GIT_TOKEN = "GITHUB_TOKEN"
37
44
  ENVVAR_GORELEASER_CUSTOMISED_TAG = "GORELEASER_CURRENT_TAG"
38
45
  ENVVAR_GO_MOD = "GO111MODULE"
39
46
  GO_MOD_ON_VALUE = "on"
47
+ # go-licenses report --template receives Name, Version, LicenseURL and LicenseName for each library.
48
+ # https://github.com/google/go-licenses/blob/master/README.md#reports-with-custom-templates
49
+ GO_LICENSES_TEMPLATE = "{{range .}}{{.Name}}\t{{.Version}}\t{{.LicenseURL}}\t{{.LicenseName}}\n{{end}}"
40
50
 
41
51
 
42
52
  def _generate_doc2go_command_list(output_directory: Path, module: str) -> List[str]:
@@ -81,24 +91,147 @@ def _install_goreleaser_command_list() -> List[str]:
81
91
  return ["go", "install", "github.com/goreleaser/goreleaser/v2@latest"]
82
92
 
83
93
 
94
+ def _install_go_licenses_command_list() -> List[str]:
95
+ return ["go", "install", "github.com/google/go-licenses/v2@latest"]
96
+
97
+
98
+ def _installed_go_tool_path(tool_name: str, env: MutableMapping[str, str]) -> str:
99
+ """Find a binary installed using go install, even when Go's bin directory is not on PATH."""
100
+ go_bin = check_output(["go", "env", "GOBIN"], env=env, encoding="utf8").strip()
101
+ if not go_bin:
102
+ go_path = check_output(["go", "env", "GOPATH"], env=env, encoding="utf8").strip()
103
+ go_bin = str(Path(go_path.split(os.pathsep)[0]) / "bin")
104
+ executable = Path(go_bin) / (f"{tool_name}.exe" if os.name == "nt" else tool_name)
105
+ if not executable.is_file():
106
+ raise FileNotFoundError(f"Could not find installed {tool_name} binary: {executable}")
107
+ return str(executable)
108
+
109
+
110
+ def _candidate_go_module_directories(include_project_root: bool = False) -> List[Path]:
111
+ """Share module selection between release tags and licence reporting."""
112
+ candidates = [SRC_DIR]
113
+ if include_project_root:
114
+ candidates.append(ROOT_DIR)
115
+ workspace_modules = _determine_go_work_module_directories()
116
+ candidates.extend(workspace_modules if workspace_modules else _determine_go_subproject_directories())
117
+ return list(dict.fromkeys(candidates))
118
+
119
+
120
+ def _go_licence_module_directories() -> List[Path]:
121
+ """Find actual Go modules for dependency reporting."""
122
+ modules = [
123
+ path for path in _candidate_go_module_directories(include_project_root=True) if (path / "go.mod").is_file()
124
+ ]
125
+ if not modules:
126
+ raise FileNotFoundError(f"No go.mod found in {SRC_DIR}, {ROOT_DIR} or their Go workspace modules.")
127
+ return modules
128
+
129
+
130
+ def _go_module_name(module_dir: Path, env: MutableMapping[str, str]) -> str:
131
+ """Read the import path declared by a Go module."""
132
+ module = json.loads(check_output(["go", "list", "-m", "-json"], cwd=module_dir, env=env, encoding="utf8"))
133
+ return str(module["Path"])
134
+
135
+
136
+ def _parse_go_licences(output: str) -> List[PackageMetadata]:
137
+ """Translate go-licenses template output into shared package metadata."""
138
+ packages = []
139
+ for row in csv.reader(io.StringIO(output), delimiter="\t"):
140
+ if len(row) != 4 or not row[0].strip():
141
+ raise ValueError(f"Unexpected go-licenses report row: {row}")
142
+ name, version, licence_url, licence = (item.strip() for item in row)
143
+ known_licence = bool(licence and licence.lower() != UNKNOWN)
144
+ evidence = [{"kind": "licence", "path": licence_url, "text": ""}] if licence_url.startswith("https://") else []
145
+ packages.append(
146
+ PackageMetadata.from_fields(
147
+ name=name,
148
+ version=version or UNKNOWN,
149
+ licence=licence if known_licence else UNKNOWN,
150
+ licence_source=LicenceSource.TOOL if known_licence else LicenceSource.UNKNOWN,
151
+ declared_licence=licence or UNKNOWN,
152
+ url=licence_url or UNKNOWN,
153
+ licence_evidence=evidence,
154
+ )
155
+ )
156
+ return packages
157
+
158
+
159
+ class GoProjectMetadataFetcher(ProjectMetadataFetcher):
160
+ """Retrieve Go dependency licences for the shared SPDX and compliance report."""
161
+
162
+ def __init__(self) -> None:
163
+ """Initialise with the configured project name."""
164
+ super().__init__(str(configuration.get_value(ConfigurationVariable.PROJECT_NAME)))
165
+
166
+ def fetch_project_metadata(self) -> ProjectMetadata:
167
+ """Collect module dependencies using go-licenses without replacing shared policy checks."""
168
+ module_directories = _go_licence_module_directories()
169
+ env = os.environ.copy()
170
+ env[ENVVAR_GO_MOD] = GO_MOD_ON_VALUE
171
+ executable = _ensure_go_tool_installed(
172
+ tool_name="go-licenses",
173
+ version_command=["go-licenses", "report", "--help"],
174
+ install_command=_install_go_licenses_command_list(),
175
+ env=env,
176
+ )
177
+ project = ProjectMetadata(self._package_name)
178
+ project.project_metadata = PackageMetadata.from_fields(
179
+ name=self._package_name,
180
+ licence=str(configuration.get_value(ConfigurationVariable.FILE_LICENCE_IDENTIFIER)),
181
+ licence_source=LicenceSource.CONFIGURATION,
182
+ )
183
+ dependencies: Dict[str, PackageMetadata] = {}
184
+ modules = [(module_dir, _go_module_name(module_dir, env)) for module_dir in module_directories]
185
+ with TemporaryDirectory() as temporary_dir:
186
+ template_path = Path(temporary_dir) / "go-licenses.tpl"
187
+ template_path.write_text(GO_LICENSES_TEMPLATE, encoding="utf8")
188
+ for module_dir, _ in modules:
189
+ output = check_output(
190
+ [executable, "report", "./...", "--template", str(template_path)],
191
+ cwd=module_dir,
192
+ env=env,
193
+ encoding="utf8",
194
+ )
195
+ for package in _parse_go_licences(output):
196
+ if any(package.name == name or package.name.startswith(f"{name}/") for _, name in modules):
197
+ continue
198
+ existing = dependencies.get(package.name)
199
+ if existing and (existing.version, existing.licence) != (package.version, package.licence):
200
+ candidates = existing.licence_candidates or [existing.licence]
201
+ dependencies[package.name] = PackageMetadata.from_fields(
202
+ name=package.name,
203
+ version=UNKNOWN if existing.version != package.version else package.version,
204
+ licence=UNKNOWN,
205
+ licence_source=LicenceSource.UNKNOWN,
206
+ licence_candidates=sorted(set(candidates + [package.licence])),
207
+ licence_evidence=existing.licence_evidence + package.licence_evidence,
208
+ )
209
+ elif existing is None:
210
+ dependencies[package.name] = package
211
+ for dependency in dependencies.values():
212
+ project.add_dependency_metadata(dependency)
213
+ return project
214
+
215
+
84
216
  def _ensure_go_tool_installed(
85
217
  tool_name: str,
86
218
  version_command: List[str],
87
219
  install_command: List[str],
88
220
  env: MutableMapping[str, str],
89
- ) -> None:
90
- """Ensure a Go-based tool is available on PATH before use."""
221
+ ) -> str:
222
+ """Return a usable Go tool on PATH or install it into the Go binary directory."""
91
223
  tool_path = shutil.which(tool_name)
92
224
  if tool_path:
93
225
  try:
94
226
  check_output(version_command, env=env)
95
227
  logger.info("Using %s from PATH: %s", tool_name, tool_path)
96
- return
228
+ return tool_path
97
229
  except Exception as exception:
98
230
  logger.warning("Could not use %s from PATH: %s", tool_name, exception)
99
231
 
100
232
  logger.info("Installing %s with go install.", tool_name)
101
233
  check_call(install_command, env=env)
234
+ return _installed_go_tool_path(tool_name, env)
102
235
 
103
236
 
104
237
  def _call_doc2go(output_directory: Path, module: str) -> None:
@@ -212,15 +345,9 @@ def _determine_go_module_tag(version: str) -> List[str]:
212
345
  https://go.dev/ref/mod#workspaces, and
213
346
  https://github.com/golang/go/wiki/Modules/a549b3e4b7ad6be6e7d11c37ef247bb2279c8146#faqs--multi-module-repositories.
214
347
  """
215
- module_directories = [SRC_DIR]
216
- go_work_module_directories = _determine_go_work_module_directories()
217
- if go_work_module_directories:
218
- module_directories.extend(go_work_module_directories)
219
- else:
220
- module_directories.extend(_determine_go_subproject_directories())
221
-
222
348
  tags = [
223
- _determine_go_module_tag_for_directory(module_directory, version) for module_directory in module_directories
349
+ _determine_go_module_tag_for_directory(module_directory, version)
350
+ for module_directory in _candidate_go_module_directories()
224
351
  ]
225
352
  return list(dict.fromkeys([tag for tag in tags if tag]))
226
353
 
@@ -263,7 +390,7 @@ class Go(BaseLanguage):
263
390
 
264
391
  def can_get_project_metadata(self) -> bool:
265
392
  """States whether project metadata can be retrieved."""
266
- return False
393
+ return True
267
394
 
268
395
  def get_secret_registry_exclude_files(self) -> List[str]:
269
396
  """Gets additional detect-secrets exclude patterns for Go projects."""
@@ -274,10 +401,9 @@ class Go(BaseLanguage):
274
401
  (r"^\.github[\\/]workflows[\\/].*"),
275
402
  ]
276
403
 
277
- def get_current_spdx_project(self) -> Optional["SpdxProject"]:
404
+ def get_current_spdx_project(self) -> Optional[SpdxProject]:
278
405
  """Gets current SPDX description."""
279
- # TODO
280
- return None
406
+ return SpdxProject(GoProjectMetadataFetcher())
281
407
 
282
408
  def should_clean_before_packaging(self) -> bool:
283
409
  """States whether the repository must be cleaned before packaging happens."""
@@ -23,7 +23,7 @@ from continuous_delivery_scripts.utils.configuration import (
23
23
  ConfigurationVariable,
24
24
  )
25
25
  from continuous_delivery_scripts.utils.hash_helpers import generate_uuid_based_on_str
26
- from continuous_delivery_scripts.utils.python.package_helpers import PackageMetadata
26
+ from continuous_delivery_scripts.utils.package_helpers import PackageMetadata
27
27
 
28
28
  if TYPE_CHECKING:
29
29
  from spdx.document import Document
@@ -240,7 +240,7 @@ class SpdxDocument:
240
240
  doc.namespace = determine_spdx_value(self.document_namespace)
241
241
  doc.spdx_id = "SPDXRef-DOCUMENT"
242
242
  doc.comment = determine_spdx_value(
243
- "This document was created automatically using available information from python packages."
243
+ "This document was created automatically using available project and dependency information."
244
244
  )
245
245
  doc.data_license = License.from_identifier("CC0-1.0")
246
246
  doc.creation_info.add_creator(Person(self.author, self.author_email))
@@ -67,7 +67,7 @@ COPYRIGHT_PATTERN = r"Copyright.*$"
67
67
  COPYRIGHT_REGEX_PATTERN = re.compile(COPYRIGHT_PATTERN, flags=re.MULTILINE | re.IGNORECASE)
68
68
  # Specification of the identifier based on https://spdx.org/spdx-specification-21-web-version#h.twlc0ztnng3b
69
69
  # and https://spdx.org/ids-how
70
- SPDX_LICENCE_IDENTIFIER_PATTERN = r"SPDX-License-Identifier: ([\.\w+\-\(\)\s]+)[\*]?$"
70
+ SPDX_LICENCE_IDENTIFIER_PATTERN = r"SPDX-License-Identifier: ([\.\w+\-\(\)\t ]+)[\*]?$"
71
71
  SPDX_IDENTIFIER_REGEX_PATTERN = re.compile(SPDX_LICENCE_IDENTIFIER_PATTERN, re.MULTILINE)
72
72
  THIRD_PARTY_CONFIG_NAMESPACE = "spdx"
73
73
  PATHS_TO_EXCLUDE = [
@@ -77,6 +77,7 @@ PATHS_TO_EXCLUDE = [
77
77
  "*.rdf",
78
78
  "*.pyc",
79
79
  "*.bin",
80
+ "*.exe",
80
81
  "*.tar",
81
82
  "*.zip",
82
83
  "**/__pycache__/**",
@@ -6,17 +6,19 @@
6
6
 
7
7
  from dataclasses import dataclass
8
8
  from pathlib import Path
9
+ import re
9
10
  from typing import TYPE_CHECKING, List, Optional
10
11
 
11
12
  from license_expression import ExpressionError
12
13
 
13
14
  from continuous_delivery_scripts.spdx_report.spdx_file import SpdxFile
15
+ from continuous_delivery_scripts.utils.hash_helpers import generate_uuid_based_on_str
14
16
  from continuous_delivery_scripts.spdx_report.spdx_helpers import (
15
17
  determine_spdx_value,
16
18
  list_project_files_for_licensing,
17
19
  )
18
20
  from continuous_delivery_scripts.utils.definitions import UNKNOWN
19
- from continuous_delivery_scripts.utils.python.package_helpers import PackageMetadata
21
+ from continuous_delivery_scripts.utils.package_helpers import PackageMetadata
20
22
  from continuous_delivery_scripts.utils.third_party_licences import (
21
23
  UNKNOWN_LICENCE,
22
24
  cleanse_licence_expression,
@@ -30,7 +32,7 @@ if TYPE_CHECKING:
30
32
 
31
33
  @dataclass(frozen=True, order=True)
32
34
  class PackageInfo:
33
- """Definition of a Python package.
35
+ """Definition of a project or dependency package.
34
36
 
35
37
  Attributes:
36
38
  metadata: metadata about an installed package.
@@ -90,7 +92,9 @@ class SpdxPackage:
90
92
  Returns:
91
93
  An ID
92
94
  """
93
- return self.name if self._is_dependency else self._package_info.uuid
95
+ if not self._is_dependency:
96
+ return self._package_info.uuid
97
+ return self.name if re.fullmatch(r"[A-Za-z0-9.-]+", self.name) else str(generate_uuid_based_on_str(self.name))
94
98
 
95
99
  @property
96
100
  def is_dependency(self) -> bool:
@@ -2,10 +2,11 @@
2
2
  # Copyright (C) 2020-2026 Arm Limited or its affiliates and Contributors. All rights reserved.
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
  #
5
- """Definition of an SPDX report for a Python project."""
5
+ """Definition of an SPDX report for a project."""
6
6
 
7
7
  from pathlib import Path
8
8
  import os
9
+ import re
9
10
  from typing import Optional, List, cast, Tuple, Dict
10
11
 
11
12
  from continuous_delivery_scripts.spdx_report.spdx_dependency import (
@@ -13,15 +14,14 @@ from continuous_delivery_scripts.spdx_report.spdx_dependency import (
13
14
  )
14
15
  from continuous_delivery_scripts.spdx_report.spdx_document import SpdxDocument
15
16
  from continuous_delivery_scripts.utils.hash_helpers import determine_sha1_hash_of_file
16
- from continuous_delivery_scripts.utils.python.package_helpers import (
17
- ProjectMetadataFetcher,
18
- )
17
+ from continuous_delivery_scripts.utils.package_helpers import ProjectMetadataFetcher
19
18
  from continuous_delivery_scripts.spdx_report.spdx_helpers import (
20
19
  is_package_licence_manually_checked,
21
20
  get_package_manual_check,
22
21
  )
23
22
  from continuous_delivery_scripts.spdx_report.spdx_summary import SummaryGenerator
24
23
  from continuous_delivery_scripts.utils.configuration import configuration, ConfigurationVariable
24
+ from continuous_delivery_scripts.utils.hash_helpers import generate_uuid_based_on_str
25
25
  from continuous_delivery_scripts.utils.third_party_licences import UNKNOWN_LICENCE
26
26
 
27
27
 
@@ -98,6 +98,16 @@ class SpdxProject:
98
98
  self._parser.project_metadata.missing_dependencies,
99
99
  ).generate_summary(dir)
100
100
 
101
+ @staticmethod
102
+ def _spdx_filename(name: str) -> str:
103
+ """Use a stable, filesystem-safe SPDX filename for any package name."""
104
+ safe_name = (
105
+ name
106
+ if re.fullmatch(r"[A-Za-z0-9_.-]+", name) and name not in (".", "..")
107
+ else str(generate_uuid_based_on_str(name))
108
+ )
109
+ return f"{safe_name}.spdx"
110
+
101
111
  def generate_tag_value_files(self, dir: Path) -> None:
102
112
  """Generates SPDX tag-value files into the specified directory.
103
113
 
@@ -115,7 +125,7 @@ class SpdxProject:
115
125
 
116
126
  externalRefs = list()
117
127
  for spdx_dependency in self.dependency_documents:
118
- file_name = f"{spdx_dependency.name}.spdx"
128
+ file_name = self._spdx_filename(spdx_dependency.name)
119
129
  checksum = SpdxProject.generate_tag_value_file(dir, spdx_dependency, file_name)
120
130
  externalRefs.append(
121
131
  DependencySpdxDocumentRef(
@@ -125,7 +135,7 @@ class SpdxProject:
125
135
  )
126
136
  )
127
137
  self.main_document.external_refs = externalRefs
128
- SpdxProject.generate_tag_value_file(dir, self.main_document, f"{self.main_document.name}.spdx")
138
+ SpdxProject.generate_tag_value_file(dir, self.main_document, self._spdx_filename(self.main_document.name))
129
139
 
130
140
  def _report_issues(self, issues: Dict[str, str]) -> None:
131
141
  if issues:
@@ -164,7 +174,7 @@ class SpdxProject:
164
174
  package.name
165
175
  for package in [self.main_document, *self.dependency_documents]
166
176
  if (
167
- package.generate_spdx_package().metadata.licence_source == "unknown"
177
+ package.generate_spdx_package().metadata.has_unknown_licence
168
178
  or package.generate_spdx_package().main_licence == UNKNOWN_LICENCE.identifier
169
179
  )
170
180
  and not is_package_licence_manually_checked(package.name)
@@ -15,6 +15,8 @@ from continuous_delivery_scripts.spdx_report.spdx_helpers import (
15
15
  get_package_manual_check,
16
16
  )
17
17
  from continuous_delivery_scripts.spdx_report.spdx_package import SpdxPackage
18
+ from continuous_delivery_scripts.utils.configuration import configuration, ConfigurationVariable
19
+ from continuous_delivery_scripts.utils.hash_helpers import generate_uuid_based_on_str
18
20
  from continuous_delivery_scripts.utils.third_party_licences import UNKNOWN_LICENCE
19
21
 
20
22
  JINJA_TEMPLATE_SUMMARY_HTML = "third_party_IP_report.html.jinja2"
@@ -28,6 +30,40 @@ JINJA_TEMPLATES = [
28
30
  logger = logging.getLogger(__name__)
29
31
 
30
32
 
33
+ def _configured_licence_policy() -> List[str]:
34
+ """List the accepted licence entries as configured, retaining wildcard patterns."""
35
+ accepted = configuration.get_value(ConfigurationVariable.ACCEPTED_THIRD_PARTY_LICENCES)
36
+ if isinstance(accepted, str):
37
+ entries = accepted.split(",")
38
+ elif isinstance(accepted, (list, tuple, dict)):
39
+ entries = [str(entry) for entry in accepted]
40
+ elif isinstance(accepted, set):
41
+ entries = sorted(str(entry) for entry in accepted)
42
+ else:
43
+ entries = []
44
+ return [entry.strip() for entry in entries if entry.strip()]
45
+
46
+
47
+ def _link_report_from_index(output_dir: Path) -> None:
48
+ """Link the completed TPIP report from a generated documentation index, when present."""
49
+ index = output_dir / "index.html"
50
+ report = output_dir / JINJA_TEMPLATE_SUMMARY_HTML.removesuffix(".jinja2")
51
+ if not index.is_file() or not report.is_file():
52
+ return
53
+ contents = index.read_text(encoding="utf8")
54
+ report_href = f'href="{report.name}"'
55
+ if report_href in contents:
56
+ return
57
+ link = (
58
+ '<section id="third-party-ip-report"><h2>Third-party IP and licence report</h2>'
59
+ f"<p><a {report_href}>View the report</a></p></section>"
60
+ )
61
+ for closing_tag in ("</main>", "</body>"):
62
+ if closing_tag in contents:
63
+ index.write_text(contents.replace(closing_tag, f"{link}{closing_tag}", 1), encoding="utf8")
64
+ return
65
+
66
+
31
67
  def _get_jinja2_env() -> jinja2.Environment:
32
68
  return jinja2.Environment(
33
69
  loader=jinja2.FileSystemLoader(str(Path(__file__).resolve().parent.joinpath("templates"))),
@@ -81,16 +117,25 @@ class SummaryGenerator:
81
117
  arguments: Dict[str, Any] = dict()
82
118
 
83
119
  global_compliance, description_list = self._generate_packages_description()
120
+ compliance_points = (
121
+ [
122
+ "The project and all assessed dependencies meet the configured licence policy, "
123
+ "including any documented manual reviews.",
124
+ ]
125
+ if global_compliance
126
+ else [
127
+ "The project or one or more dependencies do not meet the configured licence policy.",
128
+ "Review the package results below for details.",
129
+ ]
130
+ )
84
131
  arguments["project"] = {
85
132
  "name": self.project.name,
133
+ "licence": self.project.main_licence,
134
+ "accepted_licences": _configured_licence_policy(),
86
135
  "compliance": global_compliance,
87
- "compliance_details": (
88
- (
89
- f"Project [{self.project.name}]'s licence is compliant: {self.project.licence}."
90
- "All its dependencies are also compliant licence-wise."
91
- )
92
- if global_compliance
93
- else f"Project [{self.project.name}] or one, at least, of its dependencies has a non compliant licence"
136
+ "compliance_points": compliance_points,
137
+ "compliance_details": " ".join(
138
+ [f"The project is licensed under the {self.project.main_licence} licence.", *compliance_points]
94
139
  ),
95
140
  }
96
141
  arguments["packages"] = description_list
@@ -98,7 +143,7 @@ class SummaryGenerator:
98
143
  arguments["unknown_licences"] = sorted(
99
144
  p.name
100
145
  for p in self.all_packages
101
- if p.metadata.licence_source == "unknown" or p.main_licence == UNKNOWN_LICENCE.identifier
146
+ if p.metadata.has_unknown_licence or p.main_licence == UNKNOWN_LICENCE.identifier
102
147
  )
103
148
  arguments["unreviewed_licences"] = [
104
149
  name for name in arguments["unknown_licences"] if not get_package_manual_check(name)[1]
@@ -112,8 +157,14 @@ class SummaryGenerator:
112
157
  self.missing_dependencies or arguments["unreviewed_licences"] or arguments["undocumented_exemptions"]
113
158
  )
114
159
  if not arguments["project"]["complete"]:
115
- details = "Incomplete audit: review missing dependencies, unknown licences and undocumented exemptions."
116
- arguments["project"]["compliance_details"] = details
160
+ compliance_points = [
161
+ "The licence audit is incomplete.",
162
+ "Review missing dependencies, unknown licences and undocumented exemptions before using this report.",
163
+ ]
164
+ arguments["project"]["compliance_points"] = compliance_points
165
+ arguments["project"]["compliance_details"] = " ".join(
166
+ [f"The project is licensed under the {self.project.main_licence} licence.", *compliance_points]
167
+ )
117
168
  arguments["render_time"] = datetime.datetime.now()
118
169
  return arguments
119
170
 
@@ -148,6 +199,7 @@ class SummaryGenerator:
148
199
  ) -> dict:
149
200
  return {
150
201
  "name": p.name,
202
+ "anchor": f"package-{generate_uuid_based_on_str(p.name)}",
151
203
  "is_dependency": p.is_dependency,
152
204
  "url": p.url,
153
205
  "licence": p.licence,
@@ -161,12 +213,13 @@ class SummaryGenerator:
161
213
  "is_compliant": is_compliant,
162
214
  "mark_as_problematic": not is_licence_compliant,
163
215
  "licence_compliance_details": (
164
- "Licence is compliant."
216
+ "Automatic licence assessment meets the configured policy."
165
217
  if is_licence_compliant
166
218
  else (
167
- f"Package's licence manually checked: {manual_check_details}"
219
+ "Automatic licence assessment does not meet the configured policy; "
220
+ f"accepted after manual review: {manual_check_details or 'no reason recorded'}."
168
221
  if package_manually_checked
169
- else "Licence is not compliant according to project's configuration."
222
+ else "Automatic licence assessment does not meet the configured policy."
170
223
  )
171
224
  ),
172
225
  }
@@ -191,3 +244,4 @@ class SummaryGenerator:
191
244
  dir.joinpath("third_party_IP_report.json").write_text(
192
245
  json.dumps(arguments, indent=2, sort_keys=True) + "\n", encoding="utf8"
193
246
  )
247
+ _link_report_from_index(dir)