diff --git a/.env.dist b/.env.dist index 8cc743743..c75214fd4 100644 --- a/.env.dist +++ b/.env.dist @@ -4,3 +4,9 @@ SLACK_HOOK=hook_to_be_changed DEPLOY_SERVER=server_name_for_deploy DEPLOY_USER=ssh_login_for_deploy + +GITHUB_GROUP=github_organistion +GITHUB_USER=github_user +GITHUB_EMAIL=mail_of_github_user +PACKAGIST_GROUP=packagist_group +HOMEPAGE=homepage diff --git a/.travis.yml b/.travis.yml index d0b8f1555..42d40f7dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ branches: language: php -php: 5.5 +php: 7.1 sudo: false diff --git a/composer.json b/composer.json index c02ccde54..f3908c061 100644 --- a/composer.json +++ b/composer.json @@ -22,12 +22,14 @@ "symfony/console": "^3.0", "symfony/filesystem": "^3.0", "twig/twig": "^1.24", - "vlucas/phpdotenv": "^2.2" + "vlucas/phpdotenv": "^2.2", + "composer/semver": "^1.4" }, "require-dev": { "symfony/var-dumper": "^3.0" }, "autoload": { "psr-4": { "Sonata\\DevKit\\": "src" } - } + }, + "prefer-stable": true } diff --git a/config/dev-kit.yml b/config/dev-kit.yml index 7805ab1e7..d400e6d86 100644 --- a/config/dev-kit.yml +++ b/config/dev-kit.yml @@ -1,53 +1,69 @@ labels: - - name: patch + # describe priority + - name: 'patch' color: '207de5' - - name: minor + - name: 'minor' color: '009800' - - name: major + - name: 'major' color: 'e11d21' - - name: pedantic + - name: 'pedantic' color: 'd1e4fa' - - name: enhancement - color: '02d7e1' - - name: feature - color: '02e10c' - - name: bug + - name: 'bug' color: 'fc2929' - - name: unconfirmed - color: '444444' - - name: critical + - name: 'critical' color: 'fc2929' - - name: vendor + + # state on a board + - name: 'in progress' + color: 'ededed' + - name: 'review' color: 'ededed' - - name: pending author + - name: 'wip/poc' color: 'ededed' - - name: in progress + - name: 'ready' color: 'ededed' - - name: RTM - color: 'ffffff' - - name: docs - color: 'fbca04' - - name: help wanted - color: '0e8a16' - - name: Easy Pick + - name: 'won`t fix' + color: 'ededed' + + # issue classes + - name: 'help wanted' + color: '128A0C' + - name: 'good first issue' + color: '5319e7' + - name: 'question' + color: 'cc317c' + - name: 'Easy Pick' color: 'd7e102' - - name: 'Needs: Documentation' - color: '006b75' - - name: 'Needs: Tests' - color: '006b75' - - name: 'Needs: Changelog note' - color: '006b75' - - name: 'Needs: Upgrade note' - color: '006b75' - - name: 'hacktoberfest' - color: 'b0581d' + - name: 'DX' + color: '5319e7' + - name: 'UX' + color: '19E791' + - name: 'discussion' + color: 'cc317c' + - name: 'enhancement' + color: '02d7e1' + - name: 'feature' + color: '207de5' + - name: 'nice to have' + color: '0052cc' + + # persistence layer + - name: 'ORM' + color: 'bfd4f2' + - name: 'PHPCR' + color: 'bfd4f2' + + # else + - name: 'pending author' + color: 'ededed' + - name: 'blocked' + color: 'cccccc' + - name: 'duplicate' + color: 'cccccc' packages: symfony: symfony/symfony - fos_user: friendsofsymfony/user-bundle - sonata_core: sonata-project/core-bundle - sonata_admin: sonata-project/admin-bundle - sonata_block: sonata-project/block-bundle - sonata_user: sonata-project/user-bundle - ruflin_elastica: ruflin/elastica - doctrine_odm: doctrine/mongodb-odm + core_bundle: symfony-cmf/core-bundle + website: symfony-cmf/symfony-cmf-website + seo_bundle: symfony-cmf/seo-bundle + diff --git a/config/projects.yml b/config/projects.yml index 1f930648e..3904249de 100644 --- a/config/projects.yml +++ b/config/projects.yml @@ -1,411 +1,330 @@ -admin-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_block: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_block: ['3'] - -admin-search-bundle: +seo-bundle: + description: | + It provides a solution to make content bundles + aware for Search Engine Optimisation (SEO). branches: master: - php: ['7.1'] - versions: - symfony: ['2.8'] - sonata_admin: ['3'] - ruflin_elastica: ['2'] - 1.x: - php: ['5.6', '7.0'] - versions: - symfony: ['2.8'] - sonata_admin: ['3'] - ruflin_elastica: ['2'] - -article-bundle: + next_unstable: '3.0' + phpunit_version: "5.7.26" + docs_path: 'bundles/seo' + test_kernel: 'Symfony\Cmf\Bundle\SeoBundle\Tests\Fixtures\App\Kernel' + deprecation_warnings: "weak" + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + functional_tests_phpcr: ~ + functional_tests_orm: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] +core-bundle: + description: | + The CoreBundle for the Symfony content management framework provides common functionality, + helpers and utilities for the other CMF bundles. The major features are a publish workflow, + a twig extension and php templating helper to walk PHPCR-ODM trees and support for optional translated content. branches: master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - + next_unstable: '3.0' + target_branch: "2.1" + phpunit_version: "5.7.26" + docs_path: 'bundles/core' + test_kernel: Symfony\Cmf\Bundle\CoreBundle\Tests\Fixtures\App\Kernel + deprecation_warnings: "weak" + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + functional_tests_phpcr: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] block-bundle: + description: | + The BlockBundle provides integration with + [SonataBlockBundle](https://github.com/sonata-project/SonataBlockBundle). + It is used to manage fragments of content, so-called blocks, that are persisted + in a database and can be incorporated into any page layout. The BlockBundle also + provides a few commonly used standard blocks, including the ability to edit them. branches: master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_core: ['3'] - # https://travis-ci.org/sonata-project/SonataBlockBundle/jobs/131844587#L222 - #sonata_admin: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_core: ['3'] - # This has to be resolved: https://travis-ci.org/sonata-project/SonataBlockBundle/jobs/130298942#L230 - #sonata_admin: ['3'] - -cache: - excluded_files: - - README.md - docs_target: false - branches: - master: - php: ['7.1'] - 1.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - -cache-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - 2.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - -classification-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_admin: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_admin: ['3'] - -classification-media-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - -comment-bundle: + next_unstable: '3.0' + phpunit_version: "5.7.26" + target_branch: "2.1" + unlimited_memory: true + docs_path: 'bundles/block' + test_kernel: Symfony\Cmf\Bundle\BlockBundle\Tests\Fixtures\App\Kernel + deprecation_warnings: "weak" + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + functional_tests_phpcr: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] + +menu-bundle: + description: | + The MenuBundle provides menus from a doctrine object manager with the help of KnpMenuBundle. branches: master: - php: ['7.1'] - versions: - symfony: ['2.8'] - sonata_core: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8'] - sonata_core: ['3'] - -core-bundle: + next_unstable: '3.0' + phpunit_version: "5.7.26" + target_branch: "2.2" + docs_path: 'bundles/menu' + deprecation_warnings: "weak" + test_kernel: Symfony\Cmf\Bundle\MenuBundle\Tests\Fixtures\App\Kernel + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + functional_tests_phpcr: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] +routing-bundle: + description: | + The RoutingBundle enables the + [CMF Routing component](https://github.com/symfony-cmf/Routing) + as a Symfony bundle. It provides route documents for Doctrine PHPCR-ODM and a + controller for redirection routes. branches: master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] + next_unstable: '3.0' + last_stable: "2.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'bundles/routing' + deprecation_warnings: "weak" + test_kernel: Symfony\Cmf\Bundle\RoutingBundle\Tests\Fixtures\App\Kernel + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + functional_tests_phpcr: ~ + functional_tests_orm: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] + +routing: + description: | + The Symfony CMF Routing component extends the Symfony routing component with additional features: -dashboard-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_admin: ['3'] - sonata_block: ['3'] + * A ChainRouter to run several routers in parallel + * A DynamicRouter that can load routes from any database and can generate + additional information in the route match. -datagrid-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - 2.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] + The CMF Routing component does not need the Symfony full stack framework. It is + also useful in applications not using the full Symfony framework. -doctrine-extensions: - excluded_files: - - README.md - docs_target: false + For the best integration into the Symfony full stack framework, it is + recommended to use the [RoutingBundle](https://github.com/symfony-cmf/RoutingBundle) + when building Symfony full stack applications. branches: master: - php: ['7.1'] - 1.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - -doctrine-mongodb-admin-bundle: + next_unstable: '3.0' + last_stable: "2.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'components/routing' + deprecation_warnings: "weak" + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] +routing-auto: + description: | + This component automatically creates and manages routes for configured persisted document classes. + This library contains all implementation agnostic code. branches: master: - php: ['5.6'] - services: [mongodb] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_admin: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6'] - services: [mongodb] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_admin: ['3'] - -doctrine-orm-admin-bundle: + next_unstable: '3.0' + last_stable: "2.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'components/routing' + deprecation_warnings: "weak" + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + versions: + symfony: [ '3.4.*', '4.0.*', '4.1.*', '4.2.*'] +content-bundle: + description: | + The ContentBundle provides a document for static content and the controller to render it. branches: master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_admin: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_admin: ['3'] - -doctrine-phpcr-admin-bundle: + next_unstable: '3.0' + last_stable: "2.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'bundles/routing' + deprecation_warnings: "weak" + test_kernel: Symfony\Cmf\Bundle\ContentBundle\Tests\Fixtures\App\Kernel + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + functional_tests_phpcr: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] +routing-auto-bundle: + description: | + This bundle automatically creates and manages routes for configured persisted document classes. branches: master: - # sonata 2 should still support php 5.6 to be consistent with the symfony cmf - php: ['5.6', '7.0', '7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_admin: ['3'] - sonata_block: ['3'] - 1.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8'] + next_unstable: '3.0' + last_stable: "2.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'bundles/routing-auto-bundle' + test_kernel: Symfony\Cmf\Bundle\RoutingAutoBundle\Tests\Fixtures\App\Kernel + deprecation_warnings: "weak" + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + functional_tests_phpcr: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] +resource-rest-bundle: + description: | + This Bundle provides a REST API to Puli resources as provided by the CmfResource component. -easy-extends-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - 2.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] + #### Running Behat -ecommerce: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8'] - docs_path: docs - 2.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8'] - docs_path: docs + 1. Run web server: + ``` + KERNEL_CLASS="Symfony\Cmf\Bundle\ResourceRestBundle\Tests\Fixtures\App\Kernel" ./vendor/symfony-cmf/testing/bin/console server:run -d vendor/symfony-cmf/testing/resources/web/ 8000 + ``` + 2. Run the behat tests: + ``` + KERNEL_CLASS="Symfony\Cmf\Bundle\ResourceRestBundle\Tests\Fixtures\App\Kernel" ./vendor/bin/behat + ``` -exporter: - excluded_files: - - README.md branches: master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - doctrine_odm: ['1'] - services: [mongodb] - docs_path: docs - 1.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - target_php: 5.6 - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - doctrine_odm: ['1'] - services: [mongodb] - docs_path: docs + next_unstable: '2.0' + last_stable: "1.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'bundles/resource-rest' + deprecation_warnings: "weak" + test_kernel: Symfony\Cmf\Bundle\ResourceRestBundle\Tests\Fixtures\App\Kernel + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] +resource-bundle: + description: | + This bundle provides *object* resource location services based on Puli. -formatter-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_block: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_block: ['3'] + Examples: -google-authenticator: - excluded_files: - - README.md - docs_target: false - branches: - master: - php: ['7.1'] - 2.x: - php: ['7.0', '7.1'] - 1.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] + - **Static document path mapping**: Map the path `/routes` to `/cms/routes` + - **Dynamic document resolution**: Map the path `/routes` to `/cms//routes`. + - **Access documents at a static location**: Map `/role/menu/main` to `/cms/menus/main-menu`. -intl-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_user: ['3'] - 2.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_user: ['3'] + The first example could use the existing CMF base route configuration to + resolve paths. -media-bundle: - branches: - master: - php: ['7.1'] - services: [mongodb] - versions: - symfony: ['2.8', '3.2', '3.3'] - doctrine_odm: ['1'] - sonata_core: ['3'] - sonata_admin: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - services: [mongodb] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - doctrine_odm: ['1'] - sonata_core: ['3'] - sonata_admin: ['3'] + The benefit of the above two examples would be most strongly felt in + association with another component which would provide a context for the + document resource resolution. -news-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8'] - sonata_core: ['3'] - sonata_admin: ['3'] - sonata_user: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8'] - sonata_core: ['3'] - sonata_admin: ['3'] - sonata_user: ['3'] + For example, a `Site` which is matched against the incoming hostname would + provide the context with which to resolve the documents. -notification-bundle: branches: master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_core: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_core: ['3'] - -page-bundle: + next_unstable: '2.0' + last_stable: "1.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'bundles/resource-rest' + deprecation_warnings: "weak" + test_kernel: Symfony\Cmf\Bundle\ResourceBundle\Tests\Fixtures\App\Kernel + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + functional_tests_phpcr: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] +sonata-phpcr-admin-integration-bundle: + description: "" branches: master: + next_unstable: '2.0' + last_stable: "1.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'bundles/sonata-phpcr-admin-integration-bundle' + test_kernel: Symfony\Cmf\Bundle\SonataPhpcrAdminIntegrationBundle\Tests\Fixtures\App\Kernel + deprecation_warnings: 0 php: ['7.1'] + make_tasks: + unit_tests: ~ + functional_tests_phpcr: ~ versions: - symfony: ['2.8'] - sonata_core: ['3'] - sonata_admin: ['3'] - sonata_block: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8'] - sonata_core: ['3'] - sonata_admin: ['3'] - sonata_block: ['3'] - -propel-admin-bundle: ~ + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] +resource: + excluded_files: + - .travis.yml.twig + description: | + The Resource component provides PHPCR/ODM integration with Puli. -sandbox: ~ + > **CAUTION** As Puli is not yet stable, the complete component is marked + > internal. Backwards compatibility of upcoming 1.x versions is not + > guaranteed. -seo-bundle: branches: master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_block: ['3'] - sonata_admin: ['3'] - 2.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_block: ['3'] - sonata_admin: ['3'] - -timeline-bundle: - branches: - master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_admin: ['3'] - sonata_block: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_admin: ['3'] - sonata_block: ['3'] + next_unstable: '3.0' + last_stable: "1.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'components/resource' + deprecation_warnings: "weak" + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ +testing: + description: | + **NOTE**: This is an internal tool and is not intended to be used outside of + the context of the CMF. -translation-bundle: branches: master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_admin: ['3'] - 2.x: - php: ['5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8', '3.2', '3.3'] - sonata_core: ['3'] - sonata_admin: ['3'] + next_unstable: '3.0' + last_stable: "2.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'components/testing' + deprecation_warnings: "weak" + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] +tree-browser-bundle: + excluded_files: + - .travis.yml.twig + description: | + The TreeBrowserBundle provides tree navigation for the Content Repository. -user-bundle: branches: master: - php: ['7.1'] - versions: - symfony: ['2.8', '3.2', '3.3'] - fos_user: ['2'] - sonata_core: ['3'] - sonata_admin: ['3'] - 3.x: - php: ['5.3', '5.4', '5.5', '5.6', '7.0', '7.1'] - versions: - symfony: ['2.3', '2.7', '2.8'] - fos_user: ['1.3'] - sonata_core: ['3'] - sonata_admin: ['3'] + next_unstable: '3.0' + last_stable: "2.1" + target_branch: "master" + phpunit_version: "5.7.26" + minimum_stability: dev + docs_path: 'bundle/tree-browser-bundle' + deprecation_warnings: "weak" + php: ['7.2', '7.3'] + make_tasks: + unit_tests: ~ + versions: + symfony: ['3.4.*', '4.0.*', '4.1.*', '4.2.*'] \ No newline at end of file diff --git a/project/.github/Bug_report.md b/project/.github/Bug_report.md new file mode 100644 index 000000000..8b27ed79e --- /dev/null +++ b/project/.github/Bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug Report +about: Report errors and problems + +--- + + + +**Description** + + +**How to reproduce** + + +**Possible Solution** + + +**Additional context** + diff --git a/project/.github/Documentation_issue.md b/project/.github/Documentation_issue.md new file mode 100644 index 000000000..196478c4b --- /dev/null +++ b/project/.github/Documentation_issue.md @@ -0,0 +1,10 @@ +--- +name: Documentation Issue +about: Anything related to Symfony CMF Documentation + +--- + +Symfony CMF Documentation has its own dedicated repository. Please open your +documentation-related issue at https://github.com/symfony-cmf/symfony-cmf-docs/issues + +Thanks! diff --git a/project/.github/Feature_request.md b/project/.github/Feature_request.md new file mode 100644 index 000000000..db3e02ca9 --- /dev/null +++ b/project/.github/Feature_request.md @@ -0,0 +1,12 @@ +--- +name: Feature Request +about: RFC and ideas for new features and improvements + +--- + +**Description** + + +**Example** + diff --git a/project/.github/ISSUE_TEMPLATE.md b/project/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 0d504535f..000000000 --- a/project/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,50 +0,0 @@ - - - - -### Environment - -#### Sonata packages - -``` -$ composer show --latest 'sonata-project/*' -``` - -#### Symfony packages - -``` -$ composer show --latest 'symfony/*' -``` - -#### PHP version - -``` -$ php -v -# Put the result here. -``` - -## Subject - - - -## Steps to reproduce - -## Expected results - -## Actual results - - diff --git a/project/.github/PULL_REQUEST_TEMPLATE.md b/project/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index dc76dc5aa..000000000 --- a/project/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,58 +0,0 @@ - - - -I am targeting this branch, because {reason}. - -In case of bug fix, `{{ legacy_branch }}` **MUST** be targeted. - - - -Closes #{put_issue_number_here} - -## Changelog - - - - -```markdown -### Added -- Added some `Class::newMethod` to do great stuff - -### Changed - -### Deprecated - -### Removed - -### Fixed - -### Security -``` - -## To do - - - -- [ ] Update the tests -- [ ] Update the documentation -- [ ] Add an upgrade note - -## Subject - - diff --git a/project/.github/Security_issue.md b/project/.github/Security_issue.md new file mode 100644 index 000000000..34be6e0e3 --- /dev/null +++ b/project/.github/Security_issue.md @@ -0,0 +1,9 @@ +--- +name: Security Issue +about: Report security-related errors + +--- + +If you have found a security issue in Symfony, please send the details to +[David](mailto:david@liip.ch) or [Maximilian](mailto:maximilian.berghoff@gmx.de) and don't disclose it publicly until we can provide a +fix for it. diff --git a/project/.github/Support_question.md b/project/.github/Support_question.md new file mode 100644 index 000000000..c128b566f --- /dev/null +++ b/project/.github/Support_question.md @@ -0,0 +1,15 @@ +--- +name: Support Question +about: Questions about using Symfony CMF and its components + +--- + +**Description** + + +**How to reproduce (optional)** + + +**Possible Solution** + + diff --git a/project/.php_cs b/project/.php_cs deleted file mode 100644 index dcd9cd1b7..000000000 --- a/project/.php_cs +++ /dev/null @@ -1,40 +0,0 @@ - - -For the full copyright and license information, please view the LICENSE -file that was distributed with this source code. -EOF; - -// PHP-CS-Fixer 1.x -if (class_exists('Symfony\CS\Fixer\Contrib\HeaderCommentFixer')) { - \Symfony\CS\Fixer\Contrib\HeaderCommentFixer::setHeader($header); -} - -$config = ConfigBridge::create() - ->setUsingCache(true) -; - -// PHP-CS-Fixer 2.x -if (method_exists($config, 'setRules')) { - $config->setRules(array_merge($config->getRules(), array( - 'header_comment' => array('header' => $header) - ))); -} - -return $config; diff --git a/project/.styleci.yml b/project/.styleci.yml index b92598db3..2bdb04b1f 100644 --- a/project/.styleci.yml +++ b/project/.styleci.yml @@ -1,30 +1,29 @@ -# DO NOT EDIT THIS FILE! -# -# It's auto-generated by sonata-project/dev-kit package. -# -# Package `sllh/php-cs-fixer-styleci-bridge` is required to get it working. +####################################################### +# DO NOT EDIT THIS FILE! # +# # +# It's auto-generated by symfony-cmf/dev-kit package. # +####################################################### + +############################################################################ +# This file is part of the Symfony CMF package. # +# # +# (c) 2011-2017 Symfony CMF # +# # +# For the full copyright and license information, please view the LICENSE # +# file that was distributed with this source code. # +############################################################################ + preset: symfony enabled: - - class_keyword_remove - combine_consecutive_unsets - - long_array_syntax + - short_array_syntax - newline_after_open_tag - no_php4_constructor - no_useless_else - - ordered_class_elements - ordered_use -# Comment strict rules for the moment. Should be uncomment later to see StyleCI PR results -# - strict -# - strict_param -# - php_unit_construct -# - php_unit_strict + - strict + - php_unit_construct -finder: - exclude: - - 'Tests/Fixtures' - # ecommerce special case: - - 'Resources/skeleton' - # ignore vendor assets - - 'Resources/public/vendor' +disabled: [single_line_class_definition] diff --git a/project/.travis.yml.twig b/project/.travis.yml.twig index 5d254e3ee..09890ba40 100644 --- a/project/.travis.yml.twig +++ b/project/.travis.yml.twig @@ -1,94 +1,95 @@ -# DO NOT EDIT THIS FILE! -# -# It's auto-generated by sonata-project/dev-kit package. +####################################################### +# DO NOT EDIT THIS FILE! # +# # +# It's auto-generated by symfony-cmf/dev-kit package. # +####################################################### -branches: - only: -{% for branch in branches|keys %} - - {{ branch }} -{% endfor %} +############################################################################ +# This file is part of the Symfony CMF package. # +# # +# (c) 2011-2017 Symfony CMF # +# # +# For the full copyright and license information, please view the LICENSE # +# file that was distributed with this source code. # +############################################################################ language: php php: -{% for version in php %} - - '{{ version }}' -{% endfor %} - - nightly + - 7.1 -{% if services is not empty %} -services: -{% for service in services %} - - {{ service }} -{% endfor %} - -{% endif %} sudo: false -dist: precise cache: - pip: true directories: + - .phpunit - $HOME/.composer/cache/files env: +{% if versions.symfony|length > 0 %} + matrix: SYMFONY_VERSION={{ versions.symfony|last }} global: - - PATH="$HOME/.local/bin:$PATH" - - SYMFONY_DEPRECATIONS_HELPER=weak - - TARGET=test - - UPSTREAM_URL=https://github.com/sonata-project/{{ repository_name }}.git - - XMLLINT_INDENT=" " +{% endif %} +{% if failing_allowed_versions.symfony|length > 0 or versions.symfony|length > 0 %} + - SYMFONY_DEPRECATIONS_HELPER="{{ deprecation_warnings }}" + - SYMFONY_PHPUNIT_DIR=.phpunit SYMFONY_PHPUNIT_REMOVE="symfony/yaml" +{% endif %} +{% if test_kernel %} + - KERNEL_CLASS={{ test_kernel }} +{% endif %} +{% if phpunit_version %} + - SYMFONY_PHPUNIT_VERSION={{ phpunit_version }} +{% endif %} matrix: - fast_finish: true include: -{% if docs_target %} - - php: '{{ php|last }}' - env: TARGET=docs +{% if failing_allowed_versions.symfony is defined %} + - php: "{{ php|last }}" + env: STABILITY="{{ minimum_stability }}" SYMFONY_VERSION="{{ failing_allowed_versions.symfony|first }}" {% endif %} -{% if docs_target %} - - php: '{{ php|last }}' - env: TARGET=lint -{% endif %} - - php: '{{ php|first }}' - env: COMPOSER_FLAGS="--prefer-lowest" -{% for package_name,package_versions in versions %} -{% for version in package_versions %} - - php: '{{ target_php|default(php|last) }}' - env: {{ package_name|upper }}={{ version }}.* + - php: "{{ php|last }}" + env: {% if minimum_stability != 'prod' %}STABILITY="{{ minimum_stability }}" {% endif %}SYMFONY_VERSION="{{ versions.symfony|last }}" + - php: "{{ php|first }}" + env: COMPOSER_FLAGS="--prefer-lowest" SYMFONY_VERSION="{{ versions.symfony|first }}" SYMFONY_DEPRECATIONS_HELPER={% if deprecation_warnings is defined %}"{{ deprecation_warnings }}"{% else %}"weak"{% endif %} + +{% if php|length < 3 %} +{% for version in versions.symfony if version != versions.symfony|first and version != versions.symfony|last %} + - php: "{{ php|last }}" + env: {% if minimum_stability != 'prod' %}STABILITY="{{ minimum_stability }}" {% endif %}SYMFONY_VERSION="{{ version }}" {% endfor %} -{% if package_versions|length > 0 %} - - php: '{{ target_php|default(php|last) }}' - env: {{ package_name|upper }}=dev-master@dev -{% endif %} +{% else %} +{% for php_version in php if php_version != php|last and php_version != php|first %} +{% for version in versions.symfony if version != versions.symfony|first and version != versions.symfony|last %} + - php: "{{ php_version }}" + env: {% if minimum_stability != 'prod' %}STABILITY="{{ minimum_stability }}" {% endif %}SYMFONY_VERSION="{{ version }}" {% endfor %} - - php: '{{ php|last }}' - env: SYMFONY_DEPRECATIONS_HELPER=0 - - php: hhvm - dist: trusty +{% endfor %} +{% endif %} + fast_finish: true allow_failures: - - php: nightly - - php: hhvm - - env: SYMFONY_DEPRECATIONS_HELPER=0 -{% for package_name,package_versions in versions %} -{% if package_versions|length > 0 %} - - env: {{ package_name|upper }}=dev-master@dev +{% if failing_allowed_versions.symfony is defined %} + - php: "{{ php|last }}" + env: STABILITY=dev SYMFONY_VERSION="{{ failing_allowed_versions.symfony|first }}" {% endif %} -{% endfor %} before_install: - - git remote add upstream ${UPSTREAM_URL} && git fetch --all - - if [[ -x .travis/check_relevant_${TARGET}.sh && "$TRAVIS_PULL_REQUEST" != "false" ]]; then export RELEVANT=$(.travis/check_relevant_${TARGET}.sh); fi; - - if [[ ! -z ${RELEVANT} ]];then exit 0; fi; - - if [ -x .travis/before_install_${TARGET}.sh ]; then .travis/before_install_${TARGET}.sh; fi; + - phpenv config-rm xdebug.ini || true + - composer self-update + - composer validate --no-check-all --ansi + - if ! [ -z "$STABILITY" ]; then composer config minimum-stability ${STABILITY}; composer config prefer-stable true; fi; + - if [ "$SYMFONY_VERSION" != "" ]; then composer require symfony/symfony:${SYMFONY_VERSION} --no-update; fi + - export BRANCH=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo $TRAVIS_BRANCH; else echo $TRAVIS_PULL_REQUEST_BRANCH; fi) +{% if unlimited_memory %} install: - - if [ -x .travis/install_${TARGET}.sh ]; then .travis/install_${TARGET}.sh; fi; - -before_script: - - if [ -x .travis/before_script_${TARGET}.sh ]; then .travis/before_script_${TARGET}.sh; fi; - -script: make $TARGET + - phpenv config-add travis.php.ini + - php -ini | grep memory_limit + - travis_wait composer update --prefer-dist $COMPOSER_FLAGS +{% else %} +install: travis_wait composer update --prefer-dist $COMPOSER_FLAGS +{% endif %} +script: + - if [ "${TEST_INSTALLATION}" == true ]; then make test_installation; else make test; fi -after_success: - - if [ -x .travis/after_success_${TARGET}.sh ]; then .travis/after_success_${TARGET}.sh; fi; +notifications: + irc: "irc.freenode.org#symfony-cmf" diff --git a/project/.travis/after_success_test.sh b/project/.travis/after_success_test.sh deleted file mode 100755 index 87158518b..000000000 --- a/project/.travis/after_success_test.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -set -ev - -coveralls -v diff --git a/project/.travis/before_install_test.sh.twig b/project/.travis/before_install_test.sh.twig deleted file mode 100755 index 7fd389068..000000000 --- a/project/.travis/before_install_test.sh.twig +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env sh -set -ev - -if [ "${TRAVIS_PHP_VERSION}" != "hhvm" ]; then - PHP_INI_DIR="$HOME/.phpenv/versions/$(phpenv version-name)/etc/conf.d/" - TRAVIS_INI_FILE="$PHP_INI_DIR/travis.ini" - echo "memory_limit=3072M" >> "$TRAVIS_INI_FILE" - - {% if '5.3' in php %} - if [ "$TRAVIS_PHP_VERSION" '<' '5.4' ]; then - XDEBUG_INI_FILE="$PHP_INI_DIR/xdebug.ini" - if [ -f "$XDEBUG_INI_FILE" ]; then - mv "$XDEBUG_INI_FILE" /tmp - fi - fi - {% endif %} - - {% if 'mongodb' in services %} - if [ "$TRAVIS_PHP_VERSION" '<' '7.0' ]; then - echo "extension=mongo.so" >> "$TRAVIS_INI_FILE" - else - echo "extension=mongodb.so" >> "$TRAVIS_INI_FILE" - - # Backwards compatibility with old mongo extension - composer require "alcaeus/mongo-php-adapter" --no-update - fi - {% endif %} -fi - -sed --in-place "s/\"dev-master\":/\"dev-${TRAVIS_COMMIT}\":/" composer.json - -{% for package_name,package_versions in versions if package_versions|length > 0 %} -if [ "${{ package_name|upper }}" != "" ]; then composer require "{{ packages[package_name] }}:${{ package_name|upper }}" --no-update; fi; -{% endfor %} diff --git a/project/.travis/before_script_test.sh.twig b/project/.travis/before_script_test.sh.twig deleted file mode 100644 index 0d8684d69..000000000 --- a/project/.travis/before_script_test.sh.twig +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -ev - -{% if '5.3' in php %} -if [ "$TRAVIS_PHP_VERSION" != "hhvm" ] && [ "$TRAVIS_PHP_VERSION" '<' '5.4' ]; then - PHP_INI_DIR="$HOME/.phpenv/versions/$(phpenv version-name)/etc/conf.d/" - XDEBUG_INI_FILE="/tmp/xdebug.ini" - if [ -f "$XDEBUG_INI_FILE" ]; then - mv "$XDEBUG_INI_FILE" "$PHP_INI_DIR" - fi -fi -{% endif %} diff --git a/project/.travis/check_relevant_docs.sh b/project/.travis/check_relevant_docs.sh deleted file mode 100755 index 9e618c4e2..000000000 --- a/project/.travis/check_relevant_docs.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -set -ev - -RELEVANT_FILES=$(git diff --name-only HEAD upstream/${TRAVIS_BRANCH} -- '*.rst') - -if [[ -z ${RELEVANT_FILES} ]]; then echo -n 'KO'; exit 0; fi; diff --git a/project/.travis/check_relevant_lint.sh b/project/.travis/check_relevant_lint.sh deleted file mode 100755 index 42be76763..000000000 --- a/project/.travis/check_relevant_lint.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -set -ev - -RELEVANT_FILES=$(git diff --name-only HEAD upstream/${TRAVIS_BRANCH} -- '*.json' '*.yml' '*.xml' '*.xliff') - -if [[ -z ${RELEVANT_FILES} ]]; then echo -n 'KO'; exit 0; fi; diff --git a/project/.travis/check_relevant_test.sh b/project/.travis/check_relevant_test.sh deleted file mode 100755 index a4ad29036..000000000 --- a/project/.travis/check_relevant_test.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash -set -ev - -RELEVANT_FILES=$(git diff --name-only HEAD upstream/${TRAVIS_BRANCH} -- '*.php' '*.yml' '*.xml' '*.twig' '*.js' '*.css' '*.json') - -if [[ -z ${RELEVANT_FILES} ]]; then echo -n 'KO'; exit 0; fi; diff --git a/project/.travis/install_docs.sh.twig b/project/.travis/install_docs.sh.twig deleted file mode 100755 index ea1d81c48..000000000 --- a/project/.travis/install_docs.sh.twig +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -set -ev - -pip install -r {{ docs_path }}/requirements.txt --user diff --git a/project/.travis/install_lint.sh b/project/.travis/install_lint.sh deleted file mode 100755 index ab8f0f390..000000000 --- a/project/.travis/install_lint.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env sh -set -ev - -composer global require sllh/composer-lint:@stable --prefer-dist --no-interaction - -gem install yaml-lint diff --git a/project/.travis/install_test.sh b/project/.travis/install_test.sh deleted file mode 100755 index b15f8f875..000000000 --- a/project/.travis/install_test.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env sh -set -ev - -mkdir --parents "${HOME}/bin" - -# PHPUnit install -if [ ${TRAVIS_PHP_VERSION} '<' '5.6' ]; then - PHPUNIT_PHAR=phpunit-4.8.phar -else - PHPUNIT_PHAR=phpunit-5.7.phar -fi -wget "https://phar.phpunit.de/${PHPUNIT_PHAR}" --output-document="${HOME}/bin/phpunit" -chmod u+x "${HOME}/bin/phpunit" - -# Coveralls client install -wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar --output-document="${HOME}/bin/coveralls" -chmod u+x "${HOME}/bin/coveralls" - -# To be removed when these issues are resolved: -# https://github.com/composer/composer/issues/5355 -# https://github.com/composer/composer/issues/5030 -composer update --prefer-dist --no-interaction --prefer-stable --quiet --ignore-platform-reqs - -composer update --prefer-dist --no-interaction --prefer-stable ${COMPOSER_FLAGS} diff --git a/project/CONTRIBUTING.md b/project/CONTRIBUTING.md index cc2bb9548..521e2c2ce 100644 --- a/project/CONTRIBUTING.md +++ b/project/CONTRIBUTING.md @@ -1,432 +1,12 @@ -# Sonata project contribution - -Thanks for your interest in Sonata projects! - -This document is about issues and pull requests. - -## Summary - -* [Issues](#issues) -* [Pull Requests](#pull-requests) -* [Code Reviews](#code-reviews) - -## Issues - -First, check if you are up to date: is your version still supported, and are -you using the latest patch version? - -GitHub Issues is for **issues**, as opposed to question on how to use Sonata. -If you are not sure this is a bug, or simply want to ask such a question, -please post your question on [Stack Overflow](http://stackoverflow.com/questions/tagged/sonata), -using the `sonata` tags. - -If you happen to find a bug, we kindly request you report it. However, -before submitting it, please check the [project documentation available -online](https://sonata-project.org/bundles/). - -Then, if it appears that it is indeed a real bug, you may report it using -Github by following these points are taken care of: - -* Check if the bug is not already reported! -* The title sums up the issue with clarity. -* A description of the workflow needed to reproduce the bug. Please try to make - sentences, dumping an error message by itself is frowned upon. -* If your issue is an error page, you must provide us with a stack trace. With - recent versions of Symfony, you can even get stack traces as plain text at the -end of the page. Just look for "Stack Trace (Plain Text)", and copy/paste what -you see. **Do not** make a screenshot of the stack trace, as screenshots are -not indexed by search engines and will make it difficult for other people to -find your bug report. If you have an issue when using the Symfony CLI, -use the `-vvv` option to get a stack trace. -* Screenshots should be considered additional data, and therefore, you should - always provide a textual description of the bug. It is strongly recommended -to provide them when reporting UI-related bugs. -* If you need to provide code, make sure you know how to get syntactic - coloration, in particular with [fenced code -blocks](https://help.github.com/articles/creating-and-highlighting-code-blocks/). -When you feel the code is to long, use external code pastebin like -https://gist.github.com/ or http://hastebin.com/ . If this is not sufficient, -just create a repository to show the issue. - -> _NOTE:_ Don't hesitate to give as much information as you can (OS, PHP -> version, extensions...) - -## Pull Requests - -All the sonata team will be glad to review your code changes propositions! :smile: - -But please, read the following before. - -### The content - -#### Coding style - -Each project follows [PSR-1](http://www.php-fig.org/psr/psr-1/), [PSR-2](http://www.php-fig.org/psr/psr-2/) -and [Symfony Coding Standards](http://symfony.com/doc/current/contributing/code/standards.html) for coding style, -[PSR-4](http://www.php-fig.org/psr/psr-4/) for autoloading. - -Please [install PHP Coding Standard Fixer](http://cs.sensiolabs.org/#installation) -and run this command before committing your modifications: - -```bash -php-cs-fixer fix --verbose -``` - -#### The documentation - -The documentation is mostly written with the `rst` format, and can be found in the `Resources/doc` directory. -You can test the doc rendering with the `make docs` command, but to do this, you will need [Sphinx][sphinx_install]. -Just like php dependencies can be managed with Composer, python dependencies can be managed with [pip][pip_install]. -To get sphinx, simply run the following command. - -```bash -pip install --requirement {{ docs_path }}/requirements.txt --user -``` - -Some python binaries should be downloaded to `~/.local/bin` for Linux or `~/Library/Python/2.7/bin` for Mac OS, -[modify your `$PATH` environment variable](http://www.linfo.org/path_env_var.html) -so that it contains this path and then, from the root of the project, run `make docs` - -If `make docs` is successful, you should be able to see your modifications: - -```bash -$YOUR_FAVORITE_BROWSER {{ docs_path }}/_build/html/index.html -``` - -If your PR contains a new feature, you must add documentation for it. -Of course, you can also create PRs consisting only in documentation changes. - -Documentation contributions should comply with [the Symfony documentation standards][sf_docs_standards]. - -#### The tests - -If your PR contains a fix, tests should be added to prove the bug. - -If your PR contains an addition, a new feature, this one has to be fully covered by tests. - -Some rules have to be respected about the test: - -* Annotations about coverage are prohibited. This concerns: - * `@covers` - * `@coversDefaultClass` - * `@coversNothing` - * `@codeCoverageIgnore` - * `@codeCoverageIgnoreStart` - * `@codeCoverageIgnoreEnd` -* All test methods should be prefixed by `test`. Example: `public function testItReturnsNull()`. -* All test method names must be in camel case format. -* As opposed, the `@test` annotation is prohibited. -* Most of the time, the test class should have the same name as the targeted class, suffixed by `Test`. -* The `@expectedException*` annotations are prohibited. Use `PHPUnit_Framework_TestCase::setExpectedException()`. - -##### Using test doubles - -Since version 4.5, PHPUnit requires and [integrates](https://phpunit.de/manual/current/en/test-doubles.html#test-doubles.prophecy) -the [phpspec/prophecy](https://github.com/phpspec/prophecy). -Historically, Sonata has been using [the built-in test doubles implementation](https://phpunit.de/manual/current/en/test-doubles.html), -but [has decided to move to Prophecy](https://github.com/sonata-project/dev-kit/issues/89), -which is more concise than its built-in counterpart is most cases. -This means the current Sonata codebase currently uses both implementations. -If you want to contribute a test that uses test doubles, please follow these rules : - -1. All new test classes MUST use Prophecy. -2. If you are changing an existing test method, you MUST use the same implementation it already uses, -and focus on the goal of your PR and only on that. -3. If you are changing an existing test class, you MUST use the same implementation it already uses, -to be more consistent. -4. You MAY submit a PR that migrates a test class from the built-in test double implementation to Prophecy, -but it must migrate it entirely. The PR should only be about the migration. - -### Writing a Pull Request - -#### The subject - -Ideally, a Pull Request should concern one and **only one** subject, so that it -remains clear, and independent changes can be merged quickly. - -If you want to fix a typo and improve the performance of a process, you should -try as much as possible to do it in a **separate** PR, so that we can quickly -merge one while discussing the other. - -The goal is to have a clear commit history and make a possible revert easier. - -If you found an issue/typo while writing your change that is not related to -your work, please do another PR for that. In some rare cases, you might be -forced to do it on the same PR. In this kind of situation, please add a comment -on your PR explaining why you feel it is the case. - -#### The Change log - -For each PR, a change log must be provided. - -There are few cases where no change log is necessary: - -* When you fix a bug on an unreleased feature. -* When your PR concerns only the documentation (fix or improvement). - -**Do not** edit the `CHANGELOG.md` directly though, because having every -contributor write PR with changes in the same file, at roughly the same line is -a recipe for conflicts. Instead, fill in the dedicated section that should -appear in a textaread when submitting your PR. - -Your note can be put on one of these sections: - -* `Added` for new features. -* `Changed` for changes in existing functionality. -* `Deprecated` for deprecation of features that will be removed in next major release. -* `Removed` for deprecated features removed in this release. -* `Fixed` for any bug fixes. -* `Security` to invite users to upgrade in case of vulnerabilities. - -More information about the followed changelog format: [keepachangelog.com](http://keepachangelog.com/) - -#### The base branch - -Before writing a PR, you have to check on which branch your changes should be based. - -Each project follows [semver](http://semver.org/) convention for release management. - -Here is a short table resuming on which you have to start: - -Kind of modification | Backward Compatible (BC) | Type of release | Branch to target | Label | --------------------- | ------------------------ | --------------- | ----------------------- | ----- | -Bug fixes | Yes | Patch | `{{ legacy_branch }}` | | -Bug fixes | Not on legacy | Patch | `{{ stable_branch }}` | | -Bug fixes | No (Only if no choice) | Major | `{{ unstable_branch }}` | | -Feature | Yes | Minor | `{{ stable_branch }}` | | -Feature | No (Only if no choice) | Major | `{{ unstable_branch }}` | | -Deprecation | Yes (Have to) | Minor | `{{ stable_branch }}` | | -Deprecation removal | No (Can't be) | Major | `{{ unstable_branch }}` | | - -Notes: - * Branch `{{ legacy_branch }}` is the branch of the **latest stable** patch releases and - has to be used for Backward Compatible bug fixes only. - * Branch `{{ stable_branch }}` is the branch of the **latest stable** minor release and - has to be used for Backward compatible enhancement PRs. - **No bug fix will be accepted here**, except if the bug fix concerns `{{ stable_branch }}` and **only this one**. - Bug fixes merged on `{{ legacy_branch }}` will be ported on other branches by fallback merging. - * If you PR is not **Backward Compatible** but can be, it **must** be: - * Changing a function/method signature? Prefer create a new one and deprecate the old one. - * Code deletion? Don't. Please deprecate it instead. - * If your BC PR is accepted, you can do a new one on the `{{ unstable_branch }}` branch which removes the deprecated code. - * SYMFONY DOC REF (same logic)? - -If you have a non-BC PR to propose, please try to create a related BC PR first. -This BC PR should mark every piece of code that needs to be removed / uncommented / reworked -in the corresponding non-BC PR with the following marker comment : `NEXT_MAJOR`. -When the BC PR is merged in the stable branch, wait for the stable branch to be -merged in the unstable branch, and then work on your non-BC PR. - -For instance, assuming you want to introduce a new method to an existing interface, you should do something like this: - -```php -normalizeKeys(false) ->prototype('array') ->children() - ->arrayNode('excluded_files')->prototype('scalar')->defaultValue(array())->end()->end() + ->arrayNode('excluded_files')->prototype('scalar')->defaultValue([])->end()->end() ->booleanNode('docs_target')->defaultTrue()->end() + ->scalarNode('description')->defaultValue('')->end() ->arrayNode('branches') ->normalizeKeys(false) - ->defaultValue(array()) + ->defaultValue([]) ->prototype('array') ->children() - ->arrayNode('php')->prototype('scalar')->defaultValue(array())->end()->end() - ->arrayNode('services')->prototype('scalar')->defaultValue(array())->end()->end() + ->scalarNode('next_unstable')->defaultValue(null)->end() + ->scalarNode('last_stable')->defaultValue(null)->end() + ->scalarNode('target_branch')->defaultValue('master')->end() + ->booleanNode('unlimited_memory')->defaultFalse()->end() + ->scalarNode('minimum_stability')->defaultValue('prod')->end() + ->arrayNode('php')->prototype('scalar')->defaultValue([])->end()->end() + ->arrayNode('services')->prototype('scalar')->defaultValue([])->end()->end() ->scalarNode('target_php')->defaultNull()->end() ->append($this->addVersionsNode()) + ->append($this->addFailingVersionsNode()) + ->scalarNode('test_kernel')->defaultNull()->end() + ->scalarNode('deprecation_warnings')->defaultValue(24)->end() ->scalarNode('docs_path')->defaultValue('Resources/doc')->end() + ->scalarNode('docs_extra')->defaultValue('')->end() + ->scalarNode('phpunit_version')->defaultValue('5.7.26')->end() + ->arrayNode('make_tasks') + ->prototype('scalar') + ->defaultValue([]) + ->end() ->end() ->end() ->end() @@ -80,7 +95,23 @@ private function addVersionsNode() $childrenNode = $node->children(); foreach ($this->devKitConfigs['packages'] as $key => $name) { - $childrenNode->arrayNode($key)->prototype('scalar')->defaultValue(array())->end()->end(); + $childrenNode->arrayNode($key)->prototype('scalar')->defaultValue([])->end()->end(); + } + + $childrenNode->end(); + + return $node; + } + + private function addFailingVersionsNode() + { + $builder = new TreeBuilder(); + $node = $builder->root('failing_allowed_versions'); + + $childrenNode = $node->children(); + + foreach ($this->devKitConfigs['packages'] as $key => $name) { + $childrenNode->arrayNode($key)->prototype('scalar')->defaultValue([])->end()->end(); } $childrenNode->end(); diff --git a/src/Console/Command/AbstractCommand.php b/src/Console/Command/AbstractCommand.php index bbb14694c..0275b30f0 100644 --- a/src/Console/Command/AbstractCommand.php +++ b/src/Console/Command/AbstractCommand.php @@ -22,16 +22,19 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Yaml\Yaml; +use Composer\Semver; +use Packagist\Api\Result\Package\Version; /** * @author Sullivan Senechal */ abstract class AbstractCommand extends Command { - const GITHUB_GROUP = 'sonata-project'; - const GITHUB_USER = 'SonataCI'; - const GITHUB_EMAIL = 'thomas+ci@sonata-project.org'; - const PACKAGIST_GROUP = 'sonata-project'; + protected $githubGroup; + protected $githubUser; + protected $githubEmail; + protected $packagistGroup; + protected $homepage; /** * @var SymfonyStyle @@ -76,17 +79,20 @@ protected function initialize(InputInterface $input, OutputInterface $output) $this->io = new SymfonyStyle($input, $output); $processor = new Processor(); - $devKitConfigs = $processor->processConfiguration(new DevKitConfiguration(), array( - 'sonata' => Yaml::parse(file_get_contents(__DIR__.'/../../../config/dev-kit.yml')), - )); - $projectsConfigs = $processor->processConfiguration(new ProjectsConfiguration($devKitConfigs), array( - 'sonata' => array('projects' => Yaml::parse(file_get_contents(__DIR__.'/../../../config/projects.yml'))), - )); + $devKitConfigs = $processor->processConfiguration(new DevKitConfiguration(), [ + 'cmf' => Yaml::parse(file_get_contents(__DIR__.'/../../../config/dev-kit.yml')), + ]); + $projectsConfigs = $processor->processConfiguration(new ProjectsConfiguration($devKitConfigs), [ + 'cmf' => ['projects' => Yaml::parse(file_get_contents(__DIR__.'/../../../config/projects.yml'))], + ]); $this->configs = array_merge($devKitConfigs, $projectsConfigs); - if (getenv('GITHUB_OAUTH_TOKEN')) { - $this->githubAuthKey = getenv('GITHUB_OAUTH_TOKEN'); - } + $this->githubAuthKey = getenv('GITHUB_OAUTH_TOKEN'); + $this->githubGroup = getenv('GITHUB_GROUP'); + $this->githubUser = getenv('GITHUB_USER'); + $this->githubEmail = getenv('GITHUB_EMAIL'); + $this->packagistGroup = getenv('PACKAGIST_GROUP'); + $this->homepage = getenv('HOMEPAGE'); $this->packagistClient = new \Packagist\Api\Client(); @@ -112,4 +118,29 @@ final protected function getRepositoryName(Package $package) return str_replace('.git', '', end($repositoryArray)); } + + /** + * @param Packaage $package + * + * @return [] + */ + final protected function getStableVersions(Package $package): array + { + $stableVersions = array_filter( + array_keys($package->getVersions()), + function ($version) { + try { + if ('stable' !== Semver\VersionParser::parseStability($version)) { + return false; + } + } catch (\Exception $e) { + return false; + } + + return true; + } + ); + + return Semver\Semver::rsort($stableVersions); + } } diff --git a/src/Console/Command/AutoMergeCommand.php b/src/Console/Command/AutoMergeCommand.php index 4941f807c..1283b096d 100644 --- a/src/Console/Command/AutoMergeCommand.php +++ b/src/Console/Command/AutoMergeCommand.php @@ -38,7 +38,7 @@ protected function configure() $this ->setName('auto-merge') ->setDescription('Merges branches of repositories if there is no conflict.') - ->addArgument('projects', InputArgument::IS_ARRAY, 'To limit the dispatcher on given project(s).', array()) + ->addArgument('projects', InputArgument::IS_ARRAY, 'To limit the dispatcher on given project(s).', []) ; } @@ -71,7 +71,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $projectConfig = $this->configs['projects'][$name]; try { - $package = $this->packagistClient->get(static::PACKAGIST_GROUP.'/'.$name); + $package = $this->packagistClient->get($this->packagistGroup.'/'.$name); $this->io->title($package->getName()); $this->mergeBranches($package, $projectConfig); } catch (ExceptionInterface $e) { @@ -102,7 +102,7 @@ private function mergeBranches(Package $package, array $projectConfig) // Merge message should be removed when following PR will be merged and tagged. // https://github.com/KnpLabs/php-github-api/pull/379 $response = $this->githubClient->repo()->merge( - static::GITHUB_GROUP, + $this->githubGroup, $repositoryName, $base, $head, @@ -119,10 +119,10 @@ private function mergeBranches(Package $package, array $projectConfig) $message = sprintf('Merging of %s into %s contains conflicts. Skipped.', $head, $base); $this->io->warning($message); - $this->slackClient->attach(array( + $this->slackClient->attach([ 'text' => $message, 'color' => 'danger', - ))->send('Merging: '.$repositoryName); + ])->send('Merging: '.$repositoryName); continue; } diff --git a/src/Console/Command/DependsCommand.php b/src/Console/Command/DependsCommand.php index 4f29708c8..39590f72b 100644 --- a/src/Console/Command/DependsCommand.php +++ b/src/Console/Command/DependsCommand.php @@ -27,7 +27,7 @@ protected function configure() { $this ->setName('depends') - ->setDescription('Show internal sonata dependencies of each project.') + ->setDescription('Show internal symfony-cmf or symfony dependencies of each project.') ->addOption('branch-depth', null, InputOption::VALUE_OPTIONAL, 'Number of branches to show.', 2) ; } @@ -40,7 +40,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $branchDepth = intval($input->getOption('branch-depth')); foreach ($this->configs['projects'] as $name => $config) { - $package = $this->packagistClient->get(static::PACKAGIST_GROUP.'/'.$name); + $package = $this->packagistClient->get($this->packagistGroup.'/'.$name); $this->io->title($package->getName()); $bd = 0; @@ -54,7 +54,7 @@ protected function execute(InputInterface $input, OutputInterface $output) continue; } foreach ($version->getRequire() as $packageName => $constraint) { - if (!strstr($packageName, 'sonata-project/')) { + if (!strstr($packageName, 'symfony-cmf/') && !strstr($packageName, 'symfony/')) { continue; } $this->io->writeln($packageName.':'.$constraint); diff --git a/src/Console/Command/DispatchCommand.php b/src/Console/Command/DispatchCommand.php index 22ca7a3dc..8e7910d02 100644 --- a/src/Console/Command/DispatchCommand.php +++ b/src/Console/Command/DispatchCommand.php @@ -1,14 +1,5 @@ - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - namespace Sonata\DevKit\Console\Command; use Doctrine\Common\Inflector\Inflector; @@ -27,22 +18,19 @@ final class DispatchCommand extends AbstractNeedApplyCommand { const LABEL_NOTHING_CHANGED = 'Nothing to be changed.'; - + public const PACKAGIST_GROUP = 'symfony-cmf'; /** * @var GitWrapper */ private $gitWrapper; - /** * @var Filesystem */ private $fileSystem; - /** * @var \Twig_Environment */ private $twig; - /** * @var string[] */ @@ -58,9 +46,8 @@ protected function configure() $this ->setName('dispatch') ->setDescription('Dispatches configuration and documentation files for all sonata projects.') - ->addArgument('projects', InputArgument::IS_ARRAY, 'To limit the dispatcher on given project(s).', array()) - ->addOption('with-files', null, InputOption::VALUE_NONE, 'Applies Pull Request actions for projects files') - ; + ->addArgument('projects', InputArgument::IS_ARRAY, 'To limit the dispatcher on given project(s).', []) + ->addOption('with-files', null, InputOption::VALUE_NONE, 'Applies Pull Request actions for projects files'); } protected function initialize(InputInterface $input, OutputInterface $output) @@ -69,12 +56,11 @@ protected function initialize(InputInterface $input, OutputInterface $output) $this->gitWrapper = new GitWrapper(); $this->fileSystem = new Filesystem(); - $this->twig = new \Twig_Environment(new \Twig_Loader_Filesystem(__DIR__.'/../../..')); + $this->twig = new \Twig_Environment(new \Twig_Loader_Filesystem(__DIR__ . '/../../..')); $this->projects = count($input->getArgument('projects')) ? $input->getArgument('projects') - : array_keys($this->configs['projects']) - ; + : array_keys($this->configs['projects']); } /** @@ -84,18 +70,18 @@ protected function execute(InputInterface $input, OutputInterface $output) { $notConfiguredProjects = array_diff($this->projects, array_keys($this->configs['projects'])); if (count($notConfiguredProjects)) { - $this->io->error('Some specified projects are not configured: '.implode(', ', $notConfiguredProjects)); + $this->io->error('Some specified projects are not configured: ' . implode(', ', $notConfiguredProjects)); return 1; } foreach ($this->projects as $name) { try { - $package = $this->packagistClient->get(static::PACKAGIST_GROUP.'/'.$name); + $package = $this->packagistClient->get($this->packagistGroup . '/' . $name); $projectConfig = $this->configs['projects'][$name]; $this->io->title($package->getName()); - $this->updateRepositories($package, $projectConfig); - $this->updateDevKitHook($package); + $this->updateRepositories($package); + // $this->updateDevKitHook($package); $this->updateLabels($package); $this->updateBranchesProtection($package, $projectConfig); @@ -103,7 +89,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->dispatchFiles($package); } } catch (ExceptionInterface $e) { - $this->io->error('Failed with message: '.$e->getMessage()); + $this->io->error('Failed with message: ' . $e->getMessage()); + $this->io->writeln($e->getTraceAsString(), OutputInterface::VERBOSITY_DEBUG); } } @@ -114,25 +101,23 @@ protected function execute(InputInterface $input, OutputInterface $output) * Sets repository information and general settings. * * @param Package $package - * @param array $projectConfig */ - private function updateRepositories(Package $package, array $projectConfig) + private function updateRepositories(Package $package) { $repositoryName = $this->getRepositoryName($package); - $branches = array_keys($projectConfig['branches']); $this->io->section('Repository'); - $repositoryInfo = $this->githubClient->repo()->show(static::GITHUB_GROUP, $repositoryName); - $infoToUpdate = array( - 'homepage' => 'https://sonata-project.org/', + $repositoryInfo = $this->githubClient->repo()->show($this->githubGroup, $repositoryName); + $infoToUpdate = [ + 'homepage' => $this->homepage, 'has_issues' => true, 'has_projects' => true, 'has_wiki' => false, - 'default_branch' => end($branches), + 'default_branch' => 'master', 'allow_squash_merge' => true, 'allow_merge_commit' => false, 'allow_rebase_merge' => true, - ); + ]; foreach ($infoToUpdate as $info => $value) { if ($value === $repositoryInfo[$info]) { @@ -141,11 +126,13 @@ private function updateRepositories(Package $package, array $projectConfig) } if (count($infoToUpdate)) { - $this->io->comment('Following info have to be changed: '.implode(', ', array_keys($infoToUpdate)).'.'); + $this->io->comment('Following info have to be changed: ' . implode(', ', array_keys($infoToUpdate)) . '.'); if ($this->apply) { - $this->githubClient->repo()->update(static::GITHUB_GROUP, $repositoryName, array_merge($infoToUpdate, array( - 'name' => $repositoryName, - ))); + $this->githubClient->repo()->update( + $this->githubGroup, + $repositoryName, + array_merge($infoToUpdate, ['name' => $repositoryName]) + ); } } elseif (!count($infoToUpdate)) { $this->io->comment(static::LABEL_NOTHING_CHANGED); @@ -163,10 +150,10 @@ private function updateLabels(Package $package) $configuredLabels = $this->configs['labels']; $missingLabels = $configuredLabels; - $headers = array('Name', 'Actual color', 'Needed Color', 'State'); - $rows = array(); + $headers = ['Name', 'Actual color', 'Needed Color', 'State']; + $rows = []; - foreach ($this->githubClient->repo()->labels()->all(static::GITHUB_GROUP, $repositoryName) as $label) { + foreach ($this->githubClient->repo()->labels()->all($this->githubGroup, $repositoryName) as $label) { $name = $label['name']; $color = $label['color']; @@ -182,25 +169,33 @@ private function updateLabels(Package $package) if (!$shouldExist) { $state = 'Deleted'; if ($this->apply) { - $this->githubClient->repo()->labels()->remove(static::GITHUB_GROUP, $repositoryName, $name); + $this->githubClient->repo()->labels()->remove($this->githubGroup, $repositoryName, $name); } } elseif ($shouldBeUpdated) { $state = 'Updated'; if ($this->apply) { - $this->githubClient->repo()->labels()->update(static::GITHUB_GROUP, $repositoryName, $name, array( - 'name' => $name, - 'color' => $configuredColor, - )); + $this->githubClient->repo()->labels()->update( + $this->githubGroup, + $repositoryName, + $name, + [ + 'name' => $name, + 'color' => $configuredColor, + ] + ); } } if ($state) { - array_push($rows, array( - $name, - '#'.$color, - $configuredColor ? '#'.$configuredColor : 'N/A', - $state, - )); + array_push( + $rows, + [ + $name, + '#' . $color, + $configuredColor ? '#' . $configuredColor : 'N/A', + $state, + ] + ); } } @@ -208,17 +203,24 @@ private function updateLabels(Package $package) $color = $label['color']; if ($this->apply) { - $this->githubClient->repo()->labels()->create(static::GITHUB_GROUP, $repositoryName, array( - 'name' => $name, - 'color' => $color, - )); + $this->githubClient->repo()->labels()->create( + $this->githubGroup, + $repositoryName, + [ + 'name' => $name, + 'color' => $color, + ] + ); } - array_push($rows, array($name, 'N/A', '#'.$color, 'Created')); + array_push($rows, [$name, 'N/A', '#' . $color, 'Created']); } - usort($rows, function ($row1, $row2) { - return strcasecmp($row1[0], $row2[0]); - }); + usort( + $rows, + function ($row1, $row2) { + return strcasecmp($row1[0], $row2[0]); + } + ); if (empty($rows)) { $this->io->comment(static::LABEL_NOTHING_CHANGED); @@ -239,25 +241,27 @@ private function updateDevKitHook(Package $package) // Construct the hook url. $hookToken = getenv('DEK_KIT_TOKEN') ? getenv('DEK_KIT_TOKEN') : 'INVALID_TOKEN'; $hookBaseUrl = 'http://sonata-dev-kit.sullivansenechal.com/github'; - $hookCompleteUrl = $hookBaseUrl.'?'.http_build_query(array('token' => $hookToken)); + $hookCompleteUrl = $hookBaseUrl . '?' . http_build_query(['token' => $hookToken]); // Set hook configs - $config = array( + $config = [ 'url' => $hookCompleteUrl, 'insecure_ssl' => '0', 'content_type' => 'json', - ); - $events = array( + ]; + $events = [ 'issue_comment', 'pull_request', 'pull_request_review_comment', - ); + ]; // First, check if the hook exists. $devKitHook = null; - foreach ($this->githubClient->repo()->hooks()->all(static::GITHUB_GROUP, $repositoryName) as $hook) { - if (array_key_exists('url', $hook['config']) - && 0 === strncmp($hook['config']['url'], $hookBaseUrl, strlen($hookBaseUrl))) { + foreach ($this->githubClient->repo()->hooks()->all($this->githubGroup, $repositoryName) as $hook) { + if ( + array_key_exists('url', $hook['config']) + && 0 === strncmp($hook['config']['url'], $hookBaseUrl, strlen($hookBaseUrl)) + ) { $devKitHook = $hook; break; @@ -268,28 +272,38 @@ private function updateDevKitHook(Package $package) $this->io->comment('Has to be created.'); if ($this->apply) { - $this->githubClient->repo()->hooks()->create(static::GITHUB_GROUP, $repositoryName, array( - 'name' => 'web', - 'config' => $config, - 'events' => $events, - 'active' => true, - )); + $this->githubClient->repo()->hooks()->create( + $this->githubGroup, + $repositoryName, + [ + 'name' => 'web', + 'config' => $config, + 'events' => $events, + 'active' => true, + ] + ); $this->io->success('Hook created.'); } - } elseif (count(array_diff_assoc($devKitHook['config'], $config)) + } elseif ( + count(array_diff_assoc($devKitHook['config'], $config)) || count(array_diff($devKitHook['events'], $events)) || !$devKitHook['active'] ) { $this->io->comment('Has to be updated.'); if ($this->apply) { - $this->githubClient->repo()->hooks()->update(static::GITHUB_GROUP, $repositoryName, $devKitHook['id'], array( - 'name' => 'web', - 'config' => $config, - 'events' => $events, - 'active' => true, - )); - $this->githubClient->repo()->hooks()->ping(static::GITHUB_GROUP, $repositoryName, $devKitHook['id']); + $this->githubClient->repo()->hooks()->update( + $this->githubGroup, + $repositoryName, + $devKitHook['id'], + [ + 'name' => 'web', + 'config' => $config, + 'events' => $events, + 'active' => true, + ] + ); + $this->githubClient->repo()->hooks()->ping($this->githubGroup, $repositoryName, $devKitHook['id']); $this->io->success('Hook updated.'); } } else { @@ -299,7 +313,7 @@ private function updateDevKitHook(Package $package) /** * @param Package $package - * @param array $projectConfig + * @param array $projectConfig */ private function updateBranchesProtection(Package $package, array $projectConfig) { @@ -310,32 +324,43 @@ private function updateBranchesProtection(Package $package, array $projectConfig if (!$this->apply) { return; } + foreach ($branches as $branch) { + $this->updateBranch($branch, $repositoryName); + if (isset($projectConfig['branches'][$branch]['target_branch'])) { + $this->updateBranch($projectConfig['branches'][$branch]['target_branch'], $repositoryName); + } + if (isset($projectConfig['branches'][$branch]['last_stable'])) { + $this->updateBranch($projectConfig['branches'][$branch]['last_stable'], $repositoryName); + } + } + } - $protectionConfig = array( - 'required_status_checks' => array( + private function updateBranch($branch, $repositoryName) + { + $protectionConfig = [ + 'required_status_checks' => [ 'strict' => false, - 'contexts' => array( + 'contexts' => [ 'continuous-integration/travis-ci', 'continuous-integration/styleci/pr', - ), - ), - 'required_pull_request_reviews' => array( - 'dismissal_restrictions' => array( - 'users' => array(), - 'teams' => array(), - ), + ], + ], + 'required_pull_request_reviews' => [ + 'dismissal_restrictions' => [ + 'users' => [], + 'teams' => [], + ], 'dismiss_stale_reviews' => true, 'require_code_owner_reviews' => true, - ), + ], 'restrictions' => null, 'enforce_admins' => false, - ); - foreach ($branches as $branch) { - $this->githubClient->repo()->protection() - ->update(static::GITHUB_GROUP, $repositoryName, $branch, $protectionConfig) - ; - } - $this->io->comment('Branches protection applied.'); + ]; + + $this->githubClient->repo()->protection() + ->update($this->githubGroup, $repositoryName, $branch, $protectionConfig); + + $this->io->comment('Branches protection applied to branch ' . $branch); } /** @@ -344,7 +369,7 @@ private function updateBranchesProtection(Package $package, array $projectConfig private function dispatchFiles(Package $package) { $repositoryName = $this->getRepositoryName($package); - $projectConfig = $this->configs['projects'][str_replace(static::PACKAGIST_GROUP.'/', '', $package->getName())]; + $projectConfig = $this->configs['projects'][str_replace($this->packagistGroup . '/', '', $package->getName())]; // No branch to manage, continue to next project. if (0 === count($projectConfig['branches'])) { @@ -352,65 +377,70 @@ private function dispatchFiles(Package $package) } // Clone the repository. - $clonePath = sys_get_temp_dir().'/sonata-project/'.$repositoryName; + $baseDirectory = sys_get_temp_dir() . '/dev-kit'; + if (!is_dir($baseDirectory)) { + mkdir($baseDirectory); + } + $clonePath = $baseDirectory . '/' . $repositoryName; if ($this->fileSystem->exists($clonePath)) { $this->fileSystem->remove($clonePath); } $git = $this->gitWrapper->cloneRepository( - 'https://'.static::GITHUB_USER.':'.$this->githubAuthKey.'@github.com/'.static::GITHUB_GROUP.'/'.$repositoryName, + 'https://' . $this->githubUser . ':' . $this->githubAuthKey . '@github.com/' . $this->githubGroup . '/' . $repositoryName, $clonePath ); - $git - ->config('user.name', static::GITHUB_USER) - ->config('user.email', static::GITHUB_EMAIL) - ; - + $git->config('user.name', $this->githubUser)->config('user.email', $this->githubEmail); $branches = array_reverse($projectConfig['branches']); $previousBranch = null; $previousDevKit = null; while (($branchConfig = current($branches))) { // We have to fetch all branches on each step in case a PR is submitted. - $remoteBranches = array_map(function ($branch) { - return $branch['name']; - }, $this->githubClient->repos()->branches(static::GITHUB_GROUP, $repositoryName)); + $remoteBranches = array_map( + function ($branch) { + return $branch['name']; + }, + $this->githubClient->repos()->branches($this->githubGroup, $repositoryName) + ); $currentBranch = key($branches); - $currentDevKit = $currentBranch.'-dev-kit'; + $currentDevKit = $currentBranch . '-dev-kit'; next($branches); // A PR is already here for previous branch, do nothing on the current one. if (in_array($previousDevKit, $remoteBranches, true)) { - continue; + $this->io->comment('Do not build branch "'.$currentBranch.'" as its previous build "'.$previousDevKit.'" is still there.'); } + // If the previous branch is not merged into the current one, do nothing. - if ($previousBranch && $this->githubClient->repos()->commits()->compare( - static::GITHUB_GROUP, $repositoryName, $currentBranch, $previousBranch - )['ahead_by']) { - continue; + $previousIsAhead = ($previousBranch && $this->githubClient->repos()->commits()->compare($this->githubGroup, $repositoryName, $currentBranch, $previousBranch)['ahead_by']); + if ($previousIsAhead) { + $this->io->comment('Do not build branch "'.$currentBranch.'" as its previous branch "'.$previousBranch.'" is not merged into this one.'); } // Diff application - $this->io->section('Files for '.$currentBranch); + $this->io->section('Files for ' . $currentBranch); - $git->reset(array('hard' => true)); + $git->reset(['hard' => true]); // Checkout the targeted branch if (in_array($currentBranch, $git->getBranches()->all(), true)) { $git->checkout($currentBranch); } else { - $git->checkout('-b', $currentBranch, '--track', 'origin/'.$currentBranch); + $git->checkout('-b', $currentBranch, '--track', 'origin/' . $currentBranch); } // Checkout the dev-kit branch - if (in_array('remotes/origin/'.$currentDevKit, $git->getBranches()->all(), true)) { - $git->checkout('-b', $currentDevKit, '--track', 'origin/'.$currentDevKit); + if (in_array('remotes/origin/' . $currentDevKit, $git->getBranches()->all(), true)) { + $git->checkout('-b', $currentDevKit, '--track', 'origin/' . $currentDevKit); } else { $git->checkout('-b', $currentDevKit); } + $this->setVersionConstraintsInComposer($clonePath, $projectConfig, $currentBranch); $this->renderFile($package, $repositoryName, 'project', $clonePath, $projectConfig, $currentBranch); - $git->add('.', array('all' => true))->getOutput(); + + $git->add('.', ['all' => true])->getOutput(); $diff = $git->diff('--color', '--cached')->getOutput(); if (!empty($diff)) { @@ -419,17 +449,25 @@ private function dispatchFiles(Package $package) $git->commit('DevKit updates')->push('-u', 'origin', $currentDevKit); // If the Pull Request does not exists yet, create it. - $pulls = $this->githubClient->pullRequests()->all(static::GITHUB_GROUP, $repositoryName, array( - 'state' => 'open', - 'head' => 'sonata-project:'.$currentDevKit, - )); + $pulls = $this->githubClient->pullRequests()->all( + $this->githubGroup, + $repositoryName, + [ + 'state' => 'open', + 'head' => $currentDevKit, + ] + ); if (0 === count($pulls)) { - $this->githubClient->pullRequests()->create(static::GITHUB_GROUP, $repositoryName, array( - 'title' => 'DevKit updates for '.$currentBranch.' branch', - 'head' => 'sonata-project:'.$currentDevKit, - 'base' => $currentBranch, - 'body' => '', - )); + $this->githubClient->pullRequests()->create( + $this->githubGroup, + $repositoryName, + [ + 'title' => 'DevKit updates for ' . $currentBranch . ' branch', + 'head' => $currentDevKit, + 'base' => $currentBranch, + 'body' => '', + ] + ); } // Wait 200ms to be sure GitHub API is up to date with new pushed branch/PR. @@ -447,35 +485,46 @@ private function dispatchFiles(Package $package) /** * @param Package $package - * @param string $repositoryName - * @param string $localPath - * @param string $distPath - * @param array $projectConfig - * @param string $branchName + * @param string $repositoryName + * @param string $localPath + * @param string $distPath + * @param array $projectConfig + * @param string $branchName */ - private function renderFile(Package $package, $repositoryName, $localPath, $distPath, array $projectConfig, $branchName) - { - $localFullPath = __DIR__.'/../../../'.$localPath; + private function renderFile( + Package $package, + $repositoryName, + $localPath, + $distPath, + array $projectConfig, + $branchName + ) { + $localFullPath = __DIR__ . '/../../../' . $localPath; $localFileType = filetype($localFullPath); $distFileType = $this->fileSystem->exists($distPath) ? filetype($distPath) : false; if ($localFileType !== $distFileType && false !== $distFileType) { - throw new \LogicException('File type mismatch between "'.$localPath.'" and "'.$distPath.'"'); + throw new \LogicException('File type mismatch between "' . $localPath . '" and "' . $distPath . '"'); } if (in_array(substr($localPath, 8), $projectConfig['excluded_files'], true)) { return; } - + $notRendered = ['.', '..']; + $branches = $projectConfig['branches']; + $currentBranchConfig = $branches[$branchName]; + if (!isset($currentBranchConfig['unlimited_memory']) || !$currentBranchConfig['unlimited_memory']) { + $notRendered[] = 'travis.php.ini'; + } if ('dir' === $localFileType) { $localDirectory = dir($localFullPath); while (false !== ($entry = $localDirectory->read())) { - if (!in_array($entry, array('.', '..'), true)) { + if (!in_array($entry, $notRendered, true)) { $this->renderFile( $package, $repositoryName, - $localPath.'/'.$entry, - $distPath.'/'.$entry, + $localPath . '/' . $entry, + $distPath . '/' . $entry, $projectConfig, $branchName ); @@ -493,44 +542,156 @@ private function renderFile(Package $package, $repositoryName, $localPath, $dist $branchConfig = $projectConfig['branches'][$branchName]; $localPathInfo = pathinfo($localPath); + reset($projectConfig['branches']); + $unstableBranch = isset($branchConfig['next_unstable']) ? $branchConfig['next_unstable'].'-dev' : key($projectConfig['branches']); + $stableBranch = isset($branchConfig['last_stable']) ? $branchConfig['last_stable'] : $unstableBranch; + $replacements = array_merge( + $this->configs, + $projectConfig, + $branchConfig, + [ + 'package_title' => Inflector::ucwords( + str_replace( + ['cmf', '/', '-'], + ['CMF', ' ', ' '], + $package->getName() + ) + ), + 'package_description' => $package->getDescription(), + 'packagist_name' => $package->getName(), + 'package_name' => $package->getName(), + 'repository_name' => $repositoryName, + 'current_branch' => $branchName, + 'unstable_branch' => $unstableBranch, + 'stable_branch' => $stableBranch, + 'docs_path' => $branchConfig['docs_path'], + 'tests_path' => str_replace([$this->packagistGroup . '/', '-bundle'], '', $package->getName()), + ] + ); if (array_key_exists('extension', $localPathInfo) && 'twig' === $localPathInfo['extension']) { - $distPath = dirname($distPath).'/'.basename($distPath, '.twig'); - file_put_contents($distPath, $this->twig->render($localPath, array_merge( - $this->configs, - $projectConfig, - $branchConfig, - array('repository_name' => $repositoryName) - ))); + $distPath = dirname($distPath) . '/' . basename($distPath, '.twig'); + file_put_contents($distPath, $this->twig->render($localPath, $replacements)); } else { - reset($projectConfig['branches']); - $unstableBranch = key($projectConfig['branches']); - $stableBranch = next($projectConfig['branches']) ? key($projectConfig['branches']) : $unstableBranch; - $legacyBranch = next($projectConfig['branches']) ? key($projectConfig['branches']) : $stableBranch; - file_put_contents($distPath, str_replace(array( - '{{ package_title }}', - '{{ package_description }}', - '{{ packagist_name }}', - '{{ repository_name }}', - '{{ current_branch }}', - '{{ unstable_branch }}', - '{{ stable_branch }}', - '{{ legacy_branch }}', - '{{ docs_path }}', - '{{ website_path }}', - ), array( - Inflector::ucwords(str_replace(array('-project', '/', '-'), array('', ' ', ' '), $package->getName())), - $package->getDescription(), - $package->getName(), - $repositoryName, - $branchName, - $unstableBranch, - $stableBranch, - $legacyBranch, - $branchConfig['docs_path'], - str_replace(array(static::PACKAGIST_GROUP.'/', '-bundle'), '', $package->getName()), - ), $localContent)); + foreach ($replacements as $key => $value) { + if (is_array($value)) { + continue; + } + $localContent = str_replace('{{ '.$key.' }}', $value, $localContent); + } + file_put_contents($distPath, $localContent); } // Restore file permissions after content copy $this->fileSystem->chmod($distPath, fileperms($localPath)); } + + /** + * Takes care on correct version in own packages and in symfony packiges. + * Also sets the target version in composer.json file. + * + * @param string $path + * @param array $projectConfig + * + * @throws \Exception + */ + private function setVersionConstraintsInComposer($path, array $projectConfig, $currentBranch) + { + $filePath = $path . '/composer.json'; + if (!$this->fileSystem->exists($filePath)) { + throw new \Exception('No composer.json found at: ' . $filePath); + } + $composerAsString = file_get_contents($filePath); + $composerAsJson = json_decode($composerAsString, true); + if (!isset($composerAsJson['require'])) { + throw new \Exception('no require path found in composer.json at: ' . $filePath); + } + + $branchConfig = $projectConfig['branches'][$currentBranch]; + $composerAsJson['require']['php'] = $this->evaluateVersionString($branchConfig['php']); + + // set version constraints for symfony dependencies + if (isset($branchConfig['versions']['symfony'])) { + foreach ($composerAsJson['require'] as $package => $version) { + if ('symfony/monolog-bundle' === $package) { + $composerAsJson['require'][$package] = '~3.1'; + } elseif ('symfony/phpunit-bridge' === $package) { + $composerAsJson['require'][$package] = trim( + $this->evaluateVersionString($branchConfig['versions']['symfony']), + '^2.8 | ' + ); + } elseif (preg_match('/symfony\//', $package) && 'friendsofsymfony/jsrouting-bundle' !== $package) { + $composerAsJson['require'][$package] = $this->evaluateVersionString($branchConfig['versions']['symfony']); + } + } + if (isset($composerAsJson['require-dev'])) { + foreach ($composerAsJson['require-dev'] as $package => $version) { + if ('symfony/monolog-bundle' === $package) { + $composerAsJson['require-dev'][$package] = '~3.1'; + } elseif ('symfony/phpunit-bridge' === $package) { + $composerAsJson['require-dev'][$package] = $this->evaluateVersionString($branchConfig['versions']['symfony'], '3.2'); + } elseif (preg_match('/symfony\//', $package) && 'friendsofsymfony/jsrouting-bundle' !== $package) { + $composerAsJson['require-dev'][$package] = $this->evaluateVersionString($branchConfig['versions']['symfony']); + } + } + } + } + + // remove minimum stability as we should set it via travis for specific builds or require @dev + if (isset($composerAsJson['minimum-stability'])) { + unset($composerAsJson['minimum-stability']); + } + if (isset($composerAsJson['prefer-stable'])) { + unset($composerAsJson['prefer-stable']); + } + + // add type information if not set + if (preg_match('/bundle/', $composerAsJson['name'])) { + $composerAsJson['type'] = 'symfony-bundle'; + } + + // takes care on correct target branch for master + $targetMasterBranch = $branchConfig['next_unstable'] . '-dev'; + if (!isset($composerAsJson['extra'])) { + $composerAsJson['extra'] = ['branch-alias' => ['dev-master' => $targetMasterBranch]]; + } elseif (!isset($composerAsJson['extra']['branch-alias'])) { + $composerAsJson['extra'][branch - alias] = ['dev-master' => $targetMasterBranch]; + } elseif (!isset($composerAsJson['extra']['branch-alias']['dev-master'])) { + $composerAsJson['extra']['branch-alias']['dev-master'] = $targetMasterBranch; + } + + $composerAsString = json_encode($composerAsJson, JSON_PRETTY_PRINT); + $composerAsString = str_replace('\/', '/', $composerAsString); + $composerAsString .= PHP_EOL; + file_put_contents($filePath, $composerAsString); + } + + /** + * Creates a string for version constraint with different major versions allowed. + * + * @param array $versions + * + * @param string $minimumVersion + * + * @return string + */ + private function evaluateVersionString(array $versions, $minimumVersion = '1.0') + { + $sortedVersions = []; + foreach ($versions as $version) { + if ($version < $minimumVersion) { + continue; + } + $version = trim($version, '@dev^'); + list($major, $minor) = explode('.', $version); + $sortedVersions[$major][] = $minor; + } + + $versionConstraintString = ''; + foreach ($sortedVersions as $major => $minorVersions) { + sort($minorVersions); + $firstMinorVersion = array_shift($minorVersions); + $versionConstraintString .= ' || ^' . $major . '.' . $firstMinorVersion; + } + + return trim($versionConstraintString, ' || '); + } } diff --git a/src/Console/Command/MergeConflictsCommand.php b/src/Console/Command/MergeConflictsCommand.php index 86c6e328f..6c899fde8 100644 --- a/src/Console/Command/MergeConflictsCommand.php +++ b/src/Console/Command/MergeConflictsCommand.php @@ -41,7 +41,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { foreach ($this->configs['projects'] as $name => $projectConfig) { try { - $package = $this->packagistClient->get(static::PACKAGIST_GROUP.'/'.$name); + $package = $this->packagistClient->get($this->packagistGroup.'/'.$name); $this->io->title($package->getName()); $this->checkPullRequests($package); } catch (ExceptionInterface $e) { @@ -56,41 +56,41 @@ private function checkPullRequests(Package $package) { $repositoryName = $this->getRepositoryName($package); - foreach ($this->githubClient->pullRequests()->all(static::GITHUB_GROUP, $repositoryName) as $pullRequest) { + foreach ($this->githubClient->pullRequests()->all($this->githubGroup, $repositoryName) as $pullRequest) { $number = $pullRequest['number']; - $pullRequest = $this->githubClient->pullRequests()->show(static::GITHUB_GROUP, $repositoryName, $number); + $pullRequest = $this->githubClient->pullRequests()->show($this->githubGroup, $repositoryName, $number); // The value of the mergeable attribute can be true, false, or null. // If the value is null this means that the mergeability hasn't been computed yet. // @see: https://developer.github.com/v3/pulls/#get-a-single-pull-request if (false === $pullRequest['mergeable']) { $comments = array_filter( - $this->githubPaginator->fetchAll($this->githubClient->issues()->comments(), 'all', array( - static::GITHUB_GROUP, + $this->githubPaginator->fetchAll($this->githubClient->issues()->comments(), 'all', [ + $this->githubGroup, $repositoryName, $number, - )), + ]), function ($comment) { - return static::GITHUB_USER === $comment['user']['login']; + return $this->githubUser === $comment['user']['login']; } ); $lastComment = end($comments); $lastCommentDate = $lastComment ? new \DateTime($lastComment['created_at']) : null; - $commits = $this->githubPaginator->fetchAll($this->githubClient->pullRequest(), 'commits', array( - static::GITHUB_GROUP, + $commits = $this->githubPaginator->fetchAll($this->githubClient->pullRequest(), 'commits', [ + $this->githubGroup, $repositoryName, $number, - )); + ]); $lastCommit = end($commits); $lastCommitDate = new \DateTime($lastCommit['commit']['committer']['date']); if (!$lastCommentDate || $lastCommentDate < $lastCommitDate) { if ($this->apply) { - $this->githubClient->issues()->comments()->create(static::GITHUB_GROUP, $repositoryName, $number, array( + $this->githubClient->issues()->comments()->create($this->githubGroup, $repositoryName, $number, [ 'body' => 'Could you please rebase your PR and fix merge conflicts?', - )); - $this->githubClient->addIssueLabel(static::GITHUB_GROUP, $repositoryName, $number, 'pending author'); + ]); + $this->githubClient->addIssueLabel($this->githubGroup, $repositoryName, $number, 'pending author'); } $this->io->text(sprintf('#%d - %s', $pullRequest['number'], $pullRequest['title'])); diff --git a/src/Console/Command/PullRequestAutoMergeCommand.php b/src/Console/Command/PullRequestAutoMergeCommand.php index 0229b7161..0ebf353d6 100644 --- a/src/Console/Command/PullRequestAutoMergeCommand.php +++ b/src/Console/Command/PullRequestAutoMergeCommand.php @@ -38,7 +38,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { foreach ($this->configs['projects'] as $name => $projectConfig) { try { - $package = $this->packagistClient->get(static::PACKAGIST_GROUP.'/'.$name); + $package = $this->packagistClient->get($this->packagistGroup.'/'.$name); $this->io->title($package->getName()); $this->mergePullRequest($package, $projectConfig); } catch (ExceptionInterface $e) { @@ -58,10 +58,10 @@ private function mergePullRequest(Package $package, array $projectConfig) $repositoryName = $this->getRepositoryName($package); $branches = array_keys($projectConfig['branches']); - $pulls = $this->githubPaginator->fetchAll($this->githubClient->pullRequests(), 'all', array( - static::GITHUB_GROUP, + $pulls = $this->githubPaginator->fetchAll($this->githubClient->pullRequests(), 'all', [ + $this->githubGroup, $repositoryName, - )); + ]); foreach ($pulls as $pull) { // Do not managed not configured branches. if (!in_array(str_replace('-dev-kit', '', $pull['base']['ref']), $branches, true)) { @@ -81,7 +81,7 @@ private function mergePullRequest(Package $package, array $projectConfig) )); $state = $this->githubClient->repos()->statuses()->combined( - static::GITHUB_GROUP, + $this->githubGroup, $repositoryName, $pull['head']['sha'] ); @@ -95,11 +95,11 @@ private function mergePullRequest(Package $package, array $projectConfig) continue; } - $commits = $this->githubPaginator->fetchAll($this->githubClient->pullRequests(), 'commits', array( - static::GITHUB_GROUP, + $commits = $this->githubPaginator->fetchAll($this->githubClient->pullRequests(), 'commits', [ + $this->githubGroup, $repositoryName, $pull['number'], - )); + ]); $commitMessages = array_map(function ($commit) { return $commit['commit']['message']; @@ -120,7 +120,7 @@ private function mergePullRequest(Package $package, array $projectConfig) if ($this->apply) { try { $this->githubClient->pullRequests()->merge( - static::GITHUB_GROUP, + $this->githubGroup, $repositoryName, $pull['number'], $squash ? '' : $pull['title'], @@ -131,7 +131,7 @@ private function mergePullRequest(Package $package, array $projectConfig) if ('sonata-project' === $pull['head']['repo']['owner']['login']) { $this->githubClient->gitData()->references()->remove( - static::GITHUB_GROUP, + $this->githubGroup, $repositoryName, 'heads/'.$pull['head']['ref'] ); diff --git a/src/GithubHookProcessor.php b/src/GithubHookProcessor.php index 382f05458..7b31e17f0 100644 --- a/src/GithubHookProcessor.php +++ b/src/GithubHookProcessor.php @@ -43,7 +43,7 @@ public function __construct($githubAuthKey = null) */ public function processPendingAuthor($eventName, array $payload) { - if (!in_array($payload['action'], array('created', 'synchronize'), true)) { + if (!in_array($payload['action'], ['created', 'synchronize'], true)) { return; } @@ -70,7 +70,7 @@ public function processPendingAuthor($eventName, array $payload) */ public function processReviewLabels($eventName, array $payload) { - if (!in_array($payload['action'], array('opened', 'synchronize'), true)) { + if (!in_array($payload['action'], ['opened', 'synchronize'], true)) { return; } diff --git a/web/index.php b/web/index.php index 43ce48422..0803ca6a1 100644 --- a/web/index.php +++ b/web/index.php @@ -23,17 +23,17 @@ $app = new Silex\Application(); $app - ->register(new TwigServiceProvider(), array( + ->register(new TwigServiceProvider(), [ 'twig.path' => __DIR__.'/../views', - )) + ]) ; $app->get('/', function () use ($app) { $revision = file_exists(__DIR__.'/../REVISION') ? trim(file_get_contents(__DIR__.'/../REVISION')) : null; - return $app['twig']->render('index.html.twig', array( + return $app['twig']->render('index.html.twig', [ 'revision' => $revision, - )); + ]); }); $app->post('/github', function (Request $request) use ($app, $githubHookProcessor, $devKitToken) { @@ -41,7 +41,7 @@ $payload = json_decode($request->getContent(), true); if (!$devKitToken || $request->query->get('token') !== $devKitToken) { - return $app->json(array('message' => 'Invalid credentials'), 403); + return $app->json(['message' => 'Invalid credentials'], 403); } switch ($eventName) { @@ -61,7 +61,7 @@ return new Response(); default: - return new JsonResponse(array('message' => 'Nothing to do for: '.$eventName), 200); + return new JsonResponse(['message' => 'Nothing to do for: '.$eventName], 200); } });