Skip to content

Commit

Permalink
Merge pull request #21 from simplesamlphp/feature/assets
Browse files Browse the repository at this point in the history
Add support for assets-modules
  • Loading branch information
tvdijen authored Jan 8, 2023
2 parents 1430443 + d10cdab commit dcfde5e
Show file tree
Hide file tree
Showing 12 changed files with 340 additions and 34 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.phpunit.result.cache
composer.lock
composer.phar
/vendor/

# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
# composer.lock
15 changes: 15 additions & 0 deletions .php_cs.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

$finder = PhpCsFixer\Finder::create()
->in([
__DIR__ . '/src',
__DIR__ . '/tests',
])
;

return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
])
->setFinder($finder)
;
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
SimpleSAMLphp Composer module installer
=======================================
# SimpleSAMLphpSimpleSAMLphp Composer module installer

![Build Status](https://github.com/simplesamlphp/composer-module-installer/workflows/CI/badge.svg?branch=master)
[![Coverage Status](https://codecov.io/gh/simplesamlphp/composer-module-installer/branch/master/graph/badge.svg)](https://codecov.io/gh/simplesamlphp/composer-module-installer)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/simplesamlphp/composer-module-installer/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/simplesamlphp/composer-module-installer/?branch=master)
[![Type coverage](https://shepherd.dev/github/simplesamlphp/composer-module-installer/coverage.svg)](https://shepherd.dev/github/simplesamlphp/composer-module-installer)

This package is a Composer plugin that allows a SimpleSAMLphp module to be
installed through Composer. Installation can be as easy as executing:

```
```bash
composer.phar require vendor/simplesamlphp-module-mymodule 1.*
```

That command would install `vendor/simplesamlphp-module-mymodule` matching
version `1.*`.


Making a module installable through Composer
--------------------------------------------
## Making a module installable through Composer

To make a module installable through Composer, you need to add a
`composer.json`-file to the root of the module. It should look
Expand All @@ -32,7 +35,7 @@ something like:

The package name must be on the form:

```
```bash
<vendor>/simplesamlphp-module-<module name>
```

Expand All @@ -41,8 +44,14 @@ of your module. Your module will be installed in the `modules/<module name>`
directory in the SimpleSAMLphp installation directory.


Installing your custom module
-----------------------------
## Assets modules

Asset modules are a special kidn of module that will install pre-built assets in
SimpleSAMLphp's `public/` directory. These modules follow a slightly different
naming convention `simplesamlphp-assets-<module name>`


## Installing your custom module

If you publish your module on [Packagist](https://packagist.org/), no special
configuration is required to install your module. However, if your module is
Expand Down Expand Up @@ -87,7 +96,7 @@ The `repositories array may look something like:
Once you have added the repository, you should be able to install your module
by executing:

```
```bash
composer.phar require vendor/simplesamlphp-module-mymodule:dev-master
```

Expand All @@ -98,8 +107,7 @@ See the [Composer Repository documentation](https://getcomposer.org/doc/05-repos
for more information about adding your own custom repositories to Composer.


Module names that contain uppercase letters
-------------------------------------------
## Module names that contain uppercase letters

New modules should only have lowercase letters in the module name, however a
lot of existing module names contain uppercase letters. Since Composer package
Expand Down
16 changes: 16 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
coverage:
status:
project:
default:
target: 0%
threshold: 2%
patch: off
comment:
layout: "diff"
behavior: once
require_changes: true
require_base: no
require_head: yes
branches: null
github_checks:
annotations: false
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
"php": "^7.4 || ^8.0",

"composer-plugin-api": "^1.1 || ^2.0",
"simplesamlphp/simplesamlphp": "*"
"simplesamlphp/assert": "^0.8.0"
},
"require-dev": {
"composer/composer": "^2.4",
"simplesamlphp/simplesamlphp-test-framework": "^1.2.1"
}
}
15 changes: 15 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<ruleset name="SimpleSAMLphp ruleset">
<config name="ignore_warnings_on_exit" value="1"/>

<description>
By default it is less stringent about long lines than other coding standards
</description>

<file>src</file>
<file>tests</file>

<!-- This is the rule we inherit from. If you want to exclude some specific rules, see the docs on how to do that -->
<rule ref="PSR12"/>
</ruleset>

22 changes: 22 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" bootstrap="./tests/bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./src</directory>
</include>
<exclude>
<directory>./tests</directory>
</exclude>
<report>
<clover outputFile="build/logs/clover.xml"/>
<html outputDirectory="build/coverage" lowUpperBound="35" highLowerBound="70"/>
<text outputFile="php://stdout" showUncoveredFiles="true"/>
</report>
</coverage>
<testsuites>
<testsuite name="Unit tests">
<directory>./tests</directory>
</testsuite>
</testsuites>
<logging/>
</phpunit>
50 changes: 50 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?xml version="1.0"?>
<psalm
name="SimpleSAMLphp"
useDocblockTypes="true"
errorLevel="2"
reportMixedIssues="false"
hideExternalErrors="true"
allowStringToStandInForClass="true"
>
<projectFiles>
<directory name="src" />
<directory name="tests" />

<!-- Ignore certain directories -->
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>

<issueHandlers>
<LessSpecificReturnType errorLevel="info" />

<!-- level 3 issues - slightly lazy code writing, but probably low false-negatives -->
<DeprecatedClass errorLevel="info" />
<DeprecatedMethod errorLevel="info" />

<MissingClosureReturnType errorLevel="info" />
<MissingReturnType errorLevel="info" />
<MissingPropertyType errorLevel="info" />
<InvalidDocblock errorLevel="info" />

<PropertyNotSetInConstructor errorLevel="info" />
<MissingConstructor errorLevel="info" />
<MissingClosureParamType errorLevel="info" />
<MissingParamType errorLevel="info" />
<UnusedClass errorLevel="info" />
<PossiblyUnusedMethod errorLevel="info" />

<!-- Ignore these errors until we are fully typehinted -->
<DocblockTypeContradiction errorLevel="suppress" />
<RedundantConditionGivenDocblockType errorLevel="suppress" />

<!-- Ignore UnresolvableInclude on CLI-scripts -->
<UnresolvableInclude>
<errorLevel type="suppress">
<file name="tests/bootstrap.php" />
</errorLevel>
</UnresolvableInclude>
</issueHandlers>
</psalm>
94 changes: 72 additions & 22 deletions src/ModuleInstaller.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@

namespace SimpleSAML\Composer;

use InvalidArgumentException;
use SimpleSAML\Assert\Assert;
use Composer\Package\PackageInterface;
use Composer\Installer\LibraryInstaller;
use InvalidArgumentException;

use function in_array;
use function is_string;
use function mb_strtolower;
use function preg_match;
use function sprintf;

class ModuleInstaller extends LibraryInstaller
{
/** @var string */
public const MIXED_CASE = 'ssp-mixedcase-module-name';

/** @var array */
public const SUPPORTED = ['simplesamlphp-assets', 'simplesamlphp-module'];


/**
* {@inheritDoc}
*/
Expand All @@ -36,34 +46,74 @@ protected function getPackageBasePath(PackageInterface $package)
}

$name = $package->getPrettyName();
if (!preg_match('@^.*/simplesamlphp-module-(.+)$@', $name, $matches)) {
throw new InvalidArgumentException('Unable to install module ' . $name .', package name must be on the form "VENDOR/simplesamlphp-module-MODULENAME".');
$matches = [];
if ($name === 'simplesamlphp/simplesamlphp-assets') {
// Special case for SimpleSAMLphp's base assets
return $ssp_path . '/public/base';
} elseif (!preg_match('@^.*/simplesamlphp-(module|assets)-(.+)$@', $name, $matches)) {
throw new InvalidArgumentException(sprintf(
'Unable to install module %s, package name must be on the form "VENDOR/simplesamlphp-(module|assets)-MODULENAME".',
$name,
));
}
$moduleDir = $matches[1];

if (!preg_match('@^[a-z0-9_.-]*$@', $moduleDir)) {
throw new InvalidArgumentException('Unable to install module ' . $name .', module name must only contain characters from a-z, 0-9, "_", "." and "-".');
}
if ($moduleDir[0] === '.') {
throw new InvalidArgumentException('Unable to install module ' . $name .', module name cannot start with ".".');
}
Assert::count($matches, 2);
$moduleType = $matches[1];
$moduleDir = $matches[2];

Assert::regex(
$moduleDir,
'@^[a-z0-9_.-]*$@',
sprintf(
'Unable to install module %s, module name must only contain characters from a-z, 0-9, "_", "." and "-".',
$name
),
InvalidArgumentException::class
);

Assert::notStartsWith(
$moduleDir,
'.',
sprintf('Unable to install module %s, module name cannot start with ".".', $name),
InvalidArgumentException::class
);

/* Composer packages are supposed to only contain lowercase letters, but historically many modules have had names in mixed case.
* We must provide a way to handle those. Here we allow the module directory to be overridden with a mixed case name.
/**
* Composer packages are supposed to only contain lowercase letters,
* but historically many modules have had names in mixed case.
* We must provide a way to handle those. Here we allow the module directory
* to be overridden with a mixed case name.
*/
$extraData = $package->getExtra();
if (isset($extraData['ssp-mixedcase-module-name'])) {
$mixedCaseModuleName = $extraData['ssp-mixedcase-module-name'];
if (!is_string($mixedCaseModuleName)) {
throw new InvalidArgumentException('Unable to install module ' . $name .', "ssp-mixedcase-module-name" must be a string.');
}
if (mb_strtolower($mixedCaseModuleName, 'utf-8') !== $moduleDir) {
throw new InvalidArgumentException('Unable to install module ' . $name .', "ssp-mixedcase-module-name" must match the package name except that it can contain uppercase letters.');
}
if (isset($extraData[self::MIXED_CASE])) {
$mixedCaseModuleName = $extraData[self::MIXED_CASE];
Assert::string(
$mixedCaseModuleName,
sprintf('Unable to install module %s, "%s" must be a string.', $name, self::MIXED_CASE),
InvalidArgumentException::class
);

Assert::same(
mb_strtolower($mixedCaseModuleName, 'utf-8'),
$moduleDir,
sprintf(
'Unable to install module %s, "%s" must match the package name except that it can contain uppercase letters.',
$name,
self::MIXED_CASE
),
InvalidArgumentException::class
);
$moduleDir = $mixedCaseModuleName;
}

return $ssp_path . '/modules/' . $moduleDir;
switch ($moduleType) {
case 'assets':
return $ssp_path . '/public/' . $moduleDir;
case 'module':
return $ssp_path . '/modules/' . $moduleDir;
default:
throw new InvalidArgumentException(sprintf('Unsupported type: %s', $moduleType));
}
}


Expand All @@ -72,6 +122,6 @@ protected function getPackageBasePath(PackageInterface $package)
*/
public function supports($packageType)
{
return 'simplesamlphp-module' === $packageType;
return in_array($packageType, self::SUPPORTED);
}
}
1 change: 1 addition & 0 deletions src/ModuleInstallerPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class ModuleInstallerPlugin implements PluginInterface
/** @var \Composer\IO\IOInterface */
private IOInterface $io;


/**
* Apply plugin modifications to Composer
*
Expand Down
Loading

0 comments on commit dcfde5e

Please sign in to comment.