{"id":15440877,"url":"https://github.com/samuelcolvin/smokeshow","last_synced_at":"2025-05-16T05:05:41.858Z","repository":{"id":44547038,"uuid":"344495778","full_name":"samuelcolvin/smokeshow","owner":"samuelcolvin","description":"create temporary websites","archived":false,"fork":false,"pushed_at":"2025-01-07T21:00:54.000Z","size":315,"stargazers_count":172,"open_issues_count":7,"forks_count":5,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-05-12T18:00:17.788Z","etag":null,"topics":["coverage-reports","ephemeral","http","https","pydantic","temporary-websites"],"latest_commit_sha":null,"homepage":"http://smokeshow.helpmanual.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/samuelcolvin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"samuelcolvin"}},"created_at":"2021-03-04T14:07:11.000Z","updated_at":"2025-05-10T17:35:35.000Z","dependencies_parsed_at":"2025-02-25T06:10:53.798Z","dependency_job_id":null,"html_url":"https://github.com/samuelcolvin/smokeshow","commit_stats":{"total_commits":61,"total_committers":2,"mean_commits":30.5,"dds":"0.016393442622950838","last_synced_commit":"875344d78675553f66ff92c6eee802a6e55c8640"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuelcolvin%2Fsmokeshow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuelcolvin%2Fsmokeshow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuelcolvin%2Fsmokeshow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samuelcolvin%2Fsmokeshow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/samuelcolvin","download_url":"https://codeload.github.com/samuelcolvin/smokeshow/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254471061,"owners_count":22076585,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coverage-reports","ephemeral","http","https","pydantic","temporary-websites"],"created_at":"2024-10-01T19:15:41.946Z","updated_at":"2025-05-16T05:05:41.670Z","avatar_url":"https://github.com/samuelcolvin.png","language":"TypeScript","funding_links":["https://github.com/sponsors/samuelcolvin"],"categories":[],"sub_categories":[],"readme":"# smokeshow\n\n[![CI](https://github.com/samuelcolvin/smokeshow/workflows/CI/badge.svg?event=push)](https://github.com/samuelcolvin/smokeshow/actions?query=event%3Apush+branch%3Amain+workflow%3ACI)\n[![pypi](https://img.shields.io/pypi/v/smokeshow.svg)](https://pypi.python.org/pypi/smokeshow)\n[![license](https://img.shields.io/github/license/samuelcolvin/smokeshow.svg)](https://github.com/samuelcolvin/smokeshow/blob/master/LICENSE)\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://smokeshow.helpmanual.io\"\u003e\n    \u003cimg src=\"https://smokeshow.helpmanual.io/icon.svg\" alt=\"smokeshow\" width=\"200\" height=\"200\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nDeploy ephemeral websites via HTTP or [a CLI](#cli-usage).\n\nIf you need to do any of the following:\n* 🚀 preview a site before launch\n* 🙈 view the HTML version of coverage reports\n* 👀 create a quick website to show someone something\n\n_smokeshow_ is here to help. It lets you create a static website, 1 year after the site is created, it vanishes\nlike smoke in the wind.\n\nWhat's great about _smokeshow_:\n* 💸 It's free\n* 🔑 You don't need to sign up, just create a key using the instructions below\n* 💨 It's super fast around the world, _smokeshow_ uses CloudFlare's 280+ edge locations to store files meaning\n  they're next to your users wherever they are\n\n## Usage Warning\n\n_smokeshow_ is currently free for anyone to use ([within limits](#limits)), but if it starts to cost me a\nsignificant amount, I might reduce the limits, or stop it being free.\nPlease [watch the github repo](https://github.com/samuelcolvin/smokeshow)\nto get notifications of changes to the service if you're using it regularly or in an automated way.\n\n_smokeshow_ is [open source](https://github.com/samuelcolvin/smokeshow) so if you want to modify it and/or deploy\nyour own instance to cloudflare workers, you can.\n\n## Usage\n\nUploading a site to _smokeshow_ requires three steps:\n\n1. Create an upload key where a numeric representation of its `sha-256` hash is less than `2 ^ 234`.\n   In other words; a simple proof of work. This key can then be used to create multiple sites.\n2. Create a new site.\n3. Upload one or more files to that site.\n\nAll three steps can be performed **either** [the python CLI](#cli-usage), or using [manually](#manual-usage).\n\n### CLI Usage\n\nThe command line interface (CLI) for _smokeshow_ is written in python and available to download via\n[pypi](https://pypi.org/project/smokeshow/). Assuming you have python 3.7+ and pip installed, installing\nthe _smokeshow_ CLI should be as simple as:\n\n```bash\npip install smokeshow\n```\n\nYou can then get help on usage with:\n\n```bash\nsmokeshow --help\n```\n\nTo generate an upload key, use:\n\n```bash\nsmokeshow generate-key\n```\n\nYou should then set the key as an environment variable with\n\n```bash\nexport SMOKESHOW_AUTH_KEY='...'\n```\n\nWith that, you can upload a site with:\n\n```bash\nsmokeshow upload path/to/upload\n```\n\nFor more help run `smokeshow upload --help`, if you run `smokeshow upload` without either\nsetting the `SMOKESHOW_AUTH_KEY` environment variable or using the `--auth-key` option, _smokeshow_ will generate\na new upload key before uploading the site.\n\nIf you're having trouble with python versions and accessing the CLI, you can also run the _smokeshow_ library\nmodule as a script via\n\n```bash\npython -m smokeshow\n```\n\n### GitHub actions \u0026 commit status integration\n\nI build _smokeshow_ primarily to preview documentation and coverage generate with\n[github actions](https://github.com/features/actions).\n\n_smokeshow_ therefore integrates directly with github actions to add a status to commits with a link to\nthe newly created ephemeral site.\n\nIn addition, _smokeshow_ has custom logic to extract the total coverage figure from\n[coverage.py](https://coverage.readthedocs.io/en/coverage-5.5/) HTML coverage reports to both annotate commit status\nupdates and decide if the commit status is \"success\" or \"failure\".\n\nExample of setting the commit status from a github action:\n\n```yaml\n- run: smokeshow upload cli/htmlcov\n  env:\n    SMOKESHOW_GITHUB_STATUS_DESCRIPTION: CLI Coverage {coverage-percentage}\n    SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 50\n    SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n    SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}\n```\n\n(this is taken directly from smokeshow's own CI, see\n[here](https://github.com/samuelcolvin/smokeshow/blob/034e6cf416fc31a17bbb9b68c77623006d39dcd5/.github/workflows/ci.yml#L131-L136))\n\nThe following environment variables are used when setting commit statuses:\n\n* `SMOKESHOW_GITHUB_STATUS_DESCRIPTION` (or alternatively the `--github-status-description` CLI option) set the description\n  for the commit status; the string `{coverage-percentage}` has a special meaning and will be replaced by the actual\n  coverage percentage if it can be extract from the root `index.html` file being uploaded, this must be set\n  for _smokeshow_ to set the commit status\n* `SMOKESHOW_GITHUB_COVERAGE_THRESHOLD` (or alternatively the `--github-coverage-threshold` CLI option) decide\n  the \"state\" of the commit status update; `success` is used if either the total coverage number isn't available or it's\n  above the threshold, `failure` is used if the coverage number is below this threshold\n* `SMOKESHOW_GITHUB_TOKEN` this is used to authenticate the status update, more details\n  [here](https://docs.github.com/en/actions/reference/authentication-in-a-workflow)\n* `SMOKESHOW_GITHUB_PR_HEAD_SHA` or if it's omitted or empty `GITHUB_SHA` (which is set automatically by github actions)\n  are used to decide which commit to set the status on.\n  The `SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}` trick shown above is required since\n  github set the `GITHUB_SHA` env var to a merge commit on pull requests which isn't what you want\n* `SMOKESHOW_GITHUB_CONTEXT` suffix for github status context\n* `GITHUB_REPOSITORY` is set automatically by github actions, it's used to choose the repo to set the status on\n\n### Manual Usage\n\nYou can create an upload key using the following python3.6+ script:\n\n```python\nimport base64, hashlib, os\n\nprint('Searching for a key with valid hash. Hold tight, this might take a minute...')\nthreshold = 2 ** 234\nattempts = 0\nwhile True:\n    attempts += 1\n    seed = os.urandom(50)\n    h = int.from_bytes(hashlib.sha256(seed).digest(), 'big')\n    if attempts % 100_000 == 0:\n        print('.', end='', flush=True)\n    if h \u003c threshold:\n        key = base64.b64encode(seed).decode().rstrip('=')\n        print(f'\\nSuccess! Key found after {attempts:,} attempts:\\n\\n    {key}\\n')\n        break\n```\n_(This script should take between a few seconds and a minute to generate a valid key)_\n\nOnce you have your key, create a site using the following `curl` command:\n\n```bash\ncurl -X POST \\\n  https://smokeshow.helpmanual.io/create/ \\\n  -H 'Authorisation:{generated-key-from-above}'\n```\n\nThis should create a site and return a JSON object with details required\nto upload files to that site:\n\n```json\n{\n  \"message\": \"New site created successfully\",\n  \"secret_key\": \"... secret upload key ...\",\n  \"site_creation\": \"2021-03-13T18:36:44.419Z\",\n  \"site_expiration\": \"2021-04-12T18:36:44.419Z\",\n  \"sites_created_24h\": 0,\n  \"upload_expiration\": \"2021-03-13T19:36:44.419Z\",\n  \"url\": \"https://smokeshow.helpmanual.io/... 20 char random string .../\"\n}\n```\n\nYou can then upload a file, again using `curl` (here `RESPONSE_JSON` refers to the response above):\n\n```bash\ncurl -X POST \\\n  '{RESPONSE_JSON.url}path-to-upload.html' \\\n  -H 'Authorisation:{RESPONSE_JSON.secret_key}' \\\n  -H 'Content-Type:text/html' \\\n  --data-binary @file-to-upload.html\n```\n\n## Features\n\n_smokeshow_ doesn't have too many special features, most things are designed to be\nboringly predictable, But a few things warrant explanation.\n\n### Content Type\n\nThe `Content-Type` header in responses is not inferred by _smokeshow_, instead it's taken from the same\nheader in the upload request.\n\n### Path Matches\n\nThe following path equivalence is supported:\n* `/path/to/file/` should return `/path/to/file/index.html` or `/path/to/file.html` or\n  (less canonically) `/path/to/file/index.json`\n* trailing slashes don't matter\n\n### Referrer Redirects\n\n_smokeshow_ deploys sites at a random subdirectory (e.g. `/3y4x0n6a200u2n6m316j/`) this works fine, but could occasionally\nlead to problems with sites that assume they will be deployed at root (`/`), we work round that problem by\ninspecting the `Referer` header and redirecting to the intended page.\n\n**Example** of how this works:\n* 🔗 The page `https://smokeshow.helpmanual.io/3y4x0n6a200u2n6m316j/foobar/` has a link to `/another/` \\\n  which of course we want to resolve to `https://smokeshow.helpmanual.io/3y4x0n6a200u2n6m316j/another/`\n* 👆 When a user clicks on the link, the browser loads `https://smokeshow.helpmanual.io/another/`\n* 🎯 _smokeshow_ catches this request, inspects the `Referer` headers and spots `/3y4x0n6a200u2n6m316j/foobar/`\n* 🤔 _smokeshow_ calculates that the request should be to `https://smokeshow.helpmanual.io/3y4x0n6a200u2n6m316j/another/`\n* ↪️ _smokeshow_ returns a `307` redirect to that page\n* 🏗️ the browser loads that page\n* 😊 user is happy\n\n## Limits\n\nThe following limits apply to usage of _smokeshow_:\n* **200**: maximum number of sites you can create a day with a given key\n* **50 MB**: maximum site size\n* **25 MB**: maximum size of a file - this is a limit of [Cloudflare's KV store](https://developers.cloudflare.com/workers/platform/limits#kv-limits)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamuelcolvin%2Fsmokeshow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsamuelcolvin%2Fsmokeshow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamuelcolvin%2Fsmokeshow/lists"}