diff --git a/src/content/docs/hosted-app/install.mdx b/src/content/docs/hosted-app/install.mdx new file mode 100644 index 0000000..73e4401 --- /dev/null +++ b/src/content/docs/hosted-app/install.mdx @@ -0,0 +1,184 @@ +--- +title: Install the app +description: Install the Sluiceway GitHub App, merge the onboarding pull request it opens, add your secrets, and choose whether ticks can go through the app. +headerPicture: + picture: first-run + alt: No stacks yet, Penny stands beside an empty channel +--- + +import Callout from "../../../components/Callout.astro"; +import Steps from "../../../components/Steps.astro"; +import { page } from "../../../lib/routes"; + +

+ The Sluiceway app is a GitHub App that installs beside the action. It shows every stack of + every repo it covers in one place, and opens one pull request per repo to set Sluiceway up. +

+ +The app does not replace the action. Your workflow keeps running in your runners, with your +credentials, and the dashboard issue in each repo keeps working as it does without the app. + + + +## Install + +Open the Sluiceway app on GitHub, choose +**Install**, pick the org or account, and choose all repos or only some. You can change the +repos later in GitHub's settings for the installation, and the app's Settings tab links there. + +Then sign in at app.sluiceway.dev with GitHub. You see +the installations of the orgs and accounts you belong to, and nothing else. + +Installing the app on repos that have no dashboard costs nothing: only repos with a dashboard +count toward your plan. See Plans and adopted repos. + +### What GitHub asks you to accept + +These are the permissions the app asks for, and what each one is used for. + +| Permission | Access | What for | +| --- | --- | --- | +| Metadata | Read | Required by GitHub for every app. | +| Issues | Read and write | Reading the dashboard and its edit history. Writing the dashboard when a tick goes through the app, and the comment when a tick is refused. | +| Deployments | Read and write | Reading the deployment records. Opening a record for a tick that goes through the app. | +| Actions | Read and write | Reading workflow runs. Starting your Sluiceway workflow for a tick or a rescan that goes through the app. | +| Contents | Read and write | Reading the repo for the onboarding pull request, and `sluiceway.yaml` at a commit. Writing one branch with one commit for a pull request the app opens. | +| Pull requests | Read and write | Opening the onboarding pull request and the pull requests of edit mode. Reading when a merged pull request was merged, for Insights. | +| Members (organization) | Read | Checking that a person is an admin of the org before they change the plan. | + +The app does not ask for Secrets. It never reads your secrets, their names or their values. + +## The onboarding pull request + +When the app is installed on a repo with no dashboard, it opens one pull request there, on the +branch `sluiceway/onboarding`. It is written by the action's own +init and check, run over a copy of the repo's default branch, +so it says exactly what adding the action by hand would give you. + +It holds: + +- **The workflow** at `.github/workflows/deploy-dashboard.yml`, which runs `sluiceway/sluiceway@v0`. +- **A first `sluiceway.yaml`**, only when init writes one, and the export script for an env file + of secret references, only when init found one. +- **The `recordWriters` line**, which lets ticks go through the app. See + [below](#the-recordwriters-line). + +Its description lists: + +- the stacks the check found, what an `ignore` entry leaves out, and what root module discovery + left out and why; +- the dashboard the repo would get, folded. Every stack is drawn in sync there, because none has + been previewed yet, and the first scan replaces it; +- per stack, the credentials it needs and the ways to provide them, with links to the repo's + Actions secrets settings, and what the workflow does not provide yet; +- what init says is still for a person to do, and the whole summary of the check, folded. + +It names secrets and never holds a value. + +A person merges it. The app never merges anything, and never pushes to your default branch. + +### When there is no pull request + +A repo that has a dashboard already gets no pull request, and neither does one where the app's +onboarding pull request is still open: the org view links that one. + +When the app cannot open the pull request, the org view says why in one sentence under the repo. +The reasons are ones only you can change: the repo is empty, it is too large to read, a workflow +in it runs Sluiceway already, init found no stack, `sluiceway.yaml` is not valid, discovery could +not work out the stacks, or GitHub did not answer. The app does not try again on its own. Fix +the repo and press the button on the org view or the repo's page to ask again. + +A repo where init found no stack is listed on the org view in one quiet line, "No stacks found +in", and you can ask for the pull request from its page once you add stacks. + +## The secrets step stays yours + +The app never holds, asks for or supplies a cloud credential. Before you merge: + +1. **Add the secrets the pull request names** in the repo's Actions secrets settings, or provide + them another way from Credentials and your own tooling. +2. **Point the workflow at a self-hosted runner** when your infrastructure cannot be reached from + GitHub's runners, such as a cluster on a private network. See + Self-hosted runners. + +Then merge. The first scan previews every stack, creates the dashboard issue, and the repo moves +from Not set up yet to the stacks on the org view. + +### When the first scan fails + +The app explains a failed first scan in one sentence with one link to the page that fixes it. + +On the repo's page, for each stack whose preview failed and that never deployed, it says one of: + +- a credential the stack needs is not provided by the workflow; +- the stack needs a cluster, and the scan ran on a GitHub-hosted runner that cannot see it; +- a credential may be missing, where the check cannot see into a step that might provide it; +- or that the facts do not explain it, and the reason is in the summary of the run. + +On the org view, for a repo whose first scan failed before it wrote a dashboard, it says where +the run stopped: in a step before Sluiceway's, where a secret the pull request names is likely +not set yet; in Sluiceway's own step, with what to check on a self-hosted or a hosted runner; or +before the run started, when an Actions policy does not allow the action. + +The app cannot read the names of your secrets, so it says "likely" where it cannot know. + +## The recordWriters line + +The pull request adds the app to `recordWriters` in `sluiceway.yaml`, in the file init writes or +in the one the repo has, with a comment that says what it allows: + +```yaml title="sluiceway.yaml" +# The Sluiceway app may open deployment records that this repo's workflow +# deploys, after judging the tick with the same rule the action uses. +recordWriters: + - sluiceway[bot] +``` + +Every other key, comment and writer in the file stays as it was. A file that names the app +already is not changed, and the description says so. + +`recordWriters` lists the logins whose +deployment records your workflow deploys. With `sluiceway[bot]` on it: + +- **You can tick a box and ask for a rescan from the app.** The app judges the tick with the + action's own tick rule and your permission on the repo, opens the deployment record, and + starts your workflow. See Tick from the app. +- **A tick on the dashboard issue gets its row written by the app**, usually before your workflow + has started. Your workflow's run then previews the stack again and deploys only when the fresh + preview gives the same diff hash and value fingerprint, as for any tick. + +Ticking and Rescan from the app work in a repo whose `sluiceway.yaml` names `sluiceway[bot]` in +`recordWriters`, and in any other repo they open the dashboard on GitHub. + +It allows nothing else. The app cannot deploy a stack nobody ticked, and a stack that deletes +or replaces resources still waits for a person's tick. + +Without the line, the app judges no tick on that repo and every tick is left to your workflow, as +it is without the app. The org view says so under the repo in one sentence. You can add the line +later from the app's Settings tab or from edit mode, each of which opens a one-line pull request, +and take it out the same way. + + + The tick rule cannot judge a bot, which has no access level of its own. That is why an app has + to be named in `sluiceway.yaml`, where a change goes through review, rather than allowed by a + setting in the app. + diff --git a/src/content/docs/hosted-app/plans.mdx b/src/content/docs/hosted-app/plans.mdx new file mode 100644 index 0000000..f2002e5 --- /dev/null +++ b/src/content/docs/hosted-app/plans.mdx @@ -0,0 +1,112 @@ +--- +title: Plans and adopted repos +description: What counts toward a plan, what happens to the repos past it, choosing active repos in Settings, and billing and cancelling through Stripe. +headerPicture: + picture: pending-5 + alt: 5 stacks are pending, five crates wait upstream of Penny +--- + +import Callout from "../../../components/Callout.astro"; +import { page } from "../../../lib/routes"; + +

