mirror of
https://github.com/goauthentik/authentik
synced 2026-04-25 17:15:26 +02:00
website: Unlisted & Draft Release Notes (#18210)
* website: Unlisted Release Notes * Swizzle unlisted component. Revise copy for pre-release.
This commit is contained in:
@@ -2,9 +2,13 @@ import "./styles.css";
|
||||
|
||||
import { createVersionURL, parseBranchSemVer } from "#components/VersionPicker/utils.ts";
|
||||
|
||||
import type {
|
||||
AKReleaseFrontMatter,
|
||||
AKReleasesPluginEnvironment,
|
||||
} from "@goauthentik/docusaurus-theme/releases/common";
|
||||
|
||||
import clsx from "clsx";
|
||||
import React, { memo } from "react";
|
||||
import { AKReleasesPluginEnvironment } from "releases/node.mjs";
|
||||
|
||||
export interface VersionDropdownProps {
|
||||
/**
|
||||
@@ -20,12 +24,19 @@ export interface VersionDropdownProps {
|
||||
* @format semver
|
||||
*/
|
||||
releases: string[];
|
||||
|
||||
/**
|
||||
* A possible record of parsed front-matter for each release.
|
||||
*/
|
||||
frontMatterRecord: Record<string, AKReleaseFrontMatter>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A dropdown that shows the available versions of the documentation.
|
||||
*/
|
||||
export const VersionDropdown = memo<VersionDropdownProps>(({ environment, releases }) => {
|
||||
export const VersionDropdown = memo<VersionDropdownProps>((props) => {
|
||||
const { environment, releases, frontMatterRecord } = props;
|
||||
|
||||
const { branch, preReleaseOrigin } = environment;
|
||||
const parsedSemVer = parseBranchSemVer(branch);
|
||||
|
||||
@@ -65,6 +76,11 @@ export const VersionDropdown = memo<VersionDropdownProps>(({ environment, releas
|
||||
|
||||
{visibleReleases.map((semVer, idx) => {
|
||||
let label = semVer;
|
||||
const frontmatter = frontMatterRecord[semVer];
|
||||
|
||||
if (frontmatter?.unlisted || frontmatter?.draft) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (idx === 0) {
|
||||
label += " (Current Release)";
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { useHostname } from "#components/VersionPicker/utils.ts";
|
||||
import { VersionDropdown } from "#components/VersionPicker/VersionDropdown.tsx";
|
||||
|
||||
import { AKReleasesPluginData } from "@goauthentik/docusaurus-theme/releases/plugin";
|
||||
import type {
|
||||
AKReleaseFrontMatter,
|
||||
AKReleasesPluginData,
|
||||
} from "@goauthentik/docusaurus-theme/releases/common";
|
||||
|
||||
import useIsBrowser from "@docusaurus/useIsBrowser";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
|
||||
export interface VersionPickerLoaderProps {
|
||||
pluginData: AKReleasesPluginData;
|
||||
@@ -20,7 +23,23 @@ export interface VersionPickerLoaderProps {
|
||||
export const VersionPickerLoader: React.FC<VersionPickerLoaderProps> = ({ pluginData }) => {
|
||||
const { preReleaseOrigin } = pluginData.env;
|
||||
|
||||
const [releases, setReleases] = useState(pluginData.releases);
|
||||
const [releases, setReleases] = useState(() =>
|
||||
pluginData.releases.map((release) => release.name),
|
||||
);
|
||||
|
||||
const frontMatterRecord = useMemo(() => {
|
||||
const record: Record<string, AKReleaseFrontMatter> = {};
|
||||
|
||||
for (const release of pluginData.releases) {
|
||||
if (!release.frontMatter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
record[release.name] = release.frontMatter;
|
||||
}
|
||||
|
||||
return record;
|
||||
}, [pluginData.releases]);
|
||||
|
||||
const browser = useIsBrowser();
|
||||
const hostname = useHostname();
|
||||
@@ -60,5 +79,12 @@ export const VersionPickerLoader: React.FC<VersionPickerLoaderProps> = ({ plugin
|
||||
return () => controller.abort("unmount");
|
||||
}, [browser, pluginData.publicPath, preReleaseOrigin]);
|
||||
|
||||
return <VersionDropdown hostname={hostname} releases={releases} environment={pluginData.env} />;
|
||||
return (
|
||||
<VersionDropdown
|
||||
hostname={hostname}
|
||||
releases={releases}
|
||||
frontMatterRecord={frontMatterRecord}
|
||||
environment={pluginData.env}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"./redirects/plugin": "./redirects/plugin.mjs",
|
||||
"./redirects/node": "./redirects/node.mjs",
|
||||
"./redirects": "./redirects/index.mjs",
|
||||
"./releases/common": "./releases/common.mjs",
|
||||
"./releases/plugin": "./releases/plugin.mjs",
|
||||
"./releases/node": "./releases/node.mjs"
|
||||
},
|
||||
|
||||
41
website/docusaurus-theme/releases/common.mjs
Normal file
41
website/docusaurus-theme/releases/common.mjs
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @typedef {object} AKReleasesPluginEnvironment
|
||||
* @property {string} [branch] The current branch name, if available.
|
||||
* e.g. "main" `version-${year}.${month}`, "feature-branch"
|
||||
* @property {string} currentReleaseOrigin The URL to the current release documentation.
|
||||
* @property {string} preReleaseOrigin The URL to the pre-release documentation.
|
||||
* @property {string} apiReferenceOrigin The URL to the API reference documentation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AKReleaseFrontMatter
|
||||
* @property {boolean} [draft] Whether the release is a draft.
|
||||
* @property {boolean} [unlisted] Whether the release is unlisted.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AKReleaseFileMetadata
|
||||
* @property {string} name The name of the release file.
|
||||
* @property {string} path The relative path to the release file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {AKReleaseFileMetadata & { frontMatter?: AKReleaseFrontMatter }} AKReleaseFile
|
||||
*
|
||||
* Represents a release file with additional frontmatter properties.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AKReleasesPluginOptions
|
||||
* @property {string} docsDirectory The path to the documentation directory.
|
||||
* @property {AKReleasesPluginEnvironment} [environment] Optional environment variables overrides.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AKReleasesPluginData
|
||||
* @property {string} publicPath URL to the plugin's public directory.
|
||||
* @property {AKReleaseFile[]} releases Available versions of the documentation.
|
||||
* @property {AKReleasesPluginEnvironment} env Environment variables
|
||||
*/
|
||||
|
||||
export {};
|
||||
@@ -2,19 +2,26 @@
|
||||
* @file Docusaurus release utils.
|
||||
*
|
||||
* @import { SidebarItemConfig } from "@docusaurus/plugin-content-docs/src/sidebars/types.js"
|
||||
* @import { AKReleaseFile, AKReleasesPluginEnvironment } from "./common.mjs"
|
||||
*/
|
||||
|
||||
import * as path from "node:path";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { extname, join } from "node:path";
|
||||
|
||||
import { parseFileContentFrontMatter } from "@docusaurus/utils/lib/markdownUtils.js";
|
||||
import FastGlob from "fast-glob";
|
||||
import { coerce } from "semver";
|
||||
|
||||
/**
|
||||
* Collect all Markdown files from the releases directory.
|
||||
*
|
||||
* @param {string} releasesParentDirectory
|
||||
* @returns {FastGlob.Entry[]}
|
||||
* @returns {AKReleaseFile[]}
|
||||
*/
|
||||
export function collectReleaseFiles(releasesParentDirectory) {
|
||||
/**
|
||||
* @type {Array<FastGlob.Entry & AKReleaseFile>}
|
||||
*/
|
||||
const releaseFiles = FastGlob.sync("releases/**/v*.{md,mdx}", {
|
||||
cwd: releasesParentDirectory,
|
||||
onlyFiles: true,
|
||||
@@ -38,6 +45,21 @@ export function collectReleaseFiles(releasesParentDirectory) {
|
||||
return b.name.localeCompare(a.name);
|
||||
});
|
||||
|
||||
const [latestRelease] = releaseFiles;
|
||||
|
||||
if (latestRelease) {
|
||||
const extension = extname(latestRelease.dirent.name);
|
||||
|
||||
const fileContent = readFileSync(
|
||||
join(releasesParentDirectory, `${latestRelease.path}${extension}`),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const { frontMatter } = parseFileContentFrontMatter(fileContent);
|
||||
|
||||
latestRelease.frontMatter = frontMatter;
|
||||
}
|
||||
|
||||
return releaseFiles;
|
||||
}
|
||||
|
||||
@@ -45,15 +67,13 @@ export const SUPPORTED_RELEASE_COUNT = 3;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {FastGlob.Entry[]} releaseFiles
|
||||
* @param {AKReleaseFile[]} releaseFiles
|
||||
*/
|
||||
export function createReleaseSidebarEntries(releaseFiles) {
|
||||
/**
|
||||
* @type {SidebarItemConfig[]}
|
||||
*/
|
||||
let sidebarEntries = releaseFiles.map((fileEntry) => {
|
||||
return path.join(fileEntry.path);
|
||||
});
|
||||
let sidebarEntries = releaseFiles.map((fileEntry) => fileEntry.path);
|
||||
|
||||
if (releaseFiles.length > SUPPORTED_RELEASE_COUNT) {
|
||||
// Then we add the rest of the releases as a category.
|
||||
@@ -70,15 +90,6 @@ export function createReleaseSidebarEntries(releaseFiles) {
|
||||
return sidebarEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} AKReleasesPluginEnvironment
|
||||
* @property {string} [branch] The current branch name, if available.
|
||||
* e.g. "main" `version-${year}.${month}`, "feature-branch"
|
||||
* @property {string} currentReleaseOrigin The URL to the current release documentation.
|
||||
* @property {string} preReleaseOrigin The URL to the pre-release documentation.
|
||||
* @property {string} apiReferenceOrigin The URL to the API reference documentation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prepare the environment variables for the releases plugin.
|
||||
*
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @file Docusaurus releases plugin.
|
||||
*
|
||||
* @import { LoadContext, Plugin } from "@docusaurus/types"
|
||||
* @import { AKReleasesPluginEnvironment } from "./node.mjs"
|
||||
* @import { AKReleasesPluginOptions, AKReleasesPluginData } from "./common.mjs"
|
||||
*/
|
||||
|
||||
import * as fs from "node:fs/promises";
|
||||
@@ -14,19 +14,6 @@ import { collectReleaseFiles, prepareReleaseEnvironment } from "./node.mjs";
|
||||
const PLUGIN_NAME = "ak-releases-plugin";
|
||||
const RELEASES_FILENAME = "releases.gen.json";
|
||||
|
||||
/**
|
||||
* @typedef {object} AKReleasesPluginOptions
|
||||
* @property {string} docsDirectory The path to the documentation directory.
|
||||
* @property {AKReleasesPluginEnvironment} [environment] Optional environment variables overrides.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AKReleasesPluginData
|
||||
* @property {string} publicPath URL to the plugin's public directory.
|
||||
* @property {string[]} releases Available versions of the documentation.
|
||||
* @property {AKReleasesPluginEnvironment} env Environment variables
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {LoadContext} loadContext
|
||||
* @param {AKReleasesPluginOptions} options
|
||||
@@ -44,14 +31,13 @@ async function akReleasesPlugin(loadContext, options) {
|
||||
...options.environment,
|
||||
};
|
||||
|
||||
const releases = collectReleaseFiles(options.docsDirectory).map(
|
||||
(release) => release.name,
|
||||
);
|
||||
const releases = collectReleaseFiles(options.docsDirectory);
|
||||
const releaseNames = releases.map((release) => release.name);
|
||||
|
||||
const outputPath = path.join(loadContext.siteDir, "static", RELEASES_FILENAME);
|
||||
|
||||
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
||||
await fs.writeFile(outputPath, JSON.stringify(releases, null, 2), "utf-8");
|
||||
await fs.writeFile(outputPath, JSON.stringify(releaseNames, null, 2), "utf-8");
|
||||
console.log(`✅ ${RELEASES_FILENAME} generated`);
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { useDoc } from "@docusaurus/plugin-content-docs/client";
|
||||
import {
|
||||
ThemeClassNames,
|
||||
UnlistedBannerMessage,
|
||||
UnlistedBannerTitle,
|
||||
UnlistedMetadata,
|
||||
} from "@docusaurus/theme-common";
|
||||
import Translate from "@docusaurus/Translate";
|
||||
import Admonition from "@theme/Admonition";
|
||||
import type { Props } from "@theme/ContentVisibility/Unlisted";
|
||||
import clsx from "clsx";
|
||||
import React, { type ReactNode } from "react";
|
||||
|
||||
function UnlistedBanner({ className }: Props) {
|
||||
const context = useDoc();
|
||||
|
||||
if (context.metadata.id?.startsWith("releases")) {
|
||||
return (
|
||||
<Admonition
|
||||
type="note"
|
||||
title={
|
||||
<Translate
|
||||
id="theme.contentVisibility.unlistedBanner.preRelease.title"
|
||||
description="The unlisted content banner title"
|
||||
>
|
||||
Pre-Release Documentation
|
||||
</Translate>
|
||||
}
|
||||
className={clsx(className, ThemeClassNames.common.unlistedBanner)}
|
||||
>
|
||||
<Translate
|
||||
id="theme.contentVisibility.unlistedBanner.preRelease.message"
|
||||
description="The unlisted content banner message"
|
||||
>
|
||||
This documentation is for an upcoming version of authentik. It may be incomplete
|
||||
or subject to changes before the final release.
|
||||
</Translate>
|
||||
</Admonition>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Admonition
|
||||
type="caution"
|
||||
title={<UnlistedBannerTitle />}
|
||||
className={clsx(className, ThemeClassNames.common.unlistedBanner)}
|
||||
>
|
||||
<UnlistedBannerMessage />
|
||||
</Admonition>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Unlisted(props: Props): ReactNode {
|
||||
return (
|
||||
<>
|
||||
<UnlistedMetadata />
|
||||
<UnlistedBanner {...props} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user