System preferences

Back

SECTOR 02.1Transmission open

GaaS: GitHub as a Service

Stop paying for a backend. You already have one, you have had it for years, and it has never sent you a bill.

GitHub is a cloud provider that forgot to publish a pricing page. Since Microsoft bought it in 2018 it shipped Actions, in-house CI/CD, a marketplace, and npm. Everything a web application needs, behind an API you already hold a token for.

Call it GaaS. GitHub as a Service. GaaS, Gaas, Gaas!

VISUAL // ARCHIVEGas Gas Gas car640×360px

The five things any web app needs

Score GitHub against the checklist you would hand any provider.

Five for five. No other free tier on the internet fills that row, and the reason is that GitHub never set out to compete for it. Free real estate.

One disclaimer before the code. I have not read the terms of service, and I am not going to. This approach maybe exceeds their intent, and that is a real risk to price rather than a joke to wave past.

RFM, end to end

Request For Maintainers is a community platform that tracks repositories asking for help. It runs entirely on the five rows above, and it is open source, so every claim here is checkable.

Hosting. The site is statically generated and served from Pages. Jekyll, create-react-app, hand-written HTML, the generator does not matter. Static does.

Database. The client reads Issues through the public GitHub API, no key required. The body holds stringified JSON. The metadata comes free.

CODE // TRANSMISSION22 LINES
// request first 13 RFM open requests that matches "vapor"
const params = [
  'repo:sospedra/rfm',
  'state:open',
  'label:search',
  'vapor',
  'in:title,body'
]
const root = 'https://api.github.com/search/issues'
const path = `${root}?q=${params.join('+')}&per_page=13`
const response = await fetch(path)
const payload: {
  items: Items,
  total_count: number
} = await response.json()
const items = payload.items.map<Request>((item) => {
  // using the body as JSON with RFM data model
  body: JSON.parse(item.body),
  // benefit from the Issue metadata as well
  createdAt: new Date(item.created_at),
  title: item.title,
})

Compute. An Action fires on a label change and opens an issue on the target repository. A trigger and a function, which is the whole definition of serverless. Pull one off the marketplace or build your own.

CODE // TRANSMISSION29 LINES
// rfm/.github/workflows/issue.yml
name: Create external issues
on:
  # run when modify issue label
  issues:
    types: [labeled]

jobs:
  create:
    # filter only those issues with the label "search"
    if: "contains(github.event.issue.labels.*.name, 'search')"
    name: Create issue
    runs-on: ubuntu-latest
    steps:
      # parse the body of the issue
      - name: vars
        id: vars
        uses: gr2m/get-json-paths-action@v1.x
      # create a new issue in the repo that needs support
      - name: create-issue
        # notice that logic operators are available
        if: steps.vars.outputs.requestIssueFullName == 'NONE'
        uses: maxkomarychev/oction-create-issue@v0.7.1
        with:
          token: ${{ secrets.RFM_BOT }}
          title: 🚧 Is this repo looking for support?
          owner: ${{ steps.vars.outputs.owner }}
          repo: ${{ steps.vars.outputs.name }}
          body: Heeenlo hoomans!

Writes. Users submit through GitHub query parameters, so the browser never holds a credential. new-github-issue-url does the encoding, because Hooman Paws™️ are to be avoided wherever possible.

CODE // TRANSMISSION10 LINES
const createGithubIssue = (request?: SubmitRequest) => {
  if (!request) return ''
  return newGithubIssueUrl({
    body: JSON.stringify(request, null, 4),
    labels: ['search'],
    repo: 'rfm',
    title: request.fullName,
    user: 'sospedra',
  })
}

Access control. Actions run only on labelled issues, and only moderators assign labels. That single constraint is what keeps RFM from becoming a spam pipe.

What it costs you

The free tier is real. So are the four bills that arrive later.

  1. Rate limits. The unauthenticated search API allows 10 requests per minute. That is your ceiling and it arrives sooner than you expect.
  2. No transactions, no indexes, no schema. A partial write leaves you inconsistent with no way to detect it.
  3. Every row is world-readable. Which rules out anything with a user in it 🌝.
  4. None of this is a supported product. The deprecation that breaks you will not ship with a migration guide.

Survives all four: open-source tooling, public registries, anything where the data was always going to be public anyway. Issues as blog comments has run on this bet for years and still holds.

Does not: your company.

GitHub will never send you an invoice for this. That is the entire feature, and the entire risk.