+ A plan decides how many adopted repos the app shows whole, how far back the audit log goes, + and whether a second approver can hold a deploy. Every other feature is on every plan. +

+ +| Plan | Adopted repos | History | +| --- | --- | --- | +| Free | 5 | 30 days | +| Team | 25 | 13 months | +| Team Plus | 100 | 3 years | + +A required second approver is on Team and Team Plus: you add Sluiceway as a custom deployment +protection rule on a GitHub environment, and GitHub holds each deploy job there until someone +other than the ticker approves it in the app. GitHub offers custom protection rules on GitHub +Enterprise and on public repos only. On Free the app approves such a job at once, so no deploy +waits. + +The prices are on the pricing page. + +A plan belongs to an installation: one org, or one personal account, on GitHub. + +## What counts + +An **adopted repo** is a repo the app is installed on that has a dashboard: the action has run a +scan there. Only adopted repos count. + +A repo without a dashboard never counts, so you can install the app on a whole org at no cost. +It is listed under Not set up yet, with its onboarding pull request, and counts from the moment +its first scan writes the dashboard. + +Edit mode does not count either: it shows one repo at a time. + +## Active repos, and the ones past the line + +Your plan's number of adopted repos are **active**: the app shows them whole, on the org view, +their own pages, Insights and the audit log. + +Which ones is your choice. On the org's **Settings** tab, under Active repos, pick up to your +plan's number. The places you leave open go to the busiest repos: the most stacks not in sync, +then the most stacks, then by name. With no choice, the busiest are active. Anyone who can see +the installation can choose, because the choice changes what the app shows and nothing else. +Clear the choice to go back to the busiest. + +The adopted repos past the line are listed greyed out, in a group of their own called Past the +free tier, or past your plan's name: + +- **The org view** lists each by name with its state counts, and no stacks or rows. +- **Their own pages** are not shown, so Rescan for them opens the dashboard on GitHub. +- **The audit log** says how many lines each one has, and not the lines. The CSV holds what the + page shows. +- **Insights** name each one with its deploys, and do not count them. + + + A repo past the line keeps its dashboard on GitHub, and it works as always: scans, ticks and + deploys go on exactly as they did. The app keeps its facts, and shows them again as soon as + the repo is active. + + +## History + +The audit log shows the deploys inside your plan's history, and its last sentence says how far +back that is and on which plan. Nothing is deleted at the line: when your plan grows, the older +lines are shown again. + +Insights always cover the last 12 weeks against the 12 before, on every plan. + +## Upgrade + +An admin of the org on GitHub, or the person whose personal account it is, can change the plan. +Everyone else who can see the installation sees the plan, its numbers and its price, and one +sentence that says who can change it. The app checks with GitHub every time someone changes the +plan. + +**Upgrade** is on the Settings tab, and in place of the pricing link beside the repos past the +line. Choose a plan, monthly or yearly, and pay on Stripe's Checkout page. There is no trial. + +- **VAT**: Stripe works out the tax from your billing address. A business in the EU can enter + its VAT id. +- **A promotion code** goes in on the Checkout page. A code that brings the price to 0 needs no + card, and the Plan section names the code. + +You come back to Settings, which says Stripe is confirming the payment until it has. + +## Change or cancel + +**Manage billing** on the Settings tab opens Stripe's customer portal. Change between Team and +Team Plus or monthly and yearly, update your card, read your invoices, or cancel there. + +When you cancel, or a payment fails, your plan holds until the end of the period you paid for, +and the installation falls back to Free after it. Nothing is deleted: the app shows what Free +shows, and shows the rest again if a plan starts again. Settings says when your plan renews or +ends, and what then happens. + +To pay by invoice, write to hello@sluiceway.dev. An +invoice plan is changed the same way. + +## See also + +- Settings, in Using the app. +- What the app keeps and never keeps, including + what Stripe keeps and the app does not. diff --git a/src/content/docs/hosted-app/using-the-app.mdx b/src/content/docs/hosted-app/using-the-app.mdx new file mode 100644 index 0000000..c1387ef --- /dev/null +++ b/src/content/docs/hosted-app/using-the-app.mdx @@ -0,0 +1,190 @@ +--- +title: Using the app +description: The org view, a repo's dashboard, ticking from the app with Undo, Rescan, Insights, the audit log, and edit mode with its templates. +headerPicture: + picture: deploying-3 + alt: A stack is deploying and 3 are pending, the gate is open and one crate goes through it +--- + +import { glossary, page, ROUTES } from "../../../lib/routes"; + +

