Skip to content
Open
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
5 changes: 5 additions & 0 deletions .github/workflows/vale-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ jobs:
# Install Markdownlint
npm install -g markdownlint-cli
markdownlint --version
# Install mdx2vast. Vale shells out to it for every .mdx file; without
# it Vale emits "E100 [lintMDX] Runtime error" per file and moves on,
# so the whole .mdx corpus goes unlinted.
npm install -g mdx2vast
mdx2vast --version
- name: Run Quality Check
run: |
chmod +x docs-linter/lint.sh
Expand Down
42 changes: 40 additions & 2 deletions docs-linter/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,40 @@ if [ ! -d "$TARGET_DIR" ]; then
exit 1
fi

# --- TOOLCHECK ---
# Vale shells out to mdx2vast for every .mdx file. When that binary is missing,
# Vale does not fail the run -- it emits one "E100 [lintMDX] Runtime error" per
# file and keeps going, so every .mdx goes unlinted while the summary still
# looks healthy. Report what is actually installed before linting anything.
echo "🔧 Toolcheck..."

check_tool() {
local name="$1" required="$2" note="$3"
local path version
path=$(command -v "$name" 2>/dev/null)
if [ -z "$path" ]; then
if [ "$required" = "required" ]; then
echo " ❌ $name: NOT FOUND -- $note"
MISSING_REQUIRED=1
else
echo " ⚠️ $name: NOT FOUND -- $note"
fi
return
fi
version=$("$name" --version 2>&1 | head -n1 | tr -d '\r')
echo " ✅ $name: ${version:-unknown} ($path)"
}

MISSING_REQUIRED=0
check_tool vale required "install from https://vale.sh (CI pins the version in .github/workflows/vale-check.yml)"
check_tool markdownlint required "npm install -g markdownlint-cli"
check_tool mdx2vast required "npm install -g mdx2vast -- without it every .mdx file is skipped"

if [ "$MISSING_REQUIRED" -ne 0 ]; then
echo "❌ Toolchain incomplete. Install the tools above and re-run."
exit 1
fi

# --- TEMP FILES ---
LIST_FILE=$(mktemp)
VALE_LOG=$(mktemp)
Expand All @@ -27,7 +61,7 @@ MD_CLEAN=$(mktemp)
echo "🎯 Gathering files from '$TARGET_DIR'..."

find "$TARGET_DIR" -type f \( -name "*.md" -o -name "*.mdx" \) \
| grep -vE "/(node_modules|versioned_docs)/" \
| grep -vE "/([^/]*_)?(node_modules|versioned_docs)/" \
| grep -v "/_" \
> "$LIST_FILE"

Expand Down Expand Up @@ -75,8 +109,11 @@ awk -F: '
V_ERR=$(grep -c " error " "$VALE_CLEAN" || true)
V_WARN=$(grep -c " warning " "$VALE_CLEAN" || true)
V_SUG=$(grep -c " suggestion " "$VALE_CLEAN" || true)
# E100 lines carry no severity token, so they slip past the three greps above.
# Count them separately: each one is a file Vale gave up on, not a clean file.
V_RUN=$(grep -c "^E100 " "$VALE_CLEAN" || true)
MD_ERR=$(grep -c "^ " "$MD_CLEAN" || true)
TOTAL=$((V_ERR + V_WARN + V_SUG + MD_ERR))
TOTAL=$((V_ERR + V_WARN + V_SUG + V_RUN + MD_ERR))

echo -e "\n========================================================"
echo "📊 LINT SUMMARY"
Expand All @@ -85,6 +122,7 @@ echo " 📄 Files Scanned: $FILE_COUNT"
echo " 🛑 Vale Errors: $V_ERR"
echo " ⚠️ Vale Warnings: $V_WARN"
echo " 💡 Vale Suggestions: $V_SUG"
echo " 💥 Vale Runtime Errors: $V_RUN"
echo " 🧹 Markdownlint Issues: $MD_ERR"
echo "--------------------------------------------------------"
echo " 🚨 TOTAL ISSUES: $TOTAL"
Expand Down
2 changes: 1 addition & 1 deletion docs-linter/styles/config/vocabularies/terms/accept.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,6 @@ repo
Customer Connect
Live Audit
HAL
GET
(?i)get
POST
vs
18 changes: 14 additions & 4 deletions packages/docusaurus-theme/src/products.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export type ProductId =
| 'dataconnector'
| 'zlan'
| 'openziti'
| 'zrok';
| 'zrok'
| 'ziticni';

export interface Product {
id: ProductId;
Expand Down Expand Up @@ -92,13 +93,20 @@ export const PRODUCTS: Record<ProductId, Product> = {
logoDark: `${IMG}/zrok-1.0.0-rocket-green.svg`,
description: 'Secure peer-to-peer sharing built on OpenZiti.',
},
ziticni: {
id: 'ziticni',
label: 'ziti-cni',
path: 'ziti-cni/intro',
logo: NF_LOGO,
description: 'Kubernetes CNI plugin for zero-trust pod networking on OpenZiti.',
},
};

/** Visual layout of the picker -- order here = order on screen. */
const PICKER_LAYOUT: { header: string; items: ProductId[] }[] = [
{ header: 'Cloud SaaS', items: ['console', 'customerconnect', 'dataconnector', 'frontdoor'] },
{ header: 'Self-Hosted Licensed', items: ['selfhosted', 'zlan'] },
{ header: 'Self-Hosted Open Source', items: ['openziti', 'zrok'] },
{ header: 'Self-Hosted Open Source', items: ['openziti', 'zrok', 'ziticni'] },
];

export interface PickerLink {
Expand Down Expand Up @@ -146,6 +154,7 @@ export const dataconnectorLink: PickerLink = linkFor('dataconnector', '/docs
export const zlanLink: PickerLink = linkFor('zlan', '/docs');
export const openzitiLink: PickerLink = linkFor('openziti', '/docs');
export const zrokLink: PickerLink = linkFor('zrok', '/docs');
export const ziticniLink: PickerLink = linkFor('ziticni', '/docs');

export const consoleLinkAbs: PickerLink = linkFor('console', DOCS_BASE);
export const customerconnectLinkAbs: PickerLink = linkFor('customerconnect', DOCS_BASE);
Expand All @@ -155,6 +164,7 @@ export const dataconnectorLinkAbs: PickerLink = linkFor('dataconnector', DOC
export const zlanLinkAbs: PickerLink = linkFor('zlan', DOCS_BASE);
export const openzitiLinkAbs: PickerLink = linkFor('openziti', DOCS_BASE);
export const zrokLinkAbs: PickerLink = linkFor('zrok', DOCS_BASE);
export const ziticniLinkAbs: PickerLink = linkFor('ziticni', DOCS_BASE);

/**
* Picker columns for the unified docs site at netfoundry.io/docs.
Expand All @@ -166,7 +176,7 @@ export const zrokLinkAbs: PickerLink = linkFor('zrok', DOC
export const unifiedPickerColumns: PickerColumn[] = [
{ header: 'Cloud SaaS', links: [consoleLink, customerconnectLink, dataconnectorLink, frontdoorLink] },
{ header: 'Self-Hosted Licensed', links: [selfhostedLink, zlanLink] },
{ header: 'Self-Hosted Open Source', links: [openzitiLink, zrokLink] },
{ header: 'Self-Hosted Open Source', links: [openzitiLink, zrokLink, ziticniLink] },
];

/**
Expand All @@ -176,5 +186,5 @@ export const unifiedPickerColumns: PickerColumn[] = [
export const subsitePickerColumns: PickerColumn[] = [
{ header: 'Cloud SaaS', links: [consoleLinkAbs, customerconnectLinkAbs, dataconnectorLinkAbs, frontdoorLinkAbs] },
{ header: 'Self-Hosted Licensed', links: [selfhostedLinkAbs, zlanLinkAbs] },
{ header: 'Self-Hosted Open Source', links: [openzitiLinkAbs, zrokLinkAbs] },
{ header: 'Self-Hosted Open Source', links: [openzitiLinkAbs, zrokLinkAbs, ziticniLinkAbs] },
];
9 changes: 6 additions & 3 deletions scripts/vercel-ignore.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ fi

git fetch origin main --depth=100

if git diff --quiet origin/main...HEAD -- "$WATCH_PATH"; then
echo "No changes under $WATCH_PATH vs origin/main -- skipping build."
# Every project's build also depends on @netfoundry/docusaurus-theme, so a
# change there is relevant regardless of which single path a given project
# was told to watch.
if git diff --quiet origin/main...HEAD -- "$WATCH_PATH" packages/docusaurus-theme; then
echo "No changes under $WATCH_PATH or packages/docusaurus-theme vs origin/main -- skipping build."
exit 0
else
echo "Changes detected under $WATCH_PATH vs origin/main -- building."
echo "Changes detected under $WATCH_PATH or packages/docusaurus-theme vs origin/main -- building."
exit 1
fi

Expand Down
45 changes: 42 additions & 3 deletions unified-doc/build-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
// --platform-branch=BRANCH Branch for netfoundry/platform-doc (default: main)
// --data-connector-branch=BRANCH Branch for netfoundry/nf-data-connector (default: main)
// --customer-connect-branch=BRANCH Branch for netfoundry/customer-connect-docs (default: main)
// --ziti-cni-branch=BRANCH Branch for netfoundry/ziti-cni (default: main)
// --clean Wipe _remotes and .docusaurus cache before building
// --lint-only Run lint checks only; skip build
// --qualifier=VALUE Append VALUE to output dir (e.g. --qualifier=-preview -> build-preview)
Expand All @@ -31,7 +32,7 @@
// -h, --help Show this help and exit
//
// ENVIRONMENT VARIABLES
// GH_ZITI_CI_REPO_ACCESS_PAT GitHub PAT for ziti-doc and zlan (falls back to SSH)
// GH_ZITI_CI_REPO_ACCESS_PAT GitHub PAT for ziti-doc, zlan, and ziti-cni (falls back to SSH)
// BB_REPO_TOKEN_FRONTDOOR Bitbucket token for zrok-connector (falls back to SSH)
// BB_REPO_TOKEN_ONPREM Bitbucket token for k8s-on-prem-installations (falls back to SSH)
// BB_REPO_TOKEN_PLATFORM_DOC Bitbucket token for platform-doc (falls back to SSH)
Expand All @@ -41,7 +42,8 @@
// DOCUSAURUS_BUILD_MASK Hex bitmask: 0x1=openziti 0x2=frontdoor 0x4=selfhosted
// 0x8=zrok 0x10=zlan 0x20=platform
// 0x40=data-connector 0x80=llm-gateway 0x100=mcp-gateway
// 0x200=customer-connect 0x3FF=all (config default: 0x3FF)
// 0x200=customer-connect 0x400=ziti-cni
// 0x7FF=all (config default: 0x7FF)
// DOCUSAURUS_PUBLISH_ENV Set to 'prod' to use production Algolia index
// NO_MINIFY Set to any value to pass --no-minify to Docusaurus
// IS_VERCEL Set to 'true' on Vercel preview deployments
Expand All @@ -65,6 +67,7 @@ const GIT = "git";
const YARN = isWin ? "yarn.cmd" : "yarn";
const VALE = "vale";
const MDLINT = isWin ? "markdownlint.cmd" : "markdownlint";
const MDX2VAST = isWin ? "mdx2vast.cmd" : "mdx2vast";

// On Windows, markdownlint runs through cmd.exe (~8191-char command-line cap),
// so lint files in small batches there; larger batches elsewhere.
Expand Down Expand Up @@ -140,6 +143,7 @@ const branches = {
platform : "main",
dataConnector : "main",
customerConnect: "main",
zitiCni : "lipscomb-cni-docs-draft",
};
const BRANCH_FLAG = {
"--ziti-doc-branch": "zitiDoc",
Expand All @@ -150,6 +154,7 @@ const BRANCH_FLAG = {
"--platform-branch": "platform",
"--data-connector-branch": "dataConnector",
"--customer-connect-branch": "customerConnect",
"--ziti-cni-branch": "zitiCni",
};

let clean = false;
Expand Down Expand Up @@ -202,6 +207,7 @@ console.log(` BRANCH_ZLAN='${branches.zlan}'`);
console.log(` BRANCH_PLATFORM='${branches.platform}'`);
console.log(` BRANCH_DATA_CONNECTOR='${branches.dataConnector}'`);
console.log(` BRANCH_CUSTOMER_CONNECT='${branches.customerConnect}'`);
console.log(` BRANCH_ZITI_CNI='${branches.zitiCni}'`);
console.log(` CLEAN=${clean ? 1 : 0}`);
console.log(` IS_VERCEL='${process.env.IS_VERCEL ?? ""}'`);
console.log(` node: ${process.version}`);
Expand Down Expand Up @@ -275,6 +281,14 @@ function authUrl(url) {
console.error("🔑 Using SSH for customer-connect-docs");
return "git@bitbucket.org:netfoundry/customer-connect-docs.git";
}
if (url.includes("ziti-cni")) {
if (process.env.GH_ZITI_CI_REPO_ACCESS_PAT) {
console.error("🔑 Using GH_ZITI_CI_REPO_ACCESS_PAT token for ziti-cni");
return gh("netfoundry/ziti-cni.git", process.env.GH_ZITI_CI_REPO_ACCESS_PAT);
}
console.error("🔑 Using SSH for ziti-cni");
return "git@github.com:netfoundry/ziti-cni.git";
}
return url; // public (e.g. openziti/zrok) — no auth needed
}

Expand Down Expand Up @@ -401,8 +415,31 @@ function cleanLog(s) {
.join("\n");
}

// Report the resolved version of each linting tool. Vale shells out to mdx2vast
// for .mdx input; when that binary is missing Vale keeps going and emits one
// "E100 [lintMDX] Runtime error" per file, so the .mdx corpus goes unlinted
// without anything in the summary saying so.
function toolcheck() {
console.log("🔧 Toolcheck...");
const tools = [
[VALE, "install from https://vale.sh"],
[MDLINT, "npm install -g markdownlint-cli"],
[MDX2VAST, "npm install -g mdx2vast -- without it every .mdx file is skipped"],
];
for (const [cmd, note] of tools) {
const r = capture(cmd, ["--version"]);
if (r.error || r.status !== 0) {
console.log(` ⚠️ ${cmd}: NOT FOUND -- ${note}`);
continue;
}
const version = (r.stdout || "").split(/\r?\n/)[0].trim();
console.log(` ✅ ${cmd}: ${version || "unknown"}`);
}
}

function lintDocs() {
console.log("🔍 Starting Quality Checks...");
toolcheck();

const potentialTargets = [
join(remotesDir, "zlan", "docusaurus", "docs"),
Expand All @@ -413,6 +450,7 @@ function lintDocs() {
join(remotesDir, "platform", "docusaurus", "docs"),
join(remotesDir, "data-connector", "docusaurus", "docs"),
join(remotesDir, "customer-connect", "docusaurus", "docs"),
join(remotesDir, "ziti-cni", "docusaurus", "docs"),
];
const validTargets = potentialTargets.filter((t) => existsSync(t));

Expand Down Expand Up @@ -516,6 +554,7 @@ cloneOrUpdate("https://github.com/openziti/zrok.git", "zrok", branches.zrok);
cloneOrUpdate("https://bitbucket.org/netfoundry/platform-doc.git", "platform", branches.platform);
cloneOrUpdate("https://bitbucket.org/netfoundry/nf-data-connector.git", "data-connector", branches.dataConnector);
cloneOrUpdate("https://bitbucket.org/netfoundry/customer-connect-docs.git", "customer-connect", branches.customerConnect);
cloneOrUpdate("https://github.com/netfoundry/ziti-cni.git", "ziti-cni", branches.zitiCni);

// Remove stale Docusaurus caches/outputs left inside cloned remotes.
console.log("Cleaning stale build artifacts from remotes...");
Expand Down Expand Up @@ -578,7 +617,7 @@ console.log(line);
console.log("DOCUSAURUS BUILD");
console.log(line);
console.log(` Output dir: ${outDir}`);
console.log(` Build mask: ${process.env.DOCUSAURUS_BUILD_MASK ?? "0x3FF (config default)"}`);
console.log(` Build mask: ${process.env.DOCUSAURUS_BUILD_MASK ?? "0x7FF (config default)"}`);
console.log(` No-minify: ${process.env.NO_MINIFY ? "true" : "false"}`);
console.log(line);

Expand Down
9 changes: 6 additions & 3 deletions unified-doc/build-docs.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ param(
[string]$PlatformBranch = "main",
[string]$DataConnectorBranch = "main",
[string]$CustomerConnectBranch = "main",
[string]$ZitiCniBranch = "main",

# Remove all _remotes content and .docusaurus cache before building
[switch]$Clean,
Expand All @@ -49,10 +50,11 @@ param(

# Docusaurus build mask (hex). 0x1=openziti, 0x2=frontdoor, 0x4=selfhosted,
# 0x8=zrok, 0x10=zlan, 0x20=platform, 0x40=data-connector,
# 0x80=llm-gateway, 0x100=mcp-gateway, 0x200=customer-connect, 0x3FF=all.
# 0x80=llm-gateway, 0x100=mcp-gateway, 0x200=customer-connect,
# 0x400=ziti-cni, 0x7FF=all.
# Only forwarded (as $env:DOCUSAURUS_BUILD_MASK) when explicitly set;
# otherwise build-docs.mjs lets docusaurus.config.ts default it (0x3FF).
[string]$BuildMask = "0x3FF"
# otherwise build-docs.mjs lets docusaurus.config.ts default it (0x7FF).
[string]$BuildMask = "0x7FF"
)

Set-StrictMode -Version Latest
Expand All @@ -72,6 +74,7 @@ if ($PSBoundParameters.ContainsKey('ZlanBranch')) { $mjsArgs += "--zla
if ($PSBoundParameters.ContainsKey('PlatformBranch')) { $mjsArgs += "--platform-branch=$PlatformBranch" }
if ($PSBoundParameters.ContainsKey('DataConnectorBranch')) { $mjsArgs += "--data-connector-branch=$DataConnectorBranch" }
if ($PSBoundParameters.ContainsKey('CustomerConnectBranch')){ $mjsArgs += "--customer-connect-branch=$CustomerConnectBranch" }
if ($PSBoundParameters.ContainsKey('ZitiCniBranch')) { $mjsArgs += "--ziti-cni-branch=$ZitiCniBranch" }
if ($Clean) { $mjsArgs += "--clean" }
if ($LintOnly) { $mjsArgs += "--lint-only" }
if ($SkipLinkedDoc) { $mjsArgs += "-l" }
Expand Down
Loading
Loading