\Drupal\strata\Code CodeCapture

Captures the code a site owns, and the reference that stands in for the code it does not.

Runs on cron rather than on a hook, because code changes when someone deploys and a deploy is not an event Drupal dispatches. A pass compares the site's own code against the digest recorded last time and does nothing when nothing moved, so the ordinary case costs one walk and no writes.

Three things are captured and they are not the same kind of thing:

  • Own modules, themes and profiles, as bytes. 762 KiB compressed for a real module, and with delta coding across 150 deploys a year the whole realm is 8.98 MiB annually.
  • settings.php, redacted. The configuration a restore needs, without the credentials that must not leave the server.
  • Lockfiles, as the reference that reproduces vendor/. 41 KiB of composer.lock against 18 MB of compressed core bytes, and it reproduces the tree exactly rather than approximately.

And one thing is watched rather than captured: the vendor tree's fingerprint. When it moves without the lock moving, a dependency was patched by hand, the lock no longer reproduces the tree, and the drifted files are captured as bytes. That check is the only thing standing between the 440x saving and a backup silently missing a patch.

Summary

Methods
Properties
Constants
__construct
capture
measure
acceptVendorState
No public properties found
DIGEST_KEY
FINGERPRINT_KEY
DESCRIPTIONS_KEY
LOCK_KEY
No protected methods found
No protected properties found
No protected constants found
captureOwnCode
captureLockfiles
captureDrifted
append
checkVendor
scanner
vendor
journal
scope
state
tripwires
ledger
logger
No private constants found

Constant

DIGEST_KEY

DIGEST_KEY = 'strata.code.digest'

State key holding the digest of the site's own code at the last pass.

FINGERPRINT_KEY

FINGERPRINT_KEY = 'strata.code.vendor_fingerprint'

State key holding the vendor fingerprint recorded with the current lock.

DESCRIPTIONS_KEY

DESCRIPTIONS_KEY = 'strata.code.vendor_files'

State key holding the per-file descriptions the fingerprint was built from.

LOCK_KEY

LOCK_KEY = 'strata.code.lock_digest'

State key holding the digest of `composer.lock` at the last pass.

Properties

$scanner

$scanner : \Drupal\strata\Code\CodeScanner

Type

CodeScanner

$journal

$journal : \Drupal\strata\Journal\JournalInterface

Type

JournalInterface

$scope

$scope : \Drupal\strata\Capture\CaptureScope

Type

CaptureScope

$state

$state : \Drupal\Core\State\StateInterface

Type

StateInterface

$tripwires

$tripwires : \Drupal\strata\Health\TripwireRegistry

Type

TripwireRegistry

$logger

$logger : \Psr\Log\LoggerInterface

Type

LoggerInterface

Methods

__construct()

__construct(\Drupal\strata\Code\CodeScanner  $scanner, \Drupal\strata\Code\VendorDriftDetector  $vendor, \Drupal\strata\Journal\JournalInterface  $journal, \Drupal\strata\Capture\CaptureScope  $scope, \Drupal\Core\State\StateInterface  $state, \Drupal\strata\Health\TripwireRegistry  $tripwires, \Drupal\strata\Health\HealthLedgerInterface  $ledger, \Psr\Log\LoggerInterface  $logger) : mixed

Constructs a capture.

Parameters

\Drupal\strata\Code\CodeScanner $scanner

Walks the site's own code.

\Drupal\strata\Code\VendorDriftDetector $vendor

Watches the dependency tree.

\Drupal\strata\Journal\JournalInterface $journal

Where operations are appended.

\Drupal\strata\Capture\CaptureScope $scope

Decides whether the code realm is captured.

\Drupal\Core\State\StateInterface $state

Remembers what the last pass saw.

\Drupal\strata\Health\TripwireRegistry $tripwires

Runs the drift check.

\Drupal\strata\Health\HealthLedgerInterface $ledger

Where drift is recorded.

\Psr\Log\LoggerInterface $logger

Records what a pass did.

Returns

mixed —

capture()

capture(bool  $force = false) : \Drupal\strata\Code\CodeReport

Captures the code realm if anything changed.

Parameters

bool $force

TRUE to capture whether or not the digest moved, which an export and a first run want.

Returns

\Drupal\strata\Code\CodeReport —

What the pass did.

measure()

measure() : array{files: int, bytes: int, redacted: int, lockfiles: int, drifted: bool, truncated: bool}

What a capture would cost, without writing anything.

Returns

array{files: int, bytes: int, redacted: int, lockfiles: int, drifted: bool, truncated: bool} —

The measurement.

acceptVendorState()

acceptVendorState() : void

Records the vendor tree as it stands, so later passes have something to compare against.

Called after a deploy, and by the first pass on a site. Taking the fingerprint and the lock digest together is what makes "the tree moved but the lock did not" a meaningful statement.

Returns

void —

captureOwnCode()

captureOwnCode() : array{files: int, bytes: int, redacted: int, problems: list<string>}

Appends one operation per code file that changed.

Returns

array{files: int, bytes: int, redacted: int, problems: list} —

What was appended.

captureLockfiles()

captureLockfiles() : int

Appends one operation per lockfile.

The lock's own bytes are stored, not just its digest. A digest identifies a lock; only the contents reproduce a tree, and reproducing the tree is the entire reason vendor/ is not stored.

Returns

int —

How many lockfiles were captured.

captureDrifted()

captureDrifted(array{drifted: bool, reason: string, lockChanged: bool, fingerprint: string}  $drift) : int

Appends the files a patched dependency changed.

Only the drifted files, which is what makes a patched dependency cost the patch rather than the tree.

Parameters

array{drifted: bool, reason: string, lockChanged: bool, fingerprint: string} $drift

What the comparison found.

Returns

int —

How many files were captured.

append()

append(string  $path, string  $contents, string  $label) : void

Appends one code operation.

Parameters

string $path

Path relative to the project root, which is the subject.

string $contents

What to store.

string $label

What the timeline shows.

Returns

void —

checkVendor()

checkVendor() : array{drifted: bool, reason: string, lockChanged: bool, fingerprint: string}

Compares the vendor tree against what was recorded, and records drift.

Returns

array{drifted: bool, reason: string, lockChanged: bool, fingerprint: string} —

What the comparison found.