Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions src/content/docs/get-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
title: Get started
description: Install the Sluiceway GitHub App, merge the pull request it opens with the workflow and a first sluiceway.yaml, and let the console guide the rest.
headerPicture:
picture: first-run
alt: The scan found no stacks yet, Penny stands beside an empty channel
---

import Callout from "../../components/Callout.astro";
import Steps from "../../components/Steps.astro";
import { page, ROUTES } from "../../lib/routes";

<p class="sw-lead">
Install the Sluiceway GitHub App. It opens one pull request per repo that adds the workflow
and a first <code>sluiceway.yaml</code>, and the console guides you through the rest.
</p>

The app is the action, set up for you, with a console. Every deploy still runs in your own
GitHub Actions, with your own cloud credentials. The app holds no cloud credential and runs no
infrastructure as code. It is free for 5 repos.

<Steps
items={[
{
title: "You install the app and pick repos",
body: "Install it from github.com/apps/sluiceway on an org or a personal account, for every repo or for a few.",
},
{
title: "The app opens the onboarding pull request",
body: "On each repo with no dashboard yet: the workflow, a starter sluiceway.yaml, and the secrets each stack needs, by name.",
},
{
title: "You add the secrets and merge",
body: "Adding the secrets is your step. The first scan then previews every stack and writes the dashboard issue.",
},
{
title: "You tick a box",
body: "On the dashboard issue or in the console. Your workflow previews the stack again and deploys it.",
},
]}
/>

## Install the app

Open <a href="https://github.com/apps/sluiceway">the Sluiceway app on GitHub</a>, 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.

Then sign in at <a href="https://console.sluiceway.dev/">console.sluiceway.dev</a> with GitHub.
The console lists every repo of the installation. A repo with no dashboard yet is under Not set up
yet, with its onboarding pull request.

Only repos with a dashboard count toward your plan, so installing on a whole org costs nothing.
The Free plan covers 5 of them. See <a href={page("hosted-app/plans")}>Plans and adopted repos</a>.

## Merge the onboarding pull request

The pull request is written by the action's own init and check, so it holds what adding the
action by hand would give you:

- **The workflow** at `.github/workflows/deploy-dashboard.yml`, which runs `sluiceway/sluiceway@v0`.
- **A starter `sluiceway.yaml`**, or one line added to the one the repo has: `sluiceway[bot]` in
`recordWriters`, so ticks can go through the app.
- **Its description**: the stacks the check found, the dashboard the repo would get, and per
stack the credentials it needs, as names, with a link to the repo's Actions secrets settings.

Before you merge, add the secrets it names, or load them another way from
<a href={page("guides/credentials")}>Credentials and your own tooling</a>. When your
infrastructure cannot be reached from GitHub's runners, point the workflow at a
<a href={page("guides/workflow#self-hosted-runners")}>self-hosted runner</a>.

Then merge. The push of that merge starts the first scan: it previews every stack and writes the
dashboard issue with one row per stack. Nothing deploys until someone ticks a box. The console
moves the repo from Not set up yet to its stacks, and when the first scan fails it says why in one
sentence, with a link to the page that fixes it.

<a href={page("hosted-app/install")}>Install the app</a> has the permissions GitHub asks you to
accept, everything the pull request holds, and what to do when there is none.

<Callout kind="note" title="Decide who may deploy before you merge">
A tick asks for a deploy of that stack, production included. Without a tick rule, anyone with
write access to the repo can tick. Narrow it with <code>tickers</code> in
<code>sluiceway.yaml</code>, or gate the deploy job with a GitHub Environment. See{" "}
<a href={page("guides/security#who-can-tick")}>Who can tick</a>.
</Callout>

## Tick

Tick a stack's box on the dashboard issue, or in the console on the repo's Dashboard tab. The app
picks the tick up within about a second, judges it with the action's own tick rule, opens the
deployment record and starts your workflow. Your runner previews the stack again and deploys only
when the change is the one you ticked.

<a href={ROUTES.usingTheDashboard}>Using the dashboard</a> explains the rows and the boxes, and
<a href={page("hosted-app/using-the-app")}>Using the app</a> the console.

