Index

BuildTask

MMO\admin\tasks · 1 symbol · generated 5 Oct 2026

class

BuildTask

BuildTask.php:48

Turn a git commit into ONE immutable, versioned artifact: build/{name}-{version}-{commit}-{libraries}.tar.gz plus its sha256.

class MMO\admin\tasks\BuildTask extends MMO\admin\classes\aDeployTask
Namespace
MMO\admin\tasks
Extends
aDeployTask
This replaces the three-branch dance the previous script performed for every client (merge `develop` into `clients/x`, build the phars, commit, push, shuffle `packages/` and `www/bin` into `_prod` folders, merge into `prod/x`, `git rm --cached` the dev-only paths, commit, push). That dance existed only because the deploy step was a `git pull` on the server, so the production tree had to BE a branch. Once a release is an artifact, none of it is needed -- and neither are the `prod` branches: - nothing mutates your working tree except a `git archive` to a scratch folder, so a failure half-way leaves you exactly where you were, instead of on an arbitrary branch with a half-merged index; - the same artifact is shipped to every target that shares a commit, so two clients on one version are bit-for-bit identical, verified by checksum rather than by hoping two merges produced the same tree; - the build is a pure function of the commit, so re-running it is free and harmless. **The artifact is identified by the COMMIT, not just the version.** It used to be `(name, version, ref)`, which meant that forgetting to bump `mmo.json` made [`isNeeded`](https://mcp.mmoframework.fr/documentation/mmo.admin/php/tasks/BuildTask.md#buildtask-isneeded) answer "already built" and silently re-ship the previous code as though it were the new release -- a wrong deployment that looked exactly like a successful one. A commit sha cannot be forgotten to be bumped. The one precondition is explicit rather than assumed: the working tree must be clean AND already at $ref, because the `.phar` dependencies are built from the working tree while the rest of the tree comes from the commit. [`assertWorkingTree`](https://mcp.mmoframework.fr/documentation/mmo.admin/php/tasks/BuildTask.md#buildtask-assertworkingtree) refuses to build otherwise. **The libraries are built from their folders, not from a commit.** Each `.phar` is packaged from the library as it is on disk -- whatever branch its checkout is on, uncommitted changes included. So each one is checked like the project ([`checkLibraries`](https://mcp.mmoframework.fr/documentation/mmo.admin/php/tasks/BuildTask.md#buildtask-checklibraries)): uncommitted changes stop the build unless `--force` says otherwise, or the person at the terminal does; and each library's commit and branch go into the journal ([`Context::$libraries`](https://mcp.mmoframework.fr/documentation/mmo.admin/php/helpers/Context.md#context-libraries)), so `status` and `history` say which code of every library is live, not only which version number. **The libraries are part of what identifies the artifact.** Its name carries a fingerprint of the commit each one is on ([`getLibrariesFingerprint`](https://mcp.mmoframework.fr/documentation/mmo.admin/php/tasks/BuildTask.md#buildtask-getlibrariesfingerprint)): a commit in a library, and none in the project, used to find the artifact of the project's commit already built and ship it again -- the library's change never reached a release, and every step said there was nothing to do. A library with uncommitted changes has no fingerprint: what its phar holds is no commit, so an artifact built from it is never reused.

Index

buildFolder
buildOptions
built
commands
dirtyAccepted
exclude
fingerprints
git
include
manifest
apply($context)
assertWorkingTree($ref, $commit, $force)Refuse to build from a working tree that does not match what is being exported.
checkLibraries($context, $manifest)Refuse to package a library with uncommitted changes -- unless told to go on.
describeLibraries($libraries)
getArtifactFilename($manifest, $commit, $libraries)
getCommit($ref)
getDescription()
getIncludes()The packages the application includes (Application::getIncludes), as name => version.
getLibraries($manifest)The libraries a release packages, and the state of each one's checkout.
getLibrariesFingerprint($manifest)What the libraries a release packages are built from, in a few characters: the commit of each one, or its version when it is no git checkout (a phar, a plain folder).
getManifest()
isNeeded($context)An artifact for this (version, commit) that already exists is reused as-is -- which is the point of building a deterministic artifact in the first place.
key($commit, $version, $libraries)
librariesFilename($artifact)
loopOverlay($path)
overlay($context, $staging)Copy the generated (untracked) assets into the exported tree, and hash what was copied.
plan($context)
prime($context)Fill in what the rest of the pipeline needs to describe itself: the version and commit being deployed, and the artifact if one already exists.
readCheckout($folder)
readLibraries($artifact)
remember($key, $context)
verify($context)
__construct($git, $manifest, $buildFolder, $exclude, $include, $commands, $buildOptions)

Properties10

buildFolder

protected string $buildFolder
Type
string

buildOptions

protected array $buildOptions
Type
array

built

protected array $built = []
Type
array

commands

protected array $commands
Type
array

dirtyAccepted

protected ?bool $dirtyAccepted = null
Type
null | bool

exclude

protected array $exclude
Type
array

fingerprints

protected WeakMap $fingerprints
Type
WeakMap

git

protected MMO\git\helpers\Repository $git
Type
Repository

include

protected array $include
Type
array

manifest

protected ?string $manifest
Type
null | string

Methods23

assertWorkingTree($ref, $commit, $force)

BuildTask.php:367

Refuse to build from a working tree that does not match what is being exported.

protected function assertWorkingTree(string $ref, string $commit, bool $force = false): void
$refstring
$commitstring
$force = falsebool
returnsvoid
throws

checkLibraries($context, $manifest)

BuildTask.php:474

Refuse to package a library with uncommitted changes -- unless told to go on.

protected function checkLibraries( MMO\admin\helpers\Context $context, MMO\core\helpers\PackageManifest $manifest ): arrayIts phar is built from its folder as it is, so what it ships would match no commit, and nobody could tell later what is running. The same rule as the project's own working tree ([`assertWorkingTree`](https://mcp.mmoframework.fr/documentation/mmo.admin/php/tasks/BuildTask.md#buildtask-assertworkingtree)), with a way through that does not stop the whole system: `--force`, or -- at a terminal -- a yes to the question, asked once per run. A script without `--force` is refused, as it cannot be asked.
$contextContext
$manifestPackageManifestthe project's.
returnsarraywhat the journal records of each library: {version, commit, branch, dirty}.
throwswhen a library has uncommitted changes and nobody said to go on.

describeLibraries($libraries)

BuildTask.php:507
protected function describeLibraries(array $libraries): array
$librariesarray
returnsarraythe lines of the plan saying what each library is built from.

getArtifactFilename($manifest, $commit, $libraries)

BuildTask.php:111
function getArtifactFilename( MMO\core\helpers\PackageManifest $manifest, string $commit, ?string $libraries = '' ): string
$manifestPackageManifest
$commitstringthe project's
$libraries = ''null | stringgetLibrariesFingerprint ('' for none, null when a library has uncommitted changes)
returnsstringthe artifact path for a given version, commit and libraries.

getCommit($ref)

BuildTask.php:96
function getCommit(string $ref): string
$refstring
returnsstringthe full sha of the commit $ref points at.
throwswhen the ref does not exist.

getIncludes()

BuildTask.php:422

The packages the application includes (Application::getIncludes), as name => version.

protected function getIncludes(): arrayAn include is written either way -- `['mmo.doc' => '*']` or `['mmo.doc']` -- and \MMO\inc() reads both; the package manager's buildProject() reads a map only, and took a list's `0 => 'mmo.doc'` for a package named `0`: a project listing its includes by name alone could not be built at all.
returnsarraystring>

getLibraries($manifest)

BuildTask.php:398

The libraries a release packages, and the state of each one's checkout.

function getLibraries(MMO\core\helpers\PackageManifest $manifest): arrayThe project's dependencies and the application's includes ([`Application::getIncludes`](https://mcp.mmoframework.fr/documentation/mmo.core/php/Application.md#application-getincludes)), as the package manager resolves them -- the same list buildProject builds -- minus the project itself.
$manifestPackageManifestthe project's.
returnsarrayname => {version, folder, commit, branch, dirty, changes}

getLibrariesFingerprint($manifest)

BuildTask.php:125

What the libraries a release packages are built from, in a few characters: the commit of each one, or its version when it is no git checkout (a phar, a plain folder). Two builds of one project commit with the same fingerprint package the same code.

function getLibrariesFingerprint(MMO\core\helpers\PackageManifest $manifest): ?string
$manifestPackageManifestthe project's.
returnsnull | string12 hex characters ('' when the project packages no library), null when a library has uncommitted changes: its phar then holds code no commit names, and nothing built from it may be reused.
$task->getLibrariesFingerprint($manifest); // '3f2a9c0d1e4b'

isNeeded($context)

BuildTask.php:167

An artifact for this (version, commit) that already exists is reused as-is -- which is the point of building a deterministic artifact in the first place. prime still runs, so the rest of the pipeline knows the version and the checksum even when there is nothing to build.

function isNeeded(MMO\admin\helpers\Context $context): bool
$contextContext
returnsbool

librariesFilename($artifact)

BuildTask.php:518
protected function librariesFilename(string $artifact): string
$artifactstring
returnsstringwhere an artifact's libraries are recorded, beside it: a reused artifact still knows them.

loopOverlay($path)

BuildTask.php:324
protected function loopOverlay(string $path): Generator
$pathstring
returnsGeneratorevery file under $path (or $path itself when it is one), in a stable order.

overlay($context, $staging)

BuildTask.php:283

Copy the generated (untracked) assets into the exported tree, and hash what was copied.

protected function overlay(MMO\admin\helpers\Context $context, string $staging): ?stringThese are the one part of a release that does NOT come from the commit: they are whatever is in the working tree right now, and `git status --porcelain` cannot see them because they are gitignored. The hash goes into the journal so two releases built from one commit but different assets are distinguishable after the fact, and an overlay older than the commit it ships with is called out -- that is what a forgotten asset build looks like.
$contextContext
$stagingstring
returnsnull | stringsha256 over every overlaid file, or null when there is no overlay.

prime($context)

BuildTask.php:146

Fill in what the rest of the pipeline needs to describe itself: the version and commit being deployed, and the artifact if one already exists. Called by BOTH plan and apply, so a dry run's plan shows the real release name instead of a blank -- and the later tasks can tell whether they would have anything to do at all.

protected function prime(MMO\admin\helpers\Context $context): MMO\core\helpers\PackageManifest
$contextContext
returnsPackageManifest

readCheckout($folder)

BuildTask.php:437
protected function readCheckout(?string $folder): array
$foldernull | stringa library's folder.
returnsarraystring|null, commit: string|null, branch: string|null, dirty: int, changes: string[]} the commit and branch it is on (null when it is no git checkout, or a phar), and its uncommitted changes -- those under build/ aside: that is where its own phar is written, and some libraries do not ignore it.

readLibraries($artifact)

BuildTask.php:521
protected function readLibraries(string $artifact): array
$artifactstring
returnsarraythe libraries recorded beside $artifact, [] when it was built before they were.

__construct($git, $manifest, $buildFolder, $exclude, $include, $commands, $buildOptions)

BuildTask.php:58
function __construct( MMO\git\helpers\Repository $git, ?string $manifest = null, string $buildFolder = '{root}/build', array $exclude = [], array $include = [], array $commands = [], array $buildOptions = [] )
$gitRepository
$manifest = nullnull | string
$buildFolder = '{root}/build'string
$exclude = []array
$include = []array
$commands = []array
$buildOptions = []array
Inherited members (38)
ConstantsDeclared byDescription
ALERTiLogLevelAlert: Action must be taken immediately.
ALLiLogLevelMask for all
ALL_PLUSiLogLeveldebug everything
BACKTRACEiLogLevel
CRITICALiLogLevelCritical: Critical conditions.
DEBUGiLogLevelDebug: Detailed debug information.
DEEPiLogLeveldebug everything with arguments
EMERGENCYiLogLevelEmergency: System is unusable.
ERRORiLogLevelError: Runtime errors that do not require immediate action but should typically be logged and monitored.
ERROR_PLUSiLogLevelMask for all error
FATALiLogLevelMask for all fatal error
INFOiLogLevelInfo: Interesting events.
INFO_PLUSiLogLevelMask for all errors/warning/notices/info
MEMORYiLogLevelinclude backtrace object
NOTICEiLogLevelNotice: Normal but significant events.
NOTICE_PLUSiLogLevelMask for all errors/warning/notices/info
TRACEiLogLevelinclude backtrace as origins
WARNINGiLogLevelWarning: Exceptional occurrences that are not errors.
WARNING_PLUSiLogLevelMask for all errors and warnings
PropertiesDeclared byDescription
verbosityaDeployTask
MethodsDeclared byDescription
alert($message, $context)aDeployTaskAction must be taken immediately.
backtraces($message, $context)aDeployTaskForce-log a message at INFO level together with a full stack trace/memory snapshot (iLogLevel::BACKTRACE), bypassing the verbosity mask entirely (any non-zero verbosity is enough to trigger it).
critical($message, $context)aDeployTaskCritical conditions. No-op if CRITICAL is currently filtered out by the verbosity mask.
debug($message, $context)aDeployTaskDetailed debug information.
debugName()aDeployTask
emergency($message, $context)aDeployTaskSystem is unusable. No-op if EMERGENCY is currently filtered out by the verbosity mask.
error($message, $context)aDeployTaskRuntime errors that do not require immediate action but should typically be logged and monitored.
getName()aDeployTask
getVerbosity()aDeployTask
info($message, $context)aDeployTaskInteresting events. No-op if INFO is currently filtered out by the verbosity mask.
isChange()aDeployTaskWhether running this task CHANGES anything.
isRollbackCritical()aDeployTask
log($level, $message, $context)aDeployTaskLogs with an arbitrary level.
notice($message, $context)aDeployTaskNormal but significant events.
rollback($context)aDeployTaskUndo this task. Called by the runner, in reverse order, for every task already applied when a later one failed.
setVerbosity($verb)aDeployTaskSet the current verbosity mask, replacing whatever it was before.
testVerbosity($level)aDeployTaskTest whether a given level (or set of levels) is currently enabled by this instance's verbosity mask.
warning($message, $context)aDeployTaskExceptional occurrences that are not errors.