Release of v5.0.1-alpha7

Add push options to Joomla Power. Complete the Joomla Power Init and Reset features. Fix Gitea Contents class functions. Last Alpha release (feature block).
This commit is contained in:
2024-07-08 22:55:12 +02:00
parent 0cbf3c0e71
commit 18545c5b8d
57 changed files with 1343 additions and 688 deletions

View File

@ -64,10 +64,10 @@ class Contents extends Api
* @param string|null $ref Optional. The name of the commit/branch/tag.
* Default the repository's default branch (usually master).
*
* @return object|null
* @return null|array|object
* @since 3.2.0
**/
public function metadata(string $owner, string $repo, string $filepath, ?string $ref = null): ?object
public function metadata(string $owner, string $repo, string $filepath, ?string $ref = null): null|array|object
{
// Build the request path.
$path = "/repos/{$owner}/{$repo}/contents/{$filepath}";
@ -100,7 +100,7 @@ class Contents extends Api
* @param string|null $authorEmail The author's email.
* @param string|null $committerName The committer's name.
* @param string|null $committerEmail The committer's email.
* @param bool|null $newBranch Whether to create a new branch. Defaults to false.
* @param string|null $newBranch Whether to create a new branch. Defaults to null.
* @param string|null $authorDate The author's date.
* @param string|null $committerDate The committer's date.
* @param bool|null $signoff Add a Signed-off-by trailer. Defaults to null.
@ -119,7 +119,7 @@ class Contents extends Api
?string $authorEmail = null,
?string $committerName = null,
?string $committerEmail = null,
?bool $newBranch = false,
?string $newBranch = null,
?string $authorDate = null,
?string $committerDate = null,
?bool $signoff = null

View File

@ -29,7 +29,7 @@ final class Http extends JoomlaHttp
* @var string
* @since 3.2.0
*/
protected string $_token_; // to avoid collusions (but allow swapping)
protected string $_token_; // to avoid collisions (but allow swapping)
/**
* Constructor.
@ -52,7 +52,7 @@ final class Http extends JoomlaHttp
// add the token if given
if (is_string($token))
{
$config['headers']['Authorization'] = $token;
$config['headers']['Authorization'] = 'token ' . $token;
$this->_token_ = $token;
}
@ -78,7 +78,7 @@ final class Http extends JoomlaHttp
);
// add the token
$headers['Authorization'] = $token;
$headers['Authorization'] = 'token ' . $token;
$this->_token_ = $token;
$this->setOption('headers', $headers);

View File

@ -55,6 +55,14 @@ abstract class Grep implements GrepInterface
**/
protected array $order = ['local', 'remote'];
/**
* The target branch field name ['read_branch', 'write_branch']
*
* @var string
* @since 3.2.2
**/
protected string $branch_field = 'read_branch';
/**
* Gitea Repository Contents
*
@ -120,6 +128,19 @@ abstract class Grep implements GrepInterface
return empty($powers) ? null : array_unique($powers);
}
/**
* Set the branch field
*
* @param string $field The global unique id of the power
*
* @return void
* @since 3.2.2
*/
public function setBranchField(string $field): void
{
$this->branch_field = $field;
}
/**
* Get a power
*
@ -196,15 +217,12 @@ abstract class Grep implements GrepInterface
$path->path = trim($path->organisation) . '/' . trim($path->repository);
// update the branch
if ($path->read_branch === 'default' || empty($path->read_branch))
{
$path->read_branch = null;
}
$branch_field = $this->getBranchField();
$branch = $path->{$branch_field} ?? null;
// only update the write branch if set
if (isset($path->write_branch) && ($path->write_branch === 'default' || empty($path->write_branch)))
if ($branch === 'default' || empty($branch))
{
$path->write_branch = null;
$path->{$branch_field} = null;
}
// set local path
@ -219,6 +237,17 @@ abstract class Grep implements GrepInterface
}
}
}
}
/**
* Get the branch field
*
* @return string
* @since 3.2.2
*/
public function getBranchField(): string
{
return $this->branch_field;
}
}

View File

