mirror of
https://github.com/jimeh/commonflow.org.git
synced 2026-02-19 05:46:40 +00:00
wip: improve update-specs script
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
9
bun.lock
9
bun.lock
@@ -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=="],
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) => (
|
||||||
|
|||||||
@@ -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 />
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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/)
|
||||||
|
|||||||
@@ -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/)
|
||||||
|
|||||||
@@ -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/)
|
||||||
|
|||||||
@@ -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/)
|
||||||
|
|||||||
@@ -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/)
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
@@ -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
48
src/utils/versions.ts
Normal 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] ?? "";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user