+ The app draws what your dashboards say, across every repo it covers, in the dashboard's own + words. Every row it draws leads back to its repo and its dashboard on GitHub. +

+ +Sign in at app.sluiceway.dev with GitHub. The app reads +which installations you may see once, when you sign in, and keeps no token of yours. A session +lasts eight hours, so a change of your membership on GitHub shows at your next sign-in. + +The top bar shows where you are, an org or one of its repos, and switches between them. An org +has the tabs Overview, Insights, Audit and Settings. A repo has Dashboard, Insights and Audit. + +## The org view + +The org's Overview tab is every stack of every repo the app covers, at `app.sluiceway.dev/`. + +- **The counts** of the five states, pending, deploying, drifted, preview failed and in sync, + and when the last scan ran. Queued stacks count as deploying, and their rows still say queued. +- **One banner** naming every pending stack that deletes or replaces resources. +- **Chips** that filter the rows by state. +- **The groups**: Needs you (pending, drifted, preview failed), In flight (deploying) and In + sync, folded. Or By repo, one panel per repo. + +A row is one link to its repo. It says what the dashboard's row says, without the changes: the +state, how many resources it deletes or replaces, drift outside the code, and the stack's last +deploy with who ticked it or merged it, the commit and the run. +A deploy the dashboard's trail lists as an outside deploy +is marked "deployed outside the dashboard". + +Under the stacks: + +- **Not set up yet**: repos with no dashboard, each with its onboarding pull request or the button + to ask for one, and why a first scan failed when it did. See + Install the app. +- **No stacks found**: one quiet line for the repos where init found no stack. +- **Past the free tier**, or past your plan: adopted repos beyond your plan's number, greyed out. + See Plans and adopted repos. + +## A repo's dashboard + +The Dashboard tab is the repo's dashboard issue drawn again, in the same order, with the same +headings and sentences, Penny's header included. The app draws +it with the action's own renderer, at the version these docs describe, from the facts it keeps. + +The app keeps no diff, so a pending or drifted row is drawn the way a +redacted row is: its counts and no change lines. The changes +themselves are one click away on the dashboard on GitHub. A failure line points at the run, +since the app keeps no reason. + +## Tick from the app + +On a repo whose `sluiceway.yaml` names `sluiceway[bot]` in +`recordWriters`, a box on the +Dashboard tab is a real box. Tick it, and: + +1. **The row holds for five seconds**, dimmed, and a toast says "Asked GitHub to deploy" with the + stack id and an **Undo** button. Undo takes the tick back, and nothing was asked of GitHub. +2. **The app judges the tick** with the action's own tick rule + and your permission on the repo, read live from GitHub, exactly as your workflow judges a tick + on the issue. +3. **An allowed tick** starts your workflow and opens the stack's + deployment record with you as the ticker. The row + says "asking GitHub to start the run…", then waiting to start. Undo still works until the + record is open. +4. **Your runner deploys.** The run previews the stack again and deploys only when the fresh + preview gives the same diff hash, as for any tick. The row moves as the dashboard's row does. + +A refused tick writes nothing on the issue. The refusal, in the action's own words, comes back +to you on the page. + +The app never says a deploy went out: it says what it asked GitHub for. GitHub and your runner +decide the rest, and the row shows it. + +Some boxes stay on GitHub, drawn as the same square but linking to the dashboard there: + +- the bulk, confirm and merge boxes, and the rescan box of the issue; +- a drift repair, and a stack with a dependency, a phase, a deploy window or deploy on merge, or + one an `ignore` entry leaves out; +- every box on a repo that does not name the app in `recordWriters`. The tab then shows one line + with a button that opens the pull request to add it. + +A read-only dashboard has no boxes, in the app or +on GitHub. + +Ticking and Rescan from the app work in a repo whose `sluiceway.yaml` names `sluiceway[bot]` in +`recordWriters`, and in any other repo they open the dashboard on GitHub. + +## Rescan + +**Rescan** in the top bar asks for a full scan: of the repo +in view, or from the org, of one repo or every repo the app scans. It follows the rule of the +rescan box: you need write access to the repo. + +The app starts your workflow, which runs a full scan, and the rescan box on the issue stays +unticked. A rescan deploys nothing, so it is asked at once, with no Undo. The toast says "Asked +GitHub to scan" and the repo's name. Asking again while that scan runs starts nothing new. + +On a repo that does not name the app in `recordWriters`, and on one past your plan, Rescan opens +the dashboard on GitHub, where the rescan box is. + +## Insights + +Insights, for an org or for one repo, cover the last 12 weeks, from Monday in UTC, each number +against the 12 weeks before. + +- **Lead time**: from the merge of a pull request to the deploy of its merge commit, the median. + A deploy of a commit with no merged pull request, such as a direct push, is not measured. +- **Deploys per week**, from the dashboard and outside it. +- **Change failure rate**: failed deploys from the dashboard over all deploys from the dashboard. +- **Deployed outside the dashboard**: the outside deploys a full scan found. + +Four charts show lead time per week, deploys per week, stacks drifted per week, and where the +time goes between a merge and its deploy: to the scan, to the preview, waiting for a tick, and +the deploy itself. The org's Insights end with one row per repo. + +A number the facts cannot give says "Not measured yet" in one sentence, with no number. + +## The audit log + +The Audit tab lists one line per deploy, newest first: when, the repo, the stack, who ticked or +who merged, the commit, and the result in the trail's words. A deploy outside the dashboard is +flagged in red, "outside the dashboard", and names nobody, because the tool's history never says +who. + +Filter by repo, by stack or to the flagged lines only. Each filter is a plain link, so a filtered +log has an address you can paste into a ticket. **CSV** downloads what the page shows, with the +same filters, the time in UTC and the whole commit. + +The log goes back as far as your plan's history, and its last sentence says how far. + +## Edit mode + +**Customise** on the Dashboard tab turns the dashboard into its own editor: the same render, with +the settings of the block you select beside it. Every change redraws the dashboard and the diff of +`sluiceway.yaml`. + +Every setting is a documented key of `sluiceway.yaml` that the action reads without the app. What +you build keeps working exactly the same if you stop using the app. + +- **The dashboard**: `dashboard.title`, `personality` (Penny's header), `redact`, `showValues`, + `recentlyDeployed`, `timeZone` and `readOnly`, and `drift.enabled`. +- **Who may tick**: `tickers`, and `recordWriters` as the app on the list or off it. +- **Stacks**: per stack, `deploy` and `tickers`, and the `ignore` list with its reasons. + +A setting at its default takes its key out of the file. See +Configuration for what each key does. + +### Templates + +A template is a named set of those keys, applied in one click over the settings you have, and +adjusted after. It touches no key it does not name. + +- **Minimal**: `personality` off, rows redacted, and the last 3 deploys in the trail. +- **Classic**: every dashboard key and drift at the action's defaults. +- **Ops desk**: the last 30 deploys in the trail and drift on. Set your time zone yourself. + +### Save opens a pull request + +**Save** opens one pull request on the branch `sluiceway/dashboard`, from the commit the dashboard +was scanned at. It changes `sluiceway.yaml` and nothing else, keeps every other key and every +comment as they were, and its description names each change and shows the dashboard as it will +look. A person merges it, and the next scan draws the dashboard that way. + +The app never changes the file behind anyone's back. While a pull request from edit mode is open, +the app names it rather than open a second one. When the file moved on the default branch since +the scan, the pull request shows the conflict for you to resolve. + +On a repo with no dashboard yet, edit mode starts from the action's example dashboard. + +## Settings + +The org's Settings tab holds what the app decides, and nothing that belongs in `sluiceway.yaml`: + +- **Plan**: your plan, its repos and history, and billing. See + Plans and adopted repos. +- **Active repos**: which adopted repos your plan shows. +- **The installation**: the repos the app is installed on, with a link to GitHub's page to change + them. +- **Access per repo**: whether each adopted repo names the app in `recordWriters`, with a button + that opens the one-line pull request to add it or take it out. diff --git a/src/content/docs/hosted-app/what-the-app-keeps.mdx b/src/content/docs/hosted-app/what-the-app-keeps.mdx new file mode 100644 index 0000000..625bcaf --- /dev/null +++ b/src/content/docs/hosted-app/what-the-app-keeps.mdx @@ -0,0 +1,94 @@ +--- +title: What the app keeps and never keeps +description: The app keeps stack-level facts only, never a resource name, diff, value or cloud credential, never deploys on its own, and GitHub stays the source. +sidebar: + label: What the app keeps +headerPicture: + picture: in-sync + alt: Every stack is in sync, and Penny rests on a calm quay +--- + +import { glossary, page } from "../../../lib/routes"; + +

