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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ You can configure some high-level settings for the documentation website in the
* Which repositories to fetch download information from.
* Information about LTS (long-term-support) versions.

`ltsVersions` lists all known LTS versions, including past and upcoming releases,
with a `releaseDate` and an inclusive `endDate` in `YYYY-MM-DD` format (UTC).
Upcoming releases can use `TBD` as the version and `YYYY-MM` as the release date.
`fetch-repo-docs` replaces `<!-- LTS_RELEASES_TABLE -->` in the release-cycle page
with a table generated from this config, writing the result under `generated/local-docs/`.
The generators treat a version as an active LTS only once a stable GitHub release exists and through its
end date. Active LTS versions are retained in the documentation even outside the
recent-version window, included in downloads, and labeled as LTS. Expired versions
can still appear under the usual recent/latest-version rules, without an LTS label.

## Automatic Deployment

This site is automatically deployed using [Netlify](https://www.netlify.com/).
Expand Down
11 changes: 9 additions & 2 deletions docs-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,16 @@ export default {
},
],

// Long-term support versions configuration.
// All long-term support versions and their inclusive end-of-support dates.
ltsVersions: {
prometheus: ["3.13"],
prometheus: [
{ version: "2.37", releaseDate: "2022-07-14", endDate: "2023-07-31" },
{ version: "2.45", releaseDate: "2023-06-23", endDate: "2024-07-31" },
{ version: "2.53", releaseDate: "2024-06-16", endDate: "2025-07-31" },
{ version: "3.5", releaseDate: "2025-07-14", endDate: "2026-07-31" },
{ version: "3.13", releaseDate: "2026-07-01", endDate: "2027-07-31" },
{ version: "TBD", releaseDate: "2027-06", endDate: "2028-07-31" },
],
},

// Repositories for the downloads page. The order in this file is the
Expand Down
9 changes: 1 addition & 8 deletions docs/introduction/release-cycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,7 @@ having a Prometheus server maintained by the community.

## List of LTS releases

| Release | Date | End of support | Status |
| ------------------- | -------------- | -------------- | ------------- |
| Prometheus 2.37 | 2022-07-14 | 2023-07-31 | End of life |
| Prometheus 2.45 | 2023-06-23 | 2024-07-31 | End of life |
| Prometheus 2.53 | 2024-06-16 | 2025-07-31 | End of life |
| Prometheus 3.5 | 2025-07-14 | 2026-07-31 | End of life |
| **Prometheus 3.13** | **2026-07-01** | **2027-07-31** | **Supported** |
| TBD | 2027-06 | 2028-07-31 | Upcoming |
<!-- LTS_RELEASES_TABLE -->

## Limitations of LTS support

Expand Down
32 changes: 21 additions & 11 deletions scripts/fetch-downloads-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ import { octokit } from "./githubClient";
import { GetResponseDataTypeFromEndpointMethod } from "@octokit/types";
import * as fs from "fs";
import * as path from "path";
import { valid } from "semver";
import docsConfig from "../docs-config";
import { Downloads, Release, Binary } from "@/downloads-metadata-types";
import {
DownloadJSON,
DownloadRelease,
DownloadFile,
} from "@/download-json-types";
import { compareFullVersion, filterUnique, majorMinor } from "./utils";
import {
compareFullVersion,
filterUnique,
getActiveLTSVersions,
majorMinor,
} from "./utils";

const OUTDIR = "./generated";

Expand Down Expand Up @@ -72,16 +78,24 @@ for (const repoName of docsConfig.downloads.repos) {
repo: repoName,
});

