Streamlining Contributions to WordPress Core via GitHub

Authored by

In Contributing to WordPress Core via GitHub, I detailed the somewhat-tedious workflow that I’ve been using for developing and collaborating on patches that get manually uploaded to Trac. Now that the topic of using GitHub for Core contributions is again up for discussion at the WordPress Community Summit, I wanted to jot down some ideas for how to automate the workflow I’ve been using; I’ll be looking specifically at what a streamlined workflow for contributions looks like via a GitHub fork of the WordPress repo managed by a component maintainer, such as our XWP clone. (For how to set up a GitHub clone of develop.git.wordpress.org, see my previous post.)

Caveat: This is intended for users in trusted contributor teams as it requires the user to open an internal (intra-repo) pull request from feature branch to master. It will not work when opening a pull request from a fork due to Travis CI’s security restrictions on environment variables. So any pull requests from external contributors will need to be manually applied to a feature branch by a repo contributor and then open an intra-repo pull request (while closing the original inter-repo pull request).

I’ll list out the steps in the streamlined patch development workflow that I’m thinking about, along with how GitHub and Travis CI should be configured to support the workflow. To quickly summarize the steps that a user would take:

  1. Ensure that a Trac ticket exists.
  2. Push commits to a feature branch on GitHub named after the Trac ticket (e.g. trac-12345).
  3. Open pull request to master.
  4. Watch Travis CI run its tests, and when completed look at Trac to see the patch uploaded.

And that’s the contributor workflow.

And now for the devops implementation details…

0) Ensure that a Trac ticket exists

The first step is to make sure that a Trac ticket exists for the patch you want to contribute. This is key so that there is a ticket number that can be referenced from GitHub.

1) Create feature branch

Given a Trac ticket with the number “12345”, create a branch off of master called trac-12345. The branches could alternatively be called trac/12345. Also, if there are multiple branches for a given Trac ticket, some additional branch name identifier could be added, such as:

  • trac-12345-redux
  • trac-12345-plan-b
  • trac-12345-add-customize-link-to-post-edit-page

They key thing is to make sure that “trac” and “12345” are included in the branch name.

Once the branch is created, push the desired commits to the branch. See also a prototype git-apply-core-trac-patches command which can create a new branch and apply patches from Trac, setting the Git author for each commit to be the WordPress.org user and ensuring only the new changes introduced in each patch are part of the additional commits.

2) Open pull request

Once the desired changes are on the feature branch, or once you’re ready for the changes to be reviewed or collaborated on, open a pull request back to the master branch on your fork/clone. At the moment, there is no official mirror of the develop.git.wordpress.org repo on GitHub yet, as noted in my previous post. So there is no danger of opening the pull request back to master on another repo.

3) Travis CI runs

Travis CI should already be configured for the GitHub repo, with builds limited to pull requests, so once the pull request is opened Travis CI should do a build against the feature branch and run all of the automated tests. Note I have a ticket open for how to integrate the WordPress-Coding-Standards PHP_CodeSniffer and JSCS checks to ensure coding conventions are followed for the changes introduced in the patch. Because Travis is running in the context of a pull request, there is a discrete changeset as context instead of the entire repo. What this means is that if a pull request doesn’t have any modified PHP files, we can short-circuit PHPUnit from even running, and this drastically improves the overall build time. Additionally, because there is a diff of changes, we can run PHP_CodeSniffer and JSCS and other linting tools that are troublesome to implement globally because they report too many errors. By limiting checks to the files changes and by filtering out errors that do not relate to the pull request changeset, coding standards (e.g. PHP_CodeSniffer and JSCS) can now be checked in each pull request and only flag code that is being touched. (We could also do this outside the context of a pull request by computing a diff with the last stable release branch.)

Once Travis finishes a job in a build, it then runs any commands in its after_script config. We can use the travis-after-all library to ensure that the after_script commands here only get executed when all of the jobs are finished. With this we can use Travis CI to sync the patch from GitHub to WordPress Trac. We could alternatively sync a patch to Trac in response to a GitHub webhook, but when we use Travis we have access to the $TRAVIS_TEST_RESULT variable which indicates whether or not the build is passing or failing, and we can mention this in the patch uploaded to Trac.

In order to make use of after_script for our contributor fork of WordPress without modifying the .travis.yml file for each cange, we need to amend the .travis.yml file in the Core develop.git.wordpress.org repo right now to allow projects to run custom commands specific to a project, so for example:

--- .travis.yml
+++ .travis.yml
@@ -48,6 +48,12 @@ before_script:
 - npm install -g grunt-cli
 - npm install
 script: grunt $WP_TRAVISCI
