Skip to content

Commit

Permalink
Handle symbolic links when updating the files
Browse files Browse the repository at this point in the history
  • Loading branch information
jolelievre committed Feb 18, 2025
1 parent 36e0315 commit 6535cec
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 13 deletions.
19 changes: 16 additions & 3 deletions classes/Progress/Backlog.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,28 @@ class Backlog
*/
private $backlog;

/**
* @var array<string, string> Indexed by relative path from root folder, value is the symbolic link target
*/
private $symbolicLinks;

/**
* @param mixed[] $backlog
* @param array<string, string> $symbolicLinks
*/
public function __construct(array $backlog, int $initialTotal)
public function __construct(array $backlog, int $initialTotal, array $symbolicLinks = [])
{
$this->backlog = $backlog;
$this->initialTotal = $initialTotal;
$this->symbolicLinks = $symbolicLinks;
}

/**
* @param array{'backlog':mixed[],'initialTotal':int} $contents
* @param array{'backlog':mixed[],'initialTotal':int, 'symbolicLinks':array<string, string>} $contents
*/
public static function fromContents($contents): self
{
return new self($contents['backlog'], $contents['initialTotal']);
return new self($contents['backlog'], $contents['initialTotal'], $contents['symbolicLinks'] ?? []);

Check failure on line 61 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.2.5)

Offset 'symbolicLinks' on array{backlog: array, initialTotal: int, symbolicLinks: array<string, string>} on left side of ?? always exists and is not nullable.

Check failure on line 61 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.2.5)

Offset 'symbolicLinks' on array{backlog: array, initialTotal: int, symbolicLinks: array<string, string>} on left side of ?? always exists and is not nullable.

Check failure on line 61 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.4.4)

Offset 'symbolicLinks' on array{backlog: array, initialTotal: int, symbolicLinks: array<string, string>} on left side of ?? always exists and is not nullable.

Check failure on line 61 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.3.4)

Offset 'symbolicLinks' on array{backlog: array, initialTotal: int, symbolicLinks: array<string, string>} on left side of ?? always exists and is not nullable.

Check failure on line 61 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.6)

Offset 'symbolicLinks' on array{backlog: array, initialTotal: int, symbolicLinks: array<string, string>} on left side of ?? always exists and is not nullable.

Check failure on line 61 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.5.1)

Offset 'symbolicLinks' on array{backlog: array, initialTotal: int, symbolicLinks: array<string, string>} on left side of ?? always exists and is not nullable.
}

