Continuous Release on Github with Grunt

TL:DR; Just show me the Gruntfile!

This post will describe the release tasks I’ve written for Grunt which work seamlessly with our existing continuous deployment pipeline on Codeship. I also mention a step you can take if you want to do this directly from your command line instead.

I help develop WordPress themes and plugins for Texas A&M AgriLife, which are available on Github. Earlier this year, I was asked to develop and implement a continuous deployment pipeline for most of our repositories. I wrote an article about it here. Long story short, we use Codeship to watch our repository branches (staging and master) and run console commands to prep and push them to the staging and production versions of our servers.

I was recently asked to create a release for our latest theme AgriFlex3, and in hope I looked for articles on how to do so with Grunt. I soon found this article by Paul C. Pederson which showed me this approach was possible, but I needed something a little different. What follows are Grunt modules and commands based on those I use to package and release our open-source projects on Github with a release message that describes all of the commits made since the previous release.


grunt-contrib-compress

This is the simplest task. You provide the archive file name and an array of file globs to copy into it. We try to use values from package.json when we can. View the grunt-contrib-compress repository for more information.

  compress:
  main:
    options:
      archive: '<%= pkg.name %>.zip'
    files: [
      {src: ['css/*.css']},
      {src: ['img/**']},
      {src: ['js/*.js']},
      {src: ['**/*.php']},
      {src: ['*.php']},
      {src: ['README.md']},
      {src: ['screenshot.png']}
    ]


grunt-gh-release

This task creates our release using properties within package.json, a Github access token available in our Codeship environment, and a few custom tasks (shown in the next section) that populate the release message beforehand. If you want to run your release task locally then use the documentation’s method for the options token instead of what is shown here. View the grunt-gh-release repository for more information.

  gh_release:
  options:
    token: process.env.GITHUB_PERSONAL_ACCESS_TOKEN
    owner: 'agrilife'
    repo: '<%= pkg.name %>'
  release:
    tag_name: '<%= pkg.version %>'
    target_commitish: 'master'
    name: 'Release'
    body: 'First release'
    draft: false
    prerelease: false
    asset:
      name: '<%= pkg.name %>.zip'
      file: '<%= pkg.name %>.zip'
      'Content-Type': 'application/zip'


Custom tasks

The two custom tasks I’ve written will run console commands and handle their output. The first task gets the last release tag and uses it to set a config value as the range of commit messages we want to retrieve. The second task takes this range and uses it to populate the release message with a bulleted list of commits below their author’s name. This is necessary because the github token is linked to a person and that name shows near release messages. We want to ensure they are not seen by users as the author of all released commits.

  @registerTask 'setreleasemsg', 'Set release message as range of commits', ->
  done = @async()
  grunt.util.spawn {
    cmd: 'git'
    args: [ 'tag' ]
  }, (err, result, code) ->
    if(result.stdout!='')
      # Get last tag in the results
      matches = result.stdout.match(/([^\n]+)$/)
      # Set commit message timeline
      releaserange = matches[1] + '..HEAD'
      grunt.config.set 'releaserange', releaserange
      # Run the next task
      grunt.task.run('shortlog');
    done(err)
    return
  return

  @registerTask 'shortlog', 'Set gh_release body with commit messages since last release', ->
  done = @async()
  grunt.util.spawn {
    cmd: 'git'
    args: ['shortlog', grunt.config.get('releaserange'), '--no-merges']
  }, (err, result, code) ->
    if(result.stdout != '')
      # Hyphenate commit messages
      message = result.stdout.replace(/(\n)\s\s+/g, '$1- ')
      # Set release message
      grunt.config 'gh_release.release.body', message
    else
      # Just in case merges are the only commit
      grunt.config 'gh_release.release.body', 'release'
    done(err)
    return
  return

If this helps you, or if you have any suggestions for improvements, please leave a comment!