+ The app keeps a timeline, not a map of your infrastructure. It holds no cloud credential, it + cannot deploy on its own, and GitHub stays the source of truth. +

+ +## What it keeps + +Facts at the level of a stack, read from what the action publishes: the dashboard's +markers, the deployment records +and the workflow runs. See What Sluiceway writes +for that shape. + +- **Per stack**: its stack id, its row state, how many resources it deletes or replaces, drift + and resources gone outside the code, and whether its box is ticked. +- **Per deploy**: the stack, who ticked or who merged, which commit, which run, how it ended, when, + and whether it was an outside deploy. +- **Per tick in the app**: who, which stack, when, and how it was judged, in one word. +- **Per scan**: when it started, its commit, when its dashboard was written, and how many stacks + it drew drifted. +- **Per merge**, for Insights: the merge commit and when it was merged. +- **Per rescan asked in the app**: who asked, when, and the run it started. +- **Per repo**: why its onboarding pull request could not be opened, and why its first scan + failed, each as one word from the app's fixed list. + +## What it never keeps + +- **No resource name, property path, diff or value.** Not in its database, not in its pages, not + in the pull requests it opens. A row in the app shows counts, and the changes are on GitHub. +- **No text of a row, an issue title or a status description**, and of a merged pull request + nothing but its commit and time: no number, title, description, branch or author. +- **None of your repo's files.** For the onboarding pull request and to explain a failed first + scan, the app reads a copy of the repo into a temporary directory and removes it when it is + done, whether it worked or failed. The app refuses a copy larger than 100 MB. +- **No token of yours.** Signing in reads who you are and which installations you may see, and + the token is dropped. The app keeps no token of yours, so it cannot act as you. +- **No card, email, address, business name or tax id.** Stripe keeps those. The app keeps your + plan, what Stripe says about the subscription, when the period ends, and Stripe's ids. + +## No cloud credential, ever + +The app never holds, asks for or supplies a cloud or infrastructure credential. Your credentials +stay in your runners, set up as Credentials and your own tooling +describes. The app does not ask GitHub for permission to read your secrets, and the onboarding +pull request names the secrets a stack needs without ever seeing one. + +## It never causes a deploy on its own + +The app cannot deploy anything. Your runner does, as it does without the app. + +- **A deploy starts from a person's tick**, on the dashboard or in the app, judged with the + action's own tick rule, the same code your workflow uses. +- **Only on a repo that names the app** in `recordWriters`, + a line of `sluiceway.yaml` that goes through review. +- **The app opens a record and starts your workflow.** Your runner then previews the stack again + and deploys only when the fresh preview gives the diff hash the person approved. A change that + moved in between is refused. +- **A stack that deletes or replaces resources** still waits for a person's tick. + +Nothing else the app does starts a deploy. A rescan runs a scan. Edit mode and the onboarding +pull request are pull requests, and a person merges them. + +## GitHub is the source of truth + +The app is a view of what GitHub holds, and catches up with it. + +- **Your workflow keeps working without the app.** Installing the app changes nothing in it and + turns nothing off. When you tick on the dashboard, your workflow judges and deploys the tick + itself, whatever the app does. When both act on the same tick, whoever opens the deployment + record first has it, and the other finds the record open and does nothing more. +- **When the app is down, nothing is lost.** On every start it reads each repo again since the + last point it saw: the dashboard, the deployment records and the workflow runs. GitHub sends + the webhooks it missed again too. A downtime delays what the app shows and never loses it. +- **What the app shows follows GitHub.** A dashboard written again, a record closed, a repo + removed from the installation: the app brings its facts up to date. A repo the installation no + longer covers keeps its lines in the audit log. + +## If you stop using the app + +Uninstall it from GitHub's settings for the installation. Your dashboards, your workflow and +your deploys go on as before. Everything edit mode wrote is a documented key of +`sluiceway.yaml` that the action reads on its own, so the dashboard looks the same. Take +`sluiceway[bot]` out of `recordWriters` when you no longer want the app to open records. diff --git a/src/lib/pages.ts b/src/lib/pages.ts index 5151670..b03d3bd 100644 --- a/src/lib/pages.ts +++ b/src/lib/pages.ts @@ -78,6 +78,17 @@ export const SLICE_3 = { theHeader: "the-header", }; +/** + * The pages about the hosted app, in sidebar order. They are written in src/content/docs: the + * action has no Markdown about the app. + */ +export const HOSTED_APP = { + install: "hosted-app/install", + using: "hosted-app/using-the-app", + keeps: "hosted-app/what-the-app-keeps", + plans: "hosted-app/plans", +} as const; + /** * Whether one of slice 3's pages is in src/content/docs yet. Until it is, the sidebar leaves it * out and links to it go to GitHub, so no link points at a page that is not built. @@ -1004,6 +1015,10 @@ export function sidebar(): SidebarItem[] { { slug: "guides/security" }, ], }, + { + label: "Hosted app", + items: Object.values(HOSTED_APP).map((slug) => ({ slug })), + }, { label: "Reference", items: [ diff --git a/src/lib/routes.ts b/src/lib/routes.ts index 5877868..484f8b2 100644 --- a/src/lib/routes.ts +++ b/src/lib/routes.ts @@ -2,7 +2,7 @@ // id from src/lib/pages.ts and goes through its `urlFor`, so the base is added the same way // everywhere. A page id that pages.ts does not define stops the build. -import { githubSlug, pages, SLICE_3, urlFor } from "./pages"; +import { githubSlug, HOSTED_APP, pages, SLICE_3, urlFor } from "./pages"; import { REPO_URL } from "./source"; const BASE = import.meta.env.BASE_URL.replace(/\/$/, ""); @@ -17,6 +17,7 @@ export function page(id: string): string { SLICE_3.overview, SLICE_3.whatItLooksLike, SLICE_3.theHeader, + ...Object.values(HOSTED_APP), ]); const path = id.split("#")[0] ?? ""; if (!known.has(path)) {