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
36 changes: 36 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# https://editorconfig.org

# SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true

[*.yml]
indent_size = 2
indent_style = space

[*.md]
trim_trailing_whitespace = false

[*.svg]
insert_final_newline = false

[package*.json]
indent_size = 2
indent_style = space

[build/psalm-baseline.xml]
indent_size = 2
indent_style = space

[config/*config.php]
indent_size = 2
indent_style = space
18 changes: 16 additions & 2 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
# Retrofit annotation commit (opsx-annotate, 2026-05-24)
e7a6be7bfd26417b65dbca481ff0468821434f61
# Revisions to skip in `git blame`.
#
# Enable locally, once:
# git config blame.ignoreRevsFile .git-blame-ignore-revs
#
# GitHub reads this file automatically. Your terminal does not, until you run
# the line above.
#
# Only ever add commits that change formatting and NOTHING else. A commit listed
# here becomes invisible to blame, so a behaviour change hidden inside one would
# be very hard to find later.

# style: reformat with nextcloud/coding-standard — whitespace only
# The fleet-wide move from a PEAR-derived PHPCS ruleset (4 spaces, next-line
# braces) to Nextcloud's own standard (tabs, same-line braces).
a78e00aa9f0e963156dc4c58277776c783837a2d
2 changes: 1 addition & 1 deletion .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
with:
app-name: softwarecatalog
php-version: "8.3"
nextcloud-test-refs: '["stable32"]'
nextcloud-test-refs: '["stable34"]'
enable-psalm: true
enable-phpstan: true
enable-phpmetrics: true
Expand Down
20 changes: 20 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

// SPDX-FileCopyrightText: 2026 Conduction
// SPDX-License-Identifier: EUPL-1.2

// The require_once is NOT optional. php-cs-fixer includes this file before the
// app's autoloader runs, so without it the run dies with "Class not found" —
// and in --format=json that fatal is reported as ZERO FILES NEEDING CHANGES,
// which reads exactly like a clean tree.
require_once __DIR__ . '/vendor/autoload.php';

$config = new Conduction\CodingStandard\Config();
$config->getFinder()
->notPath('vendor')
->notPath('node_modules')
->notPath('build')
->in(__DIR__ . '/lib')
->in(__DIR__ . '/tests');

return $config;
24 changes: 12 additions & 12 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,20 @@ Vrij en open source onder de EUPL-licentie.
CI already tests stable32 only, so this changes the advertised app-store
range and nothing about what runs.
-->
<nextcloud min-version="32" max-version="34"/>
<php min-version="8.3" min-int-size="64"/>
<database min-version="10">pgsql</database>
<database>sqlite</database>
<database min-version="8.0">mysql</database>
<nextcloud min-version="32" max-version="34"/>
</dependencies>

<background-jobs>
<job>OCA\SoftwareCatalog\BackgroundJob\OrganizationContactSyncJob</job>
<job>OCA\SoftwareCatalog\BackgroundJob\ContractStatusJob</job>
<job>OCA\SoftwareCatalog\BackgroundJob\FederationSyncJob</job>
<job>OCA\SoftwareCatalog\BackgroundJob\EolSyncJob</job>
</background-jobs>

<repair-steps>
<post-migration>
<step>OCA\SoftwareCatalog\Repair\InitializeSettings</step>
Expand All @@ -110,12 +117,10 @@ Vrij en open source onder de EUPL-licentie.
</post-migration>
</repair-steps>

<background-jobs>
<job>OCA\SoftwareCatalog\BackgroundJob\OrganizationContactSyncJob</job>
<job>OCA\SoftwareCatalog\BackgroundJob\ContractStatusJob</job>
<job>OCA\SoftwareCatalog\BackgroundJob\FederationSyncJob</job>
<job>OCA\SoftwareCatalog\BackgroundJob\EolSyncJob</job>
</background-jobs>
<settings>
<admin>OCA\SoftwareCatalog\Settings\SoftwareCatalogAdmin</admin>
<admin-section>OCA\SoftwareCatalog\Sections\SoftwareCatalogAdmin</admin-section>
</settings>

<navigations>
<navigation>
Expand All @@ -125,9 +130,4 @@ Vrij en open source onder de EUPL-licentie.
<icon>app.svg</icon>
</navigation>
</navigations>

<settings>
<admin>OCA\SoftwareCatalog\Settings\SoftwareCatalogAdmin</admin>
<admin-section>OCA\SoftwareCatalog\Sections\SoftwareCatalogAdmin</admin-section>
</settings>
</info>
215 changes: 108 additions & 107 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,109 +1,110 @@
{
"name": "conductionnl/softwarecatalog",
"description": "Quickly build data registers based on schema.json",
"license": "EUPL-1.2",
"authors": [
{
"name": "Conduction b.v.",
"email": "info@conduction.nl",
"homepage": "https://conduction.nl"
}
],
"autoload": {
"psr-4": {
"OCA\\SoftwareCatalog\\": "lib/"
}
},
"scripts": {
"post-install-cmd": [
"@composer bin all install --ansi"
],
"post-update-cmd": [
"@composer bin all update --ansi"
],
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "./vendor/bin/phpcs --standard=phpcs.xml",
"cs:fix": "./vendor/bin/phpcbf --standard=phpcs.xml",
"phpcs": "./vendor/bin/phpcs --standard=phpcs.xml",
"phpcs:fix": "./vendor/bin/phpcbf --standard=phpcs.xml",
"phpcs:output": "./vendor/bin/phpcs --standard=phpcs.xml --report=json lib/ 2>/dev/null | tail -1 > phpcs-output.json",
"phpmd": "E=0; ./vendor/bin/phpmd lib text phpmd.xml --baseline-file phpmd.baseline.xml || E=$?; ./vendor/bin/phpmd lib text phpmd-unusedparams.xml --baseline-file phpmd.baseline.xml || E=$?; exit $E",
"phpmetrics": "./vendor/bin/phpmetrics --report-html=phpmetrics lib/",
"phpmetrics:violations": "./vendor/bin/phpmetrics --violations-xml=phpmetrics/violations.xml lib/",
"psalm": "if [ -f vendor/bin/psalm ]; then ./vendor/bin/psalm --threads=1 --no-cache; else echo 'Psalm not installed, skipping...'; fi",
"phpstan": "if [ -f vendor/bin/phpstan ]; then ./vendor/bin/phpstan analyse --memory-limit=1G; else echo 'PHPStan not installed, skipping...'; fi",
"test:unit": "phpunit tests -c tests/phpunit.xml --colors=always --fail-on-warning --fail-on-risky",
"test:all": "if [ ! -f vendor/bin/phpunit ]; then echo 'SKIPPED: phpunit not installed - run composer install'; elif [ ! -f ../../lib/base.php ]; then echo 'SKIPPED: tests/bootstrap.php requires a Nextcloud server tree (../../lib/base.php not found) - run from inside a Nextcloud checkout or in CI'; else ./vendor/bin/phpunit --colors=always; fi",
"check": "E=0; for CMD in lint phpcs psalm test:unit; do echo; echo \"=== $CMD ===\"; composer $CMD || E=1; done; echo; if [ $E -eq 0 ]; then echo \"ALL CHECKS PASSED\"; else echo \"SOME CHECKS FAILED (see above)\"; fi; exit $E",
"check:full": "E=0; for CMD in lint phpcs psalm phpstan test:all; do echo; echo \"=== $CMD ===\"; composer $CMD || E=1; done; echo; if [ $E -eq 0 ]; then echo \"ALL CHECKS PASSED\"; else echo \"SOME CHECKS FAILED (see above)\"; fi; exit $E",
"check:strict": "E=0; for CMD in lint phpcs phpmd psalm phpstan test:all; do echo; echo \"=== $CMD ===\"; composer $CMD || E=1; done; echo; if [ $E -eq 0 ]; then echo \"ALL CHECKS PASSED\"; else echo \"SOME CHECKS FAILED (see above)\"; fi; exit $E",
"fix": [
"@cs:fix"
],
"openapi": "generate-spec",
"phpqa": "./vendor/bin/phpqa --report --analyzedDirs lib --buildDir phpqa",
"phpqa:full": "./vendor/bin/phpqa --report --analyzedDirs lib --buildDir phpqa --tools phpcs:0,phpmd:0,phploc:0,phpmetrics,phpcpd:0,parallel-lint:0",
"phpqa:ci": "./vendor/bin/phpqa --report --analyzedDirs lib --buildDir phpqa --tools phpcs,phpmd,phploc,phpmetrics,phpcpd,parallel-lint",
"qa:check": [
"@phpqa"
],
"qa:full": [
"@phpqa:full"
],
"test:coverage": "./vendor/bin/phpunit --coverage-html=coverage/html --coverage-clover=coverage/clover.xml --colors=always",
"coverage:check": "php -r \"\\$xml = simplexml_load_file('coverage/clover.xml'); \\$metrics = \\$xml->project->metrics; \\$statements = (int)\\$metrics['statements']; \\$covered = (int)\\$metrics['coveredstatements']; \\$percentage = \\$statements > 0 ? round((\\$covered / \\$statements) * 100, 2) : 0; echo 'Coverage: ' . \\$percentage . '%' . PHP_EOL; exit(\\$percentage < 75 ? 1 : 0);\"",
"quality:phpcs-score": "./vendor/bin/phpcs --standard=phpcs.xml --report=json lib/ | php -r \"\\$json = json_decode(file_get_contents('php://stdin'), true); \\$errors = \\$json['totals']['errors'] ?? 0; \\$warnings = \\$json['totals']['warnings'] ?? 0; \\$score = 1000 - \\$errors - (\\$warnings / 2); echo 'PHPCS Score: ' . \\$score . ' (Errors: ' . \\$errors . ', Warnings: ' . \\$warnings . ')' . PHP_EOL;\"",
"quality:phpmd-score": "phpmd lib/ json phpmd.xml | php -r \"\\$input = file_get_contents('php://stdin'); \\$json = json_decode(\\$input, true); \\$violations = count(\\$json['files'] ?? []); \\$score = 1000 - (\\$violations * 10); echo 'PHPMD Score: ' . \\$score . ' (Violations: ' . \\$violations . ')' . PHP_EOL;\" || echo 'PHPMD not available'",
"quality:psalm-score": "./vendor/bin/psalm --output-format=json --no-cache | php -r \"\\$input = file_get_contents('php://stdin'); \\$json = json_decode(\\$input, true); \\$errors = count(\\$json ?? []); \\$score = 1000 - (\\$errors * 5); echo 'Psalm Score: ' . \\$score . ' (Errors: ' . \\$errors . ')' . PHP_EOL;\" || echo 'Psalm not available'",
"quality:phpstan-score": "./vendor/bin/phpstan analyse --memory-limit=1G --error-format=json --no-progress | php -r \"\\$input = file_get_contents('php://stdin'); \\$json = json_decode(\\$input, true); \\$errors = \\$json['totals']['file_errors'] ?? 0; \\$score = 1000 - (\\$errors * 5); echo 'PHPStan Score: ' . \\$score . ' (Errors: ' . \\$errors . ')' . PHP_EOL;\" || echo 'PHPStan not available'",
"quality:score": [
"@quality:phpcs-score",
"@quality:phpmd-score",
"@quality:psalm-score",
"@quality:phpstan-score"
]
},
"require": {
"php": "^8.3",
"adbario/php-dot-notation": "^3.3.0",
"bamarni/composer-bin-plugin": "^1.8",
"react/event-loop": "^1.5",
"react/promise": "^3.2",
"symfony/amazon-mailer": "^6.4",
"symfony/http-client": "^6.4",
"symfony/mailer": "^6.4",
"symfony/mailgun-mailer": "^6.4",
"symfony/mailjet-mailer": "^6.4",
"symfony/postmark-mailer": "^6.4",
"symfony/sendgrid-mailer": "^6.4",
"twig/twig": "^3.27.0"
},
"require-dev": {
"cyclonedx/cyclonedx-php-composer": "^6.2",
"edgedesign/phpqa": "^1.27",
"guzzlehttp/guzzle": "^7.8",
"nextcloud/coding-standard": "^1.4",
"nextcloud/ocp": "^31.0",
"phpcsstandards/phpcsextra": "^1.4",
"phpmd/phpmd": "^2.15",
"phpmetrics/phpmetrics": "^2.8",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^10.5",
"roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.9",
"vimeo/psalm": "^5.26"
},
"config": {
"allow-plugins": {
"bamarni/composer-bin-plugin": true,
"php-http/discovery": true,
"dealerdirect/phpcodesniffer-composer-installer": true,
"cyclonedx/cyclonedx-php-composer": true
},
"optimize-autoloader": true,
"sort-packages": true,
"platform": {
"php": "8.3"
}
}
"name": "conductionnl/softwarecatalog",
"description": "Quickly build data registers based on schema.json",
"license": "EUPL-1.2",
"authors": [
{
"name": "Conduction b.v.",
"email": "info@conduction.nl",
"homepage": "https://conduction.nl"
}
],
"autoload": {
"psr-4": {
"OCA\\SoftwareCatalog\\": "lib/"
}
},
"scripts": {
"post-install-cmd": [
"@composer bin all install --ansi"
],
"post-update-cmd": [
"@composer bin all update --ansi"
],
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './vendor-bin/*' -not -path './build/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"phpcs": "./vendor/bin/phpcs --standard=phpcs.xml",
"phpcs:fix": "./vendor/bin/phpcbf --standard=phpcs.xml",
"phpcs:output": "./vendor/bin/phpcs --standard=phpcs.xml --report=json lib/ 2>/dev/null | tail -1 > phpcs-output.json",
"phpmd": "E=0; ./vendor/bin/phpmd lib text phpmd.xml --baseline-file phpmd.baseline.xml || E=$?; ./vendor/bin/phpmd lib text phpmd-unusedparams.xml --baseline-file phpmd.baseline.xml || E=$?; exit $E",
"phpmetrics": "./vendor/bin/phpmetrics --report-html=phpmetrics lib/",
"phpmetrics:violations": "./vendor/bin/phpmetrics --violations-xml=phpmetrics/violations.xml lib/",
"psalm": "if [ -f vendor/bin/psalm ]; then ./vendor/bin/psalm --threads=1 --no-cache; else echo 'Psalm not installed, skipping...'; fi",
"phpstan": "if [ -f vendor/bin/phpstan ]; then ./vendor/bin/phpstan analyse --memory-limit=1G; else echo 'PHPStan not installed, skipping...'; fi",
"test:unit": "phpunit tests -c tests/phpunit.xml --colors=always --fail-on-warning --fail-on-risky",
"test:all": "if [ ! -f vendor/bin/phpunit ]; then echo 'SKIPPED: phpunit not installed - run composer install'; elif [ ! -f ../../lib/base.php ]; then echo 'SKIPPED: tests/bootstrap.php requires a Nextcloud server tree (../../lib/base.php not found) - run from inside a Nextcloud checkout or in CI'; else ./vendor/bin/phpunit --colors=always; fi",
"check": "E=0; for CMD in lint phpcs psalm test:unit; do echo; echo \"=== $CMD ===\"; composer $CMD || E=1; done; echo; if [ $E -eq 0 ]; then echo \"ALL CHECKS PASSED\"; else echo \"SOME CHECKS FAILED (see above)\"; fi; exit $E",
"check:full": "E=0; for CMD in lint phpcs psalm phpstan test:all; do echo; echo \"=== $CMD ===\"; composer $CMD || E=1; done; echo; if [ $E -eq 0 ]; then echo \"ALL CHECKS PASSED\"; else echo \"SOME CHECKS FAILED (see above)\"; fi; exit $E",
"check:strict": "E=0; for CMD in lint phpcs phpmd psalm phpstan test:all; do echo; echo \"=== $CMD ===\"; composer $CMD || E=1; done; echo; if [ $E -eq 0 ]; then echo \"ALL CHECKS PASSED\"; else echo \"SOME CHECKS FAILED (see above)\"; fi; exit $E",
"fix": [
"@cs:fix"
],
"openapi": "generate-spec",
"phpqa": "./vendor/bin/phpqa --report --analyzedDirs lib --buildDir phpqa",
"phpqa:full": "./vendor/bin/phpqa --report --analyzedDirs lib --buildDir phpqa --tools phpcs:0,phpmd:0,phploc:0,phpmetrics,phpcpd:0,parallel-lint:0",
"phpqa:ci": "./vendor/bin/phpqa --report --analyzedDirs lib --buildDir phpqa --tools phpcs,phpmd,phploc,phpmetrics,phpcpd,parallel-lint",
"qa:check": [
"@phpqa"
],
"qa:full": [
"@phpqa:full"
],
"test:coverage": "./vendor/bin/phpunit --coverage-html=coverage/html --coverage-clover=coverage/clover.xml --colors=always",
"coverage:check": "php -r \"\\$xml = simplexml_load_file('coverage/clover.xml'); \\$metrics = \\$xml->project->metrics; \\$statements = (int)\\$metrics['statements']; \\$covered = (int)\\$metrics['coveredstatements']; \\$percentage = \\$statements > 0 ? round((\\$covered / \\$statements) * 100, 2) : 0; echo 'Coverage: ' . \\$percentage . '%' . PHP_EOL; exit(\\$percentage < 75 ? 1 : 0);\"",
"quality:phpcs-score": "./vendor/bin/phpcs --standard=phpcs.xml --report=json lib/ | php -r \"\\$json = json_decode(file_get_contents('php://stdin'), true); \\$errors = \\$json['totals']['errors'] ?? 0; \\$warnings = \\$json['totals']['warnings'] ?? 0; \\$score = 1000 - \\$errors - (\\$warnings / 2); echo 'PHPCS Score: ' . \\$score . ' (Errors: ' . \\$errors . ', Warnings: ' . \\$warnings . ')' . PHP_EOL;\"",
"quality:phpmd-score": "phpmd lib/ json phpmd.xml | php -r \"\\$input = file_get_contents('php://stdin'); \\$json = json_decode(\\$input, true); \\$violations = count(\\$json['files'] ?? []); \\$score = 1000 - (\\$violations * 10); echo 'PHPMD Score: ' . \\$score . ' (Violations: ' . \\$violations . ')' . PHP_EOL;\" || echo 'PHPMD not available'",
"quality:psalm-score": "./vendor/bin/psalm --output-format=json --no-cache | php -r \"\\$input = file_get_contents('php://stdin'); \\$json = json_decode(\\$input, true); \\$errors = count(\\$json ?? []); \\$score = 1000 - (\\$errors * 5); echo 'Psalm Score: ' . \\$score . ' (Errors: ' . \\$errors . ')' . PHP_EOL;\" || echo 'Psalm not available'",
"quality:phpstan-score": "./vendor/bin/phpstan analyse --memory-limit=1G --error-format=json --no-progress | php -r \"\\$input = file_get_contents('php://stdin'); \\$json = json_decode(\\$input, true); \\$errors = \\$json['totals']['file_errors'] ?? 0; \\$score = 1000 - (\\$errors * 5); echo 'PHPStan Score: ' . \\$score . ' (Errors: ' . \\$errors . ')' . PHP_EOL;\" || echo 'PHPStan not available'",
"quality:score": [
"@quality:phpcs-score",
"@quality:phpmd-score",
"@quality:psalm-score",
"@quality:phpstan-score"
]
},
"require": {
"php": "^8.3",
"adbario/php-dot-notation": "^3.3.0",
"bamarni/composer-bin-plugin": "^1.8",
"react/event-loop": "^1.5",
"react/promise": "^3.2",
"symfony/amazon-mailer": "^6.4",
"symfony/http-client": "^6.4",
"symfony/mailer": "^6.4",
"symfony/mailgun-mailer": "^6.4",
"symfony/mailjet-mailer": "^6.4",
"symfony/postmark-mailer": "^6.4",
"symfony/sendgrid-mailer": "^6.4",
"twig/twig": "^3.27.0"
},
"require-dev": {
"conduction/coding-standard": "^1.0",
"conduction/hydra-gates": "^1.0",
"cyclonedx/cyclonedx-php-composer": "^6.2",
"edgedesign/phpqa": "^1.27",
"guzzlehttp/guzzle": "^7.8",
"nextcloud/ocp": "^34.0",
"phpcsstandards/phpcsextra": "^1.4",
"phpmd/phpmd": "^2.15",
"phpmetrics/phpmetrics": "^2.8",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^10.5",
"roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.9",
"vimeo/psalm": "^5.26"
},
"config": {
"allow-plugins": {
"bamarni/composer-bin-plugin": true,
"php-http/discovery": true,
"dealerdirect/phpcodesniffer-composer-installer": true,
"cyclonedx/cyclonedx-php-composer": true
},
"optimize-autoloader": true,
"sort-packages": true,
"platform": {
"php": "8.3"
}
}
}
Loading
Loading