/**
Expand All @@ -62,6 +69,7 @@ public function dump(): array
return [
'backlog' => $this->backlog,
'initialTotal' => $this->initialTotal,
'symbolicLinks' => $this->symbolicLinks,
];
}

Expand All @@ -82,4 +90,9 @@ public function getInitialTotal(): int
{
return $this->initialTotal;
}

public function getSymbolicLinks(): array

Check failure on line 94 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.2.5)

Method PrestaShop\Module\AutoUpgrade\Progress\Backlog::getSymbolicLinks() return type has no value type specified in iterable type array.

Check failure on line 94 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.2.5)

Method PrestaShop\Module\AutoUpgrade\Progress\Backlog::getSymbolicLinks() return type has no value type specified in iterable type array.

Check failure on line 94 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.4.4)

Method PrestaShop\Module\AutoUpgrade\Progress\Backlog::getSymbolicLinks() return type has no value type specified in iterable type array.

Check failure on line 94 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.3.4)

Method PrestaShop\Module\AutoUpgrade\Progress\Backlog::getSymbolicLinks() return type has no value type specified in iterable type array.

Check failure on line 94 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.6)

Method PrestaShop\Module\AutoUpgrade\Progress\Backlog::getSymbolicLinks() return type has no value type specified in iterable type array.

Check failure on line 94 in classes/Progress/Backlog.php

View workflow job for this annotation

GitHub Actions / PHPStan (1.7.5.1)

Method PrestaShop\Module\AutoUpgrade\Progress\Backlog::getSymbolicLinks() return type has no value type specified in iterable type array.
{
return $this->symbolicLinks;
}
}
43 changes: 33 additions & 10 deletions classes/Task/Update/UpdateFiles.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ public function run(): int
$file = $filesToUpgrade->getNext();

// Note - upgrade this file means do whatever is needed for that file to be in the final state, delete included.
if (!$this->upgradeThisFile($file)) {
$relativeFile = str_replace($this->container->getProperty(UpgradeContainer::LATEST_PATH), '', $file);
$symbolicLinkTarget = $filesToUpgrade->getSymbolicLinks()[$relativeFile] ?? null;
if (!$this->upgradeThisFile($file, $relativeFile, $symbolicLinkTarget)) {
// put the file back to the begin of the list
$this->next = TaskName::TASK_ERROR;
$this->logger->error($this->translator->trans('Error when trying to update file %s.', [$file]));
Expand All @@ -97,17 +99,12 @@ public function run(): int
* upgradeThisFile.
*
* @param mixed $orig The absolute path to the file from the upgrade archive
* @param string $file Relative file path from root
*
* @throws Exception
*/
public function upgradeThisFile($orig): bool
protected function upgradeThisFile($orig, string $file, ?string $symbolicLinkTarget): bool
{
// translations_custom and mails_custom list are currently not used
// later, we could handle customization with some kind of diff functions
// for now, just copy $file in str_replace($this->latestRootDir,_PS_ROOT_DIR_)

$file = str_replace($this->container->getProperty(UpgradeContainer::LATEST_PATH), '', $orig);

// The path to the file in our prestashop directory
$dest = $this->destUpgradePath . $file;

Expand All @@ -118,7 +115,19 @@ public function upgradeThisFile($orig): bool

return true;
}
if (is_dir($orig)) {

// Special case for symbolic links
if (!empty($symbolicLinkTarget)) {
// Remove the file/folder in case it already exists
if ($this->container->getFileSystem()->exists($dest)) {
$this->container->getFileSystem()->remove($dest);
}

symlink($symbolicLinkTarget, $dest);
$this->logger->debug($this->translator->trans('Symbolic link %s to %s.', [$file, $symbolicLinkTarget]));

return true;
} elseif (is_dir($orig)) {
// if $dest is not a directory (that can happen), just remove that file
if (!is_dir($dest) && $this->container->getFileSystem()->exists($dest)) {
$this->container->getFileSystem()->remove($dest);
Expand Down Expand Up @@ -248,6 +257,20 @@ protected function warmUp(): int
}
}

// Prepare symbolic links list
$symbolicLinks = $this->container->getChecksumCompare()->getSymbolicLinks($state->getDestinationVersion());
foreach ($symbolicLinks as $linkRelativePath => $linkTarget) {
if (substr($linkRelativePath, 0, 6) === '/admin') {
// Please make sure that the condition to check if the string starts with /admin stays here, because it was replacing
// admin even in the middle of a path, not deleting some files as a result.
// Also, do not use DIRECTORY_SEPARATOR, keep forward slash, because the path come from the XML standardized.
$newLinkRelativePath = '/' . $admin_dir . substr($linkRelativePath, 6);
$symbolicLinks[$newLinkRelativePath] = $linkTarget;
unset($symbolicLinks[$linkRelativePath]);
}
}
$this->logger->debug('Warmup symbolic links : ' . var_export($symbolicLinks, true));

// Now, we get the list of files that are either new or must be modified
$list_files_to_upgrade = $this->container->getFilesystemAdapter()->listFilesInDir(
$newReleasePath, 'upgrade', true
Expand All @@ -258,7 +281,7 @@ protected function warmUp(): int

$total_files_to_upgrade = count($list_files_to_upgrade);
$this->container->getFileStorage()->save(
(new Backlog($list_files_to_upgrade, $total_files_to_upgrade))->dump(),
(new Backlog($list_files_to_upgrade, $total_files_to_upgrade, $symbolicLinks))->dump(),
UpgradeFileNames::FILES_TO_UPGRADE_LIST
);

Expand Down
31 changes: 31 additions & 0 deletions classes/Xml/ChecksumCompare.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,17 @@ public function getTamperedFilesOnShop(string $version)
return $this->fileDifferences;
}

/**
* @param string $version
* @return string[] Indexed by relative path from root folder, value is the symbolic link target
*/
public function getSymbolicLinks(string $version): array
{
$checksum = $this->fileLoader->getXmlMd5File($version);

return $this->symbolicLinksAsArray($checksum->ps_root_dir[0]);
}

public function isAuthenticPrestashopVersion(string $version): bool
{
return !$this->getTamperedFilesOnShop($version);
Expand Down Expand Up @@ -259,6 +270,26 @@ protected function md5FileAsArray(SimpleXMLElement $node, string $dir = '/')
return $array;
}

/**
* @return array<string, string>
*/
protected function symbolicLinksAsArray(SimpleXMLElement $node, string $dir = ''): array
{
$links = [];
foreach ($node as $child) {
if (is_object($child) && $child->getName() == 'dir') {
$subDir = (string) $child['name'];
$subDirLinks = $this->symbolicLinksAsArray($child, $dir . DIRECTORY_SEPARATOR . $subDir);
$links = $links + $subDirLinks;
} elseif (is_object($child) && $child->getName() == 'link') {
$linkName = (string) $child['name'];
$links[$dir . DIRECTORY_SEPARATOR . $linkName] = (string) $child;
}
}

return $links;
}

/** populate $this->$this->file_differences with $path
* in sub arrays mail, themes, translation and core items.
*
Expand Down

0 comments on commit 6535cec

Please sign in to comment.