// Fetch releases information for the repo.
// Fetch all release pages so older supported LTS versions are included.
console.log(`Fetching releases info for ${repoName}`);
const releases = (
await octokit.rest.repos.listReleases({
await octokit.paginate(octokit.rest.repos.listReleases, {
owner: docsConfig.downloads.owner,
repo: repoName,
per_page: 100,
})
).data;
).filter((r) => !r.draft && valid(r.tag_name));

releases.sort((a, b) => compareFullVersion(a.tag_name, b.tag_name)).reverse();
const ltsVersions = getActiveLTSVersions(
docsConfig.ltsVersions,
repoName,
releases
.filter((r) => !r.prerelease && !r.tag_name.includes("-"))
.map((r) => majorMinor(r.tag_name))
);

// Select the relevant stable, pre-release, and LTS versions to show.
const preReleases: string[] = [];
Expand All @@ -103,7 +117,7 @@ for (const repoName of docsConfig.downloads.repos) {
preReleases.push(version);
}
} else if (
docsConfig.ltsVersions[repoName]?.includes(version) &&
ltsVersions.includes(version) &&
!stableReleases.includes(version)
) {
shownReleases.push(r);
Expand Down Expand Up @@ -153,9 +167,7 @@ for (const repoName of docsConfig.downloads.repos) {
name: r.name || "",
url: r.html_url,
prerelease: r.prerelease,
ltsRelease: docsConfig.ltsVersions[repoName]?.includes(
majorMinor(r.tag_name)
),
ltsRelease: !r.prerelease && ltsVersions.includes(majorMinor(r.tag_name)),
majorMinor: majorMinor(r.tag_name),
binaries: getBinaries(r),
})
Expand All @@ -172,9 +184,7 @@ for (const repoName of docsConfig.downloads.repos) {
version: r.tag_name,
stable: !r.prerelease,
latest: r.id === latestStableID,
lts:
docsConfig.ltsVersions[repoName]?.includes(majorMinor(r.tag_name)) ??
false,
lts: !r.prerelease && ltsVersions.includes(majorMinor(r.tag_name)),
files: getBinaries(r).map(
(b): DownloadFile => ({
url: b.url,
Expand Down
53 changes: 46 additions & 7 deletions scripts/fetch-repo-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ import {
} from "@/docs-collection-types";
import matter from "gray-matter";
import { octokit } from "./githubClient";
import { compareFullVersion, filterUnique, majorMinor } from "./utils";
import {
compareFullVersion,
compareMajorMinor,
filterUnique,
generateLTSTable,
getActiveLTSVersions,
majorMinor,
} from "./utils";

const OUTDIR = "./generated";

Expand Down Expand Up @@ -111,15 +118,21 @@ const fetchRepoDocs = async ({
});

const allReleaseTags: string[] = [];
const stableVersions: string[] = [];

for await (const { data: releases } of iterator) {
// Skip ancient releases. Some have non-semver compatible strings and the
// version comparison below breaks. Also note the lack of `v` in the tag.
const validReleases = (repo === "prometheus")
? releases.filter((r) => !r.tag_name.startsWith("0."))
: releases;
const validReleases = releases.filter(
(r) => !r.draft && !(repo === "prometheus" && r.tag_name.startsWith("0."))
);

allReleaseTags.push(...validReleases.map((r) => r.tag_name));
stableVersions.push(
...validReleases
.filter((r) => !r.prerelease && !r.tag_name.includes("-"))
.map((r) => majorMinor(r.tag_name))
);
// For the Prometheus repo for efficieny-reasons, stop once we have found at least
// one release starting with "v1.".
// TODO: Do we even still want to show the latest v1 release?
Expand Down Expand Up @@ -156,6 +169,18 @@ const fetchRepoDocs = async ({
}
}

// Keep supported LTS docs even when they fall outside the recent versions.
const ltsVersions =
owner === "prometheus"
? getActiveLTSVersions(docsConfig.ltsVersions, repo, stableVersions)
: [];
for (const version of ltsVersions) {
if (!recentVersions.includes(version)) {
recentVersions.push(version);
}
}
recentVersions.sort(compareMajorMinor).reverse();

const latestTag = allReleaseTags.find((tag) => !tag.includes("-"));
if (!latestTag) {
throw new Error(`No latest version found for ${owner}/${repo}.`);
Expand All @@ -169,7 +194,7 @@ const fetchRepoDocs = async ({
allRepoVersions[owner][repo] = {
versions: recentVersions,
latestVersion,
ltsVersions: (owner === "prometheus" && docsConfig.ltsVersions[repo]) || [],
ltsVersions,
};

console.log(
Expand Down Expand Up @@ -290,6 +315,20 @@ for (const sourceConfig of docsConfig.localMarkdownSources) {
}

const filePath = path.relative(docsDir, file);
let content = fs.readFileSync(file, "utf-8");
let outputFile = file;
if (content.includes("<!-- LTS_RELEASES_TABLE -->")) {
content = content.replace(
"<!-- LTS_RELEASES_TABLE -->",
generateLTSTable(
docsConfig.ltsVersions.prometheus,
allRepoVersions.prometheus.prometheus.ltsVersions
)
);
outputFile = path.join(OUTDIR, "local-docs", docsDir, filePath);
fs.mkdirSync(path.dirname(outputFile), { recursive: true });
fs.writeFileSync(outputFile, content);
}
const {
data: {
title,
Expand All @@ -298,7 +337,7 @@ for (const sourceConfig of docsConfig.localMarkdownSources) {
nav_icon: navIcon,
hide_in_nav: hideInNav,
},
} = matter(fs.readFileSync(file, "utf-8"));
} = matter(content);
if (!title) {
throw new Error(`Missing title in ${file}`);
}
Expand All @@ -319,7 +358,7 @@ for (const sourceConfig of docsConfig.localMarkdownSources) {
docsCollection[slug] = {
type: "local-doc",
slug,
filePath: file,
filePath: outputFile,
title,
navTitle,
sortRank: sortRank ?? 0,
Expand Down
50 changes: 50 additions & 0 deletions scripts/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { generateLTSTable, getActiveLTSVersions } from "./utils";

const config = {
prometheus: [
{ version: "2.53", releaseDate: "2024-06-16", endDate: "2025-07-31" },
{ version: "3.5", releaseDate: "2025-07-14", endDate: "2026-07-31" },
{ version: "3.13", releaseDate: "2026-07-01", endDate: "2027-07-31" },
],
};

test("LTS support includes the entire end date in UTC", () => {
const stableVersions = ["2.53", "3.5", "3.13"];
assert.deepEqual(
getActiveLTSVersions(config, "prometheus", stableVersions, new Date("2026-07-31T23:59:59.999Z")),
["3.5", "3.13"]
);
assert.deepEqual(
getActiveLTSVersions(config, "prometheus", stableVersions, new Date("2026-08-01T00:00:00Z")),
["3.13"]
);
});

test("upcoming LTS versions require a stable release", () => {
assert.deepEqual(
getActiveLTSVersions(config, "prometheus", ["2.53", "3.5"], new Date("2026-06-01T00:00:00Z")),
["3.5"]
);
assert.deepEqual(
getActiveLTSVersions(config, "prometheus", [], new Date("2026-06-01T00:00:00Z")),
[]
);
});

test("repositories without LTS configuration have no active LTS versions", () => {
assert.deepEqual(getActiveLTSVersions(config, "alertmanager", ["0.31"]), []);
});

test("the LTS table includes expired, supported, and upcoming releases", () => {
const table = generateLTSTable(
[...config.prometheus, { version: "TBD", releaseDate: "2027-06", endDate: "2028-07-31" }],
["3.5"],
new Date("2026-06-01T00:00:00Z")
);
assert.match(table, /\| Prometheus 2\.53 \| 2024-06-16 \| 2025-07-31 \| End of life \|/);
assert.match(table, /\| \*\*Prometheus 3\.5\*\* \| \*\*2025-07-14\*\* \| \*\*2026-07-31\*\* \| \*\*Supported\*\* \|/);
assert.match(table, /\| Prometheus 3\.13 \| 2026-07-01 \| 2027-07-31 \| Upcoming \|/);
assert.match(table, /\| TBD \| 2027-06 \| 2028-07-31 \| Upcoming \|/);
});
42 changes: 42 additions & 0 deletions scripts/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,46 @@
import { compare } from "semver";
import { LTSConfig } from "../src/docs-config-types";

// Only stable, published versions still within their support period are active LTS.
export const getActiveLTSVersions = (
config: LTSConfig,
repo: string,
stableVersions: string[],
now = new Date()
): string[] => {
const today = now.toISOString().slice(0, 10);
return (config[repo] ?? [])
.filter(
({ version, endDate }) => stableVersions.includes(version) && today <= endDate
)
.map(({ version }) => version);
};

export const generateLTSTable = (
versions: LTSConfig[string],
activeVersions: string[],
now = new Date()
): string => {
const today = now.toISOString().slice(0, 10);
const rows = versions.map(({ version, releaseDate, endDate }) => {
const supported = today <= endDate && activeVersions.includes(version);
const status = today > endDate
? "End of life"
: supported ? "Supported" : "Upcoming";
const cells = [
version === "TBD" ? "TBD" : `Prometheus ${version}`,
releaseDate,
endDate,
status,
];
return `| ${cells.map((cell) => supported ? `**${cell}**` : cell).join(" | ")} |`;
});
return [
"| Release | Date | End of support | Status |",
"| --- | --- | --- | --- |",
...rows,
].join("\n");
};

// Takes a full Prometheus tag / version string and returns the major and minor version.
// "v3.4.0-rc.0" -> "3.4"
Expand Down
6 changes: 4 additions & 2 deletions src/app/sitemap.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from "fs";
import path from "path";
import { MetadataRoute } from "next";
import { docsCollection } from "@/docs-collection";
import { allRepoVersions, docsCollection } from "@/docs-collection";
import { getAllPostFileNames, postFileNameToPath } from "@/blog-helpers";
import docsConfig from "../../docs-config";

Expand Down Expand Up @@ -44,7 +44,9 @@ export default function sitemap(): MetadataRoute.Sitemap {
if (doc.version === doc.latestVersion) {
return true;
}
return docsConfig.ltsVersions[doc.repo]?.includes(doc.version) ?? false;
return (
allRepoVersions[doc.owner]?.[doc.repo]?.ltsVersions.includes(doc.version) ?? false
);
})
.map((doc) => ({ url: `${base}/docs/${doc.slug}/` }));

Expand Down
9 changes: 8 additions & 1 deletion src/docs-config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ export type GithubSinglePageSource = {
};

export type LTSConfig = {
[repo: string]: string[];
[repo: string]: {
// Major.minor version, or TBD for an unassigned upcoming release.
version: string;
// Release date (YYYY-MM-DD), or planned month (YYYY-MM) for upcoming releases.
releaseDate: string;
// Inclusive end-of-support date in YYYY-MM-DD format (UTC).
endDate: string;
}[];
};

export type DownloadConfig = {
Expand Down
Loading