@ -16,6 +16,7 @@ use Joomla\Registry\Registry as JoomlaRegistry;
use Joomla\CMS\Factory as JoomlaFactory;
use VDM\Joomla\Utilities\GetHelper;
use VDM\Joomla\Utilities\StringHelper;
use VDM\Joomla\Componentbuilder\Utilities\RepoHelper;
use VDM\Joomla\Componentbuilder\Abstraction\BaseConfig;
@ -113,21 +114,6 @@ class Config extends BaseConfig
return $repos;
}
/**
* Get Joomla power push repo
*
* @return object|null The push repository on Gitea
* @since 3.2.1
*/
protected function getJoomlapowerspushrepo(): ?object
{
// some defaults repos we need by JCB
if (!empty($this->gitea_username))
{
return (object) ['organisation' => $this->gitea_username, 'repository' => 'joomla-powers', 'read_branch' => 'master'];
}
}
/**
* Get joomla power approved paths
*
@ -136,7 +122,26 @@ class Config extends BaseConfig
*/
protected function getApprovedjoomlapaths(): array
{
return array_values($this->joomla_powers_init_repos);
// some defaults repos we need by JCB
$approved = $this->joomla_powers_init_repos;
$paths = RepoHelper::get(2); // Joomla Power = 2
if ($paths !== null)
{
foreach ($paths as $path)
{
$owner = $path->organisation ?? null;
$repo = $path->repository ?? null;
if ($owner !== null && $repo !== null)
{
// we make sure to get only the objects
$approved = ["{$owner}.{$repo}" => $path] + $approved;
}
}
}
return array_values($approved);
}
}

View File

