smokeshow

Deploy ephemeral websites.


Keywords
coverage-reports, ephemeral, http, https, pydantic, temporary-websites
License
MIT
Install
pip install smokeshow==0.3

Documentation

smokeshow

CI pypi license

smokeshow

Deploy ephemeral websites via HTTP or a CLI.

If you need to do any of the following:

  • 🚀 preview a site before launch
  • 🙈 view the HTML version of coverage reports
  • 👀 create a quick website to show someone something

smokeshow is here to help. It lets you create a static website, 1 year after the site is created, it vanishes like smoke in the wind.

What's great about smokeshow:

  • 💸 It's free
  • 🔑 You don't need to sign up, just create a key using the instructions below
  • 💨 It's super fast around the world, smokeshow uses CloudFlare's 280+ edge locations to store files meaning they're next to your users wherever they are

Usage Warning

smokeshow is currently free for anyone to use (within limits), but if it starts to cost me a significant amount, I might reduce the limits, or stop it being free. Please watch the github repo to get notifications of changes to the service if you're using it regularly or in an automated way.

smokeshow is open source so if you want to modify it and/or deploy your own instance to cloudflare workers, you can.

Usage

Uploading a site to smokeshow requires three steps:

  1. Create an upload key where a numeric representation of its sha-256 hash is less than 2 ^ 234. In other words; a simple proof of work. This key can then be used to create multiple sites.
  2. Create a new site.
  3. Upload one or more files to that site.

All three steps can be performed either the python CLI, or using manually.

CLI Usage

The command line interface (CLI) for smokeshow is written in python and available to download via pypi. Assuming you have python 3.7+ and pip installed, installing the smokeshow CLI should be as simple as:

pip install smokeshow

You can then get help on usage with:

smokeshow --help

To generate an upload key, use:

smokeshow generate-key

You should then set the key as an environment variable with

export SMOKESHOW_AUTH_KEY='...'

With that, you can upload a site with:

smokeshow upload path/to/upload

For more help run smokeshow upload --help, if you run smokeshow upload without either setting the SMOKESHOW_AUTH_KEY environment variable or using the --auth-key option, smokeshow will generate a new upload key before uploading the site.

If you're having trouble with python versions and accessing the CLI, you can also run the smokeshow library module as a script via

python -m smokeshow

GitHub actions & commit status integration

I build smokeshow primarily to preview documentation and coverage generate with github actions.

smokeshow therefore integrates directly with github actions to add a status to commits with a link to the newly created ephemeral site.

In addition, smokeshow has custom logic to extract the total coverage figure from coverage.py HTML coverage reports to both annotate commit status updates and decide if the commit status is "success" or "failure".

Example of setting the commit status from a github action:

- run: smokeshow upload cli/htmlcov
  env:
    SMOKESHOW_GITHUB_STATUS_DESCRIPTION: CLI Coverage {coverage-percentage}
    SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 50
    SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}

(this is taken directly from smokeshow's own CI, see here)

The following environment variables are used when setting commit statuses:

  • SMOKESHOW_GITHUB_STATUS_DESCRIPTION (or alternatively the --github-status-description CLI option) set the description for the commit status; the string {coverage-percentage} has a special meaning and will be replaced by the actual coverage percentage if it can be extract from the root index.html file being uploaded, this must be set for smokeshow to set the commit status
  • SMOKESHOW_GITHUB_COVERAGE_THRESHOLD (or alternatively the --github-coverage-threshold CLI option) decide the "state" of the commit status update; success is used if either the total coverage number isn't available or it's above the threshold, failure is used if the coverage number is below this threshold
  • SMOKESHOW_GITHUB_TOKEN this is used to authenticate the status update, more details here
  • SMOKESHOW_GITHUB_PR_HEAD_SHA or if it's omitted or empty GITHUB_SHA (which is set automatically by github actions) are used to decide which commit to set the status on. The SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} trick shown above is required since github set the GITHUB_SHA env var to a merge commit on pull requests which isn't what you want
  • SMOKESHOW_GITHUB_CONTEXT suffix for github status context
  • GITHUB_REPOSITORY is set automatically by github actions, it's used to choose the repo to set the status on

Manual Usage

You can create an upload key using the following python3.6+ script:

import base64, hashlib, os

print('Searching for a key with valid hash. Hold tight, this might take a minute...')
threshold = 2 ** 234
attempts = 0
while True:
    attempts += 1
    seed = os.urandom(50)
    h = int.from_bytes(hashlib.sha256(seed).digest(), 'big')
    if attempts % 100_000 == 0:
        print('.', end='', flush=True)
    if h < threshold:
        key = base64.b64encode(seed).decode().rstrip('=')
        print(f'\nSuccess! Key found after {attempts:,} attempts:\n\n    {key}\n')
        break

(This script should take between a few seconds and a minute to generate a valid key)

Once you have your key, create a site using the following curl command:

curl -X POST \
  https://smokeshow.helpmanual.io/create/ \
  -H 'Authorisation:{generated-key-from-above}'

This should create a site and return a JSON object with details required to upload files to that site:

{
  "message": "New site created successfully",
  "secret_key": "... secret upload key ...",
  "site_creation": "2021-03-13T18:36:44.419Z",
  "site_expiration": "2021-04-12T18:36:44.419Z",
  "sites_created_24h": 0,
  "upload_expiration": "2021-03-13T19:36:44.419Z",
  "url": "https://smokeshow.helpmanual.io/... 20 char random string .../"
}

You can then upload a file, again using curl (here RESPONSE_JSON refers to the response above):

curl -X POST \
  '{RESPONSE_JSON.url}path-to-upload.html' \
  -H 'Authorisation:{RESPONSE_JSON.secret_key}' \
  -H 'Content-Type:text/html' \
  --data-binary @file-to-upload.html

Features

smokeshow doesn't have too many special features, most things are designed to be boringly predictable, But a few things warrant explanation.

Content Type

The Content-Type header in responses is not inferred by smokeshow, instead it's taken from the same header in the upload request.

Path Matches

The following path equivalence is supported:

  • /path/to/file/ should return /path/to/file/index.html or /path/to/file.html or (less canonically) /path/to/file/index.json
  • trailing slashes don't matter

Referrer Redirects

smokeshow deploys sites at a random subdirectory (e.g. /3y4x0n6a200u2n6m316j/) this works fine, but could occasionally lead to problems with sites that assume they will be deployed at root (/), we work round that problem by inspecting the Referer header and redirecting to the intended page.

Example of how this works:

  • 🔗 The page https://smokeshow.helpmanual.io/3y4x0n6a200u2n6m316j/foobar/ has a link to /another/
    which of course we want to resolve to https://smokeshow.helpmanual.io/3y4x0n6a200u2n6m316j/another/
  • 👆 When a user clicks on the link, the browser loads https://smokeshow.helpmanual.io/another/
  • 🎯 smokeshow catches this request, inspects the Referer headers and spots /3y4x0n6a200u2n6m316j/foobar/
  • 🤔 smokeshow calculates that the request should be to https://smokeshow.helpmanual.io/3y4x0n6a200u2n6m316j/another/
  • ↪️ smokeshow returns a 307 redirect to that page
  • 🏗️ the browser loads that page
  • 😊 user is happy

Limits

The following limits apply to usage of smokeshow:

  • 200: maximum number of sites you can create a day with a given key
  • 30 MB: maximum site size
  • 25 MB: maximum size of a file - this is a limit of Cloudflare's KV store