Skip to content

Commit c689885

Browse files
authored
Merge pull request #26 from Metadrop/feature/optimized-update-list
Issue #25: Optimize update list so not all direct packages are being updated
2 parents fd5174e + 60ea2db commit c689885

File tree

1 file changed

+118
-8
lines changed

1 file changed

+118
-8
lines changed

src/UpdaterCommand.php

Lines changed: 118 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -271,22 +271,132 @@ protected function consolidateConfiguration() {
271271
*/
272272
protected function checkPackages() {
273273
if ($this->onlySecurity) {
274-
$packages_to_update = $this->runCommand(sprintf('composer audit --locked %s --format plain 2>&1 | grep ^Package | cut -f2 -d: | sort -u', $this->getNoDevParameter()))->getOutput();
274+
$package_list = $this
275+
->runCommand(sprintf('composer audit --locked %s --format plain 2>&1 | grep ^Package | cut -f2 -d: | sort -u', $this->getNoDevParameter()))
276+
->getOutput();
275277
}
276278
else {
277-
$packages_to_update = $this->runCommand(sprintf('composer show --locked --direct --name-only %s 2>/dev/null', $this->getNoDevParameter()))
279+
$package_list = $this
280+
->runCommand(sprintf('composer show --locked --outdated --name-only %s 2>/dev/null', $this->getNoDevParameter()))
278281
->getOutput();
279282
}
283+
$package_list_massaged = $this->massagePackageList($package_list);
284+
$this->packagesToUpdate = $this->findDirectPackagesFromList($package_list_massaged);
285+
286+
$this->output->writeln(implode("\n", $this->packagesToUpdate));
287+
}
288+
289+
/**
290+
* Given a list of packages , find its direct packages.
291+
*
292+
* @see UpdaterCommand::findDirectPackage()
293+
*
294+
* @param array $packages_list
295+
* Packages list.
296+
* @return array
297+
* List of direct packages.
298+
*/
299+
protected function findDirectPackagesFromList(array $packages_list) {
300+
if (empty($packages_list)) {
301+
return [];
302+
}
303+
304+
$direct_packages = $this->massagePackageList($this
305+
->runCommand(sprintf('composer show --locked --direct --name-only %s 2>/dev/null', $this->getNoDevParameter()))
306+
->getOutput());
307+
308+
$direct_packages_found = array_intersect($packages_list, $direct_packages);
309+
310+
$not_direct_packages = array_diff($packages_list, $direct_packages);
311+
312+
foreach ($not_direct_packages as $package) {
313+
$direct_packages_found[] = $this->findDirectPackage($package, $direct_packages);
314+
}
315+
316+
return array_unique($direct_packages_found);
317+
}
318+
319+
/**
320+
* Find the direct package for a specific not direct dependency.
321+
*
322+
* If no direct package is found, consider the self package as direct. It is a extreme use
323+
* case where a too deep dependency is not found. For module convenience, it is needed to consider
324+
* package as direct so it can be updated.
325+
*
326+
* @param string $package
327+
* Package.
328+
* @param array $direct_packages
329+
* List of direct packages.
330+
*
331+
* @return string
332+
* The direct package.
333+
*/
334+
protected function findDirectPackage(string $package, array $direct_packages) {
335+
$composer_why_recursive_timeout = 2;
336+
$commands = [
337+
sprintf("composer why %s --locked | awk '{print $1}'", $package),
338+
sprintf("timeout %s composer why %s --locked -r | awk '{print $1}'", $composer_why_recursive_timeout, $package),
339+
];
340+
341+
foreach ($commands as $command) {
342+
$direct_package = $this->findPackageInPackageListCommand($command, $direct_packages);
343+
if (!empty($direct_package)) {
344+
return $direct_package;
345+
}
346+
}
347+
348+
return $package;
349+
}
350+
351+
/**
352+
* Finds a package from a command that is present in a package list command.
353+
*
354+
* This is used to get direct apckages from not direct packages.
355+
*
356+
* @see UpdaterCommand::findDirectPackage()
357+
*
358+
* @param string $command
359+
* List that return packages list.
360+
* @param array $package_list
361+
* List of packages we want to look for.
362+
*
363+
* @return string|null
364+
* FIrst package from command that is present in package list.
365+
*/
366+
protected function findPackageInPackageListCommand(string $command, array $package_list) {
367+
$package_list_output = array_filter(explode("\n", (string) trim($this->runCommand($command)
368+
->getOutput())));
369+
foreach ($package_list_output as $package) {
370+
if (in_array($package, $package_list)) {
371+
return $package;
372+
}
373+
}
280374

281-
$this->packagesToUpdate = explode("\n", $packages_to_update);
282-
$this->packagesToUpdate = array_map(function ($package) {
375+
return null;
376+
}
377+
378+
/**
379+
* Masssages a list of packages.
380+
*
381+
* It converts a package list bash output into a package list array,
382+
* removing any element that is not a package and removing spaces.
383+
*
384+
* @param string $package_list
385+
* List of packages coming from a bash command (s.e.: composer show --names-only).
386+
*
387+
* @return array
388+
* List of packages. Example:
389+
* - metadrop/drupal-updater
390+
* - metadrop/drupal-artifact-builder
391+
*/
392+
protected function massagePackageList(string $package_list) {
393+
$package_list = explode("\n", $package_list);
394+
$package_list = array_map(function ($package) {
283395
return trim($package);
284-
}, $this->packagesToUpdate);
285-
$this->packagesToUpdate = array_filter($this->packagesToUpdate, function ($package) {
396+
}, $package_list);
397+
return array_filter($package_list, function ($package) {
286398
return preg_match('/^([A-Za-z0-9_-]*\/[A-Za-z0-9_-]*)/', $package);
287399
});
288-
289-
$this->output->writeln(implode("\n", $this->packagesToUpdate));
290400
}
291401

292402
/**

0 commit comments

Comments
 (0)