@ -54,20 +54,36 @@ final class Grep extends ExtendingGrep implements GrepInterface
return;
}
$path->index = null;
// update the branch
$branch_field = $this->getBranchField();
$branch = $path->{$branch_field} ?? $path->read_branch ?? 'master';
try
{
$this->contents->load_($path->base ?? null, $path->token ?? null);
$path->index = $this->contents->get($path->organisation, $path->repository, 'joomla-powers.json', $path->read_branch);
$source = $this->contents->metadata($path->organisation, $path->repository, 'src', $branch);
if ($source && is_array($source))
{
$path->index = new \stdClass();
foreach ($source as $index)
{
if (is_object($index) && isset($index->name))
{
$path->index->{$index->name} = $index;
}
}
}
$this->contents->reset_();
}
catch (\Exception $e)
{
$this->app->enqueueMessage(
Text::sprintf('COM_COMPONENTBUILDER_PSUPER_POWERB_REPOSITORY_AT_BSSB_GAVE_THE_FOLLOWING_ERRORBR_SP', $this->contents->api(), $path->path, $e->getMessage()),
Text::sprintf('COM_COMPONENTBUILDER_PJOOMLA_POWERB_REPOSITORY_AT_BSSB_GAVE_THE_FOLLOWING_ERRORBR_SP', $this->contents->api(), $path->path, $e->getMessage()),
'Error'
);
$path->index = null;
}
}
@ -82,7 +98,7 @@ final class Grep extends ExtendingGrep implements GrepInterface
protected function searchRemote(string $guid): ?object
{
// we can only search if we have paths
if ($this->path && $this->paths)
if (is_array($this->paths))
{
foreach ($this->paths as $path)
{
@ -100,7 +116,7 @@ final class Grep extends ExtendingGrep implements GrepInterface
}
/**
* Get a remote power
* Get a remote joomla power
*
* @param object $path The repository path details
* @param string $guid The global unique id of the power
@ -111,20 +127,40 @@ final class Grep extends ExtendingGrep implements GrepInterface
protected function getRemote(object $path, string $guid): ?object
{
$power = null;
if (empty($path->index->{$guid}->settings))
if (empty($path->index->{$guid}->path))
{
return $power;
}
// get the branch name
$branch_field = $this->getBranchField();
$branch = $path->{$branch_field} ?? $path->read_branch ?? 'master';
// get the settings
$this->contents->load_($path->base ?? null, $path->token ?? null);
if (($power = $this->loadRemoteFile($path->organisation, $path->repository, $path->index->{$guid}->settings, $path->read_branch)) !== null &&
if (($power = $this->loadRemoteFile($path->organisation, $path->repository, $path->index->{$guid}->path . '/item.json', $branch)) !== null &&
isset($power->guid))
{
// set the git details in params
$power->params = (object) [
'source' => ['guid' => $path->guid ?? null]
];
$path_guid = $path->guid ?? null;
if ($path_guid !== null)
{
if (($meta = $this->contents->metadata($path->organisation, $path->repository, $path->index->{$guid}->path . '/item.json', $branch)) !== null &&
isset($meta->sha))
{
if (isset($power->params) && is_object($power->params) &&
isset($power->params->source) && is_array($power->params->source))
{
$power->params->source[$path_guid] = $meta->sha;
}
else
{
$power->params = (object)[
'source' => [$path_guid => $meta->sha]
];
}
}
}
}
$this->contents->reset_();

View File

@ -0,0 +1,46 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 4th September, 2022
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Componentbuilder\JoomlaPower;
use VDM\Joomla\Data\Repository as ExtendingRepository;
/**
* Set JoomlaPower based on global unique ids to remote repository
*
* @since 3.2.2
*/
final class Repository extends ExtendingRepository
{
/**
* Table Name
*
* @var string
* @since 3.2.1
*/
protected string $table = 'joomla_power';
/**
* The item map
*
* @var array
* @since 3.2.2
*/
protected array $map = [
'system_name' => 'system_name',
'settings' => 'settings',
'guid' => 'guid',
'description' => 'description'
];
}

View File

@ -18,6 +18,7 @@ use VDM\Joomla\Componentbuilder\JoomlaPower\Config;
use VDM\Joomla\Componentbuilder\Table;
use VDM\Joomla\Componentbuilder\JoomlaPower\Grep;
use VDM\Joomla\Componentbuilder\JoomlaPower\Super as Superpower;
use VDM\Joomla\Componentbuilder\JoomlaPower\Repository;
use VDM\Joomla\Componentbuilder\Compiler\Power\Parser;
@ -34,7 +35,7 @@ class JoomlaPower implements ServiceProviderInterface
* @param Container $container The DI container.
*
* @return void
* @since 3.2.0
* @since 3.2.1
*/
public function register(Container $container)
{
@ -50,6 +51,9 @@ class JoomlaPower implements ServiceProviderInterface
$container->alias(Superpower::class, 'Joomlapower')
->share('Joomlapower', [$this, 'getSuperpower'], true);
$container->alias(Repository::class, 'Joomla.Power.Repository')
->share('Joomla.Power.Repository', [$this, 'getRepository'], true);
$container->alias(Parser::class, 'Power.Parser')
->share('Power.Parser', [$this, 'getParser'], true);
}
@ -60,7 +64,7 @@ class JoomlaPower implements ServiceProviderInterface
* @param Container $container The DI container.
*
* @return Config
* @since 3.2.0
* @since 3.2.1
*/
public function getConfig(Container $container): Config
{
@ -73,7 +77,7 @@ class JoomlaPower implements ServiceProviderInterface
* @param Container $container The DI container.
*
* @return Table
* @since 3.2.0
* @since 3.2.1
*/
public function getTable(Container $container): Table
{
@ -86,7 +90,7 @@ class JoomlaPower implements ServiceProviderInterface
* @param Container $container The DI container.
*
* @return Grep
* @since 3.2.0
* @since 3.2.1
*/
public function getGrep(Container $container): Grep
{
@ -102,7 +106,7 @@ class JoomlaPower implements ServiceProviderInterface
* @param Container $container The DI container.
*
* @return Superpower
* @since 3.2.0
* @since 3.2.1
*/
public function getSuperpower(Container $container): Superpower
{
@ -112,13 +116,31 @@ class JoomlaPower implements ServiceProviderInterface
);
}
/**
* Get The Repository Class.
*
* @param Container $container The DI container.
*
* @return Repository
* @since 3.2.2
*/
public function getRepository(Container $container): Repository
{
return new Repository(
$container->get('Config')->approved_joomla_paths,
$container->get('Joomla.Power.Grep'),
$container->get('Data.Items'),
$container->get('Gitea.Repository.Contents')
);
}
/**
* Get The Parser Class.
*
* @param Container $container The DI container.
*
* @return Parser
* @since 3.2.0
* @since 3.2.1
*/
public function getParser(Container $container): Parser
{

View File

@ -135,26 +135,6 @@ class Config extends BaseConfig
return $repos;
}
/**
* Get super power push repo
*
* @return object|null The push repository on Gitea
* @since 3.2.1
*/
protected function getSuperpowerspushrepo(): ?object
{
if ($this->gitea_username !== null)
{
return (object) [
'organisation' => $this->gitea_username,
'repository' => 'super-powers',
'read_branch' => 'master'
];
}
return null;
}
/**
* get temporary path
*

View File

@ -0,0 +1,386 @@
<?php
/**
* @package Joomla.Component.Builder
*
* @created 4th September, 2022
* @author Llewellyn van der Merwe <https://dev.vdm.io>
* @git Joomla Component Builder <https://git.vdm.dev/joomla/Component-Builder>
* @copyright Copyright (C) 2015 Vast Development Method. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace VDM\Joomla\Data;
use VDM\Joomla\Interfaces\GrepInterface as Grep;
use VDM\Joomla\Interfaces\Data\ItemsInterface as Items;
use VDM\Joomla\Gitea\Repository\Contents as Git;
/**
* Set data based on global unique ids to remote repository
*
* @since 3.2.2
*/
class Repository
{
/**
* The GrepInterface Class.
*
* @var Grep
* @since 3.2.2
*/
protected Grep $grep;
/**
* The ItemsInterface Class.
*
* @var Items
* @since 3.2.2
*/
protected Items $items;
/**
* The Contents Class.
*
* @var Git
* @since 3.2.2
*/
protected Git $git;
/**
* All active repos
*
* @var array
* @since 3.2.0
**/
public array $repos;
/**
* Table Name
*
* @var string
* @since 3.2.1
*/
protected string $table;
/**
* The item map
*
* @var array
* @since 3.2.2
*/
protected array $map;
/**
* Constructor.
*
* @param array $repos The active repos
* @param Grep $grep The GrepInterface Class.
* @param Items $items The ItemsInterface Class.
* @param Git $git The Contents Class.
* @param string|null $table The table name.
*
* @since 3.2.2
*/
public function __construct(array $repos, Grep $grep, Items $items, Git $git, ?string $table = null)
{
$this->repos = $repos;
$this->grep = $grep;
$this->items = $items;
$this->git = $git;
if ($table !== null)
{
$this->table = $table;
}
// set the branch to writing
$this->grep->setBranchField('write_branch');
}
/**
* Set the current active table
*
* @param string $table The table that should be active
*
* @return self
* @since 3.2.2
*/
public function table(string $table): self
{
$this->table = $table;
return $this;
}
/**
* Set items
*
* @param array $guids The global unique id of the item
*
* @return bool
* @throws \Exception
* @since 3.2.0
*/
public function set(array $guids): bool
{
if (($items = $this->getLocalItems($guids)) === null)
{
throw new \Exception("At least one valid local [Joomla Power] must exist for the push function to operate correctly.");
}
if (!$this->canWrite())
{
throw new \Exception("At least one [Joomla Power] content repository must be configured with a [Write Branch] value in the repositories area for the push function to operate correctly.");
}
// update the existing found
if (($existing_items = $this->getRepoItems($guids)) !== [])
{
foreach ($existing_items as $e_guid => $item)
{
if (isset($items[$e_guid]))
{
$this->updateItem($items[$e_guid], $item);
unset($items[$e_guid]);
}
}
}
// create the new items
foreach ($items as $item)
{
$this->createItem($item);
}
return true;
}
/**
* Get the current active table
*
* @return string
* @since 3.2.2
*/
public function getTable(): string
{
return $this->table;
}
/**
* Get items
*
* @param array $guids The global unique id of the item
*
* @return array|null
* @since 3.2.2
*/
public function getLocalItems(array $guids): ?array
{
$items = $this->fetchLocalItems($guids);
if ($items === null)
{
return null;
}
return $this->mapItems($items);
}
/**
* Fetch items from the database
*
* @param array $guids The global unique id of the item
*
* @return array|null
* @since 3.2.2
*/
protected function fetchLocalItems(array $guids): ?array
{
return $this->items->table($this->table)->get($guids);
}
/**
* Map items to their properties
*
* @param array $items The items fetched from the database
*
* @return array
* @since 3.2.2
*/
protected function mapItems(array $items): array
{
$bucket = [];
foreach ($items as $item)
{
if (!isset($item->guid))
{
continue;
}
$bucket[$item->guid] = $this->mapItem($item);
}
return $bucket;
}
/**
* Map a single item to its properties
*
* @param object $item The item to be mapped
*
* @return object
* @since 3.2.2
*/
protected function mapItem(object $item): object
{
$power = [];
foreach ($this->map as $key => $map)
{
$power[$key] = $item->{$map} ?? null;
}
return (object) $power;
}
/**
* get existing items
*
* @param array $guids The global unique id of the item
*
* @return array|null
* @since 3.2.2
*/
protected function getRepoItems(array $guids): ?array
{
$bucket = [];
foreach ($guids as $guid)
{
if (($item = $this->grep->get($guid)) !== null)
{
$bucket[$guid] = (object) $item;
}
}
return $bucket ?? null;
}
/**
* check that we have an active repo towards which we can write data
*
* @return bool
* @since 3.2.2
*/
protected function canWrite(): bool
{
foreach ($this->repos as $repo)
{
if (!empty($repo->write_branch) && $repo->write_branch !== 'default')
{
return true;
}
}
return false;
}
/**
* Checks if two objects are equal by comparing their JSON representations.
*
* This method converts both input objects to JSON strings and compares these strings.
* If the JSON strings are identical, the objects are considered equal.
*
* @param object $obj1 The first object to compare.
* @param object $obj2 The second object to compare.
*
* @return bool True if the objects are equal, false otherwise.
* @since 3.2.2
*/
protected function areObjectsEqual(object $obj1, object $obj2): bool
{
// Convert both objects to JSON strings
$json1 = json_encode($obj1);
$json2 = json_encode($obj2);
// Compare the JSON strings
return $json1 === $json2;
}
/**
* update an existing item (if changed)
*
* @param object $item
* @param object $existing
*
* @return void
* @since 3.2.2
*/
protected function updateItem(object $item, object $existing): void
{
if (isset($existing->params->source) && is_array($existing->params->source))
{
// get the source values
$source = $existing->params->source;
// make sure there was a change
$existing = $this->mapItem($existing);
if ($this->areObjectsEqual($item, $existing))
{
return;
}
foreach ($this->repos as $repo)
{
if (isset($source[$repo->guid]))
{
$this->git->load_($repo->base ?? null, $repo->token ?? null);
$this->git->update(
$repo->organisation, // The owner name.
$repo->repository, // The repository name.
'src/' . $item->guid . '/item.json', // The file path.
json_encode($item, JSON_PRETTY_PRINT), // The file content.
'Update ' . $item->system_name, // The commit message.
$source[$repo->guid], // The blob SHA of the old file.
$repo->write_branch // The branch name.
);
$this->git->reset_();
// only update in the first found repo
return;
}
}
}
}
/**
* create a new item
*
* @param object $item
*
* @return void
* @since 3.2.2
*/
protected function createItem(object $item): void
{
foreach ($this->repos as $repo)
{
if (!empty($repo->write_branch) && $repo->write_branch !== 'default')
{
$this->git->load_($repo->base ?? null, $repo->token ?? null);
$this->git->create(
$repo->organisation, // The owner name.
$repo->repository, // The repository name.
'src/' . $item->guid . '/item.json', // The file path.
json_encode($item, JSON_PRETTY_PRINT), // The file content.
'Create ' . $item->system_name, // The commit message.
$repo->write_branch // The branch name.
);
$this->git->reset_();
// only create in the first found repo
return;
}
}
}
}

View File

@ -27,6 +27,16 @@ interface GrepInterface
*/
public function getRemotePowersGuid(): ?array;
/**
* Set the branch field
*
* @param string $field The global unique id of the power
*
* @return void
* @since 3.2.2
*/
public function setBranchField(string $field): void;
/**
* Get a power
*