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
11 changes: 11 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ on:
pull_request:
branches: [main]
workflow_dispatch:
inputs:
voice_ref:
description: "wavekat-voice ref (branch/tag/sha) to pull docs from. Empty = default (latest tag)."
required: false
default: ""
cli_ref:
description: "wavekat-cli ref. Empty = default (latest tag)."
required: false
default: ""

permissions:
pull-requests: write
Expand All @@ -27,6 +36,8 @@ jobs:
- run: npm run cf:build
env:
SYNC_DOCS_TOKEN: ${{ secrets.SYNC_DOCS_TOKEN }}
SYNC_DOCS_REF_VOICE: ${{ inputs.voice_ref }}
SYNC_DOCS_REF_CLI: ${{ inputs.cli_ref }}

- name: Deploy preview
id: deploy
Expand Down
64 changes: 58 additions & 6 deletions scripts/sync-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
// — copy from sibling working trees at <path>/<repo-name>
// instead of cloning (no GitHub token required). Falls
// through to clone if a local copy is missing.
// SYNC_DOCS_REF_<SLUG>=<ref>
// — pin a specific source to a branch/tag/sha instead of
// the default "latest tag, fallback to main" logic. E.g.
// SYNC_DOCS_REF_VOICE=feat/onboarding-v2 to preview an
// in-flight branch of wavekat-voice's docs.
//
// Auth note: cloning private repos requires SYNC_DOCS_TOKEN to be set to a
// fine-grained PAT with read access to the relevant repos. In CI it's
Expand Down Expand Up @@ -127,6 +132,35 @@ function tryLocal(source) {

const destDir = join(contentDocsDir, source.slug);
const docsPath = source.docsPath ?? "docs/site";

// Honor SYNC_DOCS_REF_<SLUG> override in local mode too — extracts via
// git archive from that ref (branch/tag/sha). No fallback to main on
// failure, matching the remote-mode behavior.
const overrideEnv = `SYNC_DOCS_REF_${source.slug.toUpperCase()}`;
const override = process.env[overrideEnv];
if (override) {
console.log(` (override ${overrideEnv}=${override})`);
try {
const tmp = join(tmpDir, `${source.slug}__override`);
rmSync(tmp, { recursive: true, force: true });
mkdirSync(tmp, { recursive: true });
execSync(`git archive --format=tar ${override} ${docsPath} | tar -x -C ${tmp}`, {
cwd: repoPath,
stdio: ["ignore", "ignore", "pipe"],
});
if (existsSync(join(tmp, docsPath))) {
copyDocs(join(tmp, docsPath), destDir);
rmSync(tmp, { recursive: true, force: true });
console.log(` ✓ ${source.slug} @ ${override} (local, ${docsPath})`);
return { version: override };
}
rmSync(tmp, { recursive: true, force: true });
} catch (err) {
console.log(` ✗ ${overrideEnv}=${override}: ${err.stderr?.toString().trim() || err.message}`);
}
return null;
}

const tag = latestTagLocal(repoPath);

// Try latest tag first.
Expand Down Expand Up @@ -167,28 +201,46 @@ function tryRemote(source) {
const { slug, repo } = source;
const docsPath = source.docsPath ?? "docs/site";
const url = repoUrl(source);
const tag = latestTagRemote(url);
const refs = [tag, "main"].filter(Boolean);

// Per-source ref override via env var, e.g. SYNC_DOCS_REF_VOICE=feat/onboarding-v2.
// When set, the override is the only ref tried — no tag/main fallback —
// so a typo surfaces as a build error instead of silently shipping main.
const overrideEnv = `SYNC_DOCS_REF_${slug.toUpperCase()}`;
const override = process.env[overrideEnv];
const refs = override
? [override]
: [latestTagRemote(url), "main"].filter(Boolean);

if (override) {
console.log(` (override ${overrideEnv}=${override})`);
}

for (const ref of refs) {
const cloneDir = join(tmpDir, `${slug}__${ref}`);
// Slashes in ref names (e.g. feat/onboarding-v2) need to be flattened so
// they don't turn into nested tmp directories.
const safeRef = ref.replace(/\//g, "__");
const cloneDir = join(tmpDir, `${slug}__${safeRef}`);
rmSync(cloneDir, { recursive: true, force: true });
mkdirSync(cloneDir, { recursive: true });
try {
execSync(
`git clone --depth 1 --branch ${ref} --filter=blob:none --sparse ${url} .`,
{ cwd: cloneDir, stdio: ["ignore", "ignore", "pipe"] }
);
execSync(`git sparse-checkout set ${docsPath}`, { cwd: cloneDir, stdio: "ignore" });
execSync(`git sparse-checkout set ${docsPath}`, {
cwd: cloneDir,
stdio: ["ignore", "ignore", "pipe"],
});
const docsSrc = join(cloneDir, docsPath);
if (existsSync(docsSrc)) {
copyDocs(docsSrc, join(contentDocsDir, slug));
rmSync(cloneDir, { recursive: true, force: true });
console.log(` ✓ ${slug} @ ${ref} (remote, ${docsPath})`);
return { version: ref };
}
} catch {
// try next ref
console.log(` ✗ ${ref}: clone ok but ${docsPath}/ missing on that ref`);
} catch (err) {
console.log(` ✗ ${ref}: ${err.stderr?.toString().trim().split("\n").slice(-2).join(" | ") || err.message}`);
}
rmSync(cloneDir, { recursive: true, force: true });
}
Expand Down
1 change: 1 addition & 0 deletions src/pages/docs/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Product = {

const PRODUCTS: Product[] = [
{ slug: 'cli', label: 'CLI', description: 'Terminal client (wk) for the WaveKat platform.', href: '/docs/cli/' },
{ slug: 'voice', label: 'Voice', description: 'Desktop SIP softphone — call from your Mac, Windows, or Linux.', href: '/docs/voice/' },
];

const entries = await getCollection('docs');
Expand Down
Loading