## Without the app

The action runs on its own too: the same deploys, with the dashboard issue as the only dashboard.
<a href={ROUTES.runTheActionYourself}>Run the action yourself</a> takes you through it step by
step. <a href={ROUTES.appAndAction}>How the app and the action work together</a> says what the
app adds, and what happens to a tick when the app is down.
75 changes: 75 additions & 0 deletions src/content/docs/get-started/the-app-and-the-action.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
title: How the app and the action work together
description: The app picks a tick up by webhook and starts your workflow, which deploys in your runners with your credentials, and ticks still deploy when the app is down.
sidebar:
label: The app and the action
headerPicture:
picture: deploying-0
alt: A stack is deploying, the gate is open, one crate goes through it and water runs downstream
---

import { glossary, page, ROUTES } from "../../../lib/routes";

<p class="sw-lead">
The action does the work: it scans, previews and deploys, in your GitHub Actions, with your
credentials. The app sets it up, makes a tick start sooner, and adds the console. It never
replaces the action.
</p>

## Where deploys run

Every deploy runs in your own GitHub Actions, on your runners, with your own cloud credentials,
with or without the app. The app holds no cloud credential, never asks for one, and runs no
infrastructure as code: it never previews or deploys a stack. See
<a href={page("hosted-app/what-the-app-keeps")}>What the app keeps and never keeps</a>.

## A tick with the app

When you tick a box on the dashboard issue:

1. **GitHub sends the app a webhook**, and the app picks the tick up within about a second.
2. **The app judges the tick** with the action's own code: the same
<a href={glossary("Tick rule")}>tick rule</a> your workflow uses, and your permission on the
repo, read live from GitHub.
3. **The app opens the <a href={glossary("Deployment record")}>deployment record</a>** with you
as the ticker, and starts your workflow.
4. **Your workflow deploys the record.** It previews the stack again and deploys only when the
fresh preview gives the diff hash you approved. A change that moved in between is refused.

A tick in the console, or from the command line, takes the same path from step 2.

## The fallback: the action's own resolve

The action judges ticks itself, as it always has. The edit of the issue starts your workflow, and
its `resolve` step checks who ticked, opens the record and deploys it, about 20 seconds after the
tick. With the app installed, that step stays in place as the fallback.

- **When the app has opened the record first**, `resolve` finds it open and does nothing more, so
the stack deploys once.
- **When the app is down**, ticks on the issue still deploy, at the old speed. A down app costs a
tick nothing but the time it saves.

Nothing needs to change in your workflow for either path. See
<a href={page("guides/workflow")}>The workflow</a> for what each event runs.

## What recordWriters allows

Ticks from the console and the command line go through the app alone, so the workflow has to
accept the records the app opens. That takes one line in `sluiceway.yaml`, which the onboarding
pull request adds:

```yaml title="sluiceway.yaml"
recordWriters:
- sluiceway[bot]
```

Without it, the app judges no tick on that repo: ticks on the issue deploy through the action's
own `resolve`, and a box in the console opens the dashboard on GitHub. See
<a href={page("hosted-app/install#the-recordwriters-line")}>The recordWriters line</a>.

## The action alone

Without the app, you get the same deploys, with the dashboard issue as the only dashboard: no
console, no org view, and ticks that start at the action's own speed. It is the path for a team
that grants no third-party app. <a href={ROUTES.runTheActionYourself}>Run the action yourself</a>
sets it up step by step.
4 changes: 3 additions & 1 deletion src/content/docs/hosted-app/install.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { page } from "../../../lib/routes";

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.
<a href={page("get-started/the-app-and-the-action")}>How the app and the action work together</a>
follows a tick through both.

