Skip to content

Commit 98130f3

Browse files
cmbolingclaude
andauthored
docs(conan): handle list-valued license in make_fossa_deps_conan (#1719)
* docs(conan): handle list-valued license in make_fossa_deps_conan Conan recipes may declare `license` as either a single string ("MIT") or a list/tuple of strings (["MIT", "Apache-2.0"]). Conan 2's `conan graph info -f json` preserves that shape (ConanFile.serialize does `list(self.license)` when it is not a string), so the script could emit a YAML array into the fossa-deps `license` field, which must be a String: Error in $['custom-dependencies'][N].license: parsing Text failed, expected String, but encountered Array license_of now normalizes any shape into a single string: a string passes through, a list/tuple is joined into one SPDX expression via MULTI_LICENSE_JOINER (" AND " by default), and empty/None becomes None. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(conan): default missing license to NOASSERTION A Conan recipe with no `license` made license_of return None, which dump() wrote as `license: null`. fossa-deps requires a String for custom dependencies, so this failed with: Error in $['custom-dependencies'][N].license: parsing Text failed, expected String, but encountered Null Default a missing/empty license to the SPDX "NOASSERTION" marker so the generated fossa-deps.yml stays valid. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent faf8907 commit 98130f3

1 file changed

Lines changed: 23 additions & 2 deletions

File tree

docs/walkthroughs/make_fossa_deps_conan.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,29 @@ def name_version_of(label: str) -> Tuple[str, str]:
107107
name, version = label.split("/", 1)
108108
return name, version
109109

110+
# Conan recipes may declare `license` as a single string ("MIT") or as a list/tuple of
111+
# strings (["MIT", "Apache-2.0"]). The fossa-deps `license` field must be a single string,
112+
# so a list is joined into one SPDX expression. We use " AND " (every license's obligations
113+
# apply) as the conservative default; change MULTI_LICENSE_JOINER to " OR " if your packages
114+
# are dual-licensed (consumer's choice).
115+
MULTI_LICENSE_JOINER = " AND "
116+
117+
# fossa-deps requires a license string for every custom dependency. When a Conan recipe
118+
# declares no license, fall back to the SPDX "NOASSERTION" marker so the file stays valid;
119+
# emitting a bare `license: null` triggers: expected String, but encountered Null.
120+
NO_LICENSE = "NOASSERTION"
121+
110122
def license_of(node: dict) -> Optional[str]:
111-
return node.get("license")
123+
raw = node.get("license")
124+
if raw is None:
125+
return None
126+
if isinstance(raw, str):
127+
return raw or None
128+
if isinstance(raw, (list, tuple)):
129+
parts = [str(item).strip() for item in raw if item is not None and str(item).strip()]
130+
return MULTI_LICENSE_JOINER.join(parts) if parts else None
131+
# Unexpected shape (number, dict, ...): coerce to a string so fossa-deps stays valid.
132+
return str(raw)
112133

113134
def homepage_of(node: dict) -> Optional[str]:
114135
candidate = node.get("homepage")
@@ -158,7 +179,7 @@ def mk_fossa_deps(graph):
158179
vendored_deps.append(FossaVendorDep(name, version, src_dir))
159180
else:
160181
logging.info(f"could not find source code in disk for: {label}, using this as vendored dependency for fossa-deps")
161-
custom_deps.append(FossaCustomDep(name, version, license, FossaCustomDepMetadata(homepage, description)))
182+
custom_deps.append(FossaCustomDep(name, version, license or NO_LICENSE, FossaCustomDepMetadata(homepage, description)))
162183

163184
fossa_dep_yml = FossaDep(vendored_deps, custom_deps)
164185
fossa_dep_yml.dump()

0 commit comments

Comments
 (0)