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
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
# openpreflight website

The marketing site, published at **https://openpreflight.xyz**. Astro +
Tailwind v4, static output, one page plus a branded 404.
Tailwind v4, static output. Positioning pages plus a branded 404; how-to lives
on [docs.openpreflight.xyz](https://docs.openpreflight.xyz).

Reference documentation lives on a separate site,
[openpreflight/docs](https://github.com/openpreflight/docs), at
Expand All @@ -32,9 +33,12 @@ npm run dev

## Structure

- `src/pages/index.astro`: the marketing page
- `src/pages/index.astro`: the marketing homepage
- `src/pages/`: Product, Why, Self-hosted, Security, and the other hub routes
- `src/pages/404.astro`: branded not-found
- `src/components/`: Header, CheckRunPanel, RunFlow
- `src/layouts/MarketingPage.astro`: shared header/footer chrome for inner pages
- `src/lib/site.ts`: nav, footer groups, CTAs
- `src/components/`: Header, CheckRunPanel, RunFlow, Rivelle blocks
- `src/layouts/Layout.astro`: head, meta, OG tags, header/footer chrome
- `src/styles/global.css`: product-green palette, JetBrains Mono, light/dark
via `prefers-color-scheme`
Expand Down
1 change: 1 addition & 0 deletions public/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ See https://openpreflight.xyz/#demo

## Start here

- [Product](https://openpreflight.xyz/product/)
- [Quickstart](https://docs.openpreflight.xyz/start/quickstart/)
- [Configuration](https://docs.openpreflight.xyz/start/configuration/)
- [Register a GitHub App](https://docs.openpreflight.xyz/setup/github-app/)
Expand Down
10 changes: 10 additions & 0 deletions public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ License: Apache-2.0

## Product

- [Product](https://openpreflight.xyz/product/): How a run happens, configuration, non-goals
- [Why](https://openpreflight.xyz/why/): Problem, shape, ceiling
- [Self-hosted](https://openpreflight.xyz/self-hosted/): You run the worker
- [Security](https://openpreflight.xyz/security/): Claims from the security model only
- [Open source](https://openpreflight.xyz/open-source/): Repos, license, contributing
- [Pipeline](https://openpreflight.xyz/pipeline/): `.ci.yml`, not a check registry
- [Integrations](https://openpreflight.xyz/integrations/): GitHub App, Coolify, Docker, process
- [vs GitHub Actions](https://openpreflight.xyz/compare/github-actions/): Complement, do not replace
- [Concepts](https://openpreflight.xyz/concepts/): Check Run, Binding, Job
- [Use cases](https://openpreflight.xyz/use-cases/): Private repos, self-hosted teams, OSS
- [Product overview (Markdown)](https://openpreflight.xyz/index.md): Positioning, what it does, what is out of scope for v1
- [Marketing home](https://openpreflight.xyz/): Human-facing landing page

Expand Down
28 changes: 22 additions & 6 deletions scripts/check-links.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ const dist = join(root, 'dist');
const required = [
'index.html',
'404.html',
'product/index.html',
'why/index.html',
'self-hosted/index.html',
'security/index.html',
'open-source/index.html',
'use-cases/index.html',
'use-cases/private-repos/index.html',
'use-cases/self-hosted-teams/index.html',
'use-cases/open-source/index.html',
'concepts/index.html',
'integrations/index.html',
'integrations/github-app/index.html',
'pipeline/index.html',
'compare/github-actions/index.html',
'favicon.svg',
'favicon.ico',
'favicon-32.png',
Expand Down Expand Up @@ -99,10 +113,12 @@ for (const file of walkHtml(dist)) {
if (!href.startsWith('/')) continue;
if (href.startsWith('//')) continue;

const pathOnly = href.split('#')[0] || '/';
if (
href.startsWith('/_') ||
href === '/' ||
href.endsWith('.svg') ||
pathOnly.startsWith('/_') ||
pathOnly === '/' ||
pathOnly === '' ||
pathOnly.endsWith('.svg') ||
href.endsWith('.png') ||
href.endsWith('.ico') ||
href.endsWith('.xml') ||
Expand All @@ -115,11 +131,11 @@ for (const file of walkHtml(dist)) {
continue;
}

const normalized = href.replace(/\/$/, '') || '';
const normalized = pathOnly.replace(/\/$/, '') || '';
const candidates = [
join(dist, href.replace(/^\//, ''), 'index.html'),
join(dist, pathOnly.replace(/^\//, ''), 'index.html'),
join(dist, `${normalized.replace(/^\//, '')}.html`),
join(dist, href.replace(/^\//, '')),
join(dist, pathOnly.replace(/^\//, '')),
];
if (!candidates.some((c) => existsSync(c))) {
broken.push(`${rel} → ${href}`);
Expand Down
2 changes: 1 addition & 1 deletion src/components/blocks/footer-01.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function Footer01({
<div>
<a
className="inline-flex items-center gap-2.5 font-semibold tracking-tight"
href="#top"
href="/"
>
<span className="grid size-9 place-items-center overflow-hidden rounded-xl bg-primary text-primary-foreground">
<img
Expand Down
9 changes: 4 additions & 5 deletions src/components/blocks/site-header-01.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,10 @@ type SiteHeader01Props = React.ComponentProps<"header"> & {
};

const defaultLinks: SiteHeaderLink[] = [
{ label: "Product", href: "#product" },
{ label: "How it runs", href: "#how" },
{ label: "Pipeline", href: "#pipeline" },
{ label: "Demo", href: "#demo" },
{ label: "Product", href: "/product/" },
{ label: "Why", href: "/why/" },
{ label: "Docs", href: "https://docs.openpreflight.xyz" },
{ label: "GitHub", href: "https://github.com/openpreflight/openpreflight" },
];

function BrandMark() {
Expand Down Expand Up @@ -89,7 +88,7 @@ function SiteHeader01({
<div className="mx-auto flex h-16 w-full max-w-7xl items-center gap-6 px-5 sm:px-8">
<a
className="flex shrink-0 items-center gap-2.5 font-semibold tracking-tight"
href="#top"
href="/"
>
<BrandMark />
{brand}
Expand Down
103 changes: 52 additions & 51 deletions src/components/templates/saas-landing-01.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,9 @@ import { SiteHeader01 } from "@/components/blocks/site-header-01";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { CTA, DOCS, REPO, DEMO_REPO, footerGroups, navLinks, tagline } from "@/lib/site";
import demoRuns from "@/data/demo-runs.json";

const DOCS = "https://docs.openpreflight.xyz";
const REPO = "https://github.com/openpreflight/openpreflight";
const DEMO_REPO = "https://github.com/openpreflight/demo";

function outcomeLabel(conclusion: string) {
switch (conclusion) {
case "success":
Expand Down Expand Up @@ -117,15 +114,9 @@ function SaasLanding01({ className, ...props }: React.ComponentProps<"div">) {
</a>
<SiteHeader01
brand="openpreflight"
links={[
{ label: "Product", href: "#product" },
{ label: "How it runs", href: "#how" },
{ label: "Pipeline", href: "#pipeline" },
{ label: "Demo", href: "#demo" },
{ label: "Docs", href: DOCS },
]}
links={navLinks}
ctaLabel="Quickstart"
ctaHref={`${DOCS}/start/quickstart/`}
ctaHref={CTA.quickstart}
showProfile={false}
/>
<main id="content">
Expand Down Expand Up @@ -275,8 +266,8 @@ function SaasLanding01({ className, ...props }: React.ComponentProps<"div">) {
Actions workflow to write and no pipeline DSL to learn.
</p>
<Button asChild className="mt-6" variant="outline">
<a href={`${DOCS}/understanding/security-model/`}>
Security model <ArrowRight />
<a href="/product/">
How it works <ArrowRight />
</a>
</Button>
</div>
Expand Down Expand Up @@ -491,13 +482,57 @@ docker compose -f compose.prod.yaml up -d`}</code>
</div>
</section>

<section className="px-5 py-24 sm:px-8 sm:py-32" id="before-after">
<div className="mx-auto max-w-7xl">
<h2 className="text-balance text-4xl font-semibold tracking-[-.05em] sm:text-5xl">
Before and after
</h2>
<p className="mt-5 max-w-2xl text-muted-foreground">
Same private GitHub repo. Different place the Check Run comes from.
</p>
<div className="mt-10 grid gap-4 md:grid-cols-2">
<article className="rounded-[1.75rem] border border-foreground/10 p-6 sm:p-8">
<p className="font-mono text-xs text-muted-foreground">Without</p>
<h3 className="mt-3 text-xl font-semibold tracking-[-.03em]">
Actions-only private CI
</h3>
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
Workflow YAML, hosted or self-hosted <code className="font-mono text-xs">actions/runner</code>,
matrices and caches if you need them. Fine when you want that
orchestration layer.
</p>
</article>
<article className="rounded-[1.75rem] border border-foreground/10 p-6 sm:p-8">
<p className="font-mono text-xs text-muted-foreground">With</p>
<h3 className="mt-3 text-xl font-semibold tracking-[-.03em]">
Worker you host + Check Run
</h3>
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
One binary, SQLite, a GitHub App you register, and{" "}
<code className="font-mono text-xs">.ci.yml</code>. Logs stay
on your disk. Not a unification of GitLab and Jenkins.
</p>
</article>
</div>
</div>
</section>

<section className="px-5 py-24 sm:px-8 sm:py-32" id="scope">
<div className="mx-auto max-w-7xl">
<h2 className="text-balance text-4xl font-semibold tracking-[-.05em] sm:text-5xl">
What it isn't
</h2>
<p className="mt-5 max-w-2xl text-muted-foreground">
If any of these are requirements for you, this is the wrong
This is not another CI provider and not a replacement for GitHub
Actions. Actions orchestrates. openpreflight is a small Check Runs
runner you host. They can coexist. See{" "}
<a
className="underline underline-offset-4 hover:text-foreground"
href="/compare/github-actions/"
>
openpreflight and GitHub Actions
</a>
. If any of these are requirements for you, this is the wrong
tool. None of them are in v1:
</p>
<ul className="mt-10 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
Expand Down Expand Up @@ -575,42 +610,8 @@ docker compose -f compose.prod.yaml up -d`}</code>
</main>
<Footer01
brand="openpreflight"
description="A small CI provider for private repos. One Go binary, one SQLite file: register a GitHub App, enable your repos, and get one Check Run per commit."
groups={[
{
title: "Product",
links: [
{ label: "Quickstart", href: `${DOCS}/start/quickstart/` },
{ label: "Pipelines", href: `${DOCS}/using/pipelines/` },
{ label: "API", href: `${DOCS}/using/api/` },
{ label: "FAQ", href: `${DOCS}/start/faq/` },
],
},
{
title: "Project",
links: [
{ label: "GitHub", href: REPO },
{ label: "Releases", href: `${REPO}/releases` },
{ label: "Security", href: `${REPO}/blob/main/SECURITY.md` },
{ label: "License", href: `${REPO}/blob/main/LICENSE` },
],
},
{
title: "Docs",
links: [
{ label: "Documentation", href: DOCS },
{
label: "Configuration",
href: `${DOCS}/start/configuration/`,
},
{
label: "Troubleshooting",
href: `${DOCS}/using/troubleshooting/`,
},
{ label: "ADRs", href: `${DOCS}/adr/003-github-app/` },
],
},
]}
description={tagline}
groups={footerGroups}
showNewsletter={false}
/>
</div>
Expand Down
5 changes: 4 additions & 1 deletion src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const siteOrigin = Astro.site ?? new URL('https://openpreflight.xyz');
const canonical = new URL(Astro.url.pathname, siteOrigin);
const ogImage = new URL('/og.png', siteOrigin);
const llmsTxt = new URL('/llms.txt', siteOrigin);
const isHome = Astro.url.pathname === '/' || Astro.url.pathname === '';
const markdownAlternate = new URL('/index.md', siteOrigin);
const DOCS = 'https://docs.openpreflight.xyz';
const REPO = 'https://github.com/openpreflight/openpreflight';
Expand All @@ -32,7 +33,9 @@ const ogAlt =
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="canonical" href={canonical} />
<link rel="describedby" href={llmsTxt} />
<link rel="alternate" type="text/markdown" href={markdownAlternate} title="Markdown" />
{isHome ? (
<link rel="alternate" type="text/markdown" href={markdownAlternate} title="Markdown" />
) : null}

<title>{title}</title>
<meta name="description" content={description} />
Expand Down
38 changes: 38 additions & 0 deletions src/layouts/MarketingPage.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
import Layout from './Layout.astro';
import { SiteHeader01 } from '../components/blocks/site-header-01';
import { Footer01 } from '../components/blocks/footer-01';
import { CTA, footerGroups, navLinks, tagline } from '../lib/site';

interface Props {
title: string;
description: string;
}

const { title, description } = Astro.props;
---

<Layout title={title} description={description} bare>
<div class="min-h-screen bg-background text-foreground">
<a class="skip-link" href="#content">Skip to content</a>
<SiteHeader01
client:load
brand="openpreflight"
links={navLinks}
ctaLabel="Quickstart"
ctaHref={CTA.quickstart}
showProfile={false}
/>
<main id="content">
<article class="marketing-article mx-auto w-full max-w-3xl px-6 py-16 sm:px-8 sm:py-20">
<slot />
</article>
</main>
<Footer01
brand="openpreflight"
description={tagline}
groups={footerGroups}
showNewsletter={false}
/>
</div>
</Layout>
54 changes: 54 additions & 0 deletions src/lib/site.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/** Shared marketing chrome. Keep claims v1-true: GitHub Check Runs CI only. */

export const DOCS = "https://docs.openpreflight.xyz";
export const REPO = "https://github.com/openpreflight/openpreflight";
export const WEBSITE_REPO = "https://github.com/openpreflight/website";
export const DOCS_REPO = "https://github.com/openpreflight/docs";
export const DEMO_REPO = "https://github.com/openpreflight/demo";

export const CTA = {
quickstart: `${DOCS}/start/quickstart/`,
github: REPO,
demo: "/#demo",
} as const;

export const tagline =
"A small CI provider for private repos. One Go binary, one SQLite file: register a GitHub App, enable your repos, and get one Check Run per commit.";

export const navLinks = [
{ label: "Product", href: "/product/" },
{ label: "Why", href: "/why/" },
{ label: "Docs", href: DOCS },
{ label: "GitHub", href: REPO },
];

export const footerGroups = [
{
title: "Product",
links: [
{ label: "Product", href: "/product/" },
{ label: "Why", href: "/why/" },
{ label: "Pipeline", href: "/pipeline/" },
{ label: "Integrations", href: "/integrations/" },
{ label: "vs GitHub Actions", href: "/compare/github-actions/" },
],
},
{
title: "Trust",
links: [
{ label: "Self-hosted", href: "/self-hosted/" },
{ label: "Security", href: "/security/" },
{ label: "Open source", href: "/open-source/" },
],
},
{
title: "Use",
links: [
{ label: "Use cases", href: "/use-cases/" },
{ label: "Concepts", href: "/concepts/" },
{ label: "Live demo", href: "/#demo" },
{ label: "Quickstart", href: CTA.quickstart },
{ label: "Documentation", href: DOCS },
],
},
];
Loading
Loading