diff --git a/.gitignore b/.gitignore
index 570cd0f..4e93986 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ build
src/ncc/ThirdParty/defuse/php-encryption/autoload_spl.php
src/ncc/ThirdParty/jelix/version/autoload_spl.php
src/ncc/ThirdParty/nikic/PhpParser/autoload_spl.php
+src/ncc/ThirdParty/php_parallel_lint/php_console_color/autoload_spl.php
src/ncc/ThirdParty/Symfony/polyfill-ctype/autoload_spl.php
src/ncc/ThirdParty/Symfony/polyfill-mbstring/autoload_spl.php
src/ncc/ThirdParty/Symfony/polyfill-uuid/autoload_spl.php
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..7a5e944
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,33 @@
+image: php:8.1
+
+before_script:
+ # Install some stuff that the image doesn't come with
+ - apt update -yqq
+ - apt install git libpq-dev libzip-dev zip make wget gnupg -yqq
+
+ # Install phive
+ - wget -O phive.phar https://phar.io/releases/phive.phar
+ - wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
+ - gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
+ - gpg --verify phive.phar.asc phive.phar
+ - chmod +x phive.phar
+ - mv phive.phar /usr/local/bin/phive
+
+ # install phpab
+ - phive install phpab --global --trust-gpg-keys 0x2A8299CE842DD38C
+
+build:
+ script:
+ - make tar
+ rules:
+ - if: $CI_COMMIT_BRANCH
+
+release:
+ script:
+ - make tar
+ - mv build/build.tar.gz build/ncc_$CI_COMMIT_TAG.tar.gz
+ artifacts:
+ paths:
+ - build/ncc_$CI_COMMIT_TAG.tar.gz
+ rules:
+ - if: $CI_COMMIT_TAG
\ No newline at end of file
diff --git a/.idea/copyright/Nosial.xml b/.idea/copyright/Nosial.xml
new file mode 100644
index 0000000..ea25c5d
--- /dev/null
+++ b/.idea/copyright/Nosial.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..85fcaf5
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/ncc.iml b/.idea/ncc.iml
index 2c5c861..f554332 100644
--- a/.idea/ncc.iml
+++ b/.idea/ncc.iml
@@ -8,6 +8,8 @@
+
+
diff --git a/.idea/php.xml b/.idea/php.xml
index a87bd36..dec4919 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -1,11 +1,26 @@
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/Autoloaders.xml b/.idea/scopes/Autoloaders.xml
new file mode 100644
index 0000000..e193470
--- /dev/null
+++ b/.idea/scopes/Autoloaders.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/Installer_Source_files.xml b/.idea/scopes/Installer_Source_files.xml
new file mode 100644
index 0000000..f4cf65d
--- /dev/null
+++ b/.idea/scopes/Installer_Source_files.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/NCC_Source_files.xml b/.idea/scopes/NCC_Source_files.xml
index 6e38ebb..a65b319 100644
--- a/.idea/scopes/NCC_Source_files.xml
+++ b/.idea/scopes/NCC_Source_files.xml
@@ -1,3 +1,3 @@
-
+
\ No newline at end of file
diff --git a/.idea/scopes/Symfony_Filesystem.xml b/.idea/scopes/Symfony_Filesystem.xml
new file mode 100644
index 0000000..00b6846
--- /dev/null
+++ b/.idea/scopes/Symfony_Filesystem.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/Symfony_Process.xml b/.idea/scopes/Symfony_Process.xml
new file mode 100644
index 0000000..7d46ad8
--- /dev/null
+++ b/.idea/scopes/Symfony_Process.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/Symfony_Uid.xml b/.idea/scopes/Symfony_Uid.xml
new file mode 100644
index 0000000..0b4c6e4
--- /dev/null
+++ b/.idea/scopes/Symfony_Uid.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/Symfony_Yaml.xml b/.idea/scopes/Symfony_Yaml.xml
new file mode 100644
index 0000000..d2796d6
--- /dev/null
+++ b/.idea/scopes/Symfony_Yaml.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/Symfony_polyfill_ctype.xml b/.idea/scopes/Symfony_polyfill_ctype.xml
new file mode 100644
index 0000000..93bc242
--- /dev/null
+++ b/.idea/scopes/Symfony_polyfill_ctype.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/Symfony_polyfill_mbstring.xml b/.idea/scopes/Symfony_polyfill_mbstring.xml
new file mode 100644
index 0000000..9125e7b
--- /dev/null
+++ b/.idea/scopes/Symfony_polyfill_mbstring.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/Symfony_polyfill_uuid.xml b/.idea/scopes/Symfony_polyfill_uuid.xml
new file mode 100644
index 0000000..fc04e03
--- /dev/null
+++ b/.idea/scopes/Symfony_polyfill_uuid.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/defuse.xml b/.idea/scopes/defuse.xml
new file mode 100644
index 0000000..3c1064b
--- /dev/null
+++ b/.idea/scopes/defuse.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/defuse_php_encryption.xml b/.idea/scopes/defuse_php_encryption.xml
new file mode 100644
index 0000000..bff7e6a
--- /dev/null
+++ b/.idea/scopes/defuse_php_encryption.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/jelix.xml b/.idea/scopes/jelix.xml
new file mode 100644
index 0000000..6e134aa
--- /dev/null
+++ b/.idea/scopes/jelix.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/jelix_version.xml b/.idea/scopes/jelix_version.xml
new file mode 100644
index 0000000..89f8c5e
--- /dev/null
+++ b/.idea/scopes/jelix_version.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/nikic.xml b/.idea/scopes/nikic.xml
new file mode 100644
index 0000000..471928a
--- /dev/null
+++ b/.idea/scopes/nikic.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/nikic_PhpParser.xml b/.idea/scopes/nikic_PhpParser.xml
new file mode 100644
index 0000000..22f7509
--- /dev/null
+++ b/.idea/scopes/nikic_PhpParser.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/theseer.xml b/.idea/scopes/theseer.xml
new file mode 100644
index 0000000..c46f520
--- /dev/null
+++ b/.idea/scopes/theseer.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/theseer_Autoload.xml b/.idea/scopes/theseer_Autoload.xml
new file mode 100644
index 0000000..f64a6e8
--- /dev/null
+++ b/.idea/scopes/theseer_Autoload.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/scopes/theseer_DirectoryScanner.xml b/.idea/scopes/theseer_DirectoryScanner.xml
new file mode 100644
index 0000000..72d0ec3
--- /dev/null
+++ b/.idea/scopes/theseer_DirectoryScanner.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..f7a58e1
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,36 @@
+
+# Code of Conduct
+
+We are committed to maintaining a welcoming and inclusive environment for all contributors. In order to ensure that
+everyone feels safe and respected, we have established the following code of conduct.
+
+## Our Standards
+
+We expect all contributors to:
+
+- Be respectful and considerate of others
+- Use inclusive language
+- Avoid demeaning, discriminatory, or harassing behavior
+- Respect the boundaries of others
+
+We will not tolerate any behavior that does not align with these standards.
+
+## Consequences of Unacceptable Behavior
+
+Unacceptable behavior will not be tolerated and may result in consequences such as warning, blocking of access, or
+permanent removal from the project.
+
+## Reporting Unacceptable Behavior
+
+If you witness or experience any behavior that is not aligned with our code of conduct, please report it immediately by
+contacting the project maintainers. You can open an issue on the project's repository at [https://git.n64.cc/nosial/ncc](https://git.n64.cc/nosial/ncc).
+
+## Attribution
+
+This Code of Conduct is adapted from the Contributor Covenant, version 2.0, available at
+[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html).
+
+## License
+
+This Code of Conduct is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of
+this license, visit [http://creativecommons.org/licenses/by/4.0/](http://creativecommons.org/licenses/by/4.0/).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..2cd7c78
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,25 @@
+# Contributing to NCC
+
+We welcome contributions to NCC! If you have an idea for how to improve the project, please don't hesitate to reach out.
+There are many ways to contribute, and we appreciate all forms of support.
+
+## How to Contribute
+
+- **Report a bug**: If you think you have found a bug in NCC, please open an issue and include as much detail as
+ possible to help us reproduce and fix the issue.
+- **Request a feature**: Have an idea for a new feature or improvement? Open an issue to start a discussion.
+- **Submit a pull request**: If you have developed a fix for a bug or have implemented a new feature, please submit a
+ pull request for review.
+
+## Code of Conduct
+
+We are committed to maintaining a welcoming and inclusive environment for all contributors. Please read and follow our
+[Code of Conduct](https://git.n64.cc/nosial/ncc/CODE_OF_CONDUCT.md).
+
+## License
+
+By contributing to NCC, you agree that your contributions will be licensed under the MIT License.
+
+## Attribution
+
+NCC is Copyright (c) Nosial. Please include the copyright notice in any distributed code.
diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md
index 629dfaa..31793f0 100644
--- a/DOCUMENTATION.md
+++ b/DOCUMENTATION.md
@@ -5,30 +5,839 @@ NCC, from basic installation, basic usage, standards and much more.
## Table of contents
- - Introduction
- - What is NCC?
- - Advantages over other software
-
- ------------------------------------------------------------------------------------
+
+* [NCC Documentation](#ncc-documentation)
+ * [Table of contents](#table-of-contents)
+ * [Introduction](#introduction)
+ * [What is NCC?](#what-is-ncc)
+* [Building NCC from source](#building-ncc-from-source)
+ * [Requirements to build](#requirements-to-build)
+ * [Installing phpab](#installing-phpab)
+ * [Building NCC](#building-ncc)
+ * [Redist](#redist)
+ * [Tar](#tar)
+* [Installing NCC](#installing-ncc)
+ * [Command line arguments](#command-line-arguments)
+* [Uninstalling NCC](#uninstalling-ncc)
+* [NCC Command-line Interface](#ncc-command-line-interface)
+ * [Management Commands](#management-commands)
+ * [Utility Commands](#utility-commands)
+ * [Options](#options)
+* [Projects](#projects)
+ * [Creating a project](#creating-a-project)
+ * [project.json structure](#projectjson-structure)
+ * [project](#project)
+ * [project.compiler](#projectcompiler)
+ * [project.update_source](#projectupdatesource)
+ * [project.update_source.repository](#projectupdatesourcerepository)
+ * [assembly](#assembly)
+ * [execution_policies](#executionpolicies)
+ * [execution_policy](#executionpolicy)
+ * [execution_policy.execute](#executionpolicyexecute)
+ * [execution_policy.exit_handlers](#executionpolicyexithandlers)
+ * [exit_handler](#exithandler)
+ * [installer](#installer)
+ * [build](#build)
+ * [dependency](#dependency)
+ * [Source Types](#source-types)
+* [Execution Policies](#execution-policies)
+ * [Supported Runners](#supported-runners)
+ * [Configuring Runners](#configuring-runners)
+* [Remote Sources](#remote-sources)
+ * [Supported sources](#supported-sources)
+ * [Default sources](#default-sources)
+ * [Managing sources](#managing-sources)
+ * [Adding a source](#adding-a-source)
+ * [Removing a source](#removing-a-source)
+ * [Listing sources](#listing-sources)
+ * [Credential Management](#credential-management)
+ * [Adding credentials](#adding-credentials)
+ * [Removing credentials](#removing-credentials)
+ * [Listing credentials](#listing-credentials)
+* [UUIDs](#uuids)
+* [Versioning](#versioning)
+ * [Version Format](#version-format)
+ * [Version Format Compatibility](#version-format-compatibility)
+* [Naming a package](#naming-a-package)
+ * [Naming conventions](#naming-conventions)
+ * [References](#references)
+* [Error Codes](#error-codes)
+
+## Introduction
-# Introduction (May 24, 2022)
-
-This section serves the basic introduction of NCC, what it's used for and how you can
-use it in your own projects or use it to run and build other projects that are designed
-to be used with NCC.
+This section serves the basic introduction of NCC, what it's used for and how you can use it in your own projects or use
+it to run and build other projects that are designed to be used with NCC.
## What is NCC?
-NCC (*Acronym for **N**osial **C**ode **C**ompiler*) is a multi-purpose compiler,
-package manager and toolkit. Allowing projects to be managed and built more easily
-without having to mess with all the traditional tools that comes with your language
-of choice. Right now NCC only supports PHP as it's written in PHP but extensions
-for other languages/frameworks can be built into the software in the future when
-the need comes for it.
+NCC (*Acronym for **N**osial **C**ode **C**ompiler*) is a multi-purpose compiler, package manager and toolkit. Allowing
+projects to be managed and built more easily without having to mess with all the traditional tools that comes with your
+language of choice. Right now NCC only supports PHP as it's written in PHP but extensions for other languages/frameworks
+can be built into the software in the future when the need comes for it.
-NCC can make the process of building your code into a redistributable package much
-more efficient by treating each building block of your project as a component that
-is interconnected in your environment instead of the more popular route taken by
-package/dependency managers such as [composer](https://getcomposer.org/),
-[npm](https://www.npmjs.com/) or [pypi (or pip)](https://pypi.org/).
\ No newline at end of file
+NCC can make the process of building your code into a redistributable package much more efficient by treating each
+building block of your project as a component that is interconnected in your environment instead of the more popular
+route taken by package/dependency managers such as [composer](https://getcomposer.org/),[npm](https://www.npmjs.com/) or
+[pypi (or pip)](https://pypi.org/).
+
+
+------------------------------------------------------------------------------------
+
+# Building NCC from source
+
+Building NCC from source is easy with very few requirements to start building. At the moment ncc can only be debugged or
+tested by building a redistributable source and installing it.
+
+## Requirements to build
+
+- php8.0+
+- php-mbstring
+- php-ctype
+- php-common (covers tokenizer & posix among others)
+- make
+- phpab
+- tar *(optional)*
+
+## Installing phpab
+
+phpab is also known as [PHP Autoload Builder](https://github.com/theseer/Autoload), phpab is an open source tool used
+for creating autoload files, ncc needs this tool in order to generate it's autoload files whenever there's any changes
+to its source code.
+
+This tool is only required for building and or creating a redistributable package of ncc. This component is not
+required to be installed to use ncc.
+
+for some components that require static loading, ncc will automatically load it using its own
+[autoloader](src/autoload/autoload.php)
+
+The recommended way to install phpab is by using [phive](https://phar.io/), if you don't have phive installed you can
+install it by running these commands in your terminal (from the official documentation)
+
+```shell
+wget -O phive.phar https://phar.io/releases/phive.phar
+wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
+gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
+gpg --verify phive.phar.asc phive.phar
+chmod +x phive.phar
+sudo mv phive.phar /usr/local/bin/phive
+```
+
+Once phive is installed, you can run the final command to install phpab
+
+```shell
+sudo phive install phpab --global
+```
+
+or you can run this command to install it locally
+
+```shell
+phive install phpab
+```
+
+**Note:** Optionally, you may want to have `phab` available in your `$PATH`, this can be done with this command.
+*(Replace `x.xx.x` with your version number)* this is if you installed it locally
+
+```shell
+ln -s /home/user/.phive/phars/phpab-x.xx.x.phar /usr/local/bin/phpab
+```
+
+## Building NCC
+
+First, navigate to the main directory of NCC's source code where the [Makefile](Makefile) is present. If you
+already attempted to or had built ncc before, it's recommended to use `make clean` before building.
+
+### Redist
+
+Running `redist` from the Makefile will generate all the required autoloader for ncc and move all the required files
+into one redistributable source folder under a directory called `build/src`
+
+```shell
+make redist
+```
+
+
+### Tar
+
+Running `tar` will run redist before packaging the redistributable source into a tar.gz file that can be distributed to
+other machines, this process is not a requirement.
+
+```shell
+make tar
+```
+
+Once you have a populated `build/src` folder, you can simply run execute the `installer` file to install your build of
+ncc onto the running machine.
+
+------------------------------------------------------------------------------------
+
+# Installing NCC
+
+Installing NCC is easy, you can either download the redistributable source from the [releases](https://git.n64.cc/nosial/ncc/-/releases)
+page or you can build it from source using the instructions above.
+
+Once you have the redistributable source, you can simply run execute the `INSTALL` file to install ncc onto the running
+machine.
+
+## Command line arguments
+
+The installer accepts a few command line arguments that can be used to customize the installation process.
+
+`--help` Displays the help message
+
+`--auto` Automatically installs ncc without asking for user input.
+
+**Note:** To install composer along with ncc, you must also provide the `--install-composer` argument.
+
+`--install-composer` Installs composer along with ncc. By default, ncc will not install composer and during the
+installation process you will be asked if you want to install composer along-side ncc, this will not conflict
+with any existing composer installation.
+
+`--install-dir` Specifies the directory where ncc will be installed to. By default, ncc will be installed to `/etc/ncc`
+
+`--bypass-cli-check` Bypasses the check in the installer that checks if the installer is being run from the command
+line, this is useful if you want to install ncc from a script.
+
+`--bypass-checksum` Bypasses the checksum check in the installer, this is useful if you made modifications to the
+installation files and want to install a modified version of ncc.
+
+But this isn't recommended and the proper way to do this is to modify the source code and build ncc from source,
+the Makefile task will automatically rebuild the checksum file for you.
+
+
+------------------------------------------------------------------------------------
+
+# Uninstalling NCC
+
+Uninstalling NCC is easy, simply delete the directory where ncc was installed to, by default this is `/etc/ncc`.
+
+It's recommended to run `ncc package --uninstall-all` before uninstalling ncc, this will uninstall all the packages
+that were installed using ncc and remove any artifacts that were created by these packages.
+
+**Note:**
+ - To delete all the data that ncc has created, you can also delete the `/var/ncc` directory.
+ - Finally, remove the symlink that was created in `/usr/local/bin`to the `ncc` entry point file.
+
+------------------------------------------------------------------------------------
+
+# NCC Command-line Interface
+
+NCC provides a command-line interface that can be used to manage packages, create projects, compile source code, manage
+remote sources, configure ncc, and more. You can run `ncc --help` to see a list of all the available commands.
+
+
+
+## Management Commands
+
+Management commands are used to manage ncc's configuration, remote sources, and packages.
+
+
+`project` Manage or create a project (*see [Projects](#projects) section*)
+
+`package` Manage packages
+
+`source` Manage remote sources
+
+`config` Manage ncc's configuration
+
+## Utility Commands
+
+Utility commands are used to perform tasks in the current directory or project.
+
+`build` Compile source code of the project
+
+`exec` Executes a package's entry point file (package must be installed)
+
+## Options
+
+NCC also accepts a few command line arguments that can be used to alter the behavior of the command-line interface.
+
+`-l , --log-level ` Sets the log level, this can be one of `debug`, `verbose`, `info`, `warn`, `error`, `fatal`
+
+`-v, --version` Displays the version of ncc
+
+`-h, --help` Displays the help message
+
+`--basic-ascii` Renders some messages using basic ASCII characters instead of unicode characters
+
+`--no-color` Disables colored output
+
+`--no-banner` Omits displaying the NCC graphical banner
+
+------------------------------------------------------------------------------------
+
+# Projects
+
+A project is a directory that contains all the source files to your program, it's similar to a workspace in other IDEs.
+Usually contains a `project.json` file which contains all the information about the project that ncc needs to know.
+
+This can include the name of the program, the version of the program, the author of the program, the dependencies of the
+program, build configurations, and more.
+
+This section will cover the basics of creating a project and managing it and the technical details of the `project.json`
+file.
+
+
+## Creating a project
+
+This is the first step in using ncc, you must create a project before you can do anything else (*not really because you
+can install packages without needing to create a project and run them directly, but you get the point*)
+
+The NCC command-line tool provides a management command called `project` that can be used to create a new project
+or to manage an existing project.
+
+```shell
+ncc project create --package "com.example.program" --name "Example Program"
+```
+
+This command will create a new project in the current directory, the `--package` argument specifies the package name of
+the project, this is used to identify the project and to avoid conflicts with other projects that may have the same name.
+
+The `--name` argument specifies the name of the project, this is used to display the name of the project in the project
+manager and in the project settings. This doesn't have to be the same as the package name or unique.
+
+**Note:** If the options are not provided, the command will prompt you for the package name and the project name.
+
+For more information about the project command, you can run `ncc project --help` to display the help message.
+
+## project.json structure
+
+The `project.json` file is a JSON file that contains all the information about the project.
+
+When a project is created, the `project.json` file is automatically created and populated with the default values, you
+can modify this file to change the default values or to add more information about the project.
+
+This section will go over the structure of the `project.json` file and what each field does.
+
+### project
+
+The `project` field contains information about the project, such as what compiler extension to use, options to pass on
+to the compiler, and more.
+
+| Name | Type | Required | Description |
+|---------------|--------------------------------------|----------|----------------------------------------------------------------------------------------------------|
+| compiler | [project.compiler](#projectcompiler) | Yes | The compiler extension that the project uses to compile the program |
+| options | `array` | No | An array of options to pass on to the compiler, the options vary depending on the compiler and NCC |
+| update_source | `project.update_source` | No | The source for where the program can fetch updates from |
+
+### project.compiler
+
+The `project.compiler` field contains information about the compiler extension that the project uses to compile
+the program.
+
+| Name | Type | Required | Description |
+|-----------------|----------|----------|------------------------------------------------------------------------------------------------|
+| extension | `string` | Yes | The name of the compiler extension that the project uses to compile the program |
+| minimum_version | `string` | No | The minimum version of the compiler extension that the project requires to compile the program |
+| maximum_version | `string` | No | The maximum version of the compiler extension that the project requires to compile the program |
+
+### project.update_source
+
+The `project.update_source` field contains information about the source where the program can fetch updates from.
+
+| Name | Type | Required | Description |
+|------------|------------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------|
+| source | `string` | Yes | The source where the program can fetch updates from, see [Remote Sources](#remote-sources) for additional information |
+| repository | `project.update_source.repository` | Yes | The source to configure in NCC when installing the package |
+
+### project.update_source.repository
+
+The `project.update_source.repository` field contains information about the source to configure in NCC when installing
+the package. This allows you to set up a remote source that your package can use to fetch updates from, this is useful
+if you want to distribute your program to other people.
+
+It would be useful to read more about [Remote Sources](#remote-sources) before continuing.
+
+| Name | Type | Required | Description |
+|------|----------|----------|---------------------------------------------------------------------------------------|
+| name | `string` | Yes | The name of the source to configure in NCC when installing the package (eg; `github`) |
+| type | `string` | Yes | The API type to use with this source, see [Supported sources](#supported-sources) |
+| host | `string` | Yes | The host of the source, this is the domain name of the source (eg; `api.github.com`) |
+| ssl | `bool` | No | Whether to use SSL or not when connecting to this source |
+
+
+### assembly
+
+The `assembly` field contains metadata about the program, such as the name, version, description, so on.
+
+| Name | Type | Required | Description |
+|-------------|----------|----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| name | `string` | Yes | The name of the package, this is used to display the name of the package (eg; `Example Program`) |
+| package | `string` | Yes | The package name of the program, this is used to identify the package and to avoid conflicts with other packages that may have the same name, see [Naming a package](#naming-a-package) for additional information |
+| description | `string` | No | The description of the package, this is used to display a description of the package when installing |
+| company | `string` | No | The company that created the package, this is used to display the company that created the package when installing |
+| product | `string` | No | The product that the package is a part of, this is used to display the product that the package is a part of when installing |
+| copyright | `string` | No | The copyright of the package |
+| trademark | `string` | No | The trademark of the package |
+| version | `string` | Yes | The version of the package, see [Versioning](#versioning) for additional information |
+| uuid | `string` | Yes | The UUID of the package, see [UUIDs](#uuids) for additional information |
+
+### execution_policies
+
+The `execution_policies` field contains information about the execution policies that the program uses, such as
+the execution policy that the program uses to run additional programs during different stages of the installation
+process of the package or used as the main execution policy for the program.
+
+Note that this field is an array of `execution_policy` objects, see [execution_policy](#executionpolicy) for additional
+information.
+
+For more information about execution policies, see [Execution Policies](#execution-policies).
+
+#### execution_policy
+
+The `execution_policy` object contains information about the execution policy.
+
+| Name | Type | Required | Description |
+|---------------|----------------------------------|----------|----------------------------------------------------------------------------------------------------|
+| name | `string` | Yes | The name of the execution policy, this is used to identify the execution policy |
+| runner | `string` | Yes | The name of the runner that the execution policy uses, see [Supported runners](#supported-runners) |
+| message | `string` | No | The message to display when the execution policy is being run |
+| execute | `execution_policy.execute` | Yes | The execution policy to run when the execution policy is being run |
+| exit_handlers | `execution_policy.exit_handlers` | No | The exit handlers to run when the execution policy has finished running |
+
+#### execution_policy.execute
+
+The `execution_policy.execute` object contains information about how to run the execution policy when triggered.
+
+| Name | Type | Required | Description |
+|-----------------------|------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
+| target | `string` | Yes | The target file to run when the execution policy is triggered, file path is relative to the location of your project.json file (eg; scripts/main.php) |
+| working_directory | `string` | No | The working directory to run the process in. If not specified, the working directory will be your current working directory. |
+| options | `string[]` | No | The options to pass to the process when running it. (eg; ["--foo", "bar", "-f"]) |
+| environment_variables | `string[]` | No | The environment variables to pass to the process when running it. (eg; ["FOO=bar"]) |
+| silent | `bool` | No | Whether to run the process silently or not. If not specified, the process will not be run silently. |
+| tty | `bool` | No | Whether to run the process in a TTY or not. If not specified, the process will not be run in a TTY. |
+| timeout | `int` | No | The timeout of the process in seconds. If not specified, the process will not have a timeout. |
+| idle_timeout | `int` | No | The idle timeout of the process in seconds. If not specified, the process will not have an idle timeout. |
+
+#### execution_policy.exit_handlers
+
+The `execution_policy.exit_handlers` object contains information about how to run the exit handlers when the execution
+policy has finished running. This is useful for running additional policies when the process has exited in a specific
+way.
+
+The two handlers that can be executed automatically despite the exit code are `success` and `error`. Which means if the
+process exits with a success exit code, the `success` handler will be run, and if the process exits with an error exit
+code, the `error` handler will be run. The `warning` handler will only be run if the process exits with specified exit
+code.
+
+| Name | Type | Required | Description |
+|---------|----------------|----------|-------------------------------------------------------------------------------|
+| success | `exit_handler` | No | The exit handler to run when the process has exited successfully. |
+| warning | `exit_handler` | No | The exit handler to run when the process has exited with a warning exit code. |
+| error | `exit_handler` | No | The exit handler to run when the process has exited with an error exit code. |
+
+#### exit_handler
+
+The `exit_handler` object contains information about how to run the exit handler when the execution policy has finished
+running.
+
+| Name | Type | Required | Description |
+|---------------|----------|----------|---------------------------------------------------------------------------------------------------------------------------------------------|
+| message | `string` | No | The message to display when the exit handler is triggered |
+| end_execution | `bool` | No | Whether to end the execution of the program or not if this exit handler is triggered. If not specified, the program will not end execution. |
+| run | `string` | No | The name of the execution policy to run when this exit handler is triggered. |
+| exit_code | `int` | No | The exit code that the process must have exited with for this exit handler to be triggered. |
+
+### installer
+
+The `installer` field contains allows you to configure the execution of policies during different stages of the
+installation process of the package. Note that these files only accepts an array of strings, which are the names of
+the execution policies that you want to run during the specified stage. NCC will reject the package if the execution
+policy does not exist.
+
+| Name | Type | Required | Description |
+|----------------|------------|----------|-------------------------------------------------------------------------|
+| pre_install | `string[]` | No | The execution policies to run before the installation of the package. |
+| post_install | `string[]` | No | The execution policies to run after the installation of the package. |
+| pre_uninstall | `string[]` | No | The execution policies to run before the uninstallation of the package. |
+| post_uninstall | `string[]` | No | The execution policies to run after the uninstallation of the package. |
+| pre_update | `string[]` | No | The execution policies to run before the update of the package. |
+| post_update | `string[]` | No | The execution policies to run after the update of the package. |
+
+
+### build
+
+The `build` field contains the configuration for the build process of the package. This field is required and must be
+configured correctly for the package to be built successfully.
+
+| Name | Type | Required | Description |
+|-----------------------|-------------------------|----------|----------------------------------------------------------------------------------------------------------------------------|
+| source_path | `string` | Yes | The path to the source directory of the package. (eg; src) |
+| default_configuration | `string` | Yes | The default build configuration to use when building the package. |
+| exclude_files | `string[]` | No | The files to exclude from the build process. |
+| options | `string[]` | No | The options to pass to the build process. |
+| main | `string` | No | The main execution policy to run when the package is executed, this is like the main entry point of the package. |
+| define_constants | `string[]` | No | Environment constants to define during the execution of your program, these values can be accessed by the NCC Runtime API. |
+| pre_build | `string[]` | No | The execution policies to run before the build process. |
+| post_build | `string[]` | No | The execution policies to run after the build process. |
+| dependencies | `dependency[]` | No | The dependencies that the package requires |
+| configurations | `build_configuration[]` | No | Predefined build configurations that can be used to produce different builds of the package |
+
+### dependency
+
+The `dependency` object contains information about a dependency that the package requires.
+
+| Name | Type | Required | Description |
+|-------------|----------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| name | `string` | Yes | The package name of the dependency (eg; com.example.package) |
+| source_type | `string` | No | Where NCC should get the dependency from, accepted values are `static`, `local` or `remote`. If not specified, NCC will assume `remote`. |
+| source | `string` | No | The source of the dependency, this can a remote source (see [Remote Sources](#remote-sources)) if the source is `remote` or a a local file path if the source is `static` |
+| version | `string` | No | The version of the dependency to use, if not specified, NCC will use the latest version of the dependency. (eg; 'latest') |
+
+#### Source Types
+
+Dependency source types are used to specify where NCC should get the dependency from, these are:
+
+- `static` - This source type is used to specify that the dependency is a local file path, this is useful for dependencies
+that are not available on the remote package repository or to bundle dependencies with the package. You can only link to
+pre-compiled .ncc packages, otherwise NCC will fail to install the package.
+- `local` - This source type is used to specify that the dependency is a local package that is already installed on the
+system. This is useful for dependencies that are already installed on the system, and you want to use them in your package
+but doesn't necessarily need to pull them from a remote repository or local path. NCC expects the package to be installed
+otherwise installing the package will fail unless `--skip-dependencies` is specified.
+- `remote` - This source type is used to specify that the dependency is a remote package that is available on the remote
+repository. This is the recommended source type to use for dependencies that are available on the remote repository.
+
+------------------------------------------------------------------------------------
+
+# Execution Policies
+
+Execution policies are the policies that are used to run additional programs during different stages of the installation
+or execution of the package. These policies are defined in your project.json `execution_policies` field with unique
+names for each policy, so that these policies can be referenced by other policies or by NCC if configured to do so.
+
+## Supported Runners
+
+At the moment, NCC only supports a select few "runners" that can be used to run the policies, these runners are:
+
+- `php` - This runner is used to run PHP scripts, it is the default runner for NCC
+- `bash` - This runner is used to run bash scripts
+- `python` - This runner is used to run python scripts
+- `python2` - This runner is used to run python2 scripts
+- `python3` - This runner is used to run python3 scripts
+- `perl` - This runner is used to run perl scripts
+- `lua` - This runner is used to run lua scripts
+
+ > Note: these runners are not installed by default, you will need to install them yourself.
+
+## Configuring Runners
+
+If for some reason NCC cannot automatically detect the runner that you want to use, you can configure the runner yourself
+by modifying your configuration file. The configuration file is located at `/var/ncc/ncc.yaml` under the `runners` field.
+
+Or you can modify the configuration file by running the following command:
+
+```bash
+ncc config -p runners.bash -v /usr/bin/bash
+```
+
+This will set the `bash` runner to use the `/usr/bin/bash` binary.
+
+ > **Note:** You must have root permissions to modify the configuration file.
+
+------------------------------------------------------------------------------------
+
+# Remote Sources
+
+Remote Sources are the locations where packages can be downloaded from, they are similar to repositories in other package
+managers. They follow a simple syntax that allows you to specify the type of source, the location of the source, and more.
+
+Examples of sources are:
+
+- `symfony/process=latest@composer` - This is a package from the `symfony/process` package from the `composer` source
+- `nosial/libs.config=latest@n64` - This is a package from the `nosial/libs.config` package from the `git.n64.cc` source
+
+A full example syntax may look like this:
+
+```
+/:=@
+```
+
+This syntax is used to specify a package from a source, the syntax is split into 4 parts:
+
+- The vendor of the package
+- The name of the package
+- The branch of the package (optional)
+- The version of the package (optional)
+- The name of the source (needs to be configured in ncc)
+
+## Supported sources
+
+NCC supports the following sources:
+
+- `github` - This source uses the GitHub API to fetch packages from GitHub (Included in the default sources)
+- `gitlab` - This source uses the GitLab API to fetch packages from GitLab (Can be used with self-hosted GitLab instances)
+
+Additional support for other sources will be added in the future.
+
+## Default sources
+
+NCC comes with a few default sources that are configured by default, these sources are:
+
+- packagist.org (`composer`) **Note:** This is an internal source that uses `composer` to fetch packages from packagist.org.
+ this is not configurable by the user.
+- api.github.com (`github`)
+- gitlab.com (`gitlab`)
+- git.n64.cc (`n64`)
+- gitgud.io (`gitgud`)
+
+Additional sources can be added by the user. See [Adding a source](#adding-a-source) for more information.
+
+## Managing sources
+
+You can manage sources using the `source` command in the ncc command-line tool. This command can be used to add, remove,
+and list sources. For more information about the `source` command, you can run `ncc source --help` to display the help
+message.
+
+### Adding a source
+
+To add a source, you can use the `add` command in the ncc `source` command-line tool.
+
+```shell
+ncc source add --name "github" --type "github" --host "github.com" --ssl
+```
+
+This command will add a new source called `github` with the type `github` and the host `github.com`, the `--ssl` option
+will tell ncc to use HTTPS instead of HTTP when fetching packages from this source.
+
+The reason to specify the type of source is to tell ncc what API to use when fetching packages from this source, for
+example if you specify the type as `github` then ncc will use the GitHub API to fetch packages from this source so it's
+important to specify the correct type when adding a source.
+
+> **Note:** You need root permissions to add a source
+
+
+### Removing a source
+
+To remove a source, you can use the `remove` command in the ncc `source` command-line tool.
+
+```shell
+ncc source remove --name "github"
+```
+
+> **Note:** You need root permissions to remove a source
+
+> **Note:** Removing a source also removes the ability for some packages to be fetched or updated from this source
+
+
+### Listing sources
+
+To list all the sources, you can use the `list` command in the ncc `source` command-line tool.
+
+```shell
+ncc source list
+```
+
+## Credential Management
+
+Some sources require credentials to be able to fetch packages from them, for example the `gitlab` source requires
+credentials to be able to fetch packages from a self-hosted GitLab instance. NCC supports storing credentials for
+sources in a secure way using the `cred` command in the ncc command-line tool.
+
+### Adding credentials
+
+To add credentials for a source, you can use the `add` command in the ncc `cred` command-line tool.
+
+```shell
+ncc cred add --alias "My Alias" --auth-type login --username "myusername" --password "mypassword"
+```
+
+To add a private access token as a credential, you can specify the `--auth-type` as `pat` and specify the token as
+`--token` instead of providing `--username` and `--password`.
+
+```shell
+ncc cred add --alias "My Alias" --auth-type pat --token="mytoken"
+```
+
+By default, ncc will encrypt the entry except for the alias using the password/token that you provide.
+
+However, because it's encrypted you will need to provide the password/token when using the credential since ncc will
+not be able to decrypt the entry without a password. To avoid being asked for the password/token every time you use the
+credential, you can pass on the `--no-encryption` option to the `cred` command-line tool.
+
+```shell
+ncc cred add --alias "My Alias" --auth-type login --username "myusername" --password "mypassword" --no-encryption
+```
+
+Encryption is applied individually to each credential, so you can have some credentials encrypted and some not encrypted.
+
+> **Note:** You need root permissions to add credentials
+
+
+### Removing credentials
+
+To remove credentials, you can use the `remove` command in the ncc `cred` command-line tool.
+
+```shell
+ncc cred remove --alias "My Alias"
+```
+
+> **Note:** You need root permissions to remove credentials
+
+
+### Listing credentials
+
+To list all the credentials, you can use the `list` command in the ncc `cred` command-line tool. this will return
+a list of all the credentials that are stored in the credential store with additional information about each entry.
+
+```shell
+ncc cred list
+```
+
+------------------------------------------------------------------------------------
+
+# UUIDs
+
+UUIDs are used to uniquely identify a package, at the moment ncc doesn't do anything meaningful with UUIDs but in the
+future it will be used to identify packages and to prevent conflicts between packages with the same name.
+
+The standard UUID format used is version 1, which is a time-based UUID. This means that the UUID is generated using
+the current time and the MAC address of the computer that generated the UUID.
+
+``````
+xxxxxxxx-xxxx-1xxx-yxxx-xxxxxxxxxxxx
+``````
+
+UUIDs are automatically generated when a package is created, you can also manually specify a UUID by editing the
+`project.json` file in the project directory, this field is found under `assembly.uuid`, see [assembly](#assembly) for
+more information.
+
+> **Note:** Invalid UUIDs will cause the package to be rejected by ncc
+
+------------------------------------------------------------------------------------
+
+# Versioning
+
+NCC uses a standard versioning system, this system is based on the [Semantic Versioning](https://semver.org/) system.
+
+## Version Format
+
+The version format is as follows:
+
+``````
+MAJOR.MINOR.PATCH
+``````
+
+- `MAJOR` is the major version of the package, this version is incremented when a major change is made to the package
+- `MINOR` is the minor version of the package, this version is incremented when a minor change is made to the package
+- `PATCH` is the patch version of the package, this version is incremented when a patch is made to the package
+
+
+## Version Format Compatibility
+
+NCC will attempt to convert non-compatible versions to a compatible version when it comes to installing packages that
+isn't built for ncc.
+
+> **Note:** NCC will reject packages with invalid version numbers, sometimes this can happen when the compatibility layer
+fails or when the version number is invalid.
+
+------------------------------------------------------------------------------------
+
+# Naming a package
+
+NCC Follows the same naming convention as Java's naming convention. The purpose of naming a package this way is
+to easily create a "Name" of the package, this string of information contains
+
+- The developer/organization behind the package
+- The package name itself
+
+
+## Naming conventions
+
+Package names are written in all lower-case due to the fact that some operating systems treats file names
+differently, for example on Linux `Aa.txt` and `aa.txt`are two entirely different file names because of the
+capitalization and on Windows it's treated as the same file name.
+
+Organizations or small developers use their domain name in reverse to begin their package names, for example
+`net.nosial.example` is a package named `example` created by a programmer at `nosial.net`
+
+Just like the Java naming convention, to avoid conflicts of the same package name developers can use something
+different, for example as pointed out in Java's package naming convention developers can instead use something
+like a region to name packages, for example `net.nosial.region.example`
+
+
+## References
+
+For Java's package naming conventions see [Naming a Package](https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html)
+from the Oracle's Java documentation resource, as the same rules apply to NCC except for *some* illegal naming
+conventions such as packages not being able to begin with `int` or numbers
+
+------------------------------------------------------------------------------------
+
+# Error Codes
+
+NCC uses error codes to identify errors, these error codes are used to identify errors in the ncc command-line tool
+and in the ncc API.
+
+| Error Code | Name |
+|:----------:|:--------------------------------------|
+| `-1700` | InvalidProjectConfigurationException |
+| `-1701` | FileNotFoundException |
+| `-1702` | DirectoryNotFoundException |
+| `-1703` | InvalidScopeException |
+| `-1704` | AccessDeniedException |
+| `-1705` | MalformedJsonException |
+| `-1706` | RuntimeException |
+| `-1707` | InvalidCredentialsEntryException |
+| `-1708` | ComponentVersionNotFoundException |
+| `-1709` | ConstantReadonlyException |
+| `-1710` | InvalidPackageNameException |
+| `-1711` | InvalidVersionNumberException |
+| `-1712` | InvalidProjectNameException |
+| `-1713` | ProjectAlreadyExistsException |
+| `-1714` | AutoloadGeneratorException |
+| `-1715` | NoUnitsFoundException |
+| `-1716` | UnsupportedPackageException |
+| `-1717` | NotImplementedException |
+| `-1718` | InvalidPackageException |
+| `-1719` | InvalidConstantNameException |
+| `-1720` | PackagePreparationFailedException |
+| `-1721` | BuildConfigurationNotFoundException |
+| `-1722` | InvalidProjectBuildConfiguration |
+| `-1723` | UnsupportedCompilerExtensionException |
+| `-1724` | InvalidPropertyValueException |
+| `-1725` | InvalidVersionConfigurationException |
+| `-1726` | UnsupportedExtensionVersionException |
+| `-1727` | BuildException |
+| `-1728` | PackageParsingException |
+| `-1729` | PackageLockException |
+| `-1730` | InstallationException |
+| `-1731` | UnsupportedComponentTypeException |
+| `-1732` | ComponentDecodeException |
+| `-1733` | ComponentChecksumException |
+| `-1734` | ResourceChecksumException |
+| `-1735` | IOException |
+| `-1736` | UnsupportedRunnerException |
+| `-1737` | VersionNotFoundException |
+| `-1738` | UndefinedExecutionPolicyException |
+| `-1739` | InvalidExecutionPolicyName |
+| `-1740` | ProjectConfigurationNotFoundException |
+| `-1741` | RunnerExecutionException |
+| `-1742` | NoAvailableUnitsException |
+| `-1743` | ExecutionUnitNotFoundException |
+| `-1744` | PackageAlreadyInstalledException |
+| `-1745` | PackageNotFoundException |
+| `-1746` | ComposerDisabledException |
+| `-1747` | InternalComposerNotAvailable |
+| `-1748` | ComposerNotAvailableException |
+| `-1749` | ComposerException |
+| `-1750` | UserAbortedOperationException |
+| `-1751` | MissingDependencyException |
+| `-1752` | HttpException |
+| `-1753` | UnsupportedRemoteSourceTypeException |
+| `-1754` | GitCloneException |
+| `-1755` | GitCheckoutException |
+| `-1756` | GitlabServiceException |
+| `-1757` | ImportException |
+| `-1758` | GitTagsException |
+| `-1759` | GithubServiceException |
+| `-1760` | AuthenticationException |
+| `-1761` | NotSupportedException |
+| `-1762` | UnsupportedProjectTypeException |
+| `-1763` | UnsupportedArchiveException |
+| `-1764` | ArchiveException |
+| `-1765` | PackageFetchException |
+| `-1766` | InvalidBuildConfigurationException |
+| `-1767` | InvalidDependencyConfiguration |
+| `-1768` | SymlinkException |
diff --git a/LICENSE b/LICENSE
index 3fad6f1..723b74e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,355 +1,14 @@
-------------------------
-Nosial - NCC
+Copyright 2022-2023 Nosial - All Rights Reserved.
-Copyright (C) 2022-2022. Nosial - All Rights Reserved.
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following conditions:
-Unauthorized copying of this file, via any medium is strictly prohibited
-Proprietary and confidential, written by Zi Xing Narrakas
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
+Software.
-------------------------
-Symfony - Process
-
-Copyright (c) 2004-2022 Fabien Potencier
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-------------------------
-defuse - php-encryption
-
-The MIT License (MIT)
-
-Copyright (c) 2016 Taylor Hornby
-Copyright (c) 2016 Paragon Initiative Enterprises .
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-------------------------
-Symfony - uid
-
-Copyright (c) 2020-2022 Fabien Potencier
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-
-------------------------
-Symfony - polyfill-mbstring
-
-Copyright (c) 2015-2019 Fabien Potencier
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-
-------------------------
-Symfony - polyfill-ctype
-
-Copyright (c) 2018-2019 Fabien Potencier
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-
-------------------------
-Symfony - polyfill-uuid
-
-Copyright (c) 2018-2019 Fabien Potencier
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-
-
-------------------------
-dealnews.com, Inc. - Inline ProgressBar CLI
-
-Copyright (c) 2010, dealnews.com, Inc. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of dealnews.com, Inc. nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
-
-------------------------
-Symfony - Filesystem
-
-Copyright (c) 2004-2022 Fabien Potencier
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-
-------------------------
-Symfony - Yaml
-
-Copyright (c) 2004-2022 Fabien Potencier
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is furnished
-to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-------------------------
-theseer - Autoload
-
-Autoload Builder
-
-Copyright (c) 2010-2016 Arne Blankerts and Contributors
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of Arne Blankerts nor the names of contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
-
-------------------------
-theseer - DirectoryScanner
-
-DirectoryScanner
-
-Copyright (c) 2009-2014 Arne Blankerts
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of Arne Blankerts nor the names of contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER ORCONTRIBUTORS
-BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
-OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
-
-
-------------------------
-nikic - PhpParser
-
-BSD 3-Clause License
-
-Copyright (c) 2011, Nikita Popov
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-------------------------
-jelix - version
-
-Copyright (C) 2009-2016 Laurent Jouanneau
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 6931230..f2a4382 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
-PHPCC=/usr/bin/php
-PHPAB=/usr/bin/phpab
+PHPCC:=$(shell which php)
+PHPAB:=$(shell which phpab)
BUILD_PATH=build
SRC_PATH=src
@@ -80,25 +80,28 @@ $(SRC_PATH)/ncc/autoload_spl.php:
$(SRC_PATH)/ncc/Objects \
$(SRC_PATH)/ncc/Runtime \
$(SRC_PATH)/ncc/Utilities \
- $(SRC_PATH)/ncc/ncc.php
+ $(SRC_PATH)/ncc/ncc.php \
+ $(SRC_PATH)/ncc/Runtime.php
redist: autoload
rm -rf $(BUILD_PATH)/src
mkdir -p $(BUILD_PATH)/src
cp -rf $(SRC_PATH)/ncc/* $(BUILD_PATH)/src
- cp $(SRC_PATH)/installer/installer $(BUILD_PATH)/$(SRC_PATH)/INSTALL
- cp $(SRC_PATH)/installer/ncc.sh $(BUILD_PATH)/$(SRC_PATH)/ncc.sh
- cp $(SRC_PATH)/config/ncc.yaml $(BUILD_PATH)/$(SRC_PATH)/default_config.yaml;
- cp $(SRC_PATH)/config/ncc.yaml $(BUILD_PATH)/$(SRC_PATH)/CLI/template_config.yaml;
- cp $(SRC_PATH)/installer/extension $(BUILD_PATH)/$(SRC_PATH)/extension
- chmod +x $(BUILD_PATH)/$(SRC_PATH)/INSTALL
- cp LICENSE $(BUILD_PATH)/$(SRC_PATH)/LICENSE
- cp README.md $(BUILD_PATH)/$(SRC_PATH)/README.md
- cp $(SRC_PATH)/installer/hash_check.php $(BUILD_PATH)/$(SRC_PATH)/hash_check.php; $(PHPCC) $(BUILD_PATH)/$(SRC_PATH)/hash_check.php; rm $(BUILD_PATH)/$(SRC_PATH)/hash_check.php
- cp $(SRC_PATH)/installer/generate_build_files.php $(BUILD_PATH)/$(SRC_PATH)/generate_build_files.php; $(PHPCC) $(BUILD_PATH)/$(SRC_PATH)/generate_build_files.php; rm $(BUILD_PATH)/$(SRC_PATH)/generate_build_files.php
+ cp $(SRC_PATH)/installer/installer $(BUILD_PATH)/src/INSTALL
+ cp $(SRC_PATH)/installer/ncc.sh $(BUILD_PATH)/src/ncc.sh
+ cp $(SRC_PATH)/config/ncc.yaml $(BUILD_PATH)/src/default_config.yaml;
+ cp $(SRC_PATH)/config/ncc.yaml $(BUILD_PATH)/src/CLI/template_config.yaml;
+ cp $(SRC_PATH)/installer/extension $(BUILD_PATH)/src/extension
+ chmod +x $(BUILD_PATH)/src/INSTALL
+ cp LICENSE $(BUILD_PATH)/src/LICENSE
+ cp README.md $(BUILD_PATH)/src/README.md
+ cp $(SRC_PATH)/installer/hash_check.php $(BUILD_PATH)/src/hash_check.php; $(PHPCC) $(BUILD_PATH)/src/hash_check.php; rm $(BUILD_PATH)/src/hash_check.php
+ cp $(SRC_PATH)/installer/generate_build_files.php $(BUILD_PATH)/src/generate_build_files.php; $(PHPCC) $(BUILD_PATH)/src/generate_build_files.php; rm $(BUILD_PATH)/src/generate_build_files.php
+ mkdir -p $(BUILD_PATH)/src/repositories
+ cp -rf $(SRC_PATH)/default_repositories/*.json $(BUILD_PATH)/src/repositories
tar: redist
- cd $(BUILD_PATH)/src; tar -czvf ../ncc.tar.gz *
+ cd $(BUILD_PATH)/src; tar -czvf ../build.tar.gz *
clean:
rm -rf $(BUILD_PATH)
diff --git a/README.md b/README.md
index 9db1970..83fa984 100644
--- a/README.md
+++ b/README.md
@@ -4,38 +4,60 @@ Nosial Code Compiler is a program written in PHP designed to be a multi-purpose
This program is a complete re-write of the now defunct [PHP Package Manager (PPM)](https://git.n64.cc/intellivoid/ppm)
toolkit offering more features, security and proper code licensing and copyrighting for the components used for the project.
-NCC Cannot compile, read or use PPM packages (.ppm) files or work with project sources designed to be built with PPM, however
-a PPM extension may be built in the future to allow for backwards compatibility.
+## Alpha Stage
+
+NCC is currently in alpha stage, meaning that it's not fully functional and may not work on your system. If you find any bugs
+or issues please report them to the [GitHub Issue Tracker](https://git.n64.cc/intellivoid/ncc/issues).
+
+At the moment NCC is currently being used while developing other software, this serves as a test run to
+improve on changes for the next version.
+
+## Version History
+
+ - 1.0.0 Alpha - Initial release ([changelog](changelog/v1.0.0_alpha.md))
+
+## Repository Mirrors
+
+The official repository for NCC is hosted on [GitLab](https://git.n64.cc/intellivoid/ncc), however, you can also find
+mirrors of the repository mirrored on different platforms, including
+community powered mirrors. (more to come)
+
+ - [git.it-kuny.ch](https://git.it-kuny.ch)
+ - [git.martinvlba.eu](https://git.martinvlba.eu/Nosial/ncc)
-## Notes
+# Contributing
- > While NCC has windows compatibility in mind, not all compiler extensions or features will work correctly. NCC is
- > designed to be used in production in a Unix environment and Windows should only be used for development purposes.
+We welcome contributions to NCC! If you have an idea for how to improve the project, please don't hesitate to reach out.
+There are many ways to contribute, and we appreciate all forms of support.
- > Compiler extensions requires their own set of dependencies to be met, for example Java compilers will require JDK
+For more information on how to contribute, please read the [CONTRIBUTING.md](CONTRIBUTING.md) file.
- > NCC is designed to run only on a PHP 8.0+ environment, compiler extensions can have support for different PHP versions.
- > Third-party dependencies and included libraries has a dedicated namespace for `ncc` to avoid user land conflicts if
- > the user wishes to install and use one of the same dependencies that NCC uses.
+# Code of Conduct
-## Authors
- - Zi Xing Narrakas (netkas) <[netkas@n64.cc](mailto:netkas@64.cc)>
+We are committed to maintaining a welcoming and inclusive environment for all contributors. Please read and follow our
+[Code of Conduct](CODE_OF_CONDUCT.md).
-## Special Thanks
- - Marc Gutt (mgutt) <[marc@gutt.it](mailto:marc@gutt.it)>
- - Debusschère Alexandre ([debuss](https://github.com/debuss))
+# Authors
+
+- Zi Xing Narrakas (netkas) <[netkas@n64.cc](mailto:netkas@64.cc)>
+
+
+# Special Thanks
+
+- Marc Gutt (mgutt) <[marc@gutt.it](mailto:marc@gutt.it)>
+- Debusschère Alexandre ([debuss](https://github.com/debuss))
+
+
+# Copyright
+
+- Copyright (c) 2022-2023, Nosial - All Rights Reserved
-## Copyright
-- Copyright (c) 2022-2022, Nosial - All Rights Reserved
-- Copyright (c) 2004-2022, Fabien Potencier
-- Copyright (c) 2010, dealnews.com, Inc. All rights reserved.
-- Copyright (c) 2013 Austin Hyde
-- Copyright (C) 2009-2016 Laurent Jouanneau
-- Copyright (c) 2011, Nikita Popov
-- Copyright (c) 2010-2016 Arne Blankerts and Contributors
# Licenses
-Multiple licenses can be found at [LICENSE](LICENSE)
\ No newline at end of file
+NCC is licensed under the MIT License, see [LICENSE](LICENSE) for more information.
+
+Multiple licenses for the open source components used in this
+project can be found at [LICENSE](LICENSES)
\ No newline at end of file
diff --git a/assets/ncc_cli.png b/assets/ncc_cli.png
new file mode 100644
index 0000000..64fb3c4
Binary files /dev/null and b/assets/ncc_cli.png differ
diff --git a/changelog/v1.0.0_alpha.md b/changelog/v1.0.0_alpha.md
new file mode 100644
index 0000000..814a5f0
--- /dev/null
+++ b/changelog/v1.0.0_alpha.md
@@ -0,0 +1,18 @@
+# v1.0.0 Alpha
+
+First release of NCC (Nosial Code Compiler) in alpha stage.
+
+## Alpha Stage
+
+NCC is in its alpha stage, meaning that it's not fully
+functional and may not work on your system. If you find
+any bugs or issues please report them to the
+[Issue Tracker](https://git.n64.cc/intellivoid/ncc/issues).
+
+At the moment NCC is currently being used while developing
+other software, this serves as a test run to improve on
+changes for the next version.
+
+## Changelog
+
+ - Initial release
\ No newline at end of file
diff --git a/docs/building_ncc.md b/docs/building_ncc.md
deleted file mode 100644
index 2d3b6f6..0000000
--- a/docs/building_ncc.md
+++ /dev/null
@@ -1,94 +0,0 @@
-# Building NCC from source
-
-Building NCC from source is easy with very few requirements
-to start building. At the moment ncc can only be debugged
-or tested by building a redistributable source and
-installing it.
-
-## Requirements to build
-
- - php8.0+
- - php-mbstring
- - php-ctype
- - php-tokenizer *(or php-common)*
- - make
- - phpab
- - tar *(optional)*
-
-## Installing phpab
-
-phpab is also known as [PHP Autoload Builder](https://github.com/theseer/Autoload),
-phpab is an open source tool used for creating autoload
-files, ncc needs this tool in order to generate it's
-autoload files whenever there's any changes to its source
-code.
-
-This tool is only required for building and or creating a
-redistributable package of ncc. This component is not
-required to be installed to use ncc.
-
-for some components that require static loading, ncc will
-automatically load it using it's own
-[autoloader](../src/autoload/autoload.php)
-
-The recommended way to install phpab is by using [phive](https://phar.io/),
-if you don't have phive installed you can install it by
-running these commands in your terminal (from the official documentation)
-
-```shell
-wget -O phive.phar https://phar.io/releases/phive.phar
-wget -O phive.phar.asc https://phar.io/releases/phive.phar.asc
-gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x9D8A98B29B2D5D79
-gpg --verify phive.phar.asc phive.phar
-chmod +x phive.phar
-sudo mv phive.phar /usr/local/bin/phive
-```
-
-Once phive is installed, you can run the final command to
-install phpab
-```shell
-phive install phpab
-```
-
-**Note:** Optionally, you may want to have `phab` available in your
-`$PATH`, this can be done with this command. *(Replace `x.xx.x` with your
-version number)*
-
-```shell
-ln -s /home/user/.phive/phars/phpab-x.xx.x.phar /usr/bin/phpab
-```
-
-## Building NCC
-
-First, navigate to the main directory of NCC's source code
-where the [Makefile](../Makefile) is present. If you
-already attempted to or had built ncc before, it's
-recommended to use `make clean` before building.
-
-
-### Redist
-
-Running `redist` from the Makefile will generate all the
-required autoloaders for ncc and move all the required
-files into one redistributable source folder under a
-directory called `build/src`
-
-```shell
-make redist
-```
-
-
-### Tar
-
-Running `tar` will run redist before packaging the
-redistributable source into a tar.gz file that can
-be distributed to other machines, this process is not
-a requirement.
-
-```shell
-make tar
-```
-
-Once you have a populated `build/src` folder, you can
-simply run execute the `installer` file to install your
-build of ncc onto the running machine.
\ No newline at end of file
diff --git a/docs/package_name.md b/docs/package_name.md
deleted file mode 100644
index 0b4586e..0000000
--- a/docs/package_name.md
+++ /dev/null
@@ -1,43 +0,0 @@
-# Naming a package
-
-**Updated on Tuesday, July 26, 2022**
-
-NCC Follows the same naming convention as Java's naming
-convention. The purpose of naming a package this way is
-to easily create a "Name" of the package, this string
-of information contains
-
- - The developer/organization behind the package
- - The package name itself
-
-
-# Naming conventions
-
-Package names are written in all lower-case due to the
-fact that some operating systems treats file names
-differently, for example on Linux `Aa.txt` and `aa.txt`
-are two entirely different file names because of the
-capitalization and on Windows it's treated as the same
-file name.
-
-Organizations or small developers use their domain name
-in reverse to begin their package names, for example
-`net.nosial.example` is a package named `example`
-created by a programmer at `nosial.net`
-
-Just like the Java naming convention, to avoid conflicts
-of the same package name developers can use something
-different, for example as pointed out in Java's package
-naming convention developers can instead use something
-like a region to name packages, for example
-`net.nosial.region.example`
-
-
-# References
-
-For Java's package naming conventions see
-[Naming a Package](https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html)
-from the Oracle's Java documentation resource, as the
-same rules apply to NCC except for *some* illegal naming
-conventions such as packages not being able to begin
-with `int` or numbers
\ No newline at end of file
diff --git a/docs/project_configuration/execution_policy.md b/docs/project_configuration/execution_policy.md
deleted file mode 100644
index f5b851b..0000000
--- a/docs/project_configuration/execution_policy.md
+++ /dev/null
@@ -1,151 +0,0 @@
-# Execution Policies
-
-**Updated on Sunday, November 20, 2022**
-
-An execution policy is a policy defined in the Project
-configuration file (`project.json`) that can be used
-to execute a script or program in any stage of the package
-
-For instance, you can have a script that is executed before
-the build process starts, or in different installation stages
-when the user is installing your package you can have a unit
-run before or after the installation/uninstallation process
-starts.#
-
-Use cases such as this allows you to properly implement
-and control your program's files & assets that are not
-handled by NCC's compiler extensions.
-
-## Table of Contents
-
-
-* [Execution Policies](#execution-policies)
- * [Table of Contents](#table-of-contents)
- * [JSON Example](#json-example)
- * [ExecutionPolicy Object](#executionpolicy-object)
- * [Object Properties](#object-properties)
- * [JSON Example](#json-example)
- * [ExecutionConfiguration Object](#executionconfiguration-object)
- * [Object Properties](#object-properties)
- * [JSON Example](#json-example)
- * [ExitHandler Object](#exithandler-object)
- * [Object Properties](#object-properties)
- * [JSON Example](#json-example)
-
-
-
-## JSON Example
-
-```json
-{
- "execution_policies": {
- "main": {
- "runner": "php",
- "message": "Running main %ASSEMBLY.PACKAGE%",
- "exec": {
- "target": "scripts/main.php",
- "working_directory": "%INSTALL_PATH.SRC%",
- "silent": false
- }
- },
- "hello_world": {
- "runner": "shell",
- "message": "Running HTOP",
- "options": {
- "htop": null
- },
- "exec": {
- "tty": true
- }
- }
- }
-}
-```
-
-------------------------------------------------------------
-
-## ExecutionPolicy Object
-
-Execution Policies for your project **must** have unique
-names, because they way you tell NCC to execute these
-policies is by referencing their name in the configuration.
-
-Invalid names/undefined policies will raise errors when
-building the project
-
-### Object Properties
-
-| Property Name | Value Type | Example Value | Description |
-|-----------------|---------------------------------|----------------------|--------------------------------------------------------------------------------------------|
-| `runner` | string | bash | The name of a supported runner instance, see runners in this document |
-| `message` | string, null | Starting foo_bar ... | *Optional* the message to display before running the execution policy |
-| `exec` | ExecutionConfiguration | N/A | The configuration object that tells how the runner should execute the process |
-| `exit_handlers` | ExitHandlersConfiguration, null | N/A | *Optional* Exit Handler Configurations that tells NCC how to handle exits from the process |
-
-### JSON Example
-
-```json
-{
- "name": "foo_bar",
- "runner": "bash",
- "message": "Running foo_bar ...",
- "exec": null,
- "exit_handlers": null
-}
-```
-
-------------------------------------------------------------
-
-## ExecutionConfiguration Object
-
-### Object Properties
-
-| Property Name | Value Type | Example Value | Description |
-|---------------------|-------------------|---------------------------------|------------------------------------------------------------------------|
-| `target` | `string` | scripts/foo_bar.bash | The target file to execute |
-| `working_directory` | `string`, `null` | %INSTALL_PATH.SRC% | *optional* The working directory to execute the process in |
-| `options` | `array`, `null` | {"run": null, "log": "verbose"} | Commandline Parameters to pass on to the target or process |
-| `silent` | `boolean`, `null` | False | Indicates if the target should run silently, by default this is false. |
-| `tty` | `boolean`, `null` | False | Indicates if the target should run in TTY mode |
-| `timeout` | `integer`, `null` | 60 | The amount of seconds to wait before the process is killed |
-
-### JSON Example
-
-```json
-{
- "target": "scripts/foo_bar.bash",
- "working_directory": "%INSTALL_PATH.SRC%",
- "options": {"run": null, "log": "verbose"},
- "silent": false,
- "tty": false,
- "timeout": 10
-}
-```
-
-
-------------------------------------------------------------
-
-## ExitHandler Object
-
-An exit handler is executed once the specified exit code is
-returned or the process exits with an error or normally, if
-an exit handler is specified it will be executed.
-
-### Object Properties
-
-| Property Name | Value Type | Example Value | Description |
-|---------------|--------------------|---------------|------------------------------------------------------------------------------|
-| `message` | `string` | Hello World! | The message to display when the exit handler is triggered |
-| `end_process` | `boolean`, `null` | False | *optional* Kills the process after this exit handler is triggered |
-| `run` | `string`, `null` | `null` | *optional* A execution policy to execute once this exit handler is triggered |
-| `exit_code` | `int`, `null` | 1 | The exit code that triggers this exit handler |
-### JSON Example
-
-```json
-{
- "message": "Hello World",
- "end_process": false,
- "run": null,
- "exit_code": 1
-}
-```
\ No newline at end of file
diff --git a/src/config/ncc.yaml b/src/config/ncc.yaml
index 0b713ba..36c6488 100644
--- a/src/config/ncc.yaml
+++ b/src/config/ncc.yaml
@@ -19,12 +19,6 @@ ncc:
php:
# The main executable path for PHP that NCC should use
executable_path: "/usr/bin/php"
- runtime:
- # Whether to initialize NCC when running `require('ncc');`
- initialize_on_require: true
-
- # if NCC should handle fatal exceptions during execution
- handle_exceptions: true
git:
# if git is enabled or not
@@ -55,10 +49,10 @@ composer:
quiet: false
# Disable ANSI output
- no_ansi: false
+ no_ansi: true
# Do not ask any interactive question
- no_interaction: false
+ no_interaction: true
# Display timing and memory usage information
profile: false
diff --git a/src/default_repositories/custom_repositories.json b/src/default_repositories/custom_repositories.json
new file mode 100644
index 0000000..aa1a562
--- /dev/null
+++ b/src/default_repositories/custom_repositories.json
@@ -0,0 +1,6 @@
+[
+ "gitgud.json",
+ "github.json",
+ "gitlab.json",
+ "n64.json"
+]
\ No newline at end of file
diff --git a/src/default_repositories/gitgud.json b/src/default_repositories/gitgud.json
new file mode 100644
index 0000000..83f904e
--- /dev/null
+++ b/src/default_repositories/gitgud.json
@@ -0,0 +1,6 @@
+{
+ "name": "gitgud",
+ "type": "gitlab",
+ "host": "gitgud.io",
+ "ssl": true
+}
\ No newline at end of file
diff --git a/src/default_repositories/github.json b/src/default_repositories/github.json
new file mode 100644
index 0000000..f05cc8c
--- /dev/null
+++ b/src/default_repositories/github.json
@@ -0,0 +1,6 @@
+{
+ "name": "github",
+ "type": "github",
+ "host": "api.github.com",
+ "ssl": true
+}
\ No newline at end of file
diff --git a/src/default_repositories/gitlab.json b/src/default_repositories/gitlab.json
new file mode 100644
index 0000000..2694fbd
--- /dev/null
+++ b/src/default_repositories/gitlab.json
@@ -0,0 +1,6 @@
+{
+ "name": "gitlab",
+ "type": "gitlab",
+ "host": "gitlab.com",
+ "ssl": true
+}
\ No newline at end of file
diff --git a/src/default_repositories/n64.json b/src/default_repositories/n64.json
new file mode 100644
index 0000000..ac57ee8
--- /dev/null
+++ b/src/default_repositories/n64.json
@@ -0,0 +1,6 @@
+{
+ "name": "n64",
+ "type": "gitlab",
+ "host": "git.n64.cc",
+ "ssl": true
+}
\ No newline at end of file
diff --git a/src/installer/extension b/src/installer/extension
index aaff950..2ccc72b 100644
--- a/src/installer/extension
+++ b/src/installer/extension
@@ -1,8 +1,19 @@
exists(PathFinder::getConfigurationFile()))
{
- $config_backup = IO::fread(PathFinder::getConfigurationFile());
+ $config_backup = Yaml::parseFile(PathFinder::getConfigurationFile());
}
}
catch (Exception $e)
@@ -736,33 +737,73 @@
// Create/Update configuration file
$config_obj = Yaml::parseFile(__DIR__ . DIRECTORY_SEPARATOR . 'default_config.yaml');
- // Update the old configuration
- if($config_backup !== null)
+ if(!function_exists('array_replace_recursive'))
{
- $old_config_obj = Yaml::parse($config_backup);
- foreach($old_config_obj as $section => $value)
+ /**
+ * @param $array
+ * @param $array1
+ * @return array|mixed
+ * @author
+ * @noinspection PhpMissingReturnTypeInspection
+ */
+ function array_replace_recursive($array, $array1)
{
- if(isset($config_obj[$section]))
+ // handle the arguments, merge one by one
+ $args = func_get_args();
+ $array = $args[0];
+ if (!is_array($array))
{
- foreach($value as $section_item => $section_value)
+ return $array;
+ }
+ for ($i = 1; $i < count($args); $i++)
+ {
+ if (is_array($args[$i]))
{
- if(!isset($config_obj[$section][$section_item]))
- {
- $config_obj[$section][$section_item] = $section_value;
- }
+ $array = recurse($array, $args[$i]);
}
}
- else
- {
- $config_obj[$section] = $value;
- }
+ return $array;
}
}
+ if(!function_exists('recurse'))
+ {
+ /**
+ * @param $array
+ * @param $array1
+ * @return mixed
+ * @author
+ * @noinspection PhpMissingReturnTypeInspection
+ */
+ function recurse($array, $array1)
+ {
+ foreach ($array1 as $key => $value)
+ {
+ // create new key in $array, if it is empty or not an array
+ /** @noinspection PhpConditionAlreadyCheckedInspection */
+ if (!isset($array[$key]) || (isset($array[$key]) && !is_array($array[$key])))
+ {
+ $array[$key] = array();
+ }
+
+ // overwrite the value in the base array
+ if (is_array($value))
+ {
+ $value = recurse($array[$key], $value);
+ }
+ $array[$key] = $value;
+ }
+ return $array;
+ }
+ }
+
+
+ if($config_backup !== null)
+ $config_obj = array_replace_recursive($config_obj, $config_backup);
+
if($config_backup == null)
{
Console::out('Generating ncc.yaml');
-
}
else
{
@@ -779,6 +820,57 @@
return;
}
+ if($NCC_FILESYSTEM->exists(__DIR__ . DIRECTORY_SEPARATOR . 'repositories'))
+ {
+ if(!$NCC_FILESYSTEM->exists(__DIR__ . DIRECTORY_SEPARATOR . 'repositories' . DIRECTORY_SEPARATOR . 'custom_repositories.json'))
+ return;
+
+ try
+ {
+ $custom_repositories = Functions::loadJsonFile(__DIR__ . DIRECTORY_SEPARATOR . 'repositories' . DIRECTORY_SEPARATOR . 'custom_repositories.json', Functions::FORCE_ARRAY);
+ }
+ catch(Exception $e)
+ {
+ $custom_repositories = null;
+ Console::outWarning(sprintf('Failed to load custom repositories: %s', $e->getMessage()));
+ }
+
+ if($custom_repositories !== null)
+ {
+ $source_manager = new RemoteSourcesManager();
+ foreach($custom_repositories as $repository)
+ {
+ $repo_path = __DIR__ . DIRECTORY_SEPARATOR . 'repositories' . DIRECTORY_SEPARATOR . $repository;
+ if($NCC_FILESYSTEM->exists($repo_path))
+ {
+ try
+ {
+ $definedEntry = DefinedRemoteSource::fromArray(Functions::loadJsonFile($repo_path, Functions::FORCE_ARRAY));
+ if(!$source_manager->getRemoteSource($definedEntry->Name))
+ $source_manager->addRemoteSource($definedEntry);
+ }
+ catch(Exception $e)
+ {
+ Console::outWarning(sprintf('Failed to load custom repository %s: %s', $repository, $e->getMessage()));
+ }
+ }
+ else
+ {
+ Console::outWarning(sprintf('Failed to load custom repository %s, file does not exist', $repository));
+ }
+ }
+
+ try
+ {
+ $source_manager->save();
+ }
+ catch (\ncc\Exceptions\IOException $e)
+ {
+ Console::outWarning(sprintf('Failed to save sources: %s', $e->getMessage()));
+ }
+ }
+ }
+
Console::out('NCC version: ' . NCC_VERSION_NUMBER . ' has been successfully installed');
Console::out('For licensing information see \'' . $NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'LICENSE\' or run \'ncc help --license\'');
diff --git a/src/ncc/Abstracts/AuthenticationSource.php b/src/ncc/Abstracts/AuthenticationSource.php
deleted file mode 100644
index c5b29f8..0000000
--- a/src/ncc/Abstracts/AuthenticationSource.php
+++ /dev/null
@@ -1,12 +0,0 @@
-getPackageLock()->getPackage($package);
+ }
+ catch(Exception $e)
+ {
+ Console::outException('Package ' . $package . ' is not installed', $e, 1);
+ return;
+ }
+
+ try
+ {
+ $version_entry = $package_entry->getVersion($version);
+ }
+ catch(Exception $e)
+ {
+ Console::outException('Version ' . $version . ' is not installed', $e, 1);
+ return;
+ }
+
+ try
+ {
+ $units = $execution_pointer_manager->getUnits($package_entry->Name, $version_entry->Version);
+ }
+ catch(Exception $e)
+ {
+ Console::outException(sprintf('Cannot load execution units for package \'%s\'', $package), $e, 1);
+ return;
+ }
+
+ if(!in_array($unit_name, $units))
+ {
+ Console::outError(sprintf('Unit \'%s\' is not configured for package \'%s\'', $unit_name, $package), true, 1);
+ return;
+ }
+
+ $options = [];
+
+ if($set_args != null)
+ {
+ global $argv;
+ $args_index = array_search('--exec-args', $argv);
+ $options = array_slice($argv, $args_index + 1);
+ }
+
+ try
+ {
+ exit($execution_pointer_manager->executeUnit($package_entry->Name, $version_entry->Version, $unit_name, $options));
+ }
+ catch(Exception $e)
+ {
+ Console::outException(sprintf('Cannot execute execution point \'%s\' in package \'%s\'', $unit_name, $package), $e, 1);
+ return;
+ }
+ }
+
+ /**
+ * Displays the main options section
+ *
+ * @return void
+ */
+ private static function displayOptions(): void
+ {
+ $options = [
+ new CliHelpSection(['help'], 'Displays this help menu about the value command'),
+ new CliHelpSection(['exec', '--package'], '(Required) The package to execute'),
+ new CliHelpSection(['--exec-version'], '(default: latest) The version of the package to execute'),
+ new CliHelpSection(['--exec-unit'], '(default: main) The unit point of the package to execute'),
+ new CliHelpSection(['--exec-args'], '(optional) Anything past this point will be passed to the execution unit'),
+ ];
+
+ $options_padding = Functions::detectParametersPadding($options) + 4;
+
+ Console::out('Usage: ncc exec --package [options] [arguments]');
+ Console::out('Options:' . PHP_EOL);
+ foreach($options as $option)
+ {
+ Console::out(' ' . $option->toString($options_padding));
+ }
+
+ Console::out(PHP_EOL . 'Arguments:' . PHP_EOL);
+ Console::out(' The arguments to pass to the program');
+ Console::out(PHP_EOL . 'Example Usage:' . PHP_EOL);
+ Console::out(' ncc exec --package com.example.program');
+ Console::out(' ncc exec --package com.example.program --exec-version 1.0.0');
+ Console::out(' ncc exec --package com.example.program --exec-version 1.0.0 --exec-unit setup');
+ Console::out(' ncc exec --package com.example.program --exec-args --foo --bar --extra=test');
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/CLI/CredentialMenu.php b/src/ncc/CLI/CredentialMenu.php
deleted file mode 100644
index a82336f..0000000
--- a/src/ncc/CLI/CredentialMenu.php
+++ /dev/null
@@ -1,72 +0,0 @@
-toString($options_padding) . PHP_EOL);
- }
- print(PHP_EOL);
- }
- }
\ No newline at end of file
diff --git a/src/ncc/CLI/HelpMenu.php b/src/ncc/CLI/HelpMenu.php
index 140bae4..8381429 100644
--- a/src/ncc/CLI/HelpMenu.php
+++ b/src/ncc/CLI/HelpMenu.php
@@ -1,9 +1,33 @@
toString($options_padding));
- }
+ ]);
}
/**
@@ -67,20 +86,14 @@
*/
private static function displayManagementCommands(): void
{
- $commands = [
+ Console::out('Management Commands:');
+ Console::outHelpSections([
new CliHelpSection(['project'], 'Manages the current project'),
new CliHelpSection(['package'], 'Manages the package system'),
- new CliHelpSection(['cache'], 'Manages the system cache'),
- new CliHelpSection(['credential'], 'Manages credentials'),
+ new CliHelpSection(['cred'], 'Manages credentials'),
new CliHelpSection(['config'], 'Changes NCC configuration values'),
- ];
- $commands_padding = \ncc\Utilities\Functions::detectParametersPadding($commands) + 2;
-
- Console::out('Management Commands:');
- foreach($commands as $command)
- {
- Console::out(' ' . $command->toString($commands_padding));
- }
+ new CliHelpSection(['source'], 'Manages remote sources'),
+ ]);
}
/**
@@ -90,35 +103,10 @@
*/
private static function displayMainCommands(): void
{
- $commands = [
- new CliHelpSection(['build'], 'Builds the current project'),
- new CliHelpSection(['main'], 'Executes the main entrypoint of a package')
- ];
- $commands_padding = \ncc\Utilities\Functions::detectParametersPadding($commands) + 2;
-
Console::out('Commands:');
- foreach($commands as $command)
- {
- Console::out(' ' . $command->toString($commands_padding));
- }
- }
-
- /**
- * Displays the main commands section
- *
- * @return void
- */
- private static function displayExtensions(): void
- {
- $extensions = [
- new CliHelpSection(['exphp'], 'The PHP compiler extension')
- ];
- $extensions_padding = \ncc\Utilities\Functions::detectParametersPadding($extensions) + 2;
-
- Console::out('Extensions:');
- foreach($extensions as $command)
- {
- Console::out(' ' . $command->toString($extensions_padding));
- }
+ Console::outHelpSections([
+ new CliHelpSection(['build'], 'Builds the current project'),
+ new CliHelpSection(['exec'], 'Executes the main entrypoint of a package')
+ ]);
}
}
\ No newline at end of file
diff --git a/src/ncc/CLI/Main.php b/src/ncc/CLI/Main.php
index 793a8fc..5ce5ad0 100644
--- a/src/ncc/CLI/Main.php
+++ b/src/ncc/CLI/Main.php
@@ -1,4 +1,24 @@
getMessage());
+ }
+ }
+
}
\ No newline at end of file
diff --git a/src/ncc/CLI/ConfigMenu.php b/src/ncc/CLI/Management/ConfigMenu.php
similarity index 79%
rename from src/ncc/CLI/ConfigMenu.php
rename to src/ncc/CLI/Management/ConfigMenu.php
index ec7bc2f..2afc0aa 100644
--- a/src/ncc/CLI/ConfigMenu.php
+++ b/src/ncc/CLI/Management/ConfigMenu.php
@@ -1,6 +1,26 @@
getVault()->getEntry($name);
+
+ if($entry === null)
+ {
+ Console::out('Entry not found.', true, 1);
+ return;
+ }
+
+ if($entry->isEncrypted())
+ {
+ $tries = 0;
+
+ while(true)
+ {
+ try
+ {
+ $password = Console::passwordInput('Password/Secret: ');
+ if (!$entry->unlock($password))
+ {
+ $tries++;
+ if ($tries >= 3)
+ {
+ Console::outError('Too many failed attempts.', true, 1);
+ return;
+ }
+
+ Console::outError('Invalid password.', true, 1);
+ }
+ else
+ {
+ Console::out('Authentication successful.');
+ return;
+ }
+ }
+ catch (RuntimeException $e)
+ {
+ Console::outException('Cannot unlock entry.', $e, 1);
+ return;
+ }
+ }
+ }
+
+ else
+ {
+ Console::out('Authentication always successful, entry is not encrypted.');
+ }
+ }
+
+ /**
+ * Prints the list of entries in the vault
+ *
+ * @return void
+ */
+ public static function listEntries(): void
+ {
+ $credential_manager = new CredentialManager();
+ $entries = $credential_manager->getVault()->getEntries();
+
+ if(count($entries) === 0)
+ {
+ Console::out('No entries found.');
+ return;
+ }
+
+ Console::out('Entries:');
+ foreach($entries as $entry)
+ {
+ Console::out(sprintf(' - %s (%s)', $entry->getName(), $entry->isEncrypted() ? ' (encrypted)' : ''));
+ }
+
+ Console::out('Total: ' . count($entries));
+ }
+
+ /**
+ * @param $args
+ * @return void
+ */
+ public static function addEntry($args): void
+ {
+ $ResolvedScope = Resolver::resolveScope();
+
+ if($ResolvedScope !== Scopes::System)
+ Console::outError('Insufficient permissions to add entries');
+
+ // Really dumb-proofing this
+ $name = $args['alias'] ?? $args['name'] ?? null;
+ $auth_type = $args['auth-type'] ?? $args['auth'] ?? null;
+ $username = $args['username'] ?? $args['usr'] ?? null;
+ $password = $args['password'] ?? $args['pwd'] ?? null;
+ $token = $args['token'] ?? $args['pat'] ?? $args['private-token'] ?? null;
+ $encrypt = !isset($args['no-encryption']);
+
+ if($name === null)
+ $name = Console::getInput('Enter a name for the entry: ');
+
+ if($auth_type === null)
+ $auth_type = Console::getInput('Enter the authentication type (login or pat): ');
+
+ if($auth_type === 'login')
+ {
+ if($username === null)
+ $username = Console::getInput('Username: ');
+
+ if($password === null)
+ $password = Console::passwordInput('Password: ');
+ }
+ elseif($auth_type === 'pat')
+ {
+ if($token === null)
+ $token = Console::passwordInput('Token: ');
+ }
+ else
+ {
+ Console::outError('Invalid authentication type');
+ }
+
+ if($name === null)
+ {
+ Console::outError('You must specify a name for the entry (alias, name)', true, 1);
+ return;
+ }
+
+ if($auth_type === null)
+ {
+ Console::outError('You must specify an authentication type for the entry (auth-type, auth)', true, 1);
+ return;
+ }
+
+ $encrypt = Functions::cbool($encrypt);
+
+ switch($auth_type)
+ {
+ case 'login':
+
+ if($username === null)
+ {
+ Console::outError('You must specify a username for the entry (username, usr)', true, 1);
+ return;
+ }
+ if($password === null)
+ {
+ Console::outError('You must specify a password for the entry (password, pwd)', true, 1);
+ return;
+ }
+
+ $pass_object = new UsernamePassword();
+ $pass_object->setUsername($username);
+ $pass_object->setPassword($password);
+
+ break;
+
+ case 'pat':
+
+ if($token === null)
+ {
+ Console::outError('You must specify a token for the entry (token, pat, private-token)', true, 1);
+ return;
+ }
+
+ $pass_object = new AccessToken();
+ $pass_object->setAccessToken($token);
+
+ break;
+
+ default:
+ Console::outError('Invalid authentication type specified', true, 1);
+ return;
+ }
+
+ $credential_manager = new CredentialManager();
+ if(!$credential_manager->getVault()->addEntry($name, $pass_object, $encrypt))
+ {
+ Console::outError('Failed to add entry, entry already exists.', true, 1);
+ return;
+ }
+
+ try
+ {
+ $credential_manager->saveVault();
+ }
+ catch(Exception $e)
+ {
+ Console::outException('Failed to save vault', $e, 1);
+ return;
+ }
+
+ Console::out('Successfully added entry', true, 0);
+ }
+
+ /**
+ * Removes an existing entry from the vault.
+ *
+ * @param $args
+ * @return void
+ */
+ private static function removeEntry($args): void
+ {
+ $ResolvedScope = Resolver::resolveScope();
+
+ if($ResolvedScope !== Scopes::System)
+ Console::outError('Insufficient permissions to remove entries');
+
+ $name = $args['alias'] ?? $args['name'] ?? null;
+
+ if($name === null)
+ $name = Console::getInput('Enter the name of the entry to remove: ');
+
+ $credential_manager = new CredentialManager();
+ if(!$credential_manager->getVault()->deleteEntry($name))
+ {
+ Console::outError('Failed to remove entry, entry does not exist.', true, 1);
+ return;
+ }
+
+ try
+ {
+ $credential_manager->saveVault();
+ }
+ catch(Exception $e)
+ {
+ Console::outException('Failed to save vault', $e, 1);
+ return;
+ }
+
+ Console::out('Successfully removed entry', true, 0);
+ }
+
+ /**
+ * Displays the main options section
+ *
+ * @return void
+ */
+ private static function displayOptions(): void
+ {
+ Console::out('Usage: ncc cred {command} [options]');
+ Console::out('Options:');
+ Console::outHelpSections([
+ new CliHelpSection(['help'], 'Displays this help menu about the value command'),
+ new CliHelpSection(['add'], 'Adds a new entry to the vault (See below)'),
+ new CliHelpSection(['remove', '--name'], 'Removes an entry from the vault'),
+ new CliHelpSection(['list'], 'Lists all entries in the vault'),
+ ]);
+ Console::out((string)null);
+
+ Console::out('If you are adding a new entry, you can run the add command in interactive mode');
+ Console::out('or you can specify the options below' . PHP_EOL);
+
+ Console::out('Add Options:');
+ Console::outHelpSections([
+ new CliHelpSection(['--name'], 'The name of the entry'),
+ new CliHelpSection(['--auth-type', '--auth'], 'The type of authentication (login, pat)'),
+ new CliHelpSection(['--no-encryption'], 'Omit encryption to the entry (By default it\'s encrypted)', true),
+ ]);
+
+ Console::out(' login authentication type options:');
+ Console::outHelpSections([
+ new CliHelpSection(['--username', '--usr'], 'The username for the entry'),
+ new CliHelpSection(['--password', '--pwd'], 'The password for the entry'),
+ ]);
+
+ Console::out(' pat authentication type options:');
+ Console::outHelpSections([
+ new CliHelpSection(['--token', '--pat',], 'The private access token for the entry', true),
+ ]);
+
+ Console::out('Authentication Types:');
+ Console::out(' login');
+ Console::out(' pat' . PHP_EOL);
+
+ Console::out('Examples:');
+ Console::out(' ncc cred add --alias "My Alias" --auth-type login --username "myusername" --password "mypassword"');
+ Console::out(' ncc cred add --alias "My Alias" --auth-type pat --token "mytoken" --no-encryption');
+ Console::out(' ncc cred remove --alias "My Alias"');
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/CLI/PackageManagerMenu.php b/src/ncc/CLI/Management/PackageManagerMenu.php
similarity index 65%
rename from src/ncc/CLI/PackageManagerMenu.php
rename to src/ncc/CLI/Management/PackageManagerMenu.php
index a66d117..5dfbc49 100644
--- a/src/ncc/CLI/PackageManagerMenu.php
+++ b/src/ncc/CLI/Management/PackageManagerMenu.php
@@ -1,15 +1,36 @@
MagicBytes?->toArray() ?? []), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
+ Console::out('header: ' . json_encode(($package->Header?->toArray() ?? []), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
+ Console::out('assembly: ' . json_encode(($package->Assembly?->toArray() ?? []), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
+ Console::out('main: ' . ($package->MainExecutionPolicy ?? 'N/A'));
+ Console::out('installer: ' . ($package->Installer?->toArray() ?? 'N/A'));
+
+ if($package->Dependencies !== null && count($package->Dependencies) > 0)
+ {
+ Console::out('dependencies:');
+ foreach($package->Dependencies as $dependency)
+ {
+ Console::out(' - ' . json_encode($dependency->toArray(), JSON_UNESCAPED_SLASHES));
+ }
+ }
+ else
+ {
+ Console::out('dependencies: N/A');
+ }
+
+ if($package->ExecutionUnits !== null && count($package->ExecutionUnits) > 0)
+ {
+ Console::out('execution_units:');
+ foreach($package->ExecutionUnits as $unit)
+ {
+ Console::out(' - ' . json_encode($unit->toArray(), JSON_UNESCAPED_SLASHES));
+ }
+ }
+ else
+ {
+ Console::out('execution_units: N/A');
+ }
+
+ if($package->Resources !== null && count($package->Resources) > 0)
+ {
+ Console::out('resources:');
+ foreach($package->Resources as $resource)
+ {
+ Console::out(' - ' . sprintf('%s - (%s)', $resource->Name, Functions::b2u(strlen($resource->Data))));
+ }
+ }
+ else
+ {
+ Console::out('resources: N/A');
+ }
+
+ if($package->Components !== null && count($package->Components) > 0)
+ {
+ Console::out('components:');
+ foreach($package->Components as $component)
+ {
+ Console::out(' - ' . sprintf('#%s %s - %s', $component->DataType, $component->Name, json_encode(($component->Flags ?? []), JSON_UNESCAPED_SLASHES)));
+ }
+ }
+ else
+ {
+ Console::out('components: N/A');
+ }
+
+ exit(0);
+ }
+
/**
* Displays all installed packages
*
@@ -156,6 +276,7 @@
$package_version = $package_manager->getPackageVersion($package, $version);
if($package_version == null)
continue;
+
Console::out(sprintf('%s=%s (%s)',
Console::formatColor($package, ConsoleColors::LightGreen),
Console::formatColor($version, ConsoleColors::LightMagenta),
@@ -165,9 +286,10 @@
catch(Exception $e)
{
unset($e);
- Console::out(sprintf('%s=%s',
+ Console::out(sprintf('%s=%s (%s)',
Console::formatColor($package, ConsoleColors::LightGreen),
- Console::formatColor($version, ConsoleColors::LightMagenta)
+ Console::formatColor($version, ConsoleColors::LightMagenta),
+ Console::formatColor('N/A', ConsoleColors::LightRed)
));
}
}
@@ -191,33 +313,66 @@
return;
}
- $path = $package;
- $parsed_source = new RemotePackageInput($path);
- if($parsed_source->Vendor !== null && $parsed_source->Package !== null)
+ // check if authentication is provided
+ $entry_arg = ($args['auth'] ?? null);
+ $credential_manager = new CredentialManager();
+
+ if($entry_arg !== null)
{
- if($parsed_source->Source == null)
+ $credential = $credential_manager->getVault()->getEntry($entry_arg);
+
+ if($credential == null)
{
- Console::outError('No source specified', true, 1);
+ Console::outError(sprintf('Unknown credential entry \'%s\'', $entry_arg), true, 1);
return;
}
+ }
+ else
+ {
+ $credential = null;
+ }
- switch($parsed_source->Source)
+ if($credential !== null && !$credential->isCurrentlyDecrypted())
+ {
+ // Try 3 times
+ for($i = 0; $i < 3; $i++)
{
- case RemoteSource::Composer:
- try
- {
- $path = ComposerSource::fetch($parsed_source);
- break;
- }
- catch(Exception $e)
- {
- Console::outException(sprintf('Failed to fetch package %s', $package), $e, 1);
- return;
- }
-
- default:
- Console::outError('Cannot install package from source: ' . $parsed_source->Source, true, 1);
+ try
+ {
+ $credential->unlock(Console::passwordInput(sprintf('Enter Password for %s: ', $credential->getName())));
+ }
+ catch (RuntimeException $e)
+ {
+ Console::outException(sprintf('Failed to unlock credential %s', $credential->getName()), $e, 1);
return;
+ }
+
+ if($credential->isCurrentlyDecrypted())
+ break;
+
+ Console::outWarning(sprintf('Invalid password, %d attempts remaining', 2 - $i));
+ }
+
+ if(!$credential->isCurrentlyDecrypted())
+ {
+ Console::outError('Failed to unlock credential', true, 1);
+ return;
+ }
+ }
+
+ $path = $package;
+ $parsed_source = new RemotePackageInput($path);
+
+ if($parsed_source->Vendor !== null && $parsed_source->Package !== null && $parsed_source->Source !== null)
+ {
+ try
+ {
+ $path = $package_manager->fetchFromSource($parsed_source->toString(), $credential);
+ }
+ catch (Exception $e)
+ {
+ Console::outException('Failed to fetch package from source', $e, 1);
+ return;
}
}
@@ -230,6 +385,18 @@
$user_confirmation = (bool)($args['y'] ?? $args['Y']);
}
+ $installer_options = [];
+
+ if((Functions::cbool($args['skip-dependencies'] ?? false)))
+ {
+ $installer_options[] = InstallPackageOptions::SkipDependencies;
+ }
+
+ if(Functions::cbool($args['reinstall'] ?? false))
+ {
+ $installer_options[] = InstallPackageOptions::Reinstall;
+ }
+
try
{
$package = Package::load($path);
@@ -261,38 +428,39 @@
Console::out(' Trademark: ' . Console::formatColor($package->Assembly->Trademark, ConsoleColors::LightGreen));
Console::out((string)null);
- if(count($package->Dependencies) > 0)
+ if(count($package->Dependencies) > 0 && !in_array(InstallPackageOptions::Reinstall, $installer_options))
{
$dependencies = [];
foreach($package->Dependencies as $dependency)
{
- $require_dependency = true;
+ $require_dependency = false;
- try
- {
- $dependency_package = $package_manager->getPackage($dependency->Name);
- }
- catch (PackageLockException $e)
- {
- unset($e);
- $dependency_package = null;
- }
-
- if($dependency_package !== null)
+ if(!in_array(InstallPackageOptions::SkipDependencies, $installer_options))
{
try
{
- $dependency_version = $dependency_package->getVersion($dependency->Version);
+ $dependency_package = $package_manager->getPackage($dependency->Name);
}
- catch (VersionNotFoundException $e)
+ catch (PackageLockException $e)
{
unset($e);
- $dependency_version = null;
+ $dependency_package = null;
}
- if($dependency_version !== null)
+ if($dependency_package !== null)
{
- $require_dependency = false;
+ try
+ {
+ $dependency_version = $dependency_package->getVersion($dependency->Version);
+ }
+ catch (VersionNotFoundException $e)
+ {
+ unset($e);
+ $dependency_version = null;
+ }
+
+ if($dependency_version == null)
+ $require_dependency = true;
}
}
@@ -305,8 +473,11 @@
}
}
- Console::out('The following dependencies will be installed:');
- Console::out(sprintf('%s', implode(PHP_EOL, $dependencies)));
+ if($dependencies !== null && count($dependencies) > 0)
+ {
+ Console::out('The package requires the following dependencies:');
+ Console::out(sprintf('%s', implode(PHP_EOL, $dependencies)));
+ }
}
Console::out(sprintf('Extension: %s',
@@ -326,11 +497,13 @@
if(!$user_confirmation)
$user_confirmation = Console::getBooleanInput(sprintf('Do you want to install %s', $package->Assembly->Package));
+
+
if($user_confirmation)
{
try
{
- $package_manager->install($path);
+ $package_manager->install($path, $credential, $installer_options);
Console::out(sprintf('Package %s installed successfully', $package->Assembly->Package));
}
catch(Exception $e)
@@ -382,7 +555,6 @@
$version_entry = null;
if($version_entry !== null && $package_entry !== null)
- /** @noinspection PhpUnhandledExceptionInspection */
/** @noinspection PhpRedundantOptionalArgumentInspection */
$version_entry = $package_entry->getVersion($version_entry, false);
@@ -490,9 +662,12 @@
new CliHelpSection(['list'], 'Lists all installed packages on the system'),
new CliHelpSection(['install', '--package', '-p'], 'Installs a specified NCC package'),
new CliHelpSection(['install', '--package', '-p', '--version', '-v'], 'Installs a specified NCC package version'),
+ new CliHelpSection(['install', '-p', '--skip-dependencies'], 'Installs a specified NCC package but skips the installation of dependencies'),
+ new CliHelpSection(['install', '-p', '--reinstall'], 'Installs a specified NCC package, reinstall if already installed'),
new CliHelpSection(['uninstall', '--package', '-p'], 'Uninstalls a specified NCC package'),
new CliHelpSection(['uninstall', '--package', '-p', '--version', '-v'], 'Uninstalls a specified NCC package version'),
new CliHelpSection(['uninstall-all'], 'Uninstalls all packages'),
+ new CliHelpSection(['sdc', '--package', '-p'], '(Debug) Semi-decompiles a specified NCC package and prints the result to the console'),
];
$options_padding = Functions::detectParametersPadding($options) + 4;
diff --git a/src/ncc/CLI/ProjectMenu.php b/src/ncc/CLI/Management/ProjectMenu.php
similarity index 80%
rename from src/ncc/CLI/ProjectMenu.php
rename to src/ncc/CLI/Management/ProjectMenu.php
index 9f8eaef..b7ce09e 100644
--- a/src/ncc/CLI/ProjectMenu.php
+++ b/src/ncc/CLI/Management/ProjectMenu.php
@@ -1,18 +1,45 @@
getSources();
+
+ if(count($sources) == 0)
+ {
+ Console::out('No remote sources defined.', 1);
+ return;
+ }
+
+ Console::out('Remote sources:', 1);
+ foreach($sources as $source)
+ {
+ Console::out(' - ' . $source->Name . ' (' . $source->Host . ')', 1);
+ }
+
+ Console::out('Total: ' . count($sources), 1);
+ }
+
+ /**
+ * @param $args
+ * @return void
+ */
+ public static function addEntry($args): void
+ {
+ if(Resolver::resolveScope() !== Scopes::System)
+ {
+ Console::outError('Insufficient permissions to add entry.', true, 1);
+ return;
+ }
+
+ $name = $args['name'] ?? null;
+ $type = $args['type'] ?? null;
+ $host = $args['host'] ?? null;
+ $ssl = $args['ssl'] ?? null;
+
+ if($name == null)
+ {
+ Console::outError(sprintf('Missing required argument \'%s\'.', 'name'), true, 1);
+ return;
+ }
+
+ if($type == null)
+ {
+ Console::outError(sprintf('Missing required argument \'%s\'.', 'type'), true, 1);
+ return;
+ }
+
+ if($host == null)
+ {
+ Console::outError(sprintf('Missing required argument \'%s\'.', 'host'), true, 1);
+ return;
+ }
+
+ if($ssl !== null)
+ {
+ $ssl = Functions::cbool($ssl);
+ }
+
+ $source_manager = new RemoteSourcesManager();
+ $source = new DefinedRemoteSource();
+ $source->Name = $name;
+ $source->Type = $type;
+ $source->Host = $host;
+ $source->SSL = $ssl;
+
+ if(!$source_manager->addRemoteSource($source))
+ {
+ Console::outError(sprintf('Cannot add entry \'%s\', it already exists', $name), true, 1);
+ return;
+ }
+
+ try
+ {
+ $source_manager->save();
+ }
+ catch (IOException $e)
+ {
+ Console::outException('Cannot save remote sources file.', $e, 1);
+ return;
+ }
+
+ Console::out(sprintf('Entry \'%s\' added successfully.', $name));
+ }
+
+ /**
+ * Removes an existing entry from the vault.
+ *
+ * @param $args
+ * @return void
+ */
+ private static function removeEntry($args): void
+ {
+ $ResolvedScope = Resolver::resolveScope();
+
+ if($ResolvedScope !== Scopes::System)
+ Console::outError('Insufficient permissions to remove entries');
+
+ $name = $args['name'] ?? null;
+
+ if($name == null)
+ {
+ Console::outError(sprintf('Missing required argument \'%s\'.', 'name'), true, 1);
+ return;
+ }
+
+ $source_manager = new RemoteSourcesManager();
+
+ if(!$source_manager->deleteRemoteSource($name))
+ {
+ Console::outError(sprintf('Cannot remove entry \'%s\', it does not exist', $name), true, 1);
+ return;
+ }
+
+ try
+ {
+ $source_manager->save();
+ }
+ catch (IOException $e)
+ {
+ Console::outException('Cannot save remote sources file.', $e, 1);
+ return;
+
+ }
+ Console::out(sprintf('Entry \'%s\' removed successfully.', $name));
+ }
+
+ /**
+ * Displays the main options section
+ *
+ * @return void
+ */
+ private static function displayOptions(): void
+ {
+ Console::out('Usage: ncc sources {command} [options]');
+ Console::out('Options:');
+ Console::outHelpSections([
+ new CliHelpSection(['help'], 'Displays this help menu about the sources command'),
+ new CliHelpSection(['add'], 'Adds a new entry to the list of remote sources (See below)'),
+ new CliHelpSection(['remove', '--name'], 'Removes an entry from the list'),
+ new CliHelpSection(['list'], 'Lists all entries defined as remote sources'),
+ ]);
+ Console::out((string)null);
+
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/CLI/PhpMenu.php b/src/ncc/CLI/PhpMenu.php
deleted file mode 100644
index 1c10f39..0000000
--- a/src/ncc/CLI/PhpMenu.php
+++ /dev/null
@@ -1,59 +0,0 @@
-toString($options_padding));
- }
- }
- }
\ No newline at end of file
diff --git a/src/ncc/Classes/BashExtension/BashRunner.php b/src/ncc/Classes/BashExtension/BashRunner.php
new file mode 100644
index 0000000..c10b993
--- /dev/null
+++ b/src/ncc/Classes/BashExtension/BashRunner.php
@@ -0,0 +1,56 @@
+Execute->Target = null;
+ $execution_unit->ExecutionPolicy = $policy;
+ $execution_unit->Data = IO::fread($path);
+
+ return $execution_unit;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getFileExtension(): string
+ {
+ return '.bash';
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/ComposerExtension/ComposerSource.php b/src/ncc/Classes/ComposerExtension/ComposerSourceBuiltin.php
similarity index 54%
rename from src/ncc/Classes/ComposerExtension/ComposerSource.php
rename to src/ncc/Classes/ComposerExtension/ComposerSourceBuiltin.php
index d732939..a70bb18 100644
--- a/src/ncc/Classes/ComposerExtension/ComposerSource.php
+++ b/src/ncc/Classes/ComposerExtension/ComposerSourceBuiltin.php
@@ -1,6 +1,26 @@
toStandard()));
}
+ /**
+ * Works with a local composer.json file and attempts to compile the required packages
+ * and their dependencies, returns the path to the compiled package.
+ *
+ * @param string $path
+ * @return string
+ * @throws AccessDeniedException
+ * @throws BuildConfigurationNotFoundException
+ * @throws BuildException
+ * @throws ComposerDisabledException
+ * @throws ComposerException
+ * @throws ComposerNotAvailableException
+ * @throws DirectoryNotFoundException
+ * @throws FileNotFoundException
+ * @throws IOException
+ * @throws InternalComposerNotAvailableException
+ * @throws MalformedJsonException
+ * @throws PackageNotFoundException
+ * @throws PackagePreparationFailedException
+ * @throws ProjectConfigurationNotFoundException
+ * @throws UnsupportedCompilerExtensionException
+ * @throws UserAbortedOperationException
+ */
+ public static function fromLocal(string $path): string
+ {
+ // Check if the file composer.json exists
+ if (!file_exists($path . DIRECTORY_SEPARATOR . 'composer.json'))
+ throw new FileNotFoundException(sprintf('File "%s" not found', $path . DIRECTORY_SEPARATOR . 'composer.json'));
+
+ // Execute composer with options
+ $options = self::getOptions();
+ $composer_exec = self::getComposerPath();
+ $process = new Process([$composer_exec, 'install']);
+ self::prepareProcess($process, $path, $options);
+
+ Console::outDebug(sprintf('executing %s', $process->getCommandLine()));
+ $process->run(function ($type, $buffer) {
+ Console::out($buffer, false);
+ });
+
+ if (!$process->isSuccessful())
+ throw new ComposerException($process->getErrorOutput());
+
+ $filesystem = new Filesystem();
+ if($filesystem->exists($path . DIRECTORY_SEPARATOR . 'build'))
+ $filesystem->remove($path . DIRECTORY_SEPARATOR . 'build');
+ $filesystem->mkdir($path . DIRECTORY_SEPARATOR . 'build');
+
+ // Compile dependencies
+ self::compilePackages($path . DIRECTORY_SEPARATOR . 'composer.lock');
+
+ $composer_lock = Functions::loadJson(IO::fread($path . DIRECTORY_SEPARATOR . 'composer.lock'), Functions::FORCE_ARRAY);
+ $version_map = self::getVersionMap(ComposerLock::fromArray($composer_lock));
+
+ // Finally convert the main package's composer.json to package.json and compile it
+ ComposerSourceBuiltin::convertProject($path, $version_map);
+ $project_manager = new ProjectManager($path);
+ $project_manager->load();
+ $built_package = $project_manager->build();
+
+ RuntimeCache::setFileAsTemporary($built_package);
+ return $built_package;
+ }
+
/**
* @param string $composer_lock_path
* @return array
@@ -109,7 +193,6 @@
* @throws PackagePreparationFailedException
* @throws ProjectConfigurationNotFoundException
* @throws UnsupportedCompilerExtensionException
- * @throws UnsupportedRunnerException
*/
private static function compilePackages(string $composer_lock_path): array
{
@@ -129,108 +212,19 @@
}
$filesystem->mkdir($base_dir . DIRECTORY_SEPARATOR . 'build');
+ $version_map = self::getVersionMap($composer_lock);
foreach ($composer_lock->Packages as $package)
{
$package_path = $base_dir . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package->Name;
- // Generate the package configuration
- $project_configuration = ComposerSource::generateProjectConfiguration($package->Name, $composer_lock);
- // Process the source files
- if ($package->Autoload !== null)
- {
- $source_directory = $package_path . DIRECTORY_SEPARATOR . '.src';
- if ($filesystem->exists($source_directory))
- {
- $filesystem->remove($source_directory);
- }
- $filesystem->mkdir($source_directory);
- $source_directories = [];
- $static_files = [];
+ // Load the composer lock file
+ $composer_package = $composer_lock->getPackage($package->Name);
+ if ($composer_package == null)
+ throw new PackageNotFoundException(sprintf('Package "%s" not found in composer lock file', $package->Name));
- // TODO: Implement static files handling
-
- // Extract all the source directories
- if ($package->Autoload->Psr4 !== null && count($package->Autoload->Psr4) > 0)
- {
- Console::outVerbose('Extracting PSR-4 source directories');
- foreach ($package->Autoload->Psr4 as $namespace_pointer)
- {
- if ($namespace_pointer->Path !== null && !in_array($namespace_pointer->Path, $source_directories))
- {
- $source_directories[] = $package_path . DIRECTORY_SEPARATOR . $namespace_pointer->Path;
- }
- }
- }
-
- if ($package->Autoload->Psr0 !== null && count($package->Autoload->Psr0) > 0)
- {
- Console::outVerbose('Extracting PSR-0 source directories');
- foreach ($package->Autoload->Psr0 as $namespace_pointer)
- {
- if ($namespace_pointer->Path !== null && !in_array($namespace_pointer->Path, $source_directories))
- {
- $source_directories[] = $package_path . DIRECTORY_SEPARATOR . $namespace_pointer->Path;
- }
- }
- }
-
- if ($package->Autoload->Files !== null && count($package->Autoload->Files) > 0)
- {
- Console::outVerbose('Extracting static files');
- foreach ($package->Autoload->Files as $file)
- {
- $static_files[] = $package_path . DIRECTORY_SEPARATOR . $file;
- }
- }
-
- Console::outDebug(sprintf('source directories: %s', implode(', ', $source_directories)));
-
- // First scan the project files and create a file struct.
- $DirectoryScanner = new DirectoryScanner();
-
- // TODO: Implement exclude-class handling
- try
- {
- $DirectoryScanner->unsetFlag(FilesystemIterator::FOLLOW_SYMLINKS);
- }
- catch (Exception $e)
- {
- throw new PackagePreparationFailedException('Cannot unset flag \'FOLLOW_SYMLINKS\' in DirectoryScanner, ' . $e->getMessage(), $e);
- }
-
- // Include file components that can be compiled
- $DirectoryScanner->setIncludes(ComponentFileExtensions::Php);
-
- foreach ($source_directories as $directory)
- {
- /** @var SplFileInfo $item */
- /** @noinspection PhpRedundantOptionalArgumentInspection */
- foreach ($DirectoryScanner($directory, True) as $item)
- {
- if (is_dir($item->getPathName()))
- continue;
-
- $parsed_path = str_ireplace($package_path . DIRECTORY_SEPARATOR, '', $item->getPathName());
-
- Console::outDebug(sprintf('copying file %s for package %s', $parsed_path, $package->Name));
- $filesystem->copy($item->getPathName(), $source_directory . DIRECTORY_SEPARATOR . $parsed_path);
- }
- }
-
- if (count($static_files) > 0)
- {
- $project_configuration->Project->Options['static_files'] = $static_files;
- $parsed_path = str_ireplace($package_path . DIRECTORY_SEPARATOR, '', $item->getPathName());
- if (!$filesystem->exists($source_directory . DIRECTORY_SEPARATOR . $parsed_path))
- {
- Console::outDebug(sprintf('copying file %s for package %s', $parsed_path, $package->Name));
- $filesystem->copy($item->getPathName(), $source_directory . DIRECTORY_SEPARATOR . $parsed_path);
- }
- }
-
- $project_configuration->toFile($package_path . DIRECTORY_SEPARATOR . 'project.json');
- }
+ // Convert it to a NCC project configuration
+ $project_configuration = self::convertProject($package_path, $version_map, $composer_package);
// Load the project
$project_manager = new ProjectManager($package_path);
@@ -238,15 +232,31 @@
$built_package = $project_manager->build();
// Copy the project to the build directory
- $out_path = $base_dir . DIRECTORY_SEPARATOR . 'build' . DIRECTORY_SEPARATOR . sprintf('%s=%s.ncc', $project_configuration->Assembly->Package, $project_configuration->Assembly->Version);
+ $out_path = $base_dir . DIRECTORY_SEPARATOR . 'build' . DIRECTORY_SEPARATOR . sprintf('%s.ncc', $project_configuration->Assembly->Package);
$filesystem->copy($built_package, $out_path);
+ $filesystem->remove($built_package);
$built_packages[$project_configuration->Assembly->Package] = $out_path;
-
}
return $built_packages;
}
+ /**
+ * Returns array of versions from the ComposerLock file
+ *
+ * @param ComposerLock $composerLock
+ * @return array
+ */
+ private static function getVersionMap(ComposerLock $composerLock): array
+ {
+ $version_map = [];
+ foreach($composerLock->Packages as $package)
+ {
+ $version_map[$package->Name] = $package->Version;
+ }
+ return $version_map;
+ }
+
/**
* Converts a composer package name to a valid package name
*
@@ -271,22 +281,33 @@
return null;
}
+ /**
+ * Returns a valid version from a version map
+ *
+ * @param string $package_name
+ * @param array $version_map
+ * @return string
+ */
+ private static function versionMap(string $package_name, array $version_map): string
+ {
+ if (array_key_exists($package_name, $version_map))
+ {
+ return Functions::convertToSemVer($version_map[$package_name]);
+ }
+
+ return '1.0.0';
+ }
+
/**
* Generates a project configuration from a package selection
* from the composer.lock file
*
- * @param string $package_name
- * @param ComposerLock $composer_lock
+ * @param ComposerJson $composer_package
+ * @param array $version_map
* @return ProjectConfiguration
- * @throws PackageNotFoundException
*/
- private static function generateProjectConfiguration(string $package_name, ComposerLock $composer_lock): ProjectConfiguration
+ private static function generateProjectConfiguration(ComposerJson $composer_package, array $version_map): ProjectConfiguration
{
- // Load the composer lock file
- $composer_package = $composer_lock->getPackage($package_name);
- if ($composer_package == null)
- throw new PackageNotFoundException(sprintf('Package "%s" not found in composer lock file', $package_name));
-
// Generate a new project configuration object
$project_configuration = new ProjectConfiguration();
@@ -294,37 +315,41 @@
$project_configuration->Assembly->Name = $composer_package->Name;
if (isset($composer_package->Description))
$project_configuration->Assembly->Description = $composer_package->Description;
- if (isset($composer_package->Version))
- $project_configuration->Assembly->Version = Functions::parseVersion($composer_package->Version);
+
+ if(isset($version_map[$composer_package->Name]))
+ $project_configuration->Assembly->Version = self::versionMap($composer_package->Name, $version_map);
+ if($project_configuration->Assembly->Version == null || $project_configuration->Assembly->Version == '')
+ $project_configuration->Assembly->Version = '1.0.0';
+
$project_configuration->Assembly->UUID = Uuid::v1()->toRfc4122();
- $project_configuration->Assembly->Package = self::toPackageName($package_name);
+ $project_configuration->Assembly->Package = self::toPackageName($composer_package->Name);
+
+ // Add the update source
+ $project_configuration->Project->UpdateSource = new ProjectConfiguration\UpdateSource();
+ $project_configuration->Project->UpdateSource->Source = sprintf('%s@composer', str_ireplace('\\', '/', $composer_package->Name));
+ $project_configuration->Project->UpdateSource->Repository = null;
// Process the dependencies
- foreach ($composer_package->Require as $item)
+ if($composer_package->Require !== null && count($composer_package->Require) > 0)
{
- $package_name = self::toPackageName($item->PackageName);
- $package_version = $composer_lock->getPackage($item->PackageName)?->Version;
- if ($package_version == null)
+ foreach ($composer_package->Require as $item)
{
- $package_version = '1.0.0';
+ // Check if the dependency is already in the project configuration
+ $package_name = self::toPackageName($item->PackageName);
+ if ($package_name == null)
+ continue;
+ $dependency = new ProjectConfiguration\Dependency();
+ $dependency->Name = $package_name;
+ $dependency->SourceType = DependencySourceType::Local;
+ $dependency->Version = self::versionMap($item->PackageName, $version_map);
+ $dependency->Source = $package_name . '.ncc';
+ $project_configuration->Build->addDependency($dependency);
}
- else
- {
- $package_version = Functions::parseVersion($package_version);
- }
- if ($package_name == null)
- continue;
- $dependency = new ProjectConfiguration\Dependency();
- $dependency->Name = $package_name;
- $dependency->SourceType = DependencySourceType::Local;
- $dependency->Version = $package_version;
- $dependency->Source = $package_name . '=' . $dependency->Version . '.ncc';
- $project_configuration->Build->Dependencies[] = $dependency;
}
// Create a build configuration
- $build_configuration = new ProjectConfiguration\BuildConfiguration();
+ $build_configuration = new ProjectConfiguration\Build\BuildConfiguration();
$build_configuration->Name = 'default';
$build_configuration->OutputPath = 'build';
@@ -341,22 +366,6 @@
return $project_configuration;
}
- /**
- * Extracts a version if available from the input
- *
- * @param string $input
- * @return string|null
- */
- private static function extractVersion(string $input): ?string
- {
- if (stripos($input, ':'))
- {
- return explode(':', $input)[1];
- }
-
- return null;
- }
-
/**
* Gets the applicable options configured for composer
*
@@ -437,7 +446,6 @@
* @param string $vendor
* @param string $package
* @param string|null $version
- * @param array $options
* @return string
* @throws AccessDeniedException
* @throws ComposerDisabledException
@@ -449,13 +457,15 @@
* @throws InvalidScopeException
* @throws UserAbortedOperationException
*/
- private static function require(string $vendor, string $package, ?string $version = null, array $options = []): string
+ private static function require(string $vendor, string $package, ?string $version = null): string
{
if (Resolver::resolveScope() !== Scopes::System)
throw new AccessDeniedException('Insufficient permissions to require');
if ($version == null)
$version = '*';
+ if($version == 'latest')
+ $version = '*';
$tpl_file = __DIR__ . DIRECTORY_SEPARATOR . 'composer.jtpl';
if (!file_exists($tpl_file))
@@ -482,29 +492,11 @@
// Execute composer with options
$options = self::getOptions();
$process = new Process(array_merge([$composer_exec, 'require'], $options));
- $process->setWorkingDirectory($tmp_dir);
-
- // Check if scripts are enabled while running as root
- if (!in_array('--no-scripts', $options) && Resolver::resolveScope() == Scopes::System)
- {
- Console::outWarning('composer scripts are enabled while running as root, this can allow malicious scripts to run as root');
- if (!isset($options['--no-interaction']))
- {
- if (!Console::getBooleanInput('Do you want to continue?'))
- throw new UserAbortedOperationException('The operation was aborted by the user');
-
- // The user understands the risks and wants to continue
- $process->setEnv(['COMPOSER_ALLOW_SUPERUSER' => 1]);
- }
- }
- else
- {
- // Composer is running "safely". We can disable the superuser check
- $process->setEnv(['COMPOSER_ALLOW_SUPERUSER' => 1]);
- }
+ self::prepareProcess($process, $tmp_dir, $options);
Console::outDebug(sprintf('executing %s', $process->getCommandLine()));
- $process->run(function ($type, $buffer) {
+ $process->run(function ($type, $buffer)
+ {
Console::out($buffer, false);
});
@@ -558,4 +550,206 @@
throw new ComposerNotAvailableException('No composer executable path is configured');
}
+
+ /**
+ * @param Process $process
+ * @param string $path
+ * @param array $options
+ * @return void
+ * @throws UserAbortedOperationException
+ */
+ private static function prepareProcess(Process $process, string $path, array $options): void
+ {
+ $process->setWorkingDirectory($path);
+
+ // Check if scripts are enabled while running as root
+ if (!in_array('--no-scripts', $options) && Resolver::resolveScope() == Scopes::System)
+ {
+ Console::outWarning('composer scripts are enabled while running as root, this can allow malicious scripts to run as root');
+ if (!isset($options['--no-interaction']))
+ {
+ if (!Console::getBooleanInput('Do you want to continue?'))
+ throw new UserAbortedOperationException('The operation was aborted by the user');
+
+ // The user understands the risks and wants to continue
+ $process->setEnv(['COMPOSER_ALLOW_SUPERUSER' => 1]);
+ }
+ }
+ else
+ {
+ // Composer is running "safely". We can disable the superuser check
+ $process->setEnv(['COMPOSER_ALLOW_SUPERUSER' => 1]);
+ }
+ }
+
+ /**
+ * Converts a composer project to a NCC project
+ *
+ * @param string $package_path
+ * @param array $version_map
+ * @param mixed $composer_package
+ * @return ProjectConfiguration
+ * @throws AccessDeniedException
+ * @throws FileNotFoundException
+ * @throws IOException
+ * @throws MalformedJsonException
+ * @throws PackagePreparationFailedException
+ */
+ private static function convertProject(string $package_path, array $version_map, ?ComposerJson $composer_package=null): ProjectConfiguration
+ {
+ if($composer_package == null)
+ $composer_package = ComposerJson::fromArray(Functions::loadJsonFile($package_path . DIRECTORY_SEPARATOR . 'composer.json', Functions::FORCE_ARRAY));
+
+ $project_configuration = ComposerSourceBuiltin::generateProjectConfiguration($composer_package, $version_map);
+ $filesystem = new Filesystem();
+
+ // Process the source files
+ if ($composer_package->Autoload !== null)
+ {
+ $source_directory = $package_path . DIRECTORY_SEPARATOR . '.src';
+ if ($filesystem->exists($source_directory))
+ $filesystem->remove($source_directory);
+ $filesystem->mkdir($source_directory);
+ $source_directories = [];
+ $static_files = [];
+
+ // Extract all the source directories
+ if ($composer_package->Autoload->Psr4 !== null && count($composer_package->Autoload->Psr4) > 0)
+ {
+ Console::outVerbose('Extracting PSR-4 source directories');
+ foreach ($composer_package->Autoload->Psr4 as $namespace_pointer)
+ {
+ if ($namespace_pointer->Path !== null && !in_array($namespace_pointer->Path, $source_directories))
+ {
+ $source_directories[] = $package_path . DIRECTORY_SEPARATOR . $namespace_pointer->Path;
+ }
+ }
+ }
+
+ if ($composer_package->Autoload->Psr0 !== null && count($composer_package->Autoload->Psr0) > 0)
+ {
+ Console::outVerbose('Extracting PSR-0 source directories');
+ foreach ($composer_package->Autoload->Psr0 as $namespace_pointer)
+ {
+ if ($namespace_pointer->Path !== null && !in_array($namespace_pointer->Path, $source_directories))
+ {
+ $source_directories[] = $package_path . DIRECTORY_SEPARATOR . $namespace_pointer->Path;
+ }
+ }
+ }
+
+ if ($composer_package->Autoload->Files !== null && count($composer_package->Autoload->Files) > 0)
+ {
+ Console::outVerbose('Extracting static files');
+ foreach ($composer_package->Autoload->Files as $file)
+ {
+ $static_files[] = $package_path . DIRECTORY_SEPARATOR . $file;
+ }
+ }
+
+ Console::outDebug(sprintf('source directories: %s', implode(', ', $source_directories)));
+
+ // First scan the project files and create a file struct.
+ $DirectoryScanner = new DirectoryScanner();
+
+ // TODO: Implement exclude-class handling
+ try
+ {
+ $DirectoryScanner->unsetFlag(FilesystemIterator::FOLLOW_SYMLINKS);
+ }
+ catch (Exception $e)
+ {
+ throw new PackagePreparationFailedException('Cannot unset flag \'FOLLOW_SYMLINKS\' in DirectoryScanner, ' . $e->getMessage(), $e);
+ }
+
+ // Include file components that can be compiled
+ $DirectoryScanner->setIncludes(ComponentFileExtensions::Php);
+
+ foreach ($source_directories as $directory)
+ {
+ /** @var SplFileInfo $item */
+ /** @noinspection PhpRedundantOptionalArgumentInspection */
+ foreach ($DirectoryScanner($directory, True) as $item)
+ {
+ if (is_dir($item->getPathName()))
+ continue;
+
+ $parsed_path = str_ireplace($package_path . DIRECTORY_SEPARATOR, '', $item->getPathName());
+
+ Console::outDebug(sprintf('copying file %s for package %s', $parsed_path, $composer_package->Name));
+ $filesystem->copy($item->getPathName(), $source_directory . DIRECTORY_SEPARATOR . $parsed_path);
+ }
+ }
+
+ if (count($static_files) > 0)
+ {
+ $project_configuration->Project->Options['static_files'] = $static_files;
+
+ foreach ($static_files as $file)
+ {
+ $parsed_path = str_ireplace($package_path . DIRECTORY_SEPARATOR, '', $file);
+ Console::outDebug(sprintf('copying file %s for package %s', $parsed_path, $composer_package->Name));
+ $filesystem->copy($file, $source_directory . DIRECTORY_SEPARATOR . $parsed_path);
+ }
+ unset($file);
+ }
+ }
+
+ $project_configuration->toFile($package_path . DIRECTORY_SEPARATOR . 'project.json');
+
+ // This part simply displays the package information to the command-line interface
+ if(ncc::cliMode())
+ {
+ $license_files = [
+ 'LICENSE',
+ 'license',
+ 'LICENSE.txt',
+ 'license.txt'
+ ];
+
+ foreach($license_files as $license_file)
+ {
+ if($filesystem->exists($package_path . DIRECTORY_SEPARATOR . $license_file))
+ {
+ // Check configuration if composer.extension.display_licenses is set
+ if(Functions::cbool(Functions::getConfigurationProperty('composer.extension.display_licenses')))
+ {
+ Console::out(sprintf('License for package %s:', $composer_package->Name));
+ Console::out(IO::fread($package_path . DIRECTORY_SEPARATOR . $license_file));
+ break;
+ }
+ }
+ }
+
+ if(Functions::cbool(Functions::getConfigurationProperty('composer.extension.display_authors')))
+ {
+ if($composer_package->Authors !== null && count($composer_package->Authors) > 0)
+ {
+ Console::out(sprintf('Authors for package %s:', $composer_package->Name));
+ foreach($composer_package->Authors as $author)
+ {
+ Console::out(sprintf(' - %s', $author->Name));
+
+ if($author->Email !== null)
+ {
+ Console::out(sprintf(' %s', $author->Email));
+ }
+
+ if($author->Homepage !== null)
+ {
+ Console::out(sprintf(' %s', $author->Homepage));
+ }
+
+ if($author->Role !== null)
+ {
+ Console::out(sprintf(' %s', $author->Role));
+ }
+
+ }
+ }
+ }
+ }
+
+ return $project_configuration;
+ }
}
\ No newline at end of file
diff --git a/src/ncc/Classes/EnvironmentConfiguration.php b/src/ncc/Classes/EnvironmentConfiguration.php
deleted file mode 100644
index 5910bd0..0000000
--- a/src/ncc/Classes/EnvironmentConfiguration.php
+++ /dev/null
@@ -1,71 +0,0 @@
- $config)
- {
- $results[$name] = PhpConfiguration::fromArray($config);
- }
-
- return $results;
- }
-
- /**
- * Returns an array of only the changed configuration values
- *
- * @return PhpConfiguration[]
- */
- public static function getChangedValues(): array
- {
- $results = [];
-
- foreach(ini_get_all() as $name => $config)
- {
- $config = PhpConfiguration::fromArray($config);
- if($config->LocalValue !== $config->GlobalValue)
- {
- $results[$name] = $config;
- }
- }
-
- return $results;
- }
-
- /**
- * @param string $file_path
- * @return void
- */
- public static function export(string $file_path)
- {
- $configuration = [];
- foreach(self::getChangedValues() as $changedValue)
- {
- $configuration[$changedValue->getName()] = $changedValue->getValue();
- }
-
- // TODO: Implement ini writing process here
- }
-
- public static function import(string $file_path)
- {
- // TODO: Implement ini reading process here
- $configuration = [];
- foreach($configuration as $item => $value)
- {
- ini_set($item, $value);
- }
- }
- }
\ No newline at end of file
diff --git a/src/ncc/Classes/GitClient.php b/src/ncc/Classes/GitClient.php
new file mode 100644
index 0000000..6245ed7
--- /dev/null
+++ b/src/ncc/Classes/GitClient.php
@@ -0,0 +1,132 @@
+setTimeout(3600); // 1 hour
+ $process->run(function ($type, $buffer)
+ {
+ Console::outVerbose($buffer);
+ });
+
+ if (!$process->isSuccessful())
+ throw new GitCloneException($process->getErrorOutput());
+
+ Console::outVerbose('Repository cloned to: ' . $path);
+
+ return $path;
+ }
+
+ /**
+ * Checks out a specific branch or tag.
+ *
+ * @param string $path
+ * @param string $branch
+ * @throws GitCheckoutException
+ */
+ public static function checkout(string $path, string $branch): void
+ {
+ Console::outVerbose('Checking out branch' . $branch);
+ $process = new Process(["git", "checkout", $branch], $path);
+ $process->setTimeout(3600); // 1 hour
+ $process->run(function ($type, $buffer)
+ {
+ if (Process::ERR === $type)
+ {
+ Console::outWarning($buffer);
+ }
+ else
+ {
+ Console::outVerbose($buffer);
+ }
+ });
+
+ if (!$process->isSuccessful())
+ throw new GitCheckoutException($process->getErrorOutput());
+
+ Console::outVerbose('Checked out branch: ' . $branch);
+ }
+
+ /**
+ * Returns an array of tags that are available in the repository.
+ *
+ * @param string $path
+ * @return array
+ * @throws GitTagsException
+ */
+ public static function getTags(string $path): array
+ {
+ Console::outVerbose('Getting tags for repository: ' . $path);
+ $process = new Process(["git", "fetch", '--all', '--tags'] , $path);
+ $process->setTimeout(3600); // 1 hour
+ $process->run(function ($type, $buffer)
+ {
+ Console::outVerbose($buffer);
+ });
+
+ if (!$process->isSuccessful())
+ throw new GitTagsException($process->getErrorOutput());
+
+ $process = new Process(['git', '--no-pager', 'tag', '-l'] , $path);
+
+ $process->run(function ($type, $buffer)
+ {
+ Console::outVerbose($buffer);
+ });
+
+ if (!$process->isSuccessful())
+ throw new GitTagsException($process->getErrorOutput());
+
+ $tags = explode(PHP_EOL, $process->getOutput());
+ $tags = array_filter($tags, function ($tag)
+ {
+ return !empty($tag);
+ });
+
+ Console::outDebug('found ' . count($tags) . ' tags');
+ return array_filter($tags);
+ }
+
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/GithubExtension/GithubService.php b/src/ncc/Classes/GithubExtension/GithubService.php
new file mode 100644
index 0000000..4b8bcc2
--- /dev/null
+++ b/src/ncc/Classes/GithubExtension/GithubService.php
@@ -0,0 +1,277 @@
+SSL ? "https" : "http");
+ $owner_f = str_ireplace("/", "%2F", $packageInput->Vendor);
+ $owner_f = str_ireplace(".", "%2F", $owner_f);
+ $repository = urlencode($packageInput->Package);
+ $httpRequest->Url = $protocol . '://' . $definedRemoteSource->Host . "/repos/$owner_f/$repository";
+ $response_decoded = self::getJsonResponse($httpRequest, $entry);
+
+ $query = new RepositoryQueryResults();
+ $query->Files->GitSshUrl = ($response_decoded['ssh_url'] ?? null);
+ $query->Files->GitHttpUrl = ($response_decoded['clone_url'] ?? null);
+ $query->Version = Functions::convertToSemVer($response_decoded['default_branch'] ?? null);
+ $query->ReleaseDescription = ($response_decoded['description'] ?? null);
+ $query->ReleaseName = ($response_decoded['name'] ?? null);
+
+
+ return $query;
+ }
+
+ /**
+ * Returns the download URL of the requested version of the package.
+ *
+ * @param RemotePackageInput $packageInput
+ * @param DefinedRemoteSource $definedRemoteSource
+ * @param Entry|null $entry
+ * @return RepositoryQueryResults
+ * @throws AuthenticationException
+ * @throws GithubServiceException
+ * @throws GitlabServiceException
+ * @throws HttpException
+ * @throws MalformedJsonException
+ * @throws VersionNotFoundException
+ */
+ public static function getRelease(RemotePackageInput $packageInput, DefinedRemoteSource $definedRemoteSource, ?Entry $entry = null): RepositoryQueryResults
+ {
+ return self::processReleases($packageInput, $definedRemoteSource, $entry);
+ }
+
+ /**
+ * @param RemotePackageInput $packageInput
+ * @param DefinedRemoteSource $definedRemoteSource
+ * @param Entry|null $entry
+ * @return RepositoryQueryResults
+ * @throws AuthenticationException
+ * @throws GithubServiceException
+ * @throws GitlabServiceException
+ * @throws HttpException
+ * @throws MalformedJsonException
+ * @throws VersionNotFoundException
+ */
+ public static function getNccPackage(RemotePackageInput $packageInput, DefinedRemoteSource $definedRemoteSource, ?Entry $entry = null): RepositoryQueryResults
+ {
+ return self::processReleases($packageInput, $definedRemoteSource, $entry);
+ }
+
+ /**
+ * Returns a list of all releases of the given repository with their download URL.
+ *
+ * @param RemotePackageInput $packageInput
+ * @param DefinedRemoteSource $definedRemoteSource
+ * @param Entry|null $entry
+ * @return array
+ * @throws AuthenticationException
+ * @throws GithubServiceException
+ * @throws GitlabServiceException
+ * @throws HttpException
+ * @throws MalformedJsonException
+ */
+ private static function getReleases(RemotePackageInput $packageInput, DefinedRemoteSource $definedRemoteSource, ?Entry $entry = null): array
+ {
+ $httpRequest = new HttpRequest();
+ $protocol = ($definedRemoteSource->SSL ? "https" : "http");
+ $owner_f = str_ireplace("/", "%2F", $packageInput->Vendor);
+ $owner_f = str_ireplace(".", "%2F", $owner_f);
+ $repository = urlencode($packageInput->Package);
+ $httpRequest->Url = $protocol . '://' . $definedRemoteSource->Host . "/repos/$owner_f/$repository/releases";
+ $response_decoded = self::getJsonResponse($httpRequest, $entry);
+
+ if(count($response_decoded) == 0)
+ return [];
+
+ $return = [];
+ foreach($response_decoded as $release)
+ {
+ $query_results = new RepositoryQueryResults();
+ $query_results->Version = Functions::convertToSemVer($release['tag_name']);
+ $query_results->ReleaseName = $release['name'];
+ $query_results->ReleaseDescription = $release['body'];
+ $query_results->Files->ZipballUrl = ($release['zipball_url'] ?? null);
+ $query_results->Files->TarballUrl = ($release['tarball_url'] ?? null);
+
+ if(isset($release['assets']))
+ {
+ foreach($release['assets'] as $asset)
+ {
+ $parsed_asset = self::parseAsset($asset);
+ if($parsed_asset !== null)
+ $query_results->Files->PackageUrl = $parsed_asset;
+ }
+ }
+
+ $return[$query_results->Version] = $query_results;
+ }
+
+ return $return;
+ }
+
+ /**
+ * Returns the asset download URL if it points to a .ncc package.
+ *
+ * @param array $asset
+ * @return string|null'
+ */
+ private static function parseAsset(array $asset): ?string
+ {
+ if(isset($asset['browser_download_url']))
+ {
+ $file_extension = pathinfo($asset['browser_download_url'], PATHINFO_EXTENSION);
+ if($file_extension == 'ncc')
+ return $asset['browser_download_url'];
+ }
+
+ return null;
+ }
+
+ /**
+ * @param HttpRequest $httpRequest
+ * @param Entry|null $entry
+ * @return array
+ * @throws AuthenticationException
+ * @throws GithubServiceException
+ * @throws GitlabServiceException
+ * @throws HttpException
+ * @throws MalformedJsonException
+ */
+ private static function getJsonResponse(HttpRequest $httpRequest, ?Entry $entry): array
+ {
+ $httpRequest->Type = HttpRequestType::GET;
+ $httpRequest = Functions::prepareGitServiceRequest($httpRequest, $entry, false);
+ $httpRequest->Headers[] = 'X-GitHub-Api-Version: 2022-11-28';
+ $httpRequest->Headers[] = 'Accept: application/vnd.github+json';
+
+ $response = HttpClient::request($httpRequest, true);
+
+ if ($response->StatusCode != 200)
+ throw new GithubServiceException(sprintf('Failed to fetch releases for the given repository. Status code: %s', $response->StatusCode));
+
+ return Functions::loadJson($response->Body, Functions::FORCE_ARRAY);
+ }
+
+ /**
+ * @param RemotePackageInput $packageInput
+ * @param DefinedRemoteSource $definedRemoteSource
+ * @param Entry|null $entry
+ * @return mixed
+ * @throws AuthenticationException
+ * @throws GithubServiceException
+ * @throws GitlabServiceException
+ * @throws HttpException
+ * @throws MalformedJsonException
+ * @throws VersionNotFoundException
+ */
+ private static function processReleases(RemotePackageInput $packageInput, DefinedRemoteSource $definedRemoteSource, ?Entry $entry): mixed
+ {
+ $releases = self::getReleases($packageInput, $definedRemoteSource, $entry);
+
+ if (count($releases) === 0)
+ throw new VersionNotFoundException('No releases found for the given repository.');
+
+ if ($packageInput->Version == Versions::Latest)
+ {
+ $latest_version = null;
+ foreach ($releases as $release)
+ {
+ if ($latest_version == null)
+ {
+ $latest_version = $release->Version;
+ continue;
+ }
+
+ if (VersionComparator::compareVersion($release->Version, $latest_version) == 1)
+ $latest_version = $release->Version;
+ }
+
+ return $releases[$latest_version];
+ }
+
+ // Query a specific version
+ if (!isset($releases[$packageInput->Version]))
+ {
+ // Find the closest thing to the requested version
+ $selected_version = null;
+ foreach ($releases as $version => $url)
+ {
+ if ($selected_version == null)
+ {
+ $selected_version = $version;
+ continue;
+ }
+
+ if (VersionComparator::compareVersion($version, $packageInput->Version) == 1)
+ $selected_version = $version;
+ }
+
+ if ($selected_version == null)
+ throw new VersionNotFoundException('No releases found for the given repository.');
+ }
+ else
+ {
+ $selected_version = $packageInput->Version;
+ }
+
+ if (!isset($releases[$selected_version]))
+ throw new VersionNotFoundException(sprintf('No releases found for the given repository. (Selected version: %s)', $selected_version));
+
+ return $releases[$selected_version];
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/GitlabExtension/GitlabService.php b/src/ncc/Classes/GitlabExtension/GitlabService.php
new file mode 100644
index 0000000..3a4dd0e
--- /dev/null
+++ b/src/ncc/Classes/GitlabExtension/GitlabService.php
@@ -0,0 +1,242 @@
+SSL ? "https" : "http");
+ $owner_f = str_ireplace("/", "%2F", $packageInput->Vendor);
+ $owner_f = str_ireplace(".", "%2F", $owner_f);
+ $project_f = str_ireplace("/", "%2F", $packageInput->Package);
+ $project_f = str_ireplace(".", "%2F", $project_f);
+ $httpRequest->Url = $protocol . '://' . $definedRemoteSource->Host . "/api/v4/projects/$owner_f%2F$project_f";
+ $httpRequest = Functions::prepareGitServiceRequest($httpRequest, $entry);
+
+ $response = HttpClient::request($httpRequest, true);
+
+ if($response->StatusCode != 200)
+ throw new GitlabServiceException(sprintf('Failed to fetch releases for the given repository. Status code: %s', $response->StatusCode));
+
+ $response_decoded = Functions::loadJson($response->Body, Functions::FORCE_ARRAY);
+
+ $query = new RepositoryQueryResults();
+ $query->Files->GitSshUrl = ($response_decoded['ssh_url_to_repo'] ?? null);
+ $query->Files->GitHttpUrl = ($response_decoded['http_url_to_repo'] ?? null);
+ $query->Version = Functions::convertToSemVer($response_decoded['default_branch']) ?? null;
+ $query->ReleaseDescription = ($response_decoded['description'] ?? null);
+ $query->ReleaseName = ($response_decoded['name'] ?? null);
+
+ return $query;
+ }
+
+ /**
+ * Returns the download URL of the requested version of the package.
+ *
+ * @param RemotePackageInput $packageInput
+ * @param DefinedRemoteSource $definedRemoteSource
+ * @param Entry|null $entry
+ * @return RepositoryQueryResults
+ * @throws AuthenticationException
+ * @throws GitlabServiceException
+ * @throws HttpException
+ * @throws MalformedJsonException
+ * @throws VersionNotFoundException
+ */
+ public static function getRelease(RemotePackageInput $packageInput, DefinedRemoteSource $definedRemoteSource, ?Entry $entry = null): RepositoryQueryResults
+ {
+ $releases = self::getReleases($packageInput->Vendor, $packageInput->Package, $definedRemoteSource, $entry);
+
+ if(count($releases) === 0)
+ throw new VersionNotFoundException('No releases found for the given repository.');
+
+ // Query the latest package only
+ if($packageInput->Version == Versions::Latest)
+ {
+ $latest_version = null;
+ foreach($releases as $release)
+ {
+ if($latest_version == null)
+ {
+ $latest_version = $release->Version;
+ continue;
+ }
+
+ if(VersionComparator::compareVersion($release->Version, $latest_version) == 1)
+ $latest_version = $release->Version;
+ }
+
+ return $releases[$latest_version];
+ }
+
+ // Query a specific version
+ if(!isset($releases[$packageInput->Version]))
+ {
+ // Find the closest thing to the requested version
+ $selected_version = null;
+ foreach($releases as $version => $url)
+ {
+ if($selected_version == null)
+ {
+ $selected_version = $version;
+ continue;
+ }
+
+ if(VersionComparator::compareVersion($version, $packageInput->Version) == 1)
+ $selected_version = $version;
+ }
+
+ if($selected_version == null)
+ throw new VersionNotFoundException('No releases found for the given repository.');
+ }
+ else
+ {
+ $selected_version = $packageInput->Version;
+ }
+
+ if(!isset($releases[$selected_version]))
+ throw new VersionNotFoundException(sprintf('No releases found for the given repository. (Selected version: %s)', $selected_version));
+
+ return $releases[$selected_version];
+ }
+
+ /**
+ * @param RemotePackageInput $packageInput
+ * @param DefinedRemoteSource $definedRemoteSource
+ * @param Entry|null $entry
+ * @return RepositoryQueryResults
+ * @throws NotSupportedException
+ */
+ public static function getNccPackage(RemotePackageInput $packageInput, DefinedRemoteSource $definedRemoteSource, ?Entry $entry = null): RepositoryQueryResults
+ {
+ throw new NotSupportedException(sprintf('The given repository source "%s" does not support ncc packages.', $definedRemoteSource->Host));
+ }
+
+ /**
+ * Returns an array of all the tags for the given owner and repository name.
+ *
+ * @param string $owner
+ * @param string $repository
+ * @param DefinedRemoteSource $definedRemoteSource
+ * @param Entry|null $entry
+ * @return array
+ * @throws AuthenticationException
+ * @throws GitlabServiceException
+ * @throws HttpException
+ * @throws MalformedJsonException
+ */
+ private static function getReleases(string $owner, string $repository, DefinedRemoteSource $definedRemoteSource, ?Entry $entry): array
+ {
+ $httpRequest = new HttpRequest();
+ $protocol = ($definedRemoteSource->SSL ? "https" : "http");
+ $owner_f = str_ireplace("/", "%2F", $owner);
+ $owner_f = str_ireplace(".", "%2F", $owner_f);
+ $repository_f = str_ireplace("/", "%2F", $repository);
+ $repository_f = str_ireplace(".", "%2F", $repository_f);
+
+ $httpRequest->Url = $protocol . '://' . $definedRemoteSource->Host . "/api/v4/projects/$owner_f%2F$repository_f/releases";
+ $httpRequest = Functions::prepareGitServiceRequest($httpRequest, $entry);
+
+ $response = HttpClient::request($httpRequest, true);
+
+ if($response->StatusCode != 200)
+ throw new GitlabServiceException(sprintf('Failed to fetch releases for the given repository. Status code: %s', $response->StatusCode));
+
+ $response_decoded = Functions::loadJson($response->Body, Functions::FORCE_ARRAY);
+
+ if(count($response_decoded) == 0)
+ return [];
+
+ $return = [];
+ foreach($response_decoded as $release)
+ {
+ $query_results = new RepositoryQueryResults();
+ $query_results->ReleaseName = ($release['name'] ?? null);
+ $query_results->ReleaseDescription = ($release['description'] ?? null);
+ $query_results->Version = Functions::convertToSemVer($release['tag_name']);
+
+ if(isset($release['assets']) && isset($release['assets']['sources']))
+ {
+ if(count($release['assets']['sources']) > 0)
+ {
+ foreach($release['assets']['sources'] as $source)
+ {
+ if($source['format'] == 'zip')
+ {
+ $query_results->Files->ZipballUrl = $source['url'];
+ break;
+ }
+
+ if($source['format'] == 'tar.gz')
+ {
+ $query_results->Files->ZipballUrl = $source['url'];
+ break;
+ }
+
+ if($source['format'] == 'ncc')
+ {
+ $query_results->Files->PackageUrl = $source['url'];
+ break;
+ }
+ }
+ }
+ }
+
+ $return[$query_results->Version] = $query_results;
+ }
+
+ return $return;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/HttpClient.php b/src/ncc/Classes/HttpClient.php
new file mode 100644
index 0000000..11b47e9
--- /dev/null
+++ b/src/ncc/Classes/HttpClient.php
@@ -0,0 +1,241 @@
+Url);
+ curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($curl, CURLOPT_HEADER, true);
+ curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($curl, CURLOPT_MAXREDIRS, 5);
+ curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
+ curl_setopt($curl, CURLOPT_TIMEOUT, 10);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($curl, CURLOPT_HTTPHEADER, $request->Headers);
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $request->Type);
+
+ switch($request->Type)
+ {
+ case HttpRequestType::GET:
+ curl_setopt($curl, CURLOPT_HTTPGET, true);
+ break;
+
+ case HttpRequestType::POST:
+ curl_setopt($curl, CURLOPT_POST, true);
+ if($request->Body !== null)
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $request->Body);
+ break;
+
+ case HttpRequestType::PUT:
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
+ if($request->Body !== null)
+ curl_setopt($curl, CURLOPT_POSTFIELDS, $request->Body);
+ break;
+
+ case HttpRequestType::DELETE:
+ curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
+ break;
+ }
+
+ if (is_array($request->Authentication))
+ {
+ curl_setopt($curl, CURLOPT_USERPWD, $request->Authentication[0] . ':' . $request->Authentication[1]);
+ }
+ else if (is_string($request->Authentication))
+ {
+ curl_setopt($curl, CURLOPT_USERPWD, $request->Authentication);
+ }
+
+ foreach ($request->Options as $option => $value)
+ curl_setopt($curl, $option, $value);
+
+ return $curl;
+ }
+
+ /**
+ * Creates a new HTTP request and returns the response.
+ *
+ * @param HttpRequest $httpRequest
+ * @param bool $cache
+ * @return HttpResponse
+ * @throws HttpException
+ */
+ public static function request(HttpRequest $httpRequest, bool $cache=false): HttpResponse
+ {
+ if($cache)
+ {
+ /** @var HttpResponseCache $cache */
+ $cache = RuntimeCache::get(sprintf('http_cache_%s', $httpRequest->requestHash()));
+ if($cache !== null && $cache->getTtl() > time())
+ {
+ Console::outDebug(sprintf('using cached response for %s', $httpRequest->requestHash()));
+ return $cache->getHttpResponse();
+ }
+ }
+
+ $curl = self::prepareCurl($httpRequest);
+
+ Console::outDebug(sprintf(' => %s request %s', $httpRequest->Type, $httpRequest->Url));
+ if($httpRequest->Headers !== null && count($httpRequest->Headers) > 0)
+ Console::outDebug(sprintf(' => headers: %s', implode(', ', $httpRequest->Headers)));
+ if($httpRequest->Body !== null)
+ Console::outDebug(sprintf(' => body: %s', $httpRequest->Body));
+
+ $response = curl_exec($curl);
+
+ if ($response === false)
+ {
+ $error = curl_error($curl);
+ curl_close($curl);
+ throw new HttpException($error);
+ }
+
+ $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
+ $headers = substr($response, 0, $headerSize);
+ $body = substr($response, $headerSize);
+
+ $httpResponse = new HttpResponse();
+ $httpResponse->StatusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+ $httpResponse->Headers = self::parseHeaders($headers);
+ $httpResponse->Body = $body;
+
+ Console::outDebug(sprintf(' <= %s response', $httpResponse->StatusCode));/** @noinspection PhpConditionAlreadyCheckedInspection */
+ if($httpResponse->Headers !== null && count($httpResponse->Headers) > 0)
+ Console::outDebug(sprintf(' <= headers: %s', implode(', ', $httpResponse->Headers)));
+ /** @noinspection PhpConditionAlreadyCheckedInspection */
+ if($httpResponse->Body !== null)
+ Console::outDebug(sprintf(' <= body: %s', $httpResponse->Body));
+
+ curl_close($curl);
+
+ if($cache)
+ {
+ $httpCacheObject = new HttpResponseCache($httpResponse, time() + 60);
+ RuntimeCache::set(sprintf('http_cache_%s', $httpRequest->requestHash()), $httpCacheObject);
+
+ Console::outDebug(sprintf('cached response for %s', $httpRequest->requestHash()));
+ }
+
+ return $httpResponse;
+ }
+
+ /**
+ * Downloads a file from the given url and saves it to the given path.
+ *
+ * @param HttpRequest $httpRequest
+ * @param string $path
+ * @return void
+ * @throws HttpException
+ */
+ public static function download(HttpRequest $httpRequest, string $path): void
+ {
+ $curl = self::prepareCurl($httpRequest);
+
+ $fp = fopen($path, 'w');
+ curl_setopt($curl, CURLOPT_FILE, $fp);
+ curl_setopt($curl, CURLOPT_HEADER, 0);
+ $response = curl_exec($curl);
+
+ if ($response === false)
+ {
+ $error = curl_error($curl);
+ curl_close($curl);
+ throw new HttpException($error);
+ }
+
+ curl_close($curl);
+ fclose($fp);
+ }
+
+ /**
+ * Displays the download progress in the console
+ *
+ * @param $downloadSize
+ * @param $downloaded
+ * @return void
+ */
+ public static function displayProgress($downloadSize, $downloaded): void
+ {
+ if(Main::getLogLevel() !== null)
+ {
+ switch(Main::getLogLevel())
+ {
+ case LogLevel::Verbose:
+ case LogLevel::Debug:
+ case LogLevel::Silent:
+ Console::outVerbose(sprintf(' <= %s of %s bytes downloaded', $downloaded, $downloadSize));
+ break;
+
+ default:
+ if ($downloadSize > 0)
+ Console::inlineProgressBar($downloaded, $downloadSize);
+ break;
+ }
+ }
+
+
+ }
+
+ /**
+ * Takes the return headers of a cURL request and parses them into an array.
+ *
+ * @param string $input
+ * @return array
+ */
+ private static function parseHeaders(string $input): array
+ {
+ $headers = array();
+ $lines = explode("\n", $input);
+ $headers['HTTP'] = array_shift($lines);
+
+ foreach ($lines as $line) {
+ $header = explode(':', $line, 2);
+ if (count($header) == 2) {
+ $headers[trim($header[0])] = trim($header[1]);
+ }
+ }
+
+ return $headers;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/LuaExtension/LuaRunner.php b/src/ncc/Classes/LuaExtension/LuaRunner.php
new file mode 100644
index 0000000..599c5b2
--- /dev/null
+++ b/src/ncc/Classes/LuaExtension/LuaRunner.php
@@ -0,0 +1,53 @@
+Execute->Target = null;
+ $execution_unit->ExecutionPolicy = $policy;
+ $execution_unit->Data = IO::fread($path);
+
+ return $execution_unit;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getFileExtension(): string
+ {
+ return '.lua';
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/NccExtension/ConstantCompiler.php b/src/ncc/Classes/NccExtension/ConstantCompiler.php
index 5ab4597..5540632 100644
--- a/src/ncc/Classes/NccExtension/ConstantCompiler.php
+++ b/src/ncc/Classes/NccExtension/ConstantCompiler.php
@@ -1,14 +1,33 @@
ProjectType == ProjectType::Composer)
+ {
+ $project_path = ComposerSourceBuiltin::fromLocal($project_type->ProjectPath);
+ }
+ elseif($project_type->ProjectType == ProjectType::Ncc)
+ {
+ $project_manager = new ProjectManager($project_type->ProjectPath);
+ $project_manager->getProjectConfiguration()->Assembly->Version = $version;
+ $project_path = $project_manager->build();
+ }
+ else
+ {
+ throw new UnsupportedProjectTypeException('The project type \'' . $project_type->ProjectType . '\' is not supported');
+ }
+
+ if($version !== null)
+ {
+ $package = Package::load($project_path);
+ $package->Assembly->Version = Functions::convertToSemVer($version);
+ $package->save($project_path);
+ }
+
+ return $project_path;
+ }
+ catch(Exception $e)
+ {
+ throw new BuildException('Failed to build project', $e);
+ }
+ }
+
+
/**
* Compiles the execution policies of the package
*
@@ -93,7 +161,7 @@
* @throws AccessDeniedException
* @throws FileNotFoundException
* @throws IOException
- * @throws UnsupportedRunnerException
+ * @throws RunnerExecutionException
*/
public static function compileExecutionPolicies(string $path, ProjectConfiguration $configuration): array
{
@@ -108,6 +176,8 @@
/** @var ProjectConfiguration\ExecutionPolicy $policy */
foreach($configuration->ExecutionPolicies as $policy)
{
+ Console::outVerbose(sprintf('Compiling Execution Policy %s', $policy->Name));
+
if($total_items > 5)
{
Console::inlineProgressBar($processed_items, $total_items);
@@ -132,29 +202,29 @@
* @param string $build_configuration
* @return string
* @throws BuildConfigurationNotFoundException
- * @throws BuildException
* @throws IOException
*/
public static function writePackage(string $path, Package $package, ProjectConfiguration $configuration, string $build_configuration=BuildConfigurationValues::DefaultConfiguration): string
{
+ Console::outVerbose(sprintf('Writing package to %s', $path));
+
// Write the package to disk
$FileSystem = new Filesystem();
$BuildConfiguration = $configuration->Build->getBuildConfiguration($build_configuration);
- if($FileSystem->exists($path . $BuildConfiguration->OutputPath))
+ if(!$FileSystem->exists($path . $BuildConfiguration->OutputPath))
{
- try
- {
- $FileSystem->remove($path . $BuildConfiguration->OutputPath);
- }
- catch(\ncc\ThirdParty\Symfony\Filesystem\Exception\IOException $e)
- {
- throw new BuildException('Cannot delete directory \'' . $path . $BuildConfiguration->OutputPath . '\', ' . $e->getMessage(), $e);
- }
+ Console::outDebug(sprintf('creating output directory %s', $path . $BuildConfiguration->OutputPath));
+ $FileSystem->mkdir($path . $BuildConfiguration->OutputPath);
}
// Finally write the package to the disk
$FileSystem->mkdir($path . $BuildConfiguration->OutputPath);
$output_file = $path . $BuildConfiguration->OutputPath . DIRECTORY_SEPARATOR . $package->Assembly->Package . '.ncc';
+ if($FileSystem->exists($output_file))
+ {
+ Console::outDebug(sprintf('removing existing package %s', $output_file));
+ $FileSystem->remove($output_file);
+ }
$FileSystem->touch($output_file);
try
@@ -169,35 +239,13 @@
return $output_file;
}
- /**
- * Compiles the special formatted constants
- *
- * @param Package $package
- * @param int $timestamp
- * @return array
- */
- public static function compileRuntimeConstants(Package $package, int $timestamp): array
- {
- $compiled_constants = [];
-
- foreach($package->Header->RuntimeConstants as $name => $value)
- {
- $compiled_constants[$name] = self::compileConstants($value, [
- ConstantReferences::Assembly => $package->Assembly,
- ConstantReferences::DateTime => $timestamp,
- ConstantReferences::Build => null
- ]);
- }
-
- return $compiled_constants;
- }
-
/**
* Compiles the constants in the package object
*
* @param Package $package
* @param array $refs
* @return void
+ * @noinspection PhpParameterByRefIsNotUsedAsReferenceInspection
*/
public static function compilePackageConstants(Package &$package, array $refs): void
{
@@ -206,6 +254,7 @@
$assembly = [];
foreach($package->Assembly->toArray() as $key => $value)
{
+ Console::outDebug(sprintf('compiling consts Assembly.%s (%s)', $key, implode(', ', array_keys($refs))));
$assembly[$key] = self::compileConstants($value, $refs);
}
$package->Assembly = Assembly::fromArray($assembly);
@@ -217,11 +266,44 @@
$units = [];
foreach($package->ExecutionUnits as $executionUnit)
{
+ Console::outDebug(sprintf('compiling execution unit consts %s (%s)', $executionUnit->ExecutionPolicy->Name, implode(', ', array_keys($refs))));
$units[] = self::compileExecutionUnitConstants($executionUnit, $refs);
}
$package->ExecutionUnits = $units;
unset($units);
}
+
+ $compiled_constants = [];
+ foreach($package->Header->RuntimeConstants as $name => $value)
+ {
+ Console::outDebug(sprintf('compiling runtime const %s (%s)', $name, implode(', ', array_keys($refs))));
+ $compiled_constants[$name] = self::compileConstants($value, $refs);
+ }
+
+ $options = [];
+ foreach($package->Header->Options as $name => $value)
+ {
+ if(is_array($value))
+ {
+ $options[$name] = [];
+ foreach($value as $key => $val)
+ {
+ if(!is_string($val))
+ continue;
+
+ Console::outDebug(sprintf('compiling option %s.%s (%s)', $name, $key, implode(', ', array_keys($refs))));
+ $options[$name][$key] = self::compileConstants($val, $refs);
+ }
+ }
+ else
+ {
+ Console::outDebug(sprintf('compiling option %s (%s)', $name, implode(', ', array_keys($refs))));
+ $options[$name] = self::compileConstants((string)$value, $refs);
+ }
+ }
+
+ $package->Header->Options = $options;
+ $package->Header->RuntimeConstants = $compiled_constants;
}
/**
@@ -303,6 +385,9 @@
if(isset($refs[ConstantReferences::Install]))
$value = ConstantCompiler::compileInstallConstants($value, $refs[ConstantReferences::Install]);
+ if(isset($refs[ConstantReferences::Runtime]))
+ $value = ConstantCompiler::compileRuntimeConstants($value);
+
return $value;
}
}
\ No newline at end of file
diff --git a/src/ncc/Classes/NccExtension/Runner.php b/src/ncc/Classes/NccExtension/Runner.php
index 2133137..5a71e81 100644
--- a/src/ncc/Classes/NccExtension/Runner.php
+++ b/src/ncc/Classes/NccExtension/Runner.php
@@ -1,20 +1,35 @@
addUnit($package, $version, $unit, true);
$ExecutionPointerManager->executeUnit($package, $version, $unit->ExecutionPolicy->Name);
- $ExecutionPointerManager->cleanTemporaryUnits();;
+ $ExecutionPointerManager->cleanTemporaryUnits();
}
}
\ No newline at end of file
diff --git a/src/ncc/Classes/PerlExtension/PerlRunner.php b/src/ncc/Classes/PerlExtension/PerlRunner.php
new file mode 100644
index 0000000..46ee10d
--- /dev/null
+++ b/src/ncc/Classes/PerlExtension/PerlRunner.php
@@ -0,0 +1,56 @@
+Execute->Target = null;
+ if(!file_exists($path) && !is_file($path))
+ throw new FileNotFoundException($path);
+ $execution_unit->ExecutionPolicy = $policy;
+ $execution_unit->Data = IO::fread($path);
+
+ return $execution_unit;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getFileExtension(): string
+ {
+ return '.pl';
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/PhpExtension/PhpCompiler.php b/src/ncc/Classes/PhpExtension/PhpCompiler.php
index bb29ce9..bc5142b 100644
--- a/src/ncc/Classes/PhpExtension/PhpCompiler.php
+++ b/src/ncc/Classes/PhpExtension/PhpCompiler.php
@@ -1,4 +1,24 @@
package->Dependencies = $this->project->Build->Dependencies;
$this->package->MainExecutionPolicy = $this->project->Build->Main;
+ // Add the option to create a symbolic link to the package
+ if(isset($this->project->Project->Options['create_symlink']) && $this->project->Project->Options['create_symlink'] === True)
+ $this->package->Header->Options['create_symlink'] = true;
+
// Add both the defined constants from the build configuration and the global constants.
// Global constants are overridden
$this->package->Header->RuntimeConstants = [];
$this->package->Header->RuntimeConstants = array_merge(
- ($selected_build_configuration?->DefineConstants ?? []),
+ ($selected_build_configuration->DefineConstants ?? []),
($this->project->Build->DefineConstants ?? []),
($this->package->Header->RuntimeConstants ?? [])
);
@@ -105,6 +128,11 @@
$this->package->Header->CompilerVersion = NCC_VERSION_NUMBER;
$this->package->Header->Options = $this->project->Project->Options;
+ if($this->project->Project->UpdateSource !== null)
+ {
+ $this->package->Header->UpdateSource = $this->project->Project->UpdateSource;
+ }
+
Console::outDebug('scanning project files');
Console::outDebug('theseer\DirectoryScanner - Copyright (c) 2009-2014 Arne Blankerts All rights reserved.');
@@ -128,67 +156,75 @@
// TODO: Re-implement the scanning process outside the compiler, as this is will be redundant
// Scan for components first.
- Console::outVerbose('Scanning for components... ');
- /** @var SplFileInfo $item */
- /** @noinspection PhpRedundantOptionalArgumentInspection */
- foreach($DirectoryScanner($source_path, True) as $item)
+
+ if(file_exists($source_path))
{
- // Ignore directories, they're not important. :-)
- if(is_dir($item->getPathName()))
- continue;
+ Console::outVerbose('Scanning for components... ');
+ /** @var SplFileInfo $item */
+ /** @noinspection PhpRedundantOptionalArgumentInspection */
+ foreach($DirectoryScanner($source_path, True) as $item)
+ {
+ // Ignore directories, they're not important. :-)
+ if(is_dir($item->getPathName()))
+ continue;
- $Component = new Package\Component();
- $Component->Name = Functions::removeBasename($item->getPathname(), $this->path);
- $this->package->Components[] = $Component;
+ $Component = new Package\Component();
+ $Component->Name = Functions::removeBasename($item->getPathname(), $this->path);
+ $this->package->Components[] = $Component;
- Console::outVerbose(sprintf('Found component %s', $Component->Name));
- }
+ Console::outVerbose(sprintf('Found component %s', $Component->Name));
+ }
- if(count($this->package->Components) > 0)
- {
- Console::outVerbose(count($this->package->Components) . ' component(s) found');
+ if(count($this->package->Components) > 0)
+ {
+ Console::outVerbose(count($this->package->Components) . ' component(s) found');
+ }
+ else
+ {
+ Console::outVerbose('No components found');
+ }
+
+ // Clear previous excludes and includes
+ $DirectoryScanner->setExcludes();
+ $DirectoryScanner->setIncludes();
+
+ // Ignore component files
+ if($selected_build_configuration->ExcludeFiles !== null && count($selected_build_configuration->ExcludeFiles) > 0)
+ {
+ $DirectoryScanner->setExcludes(array_merge($selected_build_configuration->ExcludeFiles, ComponentFileExtensions::Php));
+ }
+ else
+ {
+ $DirectoryScanner->setExcludes(ComponentFileExtensions::Php);
+ }
+
+ Console::outVerbose('Scanning for resources... ');
+ /** @var SplFileInfo $item */
+ foreach($DirectoryScanner($source_path) as $item)
+ {
+ // Ignore directories, they're not important. :-)
+ if(is_dir($item->getPathName()))
+ continue;
+
+ $Resource = new Package\Resource();
+ $Resource->Name = Functions::removeBasename($item->getPathname(), $this->path);
+ $this->package->Resources[] = $Resource;
+
+ Console::outVerbose(sprintf('found resource %s', $Resource->Name));
+ }
+
+ if(count($this->package->Resources) > 0)
+ {
+ Console::outVerbose(count($this->package->Resources) . ' resources(s) found');
+ }
+ else
+ {
+ Console::outVerbose('No resources found');
+ }
}
else
{
- Console::outVerbose('No components found');
- }
-
- // Clear previous excludes and includes
- $DirectoryScanner->setExcludes([]);
- $DirectoryScanner->setIncludes([]);
-
- // Ignore component files
- if($selected_build_configuration->ExcludeFiles !== null && count($selected_build_configuration->ExcludeFiles) > 0)
- {
- $DirectoryScanner->setExcludes(array_merge($selected_build_configuration->ExcludeFiles, ComponentFileExtensions::Php));
- }
- else
- {
- $DirectoryScanner->setExcludes(ComponentFileExtensions::Php);
- }
-
- Console::outVerbose('Scanning for resources... ');
- /** @var SplFileInfo $item */
- foreach($DirectoryScanner($source_path) as $item)
- {
- // Ignore directories, they're not important. :-)
- if(is_dir($item->getPathName()))
- continue;
-
- $Resource = new Package\Resource();
- $Resource->Name = Functions::removeBasename($item->getPathname(), $this->path);
- $this->package->Resources[] = $Resource;
-
- Console::outVerbose(sprintf('found resource %s', $Resource->Name));
- }
-
- if(count($this->package->Resources) > 0)
- {
- Console::outVerbose(count($this->package->Resources) . ' resources(s) found');
- }
- else
- {
- Console::outVerbose('No resources found');
+ Console::outWarning('Source path does not exist, skipping resource and component scanning');
}
$selected_dependencies = [];
@@ -206,7 +242,6 @@
$lib_path = $selected_build_configuration->OutputPath . DIRECTORY_SEPARATOR . 'libs';
if($filesystem->exists($lib_path))
$filesystem->remove($lib_path);
- $filesystem->mkdir($lib_path);
Console::outVerbose('Scanning for dependencies... ');
foreach($selected_dependencies as $dependency)
@@ -222,6 +257,10 @@
$package = $package_lock_manager->getPackageLock()->getPackage($dependency->Name);
$version = $package->getVersion($dependency->Version);
Console::outDebug(sprintf('copying shadow package %s=%s to %s', $dependency->Name, $dependency->Version, $out_path));
+
+ if(!$filesystem->exists($lib_path))
+ $filesystem->mkdir($lib_path);
+
$filesystem->copy($version->Location, $out_path);
$dependency->Source = 'libs' . DIRECTORY_SEPARATOR . sprintf('%s=%s.lib', $dependency->Name, $dependency->Version);
@@ -242,7 +281,7 @@
break;
}
- $this->package->Dependencies[] = $dependency;
+ $this->package->addDependency($dependency);
}
if(count($this->package->Dependencies) > 0)
@@ -265,7 +304,6 @@
* @throws BuildException
* @throws FileNotFoundException
* @throws IOException
- * @throws UnsupportedRunnerException
*/
public function build(): ?Package
{
@@ -404,11 +442,11 @@
* @throws AccessDeniedException
* @throws FileNotFoundException
* @throws IOException
- * @throws UnsupportedRunnerException
+ * @throws RunnerExecutionException
*/
public function compileExecutionPolicies(): void
{
- PackageCompiler::compileExecutionPolicies($this->path, $this->project);
+ $this->package->ExecutionUnits = PackageCompiler::compileExecutionPolicies($this->path, $this->project);
}
/**
diff --git a/src/ncc/Classes/PhpExtension/PhpInstaller.php b/src/ncc/Classes/PhpExtension/PhpInstaller.php
index 611f21e..323df19 100644
--- a/src/ncc/Classes/PhpExtension/PhpInstaller.php
+++ b/src/ncc/Classes/PhpExtension/PhpInstaller.php
@@ -1,4 +1,24 @@
package->Header->Options !== null && isset($this->package->Header->Options['static_files']))
- {
- $static_files = $this->package->Header->Options['static_files'];
- $static_files_path = $installationPaths->getBinPath() . DIRECTORY_SEPARATOR . 'static_autoload.bin';
-
- foreach($static_files as $file)
- {
- if(!file_exists($file))
- throw new InstallationException(sprintf('Static file %s does not exist', $file));
- }
-
- $static_files_exists = true;
- IO::fwrite($static_files_path, ZiProto::encode($static_files));
- }
-
$autoload_path = $installationPaths->getBinPath() . DIRECTORY_SEPARATOR . 'autoload.php';
- $autoload_src = $this->generateAutoload($installationPaths->getSourcePath(), $autoload_path, $static_files_exists);
+ $autoload_src = $this->generateAutoload($installationPaths->getSourcePath(), $autoload_path);
IO::fwrite($autoload_path, $autoload_src);
}
@@ -286,15 +288,13 @@
*
* @param string $src
* @param string $output
- * @param bool $ignore_units
* @return string
* @throws AccessDeniedException
* @throws CollectorException
* @throws FileNotFoundException
* @throws IOException
- * @throws NoUnitsFoundException
*/
- private function generateAutoload(string $src, string $output, bool $ignore_units=false): string
+ private function generateAutoload(string $src, string $output): string
{
// Construct configuration
$configuration = new Config([$src]);
@@ -315,10 +315,6 @@
$result = self::runCollector($factory, $configuration);
// Exception raises when there are no files in the project that can be processed by the autoloader
- if(!$result->hasUnits() && !$ignore_units)
- {
- throw new NoUnitsFoundException('No units were found in the project');
- }
$template = IO::fread($configuration->getTemplate());
diff --git a/src/ncc/Classes/PhpExtension/PhpRunner.php b/src/ncc/Classes/PhpExtension/PhpRunner.php
index 100a807..89f9898 100644
--- a/src/ncc/Classes/PhpExtension/PhpRunner.php
+++ b/src/ncc/Classes/PhpExtension/PhpRunner.php
@@ -1,18 +1,33 @@
Execute->Target = null;
$execution_unit->ExecutionPolicy = $policy;
- $execution_unit->Data = Base64::encode(IO::fread($target_file));
+ $execution_unit->Data = IO::fread($path);
return $execution_unit;
}
@@ -47,21 +61,4 @@
{
return '.php';
}
-
- /**
- * @param ExecutionPointer $pointer
- * @return Process
- * @throws RunnerExecutionException
- */
- public static function prepareProcess(ExecutionPointer $pointer): Process
- {
- $php_bin = new ExecutableFinder();
- $php_bin = $php_bin->find('php');
- if($php_bin == null)
- throw new RunnerExecutionException('Cannot locate PHP executable');
-
- if($pointer->ExecutionPolicy->Execute->Options !== null && count($pointer->ExecutionPolicy->Execute->Options) > 0)
- return new Process(array_merge([$php_bin, $pointer->FilePointer], $pointer->ExecutionPolicy->Execute->Options));
- return new Process([$php_bin, $pointer->FilePointer]);
- }
}
\ No newline at end of file
diff --git a/src/ncc/Classes/PhpExtension/PhpRuntime.php b/src/ncc/Classes/PhpExtension/PhpRuntime.php
new file mode 100644
index 0000000..97d3df1
--- /dev/null
+++ b/src/ncc/Classes/PhpExtension/PhpRuntime.php
@@ -0,0 +1,124 @@
+getInstallPaths()->getBinPath() . DIRECTORY_SEPARATOR . 'autoload.php';
+ $static_files = $versionEntry->getInstallPaths()->getBinPath() . DIRECTORY_SEPARATOR . 'static_autoload.bin';
+ $constants_path = $versionEntry->getInstallPaths()->getDataPath() . DIRECTORY_SEPARATOR . 'const';
+ $assembly_path = $versionEntry->getInstallPaths()->getDataPath() . DIRECTORY_SEPARATOR . 'assembly';
+
+ if(!file_exists($assembly_path))
+ throw new ImportException('Cannot locate assembly file \'' . $assembly_path . '\'');
+
+ try
+ {
+ $assembly_content = ZiProto::decode(IO::fread($assembly_path));
+ $assembly = Assembly::fromArray($assembly_content);
+ }
+ catch(Exception $e)
+ {
+ throw new ImportException('Failed to load assembly file \'' . $assembly_path . '\': ' . $e->getMessage());
+ }
+
+ if(file_exists($constants_path))
+ {
+ try
+ {
+ $constants = ZiProto::decode(IO::fread($constants_path));
+ }
+ catch(Exception $e)
+ {
+ throw new ImportException('Failed to load constants file \'' . $constants_path . '\': ' . $e->getMessage());
+ }
+
+ foreach($constants as $name => $value)
+ {
+ $value = ConstantCompiler::compileRuntimeConstants($value);
+
+ try
+ {
+ Constants::register($assembly->Package, $name, $value, true);
+ }
+ catch (ConstantReadonlyException $e)
+ {
+ trigger_error('Constant \'' . $name . '\' is readonly (' . $assembly->Package . ')', E_USER_WARNING);
+ }
+ catch (InvalidConstantNameException $e)
+ {
+ throw new ImportException('Invalid constant name \'' . $name . '\' (' . $assembly->Package . ')', $e);
+ }
+ }
+ }
+
+ if(file_exists($autoload_path) && !in_array(RuntimeImportOptions::ImportAutoloader, $options))
+ {
+ require_once($autoload_path);
+ }
+
+ if(file_exists($static_files) && !in_array(RuntimeImportOptions::ImportStaticFiles, $options))
+ {
+ try
+ {
+ $static_files = ZiProto::decode(IO::fread($static_files));
+ foreach($static_files as $file)
+ require_once($file);
+ }
+ catch(Exception $e)
+ {
+ throw new ImportException('Failed to load static files: ' . $e->getMessage(), $e);
+ }
+
+ }
+
+ if(!file_exists($autoload_path) && !file_exists($static_files))
+ return false;
+
+ return true;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/PythonExtension/Python2Runner.php b/src/ncc/Classes/PythonExtension/Python2Runner.php
new file mode 100644
index 0000000..b411a06
--- /dev/null
+++ b/src/ncc/Classes/PythonExtension/Python2Runner.php
@@ -0,0 +1,56 @@
+Execute->Target = null;
+ $execution_unit->ExecutionPolicy = $policy;
+ $execution_unit->Data = IO::fread($path);
+
+ return $execution_unit;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getFileExtension(): string
+ {
+ return '.py';
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/PythonExtension/Python3Runner.php b/src/ncc/Classes/PythonExtension/Python3Runner.php
new file mode 100644
index 0000000..bc6ff39
--- /dev/null
+++ b/src/ncc/Classes/PythonExtension/Python3Runner.php
@@ -0,0 +1,56 @@
+Execute->Target = null;
+ $execution_unit->ExecutionPolicy = $policy;
+ $execution_unit->Data = IO::fread($path);
+
+ return $execution_unit;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getFileExtension(): string
+ {
+ return '.py';
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Classes/PythonExtension/PythonRunner.php b/src/ncc/Classes/PythonExtension/PythonRunner.php
new file mode 100644
index 0000000..b3d5168
--- /dev/null
+++ b/src/ncc/Classes/PythonExtension/PythonRunner.php
@@ -0,0 +1,56 @@
+Execute->Target = null;
+ $execution_unit->ExecutionPolicy = $policy;
+ $execution_unit->Data = IO::fread($path);
+
+ return $execution_unit;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public static function getFileExtension(): string
+ {
+ return '.py';
+ }
+}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/AccessDeniedException.php b/src/ncc/Exceptions/AccessDeniedException.php
index 96bb695..f1af713 100644
--- a/src/ncc/Exceptions/AccessDeniedException.php
+++ b/src/ncc/Exceptions/AccessDeniedException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/BuildException.php b/src/ncc/Exceptions/BuildException.php
index 7fbb3d8..4394f96 100644
--- a/src/ncc/Exceptions/BuildException.php
+++ b/src/ncc/Exceptions/BuildException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/ComponentChecksumException.php b/src/ncc/Exceptions/ComponentChecksumException.php
index 68bb520..9261aa7 100644
--- a/src/ncc/Exceptions/ComponentChecksumException.php
+++ b/src/ncc/Exceptions/ComponentChecksumException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/ComponentDecodeException.php b/src/ncc/Exceptions/ComponentDecodeException.php
index 593255f..6d08524 100644
--- a/src/ncc/Exceptions/ComponentDecodeException.php
+++ b/src/ncc/Exceptions/ComponentDecodeException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/ComponentVersionNotFoundException.php b/src/ncc/Exceptions/ComponentVersionNotFoundException.php
index ae57404..8edbc99 100644
--- a/src/ncc/Exceptions/ComponentVersionNotFoundException.php
+++ b/src/ncc/Exceptions/ComponentVersionNotFoundException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/ComposerException.php b/src/ncc/Exceptions/ComposerException.php
index 9e37388..52bc4b7 100644
--- a/src/ncc/Exceptions/ComposerException.php
+++ b/src/ncc/Exceptions/ComposerException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/ComposerNotAvailableException.php b/src/ncc/Exceptions/ComposerNotAvailableException.php
index 2fa1d4b..2f99a51 100644
--- a/src/ncc/Exceptions/ComposerNotAvailableException.php
+++ b/src/ncc/Exceptions/ComposerNotAvailableException.php
@@ -1,20 +1,32 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/ConstantReadonlyException.php b/src/ncc/Exceptions/ConstantReadonlyException.php
index 5930827..9ccbeef 100644
--- a/src/ncc/Exceptions/ConstantReadonlyException.php
+++ b/src/ncc/Exceptions/ConstantReadonlyException.php
@@ -1,6 +1,26 @@
code = ExceptionCodes::DirectoryNotFoundException;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/ExecutionUnitNotFoundException.php b/src/ncc/Exceptions/ExecutionUnitNotFoundException.php
deleted file mode 100644
index 228f512..0000000
--- a/src/ncc/Exceptions/ExecutionUnitNotFoundException.php
+++ /dev/null
@@ -1,8 +0,0 @@
-code = ExceptionCodes::FileNotFoundException;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/GitCheckoutException.php b/src/ncc/Exceptions/GitCheckoutException.php
new file mode 100644
index 0000000..aaad3c4
--- /dev/null
+++ b/src/ncc/Exceptions/GitCheckoutException.php
@@ -0,0 +1,39 @@
+message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/ImportException.php b/src/ncc/Exceptions/ImportException.php
new file mode 100644
index 0000000..3e99de3
--- /dev/null
+++ b/src/ncc/Exceptions/ImportException.php
@@ -0,0 +1,39 @@
+message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/InternalComposerNotAvailableException.php b/src/ncc/Exceptions/InternalComposerNotAvailableException.php
index 718114b..1d4808c 100644
--- a/src/ncc/Exceptions/InternalComposerNotAvailableException.php
+++ b/src/ncc/Exceptions/InternalComposerNotAvailableException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/InvalidBuildConfigurationException.php b/src/ncc/Exceptions/InvalidBuildConfigurationException.php
new file mode 100644
index 0000000..80cb3cc
--- /dev/null
+++ b/src/ncc/Exceptions/InvalidBuildConfigurationException.php
@@ -0,0 +1,39 @@
+message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/InvalidCredentialsEntryException.php b/src/ncc/Exceptions/InvalidCredentialsEntryException.php
index 22bf01c..5b0d6db 100644
--- a/src/ncc/Exceptions/InvalidCredentialsEntryException.php
+++ b/src/ncc/Exceptions/InvalidCredentialsEntryException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/InvalidPackageNameException.php b/src/ncc/Exceptions/InvalidPackageNameException.php
index da9ebaa..fbaeb18 100644
--- a/src/ncc/Exceptions/InvalidPackageNameException.php
+++ b/src/ncc/Exceptions/InvalidPackageNameException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/InvalidProjectConfigurationException.php b/src/ncc/Exceptions/InvalidProjectConfigurationException.php
index 4923365..a8ec2bf 100644
--- a/src/ncc/Exceptions/InvalidProjectConfigurationException.php
+++ b/src/ncc/Exceptions/InvalidProjectConfigurationException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->previous = $previous;
$this->property = $property;
}
+
+ /**
+ * @return string|null
+ */
+ public function getProperty(): ?string
+ {
+ return $this->property;
+ }
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/InvalidProjectNameException.php b/src/ncc/Exceptions/InvalidProjectNameException.php
index 8778bce..bf3adff 100644
--- a/src/ncc/Exceptions/InvalidProjectNameException.php
+++ b/src/ncc/Exceptions/InvalidProjectNameException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/InvalidVersionNumberException.php b/src/ncc/Exceptions/InvalidVersionNumberException.php
index 708c16a..23b1e8f 100644
--- a/src/ncc/Exceptions/InvalidVersionNumberException.php
+++ b/src/ncc/Exceptions/InvalidVersionNumberException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/NoAvailableUnitsException.php b/src/ncc/Exceptions/NoAvailableUnitsException.php
index 56d61ac..eb7f325 100644
--- a/src/ncc/Exceptions/NoAvailableUnitsException.php
+++ b/src/ncc/Exceptions/NoAvailableUnitsException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/NoUnitsFoundException.php b/src/ncc/Exceptions/NoUnitsFoundException.php
index a59e3da..48423b7 100644
--- a/src/ncc/Exceptions/NoUnitsFoundException.php
+++ b/src/ncc/Exceptions/NoUnitsFoundException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/NotImplementedException.php b/src/ncc/Exceptions/NotImplementedException.php
index e53d390..55de1d1 100644
--- a/src/ncc/Exceptions/NotImplementedException.php
+++ b/src/ncc/Exceptions/NotImplementedException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/NotSupportedException.php b/src/ncc/Exceptions/NotSupportedException.php
new file mode 100644
index 0000000..a629e67
--- /dev/null
+++ b/src/ncc/Exceptions/NotSupportedException.php
@@ -0,0 +1,39 @@
+message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/PackageFetchException.php b/src/ncc/Exceptions/PackageFetchException.php
new file mode 100644
index 0000000..4dbbbed
--- /dev/null
+++ b/src/ncc/Exceptions/PackageFetchException.php
@@ -0,0 +1,39 @@
+message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/PackageNotFoundException.php b/src/ncc/Exceptions/PackageNotFoundException.php
index 88bd370..070d601 100644
--- a/src/ncc/Exceptions/PackageNotFoundException.php
+++ b/src/ncc/Exceptions/PackageNotFoundException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/PackageParsingException.php b/src/ncc/Exceptions/PackageParsingException.php
index 70c1be7..7106d1a 100644
--- a/src/ncc/Exceptions/PackageParsingException.php
+++ b/src/ncc/Exceptions/PackageParsingException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/PackagePreparationFailedException.php b/src/ncc/Exceptions/PackagePreparationFailedException.php
index 5a45e3e..223795f 100644
--- a/src/ncc/Exceptions/PackagePreparationFailedException.php
+++ b/src/ncc/Exceptions/PackagePreparationFailedException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/ProjectAlreadyExistsException.php b/src/ncc/Exceptions/ProjectAlreadyExistsException.php
index b1d5f6c..3354002 100644
--- a/src/ncc/Exceptions/ProjectAlreadyExistsException.php
+++ b/src/ncc/Exceptions/ProjectAlreadyExistsException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->code = $code;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/RunnerExecutionException.php b/src/ncc/Exceptions/RunnerExecutionException.php
index e568dd1..244a0b5 100644
--- a/src/ncc/Exceptions/RunnerExecutionException.php
+++ b/src/ncc/Exceptions/RunnerExecutionException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/RuntimeException.php b/src/ncc/Exceptions/RuntimeException.php
index a3ddb60..feb0953 100644
--- a/src/ncc/Exceptions/RuntimeException.php
+++ b/src/ncc/Exceptions/RuntimeException.php
@@ -1,6 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/UnsupportedArchiveException.php b/src/ncc/Exceptions/UnsupportedArchiveException.php
new file mode 100644
index 0000000..4a7bf33
--- /dev/null
+++ b/src/ncc/Exceptions/UnsupportedArchiveException.php
@@ -0,0 +1,39 @@
+message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/UnsupportedComponentTypeException.php b/src/ncc/Exceptions/UnsupportedComponentTypeException.php
index ecd4c79..5352a41 100644
--- a/src/ncc/Exceptions/UnsupportedComponentTypeException.php
+++ b/src/ncc/Exceptions/UnsupportedComponentTypeException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/UnsupportedExtensionVersionException.php b/src/ncc/Exceptions/UnsupportedExtensionVersionException.php
index f24b429..4084580 100644
--- a/src/ncc/Exceptions/UnsupportedExtensionVersionException.php
+++ b/src/ncc/Exceptions/UnsupportedExtensionVersionException.php
@@ -1,13 +1,39 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/UnsupportedProjectTypeException.php b/src/ncc/Exceptions/UnsupportedProjectTypeException.php
new file mode 100644
index 0000000..6cb7904
--- /dev/null
+++ b/src/ncc/Exceptions/UnsupportedProjectTypeException.php
@@ -0,0 +1,40 @@
+message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Exceptions/VersionNotFoundException.php b/src/ncc/Exceptions/VersionNotFoundException.php
index b7b4acc..c1348ea 100644
--- a/src/ncc/Exceptions/VersionNotFoundException.php
+++ b/src/ncc/Exceptions/VersionNotFoundException.php
@@ -1,8 +1,26 @@
message = $message;
- $this->previous = $previous;
}
}
\ No newline at end of file
diff --git a/src/ncc/Extensions/ZiProto/Abstracts/Options.php b/src/ncc/Extensions/ZiProto/Abstracts/Options.php
index 58534f0..40c0cdb 100644
--- a/src/ncc/Extensions/ZiProto/Abstracts/Options.php
+++ b/src/ncc/Extensions/ZiProto/Abstracts/Options.php
@@ -1,6 +1,26 @@
decodeStrData($this->decodeUint8());
+ case 0xda:
case 0xc5: return $this->decodeStrData($this->decodeUint16());
+ case 0xdb:
case 0xc6: return $this->decodeStrData($this->decodeUint32());
// float
@@ -210,9 +234,6 @@
case 0xd3: return $this->decodeInt64();
// str
- case 0xd9: return $this->decodeStrData($this->decodeUint8());
- case 0xda: return $this->decodeStrData($this->decodeUint16());
- case 0xdb: return $this->decodeStrData($this->decodeUint32());
// array
case 0xdc: return $this->decodeArrayData($this->decodeUint16());
@@ -351,7 +372,7 @@
}
/**
- * @return bool|string
+ * @return string
*/
public function decodeStr()
{
@@ -387,7 +408,7 @@
}
/**
- * @return bool|string
+ * @return string
*/
public function decodeBin()
{
diff --git a/src/ncc/Extensions/ZiProto/DecodingOptions.php b/src/ncc/Extensions/ZiProto/DecodingOptions.php
index d2fc7f8..347b2a1 100644
--- a/src/ncc/Extensions/ZiProto/DecodingOptions.php
+++ b/src/ncc/Extensions/ZiProto/DecodingOptions.php
@@ -1,5 +1,26 @@
bigIntMode = self::getSingleOption('bigint', $bitmask,
+ $self->bigIntMode = self::getSingleOption($bitmask,
Options::BIGINT_AS_STR |
Options::BIGINT_AS_GMP |
Options::BIGINT_AS_EXCEPTION
@@ -66,12 +87,11 @@
}
/**
- * @param string $name
* @param int $bitmask
* @param int $validBitmask
* @return int
*/
- private static function getSingleOption(string $name, int $bitmask, int $validBitmask) : int
+ private static function getSingleOption(int $bitmask, int $validBitmask) : int
{
$option = $bitmask & $validBitmask;
if ($option === ($option & -$option))
@@ -91,6 +111,6 @@
$validOptions[] = __CLASS__.'::'.$map[$i];
}
- throw InvalidOptionException::outOfRange($name, $validOptions);
+ throw InvalidOptionException::outOfRange('bigint', $validOptions);
}
}
diff --git a/src/ncc/Extensions/ZiProto/EncodingOptions.php b/src/ncc/Extensions/ZiProto/EncodingOptions.php
index 2525641..c713ba4 100644
--- a/src/ncc/Extensions/ZiProto/EncodingOptions.php
+++ b/src/ncc/Extensions/ZiProto/EncodingOptions.php
@@ -1,6 +1,26 @@
= 0)
{
@@ -237,7 +257,7 @@
* @param $float
* @return string
*/
- public function encodeFloat($float)
+ public function encodeFloat($float): string
{
return $this->isForceFloat32
? "\xca". pack('G', $float)
@@ -248,7 +268,7 @@
* @param $str
* @return string
*/
- public function encodeStr($str)
+ public function encodeStr($str): string
{
$length = strlen($str);
@@ -274,7 +294,7 @@
* @param $str
* @return string
*/
- public function encodeBin($str)
+ public function encodeBin($str): string
{
$length = strlen($str);
@@ -293,9 +313,9 @@
/**
* @param $array
- * @return false|string
+ * @return string
*/
- public function encodeArray($array)
+ public function encodeArray($array): string
{
$data = $this->encodeArrayHeader(count($array));
@@ -311,7 +331,7 @@
* @param $size
* @return string
*/
- public function encodeArrayHeader($size)
+ public function encodeArrayHeader($size): string
{
if ($size <= 0xf)
{
@@ -328,9 +348,9 @@
/**
* @param $map
- * @return false|string
+ * @return string
*/
- public function encodeMap($map)
+ public function encodeMap($map): string
{
$data = $this->encodeMapHeader(count($map));
@@ -371,7 +391,7 @@
* @param $size
* @return string
*/
- public function encodeMapHeader($size)
+ public function encodeMapHeader($size): string
{
if ($size <= 0xf)
{
@@ -391,7 +411,7 @@
* @param $data
* @return string
*/
- public function encodeExt($type, $data)
+ public function encodeExt($type, $data): string
{
$length = strlen($data);
diff --git a/src/ncc/Extensions/ZiProto/Type/Binary.php b/src/ncc/Extensions/ZiProto/Type/Binary.php
index 2bb9078..bb8b85d 100644
--- a/src/ncc/Extensions/ZiProto/Type/Binary.php
+++ b/src/ncc/Extensions/ZiProto/Type/Binary.php
@@ -1,6 +1,26 @@
Configuration = RuntimeCache::get('ncc.yaml');
if($this->Configuration !== null)
return;
@@ -67,6 +89,8 @@
*/
public function save(): void
{
+ Console::outDebug(sprintf('saving configuration file to %s', PathFinder::getConfigurationFile()));
+
if(Resolver::resolveScope() !== Scopes::System)
throw new AccessDeniedException('Cannot save configuration file, insufficient permissions');
@@ -88,6 +112,9 @@
*/
public function getProperty(string $property)
{
+ Console::outDebug(sprintf('getting property %s', $property));
+
+ Console::outDebug($property);
$current_selection = $this->getConfiguration();
foreach(explode('.', strtolower($property)) as $property)
{
@@ -119,202 +146,20 @@
*/
public function updateProperty(string $property, $value): bool
{
- // composer.options.quiet
- $result = match (strtolower(explode('.', $property)[0]))
- {
- 'ncc' => $this->updateNccProperties($property, $value),
- 'php' => $this->updatePhpProperties($property, $value),
- 'git' => $this->updateGitProperties($property, $value),
- 'runners' => $this->updateRunnerProperties($property, $value),
- 'composer' => $this->updateComposerProperties($property, $value),
- default => false,
- };
+ Console::outDebug(sprintf('updating property %s', $property));
+ $keys = explode('.', $property);
+ $current = &$this->Configuration;
+ foreach ($keys as $k)
+ {
+ if (!array_key_exists($k, $current))
+ {
+ return false;
+ }
+ $current = &$current[$k];
+ }
+ $current = Functions::stringTypeCast($value);
$this->save();
- return $result;
- }
-
- /**
- * Updates NCC configuration properties in the configuration
- *
- * @param string $property
- * @param $value
- * @return bool
- */
- private function updateNccProperties(string $property, $value): bool
- {
- $delete = false;
- if(is_null($value))
- $delete = true;
-
- switch(strtolower($property))
- {
- case 'ncc.cli.no_colors':
- $this->Configuration['ncc']['cli']['no_colors'] = Functions::cbool($value);
- break;
- case 'ncc.cli.basic_ascii':
- $this->Configuration['ncc']['cli']['basic_ascii'] = Functions::cbool($value);
- break;
- case 'ncc.cli.logging':
- $this->Configuration['ncc']['cli']['logging'] = ($delete ? LogLevel::Info : (string)$value);
- break;
-
- default:
- return false;
- }
-
- return true;
- }
-
- /**
- * Updates PHP properties in the configuraiton
- *
- * @param string $property
- * @param $value
- * @return bool
- */
- private function updatePhpProperties(string $property, $value): bool
- {
- $delete = false;
- if(is_null($value))
- $delete = true;
-
- switch(strtolower($property))
- {
- case 'php.executable_path':
- $this->Configuration['php']['executable_path'] = ($delete ? null : (string)$value);
- break;
- case 'php.runtime.initialize_on_require':
- $this->Configuration['php']['runtime']['initialize_on_require'] = Functions::cbool($value);
- break;
- case 'php.runtime.handle_exceptions':
- $this->Configuration['php']['runtime']['handle_exceptions'] = Functions::cbool($value);
- break;
-
- default:
- return false;
- }
-
- return true;
- }
-
- /**
- * Updated git properties
- *
- * @param string $property
- * @param $value
- * @return bool
- */
- private function updateGitProperties(string $property, $value): bool
- {
- $delete = false;
- if(is_null($value))
- $delete = true;
-
- switch(strtolower($property))
- {
- case 'git.enabled':
- $this->Configuration['git']['enabled'] = Functions::cbool($value);
- break;
- case 'git.executable_path':
- $this->Configuration['git']['executable_path'] = ($delete? null : (string)$value);
- break;
- default:
- return false;
- }
-
- return true;
- }
-
- /**
- * Updaters runner properties
- *
- * @param string $property
- * @param $value
- * @return bool
- */
- private function updateRunnerProperties(string $property, $value): bool
- {
- $delete = false;
- if(is_null($value))
- $delete = true;
-
- switch(strtolower($property))
- {
- case 'runners.php':
- $this->Configuration['runners']['php'] = ($delete? null : (string)$value);
- break;
- case 'runners.bash':
- $this->Configuration['runners']['bash'] = ($delete? null : (string)$value);
- break;
- case 'runners.sh':
- $this->Configuration['runners']['sh'] = ($delete? null : (string)$value);
- break;
- case 'runners.python':
- $this->Configuration['runners']['python'] = ($delete? null : (string)$value);
- break;
- case 'runners.python3':
- $this->Configuration['runners']['python3'] = ($delete? null : (string)$value);
- break;
- case 'runners.python2':
- $this->Configuration['runners']['python2'] = ($delete? null : (string)$value);
- break;
-
- default:
- return false;
- }
-
- return true;
- }
-
- /**
- * Updates a composer property value
- *
- * @param string $property
- * @param $value
- * @return bool
- */
- private function updateComposerProperties(string $property, $value): bool
- {
- $delete = false;
- if(is_null($value))
- $delete = true;
-
- switch(strtolower($property))
- {
- case 'composer.enabled':
- $this->Configuration['composer']['enabled'] = Functions::cbool($value);
- break;
- case 'composer.enable_internal_composer':
- $this->Configuration['composer']['enable_internal_composer'] = Functions::cbool($value);
- break;
- case 'composer.executable_path':
- $this->Configuration['composer']['executable_path'] = ($delete? null : (string)$value);
- break;
- case 'composer.options.quiet':
- $this->Configuration['composer']['options']['quiet'] = Functions::cbool($value);
- break;
- case 'composer.options.no_ansi':
- $this->Configuration['composer']['options']['no_ansi'] = Functions::cbool($value);
- break;
- case 'composer.options.no_interaction':
- $this->Configuration['composer']['options']['no_interaction'] = Functions::cbool($value);
- break;
- case 'composer.options.profile':
- $this->Configuration['composer']['options']['profile'] = Functions::cbool($value);
- break;
- case 'composer.options.no_scripts':
- $this->Configuration['composer']['options']['no_scripts'] = Functions::cbool($value);
- break;
- case 'composer.options.no_cache':
- $this->Configuration['composer']['options']['no_cache'] = Functions::cbool($value);
- break;
- case 'composer.options.logging':
- $this->Configuration['composer']['options']['logging'] = ((int)$value > 0 ? (int)$value : 1);
- break;
- default:
- return false;
- }
return true;
}
diff --git a/src/ncc/Managers/CredentialManager.php b/src/ncc/Managers/CredentialManager.php
index 4a9b2c2..679b14d 100644
--- a/src/ncc/Managers/CredentialManager.php
+++ b/src/ncc/Managers/CredentialManager.php
@@ -1,4 +1,24 @@
CredentialsPath = PathFinder::getDataPath(Scopes::System) . DIRECTORY_SEPARATOR . 'credentials.store';
- }
+ $this->Vault = null;
- /**
- * Determines if CredentialManager has correct access to manage credentials on the system
- *
- * @return bool
- */
- public function checkAccess(): bool
- {
- $ResolvedScope = Resolver::resolveScope();
-
- if($ResolvedScope !== Scopes::System)
+ try
{
- return False;
+ $this->loadVault();
+ }
+ catch(Exception $e)
+ {
+ unset($e);
}
- return True;
+ if($this->Vault == null)
+ $this->Vault = new Vault();
}
/**
@@ -59,101 +82,86 @@
*/
public function constructStore(): void
{
+ Console::outDebug(sprintf('constructing credentials store at %s', $this->CredentialsPath));
+
// Do not continue the function if the file already exists, if the file is damaged a separate function
// is to be executed to fix the damaged file.
if(file_exists($this->CredentialsPath))
return;
- if(!$this->checkAccess())
- {
+ if(Resolver::resolveScope() !== Scopes::System)
throw new AccessDeniedException('Cannot construct credentials store without system permissions');
- }
$VaultObject = new Vault();
$VaultObject->Version = Versions::CredentialsStoreVersion;
- IO::fwrite($this->CredentialsPath, ZiProto::encode($VaultObject->toArray()), 0600);
+ IO::fwrite($this->CredentialsPath, ZiProto::encode($VaultObject->toArray()), 0744);
}
/**
- * Returns the vault object from the credentials store file.
+ * Loads the vault from the disk
*
- * @return Vault
- * @throws AccessDeniedException
- * @throws IOException
- * @throws RuntimeException
- */
- public function getVault(): Vault
- {
- $this->constructStore();
-
- if(!$this->checkAccess())
- {
- throw new AccessDeniedException('Cannot read credentials store without system permissions');
- }
-
- try
- {
- $Vault = ZiProto::decode(IO::fread($this->CredentialsPath));
- }
- catch(Exception $e)
- {
- // TODO: Implement error-correction for corrupted credentials store.
- throw new RuntimeException($e->getMessage(), $e);
- }
-
- return Vault::fromArray($Vault);
- }
-
- /**
- * Saves the vault object to the credentials store
- *
- * @param Vault $vault
* @return void
* @throws AccessDeniedException
* @throws IOException
+ * @throws RuntimeException
+ * @throws FileNotFoundException
*/
- public function saveVault(Vault $vault): void
+ private function loadVault(): void
{
- if(!$this->checkAccess())
+ Console::outDebug(sprintf('loading credentials store from %s', $this->CredentialsPath));
+
+ if($this->Vault !== null)
+ return;
+
+ if(!file_exists($this->CredentialsPath))
{
- throw new AccessDeniedException('Cannot write to credentials store without system permissions');
+ $this->Vault = new Vault();
+ return;
}
- IO::fwrite($this->CredentialsPath, ZiProto::encode($vault->toArray()), 0600);
+ $VaultArray = ZiProto::decode(IO::fread($this->CredentialsPath));
+ $VaultObject = Vault::fromArray($VaultArray);
+
+ if($VaultObject->Version !== Versions::CredentialsStoreVersion)
+ throw new RuntimeException('Credentials store version mismatch');
+
+ $this->Vault = $VaultObject;
}
/**
- * Registers an entry to the credentials store file
+ * Saves the vault to the disk
*
- * @param Vault\Entry $entry
* @return void
* @throws AccessDeniedException
- * @throws InvalidCredentialsEntryException
- * @throws RuntimeException
* @throws IOException
+ * @noinspection PhpUnused
*/
- public function registerEntry(Vault\Entry $entry): void
+ public function saveVault(): void
{
- if(!preg_match('/^[\w-]+$/', $entry->Alias))
- {
- throw new InvalidCredentialsEntryException('The property \'Alias\' must be alphanumeric (Regex error)');
- }
+ Console::outDebug(sprintf('saving credentials store to %s', $this->CredentialsPath));
- // TODO: Implement more validation checks for the rest of the entry properties.
- // TODO: Implement encryption for entries that require encryption (For securing passwords and data)
+ if(Resolver::resolveScope() !== Scopes::System)
+ throw new AccessDeniedException('Cannot save credentials store without system permissions');
- $Vault = $this->getVault();
- $Vault->Entries[] = $entry;
-
- $this->saveVault($Vault);
+ IO::fwrite($this->CredentialsPath, ZiProto::encode($this->Vault->toArray()), 0744);
}
+
/**
- * @return null
+ * @return string
+ * @noinspection PhpUnused
*/
- public function getCredentialsPath(): ?string
+ public function getCredentialsPath(): string
{
return $this->CredentialsPath;
}
+
+ /**
+ * @return Vault|null
+ */
+ public function getVault(): ?Vault
+ {
+ return $this->Vault;
+ }
}
\ No newline at end of file
diff --git a/src/ncc/Managers/ExecutionPointerManager.php b/src/ncc/Managers/ExecutionPointerManager.php
index c2f2dbb..9bedc3b 100644
--- a/src/ncc/Managers/ExecutionPointerManager.php
+++ b/src/ncc/Managers/ExecutionPointerManager.php
@@ -1,4 +1,24 @@
TemporaryUnits) == 0)
return;
+ Console::outVerbose('Cleaning temporary units...');
+
try
{
foreach($this->TemporaryUnits as $datum)
{
+ Console::outDebug(sprintf('deleting unit %s=%s.%s', $datum['package'], $datum['version'], $datum['name']));
$this->removeUnit($datum['package'], $datum['version'], $datum['name']);
}
}
@@ -100,9 +128,31 @@
*/
private function getPackageId(string $package, string $version): string
{
+ Console::outDebug(sprintf('calculating package id for %s=%s', $package, $version));
return hash('haval128,4', $package . $version);
}
+ /**
+ * Returns the path to the execution pointer file
+ *
+ * @param string $package
+ * @param string $version
+ * @param string $name
+ * @return string
+ * @throws FileNotFoundException
+ */
+ public function getEntryPointPath(string $package, string $version, string $name): string
+ {
+ $package_id = $this->getPackageId($package, $version);
+ $package_bin_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id;
+ $entry_point_path = $package_bin_path . DIRECTORY_SEPARATOR . hash('haval128,4', $name) . '.entrypoint';
+
+ if(!file_exists($entry_point_path))
+ throw new FileNotFoundException('Cannot find entry point for ' . $package . '=' . $version . '.' . $name);
+
+ return $entry_point_path;
+ }
+
/**
* Adds a new Execution Unit to the
*
@@ -114,7 +164,7 @@
* @throws AccessDeniedException
* @throws FileNotFoundException
* @throws IOException
- * @throws UnsupportedRunnerException
+ * @throws RunnerExecutionException
* @noinspection PhpUnused
*/
public function addUnit(string $package, string $version, ExecutionUnit $unit, bool $temporary=false): void
@@ -122,9 +172,17 @@
if(Resolver::resolveScope() !== Scopes::System)
throw new AccessDeniedException('Cannot add new ExecutionUnit \'' . $unit->ExecutionPolicy->Name .'\' for ' . $package . ', insufficient permissions');
+ Console::outVerbose(sprintf('Adding new ExecutionUnit \'%s\' for %s', $unit->ExecutionPolicy->Name, $package));
+
$package_id = $this->getPackageId($package, $version);
$package_config_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id . '.inx';
$package_bin_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id;
+ $entry_point_path = $package_bin_path . DIRECTORY_SEPARATOR . hash('haval128,4', $unit->ExecutionPolicy->Name) . '.entrypoint';
+
+ Console::outDebug(sprintf('package_id=%s', $package_id));
+ Console::outDebug(sprintf('package_config_path=%s', $package_config_path));
+ Console::outDebug(sprintf('package_bin_path=%s', $package_bin_path));
+ Console::outDebug(sprintf('entry_point_path=%s', $entry_point_path));
$filesystem = new Filesystem();
@@ -139,11 +197,20 @@
}
$bin_file = $package_bin_path . DIRECTORY_SEPARATOR . hash('haval128,4', $unit->ExecutionPolicy->Name);
- $bin_file .= match ($unit->ExecutionPolicy->Runner) {
+ $bin_file .= match ($unit->ExecutionPolicy->Runner)
+ {
+ Runners::bash => BashRunner::getFileExtension(),
Runners::php => PhpRunner::getFileExtension(),
- default => throw new UnsupportedRunnerException('The runner \'' . $unit->ExecutionPolicy->Runner . '\' is not supported'),
+ Runners::perl => PerlRunner::getFileExtension(),
+ Runners::python => PythonRunner::getFileExtension(),
+ Runners::python2 => Python2Runner::getFileExtension(),
+ Runners::python3 => Python3Runner::getFileExtension(),
+ Runners::lua => LuaRunner::getFileExtension(),
+ default => throw new RunnerExecutionException('The runner \'' . $unit->ExecutionPolicy->Runner . '\' is not supported'),
};
+ Console::outDebug(sprintf('bin_file=%s', $bin_file));
+
if($filesystem->exists($bin_file) && $temporary)
return;
@@ -157,8 +224,19 @@
$execution_pointers->addUnit($unit, $bin_file);
IO::fwrite($package_config_path, ZiProto::encode($execution_pointers->toArray(true)));
+ $entry_point = sprintf("#!%s\nncc exec --package=\"%s\" --exec-version=\"%s\" --exec-unit=\"%s\" --exec-args \"$@\"",
+ '/bin/bash',
+ $package, $version, $unit->ExecutionPolicy->Name
+ );
+
+ if(file_exists($entry_point_path))
+ $filesystem->remove($entry_point_path);
+ IO::fwrite($entry_point_path, $entry_point);
+ chmod($entry_point_path, 0755);
+
if($temporary)
{
+ Console::outVerbose(sprintf('Adding temporary ExecutionUnit \'%s\' for %s', $unit->ExecutionPolicy->Name, $package));
$this->TemporaryUnits[] = [
'package' => $package,
'version' => $version,
@@ -183,10 +261,16 @@
if(Resolver::resolveScope() !== Scopes::System)
throw new AccessDeniedException('Cannot remove ExecutionUnit \'' . $name .'\' for ' . $package . ', insufficient permissions');
+ Console::outVerbose(sprintf('Removing ExecutionUnit \'%s\' for %s', $name, $package));
+
$package_id = $this->getPackageId($package, $version);
$package_config_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id . '.inx';
$package_bin_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id;
+ Console::outDebug(sprintf('package_id=%s', $package_id));
+ Console::outDebug(sprintf('package_config_path=%s', $package_config_path));
+ Console::outDebug(sprintf('package_bin_path=%s', $package_bin_path));
+
$filesystem = new Filesystem();
if(!$filesystem->exists($package_config_path))
return false;
@@ -225,16 +309,25 @@
*/
public function getUnits(string $package, string $version): array
{
+ Console::outVerbose(sprintf('getting execution units for %s', $package));
+
$package_id = $this->getPackageId($package, $version);
$package_config_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id . '.inx';
+ Console::outDebug(sprintf('package_id=%s', $package_id));
+ Console::outDebug(sprintf('package_config_path=%s', $package_config_path));
+
if(!file_exists($package_config_path))
+ {
+ Console::outWarning(sprintf('Path \'%s\' does not exist', $package_config_path));
return [];
+ }
$execution_pointers = ExecutionPointers::fromArray(ZiProto::decode(IO::fread($package_config_path)));
$results = [];
foreach($execution_pointers->getPointers() as $pointer)
{
+ Console::outDebug(sprintf('unit %s', $pointer->ExecutionPolicy->Name));
$results[] = $pointer->ExecutionPolicy->Name;
}
@@ -247,17 +340,18 @@
* @param string $package
* @param string $version
* @param string $name
- * @return void
+ * @param array $args
+ * @return int
* @throws AccessDeniedException
- * @throws ExecutionUnitNotFoundException
* @throws FileNotFoundException
* @throws IOException
* @throws NoAvailableUnitsException
- * @throws UnsupportedRunnerException
* @throws RunnerExecutionException
*/
- public function executeUnit(string $package, string $version, string $name): void
+ public function executeUnit(string $package, string $version, string $name, array $args=[]): int
{
+ Console::outVerbose(sprintf('executing unit %s for %s', $name, $package));
+
$package_id = $this->getPackageId($package, $version);
$package_config_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id . '.inx';
@@ -268,18 +362,37 @@
$unit = $execution_pointers->getUnit($name);
if($unit == null)
- throw new ExecutionUnitNotFoundException('The execution unit \'' . $name . '\' was not found for \'' . $package . '=' .$version .'\'');
+ throw new RunnerExecutionException('The execution unit \'' . $name . '\' was not found for \'' . $package . '=' .$version .'\'');
- $process = match (strtolower($unit->ExecutionPolicy->Runner))
+ Console::outDebug(sprintf('unit=%s', $unit->ExecutionPolicy->Name));
+ Console::outDebug(sprintf('runner=%s', $unit->ExecutionPolicy->Runner));
+ Console::outDebug(sprintf('file=%s', $unit->FilePointer));
+ Console::outDebug(sprintf('pass_thru_args=%s', json_encode($args, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)));
+
+ // Handle the arguments
+ if($unit->ExecutionPolicy->Execute->Options !== null && count($unit->ExecutionPolicy->Execute->Options) > 0)
{
- Runners::php => PhpRunner::prepareProcess($unit),
- default => throw new UnsupportedRunnerException('The runner \'' . $unit->ExecutionPolicy->Runner . '\' is not supported'),
- };
+ $args = array_merge($args, $unit->ExecutionPolicy->Execute->Options);
+
+ foreach($unit->ExecutionPolicy->Execute->Options as $option)
+ {
+ $args[] = ConstantCompiler::compileRuntimeConstants($option);
+ }
+ }
+
+ $process = new Process(array_merge(
+ [PathFinder::findRunner(strtolower($unit->ExecutionPolicy->Runner)), $unit->FilePointer], $args)
+ );
if($unit->ExecutionPolicy->Execute->WorkingDirectory !== null)
- $process->setWorkingDirectory($unit->ExecutionPolicy->Execute->WorkingDirectory);
+ {
+ $process->setWorkingDirectory(ConstantCompiler::compileRuntimeConstants($unit->ExecutionPolicy->Execute->WorkingDirectory));
+ }
+
if($unit->ExecutionPolicy->Execute->Timeout !== null)
+ {
$process->setTimeout((float)$unit->ExecutionPolicy->Execute->Timeout);
+ }
if($unit->ExecutionPolicy->Execute->Silent)
{
@@ -296,12 +409,20 @@
$process->enableOutput();
}
+ Console::outDebug(sprintf('working_directory=%s', $process->getWorkingDirectory()));
+ Console::outDebug(sprintf('timeout=%s', ($process->getTimeout() ?? 0)));
+ Console::outDebug(sprintf('silent=%s', ($unit->ExecutionPolicy->Execute->Silent ? 'true' : 'false')));
+ Console::outDebug(sprintf('tty=%s', ($unit->ExecutionPolicy->Execute->Tty ? 'true' : 'false')));
+ Console::outDebug(sprintf('options=%s', implode(' ', $args)));
+ Console::outDebug(sprintf('cmd=%s', $process->getCommandLine()));
+
try
{
if($unit->ExecutionPolicy->Message !== null)
Console::out($unit->ExecutionPolicy->Message);
- $process->run(function ($type, $buffer) {
+ $process->run(function ($type, $buffer)
+ {
Console::out($buffer);
});
@@ -313,6 +434,8 @@
$this->handleExit($package, $version, $unit->ExecutionPolicy->ExitHandlers->Error);
}
+ Console::outDebug(sprintf('exit_code=%s', $process->getExitCode()));
+
if($unit->ExecutionPolicy->ExitHandlers !== null)
{
if($process->isSuccessful() && $unit->ExecutionPolicy->ExitHandlers->Success !== null)
@@ -330,6 +453,8 @@
$this->handleExit($package, $version, $unit->ExecutionPolicy->ExitHandlers->Error, $process);
}
}
+
+ return $process->getExitCode();
}
/**
@@ -339,12 +464,10 @@
* @param string $unit_name
* @return void
* @throws AccessDeniedException
- * @throws ExecutionUnitNotFoundException
* @throws FileNotFoundException
* @throws IOException
* @throws NoAvailableUnitsException
* @throws RunnerExecutionException
- * @throws UnsupportedRunnerException
*/
public function temporaryExecute(Package $package, string $unit_name): void
{
@@ -391,12 +514,10 @@
* @param Process|null $process
* @return bool
* @throws AccessDeniedException
- * @throws ExecutionUnitNotFoundException
* @throws FileNotFoundException
* @throws IOException
* @throws NoAvailableUnitsException
* @throws RunnerExecutionException
- * @throws UnsupportedRunnerException
*/
public function handleExit(string $package, string $version, ExitHandle $exitHandle, ?Process $process=null): bool
{
@@ -410,11 +531,13 @@
}
elseif($exitHandle->EndProcess)
{
+ Console::outDebug(sprintf('exit_code=%s', $process->getExitCode()));
exit($exitHandle->ExitCode);
}
if($exitHandle->Run !== null)
{
+ Console::outVerbose('Running unit \'' . $exitHandle->Run . '\'');
$this->executeUnit($package, $version, $exitHandle->Run);
}
diff --git a/src/ncc/Managers/PackageLockManager.php b/src/ncc/Managers/PackageLockManager.php
index 56d7daf..46134d6 100644
--- a/src/ncc/Managers/PackageLockManager.php
+++ b/src/ncc/Managers/PackageLockManager.php
@@ -1,7 +1,26 @@
sync();
+ }
+ catch(Exception $e)
+ {
+ throw new PackageLockException('Failed to synchronize symlinks', $e);
+ }
}
/**
diff --git a/src/ncc/Managers/PackageManager.php b/src/ncc/Managers/PackageManager.php
index f22fafd..fb2cde0 100644
--- a/src/ncc/Managers/PackageManager.php
+++ b/src/ncc/Managers/PackageManager.php
@@ -1,4 +1,24 @@
getPackageVersion($package->Assembly->Package, $package->Assembly->Version) !== null)
- throw new PackageAlreadyInstalledException('The package ' . $package->Assembly->Package . '=' . $package->Assembly->Version . ' is already installed');
-
$extension = $package->Header->CompilerExtension->Extension;
$installation_paths = new InstallationPaths($this->PackagesPath . DIRECTORY_SEPARATOR . $package->Assembly->Package . '=' . $package->Assembly->Version);
- $installer = match ($extension) {
+
+ $installer = match ($extension)
+ {
CompilerExtensions::PHP => new PhpInstaller($package),
default => throw new UnsupportedCompilerExtensionException('The compiler extension \'' . $extension . '\' is not supported'),
};
+
+ if($this->getPackageVersion($package->Assembly->Package, $package->Assembly->Version) !== null)
+ {
+ if(in_array(InstallPackageOptions::Reinstall, $options))
+ {
+ if($this->getPackageLockManager()->getPackageLock()->packageExists(
+ $package->Assembly->Package, $package->Assembly->Version
+ ))
+ {
+ $this->getPackageLockManager()->getPackageLock()->removePackageVersion(
+ $package->Assembly->Package, $package->Assembly->Version
+ );
+ }
+ }
+ else
+ {
+ throw new PackageAlreadyInstalledException('The package ' . $package->Assembly->Package . '=' . $package->Assembly->Version . ' is already installed');
+ }
+ }
+
$execution_pointer_manager = new ExecutionPointerManager();
PackageCompiler::compilePackageConstants($package, [
ConstantReferences::Install => $installation_paths
]);
// Process all the required dependencies before installing the package
- if($package->Dependencies !== null && count($package->Dependencies) > 0)
+ if($package->Dependencies !== null && count($package->Dependencies) > 0 && !in_array(InstallPackageOptions::SkipDependencies, $options))
{
foreach($package->Dependencies as $dependency)
{
- $this->processDependency($dependency, $package, $package_path);
+ if(in_array(InstallPackageOptions::Reinstall, $options))
+ {
+ // Uninstall the dependency if the option Reinstall is passed on
+ if($this->getPackageLockManager()->getPackageLock()->packageExists($dependency->Name, $dependency->Version))
+ {
+ if($dependency->Version == null)
+ {
+ $this->uninstallPackage($dependency->Name);
+ }
+ else
+ {
+ $this->uninstallPackageVersion($dependency->Name, $dependency->Version);
+ }
+ }
+ }
+
+ $this->processDependency($dependency, $package, $package_path, $entry);
}
}
@@ -256,14 +330,15 @@
}
// Install execution units
- // TODO: Implement symlink support
- if(count($package->ExecutionUnits) > 0)
+ if($package->ExecutionUnits !== null && count($package->ExecutionUnits) > 0)
{
$execution_pointer_manager = new ExecutionPointerManager();
$unit_paths = [];
+ /** @var Package\ExecutionUnit $executionUnit */
foreach($package->ExecutionUnits as $executionUnit)
{
+ Console::outDebug(sprintf('processing execution unit %s', $executionUnit->ExecutionPolicy->Name));
$execution_pointer_manager->addUnit($package->Assembly->Package, $package->Assembly->Version, $executionUnit);
$current_steps += 1;
Console::inlineProgressBar($current_steps, $steps);
@@ -272,6 +347,16 @@
IO::fwrite($installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'exec', ZiProto::encode($unit_paths));
}
+ // After execution units are installed, create a symlink if needed
+ if(isset($package->Header->Options['create_symlink']) && $package->Header->Options['create_symlink'])
+ {
+ if($package->MainExecutionPolicy === null)
+ throw new InstallationException('Cannot create symlink, no main execution policy is defined');
+
+ $SymlinkManager = new SymlinkManager();
+ $SymlinkManager->add($package->Assembly->Package, $package->MainExecutionPolicy);
+ }
+
// Execute the post-installation stage after the installation is complete
try
{
@@ -302,32 +387,227 @@
}
}
+ if($package->Header->UpdateSource !== null && $package->Header->UpdateSource->Repository !== null)
+ {
+ $sources_manager = new RemoteSourcesManager();
+ if($sources_manager->getRemoteSource($package->Header->UpdateSource->Repository->Name) === null)
+ {
+ Console::outVerbose('Adding remote source ' . $package->Header->UpdateSource->Repository->Name);
+ $defined_remote_source = new DefinedRemoteSource();
+ $defined_remote_source->Name = $package->Header->UpdateSource->Repository->Name;
+ $defined_remote_source->Host = $package->Header->UpdateSource->Repository->Host;
+ $defined_remote_source->Type = $package->Header->UpdateSource->Repository->Type;
+ $defined_remote_source->SSL = $package->Header->UpdateSource->Repository->SSL;
+
+ $sources_manager->addRemoteSource($defined_remote_source);
+ }
+ }
+
$this->getPackageLockManager()->getPackageLock()->addPackage($package, $installation_paths->getInstallationPath());
$this->getPackageLockManager()->save();
return $package->Assembly->Package;
}
+ /**
+ * @param string $source
+ * @param Entry|null $entry
+ * @return string
+ * @throws InstallationException
+ * @throws NotImplementedException
+ * @throws PackageFetchException
+ */
+ public function fetchFromSource(string $source, ?Entry $entry=null): string
+ {
+ $input = new RemotePackageInput($source);
+
+ if($input->Source == null)
+ throw new PackageFetchException('No source specified');
+ if($input->Package == null)
+ throw new PackageFetchException('No package specified');
+ if($input->Version == null)
+ $input->Version = Versions::Latest;
+
+ Console::outVerbose('Fetching package ' . $input->Package . ' from ' . $input->Source . ' (' . $input->Version . ')');
+
+ $remote_source_type = Resolver::detectRemoteSourceType($input->Source);
+ if($remote_source_type == RemoteSourceType::Builtin)
+ {
+ Console::outDebug('using builtin source ' . $input->Source);
+ switch($input->Source)
+ {
+ case 'composer':
+ try
+ {
+ return ComposerSourceBuiltin::fetch($input);
+ }
+ catch(Exception $e)
+ {
+ throw new PackageFetchException('Cannot fetch package from composer source, ' . $e->getMessage(), $e);
+ }
+
+ default:
+ throw new NotImplementedException('Builtin source type ' . $input->Source . ' is not implemented');
+ }
+ }
+
+ if($remote_source_type == RemoteSourceType::Defined)
+ {
+ Console::outDebug('using defined source ' . $input->Source);
+ $remote_source_manager = new RemoteSourcesManager();
+ $source = $remote_source_manager->getRemoteSource($input->Source);
+ if($source == null)
+ throw new InstallationException('Remote source ' . $input->Source . ' is not defined');
+
+ $repositoryQueryResults = Functions::getRepositoryQueryResults($input, $source, $entry);
+ $exceptions = [];
+
+ if($repositoryQueryResults->Files->ZipballUrl !== null)
+ {
+ try
+ {
+ Console::outDebug(sprintf('fetching package %s from %s', $input->Package, $repositoryQueryResults->Files->ZipballUrl));
+ $archive = Functions::downloadGitServiceFile($repositoryQueryResults->Files->ZipballUrl, $entry);
+ return PackageCompiler::tryCompile(Functions::extractArchive($archive), $repositoryQueryResults->Version);
+ }
+ catch(Throwable $e)
+ {
+ Console::outDebug('cannot fetch package from zipball url, ' . $e->getMessage());
+ $exceptions[] = $e;
+ }
+ }
+
+ if($repositoryQueryResults->Files->TarballUrl !== null)
+ {
+ try
+ {
+ Console::outDebug(sprintf('fetching package %s from %s', $input->Package, $repositoryQueryResults->Files->TarballUrl));
+ $archive = Functions::downloadGitServiceFile($repositoryQueryResults->Files->TarballUrl, $entry);
+ return PackageCompiler::tryCompile(Functions::extractArchive($archive), $repositoryQueryResults->Version);
+ }
+ catch(Exception $e)
+ {
+ Console::outDebug('cannot fetch package from tarball url, ' . $e->getMessage());
+ $exceptions[] = $e;
+ }
+ }
+
+ if($repositoryQueryResults->Files->PackageUrl !== null)
+ {
+ try
+ {
+ Console::outDebug(sprintf('fetching package %s from %s', $input->Package, $repositoryQueryResults->Files->PackageUrl));
+ return Functions::downloadGitServiceFile($repositoryQueryResults->Files->PackageUrl, $entry);
+ }
+ catch(Exception $e)
+ {
+ Console::outDebug('cannot fetch package from package url, ' . $e->getMessage());
+ $exceptions[] = $e;
+ }
+ }
+
+ if($repositoryQueryResults->Files->GitHttpUrl !== null || $repositoryQueryResults->Files->GitSshUrl !== null)
+ {
+ try
+ {
+ Console::outDebug(sprintf('fetching package %s from %s', $input->Package, $repositoryQueryResults->Files->GitHttpUrl ?? $repositoryQueryResults->Files->GitSshUrl));
+ $git_repository = GitClient::cloneRepository($repositoryQueryResults->Files->GitHttpUrl ?? $repositoryQueryResults->Files->GitSshUrl);
+
+ foreach(GitClient::getTags($git_repository) as $tag)
+ {
+ if(VersionComparator::compareVersion($tag, $repositoryQueryResults->Version) === 0)
+ {
+ GitClient::checkout($git_repository, $tag);
+ return PackageCompiler::tryCompile($git_repository, $repositoryQueryResults->Version);
+ }
+ }
+
+ Console::outDebug('cannot fetch package from git repository, no matching tag found');
+ }
+ catch(Exception $e)
+ {
+ Console::outDebug('cannot fetch package from git repository, ' . $e->getMessage());
+ $exceptions[] = $e;
+ }
+ }
+
+ // Recursively create an exception with the previous exceptions as the previous exception
+ $exception = null;
+
+ if(count($exceptions) > 0)
+ {
+ foreach($exceptions as $e)
+ {
+ if($exception == null)
+ {
+ $exception = new PackageFetchException($e->getMessage(), $e);
+ }
+ else
+ {
+ if($e->getMessage() == $exception->getMessage())
+ continue;
+
+ $exception = new PackageFetchException($e->getMessage(), $exception);
+ }
+ }
+ }
+ else
+ {
+ $exception = new PackageFetchException('Cannot fetch package from remote source, no assets found');
+ }
+
+ throw $exception;
+ }
+
+ throw new PackageFetchException(sprintf('Unknown remote source type %s', $remote_source_type));
+ }
+
+ /**
+ * Installs a package from a source syntax (vendor/package=version@source)
+ *
+ * @param string $source
+ * @param Entry|null $entry
+ * @return string
+ * @throws InstallationException
+ */
+ public function installFromSource(string $source, ?Entry $entry): string
+ {
+ try
+ {
+ Console::outVerbose(sprintf('Installing package from source %s', $source));
+ $package = $this->fetchFromSource($source, $entry);
+ return $this->install($package, $entry);
+ }
+ catch(Exception $e)
+ {
+ throw new InstallationException('Cannot install package from source, ' . $e->getMessage(), $e);
+ }
+ }
+
/**
* @param Dependency $dependency
* @param Package $package
* @param string $package_path
+ * @param Entry|null $entry
* @return void
* @throws AccessDeniedException
* @throws FileNotFoundException
* @throws IOException
* @throws InstallationException
+ * @throws InvalidPackageNameException
+ * @throws InvalidScopeException
* @throws MissingDependencyException
* @throws NotImplementedException
* @throws PackageAlreadyInstalledException
* @throws PackageLockException
* @throws PackageNotFoundException
* @throws PackageParsingException
+ * @throws SymlinkException
* @throws UnsupportedCompilerExtensionException
- * @throws UnsupportedRunnerException
* @throws VersionNotFoundException
+ * @throws RunnerExecutionException
*/
- private function processDependency(Dependency $dependency, Package $package, string $package_path): void
+ private function processDependency(Dependency $dependency, Package $package, string $package_path, ?Entry $entry=null): void
{
Console::outVerbose('processing dependency ' . $dependency->Name . ' (' . $dependency->Version . ')');
$dependent_package = $this->getPackage($dependency->Name);
@@ -335,39 +615,45 @@
if ($dependent_package !== null && $dependency->Version !== null && Validate::version($dependency->Version))
{
+ Console::outDebug('dependency has version constraint, checking if package is installed');
$dependent_version = $this->getPackageVersion($dependency->Name, $dependency->Version);
if ($dependent_version !== null)
$dependency_met = true;
}
elseif ($dependent_package !== null && $dependency->Version == null)
{
+ Console::outDebug(sprintf('dependency %s has no version specified, assuming dependency is met', $dependency->Name));
$dependency_met = true;
}
- if ($dependency->SourceType !== null)
+ Console::outDebug('dependency met: ' . ($dependency_met ? 'true' : 'false'));
+
+ if ($dependency->SourceType !== null && !$dependency_met)
{
- if(!$dependency_met)
+ Console::outVerbose(sprintf('Installing dependency %s=%s for %s=%s', $dependency->Name, $dependency->Version, $package->Assembly->Package, $package->Assembly->Version));
+ switch ($dependency->SourceType)
{
- Console::outVerbose(sprintf('Installing dependency %s=%s for %s=%s', $dependency->Name, $dependency->Version, $package->Assembly->Package, $package->Assembly->Version));
- switch ($dependency->SourceType)
- {
- case DependencySourceType::Local:
- Console::outDebug('installing from local source ' . $dependency->Source);
- $basedir = dirname($package_path);
- if (!file_exists($basedir . DIRECTORY_SEPARATOR . $dependency->Source))
- throw new FileNotFoundException($basedir . DIRECTORY_SEPARATOR . $dependency->Source);
- $this->install($basedir . DIRECTORY_SEPARATOR . $dependency->Source);
- break;
+ case DependencySourceType::Local:
+ Console::outDebug('installing from local source ' . $dependency->Source);
+ $basedir = dirname($package_path);
+ if (!file_exists($basedir . DIRECTORY_SEPARATOR . $dependency->Source))
+ throw new FileNotFoundException($basedir . DIRECTORY_SEPARATOR . $dependency->Source);
+ $this->install($basedir . DIRECTORY_SEPARATOR . $dependency->Source);
+ break;
- case DependencySourceType::StaticLinking:
- throw new PackageNotFoundException('Static linking not possible, package ' . $dependency->Name . ' is not installed');
+ case DependencySourceType::StaticLinking:
+ throw new PackageNotFoundException('Static linking not possible, package ' . $dependency->Name . ' is not installed');
- default:
- throw new NotImplementedException('Dependency source type ' . $dependency->SourceType . ' is not implemented');
- }
+ case DependencySourceType::RemoteSource:
+ Console::outDebug('installing from remote source ' . $dependency->Source);
+ $this->installFromSource($dependency->Source, $entry);
+ break;
+
+ default:
+ throw new NotImplementedException('Dependency source type ' . $dependency->SourceType . ' is not implemented');
}
}
- else
+ elseif(!$dependency_met)
{
throw new MissingDependencyException(sprintf('The dependency %s=%s for %s=%s is not met', $dependency->Name, $dependency->Version, $package->Assembly->Package, $package->Assembly->Version));
}
@@ -379,10 +665,10 @@
* @param string $package
* @return PackageEntry|null
* @throws PackageLockException
- * @throws PackageLockException
*/
public function getPackage(string $package): ?PackageEntry
{
+ Console::outDebug('getting package ' . $package);
return $this->getPackageLockManager()->getPackageLock()->getPackage($package);
}
@@ -397,6 +683,7 @@
*/
public function getPackageVersion(string $package, string $version): ?VersionEntry
{
+ Console::outDebug('getting package version ' . $package . '=' . $version);
return $this->getPackage($package)?->getVersion($version);
}
@@ -407,9 +694,11 @@
* @return VersionEntry|null
* @throws VersionNotFoundException
* @throws PackageLockException
+ * @noinspection PhpUnused
*/
public function getLatestVersion(string $package): ?VersionEntry
{
+ Console::outDebug('getting latest version of package ' . $package);
return $this->getPackage($package)?->getVersion($this->getPackage($package)?->getLatestVersion());
}
@@ -442,7 +731,15 @@
$exploded = explode('=', $package);
try
{
- foreach ($this->getPackage($exploded[0])?->getVersion($exploded[1])?->Dependencies as $dependency)
+ $package = $this->getPackage($exploded[0]);
+ if($package == null)
+ throw new PackageNotFoundException('Package ' . $exploded[0] . ' not found');
+
+ $version = $package->getVersion($exploded[1]);
+ if($version == null)
+ throw new VersionNotFoundException('Version ' . $exploded[1] . ' not found for package ' . $exploded[0]);
+
+ foreach ($version->Dependencies as $dependency)
{
if(!in_array($dependency->PackageName . '=' . $dependency->Version, $tree))
$packages[] = $dependency->PackageName . '=' . $dependency->Version;
@@ -481,14 +778,21 @@
try
{
$version_entry = $this->getPackageVersion($package_e[0], $package_e[1]);
- $tree[$package] = null;
- if($version_entry->Dependencies !== null && count($version_entry->Dependencies) > 0)
+ if($version_entry == null)
{
- $tree[$package] = [];
- foreach($version_entry->Dependencies as $dependency)
+ Console::outWarning('Version ' . $package_e[1] . ' of package ' . $package_e[0] . ' not found');
+ }
+ else
+ {
+ $tree[$package] = null;
+ if($version_entry->Dependencies !== null && count($version_entry->Dependencies) > 0)
{
- $dependency_name = sprintf('%s=%s', $dependency->PackageName, $dependency->Version);
- $tree[$package] = $this->getPackageTree($tree[$package], $dependency_name);
+ $tree[$package] = [];
+ foreach($version_entry->Dependencies as $dependency)
+ {
+ $dependency_name = sprintf('%s=%s', $dependency->PackageName, $dependency->Version);
+ $tree[$package] = $this->getPackageTree($tree[$package], $dependency_name);
+ }
}
}
}
@@ -512,6 +816,7 @@
* @throws IOException
* @throws PackageLockException
* @throws PackageNotFoundException
+ * @throws SymlinkException
* @throws VersionNotFoundException
*/
public function uninstallPackageVersion(string $package, string $version): void
@@ -534,16 +839,26 @@
$scanner = new DirectoryScanner();
$filesystem = new Filesystem();
- /** @var SplFileInfo $item */
- /** @noinspection PhpRedundantOptionalArgumentInspection */
- foreach($scanner($version_entry->Location, true) as $item)
+ if($filesystem->exists($version_entry->Location))
{
- if(is_file($item->getPath()))
+ Console::outVerbose(sprintf('Removing package files from %s', $version_entry->Location));
+
+ /** @var SplFileInfo $item */
+ /** @noinspection PhpRedundantOptionalArgumentInspection */
+ foreach($scanner($version_entry->Location, true) as $item)
{
- Console::outDebug(sprintf('deleting %s', $item->getPath()));
- $filesystem->remove($item->getPath());
+ if(is_file($item->getPath()))
+ {
+ Console::outDebug('removing file ' . $item->getPath());
+ Console::outDebug(sprintf('deleting %s', $item->getPath()));
+ $filesystem->remove($item->getPath());
+ }
}
}
+ else
+ {
+ Console::outWarning(sprintf('warning: package location %s does not exist', $version_entry->Location));
+ }
$filesystem->remove($version_entry->Location);
@@ -558,6 +873,9 @@
Console::outDebug(sprintf('warning: removing execution unit %s failed', $executionUnit->ExecutionPolicy->Name));
}
}
+
+ $symlink_manager = new SymlinkManager();
+ $symlink_manager->sync();
}
/**
@@ -582,6 +900,7 @@
foreach($package_entry->getVersions() as $version)
{
$version_entry = $package_entry->getVersion($version);
+
try
{
$this->uninstallPackageVersion($package, $version_entry->Version);
@@ -600,6 +919,8 @@
*/
private static function initData(Package $package, InstallationPaths $paths): void
{
+ Console::outVerbose(sprintf('Initializing data for %s', $package->Assembly->Name));
+
// Create data files
$dependencies = [];
foreach($package->Dependencies as $dependency)
@@ -611,7 +932,7 @@
$paths->getDataPath() . DIRECTORY_SEPARATOR . 'assembly' =>
ZiProto::encode($package->Assembly->toArray(true)),
$paths->getDataPath() . DIRECTORY_SEPARATOR . 'ext' =>
- ZiProto::encode($package->Header->CompilerExtension->toArray(true)),
+ ZiProto::encode($package->Header->CompilerExtension->toArray()),
$paths->getDataPath() . DIRECTORY_SEPARATOR . 'const' =>
ZiProto::encode($package->Header->RuntimeConstants),
$paths->getDataPath() . DIRECTORY_SEPARATOR . 'dependencies' =>
@@ -622,6 +943,7 @@
{
try
{
+ Console::outDebug(sprintf('generating data file %s', $file));
IO::fwrite($file, $data);
}
catch (IOException $e)
diff --git a/src/ncc/Managers/ProjectManager.php b/src/ncc/Managers/ProjectManager.php
index 286910b..9e28ba6 100644
--- a/src/ncc/Managers/ProjectManager.php
+++ b/src/ncc/Managers/ProjectManager.php
@@ -1,4 +1,24 @@
ProjectConfiguration->Build->DefineConstants['ASSEMBLY_UID'] = '%ASSEMBLY.UID%';
// Generate configurations
- $DebugConfiguration = new ProjectConfiguration\BuildConfiguration();
+ $DebugConfiguration = new ProjectConfiguration\Build\BuildConfiguration();
$DebugConfiguration->Name = 'debug';
$DebugConfiguration->OutputPath = 'build/debug';
$DebugConfiguration->DefineConstants["DEBUG"] = '1'; // Debugging constant if the program wishes to check for this
$this->ProjectConfiguration->Build->Configurations[] = $DebugConfiguration;
- $ReleaseConfiguration = new ProjectConfiguration\BuildConfiguration();
+ $ReleaseConfiguration = new ProjectConfiguration\Build\BuildConfiguration();
$ReleaseConfiguration->Name = 'release';
$ReleaseConfiguration->OutputPath = 'build/release';
$ReleaseConfiguration->DefineConstants["DEBUG"] = '0'; // Debugging constant if the program wishes to check for this
@@ -172,14 +191,10 @@
// Process options
foreach($options as $option)
{
- switch($option)
- {
- case InitializeProjectOptions::CREATE_SOURCE_DIRECTORY:
- if(!file_exists($this->ProjectConfiguration->Build->SourcePath))
- {
- mkdir($this->ProjectConfiguration->Build->SourcePath);
- }
- break;
+ if ($option == InitializeProjectOptions::CREATE_SOURCE_DIRECTORY) {
+ if (!file_exists($this->ProjectConfiguration->Build->SourcePath)) {
+ mkdir($this->ProjectConfiguration->Build->SourcePath);
+ }
}
}
}
@@ -207,7 +222,7 @@
* @throws FileNotFoundException
* @throws IOException
*/
- public function load()
+ public function load(): void
{
if(!file_exists($this->ProjectFilePath) && !is_file($this->ProjectFilePath))
throw new ProjectConfigurationNotFoundException('The project configuration file \'' . $this->ProjectFilePath . '\' was not found');
@@ -221,7 +236,7 @@
* @return void
* @throws MalformedJsonException
*/
- public function save()
+ public function save(): void
{
if(!$this->projectLoaded())
return;
@@ -274,7 +289,6 @@
* @throws BuildException
* @throws PackagePreparationFailedException
* @throws UnsupportedCompilerExtensionException
- * @throws UnsupportedRunnerException
*/
public function build(string $build_configuration=BuildConfigurationValues::DefaultConfiguration): string
{
diff --git a/src/ncc/Managers/RemoteSourcesManager.php b/src/ncc/Managers/RemoteSourcesManager.php
new file mode 100644
index 0000000..dc1f884
--- /dev/null
+++ b/src/ncc/Managers/RemoteSourcesManager.php
@@ -0,0 +1,171 @@
+DefinedSourcesPath = PathFinder::getRemoteSources(Scopes::System);
+
+ $this->load();
+ }
+
+ /**
+ * Loads an existing remote sources file, or creates a new one if it doesn't exist
+ *
+ * @return void
+ */
+ public function load(): void
+ {
+ $this->Sources = [];
+
+ try
+ {
+
+ if(file_exists($this->DefinedSourcesPath))
+ {
+ $sources = ZiProto::decode(IO::fread($this->DefinedSourcesPath));
+ $this->Sources = [];
+ foreach($sources as $source)
+ $this->Sources[] = DefinedRemoteSource::fromArray($source);
+ }
+ }
+ catch(Exception $e)
+ {
+ unset($e);
+ }
+ }
+
+ /**
+ * Saves the remote sources file to disk
+ *
+ * @return void
+ * @throws IOException
+ */
+ public function save(): void
+ {
+ $sources = [];
+ foreach($this->Sources as $source)
+ $sources[] = $source->toArray(true);
+
+ IO::fwrite($this->DefinedSourcesPath, ZiProto::encode($sources));
+ }
+
+ /**
+ * Adds a new remote source to the list
+ *
+ * @param DefinedRemoteSource $source
+ * @return bool
+ */
+ public function addRemoteSource(DefinedRemoteSource $source): bool
+ {
+ foreach($this->Sources as $existingSource)
+ {
+ if($existingSource->Name === $source->Name)
+ return false;
+ }
+
+ $this->Sources[] = $source;
+ return true;
+ }
+
+ /**
+ * Gets a remote source by its name
+ *
+ * @param string $name
+ * @return DefinedRemoteSource|null
+ */
+ public function getRemoteSource(string $name): ?DefinedRemoteSource
+ {
+ foreach($this->Sources as $source)
+ {
+ if($source->Name === $name)
+ return $source;
+ }
+
+ return null;
+ }
+
+ /**
+ * Deletes an existing remote source
+ *
+ * @param string $name
+ * @return bool
+ */
+ public function deleteRemoteSource(string $name): bool
+ {
+ foreach($this->Sources as $index => $source)
+ {
+ if($source->Name === $name)
+ {
+ unset($this->Sources[$index]);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns an array of all the defined remote sources
+ *
+ * @return DefinedRemoteSource[]
+ */
+ public function getSources(): array
+ {
+ if($this->Sources == null)
+ $this->load();
+ return $this->Sources;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Managers/SymlinkManager.php b/src/ncc/Managers/SymlinkManager.php
new file mode 100644
index 0000000..ce3e2e1
--- /dev/null
+++ b/src/ncc/Managers/SymlinkManager.php
@@ -0,0 +1,355 @@
+SymlinkDictionaryPath = PathFinder::getSymlinkDictionary(Scopes::System);
+ $this->load();
+ }
+ catch(Exception $e)
+ {
+ Console::outWarning(sprintf('failed to load symlink dictionary from %s', $this->SymlinkDictionaryPath));
+ }
+ finally
+ {
+ if($this->SymlinkDictionary === null)
+ $this->SymlinkDictionary = [];
+
+ unset($e);
+ }
+ }
+
+ /**
+ * Loads the symlink dictionary from the file
+ *
+ * @return void
+ * @throws AccessDeniedException
+ * @throws SymlinkException
+ */
+ public function load(): void
+ {
+ if($this->SymlinkDictionary !== null)
+ return;
+
+ Console::outDebug(sprintf('loading symlink dictionary from %s', $this->SymlinkDictionaryPath));
+
+ if(!file_exists($this->SymlinkDictionaryPath))
+ {
+ Console::outDebug('symlink dictionary does not exist, creating new dictionary');
+ $this->SymlinkDictionary = [];
+ $this->save(false);
+ return;
+ }
+
+ try
+ {
+ $this->SymlinkDictionary = [];
+
+ foreach(ZiProto::decode(IO::fread($this->SymlinkDictionaryPath)) as $entry)
+ {
+ $this->SymlinkDictionary[] = SymlinkEntry::fromArray($entry);
+ }
+ }
+ catch(Exception $e)
+ {
+ $this->SymlinkDictionary = [];
+
+ Console::outDebug('symlink dictionary is corrupted, creating new dictionary');
+ $this->save(false);
+ }
+ finally
+ {
+ unset($e);
+ }
+ }
+
+ /**
+ * Saves the symlink dictionary to the file
+ *
+ * @param bool $throw_exception
+ * @return void
+ * @throws AccessDeniedException
+ * @throws SymlinkException
+ */
+ private function save(bool $throw_exception=true): void
+ {
+ if(Resolver::resolveScope() !== Scopes::System)
+ throw new AccessDeniedException('Insufficient Permissions to write to the system symlink dictionary');
+
+ Console::outDebug(sprintf('saving symlink dictionary to %s', $this->SymlinkDictionaryPath));
+
+ try
+ {
+ $dictionary = [];
+ foreach($this->SymlinkDictionary as $entry)
+ {
+ $dictionary[] = $entry->toArray(true);
+ }
+
+ IO::fwrite($this->SymlinkDictionaryPath, ZiProto::encode($dictionary));
+ }
+ catch(Exception $e)
+ {
+ if($throw_exception)
+ throw new SymlinkException(sprintf('failed to save symlink dictionary to %s', $this->SymlinkDictionaryPath), $e);
+
+ Console::outWarning(sprintf('failed to save symlink dictionary to %s', $this->SymlinkDictionaryPath));
+ }
+ finally
+ {
+ unset($e);
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getSymlinkDictionaryPath(): string
+ {
+ return $this->SymlinkDictionaryPath;
+ }
+
+ /**
+ * @return array
+ */
+ public function getSymlinkDictionary(): array
+ {
+ return $this->SymlinkDictionary;
+ }
+
+ /**
+ * Checks if a package is defined in the symlink dictionary
+ *
+ * @param string $package
+ * @return bool
+ */
+ public function exists(string $package): bool
+ {
+ foreach($this->SymlinkDictionary as $entry)
+ {
+ if($entry->Package === $package)
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds a new entry to the symlink dictionary
+ *
+ * @param string $package
+ * @param string $unit
+ * @return void
+ * @throws AccessDeniedException
+ * @throws SymlinkException
+ */
+ public function add(string $package, string $unit='main'): void
+ {
+ if(Resolver::resolveScope() !== Scopes::System)
+ throw new AccessDeniedException('Insufficient Permissions to add to the system symlink dictionary');
+
+ if($this->exists($package))
+ $this->remove($package);
+
+ $entry = new SymlinkEntry();
+ $entry->Package = $package;
+ $entry->ExecutionPolicyName = $unit;
+
+ $this->SymlinkDictionary[] = $entry;
+ $this->save();
+ }
+
+ /**
+ * Removes an entry from the symlink dictionary
+ *
+ * @param string $package
+ * @return void
+ * @throws AccessDeniedException
+ * @throws SymlinkException
+ */
+ public function remove(string $package): void
+ {
+ if(Resolver::resolveScope() !== Scopes::System)
+ throw new AccessDeniedException('Insufficient Permissions to remove from the system symlink dictionary');
+
+ if(!$this->exists($package))
+ return;
+
+ foreach($this->SymlinkDictionary as $key => $entry)
+ {
+ if($entry->Package === $package)
+ {
+ if($entry->Registered)
+ {
+ $filesystem = new Filesystem();
+
+ $symlink_name = explode('.', $entry->Package)[count(explode('.', $entry->Package)) - 1];
+ $symlink = self::$BinPath . DIRECTORY_SEPARATOR . $symlink_name;
+
+ if($filesystem->exists($symlink))
+ $filesystem->remove($symlink);
+ }
+
+ unset($this->SymlinkDictionary[$key]);
+ $this->save();
+ return;
+ }
+ }
+
+ throw new SymlinkException(sprintf('failed to remove package %s from the symlink dictionary', $package));
+ }
+
+ /**
+ * Sets the package as registered
+ *
+ * @param string $package
+ * @return void
+ * @throws AccessDeniedException
+ * @throws SymlinkException
+ */
+ private function setAsRegistered(string $package): void
+ {
+ foreach($this->SymlinkDictionary as $key => $entry)
+ {
+ if($entry->Package === $package)
+ {
+ $entry->Registered = true;
+ $this->SymlinkDictionary[$key] = $entry;
+ $this->save();
+ return;
+ }
+ }
+ }
+
+ /**
+ * Syncs the symlink dictionary with the filesystem
+ *
+ * @return void
+ * @throws AccessDeniedException
+ * @throws SymlinkException
+ */
+ public function sync(): void
+ {
+ if(Resolver::resolveScope() !== Scopes::System)
+ throw new AccessDeniedException('Insufficient Permissions to sync the system symlink dictionary');
+
+ $filesystem = new Filesystem();
+ $execution_pointer_manager = new ExecutionPointerManager();
+ $package_lock_manager = new PackageLockManager();
+
+ foreach($this->SymlinkDictionary as $entry)
+ {
+ if($entry->Registered)
+ continue;
+
+ $symlink_name = explode('.', $entry->Package)[count(explode('.', $entry->Package)) - 1];
+ $symlink = self::$BinPath . DIRECTORY_SEPARATOR . $symlink_name;
+
+ if($filesystem->exists($symlink))
+ {
+ Console::outWarning(sprintf('Symlink %s already exists, skipping', $symlink));
+ continue;
+ }
+
+ try
+ {
+ $package_entry = $package_lock_manager->getPackageLock()->getPackage($entry->Package);
+
+ if($package_entry == null)
+ {
+ Console::outWarning(sprintf('Package %s is not installed, skipping', $entry->Package));
+ continue;
+ }
+
+ $latest_version = $package_entry->getLatestVersion();
+
+ }
+ catch(Exception $e)
+ {
+ $filesystem->remove($symlink);
+ Console::outWarning(sprintf('Failed to get package %s, skipping', $entry->Package));
+ continue;
+ }
+
+ try
+ {
+ $entry_point_path = $execution_pointer_manager->getEntryPointPath($entry->Package, $latest_version, $entry->ExecutionPolicyName);
+ $filesystem->symlink($entry_point_path, $symlink);
+ }
+ catch(Exception $e)
+ {
+ $filesystem->remove($symlink);
+ Console::outWarning(sprintf('Failed to create symlink %s, skipping', $symlink));
+ continue;
+ }
+ finally
+ {
+ unset($e);
+ }
+
+ $this->setAsRegistered($entry->Package);
+
+ }
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Objects/CliHelpSection.php b/src/ncc/Objects/CliHelpSection.php
index 90c878e..c8913a5 100644
--- a/src/ncc/Objects/CliHelpSection.php
+++ b/src/ncc/Objects/CliHelpSection.php
@@ -1,4 +1,24 @@
Readonly == true)
+ if($this->Readonly)
{
throw new ConstantReadonlyException('Cannot set value to the constant \'' . $this->getFullName() . '\', constant is readonly');
}
diff --git a/src/ncc/Objects/DefinedRemoteSource.php b/src/ncc/Objects/DefinedRemoteSource.php
new file mode 100644
index 0000000..de63655
--- /dev/null
+++ b/src/ncc/Objects/DefinedRemoteSource.php
@@ -0,0 +1,95 @@
+ $this->Name,
+ ($bytecode ? Functions::cbc('type') : 'type') => $this->Type,
+ ($bytecode ? Functions::cbc('host') : 'host') => $this->Host,
+ ($bytecode ? Functions::cbc('ssl') : 'ssl') => $this->SSL
+ ];
+ }
+
+ /**
+ * Constructs object from an array representation.
+ *
+ * @param array $data
+ * @return static
+ */
+ public static function fromArray(array $data): self
+ {
+ $definedRemoteSource = new self();
+
+ $definedRemoteSource->Name = Functions::array_bc($data, 'name');
+ $definedRemoteSource->Type = Functions::array_bc($data, 'type');
+ $definedRemoteSource->Host = Functions::array_bc($data, 'host');
+ $definedRemoteSource->SSL = Functions::array_bc($data, 'ssl');
+
+ return $definedRemoteSource;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Objects/ExecutionPointers.php b/src/ncc/Objects/ExecutionPointers.php
index 9181c03..c1f6a3b 100644
--- a/src/ncc/Objects/ExecutionPointers.php
+++ b/src/ncc/Objects/ExecutionPointers.php
@@ -1,4 +1,24 @@
Pointers as $pointer)
{
if($pointer->ExecutionPolicy->Name == $name)
@@ -148,7 +172,11 @@
{
$pointers[] = $pointer->toArray($bytecode);
}
- return $pointers;
+ return [
+ ($bytecode ? Functions::cbc('package') : 'package') => $this->Package,
+ ($bytecode ? Functions::cbc('version') : 'version') => $this->Version,
+ ($bytecode ? Functions::cbc('pointers') : 'pointers') => $pointers
+ ];
}
/**
@@ -161,9 +189,18 @@
{
$object = new self();
- foreach($data as $datum)
+ $object->Version = Functions::array_bc($data, 'version');
+ $object->Package = Functions::array_bc($data, 'package');
+ $object->Pointers = Functions::array_bc($data, 'pointers');
+
+ if($object->Pointers !== null)
{
- $object->Pointers[] = ExecutionPointer::fromArray($datum);
+ $pointers = [];
+ foreach($object->Pointers as $pointer)
+ {
+ $pointers[] = ExecutionPointer::fromArray($pointer);
+ }
+ $object->Pointers = $pointers;
}
return $object;
diff --git a/src/ncc/Objects/ExecutionPointers/ExecutionPointer.php b/src/ncc/Objects/ExecutionPointers/ExecutionPointer.php
index 9a5747e..e736eca 100644
--- a/src/ncc/Objects/ExecutionPointers/ExecutionPointer.php
+++ b/src/ncc/Objects/ExecutionPointers/ExecutionPointer.php
@@ -1,4 +1,24 @@
ExecutionPolicy = Functions::array_bc($data, 'execution_policy');
$object->FilePointer = Functions::array_bc($data, 'file_pointer');
+ if($object->ExecutionPolicy !== null)
+ $object->ExecutionPolicy = ExecutionPolicy::fromArray($object->ExecutionPolicy);
+
return $object;
}
}
\ No newline at end of file
diff --git a/src/ncc/Objects/HttpRequest.php b/src/ncc/Objects/HttpRequest.php
new file mode 100644
index 0000000..138f76e
--- /dev/null
+++ b/src/ncc/Objects/HttpRequest.php
@@ -0,0 +1,128 @@
+Type = HttpRequestType::GET;
+ $this->Body = null;
+ $this->Headers = [
+ 'User-Agent: ncc/1.0'
+ ];
+ $this->Options = [];
+ }
+
+ /**
+ * Returns an array representation of the object.
+ *
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return [
+ 'type' => $this->Type,
+ 'url' => $this->Url,
+ 'headers' => $this->Headers,
+ 'body' => $this->Body,
+ 'authentication' => $this->Authentication,
+ 'options' => $this->Options
+ ];
+ }
+
+ /**
+ * Returns the hash of the object.
+ * (This is used for caching)
+ *
+ * @return string
+ */
+ public function requestHash(): string
+ {
+ return hash('sha1', json_encode($this->toArray()));
+ }
+
+ /**
+ * Constructs a new HttpRequest object from an array representation.
+ *
+ * @param array $data
+ * @return static
+ */
+ public static function fromArray(array $data): self
+ {
+ $request = new self();
+ $request->Type = $data['type'];
+ $request->Url = $data['url'];
+ $request->Headers = $data['headers'];
+ $request->Body = $data['body'];
+ $request->Authentication = $data['authentication'];
+ $request->Options = $data['options'];
+ return $request;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Objects/HttpResponse.php b/src/ncc/Objects/HttpResponse.php
new file mode 100644
index 0000000..d412b99
--- /dev/null
+++ b/src/ncc/Objects/HttpResponse.php
@@ -0,0 +1,70 @@
+StatusCode = 0;
+ $this->Headers = [];
+ $this->Body = '';
+ }
+
+ /**
+ * Returns an array representation of the object.
+ *
+ * @return array
+ */
+ public function toArray(): array
+ {
+ return [
+ 'status_code' => $this->StatusCode,
+ 'headers' => $this->Headers,
+ 'body' => $this->Body
+ ];
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Objects/HttpResponseCache.php b/src/ncc/Objects/HttpResponseCache.php
new file mode 100644
index 0000000..5ac68a0
--- /dev/null
+++ b/src/ncc/Objects/HttpResponseCache.php
@@ -0,0 +1,74 @@
+httpResponse = $httpResponse;
+ $this->ttl = $ttl;
+ }
+
+ /**
+ * Returns the cached response
+ *
+ * @return HttpResponse
+ */
+ public function getHttpResponse(): HttpResponse
+ {
+ return $this->httpResponse;
+ }
+
+ /**
+ * Returns the Unix Timestamp of when the cache becomes invalid
+ *
+ * @return int
+ */
+ public function getTtl(): int
+ {
+ return $this->ttl;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Objects/InstallationPaths.php b/src/ncc/Objects/InstallationPaths.php
index d7c745a..b39225e 100644
--- a/src/ncc/Objects/InstallationPaths.php
+++ b/src/ncc/Objects/InstallationPaths.php
@@ -1,4 +1,24 @@
Resources = [];
}
+ /**
+ * Adds a dependency to the package
+ *
+ * @param Dependency $dependency
+ * @return void
+ */
+ public function addDependency(Dependency $dependency): void
+ {
+ foreach($this->Dependencies as $dep)
+ {
+ if($dep->Name == $dependency->Name)
+ {
+ $this->removeDependency($dep->Name);
+ break;
+ }
+ }
+
+ $this->Dependencies[] = $dependency;
+ }
+
+ /**
+ * Removes a dependency from the build
+ *
+ * @param string $name
+ * @return void
+ */
+ private function removeDependency(string $name): void
+ {
+ foreach($this->Dependencies as $key => $dep)
+ {
+ if($dep->Name == $name)
+ {
+ unset($this->Dependencies[$key]);
+ return;
+ }
+ }
+ }
+
/**
* Validates the package object and returns True if the package contains the correct information
*
diff --git a/src/ncc/Objects/Package/Component.php b/src/ncc/Objects/Package/Component.php
index b48708b..8a464e0 100644
--- a/src/ncc/Objects/Package/Component.php
+++ b/src/ncc/Objects/Package/Component.php
@@ -1,4 +1,24 @@
ExecutionPolicy = Functions::array_bc($data, 'execution_policy');
$object->Data = Functions::array_bc($data, 'data');
+ if($object->ExecutionPolicy !== null)
+ $object->ExecutionPolicy = ExecutionPolicy::fromArray($object->ExecutionPolicy);
+
return $object;
}
diff --git a/src/ncc/Objects/Package/Header.php b/src/ncc/Objects/Package/Header.php
index 34804de..b53779d 100644
--- a/src/ncc/Objects/Package/Header.php
+++ b/src/ncc/Objects/Package/Header.php
@@ -1,10 +1,31 @@
$this->CompilerExtension->toArray($bytecode),
+ ($bytecode ? Functions::cbc('compiler_extension') : 'compiler_extension') => $this->CompilerExtension->toArray(),
($bytecode ? Functions::cbc('runtime_constants') : 'runtime_constants') => $this->RuntimeConstants,
($bytecode ? Functions::cbc('compiler_version') : 'compiler_version') => $this->CompilerVersion,
+ ($bytecode ? Functions::cbc('update_source') : 'update_source') => ($this->UpdateSource?->toArray($bytecode) ?? null),
($bytecode ? Functions::cbc('options') : 'options') => $this->Options,
];
}
@@ -76,10 +105,13 @@
$object->CompilerExtension = Functions::array_bc($data, 'compiler_extension');
$object->RuntimeConstants = Functions::array_bc($data, 'runtime_constants');
$object->CompilerVersion = Functions::array_bc($data, 'compiler_version');
+ $object->UpdateSource = Functions::array_bc($data, 'update_source');
$object->Options = Functions::array_bc($data, 'options');
if($object->CompilerExtension !== null)
$object->CompilerExtension = Compiler::fromArray($object->CompilerExtension);
+ if($object->UpdateSource !== null)
+ $object->UpdateSource = UpdateSource::fromArray($object->UpdateSource);
return $object;
}
diff --git a/src/ncc/Objects/Package/Installer.php b/src/ncc/Objects/Package/Installer.php
index c368aac..bf949d0 100644
--- a/src/ncc/Objects/Package/Installer.php
+++ b/src/ncc/Objects/Package/Installer.php
@@ -1,4 +1,24 @@
Assembly->Package} to package lock file");
+
if(!isset($this->Packages[$package->Assembly->Package]))
{
$package_entry = new PackageEntry();
$package_entry->addVersion($package, $install_path, true);
$package_entry->Name = $package->Assembly->Package;
+ $package_entry->UpdateSource = $package->Header->UpdateSource;
+ $package_entry->getDataPath();
$this->Packages[$package->Assembly->Package] = $package_entry;
$this->update();
return;
}
- $this->Packages[$package->Assembly->Package]->addVersion($package, true);
+ $this->Packages[$package->Assembly->Package]->UpdateSource = $package->Header->UpdateSource;
+ $this->Packages[$package->Assembly->Package]->addVersion($package, $install_path, true);
+ $this->Packages[$package->Assembly->Package]->getDataPath();
$this->update();
}
@@ -82,6 +114,8 @@
*/
public function removePackageVersion(string $package, string $version): bool
{
+ Console::outVerbose(sprintf('Removing package %s version %s from package lock file', $package, $version));
+
if(isset($this->Packages[$package]))
{
$r = $this->Packages[$package]->removeVersion($version);
@@ -104,12 +138,15 @@
*
* @param string $package
* @return bool
+ * @noinspection PhpUnused
*/
public function removePackage(string $package): bool
{
+ Console::outVerbose(sprintf('Removing package %s from package lock file', $package));
if(isset($this->Packages[$package]))
{
unset($this->Packages[$package]);
+ $this->update();
return true;
}
@@ -124,6 +161,8 @@
*/
public function getPackage(string $package): ?PackageEntry
{
+ Console::outDebug(sprintf('getting package %s from package lock file', $package));
+
if(isset($this->Packages[$package]))
{
return $this->Packages[$package];
@@ -132,6 +171,40 @@
return null;
}
+ /**
+ * Determines if the requested package exists in the package lock
+ *
+ * @param string $package
+ * @param string|null $version
+ * @return bool
+ */
+ public function packageExists(string $package, ?string $version=null): bool
+ {
+ $package_entry = $this->getPackage($package);
+ if($package_entry == null)
+ return false;
+
+ if($version !== null)
+ {
+ try
+ {
+ $version_entry = $package_entry->getVersion($version);
+ }
+ catch (VersionNotFoundException $e)
+ {
+ unset($e);
+ return false;
+ }
+
+ if($version_entry == null)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* Returns an array of all packages and their installed versions
*
diff --git a/src/ncc/Objects/PackageLock/DependencyEntry.php b/src/ncc/Objects/PackageLock/DependencyEntry.php
index 82be14e..bbd1db2 100644
--- a/src/ncc/Objects/PackageLock/DependencyEntry.php
+++ b/src/ncc/Objects/PackageLock/DependencyEntry.php
@@ -1,4 +1,24 @@
LatestVersion;
+
foreach($this->Versions as $versionEntry)
{
if($versionEntry->Version == $version)
@@ -128,6 +166,9 @@
$version->MainExecutionPolicy = $package->MainExecutionPolicy;
$version->Location = $install_path;
+ foreach($version->ExecutionUnits as $unit)
+ $unit->Data = null;
+
foreach($package->Dependencies as $dependency)
{
$version->Dependencies[] = new DependencyEntry($dependency);
@@ -186,6 +227,24 @@
return $r;
}
+ /**
+ * @return string
+ * @throws InvalidPackageNameException
+ * @throws InvalidScopeException
+ */
+ public function getDataPath(): string
+ {
+ $path = PathFinder::getPackageDataPath($this->Name);
+
+ if(!file_exists($path) && Resolver::resolveScope() == Scopes::System)
+ {
+ $filesystem = new Filesystem();
+ $filesystem->mkdir($path);
+ }
+
+ return $path;
+ }
+
/**
* Returns an array representation of the object
*
@@ -204,6 +263,7 @@
($bytecode ? Functions::cbc('name') : 'name') => $this->Name,
($bytecode ? Functions::cbc('latest_version') : 'latest_version') => $this->LatestVersion,
($bytecode ? Functions::cbc('versions') : 'versions') => $versions,
+ ($bytecode ? Functions::cbc('update_source') : 'update_source') => ($this->UpdateSource?->toArray($bytecode) ?? null),
];
}
@@ -220,6 +280,10 @@
$object->Name = Functions::array_bc($data, 'name');
$object->LatestVersion = Functions::array_bc($data, 'latest_version');
$versions = Functions::array_bc($data, 'versions');
+ $object->UpdateSource = Functions::array_bc($data, 'update_source');
+
+ if($object->UpdateSource !== null)
+ $object->UpdateSource = UpdateSource::fromArray($object->UpdateSource);
if($versions !== null)
{
diff --git a/src/ncc/Objects/PackageLock/VersionEntry.php b/src/ncc/Objects/PackageLock/VersionEntry.php
index c4fe59f..a90a22b 100644
--- a/src/ncc/Objects/PackageLock/VersionEntry.php
+++ b/src/ncc/Objects/PackageLock/VersionEntry.php
@@ -1,4 +1,24 @@
$this->Version,
- ($bytecode ? Functions::cbc('compiler') : 'compiler') => $this->Compiler->toArray($bytecode),
+ ($bytecode ? Functions::cbc('compiler') : 'compiler') => $this->Compiler->toArray(),
($bytecode ? Functions::cbc('dependencies') : 'dependencies') => $dependencies,
($bytecode ? Functions::cbc('execution_units') : 'execution_units') => $execution_units,
($bytecode ? Functions::cbc('main_execution_policy') : 'main_execution_policy') => $this->MainExecutionPolicy,
diff --git a/src/ncc/Objects/PhpConfiguration.php b/src/ncc/Objects/PhpConfiguration.php
index e6d4e50..329ae8e 100644
--- a/src/ncc/Objects/PhpConfiguration.php
+++ b/src/ncc/Objects/PhpConfiguration.php
@@ -1,4 +1,24 @@
Build->Main !== null)
+ {
+ if($this->ExecutionPolicies == null || count($this->ExecutionPolicies) == 0)
+ {
+ if($throw_exception)
+ throw new UndefinedExecutionPolicyException(sprintf('Build configuration build.main uses an execution policy "%s" but no policies are defined', $this->Build->Main));
+ return false;
+ }
+
+
+ $found = false;
+ foreach($this->ExecutionPolicies as $policy)
+ {
+ if($policy->Name == $this->Build->Main)
+ {
+ $found = true;
+ break;
+ }
+ }
+
+ if(!$found)
+ {
+ if($throw_exception)
+ throw new UndefinedExecutionPolicyException(sprintf('Build configuration build.main points to a undefined execution policy "%s"', $this->Build->Main));
+ return false;
+ }
+
+ if($this->Build->Main == BuildConfigurationValues::AllConfigurations)
+ {
+ if($throw_exception)
+ throw new InvalidBuildConfigurationException(sprintf('Build configuration build.main cannot be set to "%s"', BuildConfigurationValues::AllConfigurations));
+ return false;
+ }
+ }
+
return true;
}
diff --git a/src/ncc/Objects/ProjectConfiguration/Assembly.php b/src/ncc/Objects/ProjectConfiguration/Assembly.php
index 46e4af4..ff2259f 100644
--- a/src/ncc/Objects/ProjectConfiguration/Assembly.php
+++ b/src/ncc/Objects/ProjectConfiguration/Assembly.php
@@ -1,4 +1,24 @@
Configurations = [];
}
+ /**
+ * Adds a new dependency to the build, if it doesn't already exist
+ *
+ * @param Dependency $dependency
+ * @return void
+ */
+ public function addDependency(Dependency $dependency): void
+ {
+ foreach($this->Dependencies as $dep)
+ {
+ if($dep->Name == $dependency->Name)
+ {
+ $this->removeDependency($dep->Name);
+ break;
+ }
+ }
+
+ $this->Dependencies[] = $dependency;
+ }
+
+ /**
+ * Removes a dependency from the build
+ *
+ * @param string $name
+ * @return void
+ */
+ private function removeDependency(string $name): void
+ {
+ foreach($this->Dependencies as $key => $dep)
+ {
+ if($dep->Name == $name)
+ {
+ unset($this->Dependencies[$key]);
+ return;
+ }
+ }
+ }
+
/**
* Validates the build configuration object
*
* @param bool $throw_exception
* @return bool
+ * @throws BuildConfigurationNotFoundException
+ * @throws InvalidBuildConfigurationException
* @throws InvalidConstantNameException
* @throws InvalidProjectBuildConfiguration
*/
public function validate(bool $throw_exception=True): bool
{
- // TODO: Implement validation for Configurations, Dependencies and ExcludedFiles
-
// Check the defined constants
foreach($this->DefineConstants as $name => $value)
{
@@ -140,6 +200,37 @@
}
}
+ foreach($this->Configurations as $configuration)
+ {
+ try
+ {
+ if (!$configuration->validate($throw_exception))
+ return false;
+ }
+ catch (InvalidBuildConfigurationException $e)
+ {
+ throw new InvalidBuildConfigurationException(sprintf('Error in build configuration \'%s\'', $configuration->Name), $e);
+ }
+ }
+
+ if($this->DefaultConfiguration == null)
+ {
+ if($throw_exception)
+ throw new InvalidProjectBuildConfiguration('The default build configuration is not set');
+
+ return false;
+ }
+
+ if(!Validate::nameFriendly($this->DefaultConfiguration))
+ {
+ if($throw_exception)
+ throw new InvalidProjectBuildConfiguration('The default build configuration name \'' . $this->DefaultConfiguration . '\' is not valid');
+
+ return false;
+ }
+
+ $this->getBuildConfiguration($this->DefaultConfiguration);
+
return true;
}
@@ -206,7 +297,7 @@
if($this->Scope !== null)
$ReturnResults[($bytecode ? Functions::cbc('scope') : 'scope')] = $this->Scope;
if($this->Main !== null)
- $ReturnResults[($bytecode ? Functions::cbc('main') : 'main')] = $this->Main;
+ $ReturnResults[($bytecode ? Functions::cbc('main') : 'main')] = $this->Main;
if($this->DefineConstants !== null && count($this->DefineConstants) > 0)
$ReturnResults[($bytecode ? Functions::cbc('define_constants') : 'define_constants')] = $this->DefineConstants;
if($this->PreBuild !== null && count($this->PreBuild) > 0)
diff --git a/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php b/src/ncc/Objects/ProjectConfiguration/Build/BuildConfiguration.php
similarity index 53%
rename from src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php
rename to src/ncc/Objects/ProjectConfiguration/Build/BuildConfiguration.php
index 8991c63..22ed725 100644
--- a/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php
+++ b/src/ncc/Objects/ProjectConfiguration/Build/BuildConfiguration.php
@@ -1,10 +1,34 @@
Dependencies = [];
}
- // TODO: Add a function to validate the object data
+ /**
+ * Validates the BuildConfiguration object
+ *
+ * @param bool $throw_exception
+ * @return bool
+ * @throws InvalidBuildConfigurationException
+ */
+ public function validate(bool $throw_exception=True): bool
+ {
+ if(!Validate::nameFriendly($this->Name))
+ {
+ if($throw_exception)
+ throw new InvalidBuildConfigurationException(sprintf('Invalid build configuration name "%s"', $this->Name));
+
+ return False;
+ }
+
+ if(!Validate::pathName($this->OutputPath))
+ {
+ if($throw_exception)
+ throw new InvalidBuildConfigurationException(sprintf('\'output_path\' contains an invalid path name in %s', $this->Name));
+
+ return False;
+ }
+
+ if($this->DefineConstants !== null && !is_array($this->DefineConstants))
+ {
+ if($throw_exception)
+ throw new InvalidBuildConfigurationException(sprintf('\'define_constants\' must be an array in %s', $this->Name));
+
+ return False;
+ }
+
+ if($this->ExcludeFiles !== null && !is_array($this->ExcludeFiles))
+ {
+ if($throw_exception)
+ throw new InvalidBuildConfigurationException(sprintf('\'exclude_files\' must be an array in %s', $this->Name));
+
+ return False;
+ }
+
+ if($this->PreBuild !== null && !is_array($this->PreBuild))
+ {
+ if($throw_exception)
+ throw new InvalidBuildConfigurationException(sprintf('\'pre_build\' must be an array in %s', $this->Name));
+
+ return False;
+ }
+
+ if($this->PostBuild !== null && !is_array($this->PostBuild))
+ {
+ if($throw_exception)
+ throw new InvalidBuildConfigurationException(sprintf('\'post_build\' must be an array in %s', $this->Name));
+
+ return False;
+ }
+
+ if($this->Dependencies !== null && !is_array($this->Dependencies))
+ {
+ if($throw_exception)
+ throw new InvalidBuildConfigurationException(sprintf('\'dependencies\' must be an array in %s', $this->Name));
+
+ return False;
+ }
+
+ /** @var Dependency $dependency */
+ foreach($this->Dependencies as $dependency)
+ {
+ try
+ {
+ if (!$dependency->validate($throw_exception))
+ return False;
+ }
+ catch (InvalidDependencyConfiguration $e)
+ {
+ if($throw_exception)
+ throw new InvalidBuildConfigurationException(sprintf('Invalid dependency configuration in %s: %s', $this->Name, $e->getMessage()));
+
+ return False;
+ }
+ }
+
+ return True;
+ }
/**
* Returns an array representation of the object
diff --git a/src/ncc/Objects/ProjectConfiguration/Compiler.php b/src/ncc/Objects/ProjectConfiguration/Compiler.php
index 9796150..64ce759 100644
--- a/src/ncc/Objects/ProjectConfiguration/Compiler.php
+++ b/src/ncc/Objects/ProjectConfiguration/Compiler.php
@@ -1,4 +1,24 @@
Extension !== null && strlen($this->Extension) > 0)
diff --git a/src/ncc/Objects/ProjectConfiguration/Dependency.php b/src/ncc/Objects/ProjectConfiguration/Dependency.php
index 3fa3116..46b6f8c 100644
--- a/src/ncc/Objects/ProjectConfiguration/Dependency.php
+++ b/src/ncc/Objects/ProjectConfiguration/Dependency.php
@@ -1,11 +1,32 @@
Name))
+ {
+ if($throw_exception)
+ throw new InvalidDependencyConfiguration(sprintf('Invalid dependency name "%s"', $this->Name));
+
+ return false;
+ }
+
+ if($this->Version !== null && !Validate::version($this->Version))
+ {
+ if($throw_exception)
+ throw new InvalidDependencyConfiguration(sprintf('Invalid dependency version "%s"', $this->Version));
+
+ return false;
+ }
+
+ return true;
+ }
}
\ No newline at end of file
diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php
index e0b0a58..efc175c 100644
--- a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php
+++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php
@@ -1,10 +1,29 @@
Name = Functions::array_bc($data, 'name');
$object->Runner = Functions::array_bc($data, 'runner');
$object->Message = Functions::array_bc($data, 'message');
- $object->Execute = Functions::array_bc($data, 'exec');
+ $object->Execute = Functions::array_bc($data, 'execute');
$object->ExitHandlers = Functions::array_bc($data, 'exit_handlers');
if($object->Execute !== null)
diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php
index 8fe5b02..6839d88 100644
--- a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php
+++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php
@@ -1,4 +1,24 @@
Tty = false;
$this->Silent = false;
$this->Timeout = null;
+ $this->IdleTimeout = null;
$this->WorkingDirectory = "%CWD%";
}
@@ -82,6 +115,9 @@
if($this->Options !== null)
$results[($bytecode ? Functions::cbc("options") : "options")] = $this->Options;
+ if($this->EnvironmentVariables !== null)
+ $results[($bytecode ? Functions::cbc("environment_variables") : "environment_variables")] = $this->EnvironmentVariables;
+
if($this->Silent !== null)
$results[($bytecode ? Functions::cbc("silent") : "silent")] = (bool)$this->Silent;
@@ -91,6 +127,9 @@
if($this->Timeout !== null)
$results[($bytecode ? Functions::cbc("timeout") : "timeout")] = (int)$this->Timeout;
+ if($this->IdleTimeout !== null)
+ $results[($bytecode ? Functions::cbc("idle_timeout") : "idle_timeout")] = (int)$this->IdleTimeout;
+
return $results;
}
@@ -107,9 +146,11 @@
$object->Target = Functions::array_bc($data, 'target');
$object->WorkingDirectory = Functions::array_bc($data, 'working_directory');
$object->Options = Functions::array_bc($data, 'options');
+ $object->EnvironmentVariables = Functions::array_bc($data, 'environment_variables');
$object->Silent = Functions::array_bc($data, 'silent');
$object->Tty = Functions::array_bc($data, 'tty');
$object->Timeout = Functions::array_bc($data, 'timeout');
+ $object->IdleTimeout = Functions::array_bc($data, 'idle_timeout');
return $object;
}
diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php
index 98ea264..176858f 100644
--- a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php
+++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php
@@ -1,4 +1,24 @@
Compiler->toArray($bytecode);
+ $ReturnResults[($bytecode ? Functions::cbc('compiler') : 'compiler')] = $this->Compiler->toArray();
$ReturnResults[($bytecode ? Functions::cbc('options') : 'options')] = $this->Options;
+ if($this->UpdateSource !== null)
+ $ReturnResults[($bytecode ? Functions::cbc('update_source') : 'update_source')] = $this->UpdateSource->toArray($bytecode);
+
return $ReturnResults;
}
@@ -89,6 +117,11 @@
$ProjectObject->Options = Functions::array_bc($data, 'options');
}
+ if(Functions::array_bc($data, 'update_source') !== null)
+ {
+ $ProjectObject->UpdateSource = UpdateSource::fromArray(Functions::array_bc($data, 'update_source'));
+ }
+
return $ProjectObject;
}
}
\ No newline at end of file
diff --git a/src/ncc/Objects/ProjectConfiguration/UpdateSource.php b/src/ncc/Objects/ProjectConfiguration/UpdateSource.php
index b9344e7..1cb0c97 100644
--- a/src/ncc/Objects/ProjectConfiguration/UpdateSource.php
+++ b/src/ncc/Objects/ProjectConfiguration/UpdateSource.php
@@ -1,8 +1,78 @@
$this->Source,
+ ($bytecode ? Functions::cbc('repository') : 'repository') => ($this->Repository?->toArray($bytecode))
+ ];
+ }
+
+
+ /**
+ * Constructs object from an array representation
+ *
+ * @param array $data
+ * @return UpdateSource
+ */
+ public static function fromArray(array $data): UpdateSource
+ {
+ $obj = new UpdateSource();
+ $obj->Source = Functions::array_bc($data, 'source');
+ $obj->Repository = Functions::array_bc($data, 'repository');
+
+ if($obj->Repository !== null)
+ $obj->Repository = Repository::fromArray($obj->Repository);
+
+ return $obj;
+ }
}
\ No newline at end of file
diff --git a/src/ncc/Objects/ProjectConfiguration/UpdateSource/Repository.php b/src/ncc/Objects/ProjectConfiguration/UpdateSource/Repository.php
new file mode 100644
index 0000000..395e215
--- /dev/null
+++ b/src/ncc/Objects/ProjectConfiguration/UpdateSource/Repository.php
@@ -0,0 +1,90 @@
+ $this->Name,
+ ($bytecode ? Functions::cbc('type') : 'type') => $this->Type,
+ ($bytecode ? Functions::cbc('host') : 'host') => $this->Host,
+ ($bytecode ? Functions::cbc('ssl') : 'ssl') => $this->SSL
+ ];
+ }
+
+ /**
+ * Constructs object from an array representation
+ *
+ * @param array $data
+ * @return Repository
+ */
+ public static function fromArray(array $data): self
+ {
+ $obj = new self();
+ $obj->Name = Functions::array_bc($data, 'name');
+ $obj->Type = Functions::array_bc($data, 'type');
+ $obj->Host = Functions::array_bc($data, 'host');
+ $obj->SSL = Functions::array_bc($data, 'ssl');
+ return $obj;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Objects/ProjectDetectionResults.php b/src/ncc/Objects/ProjectDetectionResults.php
new file mode 100644
index 0000000..b5bb92e
--- /dev/null
+++ b/src/ncc/Objects/ProjectDetectionResults.php
@@ -0,0 +1,78 @@
+Files = new Files();
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Objects/RepositoryQueryResults/Files.php b/src/ncc/Objects/RepositoryQueryResults/Files.php
new file mode 100644
index 0000000..bc11788
--- /dev/null
+++ b/src/ncc/Objects/RepositoryQueryResults/Files.php
@@ -0,0 +1,70 @@
+ExecutionPolicyName = 'main';
+ $this->Registered = false;
+ }
+
+ /**
+ * Returns a string representation of the object
+ *
+ * @param bool $bytecode
+ * @return array
+ */
+ public function toArray(bool $bytecode=false): array
+ {
+ return [
+ ($bytecode ? Functions::cbc('package') : 'package') => $this->Package,
+ ($bytecode ? Functions::cbc('registered') : 'registered') => $this->Registered,
+ ($bytecode ? Functions::cbc('execution_policy_name') : 'execution_policy_name') => $this->ExecutionPolicyName
+ ];
+ }
+
+ /**
+ * Constructs a new SymlinkEntry from an array representation
+ *
+ * @param array $data
+ * @return SymlinkEntry
+ */
+ public static function fromArray(array $data): SymlinkEntry
+ {
+ $entry = new SymlinkEntry();
+
+ $entry->Package = Functions::array_bc($data, 'package');
+ $entry->Registered = (bool)Functions::array_bc($data, 'registered');
+ $entry->ExecutionPolicyName = Functions::array_bc($data, 'execution_policy_name');
+
+ return $entry;
+ }
+
+ }
\ No newline at end of file
diff --git a/src/ncc/Objects/Vault.php b/src/ncc/Objects/Vault.php
index 8a60105..f3e9517 100644
--- a/src/ncc/Objects/Vault.php
+++ b/src/ncc/Objects/Vault.php
@@ -1,9 +1,35 @@
Version = Versions::CredentialsStoreVersion;
$this->Entries = [];
}
+ /**
+ * Adds a new entry to the vault
+ *
+ * @param string $name
+ * @param PasswordInterface $password
+ * @param bool $encrypt
+ * @return bool
+ * @noinspection PhpUnused
+ */
+ public function addEntry(string $name, PasswordInterface $password, bool $encrypt=true): bool
+ {
+ // Check if the entry already exists
+ foreach($this->Entries as $entry)
+ {
+ if($entry->getName() === $name)
+ return false;
+ }
+
+ // Create the new entry
+ $entry = new Entry();
+ $entry->setName($name);
+ $entry->setEncrypted($encrypt);
+ $entry->setAuthentication($password);
+
+ // Add the entry to the vault
+ $this->Entries[] = $entry;
+ return true;
+ }
+
+ /**
+ * Deletes an entry from the vault
+ *
+ * @param string $name
+ * @return bool
+ * @noinspection PhpUnused
+ */
+ public function deleteEntry(string $name): bool
+ {
+ // Find the entry
+ foreach($this->Entries as $index => $entry)
+ {
+ if($entry->getName() === $name)
+ {
+ // Remove the entry
+ unset($this->Entries[$index]);
+ return true;
+ }
+ }
+
+ // Entry not found
+ return false;
+ }
+
+ /**
+ * Returns all the entries in the vault
+ *
+ * @return array|Entry[]
+ * @noinspection PhpUnused
+ */
+ public function getEntries(): array
+ {
+ return $this->Entries;
+ }
+
+ /**
+ * Returns an existing entry from the vault
+ *
+ * @param string $name
+ * @return Entry|null
+ */
+ public function getEntry(string $name): ?Entry
+ {
+ foreach($this->Entries as $entry)
+ {
+ if($entry->getName() === $name)
+ return $entry;
+ }
+
+ return null;
+ }
+
+ /**
+ * Authenticates an entry in the vault
+ *
+ * @param string $name
+ * @param string $password
+ * @return bool
+ * @throws RuntimeException
+ * @noinspection PhpUnused
+ */
+ public function authenticate(string $name, string $password): bool
+ {
+ $entry = $this->getEntry($name);
+ if($entry === null)
+ return false;
+
+ if($entry->getPassword() === null)
+ {
+ if($entry->isEncrypted() && !$entry->isCurrentlyDecrypted())
+ {
+ return $entry->unlock($password);
+ }
+ }
+
+ $input = [];
+ switch($entry->getPassword()->getAuthenticationType())
+ {
+ case AuthenticationType::UsernamePassword:
+ $input = ['password' => $password];
+ break;
+ case AuthenticationType::AccessToken:
+ $input = ['token' => $password];
+ break;
+ }
+
+ return $entry->authenticate($input);
+ }
+
/**
* Returns an array representation of the object
*
+ * @param bool $bytecode
* @return array
*/
- public function toArray(): array
+ public function toArray(bool $bytecode=false): array
{
- $Entries = [];
-
+ $entries = [];
foreach($this->Entries as $entry)
{
- $Entries[] = $entry->toArray();
+ $entries[] = $entry->toArray($bytecode);
}
return [
- 'version' => $this->Version,
- 'entries' => $Entries
+ ($bytecode ? Functions::cbc('version') : 'version') => $this->Version,
+ ($bytecode ? Functions::cbc('entries') : 'entries') => $entries,
];
}
/**
- * Constructs an object from an array representation
+ * Constructs a new object from an array
*
- * @param array $data
+ * @param array $array
* @return Vault
*/
- public static function fromArray(array $data): Vault
+ public static function fromArray(array $array): Vault
{
- $VaultObject = new Vault();
+ $vault = new Vault();
+ $vault->Version = Functions::array_bc($array, 'version');
+ $entries = Functions::array_bc($array, 'entries');
+ $vault->Entries = [];
- if(isset($data['version']))
- $VaultObject->Version = $data['version'];
+ foreach($entries as $entry)
+ $vault->Entries[] = Entry::fromArray($entry);
- if(isset($data['entries']))
- {
- foreach($data['entries'] as $entry)
- {
- $VaultObject->Entries[] = Entry::fromArray($entry);
- }
- }
-
- return $VaultObject;
+ return $vault;
}
+
}
\ No newline at end of file
diff --git a/src/ncc/Objects/Vault/DefaultEntry.php b/src/ncc/Objects/Vault/DefaultEntry.php
deleted file mode 100644
index c834f0d..0000000
--- a/src/ncc/Objects/Vault/DefaultEntry.php
+++ /dev/null
@@ -1,60 +0,0 @@
- $this->Alias,
- 'source' => $this->Source
- ];
- }
-
- /**
- * Constructs the object from an array representation
- *
- * @param array $data
- * @return DefaultEntry
- */
- public static function fromArray(array $data): DefaultEntry
- {
- $DefaultEntryObject = new DefaultEntry();
-
- if(isset($data['alias']))
- {
- $DefaultEntryObject->Alias = $data['alias'];
- }
-
- if(isset($data['source']))
- {
- $DefaultEntryObject->Source = $data['source'];
- }
-
- return $DefaultEntryObject;
- }
-
- }
\ No newline at end of file
diff --git a/src/ncc/Objects/Vault/Entry.php b/src/ncc/Objects/Vault/Entry.php
index 27c778f..4445ca7 100644
--- a/src/ncc/Objects/Vault/Entry.php
+++ b/src/ncc/Objects/Vault/Entry.php
@@ -1,127 +1,355 @@
Encrypted = true;
+ $this->IsCurrentlyDecrypted = true;
+ }
+
+ /**
+ * Test Authenticates the entry
+ *
+ * For UsernamePassword the $input parameter expects an array with the keys 'username' and 'password'
+ * For AccessToken the $input parameter expects an array with the key 'token'
+ *
+ * @param array $input
+ * @return bool
+ * @noinspection PhpUnused
+ */
+ public function authenticate(array $input): bool
+ {
+ if(!$this->IsCurrentlyDecrypted)
+ return false;
+
+ if($this->Password == null)
+ return false;
+
+ switch($this->Password->getAuthenticationType())
+ {
+ case AuthenticationType::UsernamePassword:
+ if(!($this->Password instanceof UsernamePassword))
+ return false;
+
+ $username = $input['username'] ?? null;
+ $password = $input['password'] ?? null;
+
+ if($username === null && $password === null)
+ return false;
+
+ if($username == null)
+ return $password == $this->Password->getPassword();
+
+ if($password == null)
+ return $username == $this->Password->getUsername();
+
+ return $username == $this->Password->getUsername() && $password == $this->Password->getPassword();
+
+ case AuthenticationType::AccessToken:
+ if(!($this->Password instanceof AccessToken))
+ return false;
+
+ $token = $input['token'] ?? null;
+
+ if($token === null)
+ return false;
+
+ return $token == $this->Password->AccessToken;
+
+ default:
+ return false;
+
+ }
+ }
+
+ /**
+ * @param PasswordInterface $password
+ * @return void
+ */
+ public function setAuthentication(PasswordInterface $password): void
+ {
+ $this->Password = $password;
+ }
+
+ /**
+ * @return bool
+ * @noinspection PhpUnused
+ */
+ public function isCurrentlyDecrypted(): bool
+ {
+ return $this->IsCurrentlyDecrypted;
+ }
+
+ /**
+ * Locks the entry by encrypting the password
+ *
+ * @return bool
+ */
+ public function lock(): bool
+ {
+ if($this->Password == null)
+ return false;
+
+ if($this->Encrypted)
+ return false;
+
+ if(!$this->IsCurrentlyDecrypted)
+ return false;
+
+ if(!($this->Password instanceof PasswordInterface))
+ return false;
+
+ $this->Password = $this->encrypt();
+ return true;
+ }
+
+ /**
+ * Unlocks the entry by decrypting the password
+ *
+ * @param string $password
+ * @return bool
+ * @throws RuntimeException
+ * @noinspection PhpUnused
+ */
+ public function unlock(string $password): bool
+ {
+ if($this->Password == null)
+ return false;
+
+ if(!$this->Encrypted)
+ return false;
+
+ if($this->IsCurrentlyDecrypted)
+ return false;
+
+ if(!is_string($this->Password))
+ return false;
+
+ try
+ {
+ $password = Crypto::decryptWithPassword($this->Password, $password, true);
+ }
+ catch (EnvironmentIsBrokenException $e)
+ {
+ throw new RuntimeException('Cannot decrypt password', $e);
+ }
+ catch (WrongKeyOrModifiedCiphertextException $e)
+ {
+ unset($e);
+ return false;
+ }
+
+ $this->Password = ZiProto::decode($password);
+ $this->IsCurrentlyDecrypted = true;
+
+ return true;
+ }
+
+ /**
+ * Returns the password object as an encrypted binary string
+ *
+ * @return string|null
+ */
+ private function encrypt(): ?string
+ {
+ if(!$this->IsCurrentlyDecrypted)
+ return false;
+
+ if($this->Password == null)
+ return false;
+
+ if(!($this->Password instanceof PasswordInterface))
+ return null;
+
+ $data = ZiProto::encode($this->Password->toArray(true));
+ return Crypto::encryptWithPassword($data, (string)$this->Password, true);
+ }
+
+ /**
+ * Returns an array representation of the object
+ *
+ * @param bool $bytecode
+ * @return array
+ */
+ public function toArray(bool $bytecode=false): array
+ {
+ if($this->Password !== null)
+ {
+ if($this->Encrypted && $this->IsCurrentlyDecrypted)
+ {
+ $password = $this->encrypt();
+ }
+ elseif($this->Encrypted)
+ {
+ $password = $this->Password;
+ }
+ else
+ {
+ $password = $this->Password->toArray(true);
+ }
+ }
+ else
+ {
+ $password = $this->Password;
+ }
+
return [
- 'alias' => $this->Alias,
- 'source' => $this->Source,
- 'source_host' => $this->SourceHost,
- 'authentication_type' => $this->AuthenticationType,
- 'encrypted' => $this->Encrypted,
- 'authentication' => $this->Authentication
+ ($bytecode ? Functions::cbc('name') : 'name') => $this->Name,
+ ($bytecode ? Functions::cbc('encrypted') : 'encrypted') => $this->Encrypted,
+ ($bytecode ? Functions::cbc('password') : 'password') => $password,
];
}
/**
- * Returns an array representation of the object
+ * Constructs an object from an array representation
*
* @param array $data
* @return Entry
*/
- public static function fromArray(array $data): Entry
+ public static function fromArray(array $data): self
{
- $EntryObject = new Entry();
+ $self = new self();
- if(isset($data['alias']))
+ $self->Name = Functions::array_bc($data, 'name');
+ $self->Encrypted = Functions::array_bc($data, 'encrypted');
+ $password = Functions::array_bc($data, 'password');
+
+ if($password !== null)
{
- $EntryObject->Alias = $data['alias'];
+ if($self->Encrypted)
+ {
+ $self->Password = $password;
+ $self->IsCurrentlyDecrypted = false;
+ }
+ elseif(gettype($password) == 'array')
+ {
+ $self->Password = match (Functions::array_bc($password, 'authentication_type'))
+ {
+ AuthenticationType::UsernamePassword => UsernamePassword::fromArray($password),
+ AuthenticationType::AccessToken => AccessToken::fromArray($password)
+ };
+ }
}
- if(isset($data['source']))
- {
- $EntryObject->Source = $data['source'];
- }
+ return $self;
+ }
- if(isset($data['source_host']))
- {
- $EntryObject->SourceHost = $data['source_host'];
- }
+ /**
+ * @return bool
+ */
+ public function isEncrypted(): bool
+ {
+ return $this->Encrypted;
+ }
- if(isset($data['authentication_type']))
- {
- $EntryObject->AuthenticationType = $data['authentication_type'];
- }
+ /**
+ * Returns false if the entry needs to be decrypted first
+ *
+ * @param bool $Encrypted
+ * @return bool
+ */
+ public function setEncrypted(bool $Encrypted): bool
+ {
+ if(!$this->IsCurrentlyDecrypted)
+ return false;
- if(isset($data['encrypted']))
- {
- $EntryObject->Encrypted = $data['encrypted'];
- }
+ $this->Encrypted = $Encrypted;
+ return true;
+ }
- if(isset($data['authentication']))
- {
- $EntryObject->Authentication = $data['authentication'];
- }
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->Name;
+ }
- return $EntryObject;
+ /**
+ * @param string $Name
+ */
+ public function setName(string $Name): void
+ {
+ $this->Name = $Name;
+ }
+
+ /**
+ * @return PasswordInterface|null
+ */
+ public function getPassword(): ?PasswordInterface
+ {
+ if(!$this->IsCurrentlyDecrypted)
+ return null;
+
+ return $this->Password;
}
}
\ No newline at end of file
diff --git a/src/ncc/Objects/Vault/Password/AccessToken.php b/src/ncc/Objects/Vault/Password/AccessToken.php
new file mode 100644
index 0000000..66b2d69
--- /dev/null
+++ b/src/ncc/Objects/Vault/Password/AccessToken.php
@@ -0,0 +1,102 @@
+ AuthenticationType::AccessToken,
+ ($bytecode ? Functions::cbc('access_token') : 'access_token') => $this->AccessToken,
+ ];
+ }
+
+ /**
+ * Constructs an object from an array representation
+ *
+ * @param array $data
+ * @return static
+ */
+ public static function fromArray(array $data): self
+ {
+ $object = new self();
+
+ $object->AccessToken = Functions::array_bc($data, 'access_token');
+
+ return $object;
+ }
+
+ /**
+ * @return string
+ */
+ public function getAccessToken(): string
+ {
+ return $this->AccessToken;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getAuthenticationType(): string
+ {
+ return AuthenticationType::AccessToken;
+ }
+
+ /**
+ * Returns a string representation of the object
+ *
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->AccessToken;
+ }
+
+ /**
+ * @param string $AccessToken
+ */
+ public function setAccessToken(string $AccessToken): void
+ {
+ $this->AccessToken = $AccessToken;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Objects/Vault/Password/UsernamePassword.php b/src/ncc/Objects/Vault/Password/UsernamePassword.php
new file mode 100644
index 0000000..de11f39
--- /dev/null
+++ b/src/ncc/Objects/Vault/Password/UsernamePassword.php
@@ -0,0 +1,129 @@
+ AuthenticationType::UsernamePassword,
+ ($bytecode ? Functions::cbc('username') : 'username') => $this->Username,
+ ($bytecode ? Functions::cbc('password') : 'password') => $this->Password,
+ ];
+ }
+
+ /**
+ * Constructs an object from an array representation
+ *
+ * @param array $data
+ * @return static
+ */
+ public static function fromArray(array $data): self
+ {
+ $instance = new self();
+
+ $instance->Username = Functions::array_bc($data, 'username');
+ $instance->Password = Functions::array_bc($data, 'password');
+
+ return $instance;
+ }
+
+ /**
+ * @return string
+ * @noinspection PhpUnused
+ */
+ public function getUsername(): string
+ {
+ return $this->Username;
+ }
+
+ /**
+ * @return string
+ * @noinspection PhpUnused
+ */
+ public function getPassword(): string
+ {
+ return $this->Password;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getAuthenticationType(): string
+ {
+ return AuthenticationType::UsernamePassword;
+ }
+
+ /**
+ * Returns a string representation of the object
+ *
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->Password;
+ }
+
+ /**
+ * @param string $Username
+ */
+ public function setUsername(string $Username): void
+ {
+ $this->Username = $Username;
+ }
+
+ /**
+ * @param string $Password
+ */
+ public function setPassword(string $Password): void
+ {
+ $this->Password = $Password;
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Runtime.php b/src/ncc/Runtime.php
new file mode 100644
index 0000000..b9ab941
--- /dev/null
+++ b/src/ncc/Runtime.php
@@ -0,0 +1,228 @@
+getPackage($package)->getLatestVersion();
+
+ $entry = "$package=$version";
+
+ return isset(self::$imported_packages[$entry]);
+ }
+
+ /**
+ * Adds a package to the imported packages list
+ *
+ * @param string $package
+ * @param string $version
+ * @return void
+ */
+ private static function addImport(string $package, string $version): void
+ {
+ $entry = "$package=$version";
+ self::$imported_packages[$entry] = true;
+ }
+
+
+ /**
+ * @param string $package
+ * @param string $version
+ * @param array $options
+ * @return void
+ * @throws ImportException
+ */
+ public static function import(string $package, string $version=Versions::Latest, array $options=[]): void
+ {
+ try
+ {
+ $package_entry = self::getPackageManager()->getPackage($package);
+ }
+ catch (PackageLockException $e)
+ {
+ throw new ImportException(sprintf('Failed to import package "%s" due to a package lock exception: %s', $package, $e->getMessage()), $e);
+ }
+ if($package_entry == null)
+ {
+ throw new ImportException(sprintf("Package '%s' not found", $package));
+ }
+
+ if($version == Versions::Latest)
+ $version = $package_entry->getLatestVersion();
+
+ try
+ {
+ /** @var VersionEntry $version_entry */
+ $version_entry = $package_entry->getVersion($version);
+
+ if($version_entry == null)
+ throw new VersionNotFoundException();
+ }
+ catch (VersionNotFoundException $e)
+ {
+ throw new ImportException(sprintf('Version %s of %s is not installed', $version, $package), $e);
+ }
+
+ try
+ {
+ if (self::isImported($package, $version))
+ return;
+ }
+ catch (PackageLockException $e)
+ {
+ throw new ImportException(sprintf('Failed to check if package %s is imported', $package), $e);
+ }
+
+ if($version_entry->Dependencies !== null && count($version_entry->Dependencies) > 0)
+ {
+ // Import all dependencies first
+ /** @var Dependency $dependency */
+ foreach($version_entry->Dependencies as $dependency)
+ self::import($dependency->PackageName, $dependency->Version, $options);
+ }
+
+ try
+ {
+ switch($version_entry->Compiler->Extension)
+ {
+ case CompilerExtensions::PHP:
+ PhpRuntime::import($version_entry, $options);
+ break;
+
+ default:
+ throw new ImportException(sprintf('Compiler extension %s is not supported in this runtime', $version_entry->Compiler->Extension));
+ }
+ }
+ catch(Exception $e)
+ {
+ throw new ImportException(sprintf('Failed to import package %s', $package), $e);
+ }
+
+ self::addImport($package, $version);
+ }
+
+ /**
+ * Returns the data path of the package
+ *
+ * @param string $package
+ * @return string
+ * @throws Exceptions\InvalidPackageNameException
+ * @throws Exceptions\InvalidScopeException
+ * @throws PackageLockException
+ * @throws PackageNotFoundException
+ */
+ public static function getDataPath(string $package): string
+ {
+ $package = self::getPackageManager()->getPackage($package);
+
+ if($package == null)
+ throw new PackageNotFoundException('Package not found (null entry error, possible bug)');
+
+ return $package->getDataPath();
+ }
+
+ /**
+ * @return PackageManager
+ */
+ private static function getPackageManager(): PackageManager
+ {
+ if(self::$package_manager == null)
+ self::$package_manager = new PackageManager();
+ return self::$package_manager;
+ }
+
+ /**
+ * Returns an array of all the packages that is currently imported
+ *
+ * @return array
+ */
+ public static function getImportedPackages(): array
+ {
+ return array_keys(self::$imported_packages);
+ }
+
+ /**
+ * Returns a registered constant
+ *
+ * @param string $package
+ * @param string $name
+ * @return string|null
+ */
+ public static function getConstant(string $package, string $name): ?string
+ {
+ return Constants::get($package, $name);
+ }
+
+ /**
+ * Registers a new constant
+ *
+ * @param string $package
+ * @param string $name
+ * @param string $value
+ * @return void
+ * @throws ConstantReadonlyException
+ * @throws InvalidConstantNameException
+ */
+ public static function setConstant(string $package, string $name, string $value): void
+ {
+ Constants::register($package, $name, $value);
+ }
+ }
\ No newline at end of file
diff --git a/src/ncc/Runtime/Constants.php b/src/ncc/Runtime/Constants.php
index 312b443..2b8195a 100644
--- a/src/ncc/Runtime/Constants.php
+++ b/src/ncc/Runtime/Constants.php
@@ -1,4 +1,24 @@
getValue();
+
+ return null;
+ }
}
\ No newline at end of file
diff --git a/src/ncc/Utilities/Base64.php b/src/ncc/Utilities/Base64.php
index 9c75af3..d827ea5 100644
--- a/src/ncc/Utilities/Base64.php
+++ b/src/ncc/Utilities/Base64.php
@@ -1,6 +1,26 @@
self::$largestTickLength)
+ {
self::$largestTickLength = strlen($tick_time);
+ }
+
if(strlen($tick_time) < self::$largestTickLength)
+ {
/** @noinspection PhpRedundantOptionalArgumentInspection */
$tick_time = str_pad($tick_time, (strlen($tick_time) + (self::$largestTickLength - strlen($tick_time))), ' ', STR_PAD_RIGHT);
+ }
- return '[' . $tick_time . ' - ' . date('TH:i:sP') . '] ' . $input;
+ $fmt_tick = $tick_time;
+ if(self::$lastTickTime !== null)
+ {
+ $timeDiff = microtime(true) - self::$lastTickTime;
+
+ if ($timeDiff > 1.0)
+ {
+ $fmt_tick = Console::formatColor($tick_time, ConsoleColors::LightRed);
+ }
+ elseif ($timeDiff > 0.5)
+ {
+ $fmt_tick = Console::formatColor($tick_time, ConsoleColors::LightYellow);
+ }
+ }
+
+ self::$lastTickTime = $tick_time;
+ return '[' . $fmt_tick . '] ' . $input;
}
/**
@@ -179,6 +229,14 @@
$trace_msg .= ' > ';
}
+ /** Apply syntax highlighting using regular expressions */
+
+ // Hyperlinks
+ $message = preg_replace('/(https?:\/\/[^\s]+)/', Console::formatColor('$1', ConsoleColors::LightBlue), $message);
+
+ // File Paths
+ $message = preg_replace('/(\/[^\s]+)/', Console::formatColor('$1', ConsoleColors::LightCyan), $message);
+
/** @noinspection PhpUnnecessaryStringCastInspection */
$message = self::setPrefix(LogLevel::Debug, (string)$trace_msg . $message);
@@ -313,16 +371,20 @@
* Prints out a detailed exception display (unfinished)
*
* @param Exception $e
+ * @param bool $sub
* @return void
*/
- private static function outExceptionDetails(Exception $e): void
+ private static function outExceptionDetails(Throwable $e, bool $sub=false): void
{
if(!ncc::cliMode())
return;
+ // Exception name without namespace
+
$trace_header = self::formatColor($e->getFile() . ':' . $e->getLine(), ConsoleColors::Magenta);
- $trace_error = self::formatColor('error: ', ConsoleColors::Red);
+ $trace_error = self::formatColor( 'Error: ', ConsoleColors::Red);
self::out($trace_header . ' ' . $trace_error . $e->getMessage());
+ self::out(sprintf('Exception: %s', get_class($e)));
self::out(sprintf('Error code: %s', $e->getCode()));
$trace = $e->getTrace();
if(count($trace) > 1)
@@ -334,7 +396,16 @@
}
}
- if(Main::getArgs() !== null)
+ if($e->getPrevious() !== null)
+ {
+ // Check if previous is the same as the current
+ if($e->getPrevious()->getMessage() !== $e->getMessage())
+ {
+ self::outExceptionDetails($e->getPrevious(), true);
+ }
+ }
+
+ if(Main::getArgs() !== null && !$sub)
{
if(isset(Main::getArgs()['dbg-ex']))
{
@@ -425,4 +496,53 @@
}
}
}
+
+ /**
+ * Prompts for a password input while hiding the user's password
+ *
+ * @param string $prompt
+ * @return string|null
+ */
+ public static function passwordInput(string $prompt): ?string
+ {
+ if(!ncc::cliMode())
+ return null;
+
+ // passwordInput() is not properly implemented yet, defaulting to prompt
+ return self::getInput($prompt);
+
+ /**
+ $executable_finder = new ExecutableFinder();
+ $bash_path = $executable_finder->find('bash');
+
+ if($bash_path == null)
+ {
+ self::outWarning('Unable to find bash executable, cannot hide password input');
+ return self::getInput($prompt);
+ }
+
+ $prompt = escapeshellarg($prompt);
+ $random = Functions::randomString(10);
+ $command = "$bash_path -c 'read -s -p $prompt $random && echo \$" . $random . "'";
+ $password = rtrim(shell_exec($command));
+ self::out((string)null);
+ return $password;
+ **/
+ }
+
+ /**
+ * @param array $sections
+ * @return void
+ */
+ public static function outHelpSections(array $sections): void
+ {
+ if(!ncc::cliMode())
+ return;
+
+ $padding = Functions::detectParametersPadding($sections);
+
+ foreach($sections as $section)
+ Console::out(' ' . $section->toString($padding));
+ }
+
}
\ No newline at end of file
diff --git a/src/ncc/Utilities/Functions.php b/src/ncc/Utilities/Functions.php
index fa4096c..863520b 100644
--- a/src/ncc/Utilities/Functions.php
+++ b/src/ncc/Utilities/Functions.php
@@ -1,26 +1,75 @@
Runner)) {
+ Runners::bash => BashRunner::processUnit($path, $policy),
Runners::php => PhpRunner::processUnit($path, $policy),
- default => throw new UnsupportedRunnerException('The runner \'' . $policy->Runner . '\' is not supported'),
+ Runners::perl => PerlRunner::processUnit($path, $policy),
+ Runners::python => PythonRunner::processUnit($path, $policy),
+ Runners::python2 => Python2Runner::processUnit($path, $policy),
+ Runners::python3 => Python3Runner::processUnit($path, $policy),
+ Runners::lua => LuaRunner::processUnit($path, $policy),
+ default => throw new RunnerExecutionException('The runner \'' . $policy->Runner . '\' is not supported'),
};
}
@@ -268,7 +323,7 @@
* @param Exception $e
* @return array
*/
- public static function exceptionToArray(Exception $e): array
+ public static function exceptionToArray(Throwable $e): array
{
$exception = [
'message' => $e->getMessage(),
@@ -281,7 +336,7 @@
if($e->getPrevious() !== null)
{
- $exception['trace'] = self::exceptionToArray($e);
+ $exception['previous'] = self::exceptionToArray($e->getPrevious());
}
return $exception;
@@ -376,6 +431,7 @@
* @throws AccessDeniedException
* @throws FileNotFoundException
* @throws IOException
+ * @noinspection PhpUnused
*/
public static function loadComposerJson(string $path): ComposerJson
{
@@ -430,6 +486,7 @@
*
* @param string $property
* @return mixed|null
+ * @noinspection PhpMissingReturnTypeInspection
*/
public static function getConfigurationProperty(string $property)
{
@@ -452,4 +509,484 @@
return Parser::parse($version)->toString();
}
+
+ /**
+ * Returns a random string
+ *
+ * @param int $length
+ * @return string
+ */
+ public static function randomString(int $length = 32): string
+ {
+ $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ $charactersLength = strlen($characters);
+ $randomString = '';
+ for ($i = 0; $i < $length; $i++)
+ {
+ $randomString .= $characters[rand(0, $charactersLength - 1)];
+ }
+ return $randomString;
+ }
+
+ /**
+ * Returns a path to a temporary directory
+ *
+ * @param bool $create
+ * @param bool $set_as_tmp
+ * @return string
+ * @throws InvalidScopeException
+ */
+ public static function getTmpDir(bool $create=true, bool $set_as_tmp=true): string
+ {
+ $path = PathFinder::getCachePath() . DIRECTORY_SEPARATOR . self::randomString(16);
+ if($create)
+ {
+ $filesystem = new Filesystem();
+ /** @noinspection PhpRedundantOptionalArgumentInspection */
+ $filesystem->mkdir($path, 0777);
+ }
+ if($set_as_tmp)
+ RuntimeCache::setFileAsTemporary($path);
+ return $path;
+ }
+
+ /**
+ * Applies the authentication to the given HTTP request.
+ *
+ * @param HttpRequest $httpRequest
+ * @param Entry|null $entry
+ * @param bool $expect_json
+ * @return HttpRequest
+ * @throws AuthenticationException
+ * @throws GitlabServiceException
+ */
+ public static function prepareGitServiceRequest(HttpRequest $httpRequest, ?Entry $entry=null, bool $expect_json=true): HttpRequest
+ {
+ if($entry !== null)
+ {
+ if (!$entry->isCurrentlyDecrypted())
+ throw new GitlabServiceException('The given Vault entry is not decrypted.');
+
+ switch ($entry->getPassword()->getAuthenticationType()) {
+ case AuthenticationType::AccessToken:
+ $httpRequest->Headers[] = "Authorization: Bearer " . $entry->getPassword();
+ break;
+
+ case AuthenticationType::UsernamePassword:
+ throw new AuthenticationException('Username/Password authentication is not supported');
+ }
+ }
+
+ if($expect_json)
+ {
+ $httpRequest->Headers[] = "Accept: application/json";
+ $httpRequest->Headers[] = "Content-Type: application/json";
+ }
+
+ return $httpRequest;
+ }
+
+ /**
+ * Downloads a file from the given URL and saves it to the given path
+ *
+ * @param string $url
+ * @param Entry|null $entry
+ * @return string
+ * @throws AuthenticationException
+ * @throws GitlabServiceException
+ * @throws InvalidScopeException
+ * @throws HttpException
+ */
+ public static function downloadGitServiceFile(string $url, ?Entry $entry=null): string
+ {
+ $out_path = Functions::getTmpDir() . "/" . basename($url);
+
+ $httpRequest = new HttpRequest();
+ $httpRequest->Url = $url;
+ $httpRequest->Type = HttpRequestType::GET;
+ $httpRequest = Functions::prepareGitServiceRequest($httpRequest, $entry, false);
+
+ Console::out('Downloading file ' . $url);
+ HttpClient::download($httpRequest, $out_path);
+
+ return $out_path;
+ }
+
+ /**
+ * @param string $path
+ * @return string|null
+ * @throws ArchiveException
+ * @throws UnsupportedArchiveException
+ */
+ public static function extractArchive(string $path): ?string
+ {
+ $executable_finder = new ExecutableFinder();
+ $unzip_executable = $executable_finder->find('unzip');
+ $tar_executable = $executable_finder->find('tar');
+ $out_path = dirname($path);
+ $filesystem = new Filesystem();
+
+ if(!$filesystem->exists($out_path))
+ $filesystem->mkdir($out_path);
+
+ RuntimeCache::setFileAsTemporary($out_path);
+
+ $mimeType = mime_content_type($path);
+ $supportedTypes = [];
+
+ if($unzip_executable !== null)
+ {
+ $supportedTypes = array_merge($supportedTypes, [
+ 'application/zip',
+ 'application/x-zip',
+ 'application/x-zip-compressed',
+ 'application/octet-stream',
+ 'application/x-compress',
+ 'application/x-compressed',
+ 'multipart/x-zip'
+ ]);
+ }
+ else
+ {
+ if(RuntimeCache::get('warning_zip_shown') !== true)
+ {
+ Console::out('unzip executable not found. ZIP archives will not be supported.');
+ RuntimeCache::set('warning_zip_shown', true);
+ }
+ }
+
+ if($tar_executable !== null)
+ {
+ $supportedTypes = array_merge($supportedTypes, [
+ 'application/x-tar',
+ 'application/x-gzip',
+ 'application/x-bzip2',
+ 'application/x-xz'
+ ]);
+ }
+ else
+ {
+ if(RuntimeCache::get('warning_tar_shown') !== true)
+ {
+ Console::outWarning('tar executable not found. TAR archives will not be supported.');
+ RuntimeCache::set('warning_tar_shown', true);
+ }
+ }
+
+ if (!in_array($mimeType, $supportedTypes))
+ throw new UnsupportedArchiveException("Unsupported archive type: $mimeType");
+
+ $command = match ($mimeType) {
+ 'application/zip' => [$unzip_executable, $path, '-d', $out_path],
+ 'application/x-tar' => [$tar_executable, '--verbose', '-xf', $path, '-C', $out_path],
+ 'application/x-gzip' => [$tar_executable, '--verbose', '-xzf', $path, '-C', $out_path],
+ 'application/x-bzip2' => [$tar_executable, '--verbose', '-xjf', $path, '-C', $out_path],
+ default => throw new UnsupportedArchiveException("Unsupported archive type: $mimeType"),
+ };
+
+ Console::out("Extracting archive $path");
+ $process = new Process($command);
+
+ // display the output of the command
+ $process->run(function ($type, $buffer) {
+ Console::outVerbose($buffer);
+ });
+
+ if (!$process->isSuccessful())
+ throw new ArchiveException($process->getErrorOutput());
+
+ return $out_path;
+ }
+
+ /**
+ * Scans the given directory for files and returns the found file
+ *
+ * @param string $path
+ * @param array $files
+ * @return string|null
+ */
+ public static function searchDirectory(string $path, array $files): ?string
+ {
+ if (!is_dir($path))
+ return null;
+
+ // Search files in the given directory recursively
+ $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path));
+ foreach ($iterator as $file)
+ {
+ if (in_array($file->getFilename(), $files))
+ return $file->getPathname();
+ }
+
+ return null;
+ }
+
+ /**
+ * Attempts to convert a weird version number to a standard version number
+ *
+ * @param $version
+ * @return string
+ */
+ public static function convertToSemVer($version): string
+ {
+ if(stripos($version, 'v') === 0)
+ $version = substr($version, 1);
+ if(!Validate::version($version))
+ {
+ $parts = explode('.', $version);
+ $major = (string)null;
+ $minor = (string)null;
+ $patch = (string)null;
+
+ if(count($parts) >= 1)
+ $major = $parts[0];
+ if(count($parts) >= 2)
+ $minor = $parts[1];
+ if(count($parts) >= 3)
+ $patch = $parts[2];
+
+ // Assemble the SemVer compatible string
+ $version = "$major.$minor.$patch";
+ }
+ if(!Validate::version($version))
+ return '1.0.0';
+
+ return $version;
+ }
+
+ /**
+ * Returns a complete RepositoryQueryResults object
+ *
+ * @param RemotePackageInput $packageInput
+ * @param DefinedRemoteSource $definedRemoteSource
+ * @param Entry|null $entry
+ * @return RepositoryQueryResults
+ */
+ public static function getRepositoryQueryResults(RemotePackageInput $packageInput, DefinedRemoteSource $definedRemoteSource, ?Entry $entry): RepositoryQueryResults
+ {
+ $results = new RepositoryQueryResults();
+
+ switch($definedRemoteSource->Type)
+ {
+ case DefinedRemoteSourceType::Github:
+ $source = GithubService::class;
+ break;
+
+ case DefinedRemoteSourceType::Gitlab:
+ $source = GitlabService::class;
+ break;
+
+ default:
+ return $results;
+ }
+
+ // Check if the specified version is a release
+ try
+ {
+ Console::outVerbose(sprintf('Attempting to fetch source code from %s', $definedRemoteSource->Host));
+ $release_results = $source::getRelease($packageInput, $definedRemoteSource, $entry);
+ }
+ catch(Exception $e)
+ {
+ $release_results = null;
+ unset($e);
+ }
+
+ // If the specified version is a release, download the source code
+ if($release_results !== null)
+ {
+ $results->ReleaseName = ($release_results->ReleaseName ?? null);
+ $results->ReleaseDescription = ($release_results->ReleaseDescription ?? null);
+ $results->Files = self::mergeFilesResults($release_results->Files, ($results->Files ?? null));
+ if($release_results->Version !== null)
+ $results->Version = $release_results->Version;
+ }
+
+ try
+ {
+ $git_results = $source::getGitRepository($packageInput, $definedRemoteSource, $entry);
+ }
+ catch(Exception $e)
+ {
+ $git_results = null;
+ unset($e);
+ }
+
+ if($git_results !== null)
+ {
+ if($results->ReleaseName == null)
+ {
+ $results->ReleaseName = ($git_results->ReleaseName ?? null);
+ }
+ elseif($git_results->ReleaseName !== null)
+ {
+ if(strlen($git_results->ReleaseName) > strlen($results->ReleaseName))
+ $results->ReleaseName = $git_results->ReleaseName;
+ }
+
+ if($results->ReleaseDescription == null)
+ {
+ $results->ReleaseDescription = ($git_results->ReleaseDescription ?? null);
+ }
+ elseif($git_results->ReleaseDescription !== null)
+ {
+ if(strlen($git_results->ReleaseDescription) > strlen($results->ReleaseDescription))
+ $results->ReleaseDescription = $git_results->ReleaseDescription;
+ }
+
+ if($results->Version == null)
+ {
+ $results->Version = ($git_results->Version ?? null);
+ }
+ elseif($git_results->Version !== null)
+ {
+ // Version compare
+ if(VersionComparator::compareVersion($git_results->Version, $results->Version) > 0)
+ $results->Version = $git_results->Version;
+ }
+
+ $results->Files = self::mergeFilesResults($git_results->Files, ($results->Files ?? null));
+ }
+
+ try
+ {
+ $ncc_package_results = $source::getNccPackage($packageInput, $definedRemoteSource, $entry);
+ }
+ catch(Exception $e)
+ {
+ unset($e);
+ $ncc_package_results = null;
+ }
+
+ if($ncc_package_results !== null)
+ {
+ if($results->ReleaseName == null)
+ {
+ $results->ReleaseName = ($ncc_package_results->ReleaseName ?? null);
+ }
+ elseif($ncc_package_results->ReleaseName !== null)
+ {
+ if(strlen($ncc_package_results->ReleaseName) > strlen($results->ReleaseName))
+ $results->ReleaseName = $ncc_package_results->ReleaseName;
+ }
+
+ if($results->ReleaseDescription == null)
+ {
+ $results->ReleaseDescription = ($ncc_package_results->ReleaseDescription ?? null);
+ }
+ elseif($ncc_package_results->ReleaseDescription !== null)
+ {
+ if(strlen($ncc_package_results->ReleaseDescription) > strlen($results->ReleaseDescription))
+ $results->ReleaseDescription = $ncc_package_results->ReleaseDescription;
+ }
+
+ if($results->Version == null)
+ {
+ $results->Version = ($ncc_package_results->Version ?? null);
+ }
+ elseif($ncc_package_results->Version !== null)
+ {
+ // Version compare
+ if(VersionComparator::compareVersion($ncc_package_results->Version, $results->Version) > 0)
+ $results->Version = $ncc_package_results->Version;
+ }
+
+ $results->Files = self::mergeFilesResults($ncc_package_results->Files, ($results->Files ?? null));
+ }
+
+ return $results;
+ }
+
+ /**
+ * Merges the given Files object with another Files object
+ *
+ * @param Files $input
+ * @param Files|null $selected
+ * @return Files
+ */
+ private static function mergeFilesResults(RepositoryQueryResults\Files $input, ?RepositoryQueryResults\Files $selected=null): RepositoryQueryResults\Files
+ {
+ if($selected == null)
+ $selected = new RepositoryQueryResults\Files();
+
+ if($input->GitSshUrl !== null)
+ $selected->GitSshUrl = $input->GitSshUrl;
+
+ if($input->GitHttpUrl !== null)
+ $selected->GitHttpUrl = $input->GitHttpUrl;
+
+ if($input->SourceUrl !== null)
+ $selected->SourceUrl = $input->SourceUrl;
+
+ if($input->TarballUrl !== null)
+ $selected->TarballUrl = $input->TarballUrl;
+
+ if($input->ZipballUrl !== null)
+ $selected->ZipballUrl = $input->ZipballUrl;
+
+ if($input->PackageUrl !== null)
+ $selected->PackageUrl = $input->PackageUrl;
+
+ return $selected;
+ }
+
+ /**
+ * Attempts to cast the correct type of the given value
+ *
+ * @param string $input
+ * @return float|int|mixed|string
+ */
+ public static function stringTypeCast(string $input): mixed
+ {
+ if (is_numeric($input))
+ {
+ if (str_contains($input, '.'))
+ return (float)$input;
+
+ if (ctype_digit($input))
+ return (int)$input;
+ }
+ elseif (in_array(strtolower($input), ['true', 'false']))
+ {
+ return filter_var($input, FILTER_VALIDATE_BOOLEAN);
+ }
+
+ return (string)$input;
+ }
+
+ /**
+ * Finalizes the permissions
+ *
+ * @return void
+ * @throws InvalidScopeException
+ */
+ public static function finalizePermissions(): void
+ {
+ if(Resolver::resolveScope() !== Scopes::System)
+ return;
+
+ Console::outVerbose('Finalizing permissions...');
+ $filesystem = new Filesystem();
+
+ try
+ {
+ if($filesystem->exists(PathFinder::getDataPath(Scopes::System)))
+ $filesystem->chmod(PathFinder::getDataPath(Scopes::System), 0777, 0000, true);
+ }
+ catch(Exception $e)
+ {
+ Console::outWarning(sprintf('Failed to finalize permissions for %s: %s', PathFinder::getDataPath() . DIRECTORY_SEPARATOR . 'data', $e->getMessage()));
+ }
+
+ try
+ {
+ if($filesystem->exists(PathFinder::getCachePath(Scopes::System)))
+ $filesystem->chmod(PathFinder::getCachePath(Scopes::System), 0777, 0000, true);
+ }
+ catch(Exception $e)
+ {
+ Console::outWarning(sprintf('Failed to finalize permissions for %s: %s', PathFinder::getDataPath() . DIRECTORY_SEPARATOR . 'data', $e->getMessage()));
+ }
+
+ }
}
\ No newline at end of file
diff --git a/src/ncc/Utilities/IO.php b/src/ncc/Utilities/IO.php
index 7b9c5be..13d76ea 100644
--- a/src/ncc/Utilities/IO.php
+++ b/src/ncc/Utilities/IO.php
@@ -1,6 +1,26 @@
find($runner);
+
+ if($exec_path !== null)
+ return $exec_path;
+
+ throw new RunnerExecutionException(sprintf('Unable to find \'%s\' executable', $runner));
+ }
}
\ No newline at end of file
diff --git a/src/ncc/Utilities/Resolver.php b/src/ncc/Utilities/Resolver.php
index 4d172a0..c58edab 100644
--- a/src/ncc/Utilities/Resolver.php
+++ b/src/ncc/Utilities/Resolver.php
@@ -1,11 +1,36 @@
$match)
@@ -241,4 +266,64 @@
return false;
}
}
+
+ /**
+ * Detects the remote source type, can also accept defined remote
+ * sources as the input, the function will look for the source
+ * type and return it
+ *
+ * @param string $input
+ * @return string
+ */
+ public static function detectRemoteSourceType(string $input): string
+ {
+ if(in_array($input, BuiltinRemoteSourceType::All))
+ return RemoteSourceType::Builtin;
+
+ $source_manager = new RemoteSourcesManager();
+ $defined_source = $source_manager->getRemoteSource($input);
+ if($defined_source == null)
+ return RemoteSourceType::Unknown;
+
+ return RemoteSourceType::Defined;
+ }
+
+ /**
+ * Detects the project type from the specified path
+ *
+ * @param string $path
+ * @return ProjectDetectionResults
+ */
+ public static function detectProjectType(string $path): ProjectDetectionResults
+ {
+ $project_files = [
+ 'project.json',
+ 'composer.json'
+ ];
+
+ $project_file = Functions::searchDirectory($path, $project_files);
+
+ $project_detection_results = new ProjectDetectionResults();
+ $project_detection_results->ProjectType = ProjectType::Unknown;
+
+ if($project_file == null)
+ {
+ return $project_detection_results;
+ }
+
+ // Get filename of the project file
+ switch(basename($project_file))
+ {
+ case 'project.json':
+ $project_detection_results->ProjectType = ProjectType::Ncc;
+ break;
+
+ case 'composer.json':
+ $project_detection_results->ProjectType = ProjectType::Composer;
+ break;
+ }
+
+ $project_detection_results->ProjectPath = dirname($project_file);
+ return $project_detection_results;
+ }
}
\ No newline at end of file
diff --git a/src/ncc/Utilities/RuntimeCache.php b/src/ncc/Utilities/RuntimeCache.php
index 9aad37e..f06ada3 100644
--- a/src/ncc/Utilities/RuntimeCache.php
+++ b/src/ncc/Utilities/RuntimeCache.php
@@ -1,7 +1,30 @@
remove($file);
+ Console::outDebug(sprintf('deleted temporary file \'%s\'', $file));
+ }
+ catch (Exception $e)
+ {
+ Console::outDebug(sprintf('failed to delete temporary file \'%s\', %s', $file, $e->getMessage()));
+ unset($e);
+ }
}
}
}
diff --git a/src/ncc/Utilities/Security.php b/src/ncc/Utilities/Security.php
index 5e4c4cc..f2d3de2 100644
--- a/src/ncc/Utilities/Security.php
+++ b/src/ncc/Utilities/Security.php
@@ -1,6 +1,26 @@
;
+chomp($name);
+print("Hello, $name\n");
\ No newline at end of file
diff --git a/tests/example_project/scripts/unit.py2 b/tests/example_project/scripts/unit.py2
new file mode 100644
index 0000000..af4b858
--- /dev/null
+++ b/tests/example_project/scripts/unit.py2
@@ -0,0 +1,3 @@
+print('Hello World!')
+name = input('What is your name? ')
+print('Hello', name)
\ No newline at end of file
diff --git a/tests/example_project/scripts/unit.py3 b/tests/example_project/scripts/unit.py3
new file mode 100644
index 0000000..af4b858
--- /dev/null
+++ b/tests/example_project/scripts/unit.py3
@@ -0,0 +1,3 @@
+print('Hello World!')
+name = input('What is your name? ')
+print('Hello', name)
\ No newline at end of file