Skip to content

Instantly share code, notes, and snippets.

@bmcbride
Last active September 11, 2024 08:13
Show Gist options
  • Save bmcbride/62600e48274961819084 to your computer and use it in GitHub Desktop.
Save bmcbride/62600e48274961819084 to your computer and use it in GitHub Desktop.
Create a new GitHub Issue from a Google Form submission

Wiring up a Google Form to GitHub is not that difficult with a little bit of Apps Script automation. All you need is a Google account, a GitHub account, and a web browser...

Set up your GitHub Personal Access Token

Personal access tokens provide an easy way to interact with the GitHub API without having to mess with OAuth. If you don't already have a personal access token with repo or public_repo access, visit your GitHub settings page and generate a new token.

Be sure to copy your token some place safe and keep it secure. Once generated, you will not be able to view or copy the token again.

Set up the Form & Spreadsheet

  1. Create a Google Form.
  2. From the Responses tab, click the More icon.
  3. Select Choose a response destination.
  4. Select New spreadsheet: Creates a new spreadsheet in Google Sheets for responses.
  5. Click Create to create and open the sheet.

Configure the App Script Logic

  1. You should have a newly created blank spreadsheet with headers automatically generated from your form.
  2. Click Tools > Script editor... to launch the App Script editor coding environment. This Script will be bound to your sheet, so you can listen for form submissions and fire off a new issue to your GitHub repo.
  3. Delete the boilerplate code in the Code.gs file and replace it with something similar to this:
var ghToken = "my-personal-access-token";

function onFormSubmit(e) {

  var title = e.values[2] + ": " + e.values[4];
  
  var body = "| Contact Email | Organization Name | Change Date | Change Type | Plan | Licenses | Comments |\n" +
             "|---|---|---|---|---|---|---|\n" +
             "| "+e.values[1]+" | "+e.values[2]+" | "+e.values[3]+" | "+e.values[4]+" | "+e.values[5]+" | "+e.values[6]+" | "+e.values[7]+" |" ;
  
  var payload = {
    "title": title,
    "body": body
  };
   
  var options = {
    "method": "POST",
    "contentType": "application/json",
    "payload": JSON.stringify(payload)
  };
  
  var response = UrlFetchApp.fetch("https://api.github.com/repos/bmcbride/my-repo/issues?access_token="+ghToken, options);
}
  1. The onFormSubmit function includes an event object e, which includes the form/spreadsheet field values as a simple array with values in the same order as they appear in the spreadsheet. e.values[0] is the first spreadsheet column (typically Timestamp).
  2. In my example, I'm collecting information from users who wish to make license changes to a web service. I'm building a simple markdown table, which includes some of the form data in the body of my issue.
  3. Reference the GitHub Issues API for a full list of options for programmatically creating a new issue.
  4. Once we've built the title and body of the issue, we can build the HTTP request using App Script's URL Fetch Service.
  5. Give your app script project a name and save it .

Set up the Trigger

  1. From within the app script editor, click Resources > Current project's triggers.
  2. Click to add a trigger
    • Run: onFormSubmit
    • Events: From spreadsheet, On form submit
  3. Click Save and accept any authorizations to access your forms and access web services on your behalf.
  4. This trigger will listen to form submissions and pass the data to your function, which POSTs the new issue to your GitHub repo.

Conclusion

This exercise demonstrates how to utilize Google Forms as a front-end for capturing information, which can then be passed on to other services, such as GitHub, CartoDB, Fulcrum, etc.

@isteiger
Copy link

also, i think this should be in googles app script samples. i can do this if you would like me to @bmcbride

@Minty123
Copy link

Minty123 commented Apr 21, 2020

does anybody know how to set the authorization in the header?

Adding the token to the header like this worked for me:

  var options = {
    "method": "POST",
    "headers": {
        "authorization": "token "+ghToken
    },
    "contentType": "application/json",
    "payload": JSON.stringify(payload)
  };

Not sure if the other parameters should also be under headers, but at least it's working.

@VocAddict
Copy link

I had placed headers outside and then reference it within the options variable, but considering headers is static all the time @Minty123's way is a bit more clean

@ScottjGardiner
Copy link

Thanks for this, can you tell me how to have the body values in rows rather tan columns?

@ppremk
Copy link

ppremk commented Oct 5, 2021

👋 Tried this out,

Some updates to the code is needed since the GitHub API now accepts authorisation token in the headers only.

  • Add authorization moved in the header
  • Change the fetch URL
  ...
  ...
  
    var options = {
        "method": "POST",
        "headers": {
            "authorization": "token "+token,
            "Accept": "application/vnd.github.v3+json",
        },
        "contentType": "application/json",
        "payload": JSON.stringify(payload)
      };

    Logger.log(payload)

    var response = UrlFetchApp.fetch("https://api.github.com/repos/"+handle+"/"+repo+"/issues", options);

Reading the log file helped to debug 🙂 also reading through feedbacks from these folks helped @Minty123 @bellerbrock

@ScottjGardiner
Copy link

thanks!

@jayteezer
Copy link

What if the GitHub enterprise server is behind a VPN?

@alifeee
Copy link

alifeee commented Apr 5, 2024

Some of the commented changes lead me to writing an updated code snippet. See it here or on my forked gist:

I store the GitHub token under Settings > Script Properties as "github-token".

It should be a fairly generic script.

let props = PropertiesService.getScriptProperties().getProperties();
let ghToken = props["github-token"];
let ghlink = "https://api.github.com/repos/myname/myrepo/issues?access_token="

function onFormSubmit(e) {
  let itemResponses = e.response.getItemResponses();
  let titles = itemResponses.map((response) => response.getItem().getTitle())
  let items = itemResponses.map((response) => response.getResponse())

  Logger.log("response items: %s", items)

  let title = items[0];

  let body = "";
  for (var i = 0; i < itemResponses.length; i++) {
    body += "### " + titles[i] + "\n\n";
    body += items[i] + "\n\n";
  }
  
  let payload = {
    "title": title,
    "body": body
  };
   
  let options = {
    "method": "POST",
    "headers": {
      "authorization": "token "+ghToken,
      "Accept": "application/vnd.github.v3+json",
    },
    "contentType": "application/json",
    "payload": JSON.stringify(payload)
  };

  Logger.log("Payload: %s", payload)
  
  var response = UrlFetchApp.fetch(ghlink + ghToken, options);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment