Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@
/docs export-ignore
/.dev export-ignore
/phpstan.neon.dist export-ignore

# Licence files whose sha256 is recorded in config.json stay byte-for-byte
# as vendored: no line-ending normalisation on any checkout.
/resources/assets/svg/licenses/** -text
6 changes: 6 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ jobs:
# (SiblingConstraintsTest). CI aliases the siblings' main into that line so
# this repository is tested against what it will ship beside, and restores
# composer.json after the install so tests read the committed constraints.
# Until the re-squash tags core again, no core tag carries
# Providers\IconPackServiceProvider, so this alias is what a pack needs to boot.
run: composer require "ichava/core:dev-main as 0.1.99" --no-update --no-interaction

- name: Install dependencies
Expand Down Expand Up @@ -68,6 +70,8 @@ jobs:
# (SiblingConstraintsTest). CI aliases the siblings' main into that line so
# this repository is tested against what it will ship beside, and restores
# composer.json after the install so tests read the committed constraints.
# Until the re-squash tags core again, no core tag carries
# Providers\IconPackServiceProvider, so this alias is what a pack needs to boot.
run: composer require "ichava/core:dev-main as 0.1.99" --no-update --no-interaction

- name: Install dependencies
Expand Down Expand Up @@ -102,6 +106,8 @@ jobs:
# (SiblingConstraintsTest). CI aliases the siblings' main into that line so
# this repository is tested against what it will ship beside, and restores
# composer.json after the install so tests read the committed constraints.
# Until the re-squash tags core again, no core tag carries
# Providers\IconPackServiceProvider, so this alias is what a pack needs to boot.
run: composer require "ichava/core:dev-main as 0.1.99" --no-update --no-interaction

- name: Install dependencies
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ jobs:
# (SiblingConstraintsTest). CI aliases the siblings' main into that line so
# this repository is tested against what it will ship beside, and restores
# composer.json after the install so tests read the committed constraints.
# Until the re-squash tags core again, no core tag carries
# Providers\IconPackServiceProvider, so this alias is what a pack needs to boot.
run: composer require "ichava/core:dev-main as 0.1.99" --no-update --no-interaction

- name: Install dependencies
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sync-upstream.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# A human reviews the PR diff and merges. release-please then opens the
# release PR, and its tag starts release.yml, which publishes the GitHub
# release. Packagist reads the tag and lists the new version.
# release. Nothing is published to Packagist; installs come from the tags.
#
# The pin is a full commit SHA of icon-sets-sync, and `toolkit-ref` must be
# the same commit. Dependabot (github-actions) advances the `uses:` pin;
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ jobs:
# (SiblingConstraintsTest). CI aliases the siblings' main into that line so
# this repository is tested against what it will ship beside, and restores
# composer.json after the install so tests read the committed constraints.
# Until the re-squash tags core again, no core tag carries
# Providers\IconPackServiceProvider, so this alias is what a pack needs to boot.
run: composer require "ichava/core:dev-main as 0.1.99" --no-update --no-interaction

- name: Install dependencies
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Contribution guidelines are centralized for the whole Ichava org:

- [Code of Conduct](CODE_OF_CONDUCT.md)
- [Code of Conduct](https://github.com/ichava/.github/blob/main/CODE_OF_CONDUCT.md)

For this pack specifically: icon assets are refreshed from upstream by the
[icon-sets-sync](https://github.com/ichava/icon-sets-sync/blob/main/docs/maintainer-sync.md).
Expand Down
35 changes: 35 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Security policy

Do not report a suspected vulnerability in a public issue, discussion or pull request.

## Reporting a vulnerability

**Use GitHub private vulnerability reporting.** On this repository's
[Security tab](https://github.com/ichava/icon-sets-ionicons/security/policy), choose
*Report a vulnerability*. The report arrives attached to the repository, with a
draft advisory and a CVE request path in place. It is off by default, so switch
it on once the repository is public, and keep naming it here only while the
button is there:

```bash
gh api -X PUT repos/ichava/icon-sets-ionicons/private-vulnerability-reporting
```

**Or email [security@simtabi.com](mailto:security@simtabi.com).** That is the only
disclosure address, and it works whether or not the button is available. Other
Simtabi addresses, including `ichava@simtabi.com`, handle community mail and
may delay a report.

Response times, coordinated disclosure, safe harbor and credit follow the
[organisation security policy](https://github.com/ichava/.github/blob/main/SECURITY.md).

## Scope

This package ships icon assets and the provider that registers them with
`ichava/core`.

Core owns sanitisation, caching, seeding and the HTTP surface, so a defect in
how an icon is sanitised, cached or served belongs to
[ichava/core](https://github.com/ichava/core) rather than here. What belongs
here is the vendored assets themselves, this package's own registration code,
and anything in its build or sync tooling that widens what a refresh can reach.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@
],
"require-dev": {
"composer/semver": "^3.4",
"laravel/pint": "^1.18",
"orchestra/testbench": "^11.0",
"pestphp/pest": "^4.6 || ^5.0",
"phpstan/phpstan": "^2.1",
"laravel/pint": "^1.18",
"symfony/yaml": "^7.0 || ^8.0"
},
"autoload": {
Expand Down
2 changes: 1 addition & 1 deletion src/Providers/IconsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Simtabi\Laranail\Ichava\IconSetsIonicons\Providers;

use Simtabi\Laranail\Ichava\Support\IconPackServiceProvider;
use Simtabi\Laranail\Ichava\Providers\IconPackServiceProvider;
use Simtabi\Laranail\Ichava\IconSetsIonicons\Constants\IconsConstants;
use Simtabi\Laranail\Ichava\IconSetsIonicons\View\Components\IconComponent;

Expand Down
86 changes: 8 additions & 78 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,87 +4,17 @@

namespace Simtabi\Laranail\Ichava\IconSetsIonicons\Tests;

use Orchestra\Testbench\TestCase as Orchestra;
use Simtabi\Laranail\Ichava\Providers\IchavaServiceProvider;
use Simtabi\Laranail\Ichava\Testing\PackTestCase;
use Simtabi\Laranail\Ichava\IconSetsIonicons\Providers\IconsServiceProvider;

abstract class TestCase extends Orchestra
/**
* Boots core and this pack, on the database `DB_CONNECTION` names and a
* non-serialising array cache: core's PackTestCase, which every pack shares.
*/
abstract class TestCase extends PackTestCase
{
protected function getPackageProviders($app): array
protected function packProvider(): string
{
return [
IchavaServiceProvider::class,
IconsServiceProvider::class,
];
}

protected function defineEnvironment($app): void
{
$this->configureDatabase($app);
$this->configureCache($app);
}

/**
* Point the suite at whichever database `DB_CONNECTION` names.
*
* This pack's own tests do not reach the database -- they assert that the provider
* boots, that the constants resolve from `config.json`, and that the enum helpers use
* the configured prefix. The harness still matches `ichava/core` and `ichava/icon-browser`
* so that `DB_CONNECTION=pgsql vendor/bin/pest` means the same thing in every PHP
* package here, and a pack test that does reach the database needs no new plumbing.
*/
protected function configureDatabase($app): void
{
$driver = env('DB_CONNECTION', 'sqlite');

$app['config']->set('database.default', 'testing');
$app['config']->set('database.connections.testing', match ($driver) {
'pgsql' => [
'driver' => 'pgsql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'ichava_test'),
'username' => env('DB_USERNAME', 'ichava'),
'password' => env('DB_PASSWORD', 'secret'),
'charset' => 'utf8',
'prefix' => '',
'search_path' => 'public',
'sslmode' => 'prefer',
],
'mysql', 'mariadb' => [
'driver' => $driver,
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'ichava_test'),
'username' => env('DB_USERNAME', 'ichava'),
'password' => env('DB_PASSWORD', 'secret'),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => 'InnoDB',
],
default => [
'driver' => 'sqlite',
'database' => env('DB_DATABASE', ':memory:'),
'prefix' => '',

// Off by default on SQLite, and Laravel only enables them when the key is
// present, so without this the engine's cascading deletes go unenforced.
'foreign_key_constraints' => true,
],
});
}

