On September 1, 2022, GitHub Learning Lab will shut down.
Read more on the GitHub blog and check out GitHub Skills for courses backed by GitHub Actions.

Building: Using Learning Lab actions

GitHub Learning Lab is no longer accepting new public courses from outside of GitHub.

GitHub Learning Lab's mascot A course on GitHub Learning Lab can guide you through this step.

When a learner triggers a step via an event, Learning Lab will execute that step's actions. Actions are abstractions of one or more GitHub API endpoints. Currently available actions are documented in Available actions. Most API endpoints accessible by a GitHub App could be built into an action in GitHub Learning Lab.

Note: Actions in GitHub Learning Lab aren't the same as GitHub Actions.

How to use an action

When using an action in a step, start with the type: key followed by the name of the action. For example:

- type: respond
  with: my-response.md

Whenever possible, actions will infer context from the event that triggered the step. Sometimes, actions have additional requirements that are listed in the action documentation.

For example:

  • the respond action requires a with option pointing to the Markdown file containing the body of the response.
  • the respond action allows the optional use of an issue option, allowing you to specify a specific issue or pull request in which to respond. If the issue or pull request isn't supplied, the action will try to respond based on the event that triggered it. If the event that triggered the action doesn't contain an issue or a pull request (for example, in the case of a page_build event), the action will fail and the step won't be completed.

Passing data into responses

Responses are usually static content. Sometimes, you'll want to inject variable data into the response. For example, you may want to:

  • Reference a particular about something the learner did, like the title of an issue they opened
  • Refer to the learner by name
  • Surface information visible to Learning Lab that isn't obvious to the learner

To pass data into the responses, include a data object. Within the data object, include a name and value for each variable you want to pass.

For example:

- title: Commit a file
  description: Commit your file to the branch.
  event: push
  link: '{{ repoUrl }}/issues/2'
  - type: respond
    issue: Your first contribution
    with: open-a-pr.md
    # the data object
      # we're passing a variable called `branch`, it contains the ref from the payload
      branch: '{{ payload.ref }}'
      # we're passing a second variable called `url`, it contains the repository's URL concatenated with some additional text
      url: '{{ payload.repository.html_url }}/compare/{{ payload.ref }}?expand=1'

This data will be accessible in the response file with {{ branch }} and {{ url }}.

Accessing the result of executing an action

When an action is executed, the response from the GitHub API is accessible to the course author, if they wish to use it.

You might want to use the response from the GitHub API for a few reasons, like:

  • providing links to newly created issues in older issues
  • using an action to make a query, and later using that response from that query to take further action
  • taking an action, and using a conditional gate to decide future actions

To access the response from the GitHub API after executing an action, append an action_id key to your action, as follows:

  - title: Comment on this issue
    event: issue_comment.created
      - type: createIssue
        title: New issue
        body: an-issue.md
        # Store the API response for this new issue by assigning it to an action_id
        action_id: new_issue

To use the information from the action, use the '{{ actions }}' object and dot notation with the identifier for the action, as follows:

  - title: Comment on this issue
    event: issue_comment.created
      - type: createIssue
        title: New issue
        body: an-issue.md
        # Store the response for this new issue by assigning an action_id
        action_id: new_issue

        # Respond in the issue the user just commented on
      - type: respond
        with: a-response.md
        # Include a `data` object to provide some template variables
          # Reference the response of the `new_issue` action
          nextIssueUrl: '{{ actions.new_issue.data.html_url }}'

Marking an action as optional

Most of the time, you'll expect the action to run before a step can be marked as completed. However, there are instances when you want to run an action, but if that action fails you'd like the learner to be able to continue the course. In these cases, you can use a required option for that action.

Here's an example:


        # get the contents of a file titled index.html, store them in an action identifier called `index_file`
      - type: getFileContents
        filename: index.html
        action_id: index_file

        # check if `index_file` contains an <html> tag, store the response in an action identifier called `contains_html`. If the action fails (because, say, no <html> tag exists), don't stop the course from executing. We'll use the information to provide the learner feedback
      - type: htmlContainsTag
        html: '{{ actions.index_file }}'
        tag: html
        action_id: contains_html
        required: false

        # Respond to the learner conditionally using `contains_html`.
      - type: gate
        left: '{{ actions.contains_html }}'
          type: createReview
          body: 03e-add-html.md
          event: REQUEST_CHANGES
      - type: createReview
        event: COMMENT
        body: 03-title-tag.md

Storing information

You can store information that your steps' actions return in a key/value store that is scoped to the user's registration. You may want to use this to create an issue in step 1, then reference it in a much later step.

- type: createIssue
  title: Creating this issue!
    first_issue_url: '{{ result.data.html_url }}'
    first_issue_number: '{{ result.data.number }}'

Some important notes on what you can store:

  • The information you can store is limited to strings, numbers, and booleans - so you cannot store an entire response object. If your use case requires the entire response object, use the action_id property.
  • Attempting to store a key that already exists will throw an error and cause that step to fail. This is to avoid accidentally overwriting stored data.

In the store object, you'll have access to a special result object; this is the direct return value of the action. You can read through the actions' source code to see what those values are.

And then you can use it later on:

  # In a step link
  link: '{{ store.first_issue_url }}'

  # Or in an action
    - type: gate
      left: '{{ store.first_issue_number }}'
      operator: ===
      right: '{{ payload.issue.number }}'

You can also use the store object from within a markdown response template:

Check out issue number [{{ store.first_issue_number }}]({{ store.first_issue_url }})!

Calling any API endpoint using octokit

If an action you require isn't available, the octokit action lets you access most any GitHub API endpoint, as long as it is enabled for GitHub Apps.

In the API docs, endpoints accessible to GitHub Apps are identified by a ℹ️ icon. Here's an example of an endpoint that's enabled for GitHub Apps and an endpoint that isn't.

Contributing to available actions

If you're interested in contributing to the evolution of Learning Lab's actions, we would love to collaborate with you in the open source github/learning-lab-components repository!