Skip to content

Commit

Permalink
Merge pull request #419 from jolicode/feat/composer-castor-json
Browse files Browse the repository at this point in the history
feat(import): use a composer file directly
  • Loading branch information
joelwurtz authored Apr 22, 2024
2 parents 7e73552 + 5cd697f commit 8d1717d
Show file tree
Hide file tree
Showing 39 changed files with 450 additions and 384 deletions.
1 change: 1 addition & 0 deletions .github/actions/install/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ runs:
uses: shivammathur/setup-php@v2
with:
php-version: '${{ inputs.php-version }}'
coverage: none

- name: Install dependencies (project)
run: composer install --prefer-dist --no-progress --optimize-autoloader --classmap-authoritative ${{ inputs.composer-flags }}
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
.castor.stub.php
/.php-cs-fixer.cache
/.phpunit.cache
/castor.*
/my-app.*
/var/
/vendor/
/tests/Examples/fixtures/**/.castor.stub.php
/castor.*
!/castor.php
!/castor.composer.json
!/castor.composer.lock
31 changes: 31 additions & 0 deletions castor.composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"config": {
"sort-packages": true
},
"replace": {
"castor\/castor": "v0.15.0"
},
"require": {
"pyrech\/castor-example": "^1.0",
"pyrech\/castor-example-package-not-published": "*",
"pyrech\/foobar": "v1"
},
"repositories": [
{
"type": "vcs",
"url": "https:\/\/github.com\/pyrech\/castor-example-package-not-published.git"
},
{
"type": "package",
"package": {
"name": "pyrech\/foobar",
"version": "v1",
"source": {
"url": "https:\/\/github.com\/pyrech\/castor-example-misc.git",
"type": "git",
"reference": "main"
}
}
}
]
}
78 changes: 78 additions & 0 deletions castor.composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

124 changes: 30 additions & 94 deletions doc/going-further/extending-castor/remote-imports.md
Original file line number Diff line number Diff line change
@@ -1,108 +1,53 @@
# Import remote functions

> [!WARNING]
> Remote imports is in a experimental state and may change in the future.
Castor can import functions from your filesystem but also from a remote resource.

## Importing functions
## Installing remote packages

When importing functions from a remote resource, Castor will use Composer to
download the packages and store them in `.castor/vendor/`.

To import functions, you need to use the same `import()` function used to import
your tasks, but this time with a different syntax for the `path` argument.

The import syntax depends on the source of the packages.

### From a Composer package (scheme `composer://`)

This is the most common use case when the functions to import are defined in a
Composer package. You can directly import them by using the package name
prefixed with the `composer://` scheme:
To import functions, you need to create a `castor.composer.json` file next to
the `castor.php` file (either at the root of your project or in the `.castor/`
directory).

```php
use function Castor\import;

import('composer://vendor-name/project-name');
```
This also can be done by running the `castor composer init` command.

