Add functionality to install project dependencies

Added a new feature in `ProjectManager.php` that allows automatic installation of a project's dependencies. The `installDependencies` function eases the task of separately installing each dependency. Now, both public and private dependencies can be installed collectively using the 'install' command added in `ProjectMenu.php`. This functionality is further documented in `DOCUMENTATION.md`.
This commit is contained in:
Netkas 2023-10-11 00:25:39 -04:00
parent c82bc7195a
commit 35981115eb
No known key found for this signature in database
GPG key ID: 5DAF58535614062B
4 changed files with 173 additions and 1 deletions

View file

@ -7,7 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [2.0.1] - Unreleased
This update introduces minor bug fixes and changes
Added a new feature in `ProjectManager.php` that allows automatic installation of a project's dependencies. The
`installDependencies` function eases the task of separately installing each dependency. Now, both public and private
dependencies can be installed collectively using the 'install' command added in `ProjectMenu.php`. This functionality
is further documented in `DOCUMENTATION.md`
### Added
- Add functionality to install project dependencies
### Fixed
- Correct exception message for configuration property in `\ncc\Objects\ProjectConfiguration\Build > BuildConfiguration > fromArray()`

View file

@ -41,6 +41,7 @@ basic usage, standards, and much more.
* [Applying Templates (template)](#applying-templates-template)
* [phpcli template](#phpcli-template)
* [phplib template](#phplib-template)
* [Install Dependencies (install)](#install-dependencies-install)
* [Package Management (package or pkg)](#package-management-package-or-pkg)
* [Listing Installed Packages (list)](#listing-installed-packages-list)
* [Installing Packages (install)](#installing-packages-install)
@ -1083,6 +1084,20 @@ about autoloaders or anything like that, ncc will take care of that for you. Thi
can only install packages onto your project directory, but it cannot bundle them into your programs if you wish to
have an easy way to distribute your project to other people, this is where ncc shines.
### Install Dependencies (install)
The `install` command satisfies the dependencies of a project by installing the required packages. This command is
not the same as `package install`, this command will install the dependencies of a project, while `package install`
only handles your dependencies, including dependencies defined in build configurations.
| Option | Required | Example | Description |
|--------------------------|----------|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--path`, `-p` | No | example_project | The project directory to use to install the dependencies, if not provided then the current working directory would be used instead |
| `--authentication`, `-a` | No | johndoe | The authentication credential to use when pulling from private repositories, see [Credentials Management (cred)](#credentials-management-cred) for more information |
## Package Management (package or pkg)
The command `package` provides a set of commands for managing packages, such as installing packages and uninstalling

View file

@ -24,10 +24,13 @@
use Exception;
use ncc\Enums\ProjectTemplates;
use ncc\Enums\Scopes;
use ncc\Managers\CredentialManager;
use ncc\Managers\ProjectManager;
use ncc\Objects\CliHelpSection;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
use ncc\Utilities\Resolver;
use ncc\Utilities\Security;
class ProjectMenu
@ -37,6 +40,7 @@
*
* @param array $args
* @return int
* @throws Exception
*/
public static function start(array $args): int
{
@ -50,6 +54,11 @@
return self::applyTemplate($args);
}
if(isset($args['install']))
{
return self::installProject($args);
}
return self::displayOptions();
}
@ -116,6 +125,93 @@
return 0;
}
/**
* Installs the project dependencies
*
* @param array $args
* @return int
* @throws Exception
*/
private static function installProject(array $args): int
{
if(Resolver::resolveScope() !== Scopes::SYSTEM)
{
Console::outError('You cannot install packages in a user scope, please run this command as root', true, 1);
return 1;
}
if(isset($args['path']) || isset($args['p']))
{
$project_path = $args['path'] ?? $args['p'];
}
elseif(is_file(getcwd() . DIRECTORY_SEPARATOR . 'project.json'))
{
$project_path = getcwd();
}
else
{
Console::outError('Missing option: --path|-p, please specify the path to the project', true, 1);
return 1;
}
$authentication = $args['authentication'] ?? $args['a'] ?? null;
$authentication_entry = null;
if($authentication !== null)
{
$entry = (new CredentialManager())->getVault()?->getEntry($authentication);
if($entry->isEncrypted())
{
$tries = 0;
while(true)
{
if (!$entry->unlock(Console::passwordInput('Password/Secret: ')))
{
$tries++;
if ($tries >= 3)
{
Console::outError('Too many failed attempts.', true, 1);
return 1;
}
Console::outError(sprintf('Incorrect password/secret, %d attempts remaining', 3 - $tries));
}
else
{
Console::out('Authentication successful.');
return 1;
}
}
}
$authentication_entry = $entry->getPassword();
}
try
{
$project_manager = new ProjectManager($project_path);
}
catch(Exception $e)
{
Console::outException('There was an error while trying to load the project', $e, 1);
return 1;
}
try
{
Console::out(sprintf('Installed %s packages', count($project_manager->installDependencies($authentication_entry))));
}
catch(Exception $e)
{
Console::outException('There was an error while trying to install the project dependencies', $e, 1);
return 1;
}
Console::out(sprintf('Project dependencies successfully installed in \'%s\'', $project_manager->getProjectPath()));
return 0;
}
/**
* Applies a template to the project
*
@ -183,6 +279,7 @@
new CliHelpSection(['help'], 'Displays this help menu about the value command'),
new CliHelpSection(['create', '--path|-p', '--name|-n', '--package|--pkg', '--ext'], 'Creates a new ncc project'),
new CliHelpSection(['template', '--path|-p', '--name|-n'], 'Applies a template to the project'),
new CliHelpSection(['install', '--path|-p', '--authentication|-a'], 'Installs the project dependencies'),
];
$options_padding = Functions::detectParametersPadding($options) + 4;

View file

@ -36,6 +36,7 @@
use ncc\Enums\Options\BuildConfigurationValues;
use ncc\Enums\Options\InitializeProjectOptions;
use ncc\Enums\ProjectTemplates;
use ncc\Enums\Scopes;
use ncc\Enums\SpecialConstants\AssemblyConstants;
use ncc\Enums\Types\BuildOutputType;
use ncc\Exceptions\BuildException;
@ -44,9 +45,11 @@
use ncc\Exceptions\NotSupportedException;
use ncc\Exceptions\OperationException;
use ncc\Exceptions\PathNotFoundException;
use ncc\Interfaces\AuthenticationInterface;
use ncc\Objects\ComposerJson;
use ncc\Objects\Package\ExecutionUnit;
use ncc\Objects\ProjectConfiguration;
use ncc\Objects\RemotePackageInput;
use ncc\Utilities\Console;
use ncc\Utilities\Functions;
use ncc\Utilities\IO;
@ -195,6 +198,56 @@
}
}
/**
* Install dependencies for the project
*
* @param AuthenticationInterface|null $authentication
* @return array Array of installed packages
* @throws OperationException
* @throws IOException
* @throws ConfigurationException
* @throws PathNotFoundException
* @throws NotSupportedException
*/
public function installDependencies(?AuthenticationInterface $authentication=null): array
{
if(Resolver::resolveScope() !== Scopes::SYSTEM)
{
throw new OperationException('Unable to install dependencies, you must be running as root');
}
$installed_packages = [];
$dependencies = $this->project_configuration->getBuild()->getDependencies();
$package_manager = new PackageManager();
foreach($this->project_configuration->getBuild()->getBuildConfigurations() as $configuration)
{
/** @noinspection SlowArrayOperationsInLoopInspection */
$dependencies = array_merge(
$dependencies,
$this->project_configuration->getBuild()->getBuildConfiguration($configuration)->getDependencies()
);
}
foreach($dependencies as $dependency)
{
if($dependency->getSource() === null)
{
Console::outVerbose('Skipping dependency \'' . $dependency->getName() . '\' because there is no source available');
}
elseif($package_manager->getPackageLock()->entryExists($dependency->getName(), $dependency->getVersion()))
{
Console::outVerbose('Skipping dependency \'' . $dependency->getName() . '\' because it is already installed');
}
else
{
$installed_packages[] = $package_manager->install(RemotePackageInput::fromString($dependency->getSource()), $authentication);
}
}
return $installed_packages;
}
/**
* Returns an array of file extensions for the components that are part of this project
*
@ -549,6 +602,7 @@
foreach($composer_json->getAutoload()?->getFiles() as $path)
{
/** @noinspection SlowArrayOperationsInLoopInspection */
$required_files = array_merge($required_files, self::copyContents($project_path, $project_src, $path));
}