diff --git a/.github/workflows/reusable-build-deploy-api-editor-docs.yml b/.github/workflows/reusable-build-deploy-api-editor-docs.yml index 0ee2b9a0..19fd5e5a 100644 --- a/.github/workflows/reusable-build-deploy-api-editor-docs.yml +++ b/.github/workflows/reusable-build-deploy-api-editor-docs.yml @@ -51,6 +51,7 @@ jobs: uses: ./.github/workflows/reusable-build-docs-application.yml with: artifact-name: docs + build-name: ${{ inputs.build-name }} api-url: https://${{ inputs.build-name }}.facturxdotnet.org/api editor-url: https://${{ inputs.build-name }}.facturxdotnet.org/editor version: ${{ needs.compute-version.outputs.version }} diff --git a/.github/workflows/reusable-build-docs-application.yml b/.github/workflows/reusable-build-docs-application.yml index 237fb916..af841d0f 100644 --- a/.github/workflows/reusable-build-docs-application.yml +++ b/.github/workflows/reusable-build-docs-application.yml @@ -8,6 +8,11 @@ on: type: string required: true + build-name: + description: The name of the build, defaults to 'build' + type: string + required: true + api-url: description: The URL at which the FacturX.NET API will be served type: string @@ -198,10 +203,20 @@ jobs: echo echo - - name: Set version number + - name: Write env.json run: | - sed -i -e 's|{VERSION}|${{ inputs.version }}|g' -e 's|{API-URL}|${{ inputs.api-url }}|g' -e 's|{EDITOR-URL}|${{ inputs.editor-url }}|g' docs/.vitepress/config.mts - sed -i -e 's|{VERSION}|${{ inputs.version }}|g' -e 's|{API-URL}|${{ inputs.api-url }}|g' -e 's|{EDITOR-URL}|${{ inputs.editor-url }}|g' docs/src/index.md + echo '{' > docs/src/env.json + echo ' "buildName": "${{ inputs.build-name }}",' >> docs/src/env.json + echo ' "version": "${{ inputs.version }}",' >> docs/src/env.json + echo ' "editor": {' >> docs/src/env.json + echo ' "url": "${{ inputs.editor-url }}"' >> docs/src/env.json + echo ' },' >> docs/src/env.json + echo ' "api": {' >> docs/src/env.json + echo ' "url": "${{ inputs.api-url }}"' >> docs/src/env.json + echo ' }' >> docs/src/env.json + echo '}' >> docs/src/env.json + + cat docs/src/env.json - name: Build run: cd docs; npm run build -- --base /docs/ --outDir dist/ diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 79ffeab5..d4505fd4 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -1,7 +1,9 @@ -import {defineConfigWithTheme} from "vitepress"; // https://vitepress.dev/reference/site-config -import {useSidebar} from "vitepress-openapi"; -import {getSidebar} from "vitepress-plugin-auto-sidebar"; -import spec from "../src/assets/facturxdotnet.openapi.json" with {type: "json"}; +import { defineConfigWithTheme } from "vitepress"; // https://vitepress.dev/reference/site-config +import { useSidebar } from "vitepress-openapi"; +import { getSidebar } from "vitepress-plugin-auto-sidebar"; +import semver from "semver"; +import env from "../src/env.json" with { type: "json" }; +import spec from "../src/assets/facturxdotnet.openapi.json" with { type: "json" }; const specSidebar = useSidebar({ spec, @@ -23,7 +25,7 @@ let cliItems = collapsed: false, collapsible: false, })[0]?.items ?? []; -cliItems = cliItems.map(item => ({ +cliItems = cliItems.map((item) => ({ ...item, text: item.text.toLowerCase() === "subcommands" ? "Sub Commands" : item.text, })); @@ -37,9 +39,26 @@ let apiReferenceItems = collapsible: true, })[0]?.items ?? []; apiReferenceItems = apiReferenceItems.filter( - i => i.items !== undefined && i.items.length > 0, + (i) => i.items !== undefined && i.items.length > 0, ); +const semVersion = semver.valid(env.version) + ? semver.parse(env.version) + : { version: env.version, build: [] }; + +const footerText = [`${env.buildName} ยท v${semVersion.version}`]; +if (semVersion.build.length) { + footerText.push(`ID: ${semVersion.build.join(".")}`); +} + +const sidebarFooter = [ + { + text: ` + ${footerText.join("
")} +
`, + }, +]; + // https://vitepress.dev/reference/site-config export default defineConfigWithTheme({ srcDir: "./src", @@ -71,11 +90,11 @@ export default defineConfigWithTheme({ items: [ { text: "Try the Editor", - link: "{EDITOR-URL}", + link: env.editor.url, }, { text: "Try the API", - link: "{API-URL}", + link: env.api.url, }, ], }, @@ -110,7 +129,7 @@ export default defineConfigWithTheme({ text: "Use cases", items: [ { - text: "Generation", + text: `Generation`, items: [ { text: "Generate a Factur-X document", @@ -123,7 +142,7 @@ export default defineConfigWithTheme({ ], }, { - text: "Validation", + text: `Validation`, items: [ { text: "Validate a Factur-X document", @@ -136,7 +155,7 @@ export default defineConfigWithTheme({ ], }, { - text: "Extraction", + text: `Extraction`, items: [ { text: "Extract Cross-Industry Invoice data", @@ -151,9 +170,7 @@ export default defineConfigWithTheme({ text: "About", link: "/guides/about", }, - { - text: "v{VERSION}", - }, + ...sidebarFooter, ], "/openapi-specification/": [ { @@ -161,22 +178,13 @@ export default defineConfigWithTheme({ link: "/openapi-specification/introduction", }, ...specSidebar.generateSidebarGroups(), - { - text: "v{VERSION}", - }, - ], - "/cli/": [ - ...cliItems, - { - text: "v{VERSION}", - }, + ...sidebarFooter, ], + "/cli/": [...cliItems, ...sidebarFooter], "/api-reference/": [ { text: "Index", link: "/api-reference/index" }, ...apiReferenceItems, - { - text: "v{VERSION}", - }, + ...sidebarFooter, ], }, @@ -209,4 +217,55 @@ export default defineConfigWithTheme({ noExternal: ["vitepress-plugin-nprogress"], }, }, + + transformPageData: (pageData) => { + const result = { ...pageData }; + result.frontmatter = expandEnvInRecord(pageData.frontmatter); + return result; + }, }); + +function expandEnv(value: unknown): unknown { + if (typeof value === "string" || value instanceof String) { + return expandEnvInString(value); + } else if (value.constructor.name == "Array") { + return expandEnvInArray(value); + } else { + return expandEnvInRecord(value); + } +} + +function expandEnvInArray(array: unknown[]): unknown[] { + return array.map(expandEnv); +} + +function expandEnvInRecord( + record: Record, +): Record { + const result = {}; + + for (const key of Object.keys(record)) { + result[key] = expandEnv(record[key]); + } + + return result; +} + +function expandEnvInString(value: string): string { + return value.replace(/\$env\.([^ ]*)/g, (_, varName) => getEnvValue(varName)); +} + +function getEnvValue(varName: string): string { + const fragments = varName.split("."); + let result = env; + + for (const fragment of fragments) { + if (!Object.keys(result).includes(fragment)) { + return ""; + } + + result = result[fragment]; + } + + return result; +} diff --git a/docs/.vitepress/theme/custom.css b/docs/.vitepress/theme/custom.css index f4c7db15..110b3b18 100644 --- a/docs/.vitepress/theme/custom.css +++ b/docs/.vitepress/theme/custom.css @@ -10,6 +10,16 @@ --vp-c-brand-3: #512bd4; } +.VPSidebarItem .item { + cursor: unset; +} + +p:has(.sidebar-footer) { + margin-top: 24px; + padding: 0 !important; + line-height: unset !important; +} + .sidebar-footer { font-size: 0.75rem; color: var(--vp-c-text-3); diff --git a/docs/package-lock.json b/docs/package-lock.json index 23635727..cf529657 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "semver": "^7.7.1", "vitepress-openapi": "^0.0.3-alpha.76", "vitepress-plugin-auto-sidebar": "^1.3.5", "vitepress-plugin-image-viewer": "^1.1.6" @@ -2715,7 +2716,6 @@ "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" diff --git a/docs/package.json b/docs/package.json index 67975c29..d26ecc87 100644 --- a/docs/package.json +++ b/docs/package.json @@ -11,6 +11,7 @@ "vitepress-plugin-nprogress": "^0.0.4" }, "dependencies": { + "semver": "^7.7.1", "vitepress-openapi": "^0.0.3-alpha.76", "vitepress-plugin-auto-sidebar": "^1.3.5", "vitepress-plugin-image-viewer": "^1.1.6" diff --git a/docs/src/env.data.ts b/docs/src/env.data.ts new file mode 100644 index 00000000..9cdb8d59 --- /dev/null +++ b/docs/src/env.data.ts @@ -0,0 +1,21 @@ +import fs from "node:fs"; + +export default { + watch: ["./env.json"], + load(watchedFile) { + const file = watchedFile[0]; + const fileContent = fs.readFileSync(file, "utf-8"); + return JSON.parse(fileContent) as Env; + }, +}; + +interface Env { + buildName: string; + version: string; + editor: { + url: string; + }; + api: { + url: string; + }; +} diff --git a/docs/src/env.json b/docs/src/env.json new file mode 100644 index 00000000..bc44b21f --- /dev/null +++ b/docs/src/env.json @@ -0,0 +1,10 @@ +{ + "buildName": "dev", + "version": "~dev", + "editor": { + "url": "http://localhost:4200" + }, + "api": { + "url": "http://localhost:5295" + } +} diff --git a/docs/src/index.md b/docs/src/index.md index ed7c2d92..f2387ff4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -28,7 +28,7 @@ features: alt: 'WebSite' } details: Create, view, and edit Factur-X documents directly in your browser with our user-friendly editor. - link: {EDITOR-URL} + link: $env.editor.url linkText: Try it live - title: API icon: { @@ -37,7 +37,7 @@ features: alt: 'API' } details: Programmatically generate, read, and validate Factur-X documents through our powerful API. - link: {API-URL} + link: $env.api.url linkText: Try it live - title: .NET library and tool icon: {