protected function configureCache($app): void
{
// The array store returns the object it was given, so nothing here crosses
// serialize()/unserialize(). A test that depends on that boundary must switch to a
// serialising store itself.
$app['config']->set('cache.default', 'array');
$app['config']->set('cache.stores.array', [
'driver' => 'array',
'serialize' => false,
]);
return IconsServiceProvider::class;
}
}
65 changes: 9 additions & 56 deletions tests/Unit/CoreOnlyDependencyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,76 +2,29 @@

declare(strict_types=1);

use Simtabi\Laranail\Ichava\Testing\PackConformance;

/*
|--------------------------------------------------------------------------
| A pack depends on ichava/core, and on nothing else
|--------------------------------------------------------------------------
|
| Core alone uses the laranail/* packages. A pack requires `php` and
| `ichava/core`, extends core's IconPackServiceProvider, and names no class
| from any other Simtabi\Laranail namespace, so a laranail change reaches the
| packs through core and nowhere else.
|
| `Simtabi\Laranail\Ichava\` is core's namespace, and this pack's own sits
| under it. `Simtabi\Laranail\Ichava\IconBrowser\` is ichava/icon-browser's,
| which a pack must not depend on either.
|
| Reads the committed composer.json. CI aliases core's main into it for the
| install and restores the file before the tests run.
| The rule lives once, in core's Testing\PackConformance; this file names it
| for this pack, so the pack's test names stay as they were.
|
| Pest functions only: a PHPUnit class in the same file would stop being
| collected.
| `composer pint` runs `vendor/bin/laranail-pint`, which reaches the pack through
| core's require: a sanctioned transitive dev tool, not a pack dependency.
|
*/