Gruntfile.coffee

  module.exports = (grunt) ->
  @initConfig
    pkg: @file.readJSON('package.json')
    compress:
      main:
        options:
          archive: '<%= pkg.name %>.zip'
        files: [
          {src: ['css/*.css']},
          {src: ['img/**']},
          {src: ['js/*.js']},
          {src: ['**/*.php']},
          {src: ['*.php']},
          {src: ['README.md']},
          {src: ['screenshot.png']}
        ]
    gh_release:
      options:
        token: process.env.GITHUB_PERSONAL_ACCESS_TOKEN
        owner: 'agrilife'
        repo: '<%= pkg.name %>'
      release:
        tag_name: '<%= pkg.version %>'
        target_commitish: 'master'
        name: 'Release'
        body: 'First release'
        draft: false
        prerelease: false
        asset:
          name: '<%= pkg.name %>.zip'
          file: '<%= pkg.name %>.zip'
          'Content-Type': 'application/zip'

  @loadNpmTasks 'grunt-contrib-compress'
  @loadNpmTasks 'grunt-gh-release'

  @registerTask 'release', ['compress', 'setreleasemsg', 'gh_release']
  @registerTask 'setreleasemsg', 'Set release message as range of commits', ->
    done = @async()
    grunt.util.spawn {
      cmd: 'git'
      args: [ 'tag' ]
    }, (err, result, code) ->
      if(result.stdout!='')
        # Get last tag in the results
        matches = result.stdout.match(/([^\n]+)$/)
        # Set commit message timeline
        releaserange = matches[1] + '..HEAD'
        grunt.config.set 'releaserange', releaserange
        # Run the next task
        grunt.task.run('shortlog');
      done(err)
      return
    return
  @registerTask 'shortlog', 'Set gh_release body with commit messages since last release', ->
    done = @async()
    grunt.util.spawn {
      cmd: 'git'
      args: ['shortlog', grunt.config.get('releaserange'), '--no-merges']
    }, (err, result, code) ->
      if(result.stdout != '')
        # Hyphenate commit messages
        message = result.stdout.replace(/(\n)\s\s+/g, '$1- ')
        # Set release message
        grunt.config 'gh_release.release.body', message
      else
        # Just in case merges are the only commit
        grunt.config 'gh_release.release.body', 'release'
      done(err)
      return
    return

Making a trellis and vertical planter

I’ve spent this year adding as much vegetation to my back yard as possible, and I finally decided on a cheap way to tame the Molokai sweet potato vines. I bought a few hundred feet of galvanized wire for fencing and some corner brackets it could slip through. This is affordable, looks ok, and ensures I only need to screw into pieces of wood that are renter-friendly (replaceable).

I designed this vertical planter for low cost, sufficient soil per row, good drainage, and the exact space I had for it.

Progress on game

Sadly, not much progress was made this week. I took a day off of work on Monday and spent 6 hours trying to get the food absorption mechanic to work, and only found ways it didn’t. The way it works now, on trigger enter, a 2d spring joint is programmatically added to the player and linked to the food. Then the player transform is assigned as the parent transform of the food, and the 2d joint is disabled. The food’s transform is moved into the player until it’s outer edge is 10% away from the player’s edge. I wish I could avoid using the joint, but when I do not use it what I can only describe as a sudden burst of force is created near the collision between the player and the food.

  1. Converted weight from tons to pounds.
  2. Restored eating functionality. I will revisit this solution later since it seems like a poor one.
  3. Began work on a food dispenser that uses object pooling.

Weekly progress

I’ve refined the prototype, reformatted my desktop PC so it performs like it should, moved my home office stuff around to make using the desktop easier, and moved my second widescreen monitor back to it so I can have tutorials up while putting them to use.

Improvements to the prototype:

1. Shift center of mass of equipment to player’s center to keep it from affecting rotation during movement. This will probably change since heavier weapons should rightly have their center of mass in the heavier area.
2. Replace hinge joints with fixed joints

3. Made a halberd, buzzsaw, and poop graphics. The halberd is in the scene.

4. Made a way to resize the player when it takes damage or eats, but it isn’t working correctly when eating.

5. Added a key that detaches equipment, but needs user feedback.

6. Added layers to separate collision between game objects.

Starting my first video game!

I am a WordPress developer at Texas A&M, with 9 years of experience designing and programming websites and a degree in studio art (focusing on graphic design) from Angelo State University. Last year I worked with two other people on a small game for Android named Army Hero VR. When we finished the project, I started to plan my own game – an adventure puzzle game with a scope and complexity that I was fairly confident I could accomplish on my own if needed. Within these restrictions, I developed an interesting story and the core mechanics, and then began work on the prototype. I am still refining it, and while the prototype will be in 2D I am making it in a way that will (hopefully) not be difficult to translate to 3D.

I do not want to reveal much of the story here, since that is best experienced in-game and may change at any time. I can say that it is an allegory for coping with disability, and – as far as I am able – I will program ways for people with special needs to be able to play it.

So yeah, not much to this post, and I may create some about the work I’ve already done, but hopefully other devs can learn from what I’m doing and vice versa!