Skip to content

fix: a component of emit sbom states the package, not the lock's spelling of it - #894

Open
NopeCharon wants to merge 1 commit into
mcpp-community:mainfrom
NopeCharon:fix/sbom-component-identity
Open

NopeCharon wants to merge 1 commit into
mcpp-community:mainfrom
NopeCharon:fix/sbom-component-identity

Conversation

@NopeCharon

@NopeCharon NopeCharon commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

mcpp emit sbom copied a lock entry's name into the component's name, bom-ref and purl, and that name is a spelling: the map key of the declaring manifest, or a qualified name for a package no manifest in view declares. So mcpplibs.cmdline became pkg:mcpp/mcpplibs/mcpplibs.cmdline@0.0.1 while cmdline gave pkg:mcpp/mcpplibs/cmdline@0.0.1 for the same package, and compat.zlib became pkg:mcpp/compat/compat.zlib@1.3.2. A purl is what correlates a component with an advisory feed, so one package needs one.

  • name and purl now use the short name. lock_short_name moves from records.cpp to mcpp.pm.lock_io as short_name, so the two readers share it; records.cpp behaves as before.
  • A purl percent-encodes its segments, so + in build metadata or in a variant's +p<digest> is %2B. The version field stays as recorded.
  • A variant keeps its digest in the name, since a patched library is another library.
  • A lock written by 2026.9.29.1 to .5 can hold one identity under two spellings. Those would now share a bom-ref, which is unique in a document, so they give one component.

docs/51-supported-versions.md states the rule in both languages.

Closes #892.

Criteria

  • tests/e2e/1016_the_bill_of_materials_states_a_packages_identity.sh (new) writes the lock by hand, so it needs no toolchain, index or network. It fails on the released 2026.11.5.1 macOS arm64 binary, at ('mcpplibs', 'mcpplibs.cmdline'). No build of the whole engine was possible on the host I had, so the passing run was on macOS arm64 against the real src/cli/cmd_sbom.cppm, src/pm/lock_io.cppm and modules/libs/src/toml.cppm compiled together, with stubs for the manifest loader and the CLI around them. That setup fails the script before the change and passes it after. On a real build it passed on this PR's linux-e2e job (linux x86_64, self-host), as did 333_named_runners_locked_and_sbom.sh and 1001_a_dependency_carries_its_patches.sh.
  • PmLockIo.TheShortNameIsTheSameWhicheverWayTheManifestSpelledIt (tests/unit/test_pm_lock_io.cpp) covers the moved function. Its assertions ran against the real lock_io on the same setup, 0 failures, and the unit jobs in CI pass.
  • src/build/prepare/records.cpp did not compile locally against the moved function. The build-* jobs compile it on every host and pass.
  • One CI job failed: linux-e2e / e2e 2/6, on 1013_optional_action_progress_preserves_outputs.sh alone. That test drives terminal progress output through a pty during mcpp build, and this change touches no code it exercises. The same test passed in the same shard on fix: a missing mcpp.lock is named by emit sbom and --locked #893, which branches from the same commit. It looks timing-dependent, though I have not shown that; I cannot re-run the job from a fork, so a maintainer's re-run would settle it.
  • check_docs_style.sh, check_docs_structure.sh, check_version_pins.sh, check_file_lengths.sh, check_reason_tokens.sh, check_workflow_assertions.py pass locally.

Intersections

New rule or feature Invariant it crosses Test at the crossing
name and purl use the short name A variant is its own component, beside the unpatched package The two zlib rows of e2e 1016
The same An entry with no namespace (a git dependency) is its own short name The acme.tool row of e2e 1016, the last-but-one case of the unit test
Purl segments are percent-encoded The version field reads as the lock recorded it The 11.0.0+7 and root 0.1.0+7 assertions of e2e 1016
One component per purl Two versions of one package stay two components none: the purl includes the version, so they differ; no test holds two versions
short_name moves to lock_io The lock's identity and --locked's comparison Unchanged callers; --locked drift in e2e 333

Compatibility

A project whose lock spells a package with its namespace, or whose versions carry +, sees different name, bom-ref and purl values in its bill of materials. A consumer that stored the old doubled purls will not match the new ones. mcpp.lock itself, cache keys and every other command are unchanged.

Checks before merging

  • bash .github/tools/check_docs_style.sh, check_docs_structure.sh and check_version_pins.sh pass.
  • python3 .github/tools/check_workflow_assertions.py passes (a step asserts what its name says).
  • No commit on the branch carries an attribution trailer:
    git log origin/main..HEAD -i --grep='Co-Authored-By' prints nothing.
  • The squash merge is given an explicit subject and body, so GitHub does not
    compose one from the branch's commits. Suggested subject: fix: a component of emit sbom states the package, not the lock's spelling of it.

…elling of it

The bill of materials copied a lock entry's name into the component's name,
bom-ref and purl, and that name is a spelling: the map key of the declaring
manifest, or a qualified name for a package no manifest in view declares.
`mcpplibs.cmdline` became pkg:mcpp/mcpplibs/mcpplibs.cmdline@0.0.1 while
`cmdline` gave pkg:mcpp/mcpplibs/cmdline@0.0.1 for the same package, and
compat.zlib became pkg:mcpp/compat/compat.zlib@1.3.2. A purl is what
correlates a component with an advisory feed, so one package needs one.

records.cpp already knew the identity through lock_short_name. It moves to
mcpp.pm.lock_io as short_name so the two readers share it; the lock's text
and the comparisons records.cpp makes do not change.

A purl also percent-encodes its segments now, so `+` in a version's build
metadata or in a variant's `+p<digest>` is written %2B. The `version` field
stays as recorded. A variant keeps its digest in the name, since a patched
library is another library.

A lock written by 2026.9.29.1 to .5 can hold one identity under two
spellings. Those would now share a bom-ref, which is unique in a document,
so they give one component.

Tests: tests/e2e/1016 writes the lock by hand and needs no toolchain or
network. PmLockIo.TheShortNameIsTheSameWhicheverWayTheManifestSpelledIt
covers the function. docs/51 states the rule in both languages.

Closes mcpp-community#892

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: emit sbom states the lock's spelling of a package, so one package has several purls

1 participant