GitHub Gists are extremely handy for sharing self-contained chunks of information like code snippets, short scripts and even complete articles.
However, I recently found myself working behind a corporate proxy which blocked access to gists, probably because of data loss prevention concerns. As a result, when searching online for answers I would often come across promising looking gists in my search results, but would also be frustratingly unable to view their contents.
The solution
After some investigation, I noticed that while gist.github.com
was blocked, other GitHub domains such as gist.githubusercontent.com
or api.github.com
were not. This was promising, because the former is where the raw content for gists is hosted, and the latter is the base URL for the GitHub REST API through which gists can be accessed as well.
I ended up building a simple static page which takes a gist ID or URL and grabs its contents from the GitHub API to display:
This allows you to view gists even if gist.github.com
is blocked, and, since it's read only, hopefully doesn't run afoul of DLP policies (just in case, I also made sure not to mention “proxy” on the page).
Additional features
- Shareable URLs. By using the URL's fragment identifier—the part starting with a
#
symbol—Essence URLs can be shared as-is and will directly load the same gist, for example: 89d5530b693dcde89fdfa9d4dd421863. - Markdown formatting. Using Marked and GitHub's own Primer Markdown styles, Markdown gists are formatted and look just like on GitHub: 838cb4c84ff6dc45620baf26bad0e231.
- Bookmarklet. Using a bookmarklet which automatically extracts the gist ID from the current page and redirects to Essence, you can open the blocked gist URL first and then view it with one click.
Possible improvements
- Display more gist information. Right now some fields such as author and date published are noticably missing, and it might be nice to see other details such as number of stars and view comments.
- Handle large gists. The GitHub API only includes up to 1 MB of individual file contents inline and only list up to 300 files in a gist (although how often do you need to open such large gists anyway?).
Source
Find Essence on GitHub:
Things I learnt along the way
Anatomy of a raw gist URL
The raw contents of a file in a gist can be found under https://gist.githubusercontent.com
at (:filename
can be omitted for the first or only file in a gist):
/:username/:gist_id/raw/:commit_hash/:filename
/:username/:gist_id/raw/:blob/:filename
/:username/:gist_id/raw/:filename
(points to the latest revision)
For example,
https://gist.githubusercontent.com/yi-jiayu/89d5530b693dcde89fdfa9d4dd421863/raw/265fe21e0930c18dace1f8da4941fbbe47ba4905/benford.ipynb
https://gist.githubusercontent.com/yi-jiayu/89d5530b693dcde89fdfa9d4dd421863/raw/265fe21e0930c18dace1f8da4941fbbe47ba4905/
https://gist.githubusercontent.com/yi-jiayu/89d5530b693dcde89fdfa9d4dd421863/raw/c16ad4ac4597a57876ded6e1288e89efd8e4f532/benford.ipynb
https://gist.githubusercontent.com/yi-jiayu/89d5530b693dcde89fdfa9d4dd421863/raw/c16ad4ac4597a57876ded6e1288e89efd8e4f532/
https://gist.githubusercontent.com/yi-jiayu/89d5530b693dcde89fdfa9d4dd421863/raw/benford.ipynb
https://gist.githubusercontent.com/yi-jiayu/89d5530b693dcde89fdfa9d4dd421863/raw
all lead to the same file.
Looking up Git objects by hash
While looking into the significance of the various parts of the raw gist URLs, I learnt about the git-cat-file
command:
$ tmpdir=$(mktemp -d)
$ git clone https://gist.github.com/89d5530b693dcde89fdfa9d4dd421863.git $tmpdir
Cloning into '/tmp/tmp.5O8PrhMFSz'...
remote: Counting objects: 27, done.
remote: Total 27 (delta 0), reused 0 (delta 0), pack-reused 27
Unpacking objects: 100% (27/27), done.
$ cd $tmpdir
$ git cat-file -t 265fe21e0930c18dace1f8da4941fbbe47ba4905
commit
$ git cat-file -p 265fe21e0930c18dace1f8da4941fbbe47ba4905
tree cc3dd4db2bada8494b9718b0d76ac09624074bc8
parent c6b728a0f7e08c8677d133e62fba3cfe35d71b1c
author Jiayu Yi <yi-jiayu@users.noreply.github.com> 1526828087 +0800
committer GitHub <noreply@github.com> 1526828087 +0800
$ git cat-file -t c16ad4ac4597a57876ded6e1288e89efd8e4f532
blob
$ git cat-file -p c16ad4ac4597a57876ded6e1288e89efd8e4f532
{
"cells": [
{
...
"nbformat": 4,
"nbformat_minor": 2
}
$
HTML5 form data validation
I got stuck on this for a while wondering why my regular expression to match a GitHub gist ID:
$ node
> const regexp = /(?:^|\/)([a-f0-9]+)$/
undefined
> regexp.exec('https://gist.github.com/yi-jiayu/89d5530b693dcde89fdfa9d4dd421863')
[ '/89d5530b693dcde89fdfa9d4dd421863',
'89d5530b693dcde89fdfa9d4dd421863',
index: 32,
input: 'https://gist.github.com/yi-jiayu/89d5530b693dcde89fdfa9d4dd421863' ]
> regexp.exec('89d5530b693dcde89fdfa9d4dd421863')
[ '89d5530b693dcde89fdfa9d4dd421863',
'89d5530b693dcde89fdfa9d4dd421863',
index: 0,
input: '89d5530b693dcde89fdfa9d4dd421863' ]
> regexp.exec('https://blog.jiayu.co/2018/06/an-alternative-github-gist-viewer/')
null
> regexp.exec('not-a-gist-id')
null
>
$
wasn't working in my <input>
element's pattern
attribute:
<input type="text" class="form-control form-control-lg" id="gist_id_or_url_input"
placeholder="https://gist.github.com/0123456789abcdef" required pattern="(?:^|/)([a-f0-9]+)$"
title="A gist ID, or the last segment of a gist URL, should be a lowercase hexadecimal string.">
I later found out that it was because the regexp in the pattern
attribute must match the entire input value for it to be considered valid. From the current version of the HTML5 specification (HTML 5.2):
This implies that the regular expression language used for this attribute is the same as that used in JavaScript, except that the
pattern
attribute is matched against the entire value, not just any subset (somewhat as if it implied a^(?:
at the start of the pattern and a)$
at the end).
To fix this, I rewrote my regular expression from (?:^|/)([a-f0-9]+)$
to (?:.*/)?([a-f0-9]+)
, consuming the leading non-ID part of a gist URL and leaving out the implied ^
and $
anchors.
The form submit
event
Due to my lack of experience with HTML, forms and JavaScript, I initially handled the form submission with a click
event handler on the submit button. It was only after I wondered why the form validation didn't work that I realised I should listen to the submit
event on the form element itself instead.
Appendix
The site on GitHub Pages:
Essence - Gist ViewerAn alternative GitHub gist viewerhttps://yi-jiayu.github.io/essence/
GitHub repository:
yi-jiayu/essenceView GitHub gists behind a proxyhttps://github.com/yi-jiayu/essence