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
234 changes: 220 additions & 14 deletions src/eleventy/.eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,37 @@ liquidEngine.registerTag('dynamic_include', {
version = version.replace(/^["']|["']$/g, '');
pathSuffix = pathSuffix.replace(/^["']|["']$/g, '');

// If version is empty or looks like a variable, try to get from context
// If version is a bare variable name (not starting with 'v' followed by a digit),
// try to resolve it from context
if (version && !version.match(/^v\d/)) {
// Try to resolve as a variable name (handles things like version_prefix)
const varValue = ctx.get([version]);
if (varValue !== undefined && varValue !== null) {
version = String(varValue).trim();
} else {
// Also try dotted paths like page.version.version
const parts = version.split('.');
const dotValue = ctx.get(parts);
if (dotValue !== undefined && dotValue !== null) {
version = String(dotValue).trim();
}
}
}

// If version is still empty or looks like an unresolved variable, try to get from context
if (!version || version.includes('{{') || version === 'page.version.version') {
let v = ctx.get(['version', 'version']);
if (!v) v = ctx.get(['page', 'version', 'version']);
version = v || 'v26.1';
}

// If version has a trailing slash, use it as-is for path building
// This supports version_prefix patterns like "v26.1/"
const versionHasTrailingSlash = version.endsWith('/');
if (versionHasTrailingSlash) {
version = version.slice(0, -1); // Remove trailing slash, will be handled by path join
}

// Ensure pathSuffix starts with / for proper path joining
if (pathSuffix && !pathSuffix.startsWith('/')) {
pathSuffix = '/' + pathSuffix;
Expand Down Expand Up @@ -217,6 +241,95 @@ liquidEngine.registerTag('dynamic_include', {
}
});

// Import EleventyFetch at module level for use in liquidEngine's remote_include
const EleventyFetch = require("@11ty/eleventy-fetch");

// remote_include tag for module-level liquidEngine
// Supports: {% remote_include "URL" %} or {% remote_include "URL", "START MARKER", "END MARKER" %}
liquidEngine.registerTag('remote_include', {
parse: function(tagToken) { this.args = tagToken.args; },
render: async function(ctx, emitter) {
try {
// Helper to resolve {{ variable }} expressions
const resolveVars = (str) => {
const varPattern = /\{\{\s*([^}]+)\s*\}\}/g;
return str.replace(varPattern, (match, varPath) => {
const parts = varPath.trim().split('.');
let resolved = ctx.get(parts);
if (resolved === undefined && parts[0] === 'page' && parts[1] === 'version') {
resolved = ctx.get(parts.slice(1));
}
if (resolved === undefined && parts[0] === 'page' && parts[1] === 'release_info') {
resolved = ctx.get(parts.slice(1));
}
return resolved !== undefined ? resolved : '';
});
};

let argsStr = resolveVars(this.args.trim());

// Parse comma-separated arguments: URL, startMarker, endMarker
const parts = [];
let current = '';
let inQuotes = false;
let quoteChar = '';
for (let i = 0; i < argsStr.length; i++) {
const char = argsStr[i];
if ((char === '"' || char === "'") && !inQuotes) {
inQuotes = true;
quoteChar = char;
} else if (char === quoteChar && inQuotes) {
inQuotes = false;
quoteChar = '';
} else if (char === ',' && !inQuotes) {
parts.push(current.trim().replace(/^["']|["']$/g, ''));
current = '';
} else {
current += char;
}
}
if (current.trim()) {
parts.push(current.trim().replace(/^["']|["']$/g, ''));
}

const resolvedUrl = parts[0] || '';
const startMarker = parts[1] || null;
const endMarker = parts[2] || null;

if (resolvedUrl.includes('{{')) {
emitter.write(`<!-- remote_include: unresolved vars -->`);
return;
}

let content = await EleventyFetch(resolvedUrl, {
duration: "1d", type: "text",
fetchOptions: { headers: { "User-Agent": "CockroachDB-Docs-Builder/1.0" } }
});
if (Buffer.isBuffer(content)) content = content.toString('utf8');

// Extract content between markers if specified
if (startMarker && endMarker) {
const startIdx = content.indexOf(startMarker);
const endIdx = content.indexOf(endMarker, startIdx + startMarker.length);
if (startIdx !== -1 && endIdx !== -1) {
content = content.substring(startIdx + startMarker.length, endIdx).trim();
}
} else if (startMarker) {
const startIdx = content.indexOf(startMarker);
if (startIdx !== -1) {
const lineEnd = content.indexOf('\n', startIdx + startMarker.length);
content = content.substring(startIdx + startMarker.length, lineEnd !== -1 ? lineEnd : undefined).trim();
}
}

emitter.write(content);
} catch (error) {
console.error(`remote_include (module) error: ${error.message}`);
emitter.write(`<!-- Remote include error: ${error.message} -->`);
}
}
});

module.exports = function(eleventyConfig) {
// ---------------------------------------------------------------------------
// Liquid Engine Configuration - Use custom Liquid instance with raw tags
Expand Down Expand Up @@ -253,27 +366,81 @@ module.exports = function(eleventyConfig) {
resolved = ctx.get(parts.slice(1));
}

// Jekyll compatibility: page.release_info.* -> release_info.*
// In Jekyll, release_info was at page.release_info, but in Eleventy it's at release_info directly
if (resolved === undefined && parts[0] === 'page' && parts[1] === 'release_info') {
// Try without the 'page' prefix
resolved = ctx.get(parts.slice(1));
}

return resolved !== undefined ? resolved : '';
});
}

// remote_include tag
// Supports: {% remote_include "URL" %} or {% remote_include "URL", "START MARKER", "END MARKER" %}
customLiquid.registerTag('remote_include', {
parse: function(tagToken) { this.args = tagToken.args; },
render: async function(ctx, emitter) {
try {
let resolvedUrl = resolveVarsInCtx(this.args.trim(), ctx);
// Strip surrounding quotes from the URL
resolvedUrl = resolvedUrl.replace(/^["']|["']$/g, '');
let argsStr = resolveVarsInCtx(this.args.trim(), ctx);

// Parse comma-separated arguments: URL, startMarker, endMarker
// Need to handle quoted strings with commas inside them
const parts = [];
let current = '';
let inQuotes = false;
let quoteChar = '';
for (let i = 0; i < argsStr.length; i++) {
const char = argsStr[i];
if ((char === '"' || char === "'") && !inQuotes) {
inQuotes = true;
quoteChar = char;
} else if (char === quoteChar && inQuotes) {
inQuotes = false;
quoteChar = '';
} else if (char === ',' && !inQuotes) {
parts.push(current.trim().replace(/^["']|["']$/g, ''));
current = '';
} else {
current += char;
}
}
if (current.trim()) {
parts.push(current.trim().replace(/^["']|["']$/g, ''));
}

const resolvedUrl = parts[0] || '';
const startMarker = parts[1] || null;
const endMarker = parts[2] || null;

if (resolvedUrl.includes('{{')) {
emitter.write(`<!-- remote_include: unresolved vars -->`);
return;
}

let content = await EleventyFetch(resolvedUrl, {
duration: "1d", type: "text",
fetchOptions: { headers: { "User-Agent": "CockroachDB-Docs-Builder/1.0" } }
});
if (Buffer.isBuffer(content)) content = content.toString('utf8');

// Extract content between markers if specified
if (startMarker && endMarker) {
const startIdx = content.indexOf(startMarker);
const endIdx = content.indexOf(endMarker, startIdx + startMarker.length);
if (startIdx !== -1 && endIdx !== -1) {
content = content.substring(startIdx + startMarker.length, endIdx).trim();
}
} else if (startMarker) {
// Single marker: extract from marker to end of line
const startIdx = content.indexOf(startMarker);
if (startIdx !== -1) {
const lineEnd = content.indexOf('\n', startIdx + startMarker.length);
content = content.substring(startIdx + startMarker.length, lineEnd !== -1 ? lineEnd : undefined).trim();
}
}

emitter.write(content);
} catch (error) {
console.error(`remote_include error: ${error.message}`);
Expand All @@ -299,14 +466,36 @@ module.exports = function(eleventyConfig) {
version = version.replace(/^["']|["']$/g, '');
pathSuffix = pathSuffix.replace(/^["']|["']$/g, '');

// If version is empty or looks like a variable, try to get from context
// If version is a bare variable name (not starting with 'v' followed by a digit),
// try to resolve it from context
if (version && !version.match(/^v\d/)) {
// Try to resolve as a variable name (handles things like version_prefix)
const varValue = ctx.get([version]);
if (varValue !== undefined && varValue !== null) {
version = String(varValue).trim();
} else {
// Also try dotted paths like page.version.version
const versionParts = version.split('.');
const dotValue = ctx.get(versionParts);
if (dotValue !== undefined && dotValue !== null) {
version = String(dotValue).trim();
}
}
}

// If version is still empty or looks like a variable, try to get from context
if (!version || version.includes('{{') || version === 'page.version.version') {
// Try to get version from context
let v = ctx.get(['version', 'version']);
if (!v) v = ctx.get(['page', 'version', 'version']);
version = v || 'v26.1'; // Default to stable
}

// If version has a trailing slash, remove it (the path joining will add separator)
if (version.endsWith('/')) {
version = version.slice(0, -1);
}

// Ensure pathSuffix starts with / for proper path joining
if (pathSuffix && !pathSuffix.startsWith('/')) {
pathSuffix = '/' + pathSuffix;
Expand Down Expand Up @@ -843,15 +1032,32 @@ module.exports = function(eleventyConfig) {

eleventyConfig.addAsyncShortcode("dynamic_include", async function(versionVar, pathSuffix) {
try {
// Get the version from the Eleventy context
// In our setup, 'version' is set by the directory data file (v26.1.11tydata.js)
let version = this.ctx?.version?.version || this.version?.version;

// Fallback: try to extract version from the current page path
if (!version) {
const inputPath = this.page?.inputPath || '';
const versionMatch = inputPath.match(/[/\\](v\d+\.\d+)[/\\]/);
version = versionMatch ? versionMatch[1] : 'v26.1'; // Default to stable
let version;

// Check if versionVar is already a version string (starts with v and digit)
// This handles cases like version_prefix which gets resolved to "v26.1/"
if (versionVar && typeof versionVar === 'string' && versionVar.match(/^v\d/)) {
version = versionVar.trim();
// Remove trailing slash if present - we'll add separator later
if (version.endsWith('/')) {
version = version.slice(0, -1);
}
} else {
// Get the version from the Eleventy context
// In our setup, 'version' is set by the directory data file (v26.1.11tydata.js)
version = this.ctx?.version?.version || this.version?.version;

// Fallback: try to extract version from the current page path
if (!version) {
const inputPath = this.page?.inputPath || '';
const versionMatch = inputPath.match(/[/\\](v\d+\.\d+)[/\\]/);
version = versionMatch ? versionMatch[1] : 'v26.1'; // Default to stable
}
}

// Ensure pathSuffix starts with / for proper path joining
if (pathSuffix && !pathSuffix.startsWith('/')) {
pathSuffix = '/' + pathSuffix;
}

// Build the full include path
Expand Down
10 changes: 5 additions & 5 deletions src/eleventy/content/_includes/releases/whats-new-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

{% for file in site.pages %}
{% unless branched == true %}
{% capture fpath %}{{ file.dir | remove:'/' }}{% endcapture %}
{% capture fpath %}{{ file.dir | remove: '/' }}{% endcapture %}
{% if fpath == page.major_version %}
{% assign branched = true %}
{% endif %}
Expand Down Expand Up @@ -54,11 +54,11 @@
{% capture install_sentence %}After downloading a supported CockroachDB binary, learn how to {{ install_link }} or {{ upgrade_link }}.{% endcapture %}
{% else %}
{% if branched %}
{% capture install_link %}[install CockroachDB](/docs/{{ page.major_version }}/install-cockroachdb.html){% endcapture %}
{% capture upgrade_link %}[upgrade your cluster](/docs/{{ page.major_version }}/upgrade-cockroach-version.html){% endcapture %}
{% capture install_link %}[install CockroachDB]({{ site.baseurl }}/{{ page.major_version }}/install-cockroachdb.html){% endcapture %}
{% capture upgrade_link %}[upgrade your cluster]({{ site.baseurl }}/{{ page.major_version }}/upgrade-cockroach-version.html){% endcapture %}
{% else %}
{% capture install_link %}[install CockroachDB](/docs/dev/install-cockroachdb.html){% endcapture %}
{% capture upgrade_link %}[upgrade your cluster](/docs/dev/upgrade-cockroach-version.html){% endcapture %}
{% capture install_link %}[install CockroachDB]({{ site.baseurl }}/dev/install-cockroachdb.html){% endcapture %}
{% capture upgrade_link %}[upgrade your cluster]({{ site.baseurl }}/dev/upgrade-cockroach-version.html){% endcapture %}
{% endif %}
{% capture install_sentence %}After downloading a supported CockroachDB binary, learn how to {{ install_link }} or {{ upgrade_link }}.{% endcapture %}
{% endif %}
Expand Down
2 changes: 1 addition & 1 deletion src/eleventy/content/_includes/v2.0/misc/basic-terms.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ Term | Definition
**Range** | CockroachDB stores all user data (tables, indexes, etc.) and almost all system data in a giant sorted map of key-value pairs. This keyspace is divided into "ranges", contiguous chunks of the keyspace, so that every key can always be found in a single range.<br><br>From a SQL perspective, a table and its secondary indexes initially map to a single range, where each key-value pair in the range represents a single row in the table (also called the primary index because the table is sorted by the primary key) or a single row in a secondary index. As soon as a range reaches 64 MiB in size, it splits into two ranges. This process continues as the table and its indexes continue growing.
**Replica** | CockroachDB replicates each range (3 times by default) and stores each replica on a different node.
**Leaseholder** | For each range, one of the replicas holds the "range lease". This replica, referred to as the "leaseholder", is the one that receives and coordinates all read and write requests for the range.<br><br>Unlike writes, read requests access the leaseholder and send the results to the client without needing to coordinate with any of the other range replicas. This reduces the network round trips involved and is possible because the leaseholder is guaranteed to be up-to-date due to the fact that all write requests also go to the leaseholder.
**Raft Leader** | For each range, one of the replicas is the "leader" for write requests. Via the [Raft consensus protocol](/docs/v2.0/architecture/replication-layer.html#raft), this replica ensures that a majority of replicas (the leader and enough followers) agree, based on their Raft logs, before committing the write. The Raft leader is almost always the same replica as the leaseholder.
**Raft Leader** | For each range, one of the replicas is the "leader" for write requests. Via the [Raft consensus protocol]({{ site.baseurl }}/v2.0/architecture/replication-layer.html#raft), this replica ensures that a majority of replicas (the leader and enough followers) agree, based on their Raft logs, before committing the write. The Raft leader is almost always the same replica as the leaseholder.
**Raft Log** | For each range, a time-ordered log of writes to the range that its replicas have agreed on. This log exists on-disk with each replica and is the range's source of truth for consistent replication.
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The CockroachDB Helm chart is undergoing maintenance for compatibility with Kube
1. You may want to modify `storage.persistentVolume.size` and `storage.persistentVolume.storageClass` for your use case. This chart defaults to 100Gi of disk space per pod. For more details on customizing disks for performance, see [these instructions](kubernetes-performance.html#disk-type).

{{site.data.alerts.callout_info}}
If necessary, you can [expand disk size](/docs/{{site.versions["stable"]}}/configure-cockroachdb-kubernetes.html?filters=helm#expand-disk-size) after the cluster is live.
If necessary, you can [expand disk size]({{ site.baseurl }}/{{site.versions["stable"]}}/configure-cockroachdb-kubernetes.html?filters=helm#expand-disk-size) after the cluster is live.
{{site.data.alerts.end}}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Secure CockroachDB deployments on Amazon EKS via Helm are [not yet supported](ht
The Helm chart defaults to a secure deployment by automatically setting `tls.enabled` to `true`.

{{site.data.alerts.callout_info}}
By default, the Helm chart will generate and sign 1 client and 1 node certificate to secure the cluster. To authenticate using your own CA, see [Certificate management](/docs/{{site.versions["stable"]}}/secure-cockroachdb-kubernetes.html?filters=helm#use-a-custom-ca).
By default, the Helm chart will generate and sign 1 client and 1 node certificate to secure the cluster. To authenticate using your own CA, see [Certificate management]({{ site.baseurl }}/{{site.versions["stable"]}}/secure-cockroachdb-kubernetes.html?filters=helm#use-a-custom-ca).
{{site.data.alerts.end}}

1. Install the CockroachDB Helm chart, specifying your custom values file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The CockroachDB Helm chart is undergoing maintenance for compatibility with Kube
1. You may want to modify `storage.persistentVolume.size` and `storage.persistentVolume.storageClass` for your use case. This chart defaults to 100Gi of disk space per pod. For more details on customizing disks for performance, see [these instructions](kubernetes-performance.html#disk-type).

{{site.data.alerts.callout_info}}
If necessary, you can [expand disk size](/docs/{{ page.version.version }}/configure-cockroachdb-kubernetes.html?filters=helm#expand-disk-size) after the cluster is live.
If necessary, you can [expand disk size]({{ site.baseurl }}/{{ page.version.version }}/configure-cockroachdb-kubernetes.html?filters=helm#expand-disk-size) after the cluster is live.
{{site.data.alerts.end}}

1. Install the CockroachDB Helm chart.
Expand Down
Loading
Loading