+after_script:
+- |
+  if [[ -n "$WP_TRAVISCI_CUSTOM_AFTER_SCRIPT_SRC" ]]; then
+    wget -O /tmp/custom-after-script.sh "$WP_TRAVISCI_CUSTOM_AFTER_SCRIPT_SRC";
+    source /tmp/custom-after-script.sh;
+  fi
 notifications:
   slack:
     rooms:

With this in place, you can then add WP_TRAVISCI_CUSTOM_AFTER_SCRIPT_SRC to your Travis CI environment variables containing the URL to the Bash script to run in after_script.

The patch attached to the Trac ticket should be uploaded by a generic/bot user account, not by a user account for anyone who would specifically be authoring patches. This is so that attribution for uploading the patch is not erroneously given to the wrong user. So if I set up this GitHub-to-Trac patch sync, I shouldn’t set up Travis CI to use my “westonruter” account on WordPress.org, but instead it should a user like “xwp-bot”. The credentials can be stored in Travis environment variables, such as WPORG_USERNAME and WPORG_PASSWORD. It’s important that when adding the WPORG_PASSWORD to add it with “Display value in build log” turned off for obvious reasons:

environment-variables

Another benefit of using a bot user dedicated for uploading patches is that the security for these credentials is not as critical. (In order for Travis to be able to upload pull-request patches to Trac, XML-RPC access needs to be enabled for the Trac user. This would need to be done by a WordPress Trac admin. XML-RPC is now enabled for all Trac users, per Dev Chat Notes: June 29, 2016.)

The branch name initially chosen for the pull request is important because this is how Travis CI can know which Trac ticket to upload the patch to:

TICKET_NUMBER=$(sed 's:trac.\([0-9]*\).*:\1:' <<< "$TRAVIS_BRANCH")

If this naming convention is not followed, the patch upload should abort with an error.

As for the naming convention for patch files that Travis uploads, I suggest that it should incorporate the GitHub owner name & repo name ($TRAVIS_REPO_SLUG), the pull request number ($TRAVIS_PULL_REQUEST), the ticket number, and then the commit or commit range that is being introduced in that new patch. For example, given a Trac ticket #12345, and a pull request #876 from the xwp/wordpress-develop repo on GitHub for a feature branch trac-12345, the following patch files could be uploaded to Trac:

  • xwp.wordpress-develop.876.12345.a1b2c3.diff – if only one commit was pushed ($TRAVIS_COMMIT)
  • xwp.wordpress-develop.876.12345.a1b2c3...g5h6i7j8.diff – if multiple commits were pushed to Travis ($TRAVIS_COMMIT_RANGE)

Programmatically this could be made via:

PATCH_FILENAME=$(tr '/' '.' <<< "$TRAVIS_REPO_SLUG")
PATCH_FILENAME="$PATCH_FILENAME.$TRAVIS_PULL_REQUEST"
PATCH_FILENAME="$PATCH_FILENAME.$TICKET_NUMBER"
PATCH_FILENAME="$PATCH_FILENAME.$TRAVIS_COMMIT_RANGE"
PATCH_FILENAME="$PATCH_FILENAME..diff"

We then just need to write the diff to this file:

git diff --no-prefix "$TRAVIS_COMMIT_RANGE" < "$PATCH_FILENAME"

The last thing we need is an attachment description for the patch file. Since a bot user is uploading the file, we need to make sure the authors of the Git commit(s) are mentioned in the description so that SVN commit props can be properly given. We can get this list of author names easily via:

git log "$TRAVIS_COMMIT_RANGE" --format="%aN" | sort -u

The challenge here is that the author names listed will be actual full names as opposed to WordPress.org profile usernames. This means that Core committers would need to look up the WordPress.org usernames when composing the SVN commit message. This isn’t the best, but it’s not the end of the world, and it could be addressed by building up an author-name-to-username mapping.

Along with the list of commit authors, the file attachment description can also include the commit summary (or summaries), the URL to the pull request, the link to the commit on GitHub or commit range via the GitHub compare view. Lastly, the description can also include whether or not the Travis build passed via the $TRAVIS_TEST_RESULT variable.

Now that we have the patch file written, we can use XML-RPC to upload this file to the Trac ticket.

Note that if you want to push a commit to GitHub but do not want to trigger Travis build, you can include [ci skip] in the Git commit message.

The Trac attachments end up looking like this (via #34694):

Trac attachments

Conclusion

The above has been set up now for the xwp/wordpress-develop repo and the after_script code that handles the uploading of the patch from the GitHub pull request to WordPress.org Trac can be found in the wp-github-pull-request-travis-ci-trac-sync GitHub project, and there are installation instructions there.

Leave a Reply

Your email address will not be published. Required fields are marked *