Skip to content
Snippets Groups Projects
Commit 39bf8c10 authored by Lucas Hedding's avatar Lucas Hedding Committed by Lucas Hedding
Browse files

Issue #3054002 by heddn, catch: Parse project name and version from composer.json

parent e375b0fe
No related branches found
No related tags found
No related merge requests found
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
"type": "drupal-module", "type": "drupal-module",
"description": "Drupal Automatic Updates", "description": "Drupal Automatic Updates",
"keywords": ["Drupal"], "keywords": ["Drupal"],
"license": "GPL-2.0+", "license": "GPL-2.0-or-later",
"homepage": "https://www.drupal.org/project/automatic_updates", "homepage": "https://www.drupal.org/project/automatic_updates",
"minimum-stability": "dev", "minimum-stability": "dev",
"support": { "support": {
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
"require": { "require": {
"ext-json": "*", "ext-json": "*",
"composer/semver": "^1.0", "composer/semver": "^1.0",
"ocramius/package-versions": "^1.4",
"webflo/drupal-finder": "^1.1" "webflo/drupal-finder": "^1.1"
},
"require-dev": {
"drupal/ctools": "3.2.0"
} }
} }
...@@ -10,6 +10,8 @@ build: ...@@ -10,6 +10,8 @@ build:
sniff-all-files: true sniff-all-files: true
halt-on-fail: true halt-on-fail: true
testing: testing:
container_command:
commands: "cd ${SOURCE_DIR} && sudo -u www-data composer require drupal/ctools:3.2.0 --prefer-source --optimize-autoloader"
# run_tests task is executed several times in order of performance speeds. # run_tests task is executed several times in order of performance speeds.
# halt-on-fail can be set on the run_tests tasks in order to fail fast. # halt-on-fail can be set on the run_tests tasks in order to fail fast.
# suppress-deprecations is false in order to be alerted to usages of # suppress-deprecations is false in order to be alerted to usages of
......
<?php
namespace Drupal\automatic_updates;
use PackageVersions\Versions;
/**
* Provide a helper to get project info.
*/
trait ProjectInfoTrait {
/**
* Get the extension version.
*
* @param string $extension_name
* The extension name.
* @param array $info
* The extension's info.
*
* @return string|null
* The version or NULL if undefined.
*/
protected function getExtensionVersion($extension_name, array $info) {
if (isset($info['version']) && strpos($info['version'], '-dev') === FALSE) {
return $info['version'];
}
$composer_json = $this->getComposerJson($extension_name, $info);
$extension_name = isset($composer_json['name']) ? $composer_json['name'] : $extension_name;
try {
$version = Versions::getVersion($extension_name);
$version = $this->getSuffix($version, '@', $version);
// If we do not have a core compatibility tagged git branch, we're
// dealing with a dev-master branch that cannot be updated in place.
return substr($version, 0, 3) === \Drupal::CORE_COMPATIBILITY ? $version : NULL;
}
catch (\OutOfBoundsException $exception) {
\Drupal::logger('automatic_updates')->error('Version cannot be located for @extension', ['@extension' => $extension_name]);
}
}
/**
* Get the extension's project name.
*
* @param string $extension_name
* The extension name.
* @param array $info
* The extension's info.
*
* @return string
* The project name or fallback to extension name if project is undefined.
*/
protected function getProjectName($extension_name, array $info) {
$project_name = $extension_name;
if (isset($info['project'])) {
$project_name = $info['project'];
}
elseif ($composer_json = $this->getComposerJson($extension_name, $info)) {
if (isset($composer_json['name'])) {
$project_name = $this->getSuffix($composer_json['name'], '/', $extension_name);
}
}
return $project_name;
}
/**
* Get string suffix.
*
* @param string $string
* The string to parse.
* @param string $needle
* The needle.
* @param string $default
* The default value.
*
* @return string
* The sub string.
*/
protected function getSuffix($string, $needle, $default) {
$pos = strrpos($string, $needle);
return $pos === FALSE ? $default : substr($string, ++$pos);
}
/**
* Get the composer.json as a JSON array.
*
* @param string $extension_name
* The extension name.
* @param array $info
* The extension's info.
*
* @return array|null
* The composer.json as an array or NULL.
*/
protected function getComposerJson($extension_name, array $info) {
try {
if ($directory = drupal_get_path($info['type'], $extension_name)) {
$composer_json = $directory . DIRECTORY_SEPARATOR . 'composer.json';
if (file_exists($composer_json)) {
return json_decode(file_get_contents($composer_json), TRUE);
}
}
}
catch (\Throwable $exception) {
\Drupal::logger('automatic_updates')->error('Composer.json could not be located for @extension', ['@extension' => $extension_name]);
}
}
}
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace Drupal\automatic_updates\ReadinessChecker; namespace Drupal\automatic_updates\ReadinessChecker;
use Drupal\automatic_updates\IgnoredPathsTrait; use Drupal\automatic_updates\IgnoredPathsTrait;
use Drupal\automatic_updates\ProjectInfoTrait;
use Drupal\Core\Extension\ExtensionList; use Drupal\Core\Extension\ExtensionList;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
use DrupalFinder\DrupalFinder; use DrupalFinder\DrupalFinder;
...@@ -12,6 +13,7 @@ use DrupalFinder\DrupalFinder; ...@@ -12,6 +13,7 @@ use DrupalFinder\DrupalFinder;
*/ */
class MissingProjectInfo extends Filesystem { class MissingProjectInfo extends Filesystem {
use IgnoredPathsTrait; use IgnoredPathsTrait;
use ProjectInfoTrait;
use StringTranslationTrait; use StringTranslationTrait;
/** /**
...@@ -74,11 +76,8 @@ class MissingProjectInfo extends Filesystem { ...@@ -74,11 +76,8 @@ class MissingProjectInfo extends Filesystem {
if ($this->isIgnoredPath(drupal_get_path($info['type'], $extension_name))) { if ($this->isIgnoredPath(drupal_get_path($info['type'], $extension_name))) {
continue; continue;
} }
if (empty($info['version'])) { if (!$this->getExtensionVersion($extension_name, $info)) {
$messages[] = $this->t('The project "@extension" will not be updated because it is missing the "version" key in the @extension.info.yml file.', ['@extension' => $extension_name]); $messages[] = $this->t('The project "@extension" can not be updated because its version is either undefined or a dev release.', ['@extension' => $extension_name]);
}
if (empty($info['project'])) {
$messages[] = $this->t('The project "@extension" will not be updated because it is missing the "project" key in the @extension.info.yml file.', ['@extension' => $extension_name]);
} }
} }
} }
......
...@@ -83,7 +83,7 @@ class ModifiedFiles implements ReadinessCheckerInterface { ...@@ -83,7 +83,7 @@ class ModifiedFiles implements ReadinessCheckerInterface {
protected function modifiedFilesCheck() { protected function modifiedFilesCheck() {
$messages = []; $messages = [];
$extensions = []; $extensions = [];
$extensions['drupal'] = $this->modules->get('system')->info; $extensions['system'] = $this->modules->get('system')->info;
foreach ($this->getExtensionsTypes() as $extension_type) { foreach ($this->getExtensionsTypes() as $extension_type) {
foreach ($this->getInfos($extension_type) as $extension_name => $info) { foreach ($this->getInfos($extension_type) as $extension_name => $info) {
if (substr($this->getPath($extension_type, $extension_name), 0, 4) !== 'core') { if (substr($this->getPath($extension_type, $extension_name), 0, 4) !== 'core') {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
namespace Drupal\automatic_updates\Services; namespace Drupal\automatic_updates\Services;
use Drupal\automatic_updates\IgnoredPathsTrait; use Drupal\automatic_updates\IgnoredPathsTrait;
use Drupal\automatic_updates\ProjectInfoTrait;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Url; use Drupal\Core\Url;
use DrupalFinder\DrupalFinder; use DrupalFinder\DrupalFinder;
...@@ -16,6 +17,7 @@ use Psr\Log\LoggerInterface; ...@@ -16,6 +17,7 @@ use Psr\Log\LoggerInterface;
*/ */
class ModifiedFiles implements ModifiedFilesInterface { class ModifiedFiles implements ModifiedFilesInterface {
use IgnoredPathsTrait; use IgnoredPathsTrait;
use ProjectInfoTrait;
/** /**
* The logger. * The logger.
...@@ -173,43 +175,6 @@ class ModifiedFiles implements ModifiedFilesInterface { ...@@ -173,43 +175,6 @@ class ModifiedFiles implements ModifiedFilesInterface {
return Url::fromUri($uri . "/$project_name/$version/$hash_name")->toString(); return Url::fromUri($uri . "/$project_name/$version/$hash_name")->toString();
} }
/**
* Get the extension version.
*
* @param string $extension_name
* The extension name.
* @param array $info
* The extension's info.
*
* @return string|null
* The version or NULL if undefined.
*/
protected function getExtensionVersion($extension_name, array $info) {
$version = isset($info['version']) ? $info['version'] : NULL;
// TODO: consider using ocramius/package-versions to discover the installed
// version from composer.lock.
// See https://www.drupal.org/project/automatic_updates/issues/3054002
return $version;
}
/**
* Get the extension's project name.
*
* @param string $extension_name
* The extension name.
* @param array $info
* The extension's info.
*
* @return string
* The project name or fallback to extension name if project is undefined.
*/
protected function getProjectName($extension_name, array $info) {
$project_name = isset($info['project']) ? $info['project'] : $extension_name;
// TODO: parse the composer.json for the name if it isn't set in info.
// See https://www.drupal.org/project/automatic_updates/issues/3054002.
return $project_name;
}
/** /**
* Get the hash file name. * Get the hash file name.
* *
......
<?php
namespace Drupal\Tests\automatic_updates\Kernel;
use Drupal\automatic_updates\ProjectInfoTrait;
use Drupal\KernelTests\KernelTestBase;
/**
* @coversDefaultClass \Drupal\automatic_updates\ProjectInfoTrait
* @group automatic_updates
*/
class ProjectInfoTraitTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'automatic_updates',
];
/**
* @covers ::getExtensionVersion
* @covers ::getProjectName
* @dataProvider providerInfos
*/
public function testTrait($expected, $info, $extension_name) {
$class = new ProjectInfoTestClass();
$this->assertSame($expected['version'], $class->getExtensionVersion($extension_name, $info));
$this->assertSame($expected['project'], $class->getProjectName($extension_name, $info));
}
/**
* Data provider for testTrait.
*/
public function providerInfos() {
$infos['node']['expected'] = [
'version' => NULL,
'project' => 'drupal',
];
$infos['node']['info'] = [
'name' => 'Node',
'type' => 'module',
'description' => 'Allows content to be submitted to the site and displayed on pages.',
'package' => 'Core',
'version' => '8.8.x-dev',
'project' => 'drupal',
'core' => '8.x',
'configure' => 'entity.node_type.collection',
'dependencies' => ['drupal:text'],
];
$infos['node']['extension_name'] = 'node';
$infos['update']['expected'] = [
'version' => NULL,
'project' => 'drupal/update',
];
$infos['update']['info'] = [
'name' => 'Update manager',
'type' => 'module',
'description' => 'Checks for available updates, and can securely install or update modules and themes via a web interface.',
'package' => 'Core',
'core' => '8.x',
'configure' => 'update.settings',
'dependencies' => ['file'],
];
$infos['update']['extension_name'] = 'drupal/update';
$infos['system']['expected'] = [
'version' => '8.8.0',
'project' => 'drupal',
];
$infos['system']['info'] = [
'name' => 'System',
'type' => 'module',
'description' => 'Handles general site configuration for administrators.',
'package' => 'Core',
'version' => '8.8.0',
'project' => 'drupal',
'core' => '8.x',
'required' => 'true',
'configure' => 'system.admin_config_system',
'dependencies' => [],
];
$infos['system']['extension_name'] = 'system';
$infos['automatic_updates']['expected'] = [
'version' => NULL,
'project' => 'automatic_updates',
];
$infos['automatic_updates']['info'] = [
'name' => 'Automatic Updates',
'type' => 'module',
'description' => 'Display public service announcements and verify readiness for applying automatic updates to the site.',
'package' => 'Core',
'core' => '8.x',
'configure' => 'automatic_updates.settings',
'dependencies' => ['system', 'update'],
];
$infos['automatic_updates']['extension_name'] = 'automatic_updates';
// TODO: Investigate switching to this project after stable release in
// https://www.drupal.org/project/automatic_updates/issues/3061229.
$infos['ctools']['expected'] = [
'version' => '8.x-3.2',
'project' => 'ctools',
];
$infos['ctools']['info'] = [
'name' => 'Chaos tool suite',
'type' => 'module',
'description' => 'Provides a number of utility and helper APIs for Drupal developers and site builders.',
'package' => 'Core',
'core' => '8.x',
'dependencies' => ['system'],
];
$infos['ctools']['extension_name'] = 'ctools';
return $infos;
}
}
/**
* Class ProjectInfoTestClass.
*/
class ProjectInfoTestClass {
use ProjectInfoTrait {
getExtensionVersion as getVersion;
getProjectName as getProject;
}
/**
* {@inheritdoc}
*/
public function getExtensionVersion($extension_name, array $info) {
return $this->getVersion($extension_name, $info);
}
/**
* {@inheritdoc}
*/
public function getProjectName($extension_name, array $info) {
return $this->getProject($extension_name, $info);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment