Skip to content

Commit e41e560

Browse files
authored
Merge pull request #176 from asgrim/use-repo-commands
Add repository management commands
2 parents 286930c + 7106f69 commit e41e560

19 files changed

+979
-32
lines changed

.github/docs/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM ghcr.io/roave/docbooktool:1.16.0 AS builder
1+
FROM ghcr.io/roave/docbooktool:1.19.0 AS builder
22

33
COPY ./.github/docs/templates /docs-src/templates
44
COPY ./docs /docs-src/book

.github/docs/templates/online.twig

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1" />
66
<title>PIE Documentation</title>
77
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
8+
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.22.0/themes/prism.css" rel="stylesheet" />
9+
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css" rel="stylesheet" />
810
<style>
911
.selected {
1012
background-color: #7A86B8;
@@ -15,6 +17,41 @@
1517
.hidden {
1618
display: none;
1719
}
20+
.markdown-alert {
21+
border-left: 0.25em solid black;
22+
padding: 1em 1em 0.25em;
23+
background-color: #f7f7f7;
24+
}
25+
.markdown-alert-note {
26+
border-left-color: #0d68d5;
27+
}
28+
.markdown-alert-note .markdown-alert-title {
29+
color: #0d68d5;
30+
}
31+
.markdown-alert-tip {
32+
border-left-color: #188337;
33+
}
34+
.markdown-alert-tip .markdown-alert-title {
35+
color: #188337;
36+
}
37+
.markdown-alert-important {
38+
border-left-color: #7844d6;
39+
}
40+
.markdown-alert-important .markdown-alert-title {
41+
color: #7844d6;
42+
}
43+
.markdown-alert-warning {
44+
border-left-color: #a67003;
45+
}
46+
.markdown-alert-warning .markdown-alert-title {
47+
color: #a67003;
48+
}
49+
.markdown-alert-caution {
50+
border-left-color: #d61c28;
51+
}
52+
.markdown-alert-caution .markdown-alert-title {
53+
color: #d61c28;
54+
}
1855
</style>
1956
</head>
2057
<body>
@@ -47,6 +84,10 @@
4784
* @param {string} title
4885
*/
4986
function loadDocBookNavigation(title) {
87+
document.querySelectorAll('table').forEach((table) => {
88+
table.classList.add("table");
89+
})
90+
5091
/**
5192
* @param {NodeListOf<HTMLElement>} unselectedListElements
5293
* @param {HTMLElement} selectedListElement
@@ -184,5 +225,7 @@
184225
185226
loadDocBookNavigation("PIE Documentation");
186227
</script>
228+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
229+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.js"></script>
187230
</body>
188231
</html>

bin/pie

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use Php\Pie\Command\BuildCommand;
99
use Php\Pie\Command\DownloadCommand;
1010
use Php\Pie\Command\InfoCommand;
1111
use Php\Pie\Command\InstallCommand;
12+
use Php\Pie\Command\RepositoryAddCommand;
13+
use Php\Pie\Command\RepositoryListCommand;
14+
use Php\Pie\Command\RepositoryRemoveCommand;
1215
use Php\Pie\Command\ShowCommand;
1316
use Php\Pie\Util\PieVersion;
1417
use Symfony\Component\Console\Application;
@@ -31,6 +34,9 @@ $application->setCommandLoader(new ContainerCommandLoader(
3134
'install' => InstallCommand::class,
3235
'info' => InfoCommand::class,
3336
'show' => ShowCommand::class,
37+
'repository:list' => RepositoryListCommand::class,
38+
'repository:add' => RepositoryAddCommand::class,
39+
'repository:remove' => RepositoryRemoveCommand::class,
3440
]
3541
));
3642

docs/extension-maintainers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ specify a default value in the `configure-options` definition.
137137
The `build-path` setting may be used if your source code is not in the root
138138
of your repository. For example, if your repository structure is like:
139139

140-
```
140+
```text
141141
/
142142
docs/
143143
src/

docs/usage.md

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ COPY --from=ghcr.io/php/pie:bin /pie /usr/bin/pie
3737

3838
Instead of `bin` tag (which represents latest binary-only image) you can also use explicit version (in `x.y.z-bin` format). Use [GitHub registry](https://ghcr.io/php/pie) to find available tags.
3939

40-
> [!IMPORTANT]
40+
> [!IMPORTANT]
4141
> Binary-only images don't include PHP runtime so you can't use them for _running_ PIE. This is just an alternative way of distributing PHAR file, you still need to satisfy PIE's runtime requirements on your own.
4242
4343
#### Example of PIE working in a Dockerfile
@@ -197,7 +197,7 @@ functionality, or to provide paths to libraries not automatically detected.
197197
In order to determine what configure options are available for an extension,
198198
you may use `pie info <vendor>/<package>` which will return a list, such as:
199199

200-
```
200+
```text
201201
Configure options:
202202
--enable-some-functionality (whether to enable some additional functionality provided)
203203
--with-some-library-name=? (Path for some-library)
@@ -215,6 +215,45 @@ pie install example/some-extension --with-some-library-name=/path/to/the/lib --e
215215

216216
### Configuring the INI file
217217

218-
At the moment, PIE does not configure the INI file, although this improvement
219-
is planned soon. In the meantime, you must enable the extension after installing
220-
by adding a line such as `extension=foo` to your `php.ini`.
218+
PIE will automatically try to enable the extension by adding `extension=...` or
219+
`zend_extension=...` in the appropriate INI file. If you want to disable this
220+
behaviour, pass the `--skip-enable-extension` flag to your `pie install`
221+
command. The following techniques are used to attempt to enable the extension:
222+
223+
* `phpenmod`, if using the deb.sury.org distribution
224+
* `docker-php-ext-enable` if using Docker's PHP image
225+
* Add a new file to the "additional .ini file" path, if configured
226+
* Append to the standard php.ini, if configured
227+
228+
If none of these techniques work, or you used the `--skip-enable-extension`
229+
flag, PIE will warn you that the extension was not enabled, and will note that
230+
you must enable the extension yourself.
231+
232+
### Adding non-Packagist.org repositories
233+
234+
Sometimes you may want to install an extension from a package repository other
235+
than Packagist.org (such as [Private Packagist](https://packagist.com/)), or
236+
from a local directory. Since PIE is based heavily on Composer, it is possible
237+
to use some other repository types:
238+
239+
* `pie repository:add [--with-php-config=...] path /path/to/your/local/extension`
240+
* `pie repository:add [--with-php-config=...] vcs https://github.com/youruser/yourextension`
241+
* `pie repository:add [--with-php-config=...] composer https://repo.packagist.com/your-private-packagist/`
242+
* `pie repository:add [--with-php-config=...] composer packagist.org`
243+
244+
The `repository:*` commands all support the optional `--with-php-config` flag
245+
to allow you to specify which PHP installation to use (for example, if you have
246+
multiple PHP installations on one machine). The above added repositories can be
247+
removed too, using the inverse `repository:remove` commands:
248+
249+
* `pie repository:remove [--with-php-config=...] /path/to/your/local/extension`
250+
* `pie repository:remove [--with-php-config=...] https://github.com/youruser/yourextension`
251+
* `pie repository:remove [--with-php-config=...] https://repo.packagist.com/your-private-packagist/`
252+
* `pie repository:remove [--with-php-config=...] packagist.org`
253+
254+
Note you do not need to specify the repository type in `repository:remove`,
255+
just the URL.
256+
257+
You can list the repositories for the target PHP installation with:
258+
259+
* `pie repository:list [--with-php-config=...]`

features/install-extensions.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Feature: Extensions can be installed with Behat
1+
Feature: Extensions can be installed with PIE
22

33
Example: The latest version of an extension can be downloaded
44
When I run a command to download the latest version of an extension
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Feature: Package repositories can be managed with PIE
2+
3+
Example: A package repository can be added
4+
Given no repositories have previously been added
5+
When I add a package repository
6+
Then I should see the package repository can be used by PIE
7+
8+
Example: A package repository can be removed
9+
Given I have previously added a package repository
10+
When I remove the package repository
11+
Then I should see the package repository is not used by PIE

src/Command/CommandHelper.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
namespace Php\Pie\Command;
66

7+
use Composer\Composer;
78
use Composer\Package\Version\VersionParser;
9+
use Composer\Repository\ComposerRepository;
10+
use Composer\Repository\PathRepository;
11+
use Composer\Repository\VcsRepository;
812
use Composer\Util\Platform;
913
use InvalidArgumentException;
1014
use Php\Pie\DependencyResolver\Package;
@@ -263,4 +267,44 @@ public static function processConfigureOptionsFromInput(Package $package, InputI
263267

264268
return $configureOptionsValues;
265269
}
270+
271+
public static function listRepositories(Composer $composer, OutputInterface $output): void
272+
{
273+
$output->writeln('The following repositories are in use for this Target PHP:');
274+
275+
foreach ($composer->getRepositoryManager()->getRepositories() as $repo) {
276+
if ($repo instanceof ComposerRepository) {
277+
$repoConfig = $repo->getRepoConfig();
278+
279+
$repoUrl = array_key_exists('url', $repoConfig) && is_string($repoConfig['url']) && $repoConfig['url'] !== '' ? $repoConfig['url'] : null;
280+
281+
if ($repoUrl === 'https://repo.packagist.org') {
282+
$output->writeln(' - Packagist');
283+
continue;
284+
}
285+
286+
$output->writeln(sprintf(' - Composer (%s)', $repoUrl ?? 'no url?'));
287+
continue;
288+
}
289+
290+
if ($repo instanceof VcsRepository) {
291+
/** @psalm-suppress InternalMethod */
292+
$output->writeln(sprintf(
293+
' - VCS Repository (%s)',
294+
$repo->getDriver()?->getUrl() ?? 'no url?',
295+
));
296+
continue;
297+
}
298+
299+
if (! $repo instanceof PathRepository) {
300+
continue;
301+
}
302+
303+
$repoConfig = $repo->getRepoConfig();
304+
$output->writeln(sprintf(
305+
' - Path Repository (%s)',
306+
array_key_exists('url', $repoConfig) && is_string($repoConfig['url']) && $repoConfig['url'] !== '' ? $repoConfig['url'] : 'no path?',
307+
));
308+
}
309+
}
266310
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Php\Pie\Command;
6+
7+
use Php\Pie\ComposerIntegration\PieComposerFactory;
8+
use Php\Pie\ComposerIntegration\PieComposerRequest;
9+
use Php\Pie\ComposerIntegration\PieJsonEditor;
10+
use Psr\Container\ContainerInterface;
11+
use Symfony\Component\Console\Attribute\AsCommand;
12+
use Symfony\Component\Console\Command\Command;
13+
use Symfony\Component\Console\Input\InputArgument;
14+
use Symfony\Component\Console\Input\InputInterface;
15+
use Symfony\Component\Console\Output\OutputInterface;
16+
use Webmozart\Assert\Assert;
17+
18+
use function realpath;
19+
use function str_contains;
20+
21+
#[AsCommand(
22+
name: 'repository:add',
23+
description: 'Add a new repository for packages that PIE can use.',
24+
)]
25+
final class RepositoryAddCommand extends Command
26+
{
27+
private const ARG_TYPE = 'type';
28+
private const ARG_URL = 'url';
29+
30+
private const ALLOWED_TYPES = ['vcs', 'path', 'composer'];
31+
32+
public function __construct(
33+
private readonly ContainerInterface $container,
34+
) {
35+
parent::__construct();
36+
}
37+
38+
public function configure(): void
39+
{
40+
parent::configure();
41+
42+
CommandHelper::configurePhpConfigOptions($this);
43+
44+
$this->addArgument(
45+
self::ARG_TYPE,
46+
InputArgument::REQUIRED,
47+
'Specify the type of the repository, e.g. vcs, path, composer',
48+
);
49+
$this->addArgument(
50+
self::ARG_URL,
51+
InputArgument::REQUIRED,
52+
'Specify the URL of the repository, e.g. a Github/Gitlab URL, a filesystem path, or Private Packagist URL',
53+
);
54+
}
55+
56+
public function execute(InputInterface $input, OutputInterface $output): int
57+
{
58+
$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);
59+
$pieJsonEditor = PieJsonEditor::fromTargetPlatform($targetPlatform);
60+
61+
$type = (string) $input->getArgument(self::ARG_TYPE);
62+
/** @psalm-var 'vcs'|'path'|'composer' $type */
63+
Assert::inArray($type, self::ALLOWED_TYPES);
64+
65+
$url = $originalUrl = (string) $input->getArgument(self::ARG_URL);
66+
67+
if ($type === 'path') {
68+
$url = realpath($originalUrl);
69+
}
70+
71+
if ($type === 'composer' && str_contains($url, 'packagist.org')) {
72+
// "adding packagist" is really just removing an exclusion
73+
$pieJsonEditor
74+
->ensureExists()
75+
->removeRepository('packagist.org');
76+
} else {
77+
Assert::stringNotEmpty($url, 'Could not resolve ' . $originalUrl . ' to a real path');
78+
79+
$pieJsonEditor
80+
->ensureExists()
81+
->addRepository($type, $url);
82+
}
83+
84+
CommandHelper::listRepositories(
85+
PieComposerFactory::createPieComposer(
86+
$this->container,
87+
PieComposerRequest::noOperation(
88+
$output,
89+
$targetPlatform,
90+
),
91+
),
92+
$output,
93+
);
94+
95+
return 0;
96+
}
97+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Php\Pie\Command;
6+
7+
use Php\Pie\ComposerIntegration\PieComposerFactory;
8+
use Php\Pie\ComposerIntegration\PieComposerRequest;
9+
use Psr\Container\ContainerInterface;
10+
use Symfony\Component\Console\Attribute\AsCommand;
11+
use Symfony\Component\Console\Command\Command;
12+
use Symfony\Component\Console\Input\InputInterface;
13+
use Symfony\Component\Console\Output\OutputInterface;
14+
15+
#[AsCommand(
16+
name: 'repository:list',
17+
description: 'List the package repositories that PIE uses.',
18+
)]
19+
final class RepositoryListCommand extends Command
20+
{
21+
public function __construct(
22+
private readonly ContainerInterface $container,
23+
) {
24+
parent::__construct();
25+
}
26+
27+
public function configure(): void
28+
{
29+
parent::configure();
30+
31+
CommandHelper::configurePhpConfigOptions($this);
32+
}
33+
34+
public function execute(InputInterface $input, OutputInterface $output): int
35+
{
36+
CommandHelper::listRepositories(
37+
PieComposerFactory::createPieComposer(
38+
$this->container,
39+
PieComposerRequest::noOperation(
40+
$output,
41+
CommandHelper::determineTargetPlatformFromInputs($input, $output),
42+
),
43+
),
44+
$output,
45+
);
46+
47+
return 0;
48+
}
49+
}

0 commit comments

Comments
 (0)