Skip to content

Extract MintApiTokenTask/SetupServiceAccountTask logic into branch-neutral services #65

Description

@jsirish

Summary

src/Tasks/MintApiTokenTask.php and src/Tasks/SetupServiceAccountTask.php are the only permanent
divergence between branch 1 (SS6, BuildTask::execute(InputInterface, PolyOutput): int) and ss5
(SS5.2, legacy BuildTask::run($request): void). Both files on ss5 carry a docblock: "there is no
shared entry point between the two branches, so a business-logic fix here must be manually ported to
branch 1's copy and vice versa."

This already cost something: when ss5 was created, tests/Tasks/SetupServiceAccountTaskTest.php
had to be rewritten around the new signature, and in the process lost its Symfony Command::SUCCESS/
FAILURE/INVALID exit-code assertions — replaced with output-string + state assertions
(assertStringContainsString('refusing to guess', $output)), which are coupled to message wording
and would silently pass through a future reword that changed the actual behavior.

Proposal

Extract the provisioning/minting logic into branch-neutral service classes, e.g.:

  • src/Tasks/Support/ServiceAccountProvisioner.phpprovision(string $groupTitle, bool $populate): TaskResult
  • src/Tasks/Support/ApiTokenMinter.phpmint(string $email): TaskResult
  • src/Tasks/Support/TaskResult.php — a small value object: Status $status (Success|Invalid|Failure
    enum, identical on both branches) + array<int,string> $lines

Each branch's BuildTask subclass collapses to a thin (~25-line) adapter whose only per-branch
content is the entry-point signature and the render loop (looping $lines into $output->writeln()

  • mapping Status to Command::* on branch 1; echoing $lines on ss5). The test file becomes
    byte-identical across branches, asserting on TaskResult::$status (a real enum) instead of parsing
    output text, and every future sync touches ~25 lines of adapter instead of ~180 lines of duplicated
    logic.

Considered and rejected: a single file with a class_exists(PolyOutput::class) conditional branch —
unanalyzable by PHPStan on whichever branch lacks the class, permanently-unreachable branches in
coverage, and costs more than the ~25 lines of divergence it would save.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions