wip: improve update-specs script

This commit is contained in:
2026-01-11 09:07:34 +00:00
parent 1eab53b7ba
commit 164729b57e
27 changed files with 998 additions and 857 deletions

View File

@@ -38,7 +38,7 @@ bun run format
bun run format:check bun run format:check
# Update specs from upstream (fetches from github.com/jimeh/common-flow) # Update specs from upstream (fetches from github.com/jimeh/common-flow)
bun run update bun run update-specs
``` ```
The site is built to `docs/` for GitHub Pages hosting. The site is built to `docs/` for GitHub Pages hosting.

View File

@@ -16,6 +16,7 @@
"rehype-stringify": "^10.0.1", "rehype-stringify": "^10.0.1",
"remark-parse": "^11.0.0", "remark-parse": "^11.0.0",
"remark-rehype": "^11.1.2", "remark-rehype": "^11.1.2",
"semver": "^7.7.3",
"unified": "^11.0.5", "unified": "^11.0.5",
}, },
"devDependencies": { "devDependencies": {
@@ -23,6 +24,8 @@
"@eslint/js": "^9.39.2", "@eslint/js": "^9.39.2",
"@tailwindcss/typography": "^0.5.19", "@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"@types/bun": "^1.3.5",
"@types/semver": "^7.7.1",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-plugin-astro": "^1.5.0", "eslint-plugin-astro": "^1.5.0",
"prettier": "^3.7.4", "prettier": "^3.7.4",
@@ -348,6 +351,8 @@
"@trysound/sax": ["@trysound/sax@0.2.0", "", {}, "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="], "@trysound/sax": ["@trysound/sax@0.2.0", "", {}, "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="],
"@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
@@ -366,6 +371,8 @@
"@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="], "@types/sax": ["@types/sax@1.2.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A=="],
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
@@ -458,6 +465,8 @@
"buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="], "buffer-crc32": ["buffer-crc32@0.2.13", "", {}, "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ=="],
"bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], "camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="],

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
} }
})(); })();
</script><link rel="stylesheet" href="/_astro/index.DF_wxGza.css"></head> <body class="min-h-screen"> <div class="flex flex-col items-center justify-center min-h-screen p-8"> <div class="text-center"> <h1 class="text-[8rem] sm:text-[12rem] font-display font-bold leading-none </script><link rel="stylesheet" href="/_astro/index.Ds9Y8AeZ.css"></head> <body class="min-h-screen"> <div class="flex flex-col items-center justify-center min-h-screen p-8"> <div class="text-center"> <h1 class="text-[8rem] sm:text-[12rem] font-display font-bold leading-none
text-gray-300 dark:text-neutral-700"> text-gray-300 dark:text-neutral-700">
404 404
</h1> <p class="text-xl mb-2 text-gray-600 dark:text-neutral-400"> </h1> <p class="text-xl mb-2 text-gray-600 dark:text-neutral-400">

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
} }
})(); })();
</script><link rel="stylesheet" href="/_astro/index.DF_wxGza.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent </script><link rel="stylesheet" href="/_astro/index.Ds9Y8AeZ.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent
translate-y-[-100%] transition-transform duration-300 translate-y-[-100%] transition-transform duration-300
backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline
text-gray-950 dark:text-neutral-50 text-gray-950 dark:text-neutral-50

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
} }
})(); })();
</script><link rel="stylesheet" href="/_astro/index.DF_wxGza.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent </script><link rel="stylesheet" href="/_astro/index.Ds9Y8AeZ.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent
translate-y-[-100%] transition-transform duration-300 translate-y-[-100%] transition-transform duration-300
backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline
text-gray-950 dark:text-neutral-50 text-gray-950 dark:text-neutral-50

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
} }
})(); })();
</script><link rel="stylesheet" href="/_astro/index.DF_wxGza.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent </script><link rel="stylesheet" href="/_astro/index.Ds9Y8AeZ.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent
translate-y-[-100%] transition-transform duration-300 translate-y-[-100%] transition-transform duration-300
backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline
text-gray-950 dark:text-neutral-50 text-gray-950 dark:text-neutral-50

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
} }
})(); })();
</script><link rel="stylesheet" href="/_astro/index.DF_wxGza.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent </script><link rel="stylesheet" href="/_astro/index.Ds9Y8AeZ.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent
translate-y-[-100%] transition-transform duration-300 translate-y-[-100%] transition-transform duration-300
backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline
text-gray-950 dark:text-neutral-50 text-gray-950 dark:text-neutral-50

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
} }
})(); })();
</script><link rel="stylesheet" href="/_astro/index.DF_wxGza.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent </script><link rel="stylesheet" href="/_astro/index.Ds9Y8AeZ.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent
translate-y-[-100%] transition-transform duration-300 translate-y-[-100%] transition-transform duration-300
backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline
text-gray-950 dark:text-neutral-50 text-gray-950 dark:text-neutral-50

View File

@@ -8,7 +8,7 @@
document.documentElement.classList.add("dark"); document.documentElement.classList.add("dark");
} }
})(); })();
</script><link rel="stylesheet" href="/_astro/index.DF_wxGza.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent </script><link rel="stylesheet" href="/_astro/index.Ds9Y8AeZ.css"></head> <body class="min-h-screen"> <header id="site-header" class="fixed top-0 inset-x-0 z-50 border-b border-transparent
translate-y-[-100%] transition-transform duration-300 translate-y-[-100%] transition-transform duration-300
backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline backdrop-blur-xl bg-gray-50/85 dark:bg-neutral-950/85"> <div class="max-w-6xl mx-auto px-4 sm:px-6 h-16 flex items-center justify-between"> <!-- Logo / Title + Version --> <div class="flex items-center gap-3"> <a href="#hero" class="flex items-center gap-3 no-underline
text-gray-950 dark:text-neutral-50 text-gray-950 dark:text-neutral-50

View File

@@ -10,7 +10,7 @@
"lint": "eslint .", "lint": "eslint .",
"format": "prettier --write .", "format": "prettier --write .",
"format:check": "prettier --check .", "format:check": "prettier --check .",
"update": "bun scripts/update-specs.ts", "update-specs": "bun scripts/update-specs.ts",
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
@@ -25,6 +25,7 @@
"rehype-stringify": "^10.0.1", "rehype-stringify": "^10.0.1",
"remark-parse": "^11.0.0", "remark-parse": "^11.0.0",
"remark-rehype": "^11.1.2", "remark-rehype": "^11.1.2",
"semver": "^7.7.3",
"unified": "^11.0.5" "unified": "^11.0.5"
}, },
"devDependencies": { "devDependencies": {
@@ -32,6 +33,8 @@
"@eslint/js": "^9.39.2", "@eslint/js": "^9.39.2",
"@tailwindcss/typography": "^0.5.19", "@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.1.18", "@tailwindcss/vite": "^4.1.18",
"@types/bun": "^1.3.5",
"@types/semver": "^7.7.1",
"eslint": "^9.39.2", "eslint": "^9.39.2",
"eslint-plugin-astro": "^1.5.0", "eslint-plugin-astro": "^1.5.0",
"prettier": "^3.7.4", "prettier": "^3.7.4",

View File

@@ -1,39 +1,85 @@
/** /**
* Fetches spec documents and diagrams from the common-flow GitHub repo * Fetches spec documents and diagrams from the common-flow GitHub repo
* and writes them to the appropriate locations for Astro to consume. * and writes them to the appropriate locations for Astro to consume.
*
* Versions are discovered from git tags and filtered based on config.
*/ */
import * as fs from "node:fs"; import * as fs from "node:fs";
import * as path from "node:path"; import * as path from "node:path";
import * as semver from "semver";
import { $ } from "bun";
import { config } from "../src/config";
const config = { const updateConfig = {
currentVersion: "1.0.0-rc.5",
versions: [
"1.0.0-rc.5",
"1.0.0-rc.4",
"1.0.0-rc.3",
"1.0.0-rc.2",
"1.0.0-rc.1",
],
update: {
urlTemplate:
"https://github.com/jimeh/common-flow/raw/{{version}}/{{file}}",
bodyTemplate: `--- bodyTemplate: `---
title: {{title}} title: {{title}}
version: {{version}} version: {{version}}
--- ---
{{content}}`, {{content}}`,
imgTemplate:
'<img src="/spec/{{file}}" alt="{{title}} diagram" width="100%" />',
outputDir: "src/content/spec", outputDir: "src/content/spec",
publicDir: "public/spec", publicDir: "public/spec",
files: {
document: "common-flow.md",
diagram: "common-flow.svg",
},
},
}; };
/**
* Fetch all tags from the GitHub repository.
*/
async function fetchTags(repository: string): Promise<string[]> {
const repoUrl = `https://github.com/${repository}.git`;
console.log(`Fetching tags from ${repoUrl}...`);
try {
const result = await $`git ls-remote --tags ${repoUrl}`.text();
return result
.split("\n")
.filter(Boolean)
.map((line: string) => line.match(/refs\/tags\/(.+)$/)?.[1])
.filter(
(tag: string | undefined): tag is string =>
tag !== undefined && !tag.endsWith("^{}"),
);
} catch (error) {
throw new Error(
`Failed to fetch tags: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
/**
* Get the prerelease type of a version (e.g., "rc", "draft", or null for
* stable).
*/
function getPrereleaseType(version: string): string | null {
const prerelease = semver.prerelease(version);
if (!prerelease) return null;
return String(prerelease[0]);
}
/**
* Filter tags based on discovery configuration.
* Stable versions are always included; prereleases only if their type is in
* the includePrereleaseTypes list.
*/
function filterVersions(tags: string[]): string[] {
const { includePrereleaseTypes, excludeVersions } = config.update.discovery;
return tags.filter((tag) => {
// Must be valid semver
if (!semver.valid(tag)) return false;
// Check explicit exclusions
if (excludeVersions.includes(tag)) return false;
// Stable versions are always included
const prereleaseType = getPrereleaseType(tag);
if (prereleaseType === null) return true;
// Prereleases only if their type is in the list
return includePrereleaseTypes.includes(prereleaseType);
});
}
function buildFileUrl( function buildFileUrl(
fileType: "document" | "diagram", fileType: "document" | "diagram",
version: string, version: string,
@@ -65,19 +111,36 @@ function writeFile(filePath: string, content: string, comment = ""): void {
console.log(` - ${filePath}${comment}`); console.log(` - ${filePath}${comment}`);
} }
function removeAllSpecs(): void { /**
console.log("\nRemoving existing spec files:"); * Remove spec files for versions not in the provided list.
* Files for versions in the list are left alone (they'll be overwritten).
*/
function removeStaleSpecs(versionsToKeep: string[]): void {
const keepSet = new Set(versionsToKeep);
let removedAny = false;
for (const dir of [updateConfig.outputDir, updateConfig.publicDir]) {
if (!fs.existsSync(dir)) continue;
for (const dir of [config.update.outputDir, config.update.publicDir]) {
if (fs.existsSync(dir)) {
const files = fs.readdirSync(dir); const files = fs.readdirSync(dir);
for (const file of files) { for (const file of files) {
// Extract version from filename (e.g., "1.0.0-rc.1.md" -> "1.0.0-rc.1")
const version = path.basename(file, path.extname(file));
if (!keepSet.has(version)) {
if (!removedAny) {
console.log("\nRemoving stale spec files:");
removedAny = true;
}
const filePath = path.join(dir, file); const filePath = path.join(dir, file);
fs.unlinkSync(filePath); fs.unlinkSync(filePath);
console.log(` ${filePath}`); console.log(` ${filePath}`);
} }
} }
} }
if (!removedAny) {
console.log("\nNo stale spec files to remove.");
}
} }
interface Spec { interface Spec {
@@ -104,16 +167,8 @@ async function fetchSpec(version: string): Promise<Spec> {
// Extract title from first line (after version replacement) // Extract title from first line (after version replacement)
const title = document.split("\n", 1)[0]; const title = document.split("\n", 1)[0];
// If diagram exists, inject image tag after the title
if (diagram) {
const imgTag = config.update.imgTemplate
.replace("{{file}}", `${version}.svg`)
.replace("{{title}}", title);
document = document.replace(/^(.*\n=+\n)/, `$1\n${imgTag}\n`);
}
// Build body with frontmatter // Build body with frontmatter
const body = config.update.bodyTemplate const body = updateConfig.bodyTemplate
.replace("{{content}}", document) .replace("{{content}}", document)
.replace("{{title}}", title) .replace("{{title}}", title)
.replace("{{version}}", version); .replace("{{version}}", version);
@@ -127,21 +182,38 @@ async function fetchSpec(version: string): Promise<Spec> {
} }
async function main(): Promise<void> { async function main(): Promise<void> {
removeAllSpecs(); // 1. Discover and filter versions
const tags = await fetchTags(config.update.repository);
console.log(`Found ${tags.length} tags`);
console.log("\nFetching configured spec versions:"); const filtered = filterVersions(tags);
const sorted = semver.rsort([...filtered]);
for (const version of config.versions) { console.log(`\nIncluded ${sorted.length} versions after filtering:`);
console.log(` ${sorted.join(", ")}`);
if (sorted.length === 0) {
console.error("\nNo versions to process. Exiting.");
process.exit(1);
}
// 2. Remove spec files for versions no longer in the list
removeStaleSpecs(sorted);
// 3. Fetch specs for all versions
console.log("\nFetching spec documents:");
for (const version of sorted) {
try { try {
const spec = await fetchSpec(version); const spec = await fetchSpec(version);
// Write markdown file to content collection // Write markdown file to content collection
const mdPath = path.join(config.update.outputDir, `${version}.md`); const mdPath = path.join(updateConfig.outputDir, `${version}.md`);
writeFile(mdPath, spec.body); writeFile(mdPath, spec.body);
// Write SVG diagram to public directory // Write SVG diagram to public directory
if (spec.diagram) { if (spec.diagram) {
const svgPath = path.join(config.update.publicDir, `${version}.svg`); const svgPath = path.join(updateConfig.publicDir, `${version}.svg`);
writeFile(svgPath, spec.diagram); writeFile(svgPath, spec.diagram);
} }
} catch (error) { } catch (error) {

View File

@@ -6,9 +6,10 @@ import { config } from "../config";
interface Props { interface Props {
version: string; version: string;
versions: string[];
} }
const { version } = Astro.props; const { version, versions } = Astro.props;
const navItems = [ const navItems = [
{ id: "about", label: "About", icon: "heroicons:information-circle" }, { id: "about", label: "About", icon: "heroicons:information-circle" },
@@ -39,10 +40,7 @@ const navItems = [
</span> </span>
</a> </a>
<div class="hidden md:block"> <div class="hidden md:block">
<VersionSelector <VersionSelector currentVersion={version} versions={versions} />
currentVersion={version}
versions={Array.from(config.versions)}
/>
</div> </div>
</div> </div>
@@ -104,10 +102,7 @@ const navItems = [
> >
<div class="px-4 py-3 space-y-1 text-center"> <div class="px-4 py-3 space-y-1 text-center">
<div class="py-2 flex justify-center"> <div class="py-2 flex justify-center">
<VersionSelector <VersionSelector currentVersion={version} versions={versions} />
currentVersion={version}
versions={Array.from(config.versions)}
/>
</div> </div>
{ {
navItems.map((item) => ( navItems.map((item) => (

View File

@@ -6,10 +6,11 @@ import { config } from "../config";
interface Props { interface Props {
version: string; version: string;
versions: string[];
svgPath: string; svgPath: string;
} }
const { version, svgPath } = Astro.props; const { version, versions, svgPath } = Astro.props;
--- ---
<section <section
@@ -42,10 +43,7 @@ const { version, svgPath } = Astro.props;
px-6 py-4 animate-fade-in-down" px-6 py-4 animate-fade-in-down"
> >
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<VersionSelector <VersionSelector currentVersion={version} versions={versions} />
currentVersion={version}
versions={Array.from(config.versions)}
/>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<ThemeToggle /> <ThemeToggle />

View File

@@ -12,16 +12,8 @@ import { Icon } from "astro-icon/components";
hover:bg-gray-100 dark:hover:bg-neutral-800" hover:bg-gray-100 dark:hover:bg-neutral-800"
aria-label="Toggle theme" aria-label="Toggle theme"
> >
<Icon <Icon name="heroicons:sun" data-theme-icon="light" class="hidden w-5 h-5" />
name="heroicons:sun" <Icon name="heroicons:moon" data-theme-icon="dark" class="hidden w-5 h-5" />
data-theme-icon="light"
class="hidden w-5 h-5"
/>
<Icon
name="heroicons:moon"
data-theme-icon="dark"
class="hidden w-5 h-5"
/>
<Icon <Icon
name="heroicons:computer-desktop" name="heroicons:computer-desktop"
data-theme-icon="auto" data-theme-icon="auto"

View File

@@ -13,23 +13,25 @@ export const config = {
url: "https://creativecommons.org/licenses/by/4.0/", url: "https://creativecommons.org/licenses/by/4.0/",
}, },
currentVersion: "1.0.0-rc.5", // Optional override for current version (null = auto-detect from specs)
versions: [ currentVersionOverride: null as string | null,
"1.0.0-rc.5",
"1.0.0-rc.4",
"1.0.0-rc.3",
"1.0.0-rc.2",
"1.0.0-rc.1",
],
// Used by update script // Used by update script
update: { update: {
repository: "jimeh/common-flow",
urlTemplate: urlTemplate:
"https://github.com/jimeh/common-flow/raw/{{version}}/{{file}}", "https://github.com/jimeh/common-flow/raw/{{version}}/{{file}}",
files: { files: {
document: "common-flow.md", document: "common-flow.md",
diagram: "common-flow.svg", diagram: "common-flow.svg",
}, },
// Version discovery settings
discovery: {
// Prerelease types to include (stable versions are always included)
includePrereleaseTypes: ["rc"] as string[],
// Explicit versions to exclude
excludeVersions: [] as string[],
},
}, },
} as const; } as const;

View File

@@ -2,7 +2,12 @@ import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders"; import { glob } from "astro/loaders";
const spec = defineCollection({ const spec = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/content/spec" }), loader: glob({
pattern: "**/*.md",
base: "./src/content/spec",
// Use filename (without extension) as ID to preserve version strings
generateId: ({ entry }) => entry.replace(/\.md$/, ""),
}),
schema: z.object({ schema: z.object({
title: z.string(), title: z.string(),
version: z.string(), version: z.string(),

View File

@@ -2,12 +2,11 @@
title: Git Common-Flow 1.0.0-rc.1 title: Git Common-Flow 1.0.0-rc.1
version: 1.0.0-rc.1 version: 1.0.0-rc.1
--- ---
Git Common-Flow 1.0.0-rc.1
==============================
# Git Common-Flow 1.0.0-rc.1 Summary
-------
<img src="/spec/1.0.0-rc.1.svg" alt="Git Common-Flow 1.0.0-rc.1 diagram" width="100%" />
## Summary
Common-Flow is an attempt to gather a sensible selection of the most common Common-Flow is an attempt to gather a sensible selection of the most common
usage patterns of git into a single and concise specification. It is based on usage patterns of git into a single and concise specification. It is based on
@@ -19,7 +18,8 @@ TL;DR: Common-Flow is basically GitHub Flow with the addition of versioned
releases, maintenance releases for old versions, and without the requirement to releases, maintenance releases for old versions, and without the requirement to
deploy to production all the time. deploy to production all the time.
## Terminology Terminology
-----------
- **Master Branch** - Must always have passing tests, is considered bleeding - **Master Branch** - Must always have passing tests, is considered bleeding
edge, and must be named `master`. edge, and must be named `master`.
@@ -43,7 +43,8 @@ deploy to production all the time.
commit and release tag are on a maintenance branch instead of the master commit and release tag are on a maintenance branch instead of the master
branch. branch.
## Git Common-Flow Specification (Common-Flow) Git Common-Flow Specification (Common-Flow)
-------------------------------------------
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
@@ -150,22 +151,23 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
3. A "maintenance release" is identical to a regular release, except the 3. A "maintenance release" is identical to a regular release, except the
version bump commit and the release tag are placed on the maintenance version bump commit and the release tag are placed on the maintenance
branch instead of on the master branch. branch instead of on the master branch.
4. A maintenance branch SHOULD follow a "stable-X.Y" naming pattern, where 3. A maintenance branch SHOULD follow a "stable-X.Y" naming pattern, where
"X" is the MAJOR version and "Y" is the minor version. "X" is the MAJOR version and "Y" is the minor version.
5. A maintenance branch MUST be created from the relevant release tag. For 4. A maintenance branch MUST be created from the relevant release tag. For
example if there is a security fix for all 2.9.x releases, the latest of example if there is a security fix for all 2.9.x releases, the latest of
which is "2.9.7", we create a new branch called "stable-2.9" off of the which is "2.9.7", we create a new branch called "stable-2.9" off of the
"2.9.7" release tag. The security fix release will then end up being "2.9.7" release tag. The security fix release will then end up being
version "2.9.8". version "2.9.8".
6. When working on a maintenance release, the relevant maintenance branch 5. When working on a maintenance release, the relevant maintenance branch
MUST be thought of as the master branch for that maintenance work. MUST be thought of as the master branch for that maintenance work.
7. Changes in a maintenance branch SHOULD typically come from work being 6. Changes in a maintenance branch SHOULD typically come from work being
done against the master branch. Meaning changes SHOULD only trickle done against the master branch. Meaning changes SHOULD only trickle
downwards from the master branch. If a change needs to trickle back up downwards from the master branch. If a change needs to trickle back up
into the master branch, that work should have happened against the master into the master branch, that work should have happened against the master
branch in the first place. branch in the first place.
## About About
-----
The Git Common-Flow specification is authored The Git Common-Flow specification is authored
by [Jim Myhrberg](http://jimeh.me). by [Jim Myhrberg](http://jimeh.me).
@@ -173,6 +175,7 @@ by [Jim Myhrberg](http://jimeh.me).
If you'd like to leave feedback, If you'd like to leave feedback,
please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues). please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues).
## License License
-------
[Creative Commons - CC BY 3.0](http://creativecommons.org/licenses/by/3.0/) [Creative Commons - CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)

View File

@@ -2,12 +2,11 @@
title: Git Common-Flow 1.0.0-rc.2 title: Git Common-Flow 1.0.0-rc.2
version: 1.0.0-rc.2 version: 1.0.0-rc.2
--- ---
Git Common-Flow 1.0.0-rc.2
==============================
# Git Common-Flow 1.0.0-rc.2 Summary
-------
<img src="/spec/1.0.0-rc.2.svg" alt="Git Common-Flow 1.0.0-rc.2 diagram" width="100%" />
## Summary
Common-Flow is an attempt to gather a sensible selection of the most common Common-Flow is an attempt to gather a sensible selection of the most common
usage patterns of git into a single and concise specification. It is based on usage patterns of git into a single and concise specification. It is based on
@@ -19,7 +18,8 @@ TL;DR: Common-Flow is basically GitHub Flow with the addition of versioned
releases, maintenance releases for old versions, and without the requirement to releases, maintenance releases for old versions, and without the requirement to
deploy to production all the time. deploy to production all the time.
## Terminology Terminology
-----------
- **Master Branch** - Must always have passing tests, is considered bleeding - **Master Branch** - Must always have passing tests, is considered bleeding
edge, and must be named `master`. edge, and must be named `master`.
@@ -38,7 +38,8 @@ deploy to production all the time.
- **Release Branches** - Used both for short-term preparations of a release, and - **Release Branches** - Used both for short-term preparations of a release, and
also for long-term maintenance of older version. also for long-term maintenance of older version.
## Git Common-Flow Specification (Common-Flow) Git Common-Flow Specification (Common-Flow)
-------------------------------------------
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
@@ -190,7 +191,8 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
and creates a merge commit to mark the integration of the branch with and creates a merge commit to mark the integration of the branch with
master. master.
## About About
-----
The Git Common-Flow specification is authored The Git Common-Flow specification is authored
by [Jim Myhrberg](http://jimeh.me). by [Jim Myhrberg](http://jimeh.me).
@@ -198,6 +200,7 @@ by [Jim Myhrberg](http://jimeh.me).
If you'd like to leave feedback, If you'd like to leave feedback,
please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues). please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues).
## License License
-------
[Creative Commons - CC BY 3.0](http://creativecommons.org/licenses/by/3.0/) [Creative Commons - CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)

View File

@@ -2,12 +2,11 @@
title: Git Common-Flow 1.0.0-rc.3 title: Git Common-Flow 1.0.0-rc.3
version: 1.0.0-rc.3 version: 1.0.0-rc.3
--- ---
Git Common-Flow 1.0.0-rc.3
===========================
# Git Common-Flow 1.0.0-rc.3 Summary
-------
<img src="/spec/1.0.0-rc.3.svg" alt="Git Common-Flow 1.0.0-rc.3 diagram" width="100%" />
## Summary
Common-Flow is an attempt to gather a sensible selection of the most common Common-Flow is an attempt to gather a sensible selection of the most common
usage patterns of git into a single and concise specification. It is based on usage patterns of git into a single and concise specification. It is based on
@@ -19,7 +18,8 @@ In short, Common-Flow is essentially GitHub Flow with the addition of versioned
releases, optional release branches, and without the requirement to deploy to releases, optional release branches, and without the requirement to deploy to
production all the time. production all the time.
## Terminology Terminology
-----------
- **Master Branch** - Must be named "master", must always have passing tests, - **Master Branch** - Must be named "master", must always have passing tests,
and is not guaranteed to always work in production environments. and is not guaranteed to always work in production environments.
@@ -39,7 +39,8 @@ production all the time.
- **Release Branches** - Used both for short-term preparations of a release, and - **Release Branches** - Used both for short-term preparations of a release, and
also for long-term maintenance of older version. also for long-term maintenance of older version.
## Git Common-Flow Specification (Common-Flow) Git Common-Flow Specification (Common-Flow)
-------------------------------------------
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
@@ -53,10 +54,10 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
"master branch". "master branch".
2. The master branch MUST always be in a non-broken state with its test 2. The master branch MUST always be in a non-broken state with its test
suite passing. suite passing.
3. The master branch IS NOT guaranteed to always work in production 4. The master branch IS NOT guaranteed to always work in production
environments. Despite test suites passing it may at times contain environments. Despite test suites passing it may at times contain
unfinished work. Only releases may be considered safe for production use. unfinished work. Only releases may be considered safe for production use.
4. The master branch SHOULD always be in a "as near as possibly ready for 5. The master branch SHOULD always be in a "as near as possibly ready for
release/production" state to reduce any friction with creating a new release/production" state to reduce any friction with creating a new
release. release.
3. Change Branches 3. Change Branches
@@ -196,7 +197,8 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
and creates a merge commit to mark the integration of the branch with and creates a merge commit to mark the integration of the branch with
master. master.
## About About
-----
The Git Common-Flow specification is authored The Git Common-Flow specification is authored
by [Jim Myhrberg](http://jimeh.me). by [Jim Myhrberg](http://jimeh.me).
@@ -204,6 +206,7 @@ by [Jim Myhrberg](http://jimeh.me).
If you'd like to leave feedback, If you'd like to leave feedback,
please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues). please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues).
## License License
-------
[Creative Commons - CC BY 3.0](http://creativecommons.org/licenses/by/3.0/) [Creative Commons - CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)

View File

@@ -2,12 +2,11 @@
title: Git Common-Flow 1.0.0-rc.4 title: Git Common-Flow 1.0.0-rc.4
version: 1.0.0-rc.4 version: 1.0.0-rc.4
--- ---
Git Common-Flow 1.0.0-rc.4
===========================
# Git Common-Flow 1.0.0-rc.4 Summary
-------
<img src="/spec/1.0.0-rc.4.svg" alt="Git Common-Flow 1.0.0-rc.4 diagram" width="100%" />
## Summary
Common-Flow is an attempt to gather a sensible selection of the most common Common-Flow is an attempt to gather a sensible selection of the most common
usage patterns of git into a single and concise specification. It is based on usage patterns of git into a single and concise specification. It is based on
@@ -19,7 +18,8 @@ In short, Common-Flow is essentially GitHub Flow with the addition of versioned
releases, optional release branches, and without the requirement to deploy to releases, optional release branches, and without the requirement to deploy to
production all the time. production all the time.
## Terminology Terminology
-----------
- **Master Branch** - Must be named "master", must always have passing tests, - **Master Branch** - Must be named "master", must always have passing tests,
and is not guaranteed to always work in production environments. and is not guaranteed to always work in production environments.
@@ -38,7 +38,8 @@ production all the time.
- **Release Branches** - Used both for short-term preparations of a release, and - **Release Branches** - Used both for short-term preparations of a release, and
also for long-term maintenance of older version. also for long-term maintenance of older version.
## Git Common-Flow Specification (Common-Flow) Git Common-Flow Specification (Common-Flow)
-------------------------------------------
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
@@ -52,10 +53,10 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
"master branch". "master branch".
2. The master branch MUST always be in a non-broken state with its test 2. The master branch MUST always be in a non-broken state with its test
suite passing. suite passing.
3. The master branch IS NOT guaranteed to always work in production 4. The master branch IS NOT guaranteed to always work in production
environments. Despite test suites passing it may at times contain environments. Despite test suites passing it may at times contain
unfinished work. Only releases may be considered safe for production use. unfinished work. Only releases may be considered safe for production use.
4. The master branch SHOULD always be in a "as near as possibly ready for 5. The master branch SHOULD always be in a "as near as possibly ready for
release/production" state to reduce any friction with creating a new release/production" state to reduce any friction with creating a new
release. release.
3. Change Branches 3. Change Branches
@@ -199,7 +200,7 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
5. To create a new release from a long-term release branch, you MUST follow 5. To create a new release from a long-term release branch, you MUST follow
the same process as a release from the master branch, except the the same process as a release from the master branch, except the
long-term release branch takes the place of the master branch. long-term release branch takes the place of the master branch.
6. A long-term release branch should be treated with the same respect as the 7. A long-term release branch should be treated with the same respect as the
master branch. It is effectively the master branch for the release series master branch. It is effectively the master branch for the release series
in question. Meaning it MUST always be in a non-broken state, MUST NOT be in question. Meaning it MUST always be in a non-broken state, MUST NOT be
force pushed to, etc. force pushed to, etc.
@@ -238,7 +239,8 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
and creates a merge commit to mark the integration of the branch with and creates a merge commit to mark the integration of the branch with
master. master.
## FAQ FAQ
---
### Why use Common-Flow instead of Git Flow, and how does it differ? ### Why use Common-Flow instead of Git Flow, and how does it differ?
@@ -321,7 +323,8 @@ complicated task and you're short on time, a short-term release branch gives you
a instant fix to the situation at hand, and let's you resolve the issues with a instant fix to the situation at hand, and let's you resolve the issues with
the master branch when you have more time on your hands. the master branch when you have more time on your hands.
## About About
-----
The Git Common-Flow specification is authored The Git Common-Flow specification is authored
by [Jim Myhrberg](http://jimeh.me). by [Jim Myhrberg](http://jimeh.me).
@@ -329,6 +332,7 @@ by [Jim Myhrberg](http://jimeh.me).
If you'd like to leave feedback, If you'd like to leave feedback,
please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues). please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues).
## License License
-------
[Creative Commons - CC BY 3.0](http://creativecommons.org/licenses/by/3.0/) [Creative Commons - CC BY 3.0](http://creativecommons.org/licenses/by/3.0/)

View File

@@ -2,12 +2,11 @@
title: Git Common-Flow 1.0.0-rc.5 title: Git Common-Flow 1.0.0-rc.5
version: 1.0.0-rc.5 version: 1.0.0-rc.5
--- ---
Git Common-Flow 1.0.0-rc.5
===========================
# Git Common-Flow 1.0.0-rc.5 Introduction
------------
<img src="/spec/1.0.0-rc.5.svg" alt="Git Common-Flow 1.0.0-rc.5 diagram" width="100%" />
## Introduction
Common-Flow is an attempt to gather a sensible selection of the most common Common-Flow is an attempt to gather a sensible selection of the most common
usage patterns of git into a single and concise specification. It is based on usage patterns of git into a single and concise specification. It is based on
@@ -19,7 +18,8 @@ In short, Common-Flow is essentially GitHub Flow with the addition of versioned
releases, optional release branches, and without the requirement to deploy to releases, optional release branches, and without the requirement to deploy to
production all the time. production all the time.
## Summary Summary
-------
- The "master" branch is the mainline branch with latest changes, and must not - The "master" branch is the mainline branch with latest changes, and must not
be broken. be broken.
@@ -32,7 +32,8 @@ production all the time.
- Release branches can be used to avoid change freezes on master. They are not - Release branches can be used to avoid change freezes on master. They are not
required, instead they are available if you need them. required, instead they are available if you need them.
## Terminology Terminology
-----------
- **Master Branch** - Must be named "master", must always have passing tests, - **Master Branch** - Must be named "master", must always have passing tests,
and is not guaranteed to always work in production environments. and is not guaranteed to always work in production environments.
@@ -51,7 +52,8 @@ production all the time.
- **Release Branches** - Used both for short-term preparations of a release, and - **Release Branches** - Used both for short-term preparations of a release, and
also for long-term maintenance of older version. also for long-term maintenance of older version.
## Git Common-Flow Specification (Common-Flow) Git Common-Flow Specification (Common-Flow)
-------------------------------------------
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
@@ -65,10 +67,10 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
"master branch". "master branch".
2. The master branch MUST always be in a non-broken state with its test 2. The master branch MUST always be in a non-broken state with its test
suite passing. suite passing.
3. The master branch IS NOT guaranteed to always work in production 4. The master branch IS NOT guaranteed to always work in production
environments. Despite test suites passing it may at times contain environments. Despite test suites passing it may at times contain
unfinished work. Only releases may be considered safe for production use. unfinished work. Only releases may be considered safe for production use.
4. The master branch SHOULD always be in a "as near as possibly ready for 5. The master branch SHOULD always be in a "as near as possibly ready for
release/production" state to reduce any friction with creating a new release/production" state to reduce any friction with creating a new
release. release.
3. Change Branches 3. Change Branches
@@ -213,7 +215,7 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
5. To create a new release from a long-term release branch, you MUST follow 5. To create a new release from a long-term release branch, you MUST follow
the same process as a release from the master branch, except the the same process as a release from the master branch, except the
long-term release branch takes the place of the master branch. long-term release branch takes the place of the master branch.
6. A long-term release branch should be treated with the same respect as the 7. A long-term release branch should be treated with the same respect as the
master branch. It is effectively the master branch for the release series master branch. It is effectively the master branch for the release series
in question. Meaning it MUST always be in a non-broken state, MUST NOT be in question. Meaning it MUST always be in a non-broken state, MUST NOT be
force pushed to, etc. force pushed to, etc.
@@ -252,7 +254,8 @@ interpreted as described in [RFC 2119](https://tools.ietf.org/html/rfc2119).
and creates a merge commit to mark the integration of the branch with and creates a merge commit to mark the integration of the branch with
master. master.
## FAQ FAQ
---
### Why use Common-Flow instead of Git Flow, and how does it differ? ### Why use Common-Flow instead of Git Flow, and how does it differ?
@@ -341,7 +344,8 @@ complicated task and you're short on time, a short-term release branch gives you
a instant fix to the situation at hand, and let's you resolve the issues with a instant fix to the situation at hand, and let's you resolve the issues with
the master branch when you have more time on your hands. the master branch when you have more time on your hands.
## About About
-----
The Git Common-Flow specification is authored The Git Common-Flow specification is authored
by [Jim Myhrberg](https://jimeh.me/). by [Jim Myhrberg](https://jimeh.me/).
@@ -349,6 +353,7 @@ by [Jim Myhrberg](https://jimeh.me/).
If you'd like to leave feedback, If you'd like to leave feedback,
please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues). please [open an issue on GitHub](https://github.com/jimeh/common-flow/issues).
## License License
-------
[Creative Commons - CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) [Creative Commons - CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)

View File

@@ -14,9 +14,10 @@ import { parseSpecContent } from "../utils/parseSpecContent";
interface Props { interface Props {
spec: CollectionEntry<"spec">; spec: CollectionEntry<"spec">;
versions: string[];
} }
const { spec } = Astro.props; const { spec, versions } = Astro.props;
const version = spec.data.version; const version = spec.data.version;
// Read the markdown file // Read the markdown file
@@ -31,10 +32,10 @@ const parsed = await parseSpecContent(markdown, version);
--- ---
<BaseLayout title={spec.data.title}> <BaseLayout title={spec.data.title}>
<Header version={version} /> <Header version={version} versions={versions} />
<main> <main>
<Hero version={version} svgPath={parsed.svgPath} /> <Hero version={version} versions={versions} svgPath={parsed.svgPath} />
<AboutSection <AboutSection
introduction={parsed.introduction} introduction={parsed.introduction}

View File

@@ -2,16 +2,16 @@
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import SpecLayout from "../layouts/SpecLayout.astro"; import SpecLayout from "../layouts/SpecLayout.astro";
import { config } from "../config"; import { getVersionInfo } from "../utils/versions";
// Render the current/latest version // Get version info and render the current/latest version
const version = config.currentVersion; const { versions, currentVersion } = await getVersionInfo();
const specs = await getCollection("spec"); const specs = await getCollection("spec");
const spec = specs.find((s) => s.data.version === version); const spec = specs.find((s) => s.data.version === currentVersion);
if (!spec) { if (!spec) {
throw new Error(`Spec version ${version} not found`); throw new Error(`Spec version ${currentVersion} not found`);
} }
--- ---
<SpecLayout spec={spec} /> <SpecLayout spec={spec} versions={versions} />

View File

@@ -2,6 +2,7 @@
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import SpecLayout from "../../layouts/SpecLayout.astro"; import SpecLayout from "../../layouts/SpecLayout.astro";
import { getVersionInfo } from "../../utils/versions";
export async function getStaticPaths() { export async function getStaticPaths() {
const specs = await getCollection("spec"); const specs = await getCollection("spec");
@@ -12,6 +13,7 @@ export async function getStaticPaths() {
} }
const { spec } = Astro.props; const { spec } = Astro.props;
const { versions } = await getVersionInfo();
--- ---
<SpecLayout spec={spec} /> <SpecLayout spec={spec} versions={versions} />

View File

@@ -315,13 +315,9 @@ export async function parseSpecContent(
// Parse markdown to AST // Parse markdown to AST
const tree = unified().use(remarkParse).parse(markdown) as Root; const tree = unified().use(remarkParse).parse(markdown) as Root;
// Remove title (h1) and SVG image from the tree // Remove title (h1) from the tree - it's displayed separately in the Hero
const nodes = tree.children.filter((node) => { const nodes = tree.children.filter((node) => {
if (node.type === "heading" && (node as Heading).depth === 1) return false; if (node.type === "heading" && (node as Heading).depth === 1) return false;
if (node.type === "paragraph") {
const text = extractText(node);
if (text.includes(".svg")) return false;
}
return true; return true;
}); });

48
src/utils/versions.ts Normal file
View File

@@ -0,0 +1,48 @@
import { getCollection } from "astro:content";
import * as semver from "semver";
import { config } from "../config";
export interface VersionInfo {
versions: string[];
currentVersion: string;
}
/**
* Get version information derived from available spec files.
* Returns all versions sorted newest-first and determines the current version.
*/
export async function getVersionInfo(): Promise<VersionInfo> {
const specs = await getCollection("spec");
const versions = specs
.map((s) => s.data.version)
.filter((v): v is string => semver.valid(v) !== null)
.sort((a, b) => semver.rcompare(a, b)); // newest first
const currentVersion =
config.currentVersionOverride ?? determineCurrentVersion(versions);
return { versions, currentVersion };
}
/**
* Determine the current version based on priority:
* 1. Latest stable version
* 2. Latest RC version
* 3. Newest available version
*/
function determineCurrentVersion(versions: string[]): string {
// Priority order: stable (null prerelease) first, then rc
const priority: (string | null)[] = [null, "rc"];
for (const type of priority) {
const match = versions.find((v) => {
const pre = semver.prerelease(v);
if (type === null) return pre === null;
return pre?.[0] === type;
});
if (match) return match;
}
// Fallback to newest overall
return versions[0] ?? "";
}