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.
- {continuous_delivery_scripts-4.1.0/continuous_delivery_scripts.egg-info → continuous_delivery_scripts-4.2.0}/PKG-INFO +1 -1
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/_version.py +3 -3
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/generate_docs.py +5 -2
- continuous_delivery_scripts-4.2.0/continuous_delivery_scripts/plugins/GOLAND.MD +52 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/golang.py +146 -20
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_document.py +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_helpers.py +2 -1
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_package.py +7 -3
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_project.py +17 -7
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_summary.py +67 -13
- continuous_delivery_scripts-4.2.0/continuous_delivery_scripts/spdx_report/templates/third_party_IP_report.html.jinja2 +289 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/tag_and_release.py +13 -4
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/configuration.py +3 -0
- continuous_delivery_scripts-4.2.0/continuous_delivery_scripts/utils/package_helpers.py +280 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0/continuous_delivery_scripts.egg-info}/PKG-INFO +1 -1
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/pyproject.toml +1 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/setup.py +3 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/GitPython.spdx +8 -8
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Incremental.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Jinja2.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Mako.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Markdown.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/MarkupSafe.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/PyYAML.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/Pygments.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/SecretStorage.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/boolean.py.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/boto3.spdx +5 -5
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/botocore.spdx +5 -5
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/bracex.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/certifi.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/cffi.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/charset-normalizer.spdx +5 -5
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/click-default-group.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/click.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/continuous-delivery-scripts.spdx +85 -85
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/cryptography.spdx +5 -5
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/detect-secrets.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/docutils.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/gibberish-detector.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/gitdb.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/id.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/idna.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jaraco.classes.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jaraco.context.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jaraco.functools.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jeepney.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jellyfish.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/jmespath.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/keyring.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/license-expression.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/licenseheaders.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/markdown-it-py.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/mdurl.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/more-itertools.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/nh3.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/packaging.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/pdoc3.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/ply.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/pyautoversion.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/pycparser.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/pyparsing.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/python-dateutil.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/python-dotenv.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/rdflib.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/readme_renderer.spdx +3 -3
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/regex.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/requests-toolbelt.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/requests.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/rfc3986.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/rich.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/s3transfer.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/semver.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/setuptools.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/six.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/smmap.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/spdx-tools.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/toml.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/towncrier.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/twine.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/urllib3.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/wcmatch.spdx +2 -2
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/spdx-tmp/xmltodict.spdx +2 -2
- continuous_delivery_scripts-4.1.0/continuous_delivery_scripts/plugins/GOLAND.MD +0 -12
- continuous_delivery_scripts-4.1.0/continuous_delivery_scripts/spdx_report/templates/third_party_IP_report.html.jinja2 +0 -148
- continuous_delivery_scripts-4.1.0/continuous_delivery_scripts/utils/package_helpers.py +0 -194
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/LICENSE +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/MANIFEST.in +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/README.md +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/__init__.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/assert_news.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/create_news_file.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/detect_secrets.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/generate_news.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/get_config.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/get_version.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/language_specifics.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/license_files.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/BASIC.MD +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/CI.MD +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/GITHUB_ACTIONS.MD +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/NOOP.MD +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/PYTHON.MD +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/__init__.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/basic.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/ci.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/docker.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/github_actions.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/noop.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/plugins/python.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/report_third_party_ip.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/__init__.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_dependency.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/spdx_report/spdx_file.py +0 -0
- {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
- {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
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/update_secrets_registry.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/__init__.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/aws_helpers.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/definitions.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/filesystem_helpers.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/git_helpers.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/hash_helpers.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/language_specifics_base.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/logging.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/news_file.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/noop/__init__.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/noop/package_helpers.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/python/__init__.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/python/package_helpers.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/python/python_helpers.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/string_helpers.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/third_party_licences.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts/utils/versioning.py +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/SOURCES.txt +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/dependency_links.txt +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/entry_points.txt +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/requires.txt +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/continuous_delivery_scripts.egg-info/top_level.txt +0 -0
- {continuous_delivery_scripts-4.1.0 → continuous_delivery_scripts-4.2.0}/setup.cfg +0 -0
|
@@ -11,8 +11,8 @@ References
|
|
|
11
11
|
This file is autogenerated, do not modify by hand.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
__version__ = "4.
|
|
15
|
-
COMMIT = "
|
|
14
|
+
__version__ = "4.2.0"
|
|
15
|
+
COMMIT = "757dd096956394de1185aa8f7bb169dc0d0fc982"
|
|
16
16
|
MAJOR = 4
|
|
17
|
-
MINOR =
|
|
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()
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
30
|
-
|
|
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
|
-
) ->
|
|
90
|
-
"""
|
|
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)
|
|
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
|
|
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[
|
|
404
|
+
def get_current_spdx_project(self) -> Optional[SpdxProject]:
|
|
278
405
|
"""Gets current SPDX description."""
|
|
279
|
-
|
|
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.
|
|
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
|
|
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+\-\(\)\
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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 =
|
|
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,
|
|
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.
|
|
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
|
-
"
|
|
88
|
-
|
|
89
|
-
|
|
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.
|
|
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
|
-
|
|
116
|
-
|
|
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
|
-
"
|
|
216
|
+
"Automatic licence assessment meets the configured policy."
|
|
165
217
|
if is_licence_compliant
|
|
166
218
|
else (
|
|
167
|
-
|
|
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 "
|
|
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)
|