Many changes

This commit is contained in:
Netkas 2023-02-23 16:22:20 -05:00
parent 085cb5a24c
commit 56561bca05
7 changed files with 364 additions and 88 deletions

2
.idea/php.xml generated
View file

@ -15,9 +15,7 @@
<path value="/etc/ncc" />
<path value="/var/ncc/packages/net.nosial.optslib=1.0.0" />
<path value="/var/ncc/packages/net.nosial.loglib=1.0.1" />
<path value="/var/ncc/packages/com.symfony.process=6.2.5" />
<path value="/var/ncc/packages/com.symfony.yaml=6.2.5" />
<path value="/var/ncc/packages/com.symfony.filesystem=6.2.5" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.2" />

171
README.md
View file

@ -9,4 +9,173 @@ One of the biggest advantages of using something like ConfigLib is that
it will allow for more complicated software to be configured more easily
by following the documented instructions on how to alter configuration
files, optionally you could use a builtin editor to edit the configuration
file manually.
file manually.
## Table of contents
<!-- TOC -->
* [ConfigLib](#configlib)
* [Table of contents](#table-of-contents)
* [Installation](#installation)
* [Compile from source](#compile-from-source)
* [Requirements](#requirements)
* [Documentation](#documentation)
* [Storage Location](#storage-location)
* [Creating a new configuration file](#creating-a-new-configuration-file)
* [Setting default values](#setting-default-values)
* [Command-line usage](#command-line-usage)
* [Editing a configuration file](#editing-a-configuration-file)
* [Using an external editor](#using-an-external-editor)
* [Inline command line editor](#inline-command-line-editor)
* [License](#license)
<!-- TOC -->
## Installation
The library can be installed using ncc:
```bash
ncc install -p "nosial/libs.config=latest@n64"
```
or by adding the following to your project.json file under the `build.dependencies` section:
```json
{
"name": "net.nosial.configlib",
"version": "latest",
"source_type": "remote",
"source": "nosial/libs.config=latest@n64"
}
```
If you don't have the n64 source configured, you can add it by running the following command:
```bash
ncc source add --name n64 --type gitlab --host git.n64.cc
```
## Compile from source
To compile the library from source, you need to have [ncc](https://git.n64.cc/nosial/ncc) installed, then run the
following command:
```bash
ncc build
```
## Requirements
The library requires PHP 8.0 or higher.
## Documentation
ConfigLib is both a library and a command line tool, the library can be used within your program to create a new
configuration file and load in default entries, either on the first run or automatically during the installation
process.
The goal to ConfigLib is to make it easy to setup configuration parameters for your program and to make it easy
for a user to edit the configuration file without having to manually edit the file. This part of the documentation
will explain both how to implement the library into your program and how to use the command line tool to edit the
configuration file.
### Storage Location
Configuration files are stored as json files in the data directory of ConfigLib which is located at
`/var/ncc/data/net.nosial.configlib`, this directory is created automatically when the library is installed.
### Creating a new configuration file
To create a new configuration file, you can create a new `\ConfigLib\Configuration()` object and pass in the
name of the configuration file, for example:
```php
require 'ncc';
import('net.nosial.configlib');
$config = new \ConfigLib\Configuration('myconfig');
```
This will only initialize the object, to save the configuration file you need to call the `save()` method:
```php
$config->save();
```
### Setting default values
You can set default values for the configuration file which will be created if the values do not exist in the
configuration file,
```php
require 'ncc';
import('net.nosial.configlib');
$config = new \ConfigLib\Configuration('com.symfony.yaml');
$config->setDefault('database.host', '127.0.0.1');
$config->setDefault('database.port', 3306);
$config->setDefault('database.username', 'root');
$config->setDefault('database.password', null);
$config->setDefault('database.name', 'test');
$config->save();
```
## Command-line usage
The command line interface can be executed by running `configlib` from the command line or by running
`ncc exec --package="net.nosial.configlib` if `configlib` isn't in your global path.
For the rest of this documentation, we will assume that you have the `configlib` command in your global path.
### Editing a configuration file
There are two ways to edit a configuration file using ConfigLib
1. Using an external editor
2. Inline command line editor
#### Using an external editor
When you use an external editor, ConfigLib will create a temporary YAML file and open it in the specified editor,
when you save and close the file, ConfigLib will parse the YAML file and save the configuration file. If the YAML
file is invalid, ConfigLib will not save the configuration file.
This is the recommended way to edit configuration files as it allows you to use your preferred editor and it
allows you to use the full power of YAML.
To edit a configuration file using an external editor, run the following command:
```bash
configlib --config <config_name> --editor nano
```
> Note: Changes will only be applied if you save the file and close the editor.
#### Inline command line editor
The inline command line editor is a simple editor that allows you to edit the configuration file from the command
line, this is useful for automated scripts.
To view the contents of a configuration file, run the following command:
```bash
configlib --config <config_name>
```
To view the value of a specific property, use the `--property` option:
```bash
configlib --config <config_name> --property database.username
```
To edit a property, specify both the `--property` and `--value` options:
```bash
configlib --config <config_name> --property database.username --value root
```
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details

View file

@ -42,6 +42,9 @@
"source_path": "src",
"default_configuration": "release",
"main": "main",
"define_constants": {
"version": "%ASSEMBLY.VERSION%"
},
"dependencies": [
{
"name": "net.nosial.optslib",

View file

@ -10,6 +10,7 @@
use RuntimeException;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Yaml\Yaml;
class Configuration
{
@ -57,7 +58,6 @@
// Figure out the path to the configuration file
try
{
/** @noinspection PhpUndefinedClassInspection */
$this->Path = Runtime::getDataPath('net.nosial.configlib') . DIRECTORY_SEPARATOR . $name . '.conf';
}
catch (Exception $e)
@ -232,8 +232,6 @@
{
if(!self::validateKey($key))
return false;
if(!isset($this->Configuration[$key]))
return false;
$path = explode('.', $key);
$current = $this->Configuration;
@ -281,6 +279,7 @@
try
{
$fs->dumpFile($this->Path, $json);
$fs->chmod($this->Path, 0777);
}
catch (IOException $e)
{
@ -356,6 +355,11 @@
return $this->Configuration;
}
public function toYaml(): string
{
return Yaml::dump($this->Configuration, 4, 2);
}
/**
* Public Destructor
*/
@ -373,4 +377,38 @@
}
}
}
/**
* Imports a YAML file into the configuration
*
* @param string $path
* @return void
* @throws Exception
*/
public function import(string $path)
{
$fs = new Filesystem();
if(!$fs->exists($path))
throw new Exception(sprintf('Unable to import configuration file "%s", file does not exist', $path));
$yaml = file_get_contents($path);
$data = Yaml::parse($yaml);
$this->Configuration = array_replace_recursive($this->Configuration, $data);
$this->Modified = true;
}
/**
* Exports the configuration to a YAML file
*
* @param string $path
* @return void
*/
public function export(string $path)
{
$fs = new Filesystem();
$fs->dumpFile($path, $this->toYaml());
$fs->chmod($path, 0777);
}
}

View file

@ -3,9 +3,17 @@
namespace ConfigLib;
use Exception;
use JetBrains\PhpStorm\NoReturn;
use ncc\Exceptions\InvalidPackageNameException;
use ncc\Exceptions\InvalidScopeException;
use ncc\Exceptions\PackageLockException;
use ncc\Exceptions\PackageNotFoundException;
use ncc\Runtime;
use OptsLib\Parse;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\Process;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;
class Program
{
@ -14,22 +22,21 @@
*
* @return void
*/
public static function main(): void
#[NoReturn] public static function main(): void
{
$args = Parse::getArguments();
if(isset($args['help']) || isset($args['h']))
self::help();
if(isset($args['version']) || isset($args['v']))
self::version();
if(isset($args['name']) || isset($args['n']))
if(isset($args['conf']) || isset($args['config']))
{
$configuration_name = $args['name'] ?? $args['n'] ?? null;
$property = $args['property'] ?? $args['p'] ?? null;
$value = $args['value'] ?? $args['v'] ?? null;
$editor = $args['editor'] ?? $args['e'] ?? null;
$configuration_name = $args['conf'] ?? $args['config'] ?? null;
$property = $args['prop'] ?? $args['property'] ?? null;
$value = $args['val'] ?? $args['value'] ?? null;
$editor = $args['editor'] ?? @$args['e'] ?? null;
$export = $args['export'] ?? null;
$import = $args['import'] ?? null;
if($configuration_name === null)
{
@ -39,21 +46,71 @@
$configuration = new Configuration($configuration_name);
// Check if the configuration exists first.
if(!file_exists($configuration->getPath()))
{
print(sprintf('Configuration \'%s\' does not exist, aborting' . PHP_EOL, $configuration->getName()));
exit(1);
}
if($import !== null)
{
try
{
$configuration->import((string)$import);
$configuration->save();
}
catch (Exception $e)
{
print($e->getMessage() . PHP_EOL);
exit(1);
}
print(sprintf('Configuration \'%s\' imported from \'%s\'' . PHP_EOL, $configuration->getName(), $import));
exit(0);
}
if($export !== null)
{
if(!is_string($export))
$export = sprintf('%s.yml', $configuration->getName());
try
{
$configuration->export($export);
}
catch (Exception $e)
{
print($e->getMessage() . PHP_EOL);
exit(1);
}
print(sprintf('Configuration \'%s\' exported to \'%s\'' . PHP_EOL, $configuration->getName(), $export));
exit(0);
}
if($editor !== null)
{
self::edit($args);
return;
try
{
self::edit($args, $configuration);
}
catch(Exception $e)
{
print($e->getMessage() . PHP_EOL);
exit(1);
}
}
if($property === null)
{
print(json_encode($configuration->getConfiguration(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . PHP_EOL);
print($configuration->toYaml() . PHP_EOL);
}
else
{
if($value === null)
{
print(json_encode($configuration->get($property, '(not set)'), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . PHP_EOL);
print(Yaml::dump($configuration->get($property), 4, 2) . PHP_EOL);
return;
}
@ -81,25 +138,27 @@
*
* @return void
*/
private static function help(): void
#[NoReturn] private static function help(): void
{
print('ConfigLib v' . Runtime::getConstant('net.nosial.configlib', 'version') . PHP_EOL . PHP_EOL);
print('Usage: configlib [options]' . PHP_EOL);
print(' -h, --help Displays the help menu' . PHP_EOL);
print(' -v, --version Displays the version of the program' . PHP_EOL);
print(' -n, --name <name> The name of the configuration' . PHP_EOL);
print(' -p, --path <path> The property name to select/read (eg; foo.bar.baz) (Inline)' . PHP_EOL);
print(' -v, --value <value> The value to set the property (Inline)' . PHP_EOL);
print(' -e, --editor <editor> (Optional) The editor to use (eg; nano, vim, notepad) (External)' . PHP_EOL);
print(' --nc (Optional) Disables type casting (eg; \'true\' > True) will always be a string' . PHP_EOL);
print(' --export <file> (Optional) Exports the configuration to a file' . PHP_EOL);
print(' --import <file> (Optional) Imports the configuration from a file' . PHP_EOL);
print('Examples:' . PHP_EOL);
print(' configlib -n com.example.package' . PHP_EOL);
print(' configlib -n com.example.package -e nano' . PHP_EOL);
print(' configlib -n com.example.package -p foo.bar.baz -v 123' . PHP_EOL);
print(' configlib -n com.example.package -p foo.bar.baz -v 123 --nc' . PHP_EOL);
print(' configlib -n com.example.package --export config.json' . PHP_EOL);
print(' configlib -n com.example.package --import config.json' . PHP_EOL);
print(' -h, --help Displays the help menu' . PHP_EOL);
print(' --conf, --config <name> The name of the configuration' . PHP_EOL);
print(' --prop, --property <property> The property name to select/read (eg; foo.bar.baz) (Inline)' . PHP_EOL);
print(' --val, --value <value> The value to set the property (Inline)' . PHP_EOL);
print(' -e, --editor <editor> (Optional) The editor to use (eg; nano, vim, notepad) (External)' . PHP_EOL);
print(' --export <file> (Optional) Exports the configuration to a file' . PHP_EOL);
print(' --import <file> (Optional) Imports the configuration from a file' . PHP_EOL);
print(' --nc (Optional) Disables type casting (eg; \'true\' > True) will always be a string' . PHP_EOL);
print('Examples:' . PHP_EOL . PHP_EOL);
print(' configlib --conf test View the configuration' . PHP_EOL);
print(' configlib --conf test --prop foo View a specific property' . PHP_EOL);
print(' configlib --conf test --prop foo --val bar Set a specific property' . PHP_EOL);
print(' configlib --conf test --editor nano Edit the configuration' . PHP_EOL);
print(' configlib --conf test --export out.json Export the configuration' . PHP_EOL);
print(' configlib --conf test --import in.json Import a configuration' . PHP_EOL);
exit(0);
}
@ -108,16 +167,19 @@
* Edits an existing configuration file or creates a new one if it doesn't exist
*
* @param array $args
* @param Configuration $configuration
* @return void
* @throws InvalidPackageNameException
* @throws InvalidScopeException
* @throws PackageLockException
* @throws PackageNotFoundException
*/
private static function edit(array $args): void
#[NoReturn] static function edit(array $args, Configuration $configuration): void
{
$editor = $args['editor'] ?? 'vi';
if(isset($args['e']))
$editor = $args['e'];
$name = $args['name'] ?? 'default';
if($editor == null)
{
print('No editor specified' . PHP_EOL);
@ -125,21 +187,25 @@
}
// Determine the temporary path to use
$tempPath = null;
if(function_exists('ini_get'))
if(file_exists(DIRECTORY_SEPARATOR . 'tmp'))
{
$tempPath = ini_get('upload_tmp_dir');
if($tempPath == null)
$tempPath = ini_get('session.save_path');
if($tempPath == null)
$tempPath = ini_get('upload_tmp_dir');
if($tempPath == null)
$tempPath = sys_get_temp_dir();
$tempPath = DIRECTORY_SEPARATOR . 'tmp';
}
else
{
if(!file_exists(Runtime::getDataPath('net.nosial.configlib') . DIRECTORY_SEPARATOR . 'tmp'))
{
mkdir(Runtime::getDataPath('net.nosial.configlib') . DIRECTORY_SEPARATOR . 'tmp', 0777, true);
if($tempPath == null && function_exists('sys_get_temp_dir'))
$tempPath = sys_get_temp_dir();
if(!file_exists(Runtime::getDataPath('net.nosial.configlib') . DIRECTORY_SEPARATOR . 'tmp'))
{
print('Unable to create the temporary path to use' . PHP_EOL);
exit(1);
}
}
$tempPath = Runtime::getDataPath('net.nosial.configlib') . DIRECTORY_SEPARATOR . 'tmp';
}
if($tempPath == null)
{
@ -147,26 +213,16 @@
exit(1);
}
// Prepare the temporary file
try
{
$configuration = new Configuration($name);
}
catch (Exception $e)
{
print($e->getMessage() . PHP_EOL);
exit(1);
}
$fs = new Filesystem();
$tempFile = $tempPath . DIRECTORY_SEPARATOR . $name . '.conf';
$fs->copy($configuration->getPath(), $tempFile);
// Convert the configuration from JSON to YAML for editing purposes
$tempFile = $tempPath . DIRECTORY_SEPARATOR . bin2hex(random_bytes(16)) . '.yaml';
$fs->dumpFile($tempFile, $configuration->toYaml());
$original_hash = hash_file('sha1', $tempFile);
// Open the editor
try
{
// Open the editor
$process = new Process([$editor, $tempFile]);
$process->setTimeout(0);
$process->setTty(true);
@ -177,10 +233,6 @@
print('Unable to open the editor, ' . $e->getMessage() . PHP_EOL);
exit(1);
}
finally
{
$fs->remove($tempFile);
}
// Check if the file has changed and if so, update the configuration
if($fs->exists($tempFile))
@ -188,26 +240,29 @@
$new_hash = hash_file('sha1', $tempFile);
if($original_hash != $new_hash)
{
$fs->copy($tempFile, $configuration->getPath());
}
else
{
print('No changes detected' . PHP_EOL);
}
// Convert the YAML back to JSON
$yaml = file_get_contents($tempFile);
$fs->remove($tempFile);
try
{
$json = Yaml::parse($yaml);
}
catch (ParseException $e)
{
print('Unable to parse the YAML file, ' . $e->getMessage() . PHP_EOL);
exit(1);
}
$path = $configuration->getPath();
$fs->dumpFile($path, json_encode($json, JSON_PRETTY_PRINT));
print('Configuration updated' . PHP_EOL);
}
}
}
// Remove the temporary file
if($fs->exists($tempFile))
$fs->remove($tempFile);
/**
* Prints out the version of the program
*
* @return void
*/
private static function version(): void
{
print('ConfigLib v1.0.0' . PHP_EOL);
exit(0);
}
}

View file

@ -3,10 +3,12 @@
require 'ncc';
import('net.nosial.configlib');
$config = new \ConfigLib\Configuration('test');
$config = new \ConfigLib\Configuration('com.symfony.yaml');
$config->setDefault('database.host', '127.0.0.1');
$config->setDefault('database.port', 3306);
$config->setDefault('database.username', 'root');
$config->setDefault('database.password', null);
$config->setDefault('database.name', 'test');
$config->setDefault('database.name', 'test');
$config->save();

11
tests/edit_test.php Normal file
View file

@ -0,0 +1,11 @@
<?php
require 'ncc';
import('net.nosial.configlib');
$config = new \ConfigLib\Configuration('test');
$config->set('database.host', '192.168.1.1');
$config->set('database.username', 'super_root');
$config->save();