Skip to content

[license] Handle missing composer license without TypeError #227

@coisa

Description

@coisa

Problem

Installing or running DevTools inside the composer-plugin consumer fixture exposed a fatal error in the LICENSE generation flow when the consumer composer.json does not declare a license field:

In Resolver.php line 54:

  [TypeError]
  FastForward\DevTools\License\Resolver::resolve(): Argument #1 ($license) must be of type string, null given, called in /Users/mentordosnerds/Sites/github.com/php-fast-forward/dev-tools/src/License/Generator.php on line 87

Generator::generateContent() passes ComposerJsonInterface::getLicense() directly into ResolverInterface::resolve(). The resolver requires a non-null string, so repositories without a license field crash before the generator can follow its documented “unsupported or unavailable license returns null” behavior.

Expected Behavior

A missing license in the consumer composer.json MUST be handled as a non-generatable LICENSE state, not as a fatal TypeError.

The license command and any sync/install flow that triggers LICENSE generation SHOULD:

  • return null / skip generation when composer.json has no license field;
  • avoid writing a LICENSE file when no supported license can be resolved;
  • emit clear command feedback when the command surface needs to explain why generation was skipped;
  • preserve the current behavior for supported and unsupported non-null license strings.

Implementation Notes

Likely areas to inspect:

  • src/License/Generator.php
  • src/License/Resolver.php
  • src/License/ResolverInterface.php
  • src/Console/Command/LicenseCommand.php
  • tests/License/GeneratorTest.php
  • tests/License/ResolverTest.php
  • tests/Console/Command/LicenseCommandTest.php
  • tests/Fixtures/composer-plugin-consumer/composer.json

Possible approaches:

  • Guard Generator::generateContent() before calling the resolver when ComposerJsonInterface::getLicense() returns null or an empty string.
  • Alternatively, update the resolver contract to accept ?string and normalize missing/empty values to null resolution.

The implementation SHOULD choose the contract that keeps responsibilities clearest and avoids spreading null checks across command orchestration.

Acceptance Criteria

  • A consumer project without license in composer.json no longer crashes when composer dev-tools license or the relevant sync/install flow runs.
  • Missing or empty license metadata produces no LICENSE output and returns a controlled skip/null result.
  • Unsupported string license identifiers still return a controlled skip/null result.
  • Supported license identifiers continue generating the expected content.
  • PHPUnit coverage includes at least one missing-license regression case.
  • The composer-plugin consumer fixture can be used to reproduce the no-license path without a fatal TypeError.
  • CHANGELOG.md documents the bug fix.

Non-Goals

  • Do not infer a default license for consumers.
  • Do not change supported license template mappings unless needed for this bug.
  • Do not broaden this issue into a full license metadata redesign.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Released

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions