Skip to content

Commit

Permalink
add spdx --creator-person --creator-organization
Browse files Browse the repository at this point in the history
  • Loading branch information
pietroalbini committed Nov 11, 2022
1 parent bf5f40c commit 0a0459f
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 9 deletions.
22 changes: 19 additions & 3 deletions src/reuse/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,12 @@ def to_dict(self):
"file_reports": [report.to_dict() for report in self.file_reports],
}

def bill_of_materials(self) -> str:
def bill_of_materials(
self,
*,
creator_person: Optional[str] = None,
creator_organization: Optional[str] = None,
) -> str:
"""Generate a bill of materials from the project.
See https://spdx.org/specifications.
Expand All @@ -124,8 +129,10 @@ def bill_of_materials(self) -> str:

# Author
# TODO: Fix Person and Organization
out.write("Creator: Person: Anonymous ()\n")
out.write("Creator: Organization: Anonymous ()\n")
out.write(f"Creator: Person: {self.format_creator(creator_person)}\n")
out.write(
f"Creator: Organization: {self.format_creator(creator_organization)}\n"
)
out.write(f"Creator: Tool: reuse-{__version__}\n")

now = datetime.datetime.utcnow()
Expand Down Expand Up @@ -176,6 +183,15 @@ def bill_of_materials(self) -> str:

return out.getvalue()

def format_creator(self, creator):
if creator is None:
return "Anonymous ()"
elif "(" in creator and creator.endswith(")"):
# The creator field already contains an email address
return creator
else:
return creator + " ()"

@classmethod
def generate(
cls,
Expand Down
36 changes: 35 additions & 1 deletion src/reuse/spdx.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,39 @@ def add_arguments(parser) -> None:
"guarantee the field is accurate."
),
)
parser.add_argument(
"--creator-person",
metavar="NAME",
help=_("Name of the person signing off on the SPDX report"),
)
parser.add_argument(
"--creator-organization",
metavar="NAME",
help=_("Name of the organization signing off on the SPDX report"),
)


def run(args, project: Project, out=sys.stdout) -> int:
"""Print the project's bill of materials."""
# The SPDX spec mandates that a creator must be specified when a license
# conclusion is made, so here we enforce that. More context:
#
# https://github.com/fsfe/reuse-tool/issues/586#issuecomment-1310425706
#
if (
args.add_license_concluded
and args.creator_person is None
and args.creator_organization is None
):
print(
_(
"error: --creator-person=NAME or --creator-organization=NAME"
" required when --add-license-concluded is provided"
),
file=sys.stderr,
)
return 1

with contextlib.ExitStack() as stack:
if args.file:
out = stack.enter_context(args.file.open("w", encoding="utf-8"))
Expand All @@ -57,6 +86,11 @@ def run(args, project: Project, out=sys.stdout) -> int:
add_license_concluded=args.add_license_concluded,
)

out.write(report.bill_of_materials())
out.write(
report.bill_of_materials(
creator_person=args.creator_person,
creator_organization=args.creator_organization,
)
)

return 0
51 changes: 46 additions & 5 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,25 +283,66 @@ def test_spdx(fake_repository, stringio):
# Ensure no LicenseConcluded is included without the flag
assert "\nLicenseConcluded: NOASSERTION\n" in output
assert "\nLicenseConcluded: GPL-3.0-or-later\n" not in output
assert "\nCreator: Person: Anonymous ()\n" in output
assert "\nCreator: Organization: Anonymous ()\n" in output

# TODO: This test is rubbish.
assert result == 0
assert output


def test_spdx_creator_info(fake_repository, stringio):
os.chdir(str(fake_repository))
result = main(
[
"spdx",
"--creator-person=Jane Doe (jane.doe@example.org)",
"--creator-organization=FSFE",
],
out=stringio,
)
output = stringio.getvalue()

assert result == 0
assert "\nCreator: Person: Jane Doe (jane.doe@example.org)\n" in output
assert "\nCreator: Organization: FSFE ()\n" in output


def test_spdx_add_license_concluded(fake_repository, stringio):
"""Compile to an SPDX document."""
"""Compile to an SPDX document with the LicenseConcluded field."""
os.chdir(str(fake_repository))
result = main(["spdx", "--add-license-concluded"], out=stringio)
result = main(
[
"spdx",
"--add-license-concluded",
"--creator-person=Jane Doe",
"--creator-organization=FSFE",
],
out=stringio,
)
output = stringio.getvalue()

# Ensure no LicenseConcluded is included without the flag
assert result == 0
assert "\nLicenseConcluded: NOASSERTION\n" not in output
assert "\nLicenseConcluded: GPL-3.0-or-later\n" in output
assert "\nCreator: Person: Jane Doe ()\n" in output
assert "\nCreator: Organization: FSFE ()\n" in output

# TODO: This test is rubbish.
assert result == 0
assert output

def test_spdx_add_license_concluded_without_creator_info(
fake_repository, stringio
):
"""Adding LicenseConcluded should require creator information"""
os.chdir(str(fake_repository))
result = main(["spdx", "--add-license-concluded"], out=stringio)
output = stringio.getvalue()

assert result == 1
# An error message is emitted, but on stderr to avoid returning mangled
# output when the caller expects an SPDX document. We cannot assert its
# contents because of that, unfortunately.
assert not output


def test_spdx_no_multiprocessing(fake_repository, stringio, multiprocessing):
Expand Down

0 comments on commit 0a0459f

Please sign in to comment.