This will import all the tasks defined in the package.
See the [Composer documentation](https://getcomposer.org/doc/04-schema.md) for
more information about the `composer.json` file.

#### Specify the version
## Importing file from a remote package

You can define the version of the package to import by using the `version`
argument:
Third party functions may not be autoloaded by Composer, as there may be
optional. To import them, you can use the `import()` function.

```php
use function Castor\import;

import('composer://vendor-name/project-name', version: '^1.0');
import('composer://vendor/package/', file: 'functions.php');
```

You can use any version constraint supported by Composer (like `*`, `dev-main`,
`^1.0`, etc.). See the [Composer documentation](https://getcomposer.org/doc/articles/versions.md#writing-version-constraints)
for more information.
File is optional, if not provided, Castor will look for a `castor.php` file in
the package.

> [!TIP]
> The `version` argument is optional and will default to `*`.
#### Import from a package not pushed to packagist.org

In some cases, you may have a Composer package that is not pushed to
packagist.org (like a private package hosted on packagist.com or another package
registry). In such cases, you can import it by using the `vcs` argument to
specify the repository URL where the package is hosted:

```php

use function Castor\import;

import('composer://vendor-name/project-name', vcs: 'https://github.com/organization/repository.git');
```
## Manipulating castor composer file

### From a repository (scheme `package://`)
Castor provide a `composer` command to manipulate the `castor.composer.json`
file.

If the functions you want to import are not available as a Composer package, you
can still import them by using a special configuration that Composer will
understand. This will now use the `package://` scheme.
For example, you can use it to add a package to the file:

```php
use function Castor\import;

import('package://vendor-name/project-name', source: [
'url' => 'https://github.com/organization/repository.git',
'type' => 'git', // 'Any source type supported by Composer (git, svn, etc)'
'reference' => 'main', // A commit id, a branch or a tag name
]);
```bash
castor composer require 'vendor/package'
```

> [!NOTE]
> The "vendor-name/project-name" name can be whatever you want, and will only be
> used internally by Castor and Composer to make the repository behave like a
> normal Composer package.
> [!TIP]
> Rather than using the `package://` scheme, it may be simpler to create a
> standard `composer.json` to your repository and import your newly created
> package by using the `composer://` scheme and the `vcs` argument.
## Import only a specific file

No matter where does the package come from (Composer package, git repository,
etc.), you can restrict the file (or directory) to be imported. This is
configured by using the `file` argument specifying the path inside the package
or repository.

```php
use function Castor\import;
Or you can use it to update packages

import('composer://vendor-name/project-name', file: 'castor/my-tasks.php');
```bash
castor composer update
```

> [!NOTE]
> The `file` argument is optional and will empty by default, causing Castor to
> import and parse all the PHP files in the package. While handy, it's probably
> not what you want if your package contains PHP code that are not related to
> Castor.
## Preventing remote imports

In case you have trouble with the imported functions (or if you don't trust
Expand All @@ -125,19 +70,10 @@ $ export CASTOR_NO_REMOTE=1
$ castor my-task # will not import any remote functions
```

## Update imported packages

When you import a package in a given version, Castor will not update
automatically update the packages once a new version of your dependency is
available.
## Lock file

To update your dependencies, you will either need to:
Like every PHP projects using Composer, it will generate a
`castor.composer.lock` file to lock the versions of the imported packages.

- change the required version yourself (thus every one using your Castor project
will profit of the update once they run your project);
- force the update on your side only by either using the `--update-remotes`
option or by removing the `.castor/vendor/` folder.

```bash
$ castor --update-remotes
```
It is recommended to commit this file to your version control system to ensure
that everyone uses the same versions of the imported packages.
10 changes: 3 additions & 7 deletions examples/remote-import.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,11 @@
use function Castor\import;

// Importing tasks from a Composer package
import('composer://pyrech/castor-example', version: '^1.0');
import('composer://pyrech/castor-example');
// Importing tasks from a Composer package not published on packagist (but still having a composer.json)
import('composer://pyrech/castor-example-package-not-published', version: '*', vcs: 'https://github.com/pyrech/castor-example-package-not-published.git');
import('composer://pyrech/castor-example-package-not-published');
// Importing tasks from a repository not using Composer
import('package://pyrech/foobar', source: [
'url' => 'https://github.com/pyrech/castor-example-misc.git',
'type' => 'git',
'reference' => 'main', // commit id, branch or tag name
]);
import('composer://pyrech/foobar');

#[AsTask(description: 'Use functions imported from remote packages')]
function remote_tasks(): void
Expand Down
2 changes: 2 additions & 0 deletions src/Console/ApplicationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Castor\Console;

use Castor\Console\Command\CompileCommand;
use Castor\Console\Command\ComposerCommand;
use Castor\Console\Command\DebugCommand;
use Castor\Console\Command\RepackCommand;
use Castor\Container;
Expand Down Expand Up @@ -199,6 +200,7 @@ private static function configureContainer(ContainerConfigurator $c, bool $repac
'$containerBuilder' => service(ContainerInterface::class),
])
->call('add', [service(DebugCommand::class)])
->call('add', [service(ComposerCommand::class)])
->call('setDispatcher', [service(EventDispatcherInterface::class)])
->call('setCatchErrors', [true])
;
Expand Down
53 changes: 53 additions & 0 deletions src/Console/Command/ComposerCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace Castor\Console\Command;

use Castor\Console\Input\GetRawTokenTrait;
use Castor\Import\Remote\Composer;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;

/** @internal */
#[AsCommand(
name: 'castor:composer',
description: 'Interact with built-in Composer for castor',
aliases: ['composer'],
)]
final class ComposerCommand extends Command
{
use GetRawTokenTrait;

public function __construct(
private readonly string $rootDir,
#[Autowire(lazy: true)]
private readonly Composer $composer,
) {
parent::__construct();
}

protected function configure(): void
{
$this
->ignoreValidationErrors()
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$extra = array_filter($this->getRawTokens($input), fn ($item) => 'composer' !== $item);

$vendorDirectory = $this->rootDir . Composer::VENDOR_DIR;

if (!file_exists($file = $this->rootDir . '/castor.composer.json') && !file_exists($file = $this->rootDir . '/.castor/castor.composer.json')) {
// Default to the root directory (so someone can do a composer init by example)
$file = $this->rootDir . '/castor.composer.json';
}

$this->composer->run($file, $vendorDirectory, $extra, $output, true);

return Command::SUCCESS;
}
}
Loading

0 comments on commit 8d1717d

Please sign in to comment.