diff --git a/.github_changelog_generator b/.github_changelog_generator index f9d85e795..13de87762 100644 --- a/.github_changelog_generator +++ b/.github_changelog_generator @@ -1,5 +1,3 @@ project=blt unreleased-only=true -future-release=8.6.10 pr-label=**Misc merged pull requests** -output=temp-changelog.md diff --git a/.gitignore b/.gitignore index b2445d8c2..eae3f46b9 100644 --- a/.gitignore +++ b/.gitignore @@ -61,8 +61,27 @@ local.properties auto-save-list tramp .\#* -temp* +template/docroot/.csslintrc +template/docroot/.editorconfig +template/docroot/.eslintignore +template/docroot/.eslintrc +template/docroot/.eslintrc.json +template/docroot/.gitattributes +template/docroot/.htaccess +template/docroot/index.php +template/docroot/robots.txt +template/docroot/sites/default/default.settings.php +template/docroot/sites/default/services.yml +template/docroot/sites/default/settings.php +template/docroot/sites/default/default.services.yml +template/docroot/sites/development.services.yml +template/docroot/sites/example.settings.local.php +template/docroot/sites/example.sites.php +template/docroot/update.php +template/docroot/web.config + +# PHPStorm .idea #XHProf @@ -89,3 +108,5 @@ nbproject/* # generates, so they must be specified here rather than in template/.gitignore. template/.idea/.name template/composer.lock + +CHANGELOG.partial diff --git a/RELEASE.md b/RELEASE.md index ab4336e66..cc57e5ddf 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,8 +1,14 @@ This document outlines the process for creating a new BLT release. -# Testing +To perform a release: -## Prerequisites +1. [Run tests](#testing) locally.. +1. [Generate and commit updated CHANGELOG.md](#generate-changelogmd). +1. [Create a release](#create-a-release) + +## Testing + +### Prerequisites In order to use these testing instructions: @@ -11,67 +17,10 @@ In order to use these testing instructions: * MySQL must use `mysql://drupal:drupal@localhost/drupal:3306`. If this is not the case, modify the instructions below for your credentials. * In order to test Drupal VM, you must install VirtualBox and Vagrant. See [Drupal VM](https://github.com/geerlingguy/drupal-vm#quick-start-guide) for more information. -## Procedure - -1. Test "Create a new project without acquia/blt-project "from scratch", uses Drupal VM". -1. Test "Update existing project Pipelines project". - -## Create a new project via acquia/blt-project, uses local LAMP stack - -This test verifies that a new project can be created using `acquia/blt-project` via composer. This also tests the `blt update` process. - - export COMPOSER_PROCESS_TIMEOUT=2000 - rm -rf blted8 - composer create-project acquia/blt-project:8.x-dev blted8 --no-interaction - cd blted8 - # Overwrite MySQL creds for your local machine, if necessary. - # echo '$databases["default"]["default"]["username"] = "drupal";' >> docroot/sites/default/settings/local.settings.php - # echo '$databases["default"]["default"]["password"] = "drupal";' >> docroot/sites/default/settings/local.settings.php - blt local:setup - cd docroot - drush uli - read -p "Press any key to continue" - - # This updates to the latest dev version. - composer require acquia/blt:8.x-dev - dr uli - read -p "Press any key to continue" - cd ../ - -## Create a new project without acquia/blt-project "from scratch", uses Drupal VM - -This test verifies that a new project can be created from scratch using blt, without blt-project. It also tests Drupal VM integration. - - rm -rf blted8 - mkdir blted8 - cd blted8 - git init - composer init --stability=dev --no-interaction - composer config prefer-stable true - composer require acquia/blt:8.x-dev - composer update - blt vm - blt local:setup - drush @blted8.local uli - drush @blted8.local ssh blt tests:behat - read -p "Press any key to continue" - vagrant destroy - cd ../ - - -## Update existing project Pipelines project - - composer require acquia/blt:8.x-dev --no-update - composer update - git add -A - git commit -m 'Updating acquia/blt to latest dev version.' - git push origin - pipelines start - pipelines log - # Replace with remote alias - drush @alias ssh blt setup:drupal:install - +### Execute tests + ./scripts/blt/test-blt.sh [tag] + ## Generate CHANGELOG.md ### Prerequisites @@ -81,9 +30,14 @@ This test verifies that a new project can be created from scratch using blt, wit * Procure a [github api token](https://github.com/skywinder/github-changelog-generator#github-token). * Determine the version of your future release. +### Execute command + Then, generate your release notes via: - github_changelog_generator --token [token] --future-release=[version] + ./bin/blt-robo blt:release-notes [tag] [token] This will update CHANGELOG.md. The information for the new release should be copied and pasted into the GitHub release draft. +## Create a release + + ./bin/blt-robo blt:release [tag] [token] diff --git a/bin/blt-robo b/bin/blt-robo new file mode 100755 index 000000000..e8bcfb2a6 --- /dev/null +++ b/bin/blt-robo @@ -0,0 +1,28 @@ +#!/usr/bin/env php +<?php + +/** + * If we're running from phar load the phar autoload file. + */ +$pharPath = \Phar::running(true); +$consoleRoot = __DIR__ . '/../'; +if ($pharPath) { + require_once "$pharPath/vendor/autoload.php"; +} else { + if (file_exists($consoleRoot.'/vendor/autoload.php')) { + require_once $consoleRoot.'/vendor/autoload.php'; + } elseif (file_exists(__DIR__.'/../../autoload.php')) { + require_once __DIR__ . '/../../autoload.php'; + } +} + +$commandClasses = [ + \Acquia\Blt\Robo\Command\BltInternal::class, +]; +$statusCode = \Robo\Robo::run( + $_SERVER['argv'], + $commandClasses, + 'BLT', + '0.0.0-alpha0' +); +exit($statusCode); diff --git a/composer.json b/composer.json index 24993ad13..f95a48ef4 100644 --- a/composer.json +++ b/composer.json @@ -51,5 +51,9 @@ "hirak/prestissimo": "^0.3" }, "minimum-stability": "dev", - "prefer-stable": true + "prefer-stable": true, + "require-dev": { + "consolidation/Robo": "^1.0", + "guzzlehttp/guzzle": "^6.2" + } } diff --git a/composer.lock b/composer.lock index e59f23f43..086d9fb17 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "2134cd5cc4e949518b3f30dad456afc2", + "content-hash": "333c417335586053fe47c4ecaba79f81", "packages": [ { "name": "alchemy/zippy", @@ -4312,7 +4312,324 @@ "time": "2016-11-23T20:04:58+00:00" } ], - "packages-dev": [], + "packages-dev": [ + { + "name": "consolidation/annotated-command", + "version": "2.2.2", + "source": { + "type": "git", + "url": "https://github.com/consolidation/annotated-command.git", + "reference": "1f1d92807f72901e049e9df048b412c3bc3652c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/1f1d92807f72901e049e9df048b412c3bc3652c9", + "reference": "1f1d92807f72901e049e9df048b412c3bc3652c9", + "shasum": "" + }, + "require": { + "consolidation/output-formatters": "^3.1.5", + "php": ">=5.4.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "psr/log": "~1", + "symfony/console": "^2.8|~3", + "symfony/event-dispatcher": "^2.5|~3", + "symfony/finder": "^2.5|~3" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "satooshi/php-coveralls": "^1.0", + "squizlabs/php_codesniffer": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\AnnotatedCommand\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Initialize Symfony Console commands from annotated command class methods.", + "time": "2016-12-16T01:23:33+00:00" + }, + { + "name": "consolidation/log", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/consolidation/log.git", + "reference": "74ba81b4edc585616747cc5c5309ce56fec41254" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/log/zipball/74ba81b4edc585616747cc5c5309ce56fec41254", + "reference": "74ba81b4edc585616747cc5c5309ce56fec41254", + "shasum": "" + }, + "require": { + "php": ">=5.5.0", + "psr/log": "~1.0", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "squizlabs/php_codesniffer": "2.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.", + "time": "2016-03-23T23:46:42+00:00" + }, + { + "name": "consolidation/output-formatters", + "version": "3.1.6", + "source": { + "type": "git", + "url": "https://github.com/consolidation/output-formatters.git", + "reference": "c8ea5734985cea4acd6343a2465a2f71cf011c82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/c8ea5734985cea4acd6343a2465a2f71cf011c82", + "reference": "c8ea5734985cea4acd6343a2465a2f71cf011c82", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "symfony/console": "~2.5|~3.0", + "symfony/finder": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "satooshi/php-coveralls": "^1.0", + "squizlabs/php_codesniffer": "2.*", + "victorjonsson/markdowndocs": "^1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Consolidation\\OutputFormatters\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Greg Anderson", + "email": "greg.1.anderson@greenknowe.org" + } + ], + "description": "Format text by applying transformations provided by plug-in formatters.", + "time": "2017-01-08T20:32:20+00:00" + }, + { + "name": "consolidation/robo", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/consolidation/Robo.git", + "reference": "d06450370e8e303ebd1495dfc956f4c6c1b9dd01" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/consolidation/Robo/zipball/d06450370e8e303ebd1495dfc956f4c6c1b9dd01", + "reference": "d06450370e8e303ebd1495dfc956f4c6c1b9dd01", + "shasum": "" + }, + "require": { + "consolidation/annotated-command": "^2.2", + "consolidation/log": "~1", + "consolidation/output-formatters": "^3.1.5", + "league/container": "^2.2", + "php": ">=5.5.0", + "symfony/console": "~2.8|~3.0", + "symfony/event-dispatcher": "~2.5|~3.0", + "symfony/filesystem": "~2.5|~3.0", + "symfony/finder": "~2.5|~3.0", + "symfony/process": "~2.5|~3.0" + }, + "replace": { + "codegyre/robo": "< 1.0" + }, + "require-dev": { + "codeception/aspect-mock": "~1", + "codeception/base": "^2.2.6", + "codeception/verify": "^0.3.2", + "henrikbjorn/lurker": "~1", + "natxet/cssmin": "~3", + "patchwork/jsqueeze": "~2", + "pear/archive_tar": "^1.4.2", + "phpunit/php-code-coverage": "~2|~4", + "satooshi/php-coveralls": "~1", + "squizlabs/php_codesniffer": "~2" + }, + "suggest": { + "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch", + "natxet/CssMin": "For minifying JS files in taskMinify", + "patchwork/jsqueeze": "For minifying JS files in taskMinify", + "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively." + }, + "bin": [ + "robo" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "scripts/composer/ScriptHandler.php" + ], + "psr-4": { + "Robo\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Davert", + "email": "davert.php@resend.cc" + } + ], + "description": "Modern task runner", + "time": "2016-11-24T02:07:48+00:00" + }, + { + "name": "container-interop/container-interop", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/container-interop/container-interop.git", + "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/container-interop/container-interop/zipball/fc08354828f8fd3245f77a66b9e23a6bca48297e", + "reference": "fc08354828f8fd3245f77a66b9e23a6bca48297e", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Interop\\Container\\": "src/Interop/Container/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Promoting the interoperability of container objects (DIC, SL, etc.)", + "time": "2014-12-30T15:22:37+00:00" + }, + { + "name": "league/container", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/container.git", + "reference": "c0e7d947b690891f700dc4967ead7bdb3d6708c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/container/zipball/c0e7d947b690891f700dc4967ead7bdb3d6708c1", + "reference": "c0e7d947b690891f700dc4967ead7bdb3d6708c1", + "shasum": "" + }, + "require": { + "container-interop/container-interop": "^1.1", + "php": ">=5.4.0" + }, + "provide": { + "container-interop/container-interop-implementation": "^1.1" + }, + "replace": { + "orno/di": "~2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev", + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Container\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Phil Bennett", + "email": "philipobenito@gmail.com", + "homepage": "http://www.philipobenito.com", + "role": "Developer" + } + ], + "description": "A fast and intuitive dependency injection container.", + "homepage": "https://github.com/thephpleague/container", + "keywords": [ + "container", + "dependency", + "di", + "injection", + "league", + "provider", + "service" + ], + "time": "2016-03-17T11:07:59+00:00" + } + ], "aliases": [], "minimum-stability": "dev", "stability-flags": { diff --git a/scripts/blt/test-blt.sh b/scripts/blt/test-blt.sh new file mode 100755 index 000000000..a3f226c7b --- /dev/null +++ b/scripts/blt/test-blt.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# Usage ./scripts/blt/release-blt 8.6.11 + +tag="$1" +branch=$tag-build + +SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +clear +echo "Make sure that your local LAMP stack is running!" +echo "This will destroy the $(cd $SCRIPT_DIR/../../../blted8 && pwd) directory." +read -p "Press any key to continue." +set -x +cd $SCRIPT_DIR/../../../ +export COMPOSER_PROCESS_TIMEOUT=2000 +# @todo prompt to delete if exists +rm -rf blted8 +composer create-project acquia/blt-project:8.x-dev blted8 --no-interaction +cd blted8 +# Overwrite MySQL creds for your local machine, if necessary. +# echo '$databases["default"]["default"]["username"] = "drupal";' >> docroot/sites/default/settings/local.settings.php +# echo '$databases["default"]["default"]["password"] = "drupal";' >> docroot/sites/default/settings/local.settings.php +./vendor/bin/blt local:setup +cd docroot +cd .. +./vendor/bin/blt validate +./vendor/bin/blt tests +read -p "Press any key to continue. This will create a VM and re-run tests there." +./vendor/bin/blt vm +./vendor/bin/blt local:setup +drush @blted8.local ssh blt tests:behat +read -p "Press any key to continue. This will destroy the VM and attempt to perform a Pipelines build." +vagrant destroy + +./vendor/bin/yaml-cli update:value blt/project.yml git.remotes.0 bolt8pipeline@svn-2420.devcloud.hosting.acquia.com:bolt8pipeline.git +./vendor/bin/blt ci:pipelines:init +git co -b ${branch} +git add -A +git commit -m "BLT-000: Creating test branch for BLT release ${tag}" +git remote add origin bolt8pipeline@svn-2420.devcloud.hosting.acquia.com:bolt8pipeline.git +git push origin ${branch} +pipelines start + +# @todo have pipelines deploy and install on ODE. diff --git a/scripts/release-notes/README.md b/scripts/release-notes/README.md deleted file mode 100644 index f13dccff3..000000000 --- a/scripts/release-notes/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Generate Release Notes Script - -## Overview -Use a script compiles PR comments for a project into a Markdown file that can -be copy and pasted into GitHub release notes. - -## Usage - -### Required Inputs -* **username:** your GitHub username -* **password:** your GitHub password. Note that if you use two factor - authentication you will need to use an [Access Token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/) - in lieu of a password. -* **repository:** the name of the GitHub repository (e.g. `https://github.com/acquia-pso/my-repo`) - -### Simple usage - - php generate-release-notes.php github username:password org:repo-name:branch > release-notes.md - -### Specify a start date - - php generate-release-notes.php github username:password org:repo-name:branch 1/30/2014 > release-notes.md - -### Specify a start date and number of PRs - - php generate-release-notes.php github username:password org:repo-name:branch 1/30/2014 50 > release-notes.md - - # Example: Commit Message diff --git a/scripts/release-notes/generate-release-notes.md b/scripts/release-notes/generate-release-notes.md deleted file mode 100644 index 87c1b9e20..000000000 --- a/scripts/release-notes/generate-release-notes.md +++ /dev/null @@ -1,32 +0,0 @@ -# Generate Release Notes Script - -## Overview -This script compiles PR comments for a project into a Markdown file that can be copy and pasted into Github release notes or a Confluence Page. - -## Usage - -### Github - -#### Simple Usage -`php generate-release-notes.php github [github username]:[github password] [github project]:[github repository]:[branch] > release-notes.md` -`php generate-release-notes.php github dan:password acquia-pso:client-repo:master > release-notes.md` - -### Specify a start date -`php generate-release-notes.php github dan:password acquia-pso:client-repo:master 12/31/2014 > release-notes.md` - -### Specify a start date and number of PRs -`php generate-release-notes.php github dan:password acquia-pso:client-repo:master 12/31/2014 15 > release-notes.md` - -### Stash and Confluence -Since stash is hosted by a client or service, you must pass in the base path, for example `stash.client.com`. You should not include `https://`. - -#### Simple Usage -`php generate-release-notes.php stash:[base path] [stash username]:[stash password] [stash project]:[stash repository]:[branch] > release-notes.md` -`php generate-release-notes.php stash:stash.client.com dan:password client-project:client-repo:master > release-notes.md` - -### Specify a start date -`php generate-release-notes.php stash:stash.client.com dan:password client-project:client-repo:master 12/31/2014 > release-notes.md` - -### Specify a start date and number of PRs -`php generate-release-notes.php stash:stash.client.com dan:password client-project:client-repo:master 12/31/2014 15 > release-notes.md` - diff --git a/scripts/release-notes/generate-release-notes.php b/scripts/release-notes/generate-release-notes.php deleted file mode 100644 index a80540caa..000000000 --- a/scripts/release-notes/generate-release-notes.php +++ /dev/null @@ -1,166 +0,0 @@ -<?php -if ($argc < 3) { - print 'You must pass in arguments for the following variables:' . PHP_EOL - . 'service(git or stash), username:password, project:repository:branch, [month/day/year], [limit]' . PHP_EOL . PHP_EOL - . 'example: php generate-release-notes.php github dan:password acquia-pso:test-repo:master > example-release-notes.md' . PHP_EOL - . 'example: php generate-release-notes.php github dan:password acquia-pso:test-repo:master 4/10/2014 > example-release-notes.md' . PHP_EOL - . 'example: php generate-release-notes.php stash:stash.client.com dan:password client-project:test-repo:master 4/10/2014 100 > example-release-notes.md' . PHP_EOL . PHP_EOL - . 'note: by default, PRs are gathered from 30 days previous and up to 100 PRs' . PHP_EOL; - exit; -} - -// Parse Service -$service_array = explode(':', $argv[1]); - -$service = array( - 'type' => $service_array[0], -); - -if (count($service_array) > 1) { - $service['base_path'] = $service_array[1]; -} - -unset($service_array); - -// Set Username / Password -$user_array = explode(':', $argv[2]); - -$user = array( - 'name' => $user_array[0], - 'password' => $user_array[1], -); - -unset($user_array); - -// Split Project / Repo / Branch into variables. -$repo_array = explode(':', $argv[3]); - -$repository = array( - 'project' => $repo_array[0], - 'repository' => $repo_array[1], - 'branch' => $repo_array[2], -); - -unset($repo_array); - -// Parse date. -$since = ($argc < 5) ? strtotime('30 days ago') : strtotime($argv[4]); - -// Default to pulling 100 PRs. -$limit = ($argc < 6) ? 100 : $argv[5]; - -// Print the header. -print '# Release notes for ' . date("F j, Y") . PHP_EOL . PHP_EOL; - -// Proceed based on service. -switch ($service['type']) { - case 'github': - process_github(); - break; - - case 'stash': - process_stash(); - break; -} - -function process_github() { - global $repository, $since, $limit; - - // Create a date like 2014-12-23T00:00:00Z. - $since_github = date('Y-m-d', $since) . 'T00:00:00Z'; - - // We can only get 100 results at a time, so we need to split the calls into chunks of 100. - $calls = ceil($limit / 100); - - $url = 'https://api.github.com/repos/' . $repository['project'] . '/' . $repository['repository'] . '/pulls?state=closed&since=' . $since_github . '&per_page=' . $limit . '&base=' . $repository['branch']; - - for ($page = 1; $page <= $calls; $page++) { - - $prs = fetch_pr($url . '&page=' . $page); - - // Print each Pull Request. - foreach ($prs as $pr) { - // We don't want to print PRs that are not merged. - if (is_null($pr['merged_at'])) { - continue; - } - - // Check our date is within the time period. - $closed_date = strtotime($pr['closed_at']); - - if ($closed_date < $since) { - continue; - } - - print_pr_compact($pr['title'], $pr['body'], $closed_date, $pr['html_url']); - } - } -} - -function process_stash() { - global $service, $repository, $since, $limit; - - $url = 'https://' . $service['base_path'] . '/rest/api/1.0/projects/' . $repository['project'] . '/repos/' . $repository['repository'] . '/pull-requests?state=merged' . '&limit=' . $limit . '&at=refs/heads/' . $repository['branch']; - - $json = fetch_pr($url); - - // Print each Pull Request. - foreach ($json['values'] as $pr) { - // Check our date is within the time period. - // Stash uses epoch time with milliseconds. - $closed_date = date($pr['updatedDate'] / 1000); - - if ($closed_date < $since) { - continue; - } - - $link = 'https://' . $service['base_path'] . $pr['link']['url']; - - print_pr($pr['title'], $pr['description'], $closed_date, $link); - } -} - -function fetch_pr($url) { - global $user; - // Download the json file. - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_USERPWD, $user['name'] . ':' . $user['password']); - curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); - curl_setopt($ch, CURLOPT_USERAGENT, 'Acquia-PS'); - curl_setopt($ch, CURLOPT_URL, $url); - - $json_raw = curl_exec($ch); - $chinfo = curl_getinfo($ch); - - // We bail if we don't get a successful connection. - if ($chinfo['http_code'] !== 200) { - print 'HTTP Error: ' . $chinfo['http_code'] . PHP_EOL; - print 'URL: ' . $url . PHP_EOL; - print $json_raw . PHP_EOL; - exit; - } - - curl_close($ch); - - // Decode the JSON. - return json_decode($json_raw, TRUE); -} - -function print_pr($title, $description, $date, $link) { - // Print the PR Title. - print '## ' . $title . PHP_EOL; - // Print the PR Time and URL. - print date("F j, Y", $date) . ' ([' . $link . ']' . '(' . $link . '))' . PHP_EOL . PHP_EOL; - // Print the PR Body. - print $description . PHP_EOL . PHP_EOL; -} - -function print_pr_compact($title, $description, $date, $link) { - $date_formatted = date("F j, Y", $date); - print "* $date_formatted: [$title]($link) \n"; -} - -?> diff --git a/scripts/release-notes/pull-request.example.md b/scripts/release-notes/pull-request.example.md deleted file mode 100644 index 2f85ccf3b..000000000 --- a/scripts/release-notes/pull-request.example.md +++ /dev/null @@ -1,23 +0,0 @@ -## [Jira Issue Number]: [Pull request title] - -### Changes -- [Description for non-devlepers.] -- [Another thing in the PR] - -### Technical Details -- [More technical description if needed.] - -### Affected Pages -- [Globally] -- [example url] -- [/product-finder/123] - -### Key Affected Code -- [example.php] -- [example.scss] - -### References -- [drupal.org/node/123456] - -### Notices -- [If there is an important change developers should be aware of such as a change in architecture, the need for database updates, etc, put that information here.] diff --git a/scripts/release-notes/release.example.md b/scripts/release-notes/release.example.md deleted file mode 100644 index 83d609036..000000000 --- a/scripts/release-notes/release.example.md +++ /dev/null @@ -1,13 +0,0 @@ -## Main Additions -- Addition 1 -- Addition 2 - -## Important Notices -- Notice 1 -- Notice 2 - -- - - -# Pull Requests in Detail -- - - - -_This is where you copy and paste the output from generate-release-notes.php_ diff --git a/src/Robo/Command/BltInternal.php b/src/Robo/Command/BltInternal.php new file mode 100644 index 000000000..a880e3b00 --- /dev/null +++ b/src/Robo/Command/BltInternal.php @@ -0,0 +1,217 @@ +<?php + +namespace Acquia\Blt\Robo\Command; + +use Robo\Tasks; +use GuzzleHttp\Client; + +/** + * This is project's console commands configuration for Robo task runner. + * + * @see http://robo.li/ + */ +class BltInternal extends Tasks +{ + + /** + * Generates release notes and cuts a new tag on GitHub. + * + * @command blt:release + * + * @param string $tag The tag name. E.g, 8.6.10 + * @param string $github_token A github access token + * + * @return int + * The CLI status code. + */ + public function bltRelease($tag, $github_token) + { + + $requirements_met = $this->checkCommandsExist([ + 'git', + 'github_changelog_generator', + ]); + if (!$requirements_met) { + return 1; + } + + $this->yell("This will destroy any uncommitted work on the current branch. It will also hard reset 8.x and 8.x-release to match the upstream history."); + $continue = $this->confirm("Continue?"); + + if (!$continue) { + return 0; + } + // Clean up all staged and unstaged files on current branch. + $this->_exec('git clean -fd .'); + $this->_exec('git remote update'); + $this->_exec('git reset --hard'); + + // Reset local 8.x to match upstream history of 8.x. + $this->_exec('git checkout 8.x'); + $this->_exec('git reset --hard origin/8.x'); + + // Reset local 8.x-release to match upstream history of 8.x-release. + $this->_exec('git checkout 8.x-release'); + $this->_exec('git reset --hard origin/8.x-release'); + + // Merge 8.x into 8.x-release and push. + $this->_exec('git merge 8.x'); + $this->_exec('git push origin 8.x-release'); + + $partial_release_notes = $this->generateReleaseNotes($tag, $github_token); + $trimmed_release_notes = $this->trimStartingLines($partial_release_notes, 3); + + $request_payload = [ + 'tag_name' => $tag, + 'name' => $tag, + 'target_commitish' => '8.x-release', + 'body' => $trimmed_release_notes, + 'draft' => true, + 'prerelease' => true, + ]; + + $client = new Client([ + // Base URI is used with relative requests + 'base_uri' => 'https://api.github.com/repos/acquia/blt/', + 'query' => [ + 'access_token' => $github_token, + ], + ]); + $response = $client->request('POST', 'releases', [ + 'json' => $request_payload, + ]); + if ($response->getStatusCode() != 201) { + $this->yell("Something went wrong when attempting to create release $tag."); + $this->say($response->getBody()); + } + + $response_body = json_decode($response->getBody(), TRUE); + $this->say("Release $tag has been created on GitHub: \n"); + $this->_exec("open {$response_body['html_url']}"); + } + + /** + * Update CHANGELOG.md with notes for new release. + * + * @param string $tag The tag name. E.g, 8.6.10 + * @param string $github_token A github access token + * + * @return int + * The CLI status code. + */ + public function bltReleaseNotes($tag, $github_token) { + + $requirements_met = $this->checkCommandsExist([ + 'github_changelog_generator', + ]); + if (!$requirements_met) { + return 1; + } + + $this->yell("You should execute this command on a clean, updated checkout of 8.x."); + $continue = $this->confirm("Continue?"); + + if (!$continue) { + return 0; + } + + if (!$trimmed_partial_changelog = $this->generateReleaseNotes($tag, $github_token)) { + $this->yell("Failed to generate release notes"); + return 1; + } + + // Remove first 4 lines from full changelog. + $full_changelog_filename = 'CHANGELOG.md'; + $full_changelog = file_get_contents($full_changelog_filename); + $trimmed_full_changelog = $this->trimStartingLines($full_changelog, 1); + + $new_full_changelog = $trimmed_partial_changelog . $trimmed_full_changelog; + file_put_contents($full_changelog_filename, $new_full_changelog); + + $this->say("$full_changelog_filename has been updated. Please commit and PUSH the changes."); + + return 0; + } + + /** + * Generate notes for new release. + * + * @param $tag + * @param $github_token + * + * @return int|string + * FALSE on failure, otherwise the release notes. + */ + protected function generateReleaseNotes($tag, $github_token) { + // Generate release notes. + $partial_changelog_filename = 'CHANGELOG.partial'; + if (!$this->taskExec("github_changelog_generator --token=$github_token --future-release=$tag --output=$partial_changelog_filename")->run()->wasSuccessful()) { + $this->yell("Unable to generate CHANGELOG using github_changelog_generator."); + return 1; + } + + // Remove last 3 lines from new, partial changelog. + $partial_changelog_contents = file_get_contents($partial_changelog_filename); + $trimmed_partial_changelog = $this->trimEndingLines($partial_changelog_contents, 3); + unlink($partial_changelog_filename); + + return $trimmed_partial_changelog; + } + + /** + * Trims the last $num_lines lines from end of a text string. + * + * @param string $text A string of text. + * @param int $num_lines The number of lines to trim from the end of the text. + * + * @return string + * The trimmed text. + */ + protected function trimEndingLines($text, $num_lines) { + return implode("\n", array_slice(explode("\n", $text), 0, sizeof($text) - $num_lines)); + } + + /** + * Trims the last $num_lines lines from beginning of a text string. + * + * @param string $text A string of text. + * @param int $num_lines The number of lines to trim from beginning of text. + * + * @return string + * The trimmed text. + */ + protected function trimStartingLines($text, $num_lines) { + return implode("\n", array_slice(explode("\n", $text), $num_lines)); + } + + /** + * Check if an array of commands exists on the system. + * + * @param $commands array An array of command binaries. + * + * @return bool + * TRUE if all commands exist, otherwise FALSE. + */ + protected function checkCommandsExist($commands) { + foreach ($commands as $command) { + if (!$this->commandExists($command)) { + $this->yell("Unable to find '$command' command!"); + return FALSE; + } + } + + return TRUE; + } + + /** + * Checks if a given command exists on the system. + * + * @param $command string the command binary only. E.g., "drush" or "php". + * + * @return bool + * TRUE if the command exists, otherwise FALSE. + */ + protected function commandExists($command) { + return $this->taskExec("command -v $command >/dev/null 2>&1")->run()->wasSuccessful(); + } +}