uid / gitstream-exercises

Exercises used by GitStream
MIT License
1 stars 2 forks source link

GitStream Exercises

Exercises for use in the GitStream interactive Git tutor.

Table of Contents

  1. Creating a New Exercise
  2. Building the Exercises
  3. Configuration File Format
    1. conf.js template
    2. global
    3. machine
      1. Events and Callbacks
      2. Events Types
      3. Utility Methods
    4. viewer
    5. repo
  4. Exercise Debugging Workflow

Creating a New Exercise

  1. Start by creating a new directory for the exercise in src/exercises. This will be the name of the repository that users clone.
    The name should have the format N-exerciseName where N is a number used to order the exercises. Omitting the N- will cause the exercise to be ignored by GitStream.
  2. In the new exercise directory, create a configuration file, conf.js.
  3. If your exercise requires additional files (perhaps for committing to the exercise repo or comparing with files in the repo), create the resources subdirectory.

Great! Now you're ready to configure the exercise! Add any necessary resources to the resources/ folder and then continue on to populating the conf file.

Building the Exercises

When you are finished designing an exercise, build them by running

$ ./createx.js 

Remember to either link your gitstream-exercises directory into GitStream's or edit the GitStream package.json to point gitstream-exercises to your own repo!

Configuration File Format

conf.js template

The following is a minimal template for the conf.js file.

'use strict'

// place constants and static functions here

module.exports = {
    // conf that applies to exercise (in general)
    global: {
    },

    // description of server-side state machine
    machine: {
        startState: 'aDoneState', // name of the start state
        aDoneState: null          // null means done
    },

    // description of what the user sees in-browser
    viewer: {
        title: 'The title of the exercise',
        steps: {
            // HTML descriptions of the goal of each step
        },
        feedback: {
            // HTML shown beneath the step when
            // transitioning from one state to another
        }
    },

    // optional: conf for custom generation of exercise repo
    repo: {
        // description of the commits to be made
    }
}

global

machine

startState:String - The name of the state (step) on which the exercise starts. This state must be present in the machine object.

Other than the startState, keys are the names of states and their values can be one of the following:

Events and Callbacks

For every event (except 404 and receive), there are two options for registering callbacks. The first and most common mode is as a listener and the second is as a handler. The difference is that the handler is called synchronously (i.e. the exercise will not continue until the handler calls the provided callbacks or times out).

The key name in the state object and callback function signature is as follows:

For listeners:

onEventName: function( repo, action, info, done )

For handlers:

handleEventName: function( repo, action, info, gitDone, stepDone )

Event types

Event Description info
Clone Happens when a user clones the already-created repository.
PostCheckout* Happens when a user checks out a ref prevHead:String - the ref of the prevous HEAD
newHead:String - the ref of the new HEAD
chBranch:Boolean - whether the branche changed
(Pre|Post)Commit* Happens after the user enters a commit message but before the commit is recorded or after the commit. PreCommit works well as a handler since cancelling the event prevents the commit from being recorded. msg:String - the full text of the log message (may contain comments inserted by Git)
(Pre)Info Happens when Git requests information about a remote repo. Notably, this occurs before a pull.
Merge Happens after a non-conflicting merge wasSquash:Boolean - whether the merge was a squash
(Pre)Pull Happens before or after a pull (and before Merge). This is another good place to use a handler. Note that local Git may display unexpected error messages when the remote changes between an info and the actual operation. head:String - the requested HEAD
(Pre)Push Happens before or after a push (and before PreReceive). A handler here can stop a push from ever reaching the remote. last:String - The oldest commit in the push
head:String - The newest commit in the push
branch:String - The name of the pushed-to branch
(Pre)Rebase Occurs before a rebase (TODO: this should forward along Git-provided data)
(Pre)Receive Happens before or after the remote repo receives a push (and after the PrePush). This is another good place to use a handler, except if trying to make a commit that changes the location of the requested HEAD, which causes an "unable to lock" error. name:String - name of the updated ref
old:String - old SHA pointed to by the ref
new:String - new SHA pointed to by the ref

* Updates the Shadowbranch, a ref containing the contents of the repository.

Note: tag events can also be detected, but it requires a fix that is in a seperate PR branch (let me know if you want it merged into master).

Utility Methods

The this of an event callback contains several helpful methods for interacting with the repo and automating common tasks (see the docs).

viewer

repo

Note: templating is done using Mustache syntax.

Caveats

Exercise Debugging Workflow

  1. Edit the exercise
  2. Build the exercises
  3. If you changed a viewer section, restart the GitStream server and view your changes via make run