<Steps
items={[
Expand Down Expand Up @@ -45,7 +47,7 @@ Open <a href="https://github.com/apps/sluiceway">the Sluiceway app on GitHub</a>
**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 <a href="https://app.sluiceway.dev/">app.sluiceway.dev</a> with GitHub. You see
Then sign in at <a href="https://console.sluiceway.dev/">console.sluiceway.dev</a> 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
Expand Down
4 changes: 2 additions & 2 deletions src/content/docs/hosted-app/using-the-app.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { glossary, page, ROUTES } from "../../../lib/routes";
words. Every row it draws leads back to its repo and its dashboard on GitHub.
</p>

Sign in at <a href="https://app.sluiceway.dev/">app.sluiceway.dev</a> with GitHub. The app reads
Sign in at <a href="https://console.sluiceway.dev/">console.sluiceway.dev</a> 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.

Expand All @@ -22,7 +22,7 @@ has the tabs Overview, Insights, Audit and Settings. A repo has Dashboard, Insig

## The org view

The org's Overview tab is every stack of every repo the app covers, at `app.sluiceway.dev/<org>`.
The org's Overview tab is every stack of every repo the app covers, at `console.sluiceway.dev/<org>`.

- **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.
Expand Down
3 changes: 2 additions & 1 deletion src/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import "../../styles/landing.css";

<div class="sw-card-grid">
<Card title="Get started" href={ROUTES.getStarted} bar>
Add Sluiceway one step at a time, starting with a check that changes nothing.
Install the GitHub App. It opens one pull request with the workflow and a first <code>sluiceway.yaml</code>, and the console guides the rest.
</Card>
<Card title="Credentials" href={ROUTES.credentials} tilt="left">
Load your cloud and state credentials on Sluiceway's step or in a step right before it, or name an env file that Sluiceway reads and masks.
Expand All @@ -57,6 +57,7 @@ import "../../styles/landing.css";
</div>

<div class="sw-landing__guides">
<p>Without the app, <a href={ROUTES.runTheActionYourself}>run the action yourself</a>: the same deploys, with the issue as the only dashboard. <a href={ROUTES.appAndAction}>How the app and the action work together</a> says what each one does.</p>
<p>The guides, in order:</p>
<ul>
<li><a href={page("guides/workflow")}>The workflow</a></li>
Expand Down
4 changes: 4 additions & 0 deletions src/lib/anchor-redirects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export const ANCHOR_REDIRECTS: Record<string, Record<string, string>> = {
"4-load-your-credentials": "guides/credentials",
"start-read-only": "guides/read-only-trial",
"pin-a-commit": "guides/workflow#pin-a-commit",
// Getting started leads with the app since 2026-09-26, and the steps without it moved to a
// page of their own.
requirements: "get-started/run-the-action-yourself#requirements",
"get-started": "get-started/run-the-action-yourself#get-started",
},
// v0.12.0 moved the README's "What it does not do yet" into the Limits of the dashboard's use.
"not-in-v1": {
Expand Down
33 changes: 27 additions & 6 deletions src/lib/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ export const SLICE_3 = {
theHeader: "the-header",
};

/**
* The pages that open Getting started, written in src/content/docs. The page that adds the
* action by hand is read from the action, under `runTheActionYourself`.
*/
export const GET_STARTED = {
withTheApp: "get-started",
runTheActionYourself: "get-started/run-the-action-yourself",
together: "get-started/the-app-and-the-action",
} as const;

/**
* The pages about the hosted app, in sidebar order. They are written in src/content/docs: the
* action has no Markdown about the app.
Expand Down Expand Up @@ -468,16 +478,24 @@ export function pages(repoUrl: string, base: string): Page[] {
],
},
{
id: "get-started",
title: "Get started",
id: GET_STARTED.runTheActionYourself,
title: "Run the action yourself",
description:
"Add Sluiceway to a repo of infrastructure as code: check what you need, run the check, then add the workflow, the tick rule and your credentials.",
"Add Sluiceway to a repo without the app: check what you need, run the check, then add the workflow, the tick rule and your credentials.",
source: README,
headerPicture: {
picture: "first-run",
alt: "The scan found no stacks yet: Penny stands beside an empty channel",
},
parts: [
generated(
README,
[
"The action runs on its own, with no app installed and nothing hosted: the same scans, the same ticks and the same deploys, in your runners with your credentials, and the dashboard issue is the only dashboard. It is the path for a team that grants no third-party app, or that wants to set every step up by hand.",
"",
`With the app, [one pull request](${base}/${GET_STARTED.withTheApp}/) holds the workflow and a first \`sluiceway.yaml\`, and the console guides the rest. [How the app and the action work together](${base}/${GET_STARTED.together}/) says what the app adds and what stays the same.`,
].join("\n"),
),
// First, so a reader checks the runner and tool versions before any step. Here since the
// README moved it, so get-started/#requirements keeps working.
sectionPart("docs/reference.md", "Requirements"),
Expand Down Expand Up @@ -807,8 +825,9 @@ export function covered(): Covered[] {
const slugs = githubSlugs(README);
return [
{ link: README, page: SLICE_3.overview },
// Get started opens on Requirements, so a link to the README section lands on the page top.
{ link: `${README}#get-started`, page: "get-started" },
// Run the action yourself opens on Requirements, so a link to the README section lands on
// the page top.
{ link: `${README}#get-started`, page: GET_STARTED.runTheActionYourself },
// The index of the action's docs: the sidebar is this site's.
{ link: "docs/README.md", page: SLICE_3.overview },
{ link: "docs/adr", page: "why" },
Expand Down Expand Up @@ -994,7 +1013,9 @@ export function sidebar(): SidebarItem[] {
label: "Start",
items: [
{ label: "Overview", link: "/" },
{ slug: "get-started" },
{ slug: GET_STARTED.withTheApp },
{ slug: GET_STARTED.runTheActionYourself },
{ slug: GET_STARTED.together },
...(written(SLICE_3.whatItLooksLike) ? [{ slug: SLICE_3.whatItLooksLike }] : []),
{ slug: "how-it-works" },
{ slug: "using-the-dashboard" },
Expand Down
7 changes: 5 additions & 2 deletions src/lib/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, HOSTED_APP, pages, SLICE_3, urlFor } from "./pages";
import { GET_STARTED, githubSlug, HOSTED_APP, pages, SLICE_3, urlFor } from "./pages";
import { REPO_URL } from "./source";

const BASE = import.meta.env.BASE_URL.replace(/\/$/, "");
Expand All @@ -17,6 +17,7 @@ export function page(id: string): string {
SLICE_3.overview,
SLICE_3.whatItLooksLike,
SLICE_3.theHeader,
...Object.values(GET_STARTED),
...Object.values(HOSTED_APP),
]);
const path = id.split("#")[0] ?? "";
Expand All @@ -27,7 +28,9 @@ export function page(id: string): string {
}

export const ROUTES = {
getStarted: page("get-started"),
getStarted: page(GET_STARTED.withTheApp),
runTheActionYourself: page(GET_STARTED.runTheActionYourself),
appAndAction: page(GET_STARTED.together),
configuration: page("guides/configuration"),
credentials: page("guides/credentials"),
usingTheDashboard: page("using-the-dashboard"),
Expand Down
8 changes: 6 additions & 2 deletions test/links.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ const blob = `${REPO_URL}/blob/${TAG}`;
describe("resolveLink", () => {
const cases: [from: string, href: string, want: string][] = [
// A section of another file that a page shows, from docs/.
["docs/workflow.md", "reference.md#requirements", "/docs/get-started/#requirements"],
[
"docs/workflow.md",
"reference.md#requirements",
"/docs/get-started/run-the-action-yourself/#requirements",
],
[
"docs/security.md",
"workflow.md#with-github-environments",
"/docs/guides/workflow/#with-github-environments",
],
// A README section shown without its heading lands on the page itself.
["docs/workflow.md", "../README.md#get-started", "/docs/get-started/"],
["docs/workflow.md", "../README.md#get-started", "/docs/get-started/run-the-action-yourself/"],
// A file's h1 is the page title: a link to it lands on the page, as on GitHub.
["docs/read-only-trial.md", "workflow.md#the-workflow", "/docs/guides/workflow/"],
// Whole files, and headings in them.
Expand Down
Loading