Skip to content
Snippets Groups Projects
Commit 56aecb46 authored by dpi's avatar dpi
Browse files

Gitlab CI Setup

parent 53cf3ba0
Branches 3.2.x
Tags 3.2.0
No related merge requests found
Pipeline #6438 failed
Showing
with 525 additions and 26 deletions
default:
image: skpr/php-cli:${PHP_VERSION}-dev-v2-latest
services:
- name: mysql:${MYSQL_VERSION}
alias: mysql
stages:
- pre
- build
- lint
- test
variables:
# Configure mysql environment variables (https://hub.docker.com/_/mysql/)
MYSQL_ROOT_PASSWORD: drupal
MYSQL_DATABASE: drupal
MYSQL_USER: drupal
MYSQL_PASSWORD: drupal
SIMPLETEST_DB: mysql://drupal:drupal@mysql/drupal
SIMPLETEST_BASE_URL: http://localhost:8080
BROWSERTEST_OUTPUT_DIRECTORY: app/sites/default/files
BROWSERTEST_OUTPUT_FILE: test-output.html
MINK_DRIVER_ARGS_WEBDRIVER: '["chrome", {"chromeOptions": { "args": [ "--disable-gpu", "--headless" ] } }, "http://chrome:4444/wd/hub" ]'
build app:
stage: build
cache:
key: ${CI_COMMIT_REF_SLUG}-composer
paths:
- app/vendor/
script:
- mkdir -p /tmp/project
- cp -r ${CI_PROJECT_DIR} /tmp/project/
- git clone --depth 1 --branch ${DRUPAL_CORE_CONSTRAINT}.x https://git.drupal.org/project/drupal.git app
- mv /tmp/project app/modules/
- cd app
- composer install
- composer config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
- composer config --unset platform.php
- composer require rlanvin/php-rrule:${RRULE_CONSTRAINT}
- composer require
--with-all-dependencies
dealerdirect/phpcodesniffer-composer-installer
dpi/drupal-phpunit-bootstrap
drupal/core-dev:^${DRUPAL_CORE_CONSTRAINT}
phpspec/prophecy-phpunit:^2
mglaman/phpstan-drupal:^1
phpstan/phpstan-deprecation-rules:*
micheh/phpcs-gitlab
- composer require
drupal/coder:^8.3
drupal/token:^1.5
- composer show
- cp modules/project/date_recur/.drupalci/* .
- mkdir -p ${BROWSERTEST_OUTPUT_DIRECTORY}
artifacts:
expire_in: 1 hour
paths:
- app/composer.lock
- app/
phpcs:
stage: lint
needs: ["build app"]
script:
- cd app
- vendor/bin/phpcs --standard=modules/project/date_recur/phpcs.xml --report=full --report-\\Micheh\\PhpCodeSniffer\\Report\\Gitlab=phpcs-quality-report.json modules/project/date_recur/
dependencies:
- build app
artifacts:
when: always
reports:
codequality: app/phpcs-quality-report.json
phpstan:
stage: lint
needs: ["build app"]
script:
- cd app
- vendor/bin/phpstan analyse --memory-limit=2G --no-progress -c phpstan.neon modules/project/date_recur --error-format gitlab > report.json
dependencies:
- build app
artifacts:
when: always
reports:
codequality: app/report.json
test unit:
needs: ["build app"]
stage: test
script:
- cd app
- vendor/bin/phpunit modules/project/date_recur/tests/src/Unit/ --log-junit report-unit.xml
dependencies:
- build app
artifacts:
when: always
reports:
junit: app/report-unit.xml
test kernel:
needs: ["build app"]
stage: test
before_script:
- cd app
- ./wait-for-it.sh -s -t 180 mysql:3306 -- echo "MySQL is Ready"
script:
- vendor/bin/phpunit modules/project/date_recur/tests/src/Kernel/ --log-junit report-kernel.xml
dependencies:
- build app
artifacts:
when: always
reports:
junit: app/report-kernel.xml
test functional:
needs: ["build app"]
stage: test
image: skpr/php-cli:${PHP_VERSION}-dev-v2-latest
services:
- name: mysql:${MYSQL_VERSION}
alias: mysql
- name: selenium/standalone-chrome:3.141.59-oxygen
alias: chrome
before_script:
- cd app
- php -S 0.0.0.0:8080 .ht.router.php >> http.log 2>&1 &
- ./wait-for-it.sh -s -t 180 mysql:3306 -- echo "MySQL is Ready"
script:
- vendor/bin/phpunit modules/project/date_recur/tests/src/Functional/ --log-junit report-functional.xml
dependencies:
- build app
artifacts:
when: always
reports:
junit: app/report-functional.xml
paths:
- app/http.log
- app/sites/simpletest/browser_output/
<?php
// phpcs:ignoreFile
// Place in app/, adjacent to web/ and vendor.
declare(strict_types=1);
use Composer\Autoload\ClassLoader;
use dpi\DrupalPhpunitBootstrap\Utility;
use Drupal\TestTools\PhpUnitCompatibility\PhpUnit8\ClassWriter;
$loader = require __DIR__ . '/vendor/autoload.php';
assert($loader instanceof ClassLoader);
foreach ([
'Drupal\\BuildTests',
'Drupal\\Tests',
'Drupal\\TestSite',
'Drupal\\KernelTests',
'Drupal\\FunctionalTests',
'Drupal\\FunctionalJavascriptTests',
'Drupal\\TestTools',
] as $ns) {
$loader->add($ns, __DIR__ . '/core/tests');
}
foreach ($loader->getPrefixesPsr4() as $prefix => $paths) {
// Some directories dont exist. E.g the drupal/core subtree split project we bring in references
// path ("Drupal\\Driver\\": "../drivers/lib/Drupal/Driver") outside of its repository.
$paths = array_filter($paths, function (string $path): bool {
return is_dir($path);
});
$loader->setPsr4($prefix, $paths);
}
$dirs = [];
foreach ([
__DIR__ . '/core/modules',
__DIR__ . '/core/profiles',
__DIR__ . '/core/themes',
__DIR__ . '/modules/project',
] as $dir) {
$dirs = array_merge($dirs, Utility::drupal_phpunit_find_extension_directories($dir));
}
foreach (Utility::drupal_phpunit_get_extension_namespaces($dirs) as $prefix => $paths) {
$loader->addPsr4($prefix, $paths);
}
date_default_timezone_set('Australia/Sydney');
if (class_exists('\Drupal\TestTools\PhpUnitCompatibility\PhpUnit8\ClassWriter')) {
ClassWriter::mutateTestBase($loader);
}
parameters:
level: 1
bootstrapFiles:
- bootstrap.php
reportUnmatchedIgnoredErrors: false
customRulesetUsed: true
paths:
- .
excludePaths:
analyseAndScan:
- *Test.php
- *TestBase.php
- *ServiceProvider.php
- vendor
scanFiles:
- core/includes/install.inc
scanDirectories:
- vendor/rlanvin/php-rrule/
- core/modules/system/
fileExtensions:
- module
- theme
- profile
- install
includes:
- vendor/mglaman/phpstan-drupal/extension.neon
- vendor/phpstan/phpstan-deprecation-rules/rules.neon
- inc
ignoreErrors:
- '#Unsafe usage of new static\(\).#'
<?xml version="1.0" encoding="UTF-8"?>
<!-- For how to customize PHPUnit configuration, see core/tests/README.md. -->
<!-- TODO set checkForUnintentionallyCoveredCode="true" once https://www.drupal.org/node/2626832 is resolved. -->
<!-- PHPUnit expects functional tests to be run with either a privileged user
or your current system user. See core/tests/README.md and
https://www.drupal.org/node/2116263 for details.
-->
<phpunit bootstrap="bootstrap.php" colors="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutChangesToGlobalState="true"
failOnWarning="true"
printerClass="\Drupal\Tests\Listeners\HtmlOutputPrinter"
cacheResult="false">
<php>
<!-- Set error reporting to E_ALL. -->
<ini name="error_reporting" value="32767"/>
<!-- Do not limit the amount of memory tests take to run. -->
<ini name="memory_limit" value="-1"/>
<!-- Example SIMPLETEST_BASE_URL value: http://localhost -->
<env name="SIMPLETEST_BASE_URL" value=""/>
<!-- Example SIMPLETEST_DB value: mysql://username:password@localhost/databasename#table_prefix -->
<env name="SIMPLETEST_DB" value=""/>
<!-- Example BROWSERTEST_OUTPUT_DIRECTORY value: /path/to/webroot/sites/simpletest/browser_output -->
<env name="BROWSERTEST_OUTPUT_DIRECTORY" value=""/>
<!-- To have browsertest output use an alternative base URL. For example if
SIMPLETEST_BASE_URL is an internal DDEV URL, you can set this to the
external DDev URL so you can follow the links directly.
-->
<env name="BROWSERTEST_OUTPUT_BASE_URL" value=""/>
<!-- To disable deprecation testing completely uncomment the next line. -->
<!-- <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/> -->
<!-- Example for changing the driver class for mink tests MINK_DRIVER_CLASS value: 'Drupal\FunctionalJavascriptTests\DrupalSelenium2Driver' -->
<env name="MINK_DRIVER_CLASS" value=''/>
<!-- Example for changing the driver args to mink tests MINK_DRIVER_ARGS value: '["http://127.0.0.1:8510"]' -->
<env name="MINK_DRIVER_ARGS" value=''/>
<!-- Example for changing the driver args to webdriver tests MINK_DRIVER_ARGS_WEBDRIVER value: '["chrome", { "chromeOptions": { "w3c": false } }, "http://localhost:4444/wd/hub"]' For using the Firefox browser, replace "chrome" with "firefox" -->
<env name="MINK_DRIVER_ARGS_WEBDRIVER" value=''/>
</php>
<testsuites>
<testsuite name="unit">
<file>./tests/TestSuites/UnitTestSuite.php</file>
</testsuite>
<testsuite name="kernel">
<file>./tests/TestSuites/KernelTestSuite.php</file>
</testsuite>
<testsuite name="functional">
<file>./tests/TestSuites/FunctionalTestSuite.php</file>
</testsuite>
<testsuite name="functional-javascript">
<file>./tests/TestSuites/FunctionalJavascriptTestSuite.php</file>
</testsuite>
<testsuite name="build">
<file>./tests/TestSuites/BuildTestSuite.php</file>
</testsuite>
</testsuites>
<listeners>
<listener class="\Drupal\Tests\Listeners\DrupalListener">
</listener>
</listeners>
<!-- Filter for coverage reports. -->
<filter>
<whitelist>
<directory>./includes</directory>
<directory>./lib</directory>
<!-- Extensions can have their own test directories, so exclude those. -->
<directory>./modules</directory>
<exclude>
<directory>./modules/*/src/Tests</directory>
<directory>./modules/*/tests</directory>
</exclude>
<directory>../modules</directory>
<exclude>
<directory>../modules/*/src/Tests</directory>
<directory>../modules/*/tests</directory>
<directory>../modules/*/*/src/Tests</directory>
<directory>../modules/*/*/tests</directory>
</exclude>
<directory>../sites</directory>
</whitelist>
</filter>
</phpunit>
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
WAITFORIT_cmdname=${0##*/}
echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
else
echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
fi
WAITFORIT_start_ts=$(date +%s)
while :
do
if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
nc -z $WAITFORIT_HOST $WAITFORIT_PORT
WAITFORIT_result=$?
else
(echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
WAITFORIT_result=$?
fi
if [[ $WAITFORIT_result -eq 0 ]]; then
WAITFORIT_end_ts=$(date +%s)
echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
break
fi
sleep 1
done
return $WAITFORIT_result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $WAITFORIT_QUIET -eq 1 ]]; then
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
else
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
fi
WAITFORIT_PID=$!
trap "kill -INT -$WAITFORIT_PID" INT
wait $WAITFORIT_PID
WAITFORIT_RESULT=$?
if [[ $WAITFORIT_RESULT -ne 0 ]]; then
echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
fi
return $WAITFORIT_RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
WAITFORIT_hostport=(${1//:/ })
WAITFORIT_HOST=${WAITFORIT_hostport[0]}
WAITFORIT_PORT=${WAITFORIT_hostport[1]}
shift 1
;;
--child)
WAITFORIT_CHILD=1
shift 1
;;
-q | --quiet)
WAITFORIT_QUIET=1
shift 1
;;
-s | --strict)
WAITFORIT_STRICT=1
shift 1
;;
-h)
WAITFORIT_HOST="$2"
if [[ $WAITFORIT_HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
WAITFORIT_HOST="${1#*=}"
shift 1
;;
-p)
WAITFORIT_PORT="$2"
if [[ $WAITFORIT_PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
WAITFORIT_PORT="${1#*=}"
shift 1
;;
-t)
WAITFORIT_TIMEOUT="$2"
if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
WAITFORIT_TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
WAITFORIT_CLI=("$@")
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}
# Check to see if timeout is from busybox?
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)
WAITFORIT_BUSYTIMEFLAG=""
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
WAITFORIT_ISBUSY=1
# Check if busybox timeout uses -t flag
# (recent Alpine versions don't support -t anymore)
if timeout &>/dev/stdout | grep -q -e '-t '; then
WAITFORIT_BUSYTIMEFLAG="-t"
fi
else
WAITFORIT_ISBUSY=0
fi
if [[ $WAITFORIT_CHILD -gt 0 ]]; then
wait_for
WAITFORIT_RESULT=$?
exit $WAITFORIT_RESULT
else
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
wait_for_wrapper
WAITFORIT_RESULT=$?
else
wait_for
WAITFORIT_RESULT=$?
fi
fi
if [[ $WAITFORIT_CLI != "" ]]; then
if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
exit $WAITFORIT_RESULT
fi
exec "${WAITFORIT_CLI[@]}"
else
exit $WAITFORIT_RESULT
fi
include:
# https://docs.gitlab.com/ee/ci/yaml/index.html#includetemplate
- template: 'Workflows/Branch-Pipelines.gitlab-ci.yml'
deploystacks:
trigger:
include: .drupalci/.gitlab-single-pipeline.yml
strategy: depend
parallel:
matrix:
- DRUPAL_CORE_CONSTRAINT: ["9.3", "9.4"]
PHP_VERSION: ["8.1", "8.0"]
MYSQL_VERSION: ["8"]
RRULE_CONSTRAINT: ["^2"]
# RRULE library is fundamentally not compatible with PHP8.1.
- DRUPAL_CORE_CONSTRAINT: ["9.3", "9.4"]
PHP_VERSION: ["8.0"]
MYSQL_VERSION: ["8"]
RRULE_CONSTRAINT: ["^1"]
# Drupal 10 requires PHP 8.1
- DRUPAL_CORE_CONSTRAINT: ["10.0"]
PHP_VERSION: ["8.1"]
MYSQL_VERSION: ["8"]
RRULE_CONSTRAINT: ["^2"]
......@@ -3,4 +3,10 @@
<rule ref="./vendor/drupal/coder/coder_sniffer/Drupal"/>
<exclude-pattern>vendor/*</exclude-pattern>
<arg name="extensions" value="inc,install,module,php,profile,test,theme,yml"/>
<!-- The sniff doesnt know how to handle throws as statement. Anyhow, this
kind of thing is covered by PHPStan.-->
<rule ref="Squiz.PHP.NonExecutableCode.Unreachable">
<severity>0</severity>
</rule>
</ruleset>
......@@ -336,7 +336,7 @@ class DateRecurViewsHooks implements ContainerInjectionInterface {
}
}
catch (PluginNotFoundException $e) {
// This can occur when a content-entity is added during a config
// This can occur when a content-entity is added during a config
// import that enables a module.
continue;
}
......
......@@ -9,15 +9,21 @@ use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
//@codingStandardsIgnoreStart
/**
* Date recur interpreter plugin manager.
*/
// @phpstan-ignore-next-line
class DateRecurInterpreterManager extends DefaultPluginManager implements DateRecurInterpreterManagerInterface {
/**
* {@inheritdoc}
*
* @param \Traversable<string,string[]> $namespaces
*/
// @phpstan-ignore-next-line
public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
//@codingStandardsIgnoreEnd
parent::__construct(
'Plugin/DateRecurInterpreter',
$namespaces,
......
......@@ -128,9 +128,7 @@ class DateRecurBasicWidget extends DateRangeDefaultWidget {
$timeZoneFieldPath = array_slice($element['#array_parents'], 0, -1);
$timeZoneFieldPath[] = 'timezone';
$timeZoneField = NestedArray::getValue($form_state->getCompleteForm(), $timeZoneFieldPath);
$submittedTimeZone = isset($timeZoneField['#value'])
? $timeZoneField['#value']
: ($timeZoneField['#default_value'] ?? NULL);
$submittedTimeZone = $timeZoneField['#value'] ?? ($timeZoneField['#default_value'] ?? NULL);
}
$allTimeZones = \DateTimeZone::listIdentifiers();
......
......@@ -7,18 +7,11 @@
use Drupal\Core\Form\FormStateInterface;
/**
* State flag to hide time zone field in basic widget via a hook_form_alter.
*
* Triggers when non empty.
*/
const DATE_RECUR_BASIC_WIDGET_TEST_HIDDEN_TIMEZONE_FIELD_HOOK_FORM_ALTER = 'DATE_RECUR_BASIC_WIDGET_TEST_HIDDEN_TIMEZONE_FIELD_HOOK_FORM_ALTER';
/**
* Implements hook_form_alter().
*/
function date_recur_basic_widget_test_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$useTimeZoneFormAlter = \Drupal::state()->get(\DATE_RECUR_BASIC_WIDGET_TEST_HIDDEN_TIMEZONE_FIELD_HOOK_FORM_ALTER);
$useTimeZoneFormAlter = \Drupal::state()->get('DATE_RECUR_BASIC_WIDGET_TEST_HIDDEN_TIMEZONE_FIELD_HOOK_FORM_ALTER');
if (!empty($useTimeZoneFormAlter)) {
if ('dr_entity_test_dr_entity_test_form' === $form_id) {
$form['dr']['widget']['0']['timezone']['#access'] = FALSE;
......
......@@ -295,7 +295,7 @@ class DateRecurBasicWidgetTest extends BrowserTestBase {
* End date must never be required, value is copied over from start date.
*/
public function testHiddenTimeZoneField() {
\Drupal::state()->set(\DATE_RECUR_BASIC_WIDGET_TEST_HIDDEN_TIMEZONE_FIELD_HOOK_FORM_ALTER, TRUE);
\Drupal::state()->set('DATE_RECUR_BASIC_WIDGET_TEST_HIDDEN_TIMEZONE_FIELD_HOOK_FORM_ALTER', TRUE);
$this->drupalGet(Url::fromRoute('entity.dr_entity_test.add_form'));
......
......@@ -254,7 +254,7 @@ class DateRecurFieldItemTest extends KernelTestBase {
public function testGenerateSampleValue() {
$entity = DrEntityTest::create();
$entity->dr->generateSampleItems();
$this->assertRegExp('/^FREQ=DAILY;COUNT=\d{1,2}$/', $entity->dr->rrule);
$this->assertMatchesRegularExpression('/^FREQ=DAILY;COUNT=\d{1,2}$/', $entity->dr->rrule);
$this->assertFalse($entity->dr->infinite);
$this->assertTrue(in_array($entity->dr->timezone, timezone_identifiers_list(), TRUE));
......
......@@ -59,9 +59,9 @@ class DateRecurViewsFieldTest extends ViewsKernelTestBase {
/**
* Name of field for testing.
*
* @var string
* @var string|null
*/
private string $fieldName;
protected ?string $fieldName = NULL;
/**
* {@inheritdoc}
......
......@@ -50,9 +50,9 @@ class DateRecurViewsOccurrenceFilterTest extends ViewsKernelTestBase {
/**
* Name of field for testing.
*
* @var string
* @var string|null
*/
private string $fieldName;
protected ?string $fieldName = NULL;
/**
* {@inheritdoc}
......
......@@ -13,14 +13,6 @@ use Drupal\Tests\UnitTestCase;
*/
class DateRecurDateRangeUnitTest extends UnitTestCase {
/**
* Test arguments required.
*/
public function testRequiredConstructorArguments() {
$this->expectException(\ArgumentCountError::class);
$this->createDateRange();
}
/**
* Tests start and end getters.
*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment