Commit 2b6e2f08 authored by Gatterdam Patrick's avatar Gatterdam Patrick
Browse files

Merge branch 'feature/typo3-v10-support' into 'master'

Update to TYPO3 v10

See merge request riconet-public/typo3/rico_social_clubs_events!1
parents 0516cb40 f79a756f
version: '2.3'
services:
#======================================================
# DB
#======================================================
mariadb10:
image: mariadb:10
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: funcp
tmpfs:
- /var/lib/mysql/:rw,noexec,nosuid
#======================================================
# Build
#======================================================
build:
image: edbizarro/gitlab-ci-pipeline-php:${PHP_VERSION}-alpine
volumes:
- ${ROOT_DIR}:${ROOT_DIR}
working_dir: ${ROOT_DIR}
command: >
/bin/sh -c "
composer install;
"
#======================================================
# Lint
#======================================================
lint:
image: typo3gmbh/php72:latest
volumes:
- ${ROOT_DIR}:${ROOT_DIR}
working_dir: ${ROOT_DIR}
command: >
/bin/sh -c "
echo \"Lint php...\";
php -v | grep '^PHP';
find . -name \\*.php ! -path "./.Build/\\*" -print0 | xargs -0 -n1 -P4 php -n -c /etc/php/cli-no-xdebug/php.ini -l >/dev/null
echo \"Lint Typoscript...\";
.Build/bin/typoscript-lint Configuration/TypoScript/
echo \"Lint for debug calls...\";
if grep -rnw "Resources/Private/" "Classes/" -e "debug" -e "DebuggerUtility"; then
exit 1
fi
"
#======================================================
# Quality
#======================================================
quality:
image: php:${PHP_VERSION}-alpine
volumes:
- ${ROOT_DIR}:${ROOT_DIR}
working_dir: ${ROOT_DIR}
command: >
/bin/sh -c "
echo \"Running phpcs...\";
.Build/bin/phpcs --error-severity=1 --warning-severity=8 --extensions=php Classes;
echo \"Running php-cs-fixer...\";
.Build/bin/php-cs-fixer fix --allow-risky=yes --dry-run --diff
echo \"Running phpmd...\";
.Build/bin/phpmd Classes text phpmd.xml;
echo \"Running phpstan...\";
php -d memory_limit=4G .Build/bin/phpstan analyse -c phpstan.neon
"
#======================================================
# Fix
#======================================================
fix:
image: riconet/typo3:9.5
volumes:
- ${ROOT_DIR}:${ROOT_DIR}
working_dir: ${ROOT_DIR}
command: >
/bin/sh -c "
echo \"Fix quality errors...\";
.Build/bin/php-cs-fixer fix --allow-risky=yes
.Build/bin/phpcbf --error-severity=1 --warning-severity=8 --extensions=php
"
#======================================================
# Functional tests
#======================================================
functional:
image: riconet/typo3:9.5
links:
- mariadb10
volumes:
- ${ROOT_DIR}:${ROOT_DIR}
environment:
typo3DatabaseName: func_test
typo3DatabaseUsername: root
typo3DatabasePassword: funcp
typo3DatabaseHost: mariadb10
working_dir: ${ROOT_DIR}/.Build
command: >
/bin/sh -c "
echo Waiting for database start...;
while ! nc -z mariadb10 3306; do
sleep 1;
done;
echo Database is up;
bin/phpunit -c vendor/nimut/testing-framework/res/Configuration/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ../Tests/Functional;
rm -rf Web/typo3temp/var/tests/func*
"
#======================================================
# Unit tests
#======================================================
unit:
image: riconet/typo3:9.5
volumes:
- ${ROOT_DIR}:${ROOT_DIR}
working_dir: ${ROOT_DIR}/.Build
command: >
/bin/sh -c "
php bin/phpunit -c vendor/nimut/testing-framework/res/Configuration/UnitTests.xml ${EXTRA_TEST_OPTIONS} ../Tests/Unit;
"
#!/usr/bin/env bash
# Function to write a .env file in Build/docker
# This is read by docker-compose and vars defined here are
# used in .ci/docker/docker-compose.yml
# Function to write a .env file in Build/testing-docker
setUpDockerComposeDotEnv() {
[ -e .env ] && rm .env
echo "TEST_FILE=${TEST_FILE}" >>.env
echo "ROOT_DIR=${ROOT_DIR}" >>.env
echo "EXTRA_TEST_OPTIONS=${EXTRA_TEST_OPTIONS}" >>.env
echo "PHP_VERSION=${PHP_VERSION}" >>.env
# we have to set it explicitly here.
echo "HOST_UID=`id -u`" >> .env
# Your local home directory for composer and npm caching
echo "HOST_HOME=${HOME}" >> .env
}
# Function to get the real path on mac os.
realpath() {
if ! pushd $1 &>/dev/null; then
pushd ${1##*/} &>/dev/null
echo $(pwd -P)/${1%/*}
else
pwd -P
fi
popd >/dev/null
}
# Load help text into $HELP
read -r -d '' HELP <<EOF
Extension test runner. Execute unit test suite and some other details.
Usage: $0 [options] [file]
Options:
-s <...>
Specifies which test suite to run
- build: Builds the project (composer)
- lint: Lints php and typoscript files. Also finds usages of debug calls.
- unit (default): PHP unit tests
- quality: executes code quality checks (phpstan, phpcs, phpmd)
- fix: Fixes some quality errors automatically.
-p <7.2>
Specifies the PHP minor version to be used
- 7.2 (default): use PHP 7.2
-e "<phpunit options>"
Only with -s functional|unit
Additional options to send to phpunit (unit & functional tests).
Starting with "--" must be added after options starting with "-".
Example -e "-v --filter canRetrieveValueWithGP" to enable verbose output AND filter tests
named "canRetrieveValueWithGP"
-h
Show this help.
Examples:
# Run unit tests
.ci/scripts/console.sh -s build
EOF
# Test if docker-compose exists, else exit out with error
if ! type "docker-compose" >/dev/null; then
echo "This script relies on docker and docker-compose. Please install" >&2
exit 1
fi
# Go to the directory this script is located, so everything else is relative
# to this dir, no matter from where this script is called.
THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
cd "$THIS_SCRIPT_DIR" || exit 1
# Go to directory that contains the local docker-compose.yml file
cd ../docker || exit 1
# Option defaults
ROOT_DIR=$(realpath $PWD"/../../")
TEST_SUITE="unit"
PHP_VERSION="7.2"
EXTRA_TEST_OPTIONS=""
# Option parsing
# Reset in case getopts has been used previously in the shell
OPTIND=1
# Array for invalid options
INVALID_OPTIONS=()
# Simple option parsing based on getopts (! not getopt)
while getopts ":s:d:p:e:xy:huv" OPT; do
case ${OPT} in
s)
TEST_SUITE=${OPTARG}
;;
p)
PHP_VERSION=${OPTARG}
;;
e)
EXTRA_TEST_OPTIONS=${OPTARG}
;;
h)
echo "${HELP}"
exit 0
;;
\?)
INVALID_OPTIONS+=(${OPTARG})
;;
:)
INVALID_OPTIONS+=(${OPTARG})
;;
esac
done
# Exit on invalid options
if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then
echo "Invalid option(s):" >&2
for I in "${INVALID_OPTIONS[@]}"; do
echo "-"${I} >&2
done
echo >&2
echo "${HELP}" >&2
exit 1
fi
# Set $1 to first mass argument, this is the optional test file or test directory to execute
shift $((OPTIND - 1))
if [ -n "${1}" ]; then
TEST_FILE="${ROOT_DIR}/${1}"
else
case ${TEST_SUITE} in
unit)
TEST_FILE="${ROOT_DIR}/tests/Unit"
;;
esac
fi
# Suite execution
case ${TEST_SUITE} in
build)
setUpDockerComposeDotEnv
docker-compose run build
SUITE_EXIT_CODE=$?
docker-compose down
;;
lint)
setUpDockerComposeDotEnv
docker-compose run lint
SUITE_EXIT_CODE=$?
docker-compose down
;;
quality)
setUpDockerComposeDotEnv
docker-compose run quality
SUITE_EXIT_CODE=$?
docker-compose down
;;
fix)
setUpDockerComposeDotEnv
docker-compose run fix
SUITE_EXIT_CODE=$?
docker-compose down
;;
unit)
setUpDockerComposeDotEnv
docker-compose run unit
SUITE_EXIT_CODE=$?
docker-compose down
;;
functional)
setUpDockerComposeDotEnv
docker-compose run functional
SUITE_EXIT_CODE=$?
docker-compose down
;;
*)
echo "Invalid -s option argument ${TEST_SUITE}" >&2
echo >&2
echo "${HELP}" >&2
exit 1
;;
esac
exit $SUITE_EXIT_CODE
; top-most EditorConfig file
root = true
; Unix-style newlines
[*]
charset = utf-8
end_of_line = LF
insert_final_newline = true
trim_trailing_whitespace = true
[*.{php,html,typoscript,yml,yaml,xml}]
indent_style = space
indent_size = 4
max_line_length = 120
[*.{yml,yaml}]
indent_size = 2
[*.md]
max_line_length = 120
[COMMIT_EDITMSG]
max_line_length = 0
/yarn.lock
Resources/Public/Vendor/
composer.lock
vendor/
typo3/
index.php
.idea/
.Build/
.ci/docker/.env
.ci/docker/.database
.php_cs.cache
/var/
Resources/Public/Dist/*
!Resources/Public/Dist/.gitkeep
node_modules/
package-lock.json
calendar.css.map
events.css.map
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .Build/
- node_modules/
stages:
- build
- lint
- quality
- test
composer:
image: composer:2.0
stage: build
script:
- composer -q -n install
artifacts:
expire_in: 1 hour
paths:
- .Build/
only:
- merge_requests
lint-php:
image: php:7.2-alpine
stage: lint
script:
- php -v | grep '^PHP';
- find . -name \\*.php ! -path "./.Build/\\*" -print0 | xargs -0 -n1 -P4 php -n -c /etc/php/cli-no-xdebug/php.ini -l >/dev/null;
only:
- merge_requests
lint-typoscript:
image: php:7.2-alpine
stage: quality
script:
- .Build/bin/typoscript-lint Configuration/TypoScript/
- .Build/bin/typoscript-lint Configuration/PageTSconfig/
only:
- merge_requests
phpstan:
image: php:7.2-alpine
stage: quality
script:
- php -d memory_limit=4G .Build/bin/phpstan analyse
only:
- merge_requests
codesniffer:
image: php:7.2-alpine
stage: quality
script:
- .Build/bin/phpcs --error-severity=1 --warning-severity=8 --extensions=php Classes
only:
- merge_requests
php-cs-fixer:
image: php:7.2-alpine
stage: quality
script:
- .Build/bin/php-cs-fixer fix --allow-risky=yes --dry-run --diff
only:
- merge_requests
phpmd:
image: php:7.2-alpine
stage: quality
script:
- .Build/bin/phpmd Classes text phpmd.xml
only:
- merge_requests
debug_call_usages:
image: php:7.2-alpine
stage: quality
script:
- |
if grep -rnw "Resources/Private/" "Classes/" -e "debug" -e "DebuggerUtility"
then
exit 1;
fi
only:
- merge_requests
unittests:
image: riconet/typo3:10.4
stage: test
script:
.Build/bin/phpunit -c .Build/vendor/nimut/testing-framework/res/Configuration/UnitTests.xml "" .Build/Web/typo3conf/ext/${CI_PROJECT_NAME}/Tests/Unit
only:
- merge_requests
functionaltests:
image: riconet/typo3:10.4
stage: test
variables:
MYSQL_ROOT_PASSWORD: funcp
typo3DatabaseName: func_test
typo3DatabaseUsername: root
typo3DatabasePassword: funcp
typo3DatabaseHost: mariadb
services:
- mariadb:10
script:
.Build/bin/phpunit -c .Build/vendor/nimut/testing-framework/res/Configuration/FunctionalTests.xml "" .Build/Web/typo3conf/ext/${CI_PROJECT_NAME}/Tests/Functional
only:
- merge_requests
<?php
$finder = PhpCsFixer\Finder::create()
->in([
__DIR__ . '/Classes/',
__DIR__ . '/Tests/',
__DIR__ . '/Configuration/TCA/'
])
->exclude(__DIR__ . '/.Build/');
$header = <<<EOF
This file is part of the "rico_social_clubs_events" Extension for TYPO3 CMS.
For the full copyright and license information, please read the
LICENSE.txt file that was distributed with this source code.
(c) 2020 PSVneo
EOF;
return PhpCsFixer\Config::create()
->setUsingCache(false)
->setRules([
'@PSR1' => true,
'@PSR2' => true,
'@Symfony' => true,
'header_comment' => [
'header' => $header,
'location' => 'after_open',
'separate' => 'both',
'commentType' => 'PHPDoc',
],
'no_useless_else' => true,
'no_useless_return' => true,
'no_unused_imports' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'phpdoc_order' => true,
'phpdoc_summary' => false,
'blank_line_after_opening_tag' => false,
'concat_space' => ['spacing' => 'one'],
'array_syntax' => ['syntax' => 'short'],
'yoda_style' => true,
'declare_strict_types' => true,
'psr4' => true,
'no_php4_constructor' => true,
'no_short_echo_tag' => true,
'semicolon_after_instruction' => true,
'align_multiline_comment' => true,
'general_phpdoc_annotation_remove' => ['annotations' => ["author", "package"]],
'phpdoc_add_missing_param_annotation' => ['only_untyped' => false],
])
->setFinder($finder);
<?php
/**
* This file is part of the "rico_social_clubs_events" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* (c) 2020 PSVneo
*/
declare(strict_types=1);
namespace Riconet\RicoSocialClubsEvents\Command;
use DateTime;
use ParseCsv\Csv;
use Riconet\RicoSocialClubsEvents\Domain\Component\TimeTrackerInterface;
use Riconet\RicoSocialClubsEvents\Domain\Model\Event;
use Riconet\RicoSocialClubsEvents\Domain\Repository\EventRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class ExportCommand extends Command
{
public const SUCCESS = 0;
public const FAILURE = 1;
/**
* @var EventRepository
*/
protected $eventRepository;
/**
* @var TimeTrackerInterface
*/
protected $timeTracker;
public function __construct(EventRepository $eventRepository, TimeTrackerInterface $timeTracker)
{
$this->eventRepository = $eventRepository;
$this->timeTracker = $timeTracker;
parent::__construct(null);
}
protected function configure(): void
{
$this->setDescription('This command exports all events in the give category as a csv file.');
$this->addArgument('year', InputArgument::REQUIRED, 'The minimum year of the start date time of the events.');
$this->addArgument('pids', InputArgument::REQUIRED, 'Comma separated string of storage uids.');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
$this->timeTracker->start();
$year = intval($input->getArgument('year'));
$pids = GeneralUtility::intExplode(',', strval($input->getArgument('pids')));
$events = $this->getEvents($year, $pids);
$output->writeln('Start export of ' . count($events) . ' events...');
$filePath = $this->buildCsvFile($events);
$passedTime = $this->timeTracker->stop();
$output->writeln("Execution took $passedTime seconds.");
$output->writeln("Saved export file to \"$filePath\".");
} catch (\Exception $exception) {
$output->writeln('Error occurred: ' . $exception->getMessage());
return self::FAILURE;
}
return self::SUCCESS;
}
/**
* @return Event[]
*/
protected function getEvents(int $year, array $pids): array
{
$date = DateTime::createFromFormat('d.m.Y', "01.01.$year");
/** @var Event[] $result */
$result = $this->eventRepository->findByStartDateGreaterThanOrEqual(
$date ? $date : new DateTime(),
$pids
);
return $result;
}
/**
* @param Event[] $events
*/
protected function buildCsvFile(array $events): string
{
$csv = $this->buildCsvObject();
$filePath = $this->buildCsvFilePath();
$csv->save($filePath, $this->buildCsvData($events), true);
return $filePath;
}
protected function buildCsvObject(): Csv
{
$csv = new Csv();
$csv->encoding('UTF-8', 'UTF-8');
$csv->delimiter = ';';
return $csv;
}
protected function buildCsvFilePath(): string
{
$name = 'export-' . (string) date('d-m-Y-H-i-s');
$exportFolderPath = (string) GeneralUtility::getFileAbsFileName('fileadmin/export');
echo $exportFolderPath . PHP_EOL;
if (!is_dir($exportFolderPath)) {
mkdir($exportFolderPath, 0777, true);
}
return "$exportFolderPath/$name.csv";
}
/**
* @param Event[] $events
*/
protected function buildCsvData(array $events): array