Because who doesn't want good boys to show up whenever they push?
The long awaited GitHub Action feature is finally out of beta and ready to be present in every repositories. GitHub even organized a Hackathon through out March to encourage folks to create more awesome and useful actions. While browsing through the submissions, I found a cool GitHub Action that posts cat gifs on pull requests. Shout out to Ruairidh with his cool idea 👏👏👏.
It'll only do justice to dogs if there's an action that deliver them good boys to our PRs. That moment I knew exactly what my next Github Action project would be. Time to get to work.
GitHub actions are basically premade (with ❤️) units of work to be used GitHub workflows (think Travis's builds). Github actions can be either built with Docker containers or JS/TS scripts. An advantage to creating a GitHub action with JS/TS is readily available modules from GitHub toolkit. With such integration support, it's much easier to connect with GitHub services (C'mon 🙄 Who wanna write
curl
commands to make API calls). It's obvious to go with TypeScript action template. With that decision made, let's get down to writing action-dogs.In a JS/TS GitHub action, workload will be started out from a main entrypoint (think running
node src/index.js
to start a Node process for web applications, etc). For action-dogs, this is my basic setup for the main programimport * as core from "@actions/core";
import * as github from "@actions/github";
import { generate } from "./doggo/generator";
(async function run(): Promise<void> {
try {
const ctx = github.context;
if (!ctx.payload.pull_request) {
throw new Error("Not in the context of a PR!");
}
const ghCli = new github.GitHub(core.getInput("github-token"));
const doggo = generate();
ghCli.issues.createComment({
...ctx.repo,
issue_number: ctx.payload.pull_request.number,
body: `![Doggo](${doggo})`
});
} catch (e) {
core.setFailed(e.message);
}
})();
During an event that can trigger GitHub workflows, we are provided with a context object that can be accessed through
@actions/github
module. Using that, I'm able to check whether my payload is coming from a pull_request
and reject otherwise. Next up, I need to post a comment to the corresponding pull request with content of a doggo gif. Given that my doggo generator (of which I will explain in the next section) works properly, I can retrieve an URL of a doggo gif, creating a comment on pull requests is super simple as I just need to pass in the repo's information from our context object and the PR's number. Also, in case we get any errors during these operations, calling core.setFailed(e.message)
will mark the build as failed with the error message.After much research with the intention to use one of the public APIs to get random doggo gifs, I couldn't find one that's public (like dog.ceo) and also serves gifs (like GIPHY). Since there's no avenue for me to store my GIPHY API key securely to be used in action-dogs, I fell back to the good-ol-way of static JSON array.
Wanna know how I obtained my array full of doggo awesomeness (from ❤️ GIPHY ❤️) without any API key generated in the process? I actually went to GIPHY site, searched for doggoes, and scrolled down util a good amount of "giffy-boys" got rendered before pulling up my console
And with these few lines of JS
const dogsData = [];
document
.querySelectorAll("a._2SwDiFPqIlZmUDkxHNOeqU")
.forEach(e => dogsData.push(e.href));
var dataStr =
"data:text/json;charset=utf-8," +
encodeURIComponent(JSON.stringify(dogsData));
var dlAnchorElem = document.createElement("a");
dlAnchorElem.setAttribute("href", dataStr);
dlAnchorElem.setAttribute("download", "dogs.json");
dlAnchorElem.click();
which basically grab `href` values from all "copy link" elements on the search results page, stream them to a JSON array and fill in a file for me to "download", "generating" is simply picking a random URL from the array.
import dogs from "./dogs.json";
export function generate(): string {
return dogs[Math.floor(Math.random() * dogs.length)];
}
I wrote a unit test for my doggo generator using jest (but actually mainly as an avenue for funny descriptions).
import { generate } from "../../src/doggo/generator";
describe("doggo generator", () => {
test("to return a good boy", () => {
Math.random = jest.fn().mockReturnValue(0);
const good = "https://media3.giphy.com/media/mCRJDo24UvJMA/giphy.gif";
const boy = generate();
expect(boy).toBe(good);
});
});
But the real test is a workflow using
action-dogs
itself (Yes, you can use a GitHub action on its own repo 🤯).name: "doggo"
on: pull_request
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: stanleynguyen/action-dogs@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
Now I can see
action-dogs
in action with a sample PR. Hurray 🙌🙌🙌!! Now I can safely publish it on GitHub Marketplace.So that's my story of creating
action-dogs
for fun and learning. You can find source code right on GitHub (well, because where else could it be 🤷♂️) and action-dogs
on Marketplace.