| @@ -0,0 +1,148 @@ |
| | # Sites |
| | |
| | A public repository can have a website. You deploy a directory of files, |
| | such as a build's output, and knotless serves it at |
| | `https://<owner>.knotless.page/<repo>/`. The files are stored as blobs |
| | in your own account, in one record that each deploy replaces. |
| | |
| | [Pages](../design/pages.md) explains how sites are stored and served, and |
| | the [command reference](../reference/cli.md) lists every flag of |
| | `knotless site`. |
| | |
| | ## Deploy a site |
| | |
| | As the repository's owner, give `knotless site` the repository and the |
| | directory to deploy: |
| | |
| | ```sh |
| | knotless site knotless://alice.example.com/blog dist |
| | ``` |
| | |
| | It uploads only the files the live site does not hold already, then |
| | replaces the site's record. It prints how many it uploaded and the |
| | address: |
| | |
| | ``` |
| | uploaded 3 of 41 files |
| | https://alice-example-com.knotless.page/blog/ |
| | ``` |
| | |
| | The new site is live within seconds. A visitor sees the old site or the |
| | new one, never a mix of the two. |
| | |
| | The subdomain is your handle with each `-` doubled and each `.` turned |
| | into a `-`: `alice.example.com` is `alice-example-com`. It follows your |
| | handle, so it changes when your handle does. |
| | |
| | A site holds 1 to 2,000 files. A private repository cannot have a site |
| | yet. |
| | |
| | ## Build for the path |
| | |
| | The site is served under `/<repo>/`, not at the root, so build it with |
| | that as its base path. In Vite, that is `base`: |
| | |
| | ```js |
| | // vite.config.js |
| | export default { base: '/blog/' } |
| | ``` |
| | |
| | In SvelteKit it is `kit.paths.base`. A build with the wrong base loads |
| | its pages but not their scripts and styles. |
| | |
| | ## Serve a single-page app |
| | |
| | A single-page app routes in the browser, so every page has to load the |
| | app. Name the app's page as the fallback: |
| | |
| | ```sh |
| | knotless site knotless://alice.example.com/app dist --fallback index.html |
| | ``` |
| | |
| | A request for a page that no file matches then gets `index.html`, with |
| | status 200, and the app's router takes the path from there. A request |
| | counts as one for a page when it accepts `text/html`, or when the last |
| | part of its path has no extension. |
| | |
| | A file still comes first: `/app/assets/main.js` serves that file. A |
| | missing script or image gets a 404, not the app's HTML, so a broken build |
| | shows as one. |
| | |
| | ## Add a 404 page |
| | |
| | To answer a missing path with a page of your own, name it: |
| | |
| | ```sh |
| | knotless site knotless://alice.example.com/blog dist --not-found 404.html |
| | ``` |
| | |
| | A path that no file matches gets that page with status 404. With a |
| | fallback too, pages get the fallback, and other files get the 404 page. |
| | |
| | The fallback and the 404 page must each be one of the files you deploy. |
| | |
| | ## Deploy on every push |
| | |
| | The `site` shared step deploys from a workflow. It runs `knotless site` as |
| | the repository's owner, and installs knotless first if the image has |
| | none. |
| | |
| | The step logs in with an app password of the owner's account. Make one |
| | at your PDS (on Bluesky, under Settings, Privacy and security, App |
| | passwords). Then give it to the runners as a secret, limited to the |
| | branch you deploy from: |
| | |
| | ```sh |
| | printf %s "$APP_PASSWORD" | knotless secret knotless://alice.example.com/blog set SITE_PASSWORD --ref refs/heads/main |
| | ``` |
| | |
| | Then add a workflow that builds the site and deploys it, on a push to |
| | that branch only: |
| | |
| | ```yaml |
| | # .knotless/workflows/site.yml |
| | on: |
| | push: {refs: [refs/heads/main]} |
| | image: node:24 |
| | secrets: [SITE_PASSWORD] |
| | steps: |
| | - name: build |
| | run: npm ci && npm run build |
| | - uses: chadtmiller.com/steps/site@1 |
| | with: {dir: dist, fallback: index.html} |
| | secrets: [SITE_PASSWORD] |
| | ``` |
| | |
| | The step's inputs are `dir`, the directory to deploy, and optionally |
| | `fallback`, `not-found`, `repo` (this run's repository if not given) and |
| | `appview`. Pin it before you push, and commit the lock file: |
| | |
| | ```sh |
| | knotless uses update |
| | git add .knotless/uses.lock |
| | ``` |
| | |
| | Each deploy records the commit it was built from. [Shared |
| | steps](../reference/workflows.md#shared-steps) explains how `uses` works, |
| | and [Workflows](workflows.md) how to run a runner and give it secrets. |
| | |
| | ## See what is live |
| | |
| | With no directory, `knotless site` shows the live site: its address, how |
| | many files it holds, the commit it was built from, and when it was |
| | deployed: |
| | |
| | ```sh |
| | knotless site knotless://alice.example.com/blog |
| | ``` |
| | |
| | There is no history of deploys. To roll back, deploy an older commit's |
| | build again. |
| | |
| | ## Take a site down |
| | |
| | ```sh |
| | knotless site knotless://alice.example.com/blog --down |
| | ``` |
| | |
| | This deletes the site's record, and the site stops being served. |