/** @return array<string, mixed> */
function coreOnlyComposerJson(): array
{
$composer = json_decode((string) file_get_contents(dirname(__DIR__, 2) . '/composer.json'), true, flags: JSON_THROW_ON_ERROR);

return is_array($composer) ? $composer : [];
}

it('requires php and ichava/core, and nothing else', function (): void {
$require = array_keys(coreOnlyComposerJson()['require'] ?? []);
sort($require);

expect($require)->toBe(['ichava/core', 'php']);
PackConformance::requiresOnlyCore(dirname(__DIR__, 2));
});

it('declares the composer type core discovers packs by', function (): void {
expect(coreOnlyComposerJson()['type'] ?? null)->toBe('ichava-icon-set');
PackConformance::declaresPackType(dirname(__DIR__, 2));
});

it('imports no laranail namespace but ichava/core from src/', function (): void {
$inspected = 0;
$offenders = [];

$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(dirname(__DIR__, 2) . '/src', FilesystemIterator::SKIP_DOTS),
);

foreach ($files as $file) {
if (! $file instanceof SplFileInfo || ! $file->isFile() || $file->getExtension() !== 'php') {
continue;
}

$inspected++;
$source = (string) file_get_contents($file->getPathname());

// Every spelling: a `use` line, a fully qualified name, or a string with
// escaped backslashes.
preg_match_all('/Simtabi\\\\+Laranail\\\\+(\w+)(?:\\\\+(\w+))?/', $source, $matches, PREG_SET_ORDER);

foreach ($matches as $match) {
if ($match[1] !== 'Ichava' || ($match[2] ?? '') === 'IconBrowser') {
$offenders[] = $file->getFilename() . ': ' . $match[0];
}
}
}

// Every pack has a provider, a constants class, an enum and a component. A
// walk that stops finding them must fail, not pass over an empty tree.
expect($inspected)->toBeGreaterThanOrEqual(4)
->and($offenders)->toBe([]);
PackConformance::importsOnlyCore(dirname(__DIR__, 2));
});
Loading
Loading