├── .gitignore ├── .rmt.yml ├── .travis.php.ini ├── .travis.yml ├── CHANGELOG ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── UPGRADE-3.0.md ├── bin └── qa-tools ├── box.release.json ├── box.test.json ├── build ├── release │ ├── .gitkeep │ └── qa-tools.phar.pubkey └── test │ └── .gitkeep ├── composer.json ├── composer.lock ├── couscous.yml ├── docs ├── couscous │ └── template │ │ ├── .gitignore │ │ ├── README.md │ │ ├── css │ │ ├── bootstrap.min.css │ │ ├── highlight.forest-light.css │ │ └── main.css │ │ ├── default.twig │ │ └── js │ │ └── highlight.min.js ├── development.md ├── development │ ├── configuration-process.md │ ├── task-development.md │ ├── tool-development.md │ ├── writing-documentation.md │ └── writing-system-tests.md ├── phar.md ├── release-process.md ├── reporting-a-bug.md └── ubiquitous-language.md ├── installer.php ├── phpmd.xml ├── phpunit.xml ├── qa-tools.json ├── ruleset.xml ├── src ├── Composer │ ├── BoxDownloaderScriptHandler.php │ └── BuildPharScriptHandler.php ├── Core │ ├── Application │ │ ├── Application.php │ │ ├── Basedir.php │ │ ├── Command │ │ │ ├── ConfigureCommand.php │ │ │ ├── PreCommitCommand.php │ │ │ ├── PrePushCommand.php │ │ │ └── SelfUpdateCommand.php │ │ ├── Compiler │ │ │ ├── RegisterConfiguratorsCompilerPass.php │ │ │ └── RegisterTaskExecutorsCompilerPass.php │ │ └── ContainerLoader.php │ ├── Assert │ │ └── Assertion.php │ ├── Build │ │ ├── Build.php │ │ ├── Snippet.php │ │ └── Tool.php │ ├── Composer │ │ ├── CliComposerProject.php │ │ ├── CliComposerProjectFactory.php │ │ ├── Configuration.php │ │ ├── Package.php │ │ ├── PackageName.php │ │ ├── PackageSet.php │ │ ├── PackageVersionConstraint.php │ │ ├── Project.php │ │ ├── ProjectFactory.php │ │ ├── RequireCache.php │ │ └── RuntimeException.php │ ├── Configuration │ │ ├── Configuration.php │ │ ├── ConfigurationRepository.php │ │ ├── ConfigurationService.php │ │ ├── FileConfigurationRepository.php │ │ ├── InMemoryConfigurationRepository.php │ │ ├── InMemoryTaskDirectory.php │ │ ├── InMemoryTaskDirectoryFactory.php │ │ ├── MemorizingInterviewer.php │ │ ├── ProjectConfigurator.php │ │ ├── QuestionId.php │ │ ├── TaskDirectory.php │ │ ├── TaskDirectoryFactory.php │ │ ├── TaskHelperSet.php │ │ └── ToolConfigurator.php │ ├── Configurator │ │ ├── Configurator.php │ │ ├── ConfiguratorList.php │ │ └── ConfiguratorRepository.php │ ├── Exception │ │ ├── InvalidAnswerGivenException.php │ │ ├── InvalidArgumentException.php │ │ ├── LogicException.php │ │ └── RuntimeException.php │ ├── GitHook │ │ ├── GitHookInstaller.php │ │ └── files │ │ │ ├── pre-commit │ │ │ └── pre-push │ ├── IO │ │ ├── Cli │ │ │ ├── ConsoleQuestionFactory.php │ │ │ ├── ConsoleQuestionFormatter.php │ │ │ ├── Interviewer.php │ │ │ ├── InterviewerFactory.php │ │ │ └── Validator │ │ │ │ ├── TextualAnswerValidator.php │ │ │ │ └── YesOrNoAnswerValidator.php │ │ └── File │ │ │ ├── FileHandler.php │ │ │ └── FilesystemFileHandler.php │ ├── Interviewer │ │ ├── Answer │ │ │ ├── Answer.php │ │ │ ├── AnswerFactory.php │ │ │ ├── Choices.php │ │ │ ├── NoDefaultAnswer.php │ │ │ ├── TextualAnswer.php │ │ │ └── YesOrNoAnswer.php │ │ ├── AutomatedResponseInterviewer.php │ │ ├── Interviewer.php │ │ ├── Question │ │ │ ├── ListChoiceQuestion.php │ │ │ ├── MultipleChoiceQuestion.php │ │ │ ├── Question.php │ │ │ ├── QuestionFactory.php │ │ │ ├── TextualQuestion.php │ │ │ └── YesOrNoQuestion.php │ │ └── ScopedInterviewer.php │ ├── Project │ │ ├── Directory.php │ │ ├── Project.php │ │ ├── ProjectType.php │ │ └── ProjectTypeSet.php │ ├── Resources │ │ ├── config │ │ │ ├── config.yml │ │ │ ├── services.yml │ │ │ └── task_executors.yml │ │ └── templates │ │ │ └── build.xml.twig │ ├── Task │ │ ├── AddAntBuildTask.php │ │ ├── Executor │ │ │ ├── AddBuildTaskExecutor.php │ │ │ ├── ArrayExecutorCollection.php │ │ │ ├── Executor.php │ │ │ ├── ExecutorCollection.php │ │ │ ├── InstallComposerDevDependencyTaskExecutor.php │ │ │ ├── Sigints.php │ │ │ ├── TaskDirectoryExecutor.php │ │ │ ├── TransactionalTaskDirectoryExecutor.php │ │ │ └── WriteFileTaskExecutor.php │ │ ├── InstallComposerDevDependencyTask.php │ │ ├── Task.php │ │ ├── TaskList.php │ │ └── WriteFileTask.php │ ├── Templating │ │ ├── Escape.php │ │ ├── TemplateEngine.php │ │ └── TwigFactory.php │ └── Tool │ │ ├── AbstractTool.php │ │ └── Tool.php ├── PharUpdater │ └── Strategy │ │ └── GitHubReleasesApiStrategy.php └── Tool │ ├── Behat │ ├── Behat.php │ ├── Configurator │ │ ├── BehatConfigurator.php │ │ └── DrupalBehatConfigurator.php │ └── Resources │ │ ├── config │ │ └── configurators.yml │ │ └── templates │ │ ├── FeatureContext.php.twig │ │ ├── ant-build.xml.twig │ │ ├── behat.yml │ │ └── drupal │ │ ├── FeatureContext.php.twig │ │ └── behat.yml │ ├── PhpCs │ ├── Configurator │ │ ├── PhpCsConfigurator.php │ │ ├── PhpCsDrupal7Configurator.php │ │ └── PhpCsDrupal8Configurator.php │ ├── PhpCs.php │ └── Resources │ │ ├── config │ │ └── configurators.yml │ │ └── templates │ │ ├── ant-build.xml.twig │ │ ├── ruleset-reference.xml.twig │ │ ├── ruleset.xml.twig │ │ ├── snippet-ignore-function-comments-in-tests.xml.twig │ │ └── snippet-line-length.xml.twig │ ├── PhpMd │ ├── Configurator │ │ ├── DrupalPhpMdConfigurator.php │ │ └── PhpMdConfigurator.php │ ├── PhpMd.php │ └── Resources │ │ ├── config │ │ └── configurators.yml │ │ └── templates │ │ ├── ant-build.xml.twig │ │ └── phpmd-default.xml.twig │ ├── PhpParallelLint │ ├── Configurator │ │ ├── DrupalPhpParallelLintConfigurator.php │ │ └── PhpParallelLintConfigurator.php │ ├── PhpParallelLint.php │ └── Resources │ │ ├── config │ │ └── configurators.yml │ │ └── templates │ │ ├── ant-diff.xml.twig │ │ └── ant-full.xml.twig │ ├── PhpUnit │ ├── Configurator │ │ ├── Drupal8PhpUnitConfigurator.php │ │ └── PhpUnitConfigurator.php │ ├── PhpUnit.php │ └── Resources │ │ ├── config │ │ └── configurators.yml │ │ └── templates │ │ ├── ant-build.xml.twig │ │ ├── drupal8 │ │ ├── bootstrap.php.twig │ │ └── phpunit.xml.twig │ │ ├── phpunit.default.xml.twig │ │ └── phpunit.symfony.xml.twig │ └── SensioLabsSecurityChecker │ ├── Configurator │ └── SecurityCheckerConfigurator.php │ ├── Resources │ ├── config │ │ └── configurators.yml │ └── templates │ │ └── ant-build.xml.twig │ └── SensioLabsSecurityChecker.php ├── tests ├── MockeryTestCase.php ├── bootstrap.php ├── composer │ ├── Composer.php │ └── packages │ │ ├── behat │ │ └── behat │ │ │ └── composer.json │ │ ├── drupal │ │ ├── coder7 │ │ │ └── composer.json │ │ ├── coder8 │ │ │ └── composer.json │ │ └── drupal-extension │ │ │ └── composer.json │ │ ├── escapestudios │ │ └── symfony2-coding-standard │ │ │ └── composer.json │ │ ├── jakub-onderka │ │ └── php-parallel-lint │ │ │ └── composer.json │ │ ├── phpmd │ │ └── phpmd │ │ │ └── composer.json │ │ ├── phpunit │ │ └── phpunit │ │ │ └── composer.json │ │ ├── sensiolabs │ │ └── security-checker │ │ │ └── composer.json │ │ └── squizlabs │ │ └── php_codesniffer │ │ └── composer.json ├── integration │ ├── .gitkeep │ ├── ContainerTestCase.php │ ├── Core │ │ ├── Composer │ │ │ └── CliComposerProjectTest.php │ │ ├── IO │ │ │ └── File │ │ │ │ └── FilesystemFileHandlerTest.php │ │ ├── Task │ │ │ └── Executor │ │ │ │ └── SigintsTest.php │ │ ├── Templating │ │ │ ├── TemplateEngineTest.php │ │ │ └── templates │ │ │ │ └── non-existent-variable.twig │ │ └── Tool │ │ │ ├── AbstractToolTest.php │ │ │ └── Hammer │ │ │ ├── Hammer.php │ │ │ └── Resources │ │ │ └── .gitkeep │ └── Installer │ │ └── InstallerTest.php ├── security │ ├── .gitkeep │ └── verify-readme-installer-hash ├── system │ ├── Composer.php │ ├── SystemTest.php │ ├── assert.php │ ├── harness.tcl │ └── specs │ │ ├── 000_can-configure-a-php-project.php │ │ ├── 000_can-configure-a-php-project.tcl │ │ ├── 003_can-configure-a-symfony-project.php │ │ ├── 003_can-configure-a-symfony-project.tcl │ │ ├── 005_can-configure-a-drupal8-project.php │ │ ├── 005_can-configure-a-drupal8-project.tcl │ │ ├── 010_developer-need-not-reanswer-previously-answered-questions.php │ │ ├── 010_developer-need-not-reanswer-previously-answered-questions.tcl │ │ ├── 020_aborts-on-composer-dependency-conflict.php │ │ ├── 020_aborts-on-composer-dependency-conflict.tcl │ │ ├── 025_developer-need-not-reanswer-even-when-previous-run-failed.php │ │ ├── 025_developer-need-not-reanswer-even-when-previous-run-failed_configure.tcl │ │ ├── 025_developer-need-not-reanswer-even-when-previous-run-failed_reconfigure.tcl │ │ ├── 030_aborts-when-config-cannot-be-written.php │ │ ├── 030_aborts-when-config-cannot-be-written_setup.tcl │ │ ├── 030_aborts-when-config-cannot-be-written_try-writing.tcl │ │ ├── 040_offers_to_initialise_composer.php │ │ ├── 040_offers_to_initialise_composer.tcl │ │ ├── 050_can-install-git-pre-commit-hook.php │ │ ├── 050_can-install-git-pre-commit-hook.tcl │ │ ├── 055_asks-confirmation-before-overwriting-pre-commit-hook.php │ │ ├── 055_asks-confirmation-before-overwriting-pre-commit-hook.tcl │ │ ├── 060_can-install-git-pre-push-hook.php │ │ ├── 060_can-install-git-pre-push-hook.tcl │ │ ├── 065_asks-confirmation-before-overwriting-pre-push-hook.php │ │ └── 065_asks-confirmation-before-overwriting-pre-push-hook.tcl └── unit │ ├── AddBuildTaskMatcher.php │ ├── Core │ ├── Application │ │ └── Compiler │ │ │ └── RegisterTaskExecutorsCompilerPassTest.php │ ├── Assert │ │ └── AssertionTest.php │ ├── Composer │ │ ├── PackageSetTest.php │ │ ├── PackageTest.php │ │ ├── PackageVersionConstraintTest.php │ │ └── RequireCacheTest.php │ ├── Configuration │ │ ├── ConfiguratorRepositoryTest.php │ │ ├── FakeConfigurator.php │ │ ├── FileConfigurationRepositoryTest.php │ │ ├── InMemoryConfigurationRepositoryTest.php │ │ ├── InMemoryTaskDirectoryTest.php │ │ └── TaskHelperSetTest.php │ ├── GitHook │ │ └── GitHookInstallerTest.php │ ├── IO │ │ ├── Cli │ │ │ ├── ConsoleQuestionFactoryTest.php │ │ │ ├── ConsoleQuestionFormatterTest.php │ │ │ ├── InterviewerTest.php │ │ │ └── Validator │ │ │ │ ├── TextualAnswerValidatorTest.php │ │ │ │ └── YesOrNoAnswerValidatorTest.php │ │ └── File │ │ │ └── FilesystemFileHandlerTest.php │ ├── Interviewer │ │ ├── Answer │ │ │ ├── AnswerFactoryTest.php │ │ │ ├── ChoicesTest.php │ │ │ ├── NoDefaultAnswerTest.php │ │ │ ├── TextualAnswerTest.php │ │ │ └── YesOrNoAnswerTest.php │ │ ├── AutomatedResponseInterviewerTest.php │ │ └── Question │ │ │ ├── ListChoiceQuestionTest.php │ │ │ ├── MultipleChoiceQuestionTest.php │ │ │ ├── QuestionFactoryTest.php │ │ │ ├── TextualQuestionTest.php │ │ │ └── YesOrNoQuestionTest.php │ ├── MemorizingInterviewerTest.php │ ├── Project │ │ ├── DirectoryTest.php │ │ ├── ProjectTypeSetTest.php │ │ └── ProjectTypeTest.php │ └── Task │ │ ├── Executor │ │ ├── AddBuildTaskExecutorTest.php │ │ ├── ArrayExecutorCollectionTest.php │ │ ├── InstallComposerDevDependencyTaskExecutorTest.php │ │ ├── TransactionalTaskDirectoryExecutorTest.php │ │ └── WriteFileTaskExecutorTest.php │ │ ├── NoopTask.php │ │ └── TaskListTest.php │ ├── Diffing.php │ ├── InstallComposerDevDependencyTaskMatcher.php │ ├── Installer │ └── InstallerTest.php │ ├── PharUpdater │ └── Strategy │ │ ├── GitHubReleasesApiStrategyTest.php │ │ ├── no-releases.json │ │ ├── two-releases.json │ │ ├── two-stable-releases-one-phar.json │ │ └── two-stable-releases.json │ ├── TestDataProvider.php │ ├── Tool │ ├── Behat │ │ └── Configurator │ │ │ ├── BehatConfiguratorTest.php │ │ │ └── DrupalBehatConfiguratorTest.php │ ├── PhpCs │ │ └── Configurator │ │ │ ├── PhpCsConfiguratorTest.php │ │ │ ├── PhpCsDrupal7ConfiguratorTest.php │ │ │ └── PhpCsDrupal8ConfiguratorTest.php │ ├── PhpMd │ │ └── Configurator │ │ │ ├── DrupalPhpMdConfiguratorTest.php │ │ │ └── PhpMdConfiguratorTest.php │ ├── PhpParallelLint │ │ └── Configurator │ │ │ ├── DrupalPhpParallelLintConfiguratorTest.php │ │ │ └── PhpParallelLintConfiguratorTest.php │ ├── PhpUnit │ │ └── Configurator │ │ │ ├── Drupal8PhpUnitConfiguratorTest.php │ │ │ └── PhpUnitConfiguratorTest.php │ └── SensioLabsSecurityChecker │ │ └── Configurator │ │ └── SecurityCheckerConfiguratorTest.php │ ├── ValueObject.php │ └── WriteFileTaskMatcher.php ├── tools ├── build-phar.php └── git │ ├── pre-commit │ ├── pre-commit-install │ ├── pre-push │ └── pre-push-install └── var ├── cache └── .gitkeep └── composer └── .gitignore /.gitignore: -------------------------------------------------------------------------------- 1 | /.couscous/ 2 | /build/test/qa-tools* 3 | /build/release/qa-tools* 4 | !/build/release/qa-tools.pubkey 5 | /vendor/ 6 | /var/cache/* 7 | /bin/box 8 | /box.json 9 | /build.xml 10 | /signing-key-*.pem 11 | -------------------------------------------------------------------------------- /.rmt.yml: -------------------------------------------------------------------------------- 1 | _default: 2 | 3 | # VCS CONFIG 4 | vcs: git 5 | 6 | # PREREQUISITES 7 | # Actions executed before any questions get asked to the user. 8 | # Custom action can be added by provided a relative path the the php script. Example: 9 | # - relative/path/to/your-own-sript.php 10 | prerequisites: 11 | working-copy-check: ~ 12 | display-last-changes: ~ 13 | composer-stability-check: ~ 14 | tests-check: {command: make test, timeout: 120} 15 | 16 | # GENERAL CONFIG 17 | # Apply to all branches except the one from the 'branch-specific' section 18 | # Like prerequisites, you can add your own script. Example: 19 | # - relative/path/to/your-own-sript.php 20 | version-generator: 21 | semantic: 22 | allow-label: true 23 | version-persister: 24 | vcs-tag: # Release with VCS tag 25 | tag-prefix: "{branch-name}_" # Prefix any tag with the VCS branch name 26 | dump-commits: true 27 | pre-release-actions: 28 | changelog-update: # Update a CHANGELOG file before the release 29 | format: semantic 30 | vcs-commit: ~ # Commit the CHANGELOG 31 | post-release-actions: 32 | vcs-publish: # Publish the release to the VCS 33 | ask-confirmation: true 34 | 35 | # BRANCH SPECIFIC CONFIG 36 | # On master, we override the general config 37 | master: 38 | version-persister: 39 | vcs-tag: 40 | tag-prefix: '' # No more prefix for tags 41 | -------------------------------------------------------------------------------- /.travis.php.ini: -------------------------------------------------------------------------------- 1 | date.timezone = "UTC" 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: php 3 | php: 4 | - 5.6 5 | - 7.0 6 | - 7.1 7 | - nightly 8 | matrix: 9 | fast_finish: true 10 | allow_failures: 11 | - php: nightly 12 | addons: 13 | apt: 14 | packages: [expect] 15 | 16 | cache: 17 | directories: 18 | - ~/.composer/cache/files/ 19 | 20 | before_install: 21 | - phpenv config-add .travis.php.ini 22 | 23 | install: 24 | - composer install --prefer-dist 25 | 26 | script: 27 | - make test 28 | 29 | after_success: 30 | # Deploy documentation to gh-pages branch when Travis CI build against PHP 7.1 on master succeeds 31 | - GIT_NAME=TravisCI GIT_EMAIL=info@ibuildings.nl GH_REF=github.com/ibuildingsnl/qa-tools vendor/bin/couscous travis-auto-deploy --php-version=7.1 32 | 33 | branches: 34 | only: [master, develop] 35 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | VERSION 3 STAND-ALONE QA TOOLS 3 | ============================================================ 4 | 5 | Version 3.0 - Stand-alone QA Tools 6 | 17/03/2017 14:02 3.0.0-beta Internal beta release 7 | 24/02/2017 09:57 3.0.0-alpha2 Make installation and updating more robust 8 | 17/02/2017 14:23 3.0.0-alpha initial release 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ibuildings 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /UPGRADE-3.0.md: -------------------------------------------------------------------------------- 1 | Upgrading from 2.x to 3.0 2 | ========================= 3 | 4 | The third major release of QA Tools takes a whole different approach and requires you to reconfigure your project's quality assurance tools. If this is acceptable, follow these steps to remove QA Tools 2.x from your project: 5 | 6 | * Remove all artifacts related to QA Tools 2.x: `qa-tools.json`, `.jshintrc`, `.travis.php.ini`, `.travis.yml`, `behat.dev.yml`, `behat.yml`, `build-pre-commit.xml`, `build.xml`, `phpcs.xml`, `phpmd-pre-commit.xml`, `phpmd.xml`, `phpunit.xml`, `pre-commit`. 7 | * Remove `ibuildings/qa-tools` from your Composer dependencies by running `composer remove ibuildings/qa-tools`. *(Note: if you run Composer <1.0 add the `--update-with-dependencies` flag to also remove QA Tools 2's dependencies.)* 8 | * Install QA Tools 3 by following the [installation instructions](./README.md#Installation) 9 | * Reconfigure QA Tools by calling `/path/to/qa-tools.phar configure` in your project directory. 10 | -------------------------------------------------------------------------------- /bin/qa-tools: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | addCommands([ 18 | new ConfigureCommand(), 19 | new SelfUpdateCommand(), 20 | new PreCommitCommand(), 21 | new PrePushCommand(), 22 | ]); 23 | $application->boot(); 24 | 25 | exit($application->run()); 26 | -------------------------------------------------------------------------------- /box.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "algorithm": "OPENSSL", 3 | "chmod": "0755", 4 | "compression": "GZ", 5 | "directories": [ 6 | "var/cache", 7 | "src" 8 | ], 9 | "files": [ 10 | "LICENSE" 11 | ], 12 | "finder": [ 13 | { 14 | "name": "*.php", 15 | "exclude": [], 16 | "in": "vendor" 17 | } 18 | ], 19 | "git-version": "package_version", 20 | "intercept": false, 21 | "key": "signing-key-release.pem", 22 | "main": "bin/qa-tools", 23 | "output": "build/release/qa-tools.phar", 24 | "stub": true 25 | } 26 | -------------------------------------------------------------------------------- /box.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "algorithm": "OPENSSL", 3 | "chmod": "0755", 4 | "compression": "GZ", 5 | "directories": [ 6 | "var/cache", 7 | "src" 8 | ], 9 | "files": [ 10 | "LICENSE" 11 | ], 12 | "finder": [ 13 | { 14 | "name": "*.php", 15 | "exclude": [], 16 | "in": "vendor" 17 | } 18 | ], 19 | "git-version": "package_version", 20 | "intercept": false, 21 | "key": "signing-key-test.pem", 22 | "main": "bin/qa-tools", 23 | "output": "build/test/qa-tools.phar", 24 | "stub": true 25 | } 26 | -------------------------------------------------------------------------------- /build/release/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibuildingsnl/qa-tools/0c4999a74c55b03a826e1c881f75ee3fba6329f7/build/release/.gitkeep -------------------------------------------------------------------------------- /build/release/qa-tools.phar.pubkey: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEA2QbIDqX9CmKDUpXXrKEo 3 | UiYQiR7h0Cx6iKh0LlogqAuVmm/qFH27LbMErzTNKhd9HLlq361J1JTgCKNMM79S 4 | MgQhm+WUU4Zdzd3MsmHtDuGyv6bd92kTuTx4csm5Jnh3je4vy4kTOKQ/0cQ9/f4y 5 | 926sL2up0akrcVhqTzXYxM9oEEPIrEky4CFgsXw5RtNSYqUiwerPV4c2uojQjznh 6 | 0qQEAqH252z2xJB45FlrMMDaK2eBiBKYcsaLpKORJtQJGDuOCl5tSst+nUHoB/Sz 7 | IjFY2dzBF4Yt8vGN0alxlqFYonnKtnD7ifz6pnya5+oGt47IQznrNm0DB7zq0anv 8 | 2b1gP7NBuqG/7sbvaCHqG8REridqXK/maOm/YBy82QaoqC9UW1K8Gz/W3khUMHTZ 9 | cv3qwKVJIOs73Clrh4oRxnds4UHGhLxANtiLzrD7U+eJQaZ8ecGh3L8ZOFeounsv 10 | YSLyqGxoqHNslBfmdmZupoWQLG4VzIWCR0ZcNfso7YPOsaJbJGUenC/s0ZjcYeWR 11 | WJUnYWTeY6n3OHxh2v/OO5BAGsrJFqkpTNMxhDkb+gTMw9AVMIlhIYvBmD6UIwz7 12 | 7pWAfPLgXggx+i7BzxeSq+pQNYdAwO04w4P7UpKEWQUjVfsPfABaLQK8E6kMJpN+ 13 | EuwPcyFixgMDULX2c/QYO6ndcYDMW4qTLzRrv94OGy/WZMaihQoAe23TGB7PuJxb 14 | v/Aklr1203wNA0HYvuK3U2j7K7JlSVIn85NKKSRbE8yeR6No0We8ztbHSaakZFUD 15 | SJObFEtJ2JgA4hIs+XP+w4CSAB5fWQ0UjPMW6pwQNG5cop6Tgid+2a15xg9hQeQL 16 | 4LPKCRxX3qJDG0nMVtFrtpwQSWngZ72Cn2dotqfnm7XN2QgsxCFUK2JkiKHFGj9C 17 | HCY4R6D468/8KIqjAZGyYIUwX6JuA8RJnB7sZ70R5lsdB6pMrVqDpNF471umZ/+g 18 | aEb06j1bKh6I/66Cl9dCVFIhW6I11WQqFcQY+t1annIzDuZWzuH5TGqEuaA7ayaS 19 | No9g9v8bevKvcs3icq8QcIhYFcOtC+ATLTeZ4OrubOmzistFFsSk47NuWtcqSvbB 20 | jkjQFRRHozjXWhJz2z8szZpm3yxCYKpG5IZ+sKFXabo5M3lQZa4HgAomkhtdSgqh 21 | oC9ui6YzVpUlxFFUuNEogPevFH4sL4p98GeY4jb5ma9juNu8mGnDuZtjCSe3JEUM 22 | 8IrUn+eTp5K7w1xYTMnHgaSYjv+HI2R163uAiAEL5g/zu+Aounn414Q4ntRv1t2f 23 | 75c55erX/UMFApw5qDcbfu4BFIi2tEv8+om+pItBVaf7/LIMoQIOmF+L1IV0p1C+ 24 | KQIDAQAB 25 | -----END PUBLIC KEY----- 26 | -------------------------------------------------------------------------------- /build/test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibuildingsnl/qa-tools/0c4999a74c55b03a826e1c881f75ee3fba6329f7/build/test/.gitkeep -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ibuildings/qa-tools", 3 | "autoload": { 4 | "psr-4": { 5 | "Ibuildings\\QaTools\\" : "src/" 6 | } 7 | }, 8 | "autoload-dev": { 9 | "psr-4": { 10 | "Ibuildings\\QaTools\\UnitTest\\": ["tests/unit"], 11 | "Ibuildings\\QaTools\\IntegrationTest\\": ["tests/integration"], 12 | "Ibuildings\\QaTools\\SystemTest\\": ["tests/system"], 13 | "Ibuildings\\QaTools\\ComposerTest\\": ["tests/composer"], 14 | "Ibuildings\\QaTools\\Test\\": ["tests"] 15 | }, 16 | "files": ["tests/system/assert.php"] 17 | }, 18 | "scripts": { 19 | "post-install-cmd": [ 20 | "Ibuildings\\QaTools\\Composer\\BoxDownloaderScriptHandler::downloadBoxPhar" 21 | ], 22 | "build": [ 23 | "Ibuildings\\QaTools\\Composer\\BuildPharScriptHandler::buildPhar" 24 | ] 25 | }, 26 | "require": { 27 | "php": "^5.6|^7.0", 28 | "ext-pcntl": "*", 29 | "beberlei/assert": "^2.5", 30 | "composer/semver": "^1.4", 31 | "guzzlehttp/guzzle": "^6.2", 32 | "padraic/phar-updater": "^1.0", 33 | "psr/log": "^1.0", 34 | "symfony/config": "^3.2", 35 | "symfony/console": "^3.2", 36 | "symfony/dependency-injection": "^3.2", 37 | "symfony/process": "^3.2", 38 | "symfony/yaml": "^3.2", 39 | "twig/twig": "^1.24", 40 | "zendframework/zend-json": "^3.0" 41 | }, 42 | "require-dev": { 43 | "ext-posix": "*", 44 | "composer/composer": "^1.2", 45 | "couscous/couscous": "^1.5", 46 | "jakub-onderka/php-console-highlighter": "^0.3.2", 47 | "jakub-onderka/php-parallel-lint": "^0.9.2", 48 | "liip/rmt": "^1.2.5", 49 | "mockery/mockery": "^0.9.4", 50 | "phpmd/phpmd": "^2.0", 51 | "phpunit/phpunit": "^5.7", 52 | "sebastian/diff": "^1.4", 53 | "sebastian/exporter": "^2", 54 | "sensiolabs/security-checker": "^3.0", 55 | "squizlabs/php_codesniffer": "^2.7", 56 | "symfony/filesystem": "^3.2" 57 | }, 58 | "config": { 59 | "sort-packages": true 60 | }, 61 | "extra": { 62 | "qa-tools-box-source": "https://github.com/box-project/box2/releases/download/2.7.5/box-2.7.5.phar", 63 | "qa-tools-box-install-path": "./bin/box", 64 | "qa-tools-box-sha-sum": "77561a72b84880572bc6d3ad3b36a905c42b68ba" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /couscous.yml: -------------------------------------------------------------------------------- 1 | baseUrl: https://ibuildingsnl.github.io/qa-tools 2 | title: Ibuildings QA Tools 3 | github: 4 | user: ibuildingsnl 5 | repo: qa-tools 6 | 7 | template: 8 | directory: docs/couscous/template 9 | 10 | menu: 11 | items: 12 | home: 13 | text: Ibuildings QA Tools 14 | relativeUrl: "" 15 | github: 16 | text: GitHub 17 | absoluteUrl: https://github.com/ibuildingsnl/qa-tools 18 | contributing-guidelines: 19 | text: Contributing guidelines 20 | relativeUrl: contributing.html 21 | development: 22 | text: Development 23 | relativeUrl: docs/development.html 24 | items: 25 | configuration-process: 26 | text: Configuration process 27 | relativeUrl: docs/development/configuration-process.html 28 | task-development: 29 | text: Task development 30 | relativeUrl: docs/development/task-development.html 31 | tool-development: 32 | text: Tool development 33 | relativeUrl: docs/development/tool-development.html 34 | writing-documentation: 35 | text: Writing documentation 36 | relativeUrl: docs/development/writing-documentation.html 37 | writing-system-tests: 38 | text: Writing system tests 39 | relativeUrl: docs/development/writing-system-tests.html 40 | phar: 41 | text: Phar 42 | relativeUrl: docs/phar.html 43 | release-process: 44 | text: Release process 45 | relativeUrl: docs/release-process.html 46 | reporting-a-bug: 47 | text: Reporting a bug 48 | relativeUrl: docs/reporting-a-bug.html 49 | ubiquitous-language: 50 | text: Ubiquitous language 51 | relativeUrl: docs/ubiquitous-language.html 52 | -------------------------------------------------------------------------------- /docs/couscous/template/.gitignore: -------------------------------------------------------------------------------- 1 | /.couscous/ 2 | -------------------------------------------------------------------------------- /docs/couscous/template/README.md: -------------------------------------------------------------------------------- 1 | # Couscous template 2 | 3 | Based off https://github.com/CouscousPHP/Template-Light. 4 | 5 | ## Configuration 6 | 7 | Here are all the variables you can set in your `couscous.yml`: 8 | 9 | ```yaml 10 | # Base URL of the published website 11 | baseUrl: http://username.github.io/project 12 | 13 | # Used to link to the GitHub project 14 | github: 15 | user: myself 16 | repo: my-project 17 | 18 | title: My project 19 | 20 | # The left menu bar 21 | menu: 22 | items: 23 | home: 24 | text: Home page 25 | # You can use relative urls 26 | relativeUrl: doc/faq.html 27 | # You can add subitems 28 | items: 29 | text: Subitem 30 | relativeUrl: sub/item.html 31 | foo: 32 | text: Another link 33 | # Or absolute urls 34 | absoluteUrl: https://example.com 35 | ``` 36 | 37 | Note that the menu items can also contain HTML: 38 | 39 | ```yaml 40 | home: 41 | text: " Home page" 42 | relativeUrl: doc/faq.html 43 | ``` 44 | -------------------------------------------------------------------------------- /docs/couscous/template/css/highlight.forest-light.css: -------------------------------------------------------------------------------- 1 | .hljs-comment,.hljs-quote{color:#766e6b}.hljs-variable,.hljs-template-variable,.hljs-attribute,.hljs-tag,.hljs-name,.hljs-regexp,.hljs-link,.hljs-name,.hljs-selector-id,.hljs-selector-class{color:#f22c40}.hljs-number,.hljs-meta,.hljs-built_in,.hljs-builtin-name,.hljs-literal,.hljs-type,.hljs-params{color:#df5320}.hljs-string,.hljs-symbol,.hljs-bullet{color:#7b9726}.hljs-title,.hljs-section{color:#407ee7}.hljs-keyword,.hljs-selector-tag{color:#6666ea}.hljs{display:block;overflow-x:auto;background:#f1efee;color:#68615e;padding:0.5em}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} 2 | -------------------------------------------------------------------------------- /docs/couscous/template/css/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | position: relative; 3 | min-height: 100%; 4 | } 5 | body { 6 | font-size: 19px; 7 | } 8 | 9 | main { 10 | margin-top: 40px; 11 | } 12 | 13 | nav .nav .nav { 14 | margin-left: 1em; 15 | } 16 | 17 | section { 18 | margin-bottom: 120px; 19 | } 20 | 21 | footer { 22 | position: absolute; 23 | bottom: 0; 24 | width: 100%; 25 | background-color: #f5f5f5; 26 | padding: 25px 0; 27 | font-size: 15px; 28 | text-align: center; 29 | text-transform: uppercase; 30 | opacity: 0.6; 31 | transition: opacity .2s ease; 32 | } 33 | footer:hover { 34 | opacity: 1.0; 35 | transition: opacity .2s ease; 36 | } 37 | footer p { 38 | margin: 0; 39 | } 40 | 41 | h3 { 42 | font-size: 23px; 43 | } 44 | 45 | li { 46 | margin-bottom: 3px; 47 | } 48 | 49 | #content img { 50 | max-width: 100%; 51 | padding: 4px; 52 | background-color: #fff; 53 | border: 1px solid #ddd; 54 | border-radius: 4px; 55 | } 56 | 57 | header.navbar { 58 | opacity: 0.9; 59 | } 60 | .navbar .navbar-brand { 61 | font-size: 28px; 62 | height: auto; 63 | line-height: 40px; 64 | margin-left: 20px; 65 | } 66 | .navbar .navbar-brand small { 67 | font-size: 18px; 68 | font-weight: 300; 69 | margin-left: 10px; 70 | } 71 | 72 | @media (min-width: 768px) { 73 | #sidebar { 74 | margin-top: 30px; 75 | } 76 | } 77 | @media (max-width: 960px) { 78 | body { 79 | font-size: 17px; 80 | } 81 | pre { 82 | font-size: 12px; 83 | } 84 | } 85 | 86 | .page-header { 87 | margin-top: 0; 88 | } 89 | 90 | #sidebar .text-muted { 91 | color: #bbbbbb; 92 | } 93 | 94 | pre { 95 | padding: 0; 96 | border-radius: 4px; 97 | margin: 15px; 98 | font-size: 15px; 99 | } 100 | pre code { 101 | border: none; 102 | } 103 | -------------------------------------------------------------------------------- /docs/development.md: -------------------------------------------------------------------------------- 1 | Development 2 | =========== 3 | 4 | * [Writing system tests](development/writing-system-tests.md) 5 | 6 | -------------------------------------------------------------------------------- 7 | 8 | To get ready for contributing to the Ibuildings QA Tools: 9 | 10 | ```sh-session 11 | $ # Check out the repository 12 | $ git clone git@github.com:ibuildingsnl/qa-tools.git 13 | $ # Install the Composer dependencies 14 | $ composer install 15 | $ # Install the Git hooks 16 | $ tools/git/pre-commit-install 17 | $ tools/git/pre-push-install 18 | ``` 19 | 20 | ## Building a QA Tools distributable Phar 21 | 22 | The command below will build the QA Tools. For more specifics about building the 23 | Phar, see [Phar](phar.md). 24 | 25 | ```sh-session 26 | $ make build 27 | ``` 28 | 29 | ## Making your contribution 30 | 31 | We appreciate you helping out! Please look at the 32 | [Contributing guidelines](../CONTRIBUTING.md) to help get your contribution 33 | accepted. 34 | 35 | -------------------------------------------------------------------------------- /docs/development/configuration-process.md: -------------------------------------------------------------------------------- 1 | Configuration process 2 | ===================== 3 | 4 | The QA Tools configuration process is as follows: 5 | 6 | 0. The developer is "interviewed" the project's name, type, whether the 7 | developer uses Travis, and where all the tools' configuration files ought to be 8 | stored. These answers influence the type of configuration that takes place 9 | directly afterwards. 10 | 0. Based on the project settings (eg. project type), a list of tool 11 | configurators is compiled. Each configurator gets the chance to interview the 12 | developer for more settings pertinent to each configurator's tool. Based on 13 | the developer's answers, the configurator adds tasks to the task directory. 14 | These tasks and how they are executed are defined in the core of QA Tools; 15 | tools cannot create new types of tasks. Examples of tasks are the installation 16 | of a Composer development dependency or the writing of a tool's configuration 17 | file. 18 | 0. In the following execution stage, each task executor is tested for tasks it 19 | supports. Each executor's supported tasks are then passed to each executors 20 | various stages. 21 | 0. The tasks' prerequisites are checked by each executor. An example would 22 | be checking whether the required Composer packages don't conflict with any 23 | installed packages. 24 | 0. The tasks are executed. If a task fails, tasks that have already been 25 | executed are rolled back in reverse order. 26 | 0. Each executor gets the chance to clean up, like discarding any backups of 27 | files it was to write. 28 | 0. The project settings and the answers given to each question are stored in a 29 | configuration file. Based on this configuration file, all question's are 30 | pre-filled on a subsequent run of the QA Tools. 31 | 32 | This process is managed in the [`ConfigurationService`][src-config-service]. 33 | 34 | [src-config-service]: ../../src/Core/Service/ConfigurationService.php 35 | -------------------------------------------------------------------------------- /docs/development/writing-documentation.md: -------------------------------------------------------------------------------- 1 | Writing documentation 2 | ===================== 3 | 4 | Documentation is written in Markdown in the `docs/` directories. It is also 5 | compiled to a HTML version by [Couscous][couscous] and deployed 6 | to GitHub Pages when the Travis build against master succeeds. This is 7 | configured in this project's Travis configuration. 8 | 9 | [couscous]: https://couscous.io/ 10 | 11 | To preview how the compiled documentation looks, run: 12 | 13 | ```sh-session 14 | $ vendor/bin/couscous preview 15 | ``` 16 | 17 | When you add a new Markdown file, and would like it to be included in the 18 | compiled documentation's navigation, add it to this project's Couscous 19 | configuration in `/couscous.yml`. This configuration should speak for itself, 20 | but more information can be found in the 21 | [template's documentation](../couscous/template/README.md) or in 22 | [Couscous' documentation][couscous]. 23 | -------------------------------------------------------------------------------- /docs/phar.md: -------------------------------------------------------------------------------- 1 | Phar 2 | ==== 3 | 4 | The QA Tools are distributed as a PHP Archive (Phar) file in order to separate the QA Tools' dependencies from 5 | the host projects' dependencies. The compiled Phar is also able to verify its 6 | own integrity with the provided public key. 7 | 8 | ## Types of builds 9 | 10 | There are two types of build: a test build, and a release build. The test build is used 11 | for system testing and is signed using an insecure, ephemeral private key. The 12 | release build is for release to the public, and is signed using a secure private 13 | key managed by Ibuildings. 14 | 15 | This serves the following purposes: 16 | 17 | 0. The test process can be completely automated on the contributor's machine 18 | and on Travis, the automated build server; the private key used for signing 19 | the test build requires no passphrase. 20 | 0. There is no confusion as to with which private key the build is signed. This 21 | prevents a release of a Phar signed with the test key. 22 | 23 | ## Building the Phar 24 | 25 | [Box][^box] is used for building the QA Tools Phar file, which is configured in 26 | the `box.json` file. It is installed as a Phar file itself to prevent version 27 | conflicts between Box' and QA Tools' dependencies. 28 | 29 | To enable the writing of Phar files, disable the PHP ini setting `phar.readonly` 30 | by setting it to `Off`. 31 | 32 | Run `make build-test` in the project root directory to build the test build. 33 | This creates `./build/test/qa-tools.phar` and `./build/test/qa-tools.phar.pubkey`. 34 | 35 | ## Updating Box 36 | Box is installed during Composer's `post-install-cmd` phase. 37 | To update Box, manually update the source and sha-sum inside `composer.json` under the `extra` parameters. 38 | 39 | [^box]: https://box-project.github.io/box2/ 40 | [^secure-phar]: https://mwop.net/blog/2015-12-14-secure-phar-automation.html 41 | -------------------------------------------------------------------------------- /docs/release-process.md: -------------------------------------------------------------------------------- 1 | Release process 2 | =============== 3 | 4 | The QA Tools' release process is largely automated by [RMT][github-rmt]. The 5 | configured release process, in this order: 6 | 7 | 0. verifies that the working copy is clean; 8 | 0. verifies that the software is in working order by running all tests; 9 | 0. keeps a change log; 10 | 0. creates a new tag according to [semver][semver] rules; 11 | 0. and published the tag to the Git tracked repository `origin`. 12 | 13 | If any of the verification steps fail, the release process is aborted. These 14 | verification steps are there for a very good reason and *may never* be skipped. 15 | 16 | The release build is signed using a separate private key and is managed by 17 | Ibuildings. This key can be found in Ibuildings' LastPass and is named "QA Tools 18 | private key for releases". Place this key in `./signing-key-release.pem`. 19 | 20 | Then, to run the release process, execute the following in a terminal: 21 | 22 | ```sh-session 23 | $ make release 24 | ``` 25 | 26 | ## Distributing the release build 27 | 28 | After the new tag has been published, the [Phar](phar.md) built during the 29 | publishing process must be uploaded to the [Releases][github-qa-releases] page 30 | on GitHub. 31 | 32 | 0. Click *Draft a new release*. 33 | 0. Select the tag you just published. 34 | 0. Use the tag as release title. 35 | 0. If it's an unstable version, indicated by a stability label like `#.#.#-beta`, tick the pre-release checkbox. Ticking this checkbox does not have any functional consequences for the self-updating process, but functions primarily as documentation. 36 | 0. Copy this release's documented changes (see the change log) into the 37 | description field. 38 | 0. Attach the Phar and its public key to the release. You find these in the `./build/release/` directory. 39 | 0. Publish the release. 40 | 41 | [github-rmt]: https://github.com/liip/RMT 42 | [semver]: http://semver.org/ 43 | [path]: https://en.wikipedia.org/wiki/PATH_(variable) 44 | [github-qa-releases]: https://github.com/ibuildingsnl/qa-tools/releases 45 | -------------------------------------------------------------------------------- /docs/reporting-a-bug.md: -------------------------------------------------------------------------------- 1 | Reporting a bug 2 | =============== 3 | 4 | Whenever you find a bug in the QA Tools, we kindly ask you to report it. It 5 | helps us make it better! 6 | 7 | Before submitting a bug: 8 | 9 | * double-check the official [documentation][qa-tools-docs] to see if you're not 10 | misusing the tools; 11 | * verify someone else hasn't already reported your issue in the 12 | [issue tracker](qa-tools-issues). 13 | 14 | If your problem definitely looks like a bug, report it using the 15 | [issue tracker](qa-tools-issues) and follow some basic rules: 16 | 17 | * use the title field to clearly describe the issue; 18 | * describe the steps needed to reproduce the bug; 19 | * and give as much detail as possible about your environment (OS, PHP version, 20 | enabled extensions, ...). 21 | 22 | [qa-tools-docs]: https://github.com/ibuildingsnl/qa-tools#documentation 23 | [qa-tools-issues]: https://github.com/ibuildingsnl/qa-tools/issues 24 | -------------------------------------------------------------------------------- /docs/ubiquitous-language.md: -------------------------------------------------------------------------------- 1 | Ubiquitous language 2 | =================== 3 | 4 | | **Term** | **Explanation** | 5 | | -------------:|:--------------- | 6 | | **Configure** | The process where a *Developer* answers questions pertinent to a specific *Tool*. These answers will result in *Tasks*. | 7 | | **Contributor** | A developer that works on the QA Tools application itself by writing source code, documentation, etc. | 8 | | **Developer** | A developer that uses the QA Tools application to configure QA tools for their project. | 9 | | **Target project** | The project the QA Tools application configures tools for. | 10 | | **Task** | Describes a concrete action to be performed on the target project, like installing a set of Composer packages, or writing an Ant build file. | 11 | | **Tool** | A component that configures an actual QA tool, like PHPUnit, by, for example, writing configuration files to disk. | 12 | -------------------------------------------------------------------------------- /phpmd.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | Ibuildings QA Tools Default Ruleset for QA Tools 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | tests 16 | 17 | 18 | 19 | 20 | 21 | src 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /qa-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "QA Tools", 3 | "configurationFilesLocation": ".\/", 4 | "projectTypes": [ 5 | "php.other" 6 | ], 7 | "travisEnabled": false, 8 | "answers": { 9 | "4c6702ea73cad3d4fbeb1a275b6dbb4a": "QA Tools", 10 | "82191a2f54943a61240ff714d692663d": ".\/", 11 | "64d5c97247c332c17b131abdde8c873a": "Other PHP Project", 12 | "4735c10b621d2c15142ff92e6ec0a9a9": true, 13 | "1f73d735e7d5a37ff6e8a9c30311a00a": true, 14 | "97016cab401f3bbfaf10dfa47ec8cbd8": true, 15 | "80a50eef71a0a84f0779597a5185008e": true, 16 | "743bcf201e8b6c07fbad359c2bdf79ac": "PSR2", 17 | "be2a788ae62f9166970c6de91c828c53": "Warn when \u003E120. Fail when \u003E150", 18 | "6dd3ae893178cd90a45edaaeceab33f5": false, 19 | "809d51753d308ccf218c0217c00c779d": false, 20 | "5c08b9b1487086e2e506013f8e3fc65f": true, 21 | "c8214ea1830f49699c20dc2b42c4c190": false 22 | } 23 | } -------------------------------------------------------------------------------- /ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Enforce coding standards 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Core/Application/Basedir.php: -------------------------------------------------------------------------------- 1 | setName('configure') 23 | ->setDescription('Configure the Ibuildings QA Tools') 24 | ->setHelp('Configure the Ibuildings QA Tools'); 25 | } 26 | 27 | protected function execute(InputInterface $input, OutputInterface $output) 28 | { 29 | /** @var InterviewerFactory $interviewerFactory */ 30 | $interviewerFactory = $this->container->get('qa_tools.io.cli.interviewer_factory'); 31 | $interviewer = $interviewerFactory->createWith($input, $output); 32 | 33 | /** @var ConfigurationService $service */ 34 | $service = $this->container->get('qa_tools.configuration_service'); 35 | if (!$service->configureProject($interviewer, new Directory(getcwd()))) { 36 | return 1; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Core/Application/Command/PreCommitCommand.php: -------------------------------------------------------------------------------- 1 | setName('configure:pre-commit') 21 | ->setDescription('Configure the pre-commit hook for the Ibuildings QA Tools') 22 | ->setHelp('Configure the pre-commit hook for the Ibuildings QA Tools'); 23 | } 24 | 25 | protected function execute(InputInterface $input, OutputInterface $output) 26 | { 27 | /** @var InterviewerFactory $interviewerFactory */ 28 | $interviewerFactory = $this->container->get('qa_tools.io.cli.interviewer_factory'); 29 | $interviewer = $interviewerFactory->createWith($input, $output); 30 | 31 | $installer = $this->container->get('qa_tools.git.hook_installer'); 32 | $projectRoot = new Directory(getcwd()); 33 | 34 | $installer->installPreCommitHook($interviewer, $projectRoot); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Core/Application/Command/PrePushCommand.php: -------------------------------------------------------------------------------- 1 | setName('configure:pre-push') 21 | ->setDescription('Configure up the pre-push hook for the Ibuildings QA Tools') 22 | ->setHelp('Configure the pre-push hook for the Ibuildings QA Tools'); 23 | } 24 | 25 | protected function execute(InputInterface $input, OutputInterface $output) 26 | { 27 | /** @var InterviewerFactory $interviewerFactory */ 28 | $interviewerFactory = $this->container->get('qa_tools.io.cli.interviewer_factory'); 29 | $interviewer = $interviewerFactory->createWith($input, $output); 30 | 31 | $installer = $this->container->get('qa_tools.git.hook_installer'); 32 | $projectRoot = new Directory(getcwd()); 33 | 34 | $installer->installPrePushHook($interviewer, $projectRoot); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Core/Application/Compiler/RegisterConfiguratorsCompilerPass.php: -------------------------------------------------------------------------------- 1 | findDefinition('qa_tools.configurator_repository'); 17 | $taggedConfigurators = $container->findTaggedServiceIds('qa_tools.tool'); 18 | 19 | foreach ($taggedConfigurators as $configurator => $tags) { 20 | foreach ($tags as $tag) { 21 | if (!isset($tag['project_type'])) { 22 | throw new LogicException(sprintf( 23 | 'Cannot register Configurator "%s" for a ProjectType: property "project_type" not found on tag', 24 | $configurator 25 | )); 26 | } 27 | 28 | $projectType = new ProjectType($tag['project_type']); 29 | $projectTypeDefinition = new Definition(ProjectType::class, [$projectType->getProjectType()]); 30 | 31 | $configuratorRepositoryDefinition->addMethodCall( 32 | 'add', 33 | [ 34 | new Reference($configurator), 35 | $projectTypeDefinition, 36 | ] 37 | ); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Core/Assert/Assertion.php: -------------------------------------------------------------------------------- 1 | buildIdentifier = $targetIdentifier; 39 | } 40 | 41 | /** 42 | * @return string 43 | */ 44 | public function getBuildIdentifier() 45 | { 46 | return $this->buildIdentifier; 47 | } 48 | 49 | /** 50 | * @param Build $other 51 | * @return bool 52 | */ 53 | public function equals(Build $other) 54 | { 55 | return $this->buildIdentifier === $other->buildIdentifier; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Core/Build/Snippet.php: -------------------------------------------------------------------------------- 1 | contents = $contents; 44 | $this->target = $target; 45 | } 46 | 47 | /** 48 | * @return string 49 | */ 50 | public function getContents() 51 | { 52 | return $this->contents; 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getTarget() 59 | { 60 | return $this->target; 61 | } 62 | 63 | /** 64 | * @param Snippet $other 65 | * @return bool 66 | */ 67 | public function equals(Snippet $other) 68 | { 69 | return $this->target === $other->target 70 | && $this->contents === $other->contents; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Core/Build/Tool.php: -------------------------------------------------------------------------------- 1 | toolIdentifier = $toolIdentifier; 32 | } 33 | 34 | /** 35 | * @param Tool $other 36 | * @param string[] $toolOrder 37 | * @return int 38 | */ 39 | public function compare(Tool $other, array $toolOrder) 40 | { 41 | $otherIndex = array_search($other->toolIdentifier, $toolOrder); 42 | $thisIndex = array_search($this->toolIdentifier, $toolOrder); 43 | 44 | if ($otherIndex === $thisIndex) { 45 | return 0; 46 | } 47 | 48 | if ($otherIndex > $thisIndex) { 49 | return -1; 50 | } 51 | 52 | return 1; 53 | } 54 | 55 | /** 56 | * @param Tool $other 57 | * @return bool 58 | */ 59 | public function equals(Tool $other) 60 | { 61 | return $this->toolIdentifier == $other->toolIdentifier; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Core/Composer/CliComposerProjectFactory.php: -------------------------------------------------------------------------------- 1 | logger = $logger; 18 | } 19 | 20 | public function forDirectory($directory) 21 | { 22 | Assertion::string($directory, 'Composer project directory ought to be a string, got "%s" of type "%s"'); 23 | 24 | $envComposerPath = getenv('COMPOSER_BIN'); 25 | $pharPath = $directory . '/composer.phar'; 26 | 27 | if ($envComposerPath) { 28 | $composerBinary = $envComposerPath; 29 | } elseif (file_exists($pharPath)) { 30 | $composerBinary = $pharPath; 31 | } else { 32 | $composerBinary = 'composer'; 33 | } 34 | 35 | return new CliComposerProject($directory, $composerBinary, $this->logger); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Core/Composer/Configuration.php: -------------------------------------------------------------------------------- 1 | composerJson = $composerJson; 50 | $this->composerLockJson = $composerLockJson; 51 | } 52 | 53 | /** 54 | * @return bool 55 | */ 56 | public function hasLockedDependencies() 57 | { 58 | return $this->composerLockJson !== null; 59 | } 60 | 61 | /** 62 | * @return string 63 | */ 64 | public function getComposerJson() 65 | { 66 | return $this->composerJson; 67 | } 68 | 69 | /** 70 | * @return string|null 71 | */ 72 | public function getComposerLockJson() 73 | { 74 | return $this->composerLockJson; 75 | } 76 | 77 | /** 78 | * @param Configuration $configuration 79 | * @return boolean 80 | */ 81 | public function equals(Configuration $configuration) 82 | { 83 | return $this->composerJson === $configuration->composerJson 84 | && $this->composerLockJson === $configuration->composerLockJson; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Core/Composer/Package.php: -------------------------------------------------------------------------------- 1 | name = $name; 34 | $this->versionConstraint = $versionConstraint; 35 | } 36 | 37 | /** 38 | * @param PackageVersionConstraint $versionConstraint 39 | * @return bool 40 | */ 41 | public function versionConstraintEquals(PackageVersionConstraint $versionConstraint) 42 | { 43 | return $this->versionConstraint->equals($versionConstraint); 44 | } 45 | 46 | /** 47 | * @param Package $other 48 | * @return bool 49 | */ 50 | public function equals(Package $other) 51 | { 52 | return $this->name->equals($other->name) 53 | && $this->versionConstraint->equals($other->versionConstraint); 54 | } 55 | 56 | /** 57 | * Returns this package's descriptor, eg. "phpmd/phpmd:^2.0". 58 | * 59 | * @return string 60 | */ 61 | public function getDescriptor() 62 | { 63 | return sprintf( 64 | '%s:%s', 65 | $this->getName()->getName(), 66 | $this->getVersionConstraint()->getConstraint() 67 | ); 68 | } 69 | 70 | /** 71 | * @return PackageName 72 | */ 73 | public function getName() 74 | { 75 | return $this->name; 76 | } 77 | 78 | /** 79 | * @return PackageVersionConstraint 80 | */ 81 | public function getVersionConstraint() 82 | { 83 | return $this->versionConstraint; 84 | } 85 | 86 | public function __toString() 87 | { 88 | return sprintf('Package("%s:%s")', $this->name->getName(), $this->versionConstraint->getConstraint()); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Core/Composer/PackageName.php: -------------------------------------------------------------------------------- 1 | name = $name; 27 | } 28 | 29 | /** 30 | * @param PackageName $other 31 | * @return bool 32 | */ 33 | public function equals(PackageName $other) 34 | { 35 | return $this->name === $other->name; 36 | } 37 | 38 | /** 39 | * @return string 40 | */ 41 | public function getName() 42 | { 43 | return $this->name; 44 | } 45 | 46 | public function __toString() 47 | { 48 | return sprintf('PackageName("%s")', $this->name); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Core/Composer/PackageVersionConstraint.php: -------------------------------------------------------------------------------- 1 | parseConstraints($constraint); 32 | } catch (UnexpectedValueException $e) { 33 | throw new InvalidArgumentException( 34 | sprintf('Package version constraint "%s" is invalid', $constraint) 35 | ); 36 | } 37 | 38 | return new PackageVersionConstraint($constraint, $parsedConstraint); 39 | } 40 | 41 | /** 42 | * @param string $constraint 43 | * @param string $parsedConstraint 44 | */ 45 | private function __construct($constraint, $parsedConstraint) 46 | { 47 | $this->constraint = $constraint; 48 | $this->parsedConstraint = $parsedConstraint; 49 | } 50 | 51 | /** 52 | * @param PackageVersionConstraint $other 53 | * @return bool 54 | */ 55 | public function equals(PackageVersionConstraint $other) 56 | { 57 | return $this->parsedConstraint === $other->parsedConstraint; 58 | } 59 | 60 | /** 61 | * @return string 62 | */ 63 | public function getConstraint() 64 | { 65 | return $this->constraint; 66 | } 67 | 68 | public function __toString() 69 | { 70 | return sprintf('PackageVersionConstraint("%s", parsed="%s")', $this->constraint, $this->parsedConstraint); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Core/Composer/Project.php: -------------------------------------------------------------------------------- 1 | cause = $cause; 24 | } 25 | 26 | /** 27 | * Returns a detailed, ideally human-readable, explanation of the cause of this exception. 28 | * 29 | * @return string 30 | */ 31 | public function getCause() 32 | { 33 | return $this->cause; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Core/Configuration/Configuration.php: -------------------------------------------------------------------------------- 1 | project = $project; 41 | $this->answers = $answers; 42 | } 43 | 44 | public function reconfigureProject(Project $project) 45 | { 46 | $this->project = $project; 47 | } 48 | 49 | /** 50 | * @param QuestionId $questionId 51 | * @param Answer $answer 52 | */ 53 | public function answer(QuestionId $questionId, Answer $answer) 54 | { 55 | $this->answers[$questionId->getQuestionId()] = $answer; 56 | } 57 | 58 | /** 59 | * @return Project|null 60 | */ 61 | public function getProject() 62 | { 63 | return $this->project; 64 | } 65 | 66 | /** 67 | * @param QuestionId $questionId 68 | * @return bool 69 | */ 70 | public function hasAnswer(QuestionId $questionId) 71 | { 72 | return isset($this->answers[$questionId->getQuestionId()]); 73 | } 74 | 75 | /** 76 | * @param QuestionId $questionId 77 | * @return Answer 78 | */ 79 | public function getAnswer(QuestionId $questionId) 80 | { 81 | if (!$this->hasAnswer($questionId)) { 82 | throw new RuntimeException(sprintf('No answer with id "%s" stored in configuration', $questionId)); 83 | } 84 | 85 | return $this->answers[$questionId->getQuestionId()]; 86 | } 87 | 88 | /** 89 | * @return Answer[] 90 | */ 91 | public function getAnswers() 92 | { 93 | return $this->answers; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Core/Configuration/ConfigurationRepository.php: -------------------------------------------------------------------------------- 1 | storedConfiguration !== null; 17 | } 18 | 19 | public function load() 20 | { 21 | if (!$this->configurationExists()) { 22 | throw new RuntimeException('No configuration stored in memory'); 23 | } 24 | 25 | return $this->storedConfiguration; 26 | } 27 | 28 | public function save(Configuration $configuration) 29 | { 30 | $this->storedConfiguration = $configuration; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Core/Configuration/InMemoryTaskDirectory.php: -------------------------------------------------------------------------------- 1 | project = $project; 24 | $this->tasks = new TaskList(); 25 | } 26 | 27 | public function registerTask(Task $task) 28 | { 29 | $this->tasks = $this->tasks->add($task); 30 | } 31 | 32 | /** 33 | * @param callable $predicate 34 | * @return TaskList 35 | */ 36 | public function filterTasks(callable $predicate) 37 | { 38 | return $this->tasks->filter($predicate); 39 | } 40 | 41 | /** 42 | * @return TaskList 43 | */ 44 | public function getTasks() 45 | { 46 | return $this->tasks; 47 | } 48 | 49 | public function getProject() 50 | { 51 | return $this->project; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Core/Configuration/InMemoryTaskDirectoryFactory.php: -------------------------------------------------------------------------------- 1 | interviewer = $interviewer; 34 | $this->configuration = $configuration; 35 | } 36 | 37 | public function setScope($scope) 38 | { 39 | Assertion::string($scope); 40 | 41 | $this->scope = $scope; 42 | } 43 | 44 | public function ask(QuestionInterface $question) 45 | { 46 | $questionIdentifier = QuestionId::fromScopeAndQuestion($this->scope, $question); 47 | 48 | if ($this->configuration->hasAnswer($questionIdentifier)) { 49 | $previousAnswer = $this->configuration->getAnswer($questionIdentifier); 50 | $question = $question->withDefaultAnswer($previousAnswer); 51 | } 52 | 53 | $givenAnswer = $this->interviewer->ask($question); 54 | $this->configuration->answer($questionIdentifier, $givenAnswer); 55 | 56 | return $givenAnswer; 57 | } 58 | 59 | public function notice($sentence) 60 | { 61 | $this->interviewer->notice($sentence); 62 | } 63 | 64 | public function giveDetails($sentence) 65 | { 66 | $this->interviewer->giveDetails($sentence); 67 | } 68 | 69 | public function success($sentence) 70 | { 71 | $this->interviewer->success($sentence); 72 | } 73 | 74 | public function warn($sentence) 75 | { 76 | $this->interviewer->warn($sentence); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/Core/Configuration/QuestionId.php: -------------------------------------------------------------------------------- 1 | getQuestion())); 29 | } 30 | 31 | /** 32 | * @param string $questionId 33 | */ 34 | public function __construct($questionId) 35 | { 36 | Assertion::nonEmptyString( 37 | $questionId, 38 | 'Expected non-empty string for "%3$s", got "%s" of type "%s"', 39 | 'Question ID ought to be a non-empty string, got "%s" of type "%s"' 40 | ); 41 | 42 | $this->questionId = $questionId; 43 | } 44 | 45 | /** 46 | * @param QuestionId $other 47 | * @return bool 48 | */ 49 | public function equals(QuestionId $other) 50 | { 51 | return $this->questionId === $other->questionId; 52 | } 53 | 54 | /** 55 | * @return string 56 | */ 57 | public function getQuestionId() 58 | { 59 | return $this->questionId; 60 | } 61 | 62 | public function __toString() 63 | { 64 | return sprintf('%s("%s")', self::class, $this->questionId); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Core/Configuration/TaskDirectory.php: -------------------------------------------------------------------------------- 1 | templateEngine = $templateEngine; 18 | } 19 | 20 | /** 21 | * @param string $template 22 | * @param array $params 23 | * @return string 24 | */ 25 | public function renderTemplate($template, array $params = []) 26 | { 27 | Assertion::nonEmptyString($template, 'Expected non-empty string for "%3$s", got "%s" of type "%s"', 'template'); 28 | 29 | return $this->templateEngine->render($template, $params); 30 | } 31 | 32 | /** 33 | * @param string $path 34 | */ 35 | public function setTemplatePath($path) 36 | { 37 | Assertion::nonEmptyString($path, 'Expected non-empty string for "%3$s", got "%s" of type "%s"', 'path'); 38 | 39 | $this->templateEngine->setPath($path); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Core/Configuration/ToolConfigurator.php: -------------------------------------------------------------------------------- 1 | taskHelperSet = $taskHelperSet; 25 | $this->container = $container; 26 | } 27 | 28 | public function configure( 29 | ConfiguratorList $configurators, 30 | ScopedInterviewer $interviewer, 31 | TaskDirectory $taskDirectory 32 | ) { 33 | foreach ($configurators as $configurator) { 34 | /** @var Configurator $configurator */ 35 | $interviewer->setScope($configurator->getToolClassName()); 36 | 37 | $resourcePath = $this->container->getParameter( 38 | sprintf('tool.%s.resource_path', $configurator->getToolClassName()) 39 | ); 40 | $templatePath = $resourcePath . '/templates'; 41 | $this->taskHelperSet->setTemplatePath($templatePath); 42 | 43 | $configurator->configure($interviewer, $taskDirectory, $this->taskHelperSet); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Core/Configurator/Configurator.php: -------------------------------------------------------------------------------- 1 | configurators = $configurators; 21 | } 22 | 23 | /** 24 | * @param Configurator $configurator 25 | * @return bool 26 | */ 27 | public function contains(Configurator $configurator) 28 | { 29 | return in_array($configurator, $this->configurators); 30 | } 31 | 32 | /** 33 | * @param Configurator $configurator 34 | * @return ConfiguratorList 35 | */ 36 | public function append(Configurator $configurator) 37 | { 38 | return new self(array_merge($this->configurators, $configurator)); 39 | } 40 | 41 | public function appendList(ConfiguratorList $other) 42 | { 43 | return new self(array_merge($this->configurators, $other->configurators)); 44 | } 45 | 46 | public function getIterator() 47 | { 48 | return new ArrayIterator($this->configurators); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Core/Configurator/ConfiguratorRepository.php: -------------------------------------------------------------------------------- 1 | getProjectType(); 26 | 27 | if (!key_exists($projectTypeKey, $this->registeredConfigurators)) { 28 | $this->registeredConfigurators[$projectTypeKey] = []; 29 | } 30 | 31 | $toolClassName = $configurator->getToolClassName(); 32 | if (array_key_exists($toolClassName, $this->registeredConfigurators[$projectTypeKey])) { 33 | throw new LogicException( 34 | sprintf( 35 | 'Cannot register Configurator "%s" under ProjectType "%s"; ' . 36 | 'Configurator "%s" has already been registered for the same tool ("%s")', 37 | get_class($configurator), 38 | $projectType->getProjectType(), 39 | get_class($this->registeredConfigurators[$projectTypeKey][$toolClassName]), 40 | $toolClassName 41 | ) 42 | ); 43 | } 44 | 45 | $this->registeredConfigurators[$projectTypeKey][$toolClassName] = $configurator; 46 | } 47 | 48 | /** 49 | * @param Project $project 50 | * @return ConfiguratorList 51 | */ 52 | public function getConfiguratorsForProject(Project $project) 53 | { 54 | $configurators = new ConfiguratorList([]); 55 | 56 | foreach ($project->getProjectTypes() as $projectType) { 57 | if (isset($this->registeredConfigurators[$projectType->getProjectType()])) { 58 | $configurators = $configurators->appendList( 59 | new ConfiguratorList($this->registeredConfigurators[$projectType->getProjectType()]) 60 | ); 61 | } 62 | } 63 | 64 | return $configurators; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/Core/Exception/InvalidAnswerGivenException.php: -------------------------------------------------------------------------------- 1 | fileHandler = $fileHandler; 33 | $this->questionHelper = $questionHelper; 34 | $this->consoleQuestionFactory = $consoleQuestionFactory; 35 | } 36 | 37 | /** 38 | * @param InputInterface $input 39 | * @param OutputInterface $output 40 | * @return Interviewer 41 | */ 42 | public function createWith(InputInterface $input, OutputInterface $output) 43 | { 44 | return new Interviewer($input, $output, $this->questionHelper, $this->consoleQuestionFactory); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Core/IO/Cli/Validator/TextualAnswerValidator.php: -------------------------------------------------------------------------------- 1 | answers = $answers; 24 | } 25 | 26 | /** 27 | * @param TextualAnswer $other 28 | * @return bool 29 | */ 30 | public function contain(TextualAnswer $other) 31 | { 32 | /** @var TextualAnswer $answer */ 33 | foreach ($this->answers as $answer) { 34 | if ($answer->equals($other)) { 35 | return true; 36 | } 37 | } 38 | 39 | return false; 40 | } 41 | 42 | /** 43 | * @param Answer $other 44 | * @return bool 45 | */ 46 | public function equals(Answer $other) 47 | { 48 | if (!$this instanceof self || count($other) !== count($this)) { 49 | return false; 50 | } 51 | 52 | foreach ($other as $otherAnswer) { 53 | if (!$this->contain($otherAnswer)) { 54 | return false; 55 | } 56 | } 57 | 58 | return true; 59 | } 60 | 61 | public function getRaw() 62 | { 63 | return array_map( 64 | function (TextualAnswer $answer) { 65 | return $answer->getRaw(); 66 | }, 67 | $this->answers 68 | ); 69 | } 70 | 71 | public function getIterator() 72 | { 73 | return new ArrayIterator($this->answers); 74 | } 75 | 76 | public function count() 77 | { 78 | return count($this->answers); 79 | } 80 | 81 | public function convertToString() 82 | { 83 | return implode(', ', $this->convertToArrayOfStrings()); 84 | } 85 | 86 | /** 87 | * @return string[] 88 | */ 89 | public function convertToArrayOfStrings() 90 | { 91 | return array_map(function (TextualAnswer $answer) { 92 | return $answer->getAnswer(); 93 | }, $this->answers); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Core/Interviewer/Answer/NoDefaultAnswer.php: -------------------------------------------------------------------------------- 1 | answer = $answer; 18 | } 19 | 20 | /** 21 | * @param Answer $other 22 | * @return bool 23 | */ 24 | public function equals(Answer $other) 25 | { 26 | return $other instanceof self && $this->answer === $other->answer; 27 | } 28 | 29 | /** 30 | * @return string 31 | */ 32 | public function getAnswer() 33 | { 34 | return $this->answer; 35 | } 36 | 37 | public function getRaw() 38 | { 39 | return $this->answer; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function convertToString() 46 | { 47 | return $this->answer; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Core/Interviewer/Answer/YesOrNoAnswer.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | public static function yes() 21 | { 22 | return new self(self::YES); 23 | } 24 | 25 | /** 26 | * @return YesOrNoAnswer 27 | */ 28 | public static function no() 29 | { 30 | return new self(self::NO); 31 | } 32 | 33 | private function __construct($answer) 34 | { 35 | Assertion::boolean($answer); 36 | 37 | $this->answer = $answer; 38 | } 39 | 40 | /** 41 | * @param Answer $other 42 | * @return bool 43 | */ 44 | public function equals(Answer $other) 45 | { 46 | return $other instanceof self && $this->answer === $other->answer; 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getRaw() 53 | { 54 | return $this->answer; 55 | } 56 | 57 | /** 58 | * @param $yesOrNo 59 | * @return boolean 60 | */ 61 | public function is($yesOrNo) 62 | { 63 | Assertion::boolean($yesOrNo); 64 | 65 | return $this->answer === $yesOrNo; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Core/Interviewer/AutomatedResponseInterviewer.php: -------------------------------------------------------------------------------- 1 | recordedAnswers, [$partialQuestionText, $answer]); 27 | } 28 | 29 | /** 30 | * Records that the question's default answer ought to be given when a 31 | * question's text matches the partial question text. 32 | * 33 | * @param string $partialQuestionText 34 | * @return void 35 | */ 36 | public function respondWithDefaultAnswerTo($partialQuestionText) 37 | { 38 | $this->questionsToAnswerToWithDefault[] = $partialQuestionText; 39 | } 40 | 41 | public function ask(Question $question) 42 | { 43 | foreach ($this->recordedAnswers as list($partialQuestionText, $answer)) { 44 | if (strpos($question, $partialQuestionText) !== false) { 45 | return $answer; 46 | } 47 | } 48 | foreach ($this->questionsToAnswerToWithDefault as $partialQuestionText) { 49 | if (strpos($question, $partialQuestionText) !== false) { 50 | if (!$question->hasDefaultAnswer()) { 51 | throw new RuntimeException(sprintf('No default answer available for question "%s"', $question)); 52 | } 53 | return $question->getDefaultAnswer(); 54 | } 55 | } 56 | 57 | throw new RuntimeException(sprintf('No answer recorded for question "%s"', $question)); 58 | } 59 | 60 | public function notice($sentence) 61 | { 62 | } 63 | 64 | public function giveDetails($sentence) 65 | { 66 | } 67 | 68 | public function success($sentence) 69 | { 70 | } 71 | 72 | public function warn($sentence) 73 | { 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Core/Interviewer/Interviewer.php: -------------------------------------------------------------------------------- 1 | question = $question; 32 | $this->defaultAnswer = $defaultAnswer; 33 | } 34 | 35 | /** 36 | * @param TextualQuestion $other 37 | * @return bool 38 | */ 39 | public function equals(TextualQuestion $other) 40 | { 41 | return $this->question === $other->question && $this->defaultAnswer->equals($other->defaultAnswer); 42 | } 43 | 44 | public function hasDefaultAnswer() 45 | { 46 | return !$this->defaultAnswer instanceof NoDefaultAnswer; 47 | } 48 | 49 | public function getQuestion() 50 | { 51 | return $this->question; 52 | } 53 | 54 | public function getDefaultAnswer() 55 | { 56 | return $this->defaultAnswer; 57 | } 58 | 59 | public function withDefaultAnswer(Answer $answer) 60 | { 61 | return new TextualQuestion($this->question, $answer); 62 | } 63 | 64 | /** 65 | * @return string 66 | */ 67 | public function __toString() 68 | { 69 | return sprintf('%s(question="%s")', self::class, $this->question); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Core/Interviewer/Question/YesOrNoQuestion.php: -------------------------------------------------------------------------------- 1 | question = $question; 31 | $this->defaultAnswer = $defaultAnswer; 32 | } 33 | 34 | /** 35 | * @param YesOrNoQuestion $other 36 | * @return bool 37 | */ 38 | public function equals(YesOrNoQuestion $other) 39 | { 40 | return $this->question === $other->question && $this->defaultAnswer->equals($other->defaultAnswer); 41 | } 42 | 43 | public function hasDefaultAnswer() 44 | { 45 | return !$this->defaultAnswer instanceof NoDefaultAnswer; 46 | } 47 | 48 | public function withDefaultAnswer(Answer $answer) 49 | { 50 | return new YesOrNoQuestion($this->question, $answer); 51 | } 52 | 53 | public function getQuestion() 54 | { 55 | return $this->question; 56 | } 57 | 58 | public function getDefaultAnswer() 59 | { 60 | return $this->defaultAnswer; 61 | } 62 | 63 | /** 64 | * @return string 65 | */ 66 | public function __toString() 67 | { 68 | return sprintf('%s(question="%s")', self::class, $this->question); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Core/Interviewer/ScopedInterviewer.php: -------------------------------------------------------------------------------- 1 | directory = $directory; 31 | } 32 | 33 | /** 34 | * @param Directory $directory 35 | * @return bool 36 | */ 37 | public function equals(Directory $directory) 38 | { 39 | return $this->directory === $directory->directory; 40 | } 41 | 42 | /** 43 | * @return string 44 | */ 45 | public function getDirectory() 46 | { 47 | return str_replace('/', DIRECTORY_SEPARATOR, $this->directory); 48 | } 49 | 50 | public function __toString() 51 | { 52 | return sprintf('Directory("%s")', $this->directory); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Core/Project/ProjectTypeSet.php: -------------------------------------------------------------------------------- 1 | initializeWith($projectType); 23 | } 24 | } 25 | 26 | /** 27 | * @param ProjectType $projectType The project type to search for. 28 | * @return boolean TRUE if the collection contains the element, FALSE otherwise. 29 | */ 30 | public function contains(ProjectType $projectType) 31 | { 32 | foreach ($this->projectTypes as $existingProjectType) { 33 | if ($projectType->equals($existingProjectType)) { 34 | return true; 35 | } 36 | } 37 | 38 | return false; 39 | } 40 | 41 | /** 42 | * @param ProjectTypeSet $other 43 | * @return bool 44 | */ 45 | public function equals(ProjectTypeSet $other) 46 | { 47 | if (count($this->projectTypes) !== count($other->projectTypes)) { 48 | return false; 49 | } 50 | 51 | foreach ($this->projectTypes as $projectType) { 52 | if (!$other->contains($projectType)) { 53 | return false; 54 | } 55 | } 56 | 57 | return true; 58 | } 59 | 60 | /** 61 | * @return ProjectType[] 62 | */ 63 | public function asArray() 64 | { 65 | return $this->projectTypes; 66 | } 67 | 68 | public function getIterator() 69 | { 70 | return new ArrayIterator($this->projectTypes); 71 | } 72 | 73 | public function count() 74 | { 75 | return count($this->projectTypes); 76 | } 77 | 78 | /** 79 | * @param ProjectType $projectType 80 | */ 81 | private function initializeWith(ProjectType $projectType) 82 | { 83 | if ($this->contains($projectType)) { 84 | return; 85 | } 86 | 87 | $this->projectTypes[] = $projectType; 88 | } 89 | 90 | public function __toString() 91 | { 92 | return sprintf( 93 | 'ProjectTypeSet[%d](%s)', 94 | count($this->projectTypes), 95 | join(', ', array_map('strval', $this->projectTypes)) 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/Core/Resources/config/config.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | qa_tools.configuration_location: "./qa-tools.json" 3 | 4 | # Path relative to the application entrypoint (./bin/qa-tools) 5 | qa_tools.ant_template_location: "../src/Core/Resources/templates" 6 | 7 | qa_tools.ant.tool_priorities: ["phplint", "phpcs", "phpmd"] 8 | -------------------------------------------------------------------------------- /src/Core/Resources/config/task_executors.yml: -------------------------------------------------------------------------------- 1 | services: 2 | qa_tools.task.install_composer_dev_dependency_task_executor: 3 | class: Ibuildings\QaTools\Core\Task\Executor\InstallComposerDevDependencyTaskExecutor 4 | arguments: 5 | - "@qa_tools.composer.project_factory" 6 | tags: [{ name: qa_tools.task_executor, priority: 10 }] 7 | 8 | qa_tools.task.write_file_task_executor: 9 | class: Ibuildings\QaTools\Core\Task\Executor\WriteFileTaskExecutor 10 | arguments: 11 | - "@qa_tools.file_handler" 12 | tags: [{ name: qa_tools.task_executor, priority: 0 }] 13 | 14 | qa_tools.task.add_build_task_executor: 15 | class: Ibuildings\QaTools\Core\Task\Executor\AddBuildTaskExecutor 16 | arguments: 17 | - "@qa_tools.file_handler" 18 | - "@qa_tools.template_engine" 19 | - "%qa_tools.ant_template_location%" 20 | - "%qa_tools.ant.tool_priorities%" 21 | tags: [{ name: qa_tools.task_executor, priority: 5 }] 22 | -------------------------------------------------------------------------------- /src/Core/Resources/templates/build.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {% if precommit_snippets | length > 0 %} 9 | 10 | 11 | {{ precommit_snippets | join('\n') | raw }} 12 | {% endif %} 13 | 14 | {% if main_snippets | length > 0 %} 15 | 16 | 17 | {{ main_snippets | join('\n')| raw }} 18 | {% endif %} 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Core/Task/AddAntBuildTask.php: -------------------------------------------------------------------------------- 1 | tool = $tool; 33 | $this->snippet = $snippet; 34 | $this->build = $build; 35 | } 36 | 37 | /** 38 | * @param AddAntBuildTask $other 39 | * @param string[] $toolOrder 40 | * @return int 41 | */ 42 | public function compare(AddAntBuildTask $other, array $toolOrder) 43 | { 44 | return $this->tool->compare($other->tool, $toolOrder); 45 | } 46 | 47 | /** 48 | * @return string 49 | */ 50 | public function getSnippetContents() 51 | { 52 | return $this->snippet->getContents(); 53 | } 54 | 55 | /** 56 | * @return string 57 | */ 58 | public function getSnippetTargetIdentifier() 59 | { 60 | return $this->snippet->getTarget(); 61 | } 62 | 63 | /** 64 | * @param Build $build 65 | * @return bool 66 | */ 67 | public function hasTarget(Build $build) 68 | { 69 | return $this->build->equals($build); 70 | } 71 | 72 | /** 73 | * @param AddAntBuildTask $other 74 | * @return bool 75 | */ 76 | public function equals(AddAntBuildTask $other) 77 | { 78 | return $this->build->equals($other->build) 79 | && $this->tool->equals($other->tool) 80 | && $this->snippet->equals($other->snippet); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Core/Task/Executor/ArrayExecutorCollection.php: -------------------------------------------------------------------------------- 1 | executors = $executors; 26 | } 27 | 28 | public function findExecutorsWithAtLeastOneTaskToExecute(TaskDirectory $taskDirectory) 29 | { 30 | return new ArrayExecutorCollection( 31 | array_filter( 32 | $this->executors, 33 | function (Executor $executor) use ($taskDirectory) { 34 | return count($taskDirectory->filterTasks([$executor, 'supports'])) > 0; 35 | } 36 | ) 37 | ); 38 | } 39 | 40 | public function count() 41 | { 42 | return count($this->executors); 43 | } 44 | 45 | public function getIterator() 46 | { 47 | return new ArrayIterator($this->executors); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Core/Task/Executor/Executor.php: -------------------------------------------------------------------------------- 1 | packageName = $packageName; 27 | $this->packageVersionConstraint = $packageVersionConstraint; 28 | } 29 | 30 | /** 31 | * @return string 32 | */ 33 | public function getPackageName() 34 | { 35 | return $this->packageName; 36 | } 37 | 38 | /** 39 | * @return string 40 | */ 41 | public function getPackageVersionConstraint() 42 | { 43 | return $this->packageVersionConstraint; 44 | } 45 | 46 | public function __toString() 47 | { 48 | return sprintf( 49 | 'InstallComposerDevDependencyTask("%s:%s")', 50 | $this->packageName, 51 | $this->packageVersionConstraint 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Core/Task/Task.php: -------------------------------------------------------------------------------- 1 | filePath = $filePath; 37 | $this->fileContents = $fileContents; 38 | $this->mode = $mode; 39 | } 40 | 41 | /** 42 | * @return string 43 | */ 44 | public function getFilePath() 45 | { 46 | return $this->filePath; 47 | } 48 | 49 | /** 50 | * @return string 51 | */ 52 | public function getFileContents() 53 | { 54 | return $this->fileContents; 55 | } 56 | 57 | /** 58 | * @return int 59 | */ 60 | public function getMode() 61 | { 62 | return $this->mode; 63 | } 64 | 65 | public function __toString() 66 | { 67 | return sprintf( 68 | 'WriteFileTask(filePath="%s", fileContents="%s, mode="%o")', 69 | $this->filePath, 70 | substr($this->fileContents, 0, 20), 71 | $this->mode 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Core/Templating/Escape.php: -------------------------------------------------------------------------------- 1 | '"', 21 | "'" => ''', 22 | '<' => '<', 23 | '>' => '>', 24 | '&' => '&', 25 | ] 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Core/Templating/TemplateEngine.php: -------------------------------------------------------------------------------- 1 | twig = $twig; 20 | } 21 | 22 | /** 23 | * @param string $path A path relative to the application entrypoint `./bin/qa-tools`. 24 | */ 25 | public function setPath($path) 26 | { 27 | Assertion::string($path); 28 | 29 | $this->twig->setLoader(new Twig_Loader_Filesystem(Basedir::get() . '/'. $path)); 30 | } 31 | 32 | /** 33 | * @param string $template 34 | * @param array $params 35 | * @return string 36 | */ 37 | public function render($template, array $params) 38 | { 39 | Assertion::string($template); 40 | 41 | return $this->twig->render($template, $params); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Core/Templating/TwigFactory.php: -------------------------------------------------------------------------------- 1 | true, 17 | ]); 18 | 19 | /** @var CoreExtension $coreExtension */ 20 | $coreExtension = $twig->getExtension('core'); 21 | $coreExtension->setEscaper('xml', [Escape::class, 'xml']); 22 | 23 | return $twig; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Core/Tool/AbstractTool.php: -------------------------------------------------------------------------------- 1 | determineResourcePath(); 17 | 18 | $containerBuilder->setParameter('tool.' . static::class . '.resource_path', $resourcePath); 19 | $configurationFileLoader = new YamlFileLoader( 20 | $containerBuilder, 21 | new FileLocator(Basedir::get() . '/' . $resourcePath . '/config') 22 | ); 23 | 24 | foreach ($this->getConfigurationFiles() as $configurationFile) { 25 | $configurationFileLoader->load($configurationFile); 26 | } 27 | } 28 | 29 | /** 30 | * Returns the path to this tool's resources relative to the application 31 | * entrypoint `./bin/qa-tools`. 32 | * 33 | * @return string 34 | */ 35 | protected function determineResourcePath() 36 | { 37 | $toolReflection = new ReflectionClass($this); 38 | $toolFilePath = $toolReflection->getFileName(); 39 | $absoluteResourcesPath = dirname($toolFilePath) . '/Resources'; 40 | 41 | // dirname() is used to traverse up several directories. `../` directories 42 | // are not supported by Filesystem::makePathRelative(). 43 | $projectDirectory = dirname(dirname(dirname(__DIR__))); 44 | 45 | $fs = new Filesystem(); 46 | $relativeResourcesPath = $fs->makePathRelative( 47 | $absoluteResourcesPath, 48 | $projectDirectory . '/bin' 49 | ); 50 | 51 | return $relativeResourcesPath; 52 | } 53 | 54 | /** 55 | * @return string[] 56 | */ 57 | protected function getConfigurationFiles() 58 | { 59 | return [ 60 | 'configurators.yml' 61 | ]; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Core/Tool/Tool.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Tool/Behat/Resources/templates/behat.yml: -------------------------------------------------------------------------------- 1 | default: 2 | suites: 3 | default: 4 | contexts: 5 | - FeatureContext 6 | -------------------------------------------------------------------------------- /src/Tool/Behat/Resources/templates/drupal/FeatureContext.php.twig: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Tool/PhpCs/Resources/templates/ruleset-reference.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | Project ruleset 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Tool/PhpCs/Resources/templates/ruleset.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | Enforce coding standards 4 | 5 | 6 | 7 | {% if shouldIgnoreSomeLocationsCompletely %} 8 | 9 | {{ ignoredLocation }} 10 | {% endif %} 11 | 12 | {% if useCustomizedLineLengthSettings %} 13 | {{ include('snippet-line-length.xml.twig') }} 14 | {% endif %} 15 | 16 | {% if beLessStrictAboutDocblocksInTests %} 17 | {{ include('snippet-ignore-function-comments-in-tests.xml.twig') }} 18 | {% endif %} 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Tool/PhpCs/Resources/templates/snippet-line-length.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Tool/PhpMd/Configurator/PhpMdConfigurator.php: -------------------------------------------------------------------------------- 1 | ask( 28 | QuestionFactory::createYesOrNo('Would you like to use PHP Mess Detector?', YesOrNoAnswer::YES) 29 | ); 30 | if ($usePhpMd->is(YesOrNoAnswer::NO)) { 31 | return; //do nothing 32 | } 33 | 34 | $taskDirectory->registerTask(new InstallComposerDevDependencyTask('phpmd/phpmd', '^2.0')); 35 | 36 | $project = $taskDirectory->getProject(); 37 | $configurationFilesLocation = $project->getConfigurationFilesLocation(); 38 | 39 | $phpMdConfiguration = $taskHelperSet->renderTemplate('phpmd-default.xml.twig', ['project' => $project]); 40 | $taskDirectory->registerTask( 41 | new WriteFileTask($configurationFilesLocation->getDirectory() . 'phpmd.xml', $phpMdConfiguration) 42 | ); 43 | 44 | $antBuildSnippet = $taskHelperSet->renderTemplate( 45 | 'ant-build.xml.twig', 46 | ['targetName' => PhpMd::ANT_TARGET, 'suffixes' => ['php']] 47 | ); 48 | $taskDirectory->registerTask( 49 | new AddAntBuildTask( 50 | Build::main(), 51 | Tool::withIdentifier('phpmd'), 52 | Snippet::withContentsAndTargetName($antBuildSnippet, PhpMd::ANT_TARGET) 53 | ) 54 | ); 55 | } 56 | 57 | public function getToolClassName() 58 | { 59 | return PhpMd::class; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Tool/PhpMd/PhpMd.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/Tool/PhpMd/Resources/templates/phpmd-default.xml.twig: -------------------------------------------------------------------------------- 1 | {# @var project \Ibuildings\QaTools\Core\Project\Project #} 2 | 3 | 10 | 11 | Ibuildings QA Tools Default Ruleset for {{ project.name|escape('xml') }} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/Tool/PhpParallelLint/PhpParallelLint.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/Tool/PhpParallelLint/Resources/templates/ant-full.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/Tool/PhpUnit/PhpUnit.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Tool/PhpUnit/Resources/templates/drupal8/bootstrap.php.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | tests 17 | 18 | 19 | 20 | 21 | 22 | src 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Tool/PhpUnit/Resources/templates/phpunit.default.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | tests 16 | 17 | 18 | 19 | 20 | 21 | src 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/Tool/PhpUnit/Resources/templates/phpunit.symfony.xml.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | tests 18 | 19 | 20 | 21 | 22 | 23 | src 24 | 25 | src/*Bundle/Resources 26 | src/*/*Bundle/Resources 27 | src/*/Bundle/*Bundle/Resources 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Tool/SensioLabsSecurityChecker/Configurator/SecurityCheckerConfigurator.php: -------------------------------------------------------------------------------- 1 | ask( 26 | QuestionFactory::createYesOrNo( 27 | 'Would you like to check for vulnerable dependencies using SensioLabs Security Checker?', 28 | YesOrNoAnswer::YES 29 | ) 30 | ); 31 | 32 | /** @var YesOrNoAnswer $useSecurityChecker */ 33 | if ($useSecurityChecker->is(false)) { 34 | return; 35 | } 36 | 37 | $taskDirectory->registerTask(new InstallComposerDevDependencyTask('sensiolabs/security-checker', '^3.0')); 38 | 39 | $antSnippet = $taskHelperSet->renderTemplate( 40 | 'ant-build.xml.twig', 41 | ['targetName' => SensioLabsSecurityChecker::ANT_TARGET] 42 | ); 43 | 44 | $taskDirectory->registerTask( 45 | new AddAntBuildTask( 46 | Build::main(), 47 | Tool::withIdentifier(SensioLabsSecurityChecker::ANT_TARGET), 48 | Snippet::withContentsAndTargetName($antSnippet, SensioLabsSecurityChecker::ANT_TARGET) 49 | ) 50 | ); 51 | } 52 | 53 | public function getToolClassName() 54 | { 55 | return SensioLabsSecurityChecker::class; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Tool/SensioLabsSecurityChecker/Resources/config/configurators.yml: -------------------------------------------------------------------------------- 1 | services: 2 | qa_tools.tool.configurator.security_checker: 3 | class: Ibuildings\QaTools\Tool\SensioLabsSecurityChecker\Configurator\SecurityCheckerConfigurator 4 | tags: 5 | - name: qa_tools.tool 6 | project_type: 'php.sf2' 7 | - name: qa_tools.tool 8 | project_type: 'php.sf3' 9 | - name: qa_tools.tool 10 | project_type: 'php.drupal7' 11 | - name: qa_tools.tool 12 | project_type: 'php.drupal8' 13 | - name: qa_tools.tool 14 | project_type: 'php.other' 15 | -------------------------------------------------------------------------------- /src/Tool/SensioLabsSecurityChecker/Resources/templates/ant-build.xml.twig: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/Tool/SensioLabsSecurityChecker/SensioLabsSecurityChecker.php: -------------------------------------------------------------------------------- 1 | container = $container; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/integration/Core/Templating/TemplateEngineTest.php: -------------------------------------------------------------------------------- 1 | container->get('twig.environment'); 18 | 19 | $engine = new TemplateEngine($twig); 20 | $engine->setPath('../tests/integration/Core/Templating/templates'); 21 | 22 | $this->expectException(Twig_Error_Runtime::class); 23 | $this->expectExceptionMessage('Variable "variableDoesNotExist" does not exist in '); 24 | $engine->render('non-existent-variable.twig', []); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/integration/Core/Templating/templates/non-existent-variable.twig: -------------------------------------------------------------------------------- 1 | This {{ variableDoesNotExist }}. 2 | -------------------------------------------------------------------------------- /tests/integration/Core/Tool/AbstractToolTest.php: -------------------------------------------------------------------------------- 1 | build($containerBuilder); 21 | 22 | $resourcePath = $containerBuilder->getParameter('tool.' . Hammer::class . '.resource_path'); 23 | $this->assertStringStartsNotWith( 24 | '/', 25 | $resourcePath, 26 | 'Resource path is an Unix-style absolute path. It must be a relative path.' 27 | ); 28 | $this->assertLessThanOrEqual( 29 | 1, 30 | substr_count($resourcePath, '..'), 31 | 'Resource path should not traverse up out of the QA Tools project directory structure' 32 | ); 33 | $this->assertFileExists(__DIR__ . '/../../../../bin/' . $resourcePath); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/integration/Core/Tool/Hammer/Hammer.php: -------------------------------------------------------------------------------- 1 | 1) { 24 | printf("\e[31;31mFound %d SHA384 hashes in README.md, expected only 1\e[0m\n", count($regexResults[1])); 25 | exit(1); 26 | } 27 | 28 | if ($shaOfInstaller !== $regexResults[1][0]) { 29 | echo <<120. Fail when >150" 21 | answer "Would you like to skip any sniffs regarding the doc blocks in tests?" with "n" 22 | answer "Would you like PHPCS to ignore some locations completely? (you may use a regex to match multiple directories)" with "n" 23 | 24 | answer "Would you like to check for vulnerable dependencies using SensioLabs Security Checker?" with "n" 25 | answer "Would you like to install Behat?" with "n" 26 | 27 | give_tasks_time_to_run 28 | 29 | exits_with 0 30 | -------------------------------------------------------------------------------- /tests/system/specs/005_can-configure-a-drupal8-project.php: -------------------------------------------------------------------------------- 1 | /)" with "qa-tools/test-package" 21 | accept_default_for "Description \[\]:" 22 | answer "Author \[" with "n" 23 | accept_default_for "Minimum Stability \[\]:" 24 | accept_default_for "Package Type" 25 | accept_default_for "License \[\]:" 26 | answer "Would you like to define your dependencies (require) interactively" with "no" 27 | answer "Would you like to define your dev dependencies (require-dev) interactively" with "no" 28 | accept_default_for "Do you confirm generation" 29 | 30 | give_tasks_time_to_run 31 | 32 | exits_with 0 33 | -------------------------------------------------------------------------------- /tests/system/specs/050_can-install-git-pre-commit-hook.php: -------------------------------------------------------------------------------- 1 | equals($other); 27 | } 28 | return false; 29 | } 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/unit/Core/Assert/AssertionTest.php: -------------------------------------------------------------------------------- 1 | expectException(InvalidArgumentException::class); 32 | 33 | Assertion::nonEmptyString($value, 'Expected non-empty string for "%3$s", got "%s" of type "%s"', 'value'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/unit/Core/Composer/PackageVersionConstraintTest.php: -------------------------------------------------------------------------------- 1 | assertTrue($a->equals($b), $this->diff($a, $b, 'Package version constraints ought to be equal')); 27 | } 28 | 29 | public function equalConstraints() 30 | { 31 | return [ 32 | '^3.1.1 == ^3.1.1' => ['^3.1.1', '^3.1.1',], 33 | '^3.1 == ^3.1.0' => ['^3.1', '^3.1.0',], 34 | '1.0 == 1.0' => ['1.0', '1.0',], 35 | '1.0 == 1.0.0' => ['1.0', '1.0.0',], 36 | 'dev-master == dev-master' => ['dev-master', 'dev-master',], 37 | ]; 38 | } 39 | 40 | /** 41 | * @test 42 | * @dataProvider unequalConstraints 43 | * @param string $stringA 44 | * @param string $stringB 45 | */ 46 | public function constraints_can_not_be_equal($stringA, $stringB) 47 | { 48 | $a = PackageVersionConstraint::parse($stringA); 49 | $b = PackageVersionConstraint::parse($stringB); 50 | $this->assertFalse($a->equals($b), $this->diff($a, $b, 'Package version constraints ought not to be equal')); 51 | } 52 | 53 | public function unequalConstraints() 54 | { 55 | return [ 56 | '^3.1.1 != ~3.1.1' => ['^3.1.1', '~3.1.1',], 57 | '1 != 2' => ['1', '2',], 58 | 'dev-master != dev-develop' => ['dev-master', 'dev-develop',], 59 | '* != 1.0' => ['*', '1.0',], 60 | ]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/unit/Core/Configuration/ConfiguratorRepositoryTest.php: -------------------------------------------------------------------------------- 1 | add(new FakeConfigurator(FooTool::class), new ProjectType('php.sf2')); 20 | } 21 | 22 | /** @test */ 23 | public function two_configurators_for_two_different_tools_can_be_registered_under_the_same_project_type() 24 | { 25 | $registry = new ConfiguratorRepository(); 26 | $registry->add(new FakeConfigurator(FooTool::class), new ProjectType('php.drupal8')); 27 | $registry->add(new FakeConfigurator(BarTool::class), new ProjectType('php.drupal8')); 28 | } 29 | 30 | /** @test */ 31 | public function the_same_configurator_can_be_registered_under_two_different_project_types() 32 | { 33 | $registry = new ConfiguratorRepository(); 34 | 35 | $configurator = new FakeConfigurator(FooTool::class); 36 | $registry->add($configurator, new ProjectType('php.other')); 37 | $registry->add($configurator, new ProjectType('php.drupal7')); 38 | } 39 | 40 | /** @test */ 41 | public function two_configurators_for_the_same_tool_can_not_be_registered_under_the_same_project_type() 42 | { 43 | $this->expectException(LogicException::class); 44 | $this->expectExceptionMessage('same tool'); 45 | 46 | $registry = new ConfiguratorRepository(); 47 | $registry->add(new FakeConfigurator(FooTool::class), new ProjectType('js.angular1')); 48 | $registry->add(new FakeConfigurator(FooTool::class), new ProjectType('js.angular1')); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/unit/Core/Configuration/FakeConfigurator.php: -------------------------------------------------------------------------------- 1 | toolClassName = $toolClassName; 17 | } 18 | 19 | public function configure(Interviewer $interviewer, TaskDirectory $taskDirectory, TaskHelperSet $taskHelperSet) 20 | { 21 | } 22 | 23 | public function getToolClassName() 24 | { 25 | return $this->toolClassName; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/unit/Core/Configuration/InMemoryConfigurationRepositoryTest.php: -------------------------------------------------------------------------------- 1 | assertFalse($repository->configurationExists(), 'Repository should not have configuration in memory'); 20 | } 21 | 22 | /** @test */ 23 | public function stores_configuration() 24 | { 25 | $configuration = Configuration::create(); 26 | 27 | $repository = new InMemoryConfigurationRepository(); 28 | $repository->save($configuration); 29 | 30 | $this->assertTrue($repository->configurationExists(), 'Repository should have configuration in memory'); 31 | $this->assertSame( 32 | $configuration, 33 | $repository->load(), 34 | 'Repository should have the same configuration in memory' 35 | ); 36 | } 37 | 38 | /** @test */ 39 | public function throws_an_exception_when_attempting_to_load_nonexistent_configuration() 40 | { 41 | $repository = new InMemoryConfigurationRepository(); 42 | 43 | $this->expectException(RuntimeException::class); 44 | $this->expectExceptionMessage('No configuration stored in memory'); 45 | $repository->load(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/unit/Core/Configuration/InMemoryTaskDirectoryTest.php: -------------------------------------------------------------------------------- 1 | assertCount( 28 | 0, 29 | $taskDirectory->getTasks(), 30 | 'Task directory ought to contain no registered tasks' 31 | ); 32 | } 33 | 34 | /** 35 | * @test 36 | */ 37 | public function the_project_given_during_instantiation_can_be_retrieved_from_the_task_directory() 38 | { 39 | $dummyProject = Mockery::mock(Project::class); 40 | 41 | $taskDirectory = new InMemoryTaskDirectory($dummyProject); 42 | 43 | $retrievedProject = $taskDirectory->getProject(); 44 | 45 | $this->assertEquals($dummyProject, $retrievedProject); 46 | } 47 | 48 | /** 49 | * @test 50 | */ 51 | public function a_task_can_be_registered() 52 | { 53 | $dummyProject = Mockery::mock(Project::class); 54 | 55 | $fakeTask = new NoopTask('Some task'); 56 | 57 | $taskDirectory = new InMemoryTaskDirectory($dummyProject); 58 | $taskDirectory->registerTask($fakeTask); 59 | 60 | $this->assertTrue( 61 | $taskDirectory->getTasks()->equals(new TaskList([$fakeTask])), 62 | 'Task directory ought to contain the registered task' 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/unit/Core/Configuration/TaskHelperSetTest.php: -------------------------------------------------------------------------------- 1 | expectException(InvalidArgumentException::class); 21 | 22 | $dummyTemplateEngine = Mockery::mock(TemplateEngine::class); 23 | 24 | $taskHelperSet = new TaskHelperSet($dummyTemplateEngine); 25 | $taskHelperSet->setTemplatePath($path); 26 | } 27 | 28 | /** 29 | * @test 30 | */ 31 | public function a_template_path_can_be_set() 32 | { 33 | $dummyTemplateEngine = Mockery::mock(TemplateEngine::class); 34 | 35 | $path = 'some/file/path'; 36 | 37 | $dummyTemplateEngine 38 | ->shouldReceive('setPath') 39 | ->with($path); 40 | 41 | $taskHelperSet = new TaskHelperSet($dummyTemplateEngine); 42 | $taskHelperSet->setTemplatePath($path); 43 | } 44 | 45 | /** 46 | * @test 47 | * 48 | * @dataProvider \Ibuildings\QaTools\UnitTest\TestDataProvider::notStringOrEmptyString 49 | */ 50 | public function a_template_that_can_be_rendered_can_only_be_a_string($template) 51 | { 52 | $this->expectException(InvalidArgumentException::class); 53 | 54 | $dummyTemplateEngine = Mockery::mock(TemplateEngine::class); 55 | 56 | $taskHelperSet = new TaskHelperSet($dummyTemplateEngine); 57 | $taskHelperSet->renderTemplate($template); 58 | } 59 | 60 | /** 61 | * @test 62 | */ 63 | public function a_template_is_rendered() 64 | { 65 | $dummyTemplateEngine = Mockery::mock(TemplateEngine::class); 66 | 67 | $template = 'some/file/path'; 68 | 69 | $dummyTemplateEngine 70 | ->shouldReceive('render') 71 | ->with($template, []); 72 | 73 | $taskHelperSet = new TaskHelperSet($dummyTemplateEngine); 74 | $taskHelperSet->renderTemplate($template); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /tests/unit/Core/IO/Cli/InterviewerTest.php: -------------------------------------------------------------------------------- 1 | expectException(InvalidArgumentException::class); 28 | 29 | $interviewer = new Interviewer( 30 | new ArgvInput, 31 | new DummyOutput, 32 | new QuestionHelper, 33 | new ConsoleQuestionFactory(new ConsoleQuestionFormatter) 34 | ); 35 | 36 | $interviewer->notice($nonString); 37 | } 38 | 39 | /** 40 | * @test 41 | * 42 | * @dataProvider \Ibuildings\QaTools\UnitTest\TestDataProvider::notString 43 | */ 44 | public function interviewer_can_only_warn_with_sentences_that_are_strings($nonString) 45 | { 46 | $this->expectException(InvalidArgumentException::class); 47 | 48 | $interviewer = new Interviewer( 49 | new ArgvInput, 50 | new DummyOutput, 51 | new QuestionHelper, 52 | new ConsoleQuestionFactory(new ConsoleQuestionFormatter) 53 | ); 54 | 55 | $interviewer->warn($nonString); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/unit/Core/IO/Cli/Validator/TextualAnswerValidatorTest.php: -------------------------------------------------------------------------------- 1 | expectException(InvalidArgumentException::class); 31 | 32 | TextualAnswerValidator::validate($answer); 33 | } 34 | 35 | /** 36 | * @test 37 | * 38 | * @dataProvider \Ibuildings\QaTools\UnitTest\TestDataProvider::emptyString 39 | */ 40 | public function validator_sees_empty_or_emptyish_strings_as_invalid_answers($answer) 41 | { 42 | $this->expectException(InvalidAnswerGivenException::class); 43 | 44 | TextualAnswerValidator::validate($answer); 45 | } 46 | 47 | /** 48 | * @test 49 | */ 50 | public function validator_sees_null_as_invalid_answers() 51 | { 52 | $this->expectException(InvalidAnswerGivenException::class); 53 | 54 | TextualAnswerValidator::validate(null); 55 | } 56 | 57 | /** 58 | * @test 59 | */ 60 | public function validator_passes_through_answers_that_are_non_empty_strings() 61 | { 62 | $answer = 'An answer'; 63 | 64 | $validatedAnswer = TextualAnswerValidator::validate($answer); 65 | 66 | $this->assertEquals($answer, $validatedAnswer); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/unit/Core/Interviewer/Answer/AnswerFactoryTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($expectedAnswer, $actualAnswer); 29 | } 30 | 31 | /** 32 | * @test 33 | */ 34 | public function factory_creates_a_negative_yes_or_no_answer() 35 | { 36 | $expectedAnswer = YesOrNoAnswer::no(); 37 | $actualAnswer = AnswerFactory::createFrom(false); 38 | 39 | $this->assertEquals($expectedAnswer, $actualAnswer); 40 | } 41 | 42 | /** 43 | * @test 44 | */ 45 | public function factory_creates_a_textual_answer() 46 | { 47 | $answerText = 'Test answer.'; 48 | $expectedAnswer = new TextualAnswer($answerText); 49 | $actualAnswer = AnswerFactory::createFrom($answerText); 50 | 51 | $this->assertEquals($expectedAnswer, $actualAnswer); 52 | } 53 | 54 | /** 55 | * @test 56 | */ 57 | public function factory_creates_choices() 58 | { 59 | $answerTexts = ['Test answer.', 'Another test answer']; 60 | $expectedAnswer = new Choices( 61 | [ 62 | new TextualAnswer($answerTexts[0]), 63 | new TextualAnswer($answerTexts[1]), 64 | ] 65 | ); 66 | 67 | $actualAnswer = AnswerFactory::createFrom($answerTexts); 68 | 69 | $this->assertEquals($expectedAnswer, $actualAnswer); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/unit/Core/Interviewer/Answer/NoDefaultAnswerTest.php: -------------------------------------------------------------------------------- 1 | assertNull($noDefaultAnswer->getRaw()); 24 | } 25 | 26 | /** 27 | * @test 28 | */ 29 | public function no_default_answer_does_not_equal_answer_of_other_type() 30 | { 31 | $noDefaultAnswer = new NoDefaultAnswer(); 32 | $otherAnswer = new TextualAnswer('Test'); 33 | 34 | $this->assertFalse($noDefaultAnswer->equals($otherAnswer)); 35 | } 36 | 37 | /** 38 | * @test 39 | */ 40 | public function no_default_answer_equals_another_missing_answer() 41 | { 42 | $noDefaultAnswer = new NoDefaultAnswer(); 43 | $sameAnswer = new NoDefaultAnswer(); 44 | 45 | $this->assertTrue($noDefaultAnswer->equals($sameAnswer)); 46 | } 47 | 48 | /** 49 | * @test 50 | */ 51 | public function no_default_answer_is_converted_to_an_empty_string() 52 | { 53 | $noDefaultAnswer = new NoDefaultAnswer(); 54 | 55 | $this->assertEquals('', $noDefaultAnswer->convertToString()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/unit/Core/Interviewer/Answer/TextualAnswerTest.php: -------------------------------------------------------------------------------- 1 | expectException(InvalidArgumentException::class); 24 | 25 | new TextualAnswer($value); 26 | } 27 | 28 | /** 29 | * @test 30 | */ 31 | public function textual_answer_does_not_equal_another_answer() 32 | { 33 | $answer = new TextualAnswer('The answer.'); 34 | $otherAnswer = new TextualAnswer('Another answer.'); 35 | 36 | $this->assertFalse($answer->equals($otherAnswer)); 37 | } 38 | 39 | /** 40 | * @test 41 | */ 42 | public function textual_answer_equals_another_answer() 43 | { 44 | $answer = new TextualAnswer('The answer.'); 45 | $otherAnswer = new TextualAnswer('The answer.'); 46 | 47 | $this->assertTrue($answer->equals($otherAnswer)); 48 | } 49 | 50 | /** 51 | * @test 52 | */ 53 | public function textual_answer_has_an_textual_answer_value() 54 | { 55 | $expectedValue = 'An answer.'; 56 | $answer = new TextualAnswer($expectedValue); 57 | 58 | $actualValue = $answer->getRaw(); 59 | 60 | $this->assertEquals($actualValue, $expectedValue); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/unit/Core/Interviewer/Answer/YesOrNoAnswerTest.php: -------------------------------------------------------------------------------- 1 | assertFalse($answer->equals($differentAnswer)); 24 | } 25 | 26 | /** 27 | * @test 28 | */ 29 | public function yes_or_no_answer_equals_the_same_yes_or_no_answer() 30 | { 31 | $answer = YesOrNoAnswer::yes(); 32 | $sameAnswer = YesOrNoAnswer::yes(); 33 | 34 | $this->assertTrue($answer->equals($sameAnswer)); 35 | } 36 | 37 | /** 38 | * @test 39 | */ 40 | public function yes_or_no_answer_has_an_answer_value() 41 | { 42 | $answer = YesOrNoAnswer::yes(); 43 | 44 | $actualValue = $answer->getRaw(); 45 | 46 | $this->assertTrue($actualValue); 47 | } 48 | 49 | /** 50 | * @test 51 | */ 52 | public function yes_or_no_answer_created_as_yes_is_yes() 53 | { 54 | $answer = YesOrNoAnswer::yes(); 55 | 56 | $this->assertTrue($answer->is(YesOrNoAnswer::YES)); 57 | } 58 | 59 | /** 60 | * @test 61 | */ 62 | public function yes_or_no_answer_created_as_no_is_no() 63 | { 64 | $answer = YesOrNoAnswer::no(); 65 | 66 | $this->assertTrue($answer->is(YesOrNoAnswer::NO)); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/unit/Core/Project/DirectoryTest.php: -------------------------------------------------------------------------------- 1 | assertSame($directoryString, $directory->getDirectory()); 24 | } 25 | 26 | /** 27 | * @test 28 | * @dataProvider equalDirectories 29 | * @param string $a 30 | * @param string $b 31 | */ 32 | public function can_equal_another_directory($a, $b) 33 | { 34 | $dirA = new Directory($a); 35 | $dirB = new Directory($b); 36 | 37 | $this->assertTrue( 38 | $dirA->equals($dirB), 39 | $this->diff($a, $b, 'Directories were expected to equal') 40 | ); 41 | } 42 | 43 | public function equalDirectories() 44 | { 45 | return [ 46 | '/abc == /abc' => ['/abc', '/abc'], 47 | './ == ./' => ['./', './'], 48 | 'vendor/. == vendor' => ['vendor/.', 'vendor'], 49 | './vendor == vendor' => ['./vendor', 'vendor'], 50 | 'rel\\ative == rel/ative' => ['rel\\ative', 'rel/ative'], 51 | './vendor/./psr == vendor/psr' => ['./vendor/./psr', 'vendor/psr'], 52 | '/./abc == /abc/./' => ['/./abc', '/abc/./'], 53 | '/./abc/.. == /abc/./..' => ['/./abc/..', '/abc/./../'], 54 | ]; 55 | } 56 | 57 | /** @test */ 58 | public function can_not_equal_another_directory() 59 | { 60 | $this->assertFalse((new Directory(' ./'))->equals(new Directory(' ./vendor'))); 61 | $this->assertFalse((new Directory(' ../'))->equals(new Directory(' ./'))); 62 | } 63 | 64 | /** @test */ 65 | public function a_relative_directory_can_be_subtracted() 66 | { 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/unit/Core/Project/ProjectTypeTest.php: -------------------------------------------------------------------------------- 1 | assertTrue( 18 | (new ProjectType(ProjectType::TYPE_PHP_DRUPAL_8))->equals(new ProjectType(ProjectType::TYPE_PHP_DRUPAL_8)), 19 | 'Two equal project types should be equal' 20 | ); 21 | } 22 | 23 | /** @test */ 24 | public function must_be_one_of_the_recognised_types() 25 | { 26 | $this->expectException(InvalidArgumentException::class); 27 | $this->expectExceptionMessage('is not an element of the valid values'); 28 | 29 | new ProjectType('ruby.ror'); 30 | } 31 | 32 | /** @test */ 33 | public function can_be_created_from_a_human_readable_string() 34 | { 35 | $this->assertTrue( 36 | ProjectType::fromHumanReadableString('Symfony 3')->equals(new ProjectType(ProjectType::TYPE_PHP_SF_3)), 37 | 'Project type created from human readable string ought to equal project type created from constant' 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/unit/Core/Task/NoopTask.php: -------------------------------------------------------------------------------- 1 | assertTrue($a->equals($b), $this->diff($a, $b, 'A ought to equal B')); 19 | * 20 | * @param mixed $expected 21 | * @param mixed $actual 22 | * @param string $title 23 | * @return string 24 | */ 25 | protected function diff($expected, $actual, $title) 26 | { 27 | Assertion::string($title, 'Diff title ought to be a string, got "%s" of type "%s"'); 28 | 29 | $differ = new Differ(); 30 | $exporter = new Exporter(); 31 | 32 | $diff = $differ->diff($exporter->export($expected), $exporter->export($actual)); 33 | 34 | return sprintf("%s\n\n%s\n", $title, $diff); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/unit/InstallComposerDevDependencyTaskMatcher.php: -------------------------------------------------------------------------------- 1 | getPackageName() === $expected; 21 | } 22 | ); 23 | } 24 | 25 | /** 26 | * @param string $expectedPackage 27 | * @param string $expectedVersion 28 | * @return Mockery\Matcher\Closure 29 | */ 30 | public static function forVersionOf($expectedPackage, $expectedVersion) 31 | { 32 | return Mockery::on( 33 | function (Task $task) use ($expectedPackage, $expectedVersion) { 34 | return $task instanceof InstallComposerDevDependencyTask 35 | && $task->getPackageName() === $expectedPackage 36 | && $task->getPackageVersionConstraint() == $expectedVersion; 37 | } 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/unit/PharUpdater/Strategy/no-releases.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /tests/unit/ValueObject.php: -------------------------------------------------------------------------------- 1 | equals($expected)) { 18 | Assert::assertEquals($expected, $actual, "Value object don't equal each other"); 19 | } 20 | 21 | return true; 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/unit/WriteFileTaskMatcher.php: -------------------------------------------------------------------------------- 1 | getFilePath() === $expectedPath 22 | && $task->getFileContents() === $expectedContent; 23 | } 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tools/build-phar.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | &1', $installOutput)) { 6 | error("Something went wrong while installing the production dependencies:"); 7 | debug($installOutput); 8 | exit(1); 9 | } 10 | 11 | // Increase open file limit 12 | // See https://github.com/box-project/box2/issues/80#issuecomment-76630852 13 | execute('command -v ulimit 2>/dev/null', $ulimit); 14 | if ($ulimit) { 15 | info("Building distributable with increased file descriptor limit..."); 16 | $buildSucceeded = execute("$ulimit -Sn 4096 && composer build --ansi 2>&1", $buildOutput); 17 | } else { 18 | $buildSucceeded = execute("composer build --ansi 2>&1", $buildOutput); 19 | } 20 | 21 | if (!$buildSucceeded) { 22 | error("Something went wrong while building the distributable:"); 23 | debug($buildOutput); 24 | } 25 | 26 | info("Restoring development dependencies..."); 27 | if (!execute("composer install --ansi 2>&1", $installOutput)) { 28 | error("Something went wrong while restoring the development dependencies:"); 29 | debug($installOutput); 30 | } 31 | 32 | exit($buildSucceeded ? 0 : 1); 33 | 34 | function debug($message) { 35 | fprintf(STDERR, "%s\n", $message); 36 | } 37 | function info($message) { 38 | fprintf(STDERR, "%s%s%s\n", "\033[33m", $message, "\033[0m"); 39 | } 40 | function error($message) { 41 | fprintf(STDERR, "%s%s%s\n", "\033[41m", $message, "\033[0m"); 42 | } 43 | 44 | /** 45 | * @param string $command 46 | * @param string &$stdout 47 | * @return bool Whether the process exited with exit code 0 48 | */ 49 | function execute($command, &$stdout) { 50 | exec($command, $output, $exitCode); 51 | $stdout = join("\n", $output); 52 | 53 | return $exitCode === 0; 54 | } 55 | -------------------------------------------------------------------------------- /tools/git/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Absolute path to this script, e.g. /path/to/pre-commit.bash 4 | SCRIPT=$(readlink -f "$0") 5 | # Absolute path this script is in, thus /path/to 6 | SCRIPTPATH=$(dirname "$SCRIPT") 7 | 8 | (cd $SCRIPTPATH/../.. && make test-fast) 9 | -------------------------------------------------------------------------------- /tools/git/pre-commit-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Absolute path to this script, e.g. /path/to/pre-commit-install 4 | SCRIPT=$(readlink -f "$0") 5 | # Absolute path this script is in, thus /path/to 6 | SCRIPTPATH=$(dirname "$SCRIPT") 7 | 8 | ln -is $SCRIPTPATH/pre-commit $SCRIPTPATH/../../.git/hooks/pre-commit 9 | echo "Pre-commit hook installed!" 10 | -------------------------------------------------------------------------------- /tools/git/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Absolute path to this script, e.g. /path/to/pre-push 4 | SCRIPT=$(readlink -f "$0") 5 | # Absolute path this script is in, thus /path/to 6 | SCRIPTPATH=$(dirname "$SCRIPT") 7 | 8 | (cd $SCRIPTPATH/../.. && make test) 9 | -------------------------------------------------------------------------------- /tools/git/pre-push-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Absolute path to this script, e.g. /path/to/pre-push-install 4 | SCRIPT=$(readlink -f "$0") 5 | # Absolute path this script is in, thus /path/to 6 | SCRIPTPATH=$(dirname "$SCRIPT") 7 | 8 | ln -is $SCRIPTPATH/pre-push $SCRIPTPATH/../../.git/hooks/pre-push 9 | echo "Pre-push hook installed!" 10 | -------------------------------------------------------------------------------- /var/cache/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibuildingsnl/qa-tools/0c4999a74c55b03a826e1c881f75ee3fba6329f7/var/cache/.gitkeep -------------------------------------------------------------------------------- /var/composer/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | --------------------------------------------------------------------------------