diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c9cc542b27cbc0c2a14e6195d23dcc68bd389add..e30836c72e59fdfabee40f8479a6a7c7238e4fbf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,6 +39,13 @@ include: - '/includes/include.drupalci.variables.yml' - '/includes/include.drupalci.workflows.yml' +stages: + - development + - build + - validate + - test + - publish + ################ # Pipeline configuration variables # @@ -59,6 +66,49 @@ variables: OPT_IN_TEST_NEXT_MINOR: 1 OPT_IN_TEST_MAX_PHP: 1 +# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands. +.is-push-rule: &is-push-rule + if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ROOT_NAMESPACE == "project" + +.manual-rule: &manual-rule + - when: manual + allow_failure: true + +# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands. +.php-files-change-rule: &php-files-change-rule + - changes: + - "**/*.{php,module,inc,install,theme,profile}" + when: on_success + +# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands. +.css-files-change-rule: &css-files-change-rule + - changes: + - "**/*.css" + when: on_success + +# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands. +.module-metadata-files-change-rule: &module-metadata-files-change-rule + - changes: + - "**/*.yml" + when: on_success + +# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands. +.module-config-files-change-rule: &module-config-files-change-rule + - changes: + # config schema and/or default config + - config + when: on_success + +# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands. +composer-lint: + rules: + - *is-push-rule + - *php-files-change-rule + - changes: + - composer.json + when: on_success + - *manual-rule + ################ # Require composer checks to pass. ################ @@ -84,6 +134,50 @@ stylelint: ################ phpcs: allow_failure: false + # @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands. + rules: + - *is-push-rule + - *php-files-change-rule + # Also run whenever we catch up to the upstream PHPCS rules. + - changes: + - core.phpcs.xml.dist + when: on_success + - *manual-rule + +phpcs-rules-match-drupal-11: + stage: development + script: + - | + # Download the file from the Drupal repository + curl -o drupal_core_phpcs.xml.dist https://git.drupalcode.org/project/drupal/-/raw/11.x/core/phpcs.xml.dist?ref_type=heads + + # Check if the download was successful + if [ $? -ne 0 ]; then + echo "🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥" + echo "Error: Failed to download $DRUPAL_URL" + exit 1 + fi + + # Compare the downloaded file with the local file using a subshell to prevent premature exit + echo "Comparing downloaded file with local file..." + # With uses the braces the job just ends here for some reason. + { + diff drupal_core_phpcs.xml.dist core.phpcs.xml.dist + DIFF_EXIT_CODE=$? + } || true + + if [ $DIFF_EXIT_CODE -ne 0 ]; then + echo "🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥" + echo "Error: core.phpcs.xml.dist not up to date with Drupal 11.x core/phpcs.xml.dist. Please create an issue to update this file." + else + echo "👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍" + echo "core.phpcs.xml.dist is up to date with Drupal 11.x core/phpcs.xml.dist" + fi + + # Exit with the exit code of the diff command + exit $DIFF_EXIT_CODE + # Allow this to fail, to avoid disrupting XB's CI when upstream changes. + allow_failure: true ################ # Require phpstan checks to pass. diff --git a/core.phpcs.xml.dist b/core.phpcs.xml.dist new file mode 100644 index 0000000000000000000000000000000000000000..b8902c685c702b3457c35a0afd482ab7cabc99b2 --- /dev/null +++ b/core.phpcs.xml.dist @@ -0,0 +1,422 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ruleset name="drupal_core"> + <arg name="extensions" value="engine,inc,install,module,php,profile,test,theme,yml"/> + <description>Default PHP CodeSniffer configuration for Drupal core.</description> + + <!--Exclude folders used by common frontend tools. These folders match the file_scan_ignore_directories setting in default.settings.php--> + <exclude-pattern>*/bower_components/*</exclude-pattern> + <exclude-pattern>*/node_modules/*</exclude-pattern> + <!--Exclude third party code.--> + <exclude-pattern>./assets/vendor/*</exclude-pattern> + <!--Exclude the PHPStan baseline and temp dir from coding standards.--> + <exclude-pattern>./core/.phpstan-baseline.php</exclude-pattern> + <!-- Exclude third-party code maintained within core that does not follow our standards. --> + <!-- @todo This rule may be removed when https://www.drupal.org/node/1848264 is resolved. --> + <exclude-pattern>./core/lib/Drupal/Component/Diff/</exclude-pattern> + <exclude-pattern>./core/phpstan-tmp/*</exclude-pattern> + <exclude-pattern>./core/tests/Drupal/Tests/Component/Annotation/Doctrine/</exclude-pattern> + + <!--Exclude test files that are intentionally empty, or intentionally violate coding standards.--> + <exclude-pattern>./modules/system/tests/fixtures/HtaccessTest</exclude-pattern> + + <file>.</file> + <file>../composer</file> + <file>scripts/password-hash.sh</file> + <file>scripts/rebuild_token_calculator.sh</file> + <file>scripts/run-tests.sh</file> + <file>scripts/update-countries.sh</file> + + <!-- Only include specific sniffs that pass. This ensures that, if new sniffs are added, HEAD does not fail.--> + + <!-- Drupal sniffs --> + <rule ref="Drupal.Arrays.Array"> + <!-- Sniff for these errors: ArrayClosingIndentation, ArrayIndentation, CommaLastItem --> + <exclude name="Drupal.Arrays.Array.LongLineDeclaration"/> + </rule> + <rule ref="Drupal.CSS.ClassDefinitionNameSpacing"/> + <rule ref="Drupal.CSS.ColourDefinition"/> + <rule ref="Drupal.Classes.ClassCreateInstance"/> + <rule ref="Drupal.Classes.ClassDeclaration"/> + <rule ref="Drupal.Classes.ClassFileName"/> + <rule ref="Drupal.Classes.FullyQualifiedNamespace"/> + <rule ref="Drupal.Classes.InterfaceName"/> + <rule ref="Drupal.Classes.PropertyDeclaration"/> + <rule ref="Drupal.Classes.UnusedUseStatement"/> + <rule ref="Drupal.Classes.UseGlobalClass"/> + <rule ref="Drupal.Classes.UseLeadingBackslash"/> + <rule ref="Drupal.Commenting.ClassComment"/> + <rule ref="Drupal.Commenting.ClassComment.Missing"> + <include-pattern>*/Functional/*</include-pattern> + </rule> + <rule ref="Drupal.Commenting.DataTypeNamespace"/> + <rule ref="Drupal.Commenting.Deprecated"/> + <rule ref="Drupal.Commenting.DocComment"> + <!-- Sniff for these errors: SpacingAfterTagGroup, WrongEnd, SpacingBetween, + ContentAfterOpen, SpacingBeforeShort, TagValueIndent, ShortStartSpace, + SpacingAfter, LongNotCapital, ShortFullStop, TagGroupSpacing, Empty, + TagsNotGrouped, ParamGroup --> + <exclude name="Drupal.Commenting.DocComment.LongFullStop"/> + <exclude name="Drupal.Commenting.DocComment.MissingShort"/> + <exclude name="Drupal.Commenting.DocComment.ShortNotCapital"/> + </rule> + <rule ref="Drupal.Commenting.DocCommentAlignment"/> + <rule ref="Drupal.Commenting.DocCommentLongArraySyntax"/> + <rule ref="Drupal.Commenting.DocCommentStar"/> + <rule ref="Drupal.Commenting.FileComment"/> + <rule ref="Drupal.Commenting.FunctionComment"> + <exclude name="Drupal.Commenting.FunctionComment.ParamCommentFullStop"/> + </rule> + <rule ref="Drupal.Commenting.FunctionComment.Missing"> + <include-pattern>core/modules/*/Plugin/views/argument/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/filter/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/access/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/cache/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/query/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/sort/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/display/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/exposed_form/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/field/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/pager/*</include-pattern> + <include-pattern>core/modules/*/Plugin/views/style/*</include-pattern> + <include-pattern>*/Database/*</include-pattern> + <include-pattern>*/FunctionalJavascript/*</include-pattern> + </rule> + <rule ref="Drupal.Commenting.FunctionComment.MissingParamType"/> + <rule ref="Drupal.Commenting.FunctionComment.MissingReturnComment"> + <include-pattern>core/lib/Drupal/Core/*</include-pattern> + <include-pattern>core/lib/Drupal/Component/*</include-pattern> + <include-pattern>core/tests/*</include-pattern> + </rule> + <rule ref="Drupal.Commenting.FunctionComment.MissingReturnComment"> + <exclude-pattern>core/lib/Drupal/Core/*</exclude-pattern> + <exclude-pattern>core/lib/Drupal/Component/*</exclude-pattern> + <exclude-pattern>core/tests/*</exclude-pattern> + <exclude-pattern>core/*/tests/*</exclude-pattern> + </rule> + <rule ref="Drupal.Commenting.GenderNeutralComment"/> + <rule ref="Drupal.Commenting.HookComment"/> + <rule ref="Drupal.Commenting.InlineComment"> + <!-- Sniff for: NoSpaceBefore, SpacingBefore, WrongStyle --> + <exclude name="Drupal.Commenting.InlineComment.DocBlock"/> + <exclude name="Drupal.Commenting.InlineComment.InvalidEndChar"/> + <exclude name="Drupal.Commenting.InlineComment.SpacingAfter"/> + </rule> + <rule ref="Drupal.Commenting.InlineVariableComment"/> + <rule ref="Drupal.Commenting.PostStatementComment"/> + <rule ref="Drupal.Commenting.TodoComment" /> + <rule ref="Drupal.Commenting.VariableComment"/> + <rule ref="Drupal.ControlStructures.ControlSignature"/> + <rule ref="Drupal.ControlStructures.ElseIf"/> + <rule ref="Drupal.ControlStructures.InlineControlStructure"/> + <rule ref="Drupal.Files.EndFileNewline"/> + <rule ref="Drupal.Files.FileEncoding"/> + <rule ref="Drupal.Files.TxtFileLineLength"/> + <rule ref="Drupal.Formatting.MultiLineAssignment"/> + <rule ref="Drupal.Formatting.MultipleStatementAlignment"/> + <rule ref="Drupal.Formatting.SpaceInlineIf"/> + <rule ref="Drupal.Formatting.SpaceUnaryOperator"/> + <rule ref="Drupal.Functions.DiscouragedFunctions"/> + <rule ref="Drupal.Functions.FunctionDeclaration"/> + <rule ref="Drupal.Functions.MultiLineFunctionDeclaration"/> + <rule ref="Drupal.InfoFiles.AutoAddedKeys"/> + <rule ref="Drupal.InfoFiles.ClassFiles"/> + <rule ref="Drupal.InfoFiles.DuplicateEntry"/> + <rule ref="Drupal.InfoFiles.Required"/> + <rule ref="Drupal.Methods.MethodDeclaration"> + <!-- Silence method name underscore warning which is covered already in + Drupal.NamingConventions.ValidFunctionName.ScopeNotCamelCaps. --> + <exclude name="Drupal.Methods.MethodDeclaration.Underscore"/> + </rule> + <rule ref="Drupal.NamingConventions.ValidClassName"/> + <rule ref="Drupal.NamingConventions.ValidGlobal"/> + <rule ref="Drupal.NamingConventions.ValidVariableName"/> + <rule ref="Drupal.NamingConventions.ValidVariableName.LowerCamelName"/> + <rule ref="Drupal.Scope.MethodScope"/> + <rule ref="Drupal.Semantics.EmptyInstall"/> + <rule ref="Drupal.Semantics.FunctionAlias"/> + <rule ref="Drupal.Semantics.FunctionT"/> + <rule ref="Drupal.Semantics.FunctionTriggerError"/> + <rule ref="Drupal.Semantics.FunctionWatchdog"/> + <rule ref="Drupal.Semantics.InstallHooks"/> + <rule ref="Drupal.Semantics.LStringTranslatable"/> + <rule ref="Drupal.Semantics.PregSecurity"/> + <rule ref="Drupal.Semantics.RemoteAddress"/> + <rule ref="Drupal.Semantics.TInHookMenu"/> + <rule ref="Drupal.Semantics.TInHookSchema"/> + <rule ref="Drupal.Strings.UnnecessaryStringConcat"/> + <rule ref="Drupal.WhiteSpace.CloseBracketSpacing"/> + <rule ref="Drupal.WhiteSpace.Comma"/> + <rule ref="Drupal.WhiteSpace.EmptyLines"/> + <rule ref="Drupal.WhiteSpace.Namespace"/> + <rule ref="Drupal.WhiteSpace.ObjectOperatorIndent"/> + <rule ref="Drupal.WhiteSpace.ObjectOperatorSpacing"/> + <rule ref="Drupal.WhiteSpace.OpenBracketSpacing"/> + <rule ref="Drupal.WhiteSpace.OpenTagNewline"/> + <rule ref="Drupal.WhiteSpace.ScopeClosingBrace"/> + <rule ref="Drupal.WhiteSpace.ScopeIndent"/> + + <!-- Drupal Practice sniffs --> + <rule ref="DrupalPractice.CodeAnalysis.VariableAnalysis"> + <!-- Do not run this sniff on API files or transliteration data. --> + <exclude-pattern>*.api.php</exclude-pattern> + <exclude-pattern>core/lib/Drupal/Component/Transliteration/data/*.php</exclude-pattern> + <properties> + <property name="allowUnusedFunctionParameters" value="true"/> + </properties> + </rule> + <rule ref="DrupalPractice.CodeAnalysis.VariableAnalysis.UndefinedUnsetVariable"> + <severity>0</severity> + </rule> + <rule ref="DrupalPractice.CodeAnalysis.VariableAnalysis.UndefinedVariable"> + <!-- Setting severity to 0 to completely disable an error message in this sniff, without excluding the whole sniff --> + <!-- See https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options#changing-the-default-severity-levels --> + <severity>0</severity> + </rule> + <rule ref="DrupalPractice.Commenting.ExpectedException"/> + <rule ref="DrupalPractice.General.ExceptionT"/> + <rule ref="DrupalPractice.InfoFiles.NamespacedDependency"/> + <rule ref="DrupalPractice.Objects.GlobalFunction"> + <include-pattern>*/Plugin/*</include-pattern> + <include-pattern>*/ListBuilder/*</include-pattern> + <include-pattern>*/tests/*</include-pattern> + <include-pattern>./core/lib/*</include-pattern> + <include-pattern>./core/modules/*</include-pattern> + <include-pattern>*/Hook/*</include-pattern> + </rule> + + <!-- Generic sniffs --> + <rule ref="Generic.Arrays.DisallowLongArraySyntax"/> + <rule ref="Generic.CodeAnalysis.EmptyPHPStatement"/> + <rule ref="Generic.Files.ByteOrderMark"/> + <rule ref="Generic.Files.LineEndings"/> + <rule ref="Generic.Formatting.DisallowMultipleStatements"/> + <rule ref="Generic.Formatting.SpaceAfterCast"/> + <rule ref="Generic.Functions.FunctionCallArgumentSpacing"/> + <rule ref="Generic.NamingConventions.ConstructorName"/> + <rule ref="Generic.NamingConventions.UpperCaseConstantName"/> + <rule ref="Generic.PHP.DeprecatedFunctions"/> + <rule ref="Generic.PHP.DisallowShortOpenTag"/> + <rule ref="Generic.PHP.LowerCaseKeyword"/> + <rule ref="Generic.PHP.UpperCaseConstant"/> + <rule ref="Generic.WhiteSpace.DisallowTabIndent"/> + + <!-- Internal sniffs --> + <rule ref="Internal.NoCodeFound"> + <!-- No PHP code in *.yml --> + <exclude-pattern>*.yml</exclude-pattern> + </rule> + + <!-- MySource sniffs --> + <rule ref="MySource.Debug.DebugCode"/> + + <!-- PEAR sniffs --> + <rule ref="PEAR.Files.IncludingFile"/> + <!-- Disable some error messages that we do not want. --> + <rule ref="PEAR.Files.IncludingFile.UseInclude"> + <severity>0</severity> + </rule> + <rule ref="PEAR.Files.IncludingFile.UseIncludeOnce"> + <severity>0</severity> + </rule> + <rule ref="PEAR.Files.IncludingFile.UseRequire"> + <severity>0</severity> + </rule> + <rule ref="PEAR.Files.IncludingFile.UseRequireOnce"> + <severity>0</severity> + </rule> + <rule ref="PEAR.Functions.FunctionCallSignature"/> + <!-- The sniffs inside PEAR.Functions.FunctionCallSignature silenced below are + also silenced in Drupal CS' ruleset.xml. The code below is a 1-on-1 copy + from that file. --> + <rule ref="PEAR.Functions.FunctionCallSignature.CloseBracketLine"> + <severity>0</severity> + </rule> + <rule ref="PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket"> + <severity>0</severity> + </rule> + <rule ref="PEAR.Functions.FunctionCallSignature.EmptyLine"> + <severity>0</severity> + </rule> + <!-- Disable some error messages that we do not want. --> + <rule ref="PEAR.Functions.FunctionCallSignature.Indent"> + <severity>0</severity> + </rule> + <rule ref="PEAR.Functions.FunctionCallSignature.OpeningIndent"> + <severity>0</severity> + </rule> + <!-- Disable some error messages that we already cover. --> + <rule ref="PEAR.Functions.FunctionCallSignature.SpaceAfterOpenBracket"> + <severity>0</severity> + </rule> + <rule ref="PEAR.Functions.FunctionCallSignature.SpaceBeforeCloseBracket"> + <severity>0</severity> + </rule> + <rule ref="PEAR.Functions.ValidDefaultValue"/> + + <!-- PSR-2 sniffs --> + <rule ref="PSR2.Classes.PropertyDeclaration"> + <!-- Silence method name underscore warning which is covered already in + Drupal.Classes.PropertyDeclaration. --> + <exclude name="PSR2.Classes.PropertyDeclaration.Underscore"/> + </rule> + <rule ref="PSR2.Namespaces.NamespaceDeclaration"/> + <rule ref="PSR2.Namespaces.UseDeclaration"/> + + <!-- SlevomatCodingStandard sniffs --> + <rule ref="SlevomatCodingStandard.Classes.BackedEnumTypeSpacing"/> + <rule ref="SlevomatCodingStandard.Commenting.ForbiddenAnnotations"> + <properties> + <property name="forbiddenAnnotations" type="array"> + <element value="@inheritDoc"/> + <element value="@inheritdoc"/> + </property> + </properties> + </rule> + <rule ref="SlevomatCodingStandard.Commenting.ForbiddenComments"> + <properties> + <property name="forbiddenCommentPatterns" type="array"> + <element value="/@inheritDoc/"/> + </property> + </properties> + </rule> + <rule ref="SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator"/> + <rule ref="SlevomatCodingStandard.ControlStructures.RequireShortTernaryOperator"/> + <rule ref="SlevomatCodingStandard.Exceptions.RequireNonCapturingCatch" /> + <rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes"> + <properties> + <property name="spacesCountAroundEqualsSign" value="0" /> + </properties> + <include-pattern>*/tests/*</include-pattern> + <exclude-pattern>*/fixtures/*</exclude-pattern> + </rule> + <rule ref="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue"/> + + <!-- Squiz sniffs --> + <rule ref="Squiz.Arrays.ArrayBracketSpacing"/> + <rule ref="Squiz.Arrays.ArrayDeclaration"> + <exclude name="Squiz.Arrays.ArrayDeclaration.KeySpecified"/> + <exclude name="Squiz.Arrays.ArrayDeclaration.NoKeySpecified"/> + </rule> + <!-- Disable some error messages that we do not want. --> + <rule ref="Squiz.Arrays.ArrayDeclaration.CloseBraceNotAligned"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.DoubleArrowNotAligned"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.FirstValueNoNewline"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.KeyNotAligned"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.MultiLineNotAllowed"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.NoComma"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.NoCommaAfterLast"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.NotLowerCase"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.SingleLineNotAllowed"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.ValueNoNewline"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Arrays.ArrayDeclaration.ValueNotAligned"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.ForEachLoopDeclaration"/> + <!-- Disable some error messages that we already cover. --> + <rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.AsNotLower"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.SpaceAfterOpen"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.SpaceBeforeClose"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.ForLoopDeclaration"/> + <!-- Disable some error messages that we already cover. --> + <rule ref="Squiz.ControlStructures.ForLoopDeclaration.SpacingAfterOpen"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.ForLoopDeclaration.SpacingBeforeClose"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration"/> + <!-- Disable some error messages that we do not want. --> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.BreakIndent"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.CaseIndent"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.CloseBraceAlign"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.DefaultIndent"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.DefaultNoBreak"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.EmptyCase"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.EmptyDefault"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.MissingDefault"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.SpacingAfterCase"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.SpacingAfterDefaultBreak"> + <severity>0</severity> + </rule> + <rule ref="Squiz.ControlStructures.SwitchDeclaration.SpacingBeforeBreak"> + <severity>0</severity> + </rule> + <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing"> + <properties> + <property name="equalsSpacing" value="1"/> + </properties> + </rule> + <rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.NoSpaceBeforeArg"> + <severity>0</severity> + </rule> + <rule ref="Squiz.PHP.LowercasePHPFunctions"/> + <rule ref="Squiz.PHP.NonExecutableCode"/> + <rule ref="Squiz.Strings.ConcatenationSpacing"> + <properties> + <property name="spacing" value="1"/> + <property name="ignoreNewlines" value="true"/> + </properties> + </rule> + <rule ref="Squiz.WhiteSpace.FunctionSpacing"> + <properties> + <property name="spacing" value="1"/> + </properties> + </rule> + <rule ref="Squiz.WhiteSpace.LanguageConstructSpacing"/> + <rule ref="Squiz.WhiteSpace.OperatorSpacing"> + <properties> + <property name="ignoreNewlines" value="true"/> + </properties> + </rule> + <rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/> + <rule ref="Squiz.WhiteSpace.SemicolonSpacing"/> + <rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/> + + <!-- Zend sniffs --> + <rule ref="Zend.Files.ClosingTag"/> + +</ruleset> diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..92f1b9ce1bddb2e00c1f04c273d9e84aadaed118 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ruleset name="project_browser"> + <!-- Drupal (core) uses stylelint: https://www.drupal.org/list-changes/drupal/published?keywords_description=stylelint--> + <exclude-pattern>*.css</exclude-pattern> + <!-- Drupal (core) uses eslint: https://www.drupal.org/list-changes/drupal/published?keywords_description=eslint--> + <exclude-pattern>*.js</exclude-pattern> + + <!-- Use the Drupal core coding standard --> + <rule ref="./core.phpcs.xml.dist"></rule> +</ruleset> diff --git a/tests/modules/project_browser_test/src/TestInstallReadiness.php b/tests/modules/project_browser_test/src/TestInstallReadiness.php index bc17893aee871f02adb63d39e16bbd6a7175d31b..4823c2b75dc1fd72a99b0403170c87c3242e50dd 100644 --- a/tests/modules/project_browser_test/src/TestInstallReadiness.php +++ b/tests/modules/project_browser_test/src/TestInstallReadiness.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Drupal\project_browser_test; use Drupal\Core\State\StateInterface; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\package_manager\Event\StatusCheckEvent; use Drupal\system\SystemManager; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -42,12 +43,12 @@ class TestInstallReadiness implements EventSubscriberInterface { if ($severity === SystemManager::REQUIREMENT_ERROR) { $event->addError([ - t('Simulate an error message for the project browser.'), + new TranslatableMarkup('Simulate an error message for the project browser.'), ]); } elseif ($severity === SystemManager::REQUIREMENT_WARNING) { $event->addWarning([ - t('Simulate a warning message for the project browser.'), + new TranslatableMarkup('Simulate a warning message for the project browser.'), ]); } } diff --git a/tests/src/Functional/InstallerControllerTest.php b/tests/src/Functional/InstallerControllerTest.php index 055670422d2ae85d0c4f11c6ac920c60e5c9c57e..cfe51d95b17c4e8fd5df35685ceb6e9f52a217c0 100644 --- a/tests/src/Functional/InstallerControllerTest.php +++ b/tests/src/Functional/InstallerControllerTest.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Drupal\Tests\project_browser\Functional; use Drupal\Component\Serialization\Json; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Core\Url; use Drupal\Tests\ApiRequestTrait; use Drupal\Tests\BrowserTestBase; @@ -197,6 +198,7 @@ class InstallerControllerTest extends BrowserTestBase { $this->stageId = Json::decode($contents)['stage_id']; $this->assertSession()->statusCodeEquals(200); $expected_output = sprintf('{"phase":"create","status":0,"stage_id":"%s"}', $this->stageId); + $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); } /** @@ -208,8 +210,8 @@ class InstallerControllerTest extends BrowserTestBase { $response = $this->getPostResponse('project_browser.stage.require', 'project_browser_test_mock/awesome_module', [ 'stage_id' => $this->stageId, ]); - $expected_output = sprintf('{"phase":"create","status":0,"stage_id":"%s"}', $this->stageId); - $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); + $expected_output = sprintf('{"phase":"require","status":0,"stage_id":"%s"}', $this->stageId); + $this->assertSame($expected_output, (string) $response->getBody()); $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'requiring'); } @@ -266,7 +268,7 @@ class InstallerControllerTest extends BrowserTestBase { * @covers ::create */ public function testPreCreateError(): void { - $message = t('This is a PreCreate error.'); + $message = new TranslatableMarkup('This is a PreCreate error.'); $result = ValidationResult::createError([$message]); TestSubscriber::setTestResult([$result], PreCreateEvent::class); $contents = $this->drupalGet('admin/modules/project_browser/install-begin'); @@ -306,7 +308,7 @@ class InstallerControllerTest extends BrowserTestBase { * @covers ::require */ public function testPreRequireError(): void { - $message = t('This is a PreRequire error.'); + $message = new TranslatableMarkup('This is a PreRequire error.'); $result = ValidationResult::createError([$message]); $this->doStart(); TestSubscriber::setTestResult([$result], PreRequireEvent::class); @@ -355,7 +357,7 @@ class InstallerControllerTest extends BrowserTestBase { * @covers ::apply */ public function testPreApplyError(): void { - $message = t('This is a PreApply error.'); + $message = new TranslatableMarkup('This is a PreApply error.'); $result = ValidationResult::createError([$message]); TestSubscriber::setTestResult([$result], PreApplyEvent::class); $this->doStart(); diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php index 78cef31cc9e9a8289982d5687fc39a081c5baa2c..2e1560123835489e3d8b2e0fbcb7465554334375 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php @@ -8,7 +8,6 @@ use Behat\Mink\Element\NodeElement; use Drupal\Core\Recipe\Recipe; use Drupal\Core\State\StateInterface; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; -use Drupal\project_browser\EnabledSourceHandler; use Drupal\project_browser\InstallState; use Drupal\project_browser_test\TestActivator; use Drupal\system\SystemManager; @@ -80,7 +79,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { TestActivator::handle('drupal/cream_cheese'); $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; @@ -106,7 +105,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { */ public function testInstallModuleAlreadyInFilesystem(): void { $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Pinky and the Brain'); $pinky_brain_selector = '#project-browser .pb-layout__main ul > li:nth-child(2)'; @@ -129,9 +128,6 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { * Tests applying a recipe from the project browser UI. */ public function testApplyRecipe(): void { - if (!class_exists(Recipe::class)) { - $this->markTestSkipped('This test cannot run because this version of Drupal does not support recipes.'); - } $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); @@ -263,9 +259,6 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); - // Find a project we can install. - $project_id = $this->chooseProjectToInstall(['cream_cheese']); - // Start install begin. $this->drupalGet('admin/modules/project_browser/install-begin', [ 'query' => ['source' => 'project_browser_test_mock'], @@ -321,7 +314,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { ->set('project_browser_test.simulated_result_severity', SystemManager::REQUIREMENT_WARNING); $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); + $this->drupalGet('admin/modules/browse/project_browser_test_mock'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); $cream_cheese_module_selector = '#project-browser .pb-layout__main ul > li:nth-child(1)'; @@ -339,31 +332,6 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $assert_session->pageTextContains('Simulate a warning message for the project browser.'); } - /** - * Finds a project from the provided source that can be installed. - * - * @param string[] $except_these_machine_names - * Project machine names that should be ignored. - * @param string $source_id - * The ID of the source to query for projects. - * - * @return string - * The project ID to use. - */ - private function chooseProjectToInstall(array $except_these_machine_names = [], string $source_id = 'project_browser_test_mock'): string { - $handler = $this->container->get(EnabledSourceHandler::class); - $results = $handler->getProjects($source_id); - - foreach ($results->list as $project) { - if (in_array($project->machineName, $except_these_machine_names, TRUE)) { - continue; - } - return $project->id; - } - - $this->fail("Could not find a project to install from amongst the enabled sources."); - } - /** * Tests the "Install selected projects" button functionality. */ @@ -447,7 +415,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { */ public function testUnlockLinkMarkup(): void { $assert_session = $this->assertSession(); - $page = $this->getSession()->getPage(); + $this->drupalGet('admin/modules/project_browser/install-begin', [ 'query' => ['source' => 'project_browser_test_mock'], ]); diff --git a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php index 66f6270a28a175f0dd930ef2249ccd59651dde0b..fa833827f2c6da5c7156f879139bb24ea6956c8f 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserUiTest.php @@ -6,7 +6,6 @@ namespace Drupal\Tests\project_browser\FunctionalJavascript; use Behat\Mink\Element\NodeElement; use Drupal\Core\Extension\MissingDependencyException; -use Drupal\Core\Recipe\Recipe; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; use Drupal\project_browser\EnabledSourceHandler; @@ -1114,9 +1113,6 @@ class ProjectBrowserUiTest extends WebDriverTestBase { * Tests that recipes show instructions for applying them. */ public function testRecipeInstructions(): void { - if (!class_exists(Recipe::class)) { - $this->markTestSkipped('This test cannot run because this version of Drupal does not support recipes.'); - } $assert_session = $this->assertSession(); $this->config('project_browser.admin_settings') diff --git a/tests/src/Kernel/InstallerTest.php b/tests/src/Kernel/InstallerTest.php index 04cc7ab648637dda92d847f309c95f6cd5291c10..01775be4bf3d9ebc1d2058d0b19da63d715d92b4 100644 --- a/tests/src/Kernel/InstallerTest.php +++ b/tests/src/Kernel/InstallerTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\Tests\project_browser\Kernel; +use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\Tests\package_manager\Kernel\PackageManagerKernelTestBase; use Drupal\Tests\package_manager\Traits\ComposerStagerTestTrait; use Drupal\Tests\user\Traits\UserCreationTrait; @@ -120,7 +121,7 @@ class InstallerTest extends PackageManagerKernelTestBase { $installer->create(); $installer->require(['org/package-name']); $results = [ - ValidationResult::createError([t('These are not the projects you are looking for.')]), + ValidationResult::createError([new TranslatableMarkup('These are not the projects you are looking for.')]), ]; TestSubscriber::setTestResult($results, PreApplyEvent::class); $this->expectException(StageEventException::class); diff --git a/tests/src/Kernel/RecipesSourceTest.php b/tests/src/Kernel/RecipesSourceTest.php index e167d58bcc63ba2fd74faaa5bba3a67ba42a33ec..5c5bd3ffa8ef96d3b2c010c9bd4b304f46ebd358 100644 --- a/tests/src/Kernel/RecipesSourceTest.php +++ b/tests/src/Kernel/RecipesSourceTest.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Drupal\Tests\project_browser\Kernel; use Drupal\Component\FileSystem\FileSystem; -use Drupal\Core\Recipe\Recipe; use Drupal\KernelTests\KernelTestBase; use Drupal\project_browser\Plugin\ProjectBrowserSourceManager; use Drupal\project_browser\ProjectType; @@ -31,9 +30,6 @@ class RecipesSourceTest extends KernelTestBase { protected function setUp(): void { parent::setUp(); - if (!class_exists(Recipe::class)) { - $this->markTestSkipped('This test cannot be run because the recipe system is not available.'); - } $this->installSchema('project_browser_test', [ 'project_browser_projects', 'project_browser_categories', @@ -175,10 +171,7 @@ class RecipesSourceTest extends KernelTestBase { $this->setSetting('project_browser_recipe_directories', [$installed_recipes_dir]); // Fetch discovered recipes. - /** @var \Drupal\project_browser\ProjectBrowser\ProjectsResultsPage $projects */ - $projects = $this->container->get(ProjectBrowserSourceManager::class) - ->createInstance('recipes') - ->getProjects(); + $projects = $source->getProjects(); $found_recipes = array_column($projects->list, 'title'); $generated_recipe_titles = array_keys($generated_recipes);