mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 21:52:54 +00:00
## Todos for release - [x] Configure Env in docs project on vercel - [x] Configure Root Path in the docs project on vercel - [ ] Remove old CSP https://github.com/zitadel/website/pull/1592 ## What we did This pull request migrates the project documentation from the old `docs/` directory to the new `apps/docs/` directory, introduces a new documentation system built with Next.js and Fumadocs, and updates all relevant references, configuration files, and documentation to reflect this change. It also adds new configuration and ignore files for the new documentation app, updates CI and linting to exclude the new docs from certain checks, and revises the contributing guidelines accordingly. **Documentation System Migration and New Docs App** * Migrated all documentation from `docs/` to `apps/docs/`, and updated all references in `README.md`, `CONTRIBUTING.md`, and other files to point to the new location. [[1]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L585-L640) [[2]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L660-R607) [[3]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L740-R687) [[4]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L2-R3) [[5]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L30-R30) * Added a new Next.js/Fumadocs-based documentation app under `apps/docs/`, including core app files, layouts, routing, search API, and a comprehensive `README.md` with development and contribution instructions. [[1]](diffhunk://#diff-5a1b07344a2c1b4d3f37b23ff1388b62cd9f57dea3c1cd23d8a0412b7602b132R1-R74) [[2]](diffhunk://#diff-462b9ad1eabbb7d1c29bb9c36e4931eb180fd7190900e6d2babf8f4d66ad1c28R1-R39) [[3]](diffhunk://#diff-e16ae25660ded787b10ac35dea96d5ecaacf895dae0afc8a9bd4382dc79a8c87R1-R7) [apps/docs/app/[[...slug]]/layout.tsxR1-R81](diffhunk://#diff-59e08acde4e805b7aeccef1dcf98f1d71dfc550777e6b9402085cee0e9fa4e0aR1-R81), [apps/docs/app/[[...slug]]/page.tsxR1-R79](diffhunk://#diff-e5df3f80d0fa01e12d63d81f29c57a9d14e846c78fd3beabb2ef768e38fd9580R1-R79), [[4]](diffhunk://#diff-389b34918e040cacaa87cd7201ffa462cc2b0b716736f537e3d3c660ac69353bR1-R7) [[5]](diffhunk://#diff-d3b03416d1c457b19f1c27b26f2db741412df841b875c26ccadc36e4522247f4R1-R29) [[6]](diffhunk://#diff-c8fb8339570a5305809be7c618e14705fd86390dc278e6ce8ba224a7bc8b0c3cR1-R25) **Configuration and Tooling Updates** * Updated `.github/workflows/codeql.yml`, `.golangci.yaml`, and `.github/dependabot.yml` to properly handle the new docs app: excluded `apps/docs` from certain checks, added npm dependency updates for the docs app, and excluded generated content. [[1]](diffhunk://#diff-12783128521e452af0cfac94b99b8d250413c516ec71fe6d97dbea666ff7ba27L8-R14) [[2]](diffhunk://#diff-9917ddc9f1c3304218f7269265b746d997c5c0615478177b5fceecd33ef47cb5R5-R6) [[3]](diffhunk://#diff-9917ddc9f1c3304218f7269265b746d997c5c0615478177b5fceecd33ef47cb5R126-R129) [[4]](diffhunk://#diff-dd4fbda47e51f1e35defb9275a9cd9c212ecde0b870cba89ddaaae65c5f3cd28R89-R106) * Updated `.devcontainer/devcontainer.json` to use the latest Go 1.25.3 version for consistency. **Licensing and Miscellaneous** * Added `apps/docs/` to the list of licensed directories in `LICENSING.md`. These changes ensure the documentation is now maintained in a modern, scalable system and all project tooling is updated to support the new structure. --------- Co-authored-by: Federico Coppede <[email protected]>
207 lines
7.8 KiB
JavaScript
207 lines
7.8 KiB
JavaScript
import fs from 'fs';
|
|
import { glob } from 'glob';
|
|
import { join, dirname, resolve } from 'path';
|
|
import { spawnSync } from 'child_process';
|
|
import { fileURLToPath } from 'url';
|
|
import os from 'os';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const ROOT_DIR = join(__dirname, '..');
|
|
const PROTO_DIR = join(ROOT_DIR, '../../proto');
|
|
const OPENAPI_DIR = join(ROOT_DIR, 'openapi');
|
|
const VERSIONS_FILE = join(ROOT_DIR, 'content/versions.json');
|
|
const REPO_URL = 'https://github.com/zitadel/zitadel.git';
|
|
|
|
async function run() {
|
|
if (!fs.existsSync(VERSIONS_FILE)) {
|
|
console.error('versions.json not found. Run fetch-docs.mjs first.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const versions = JSON.parse(fs.readFileSync(VERSIONS_FILE, 'utf8'));
|
|
console.log(`Processing ${versions.length} versions from versions.json`);
|
|
|
|
const baseTempDir = fs.mkdtempSync(join(os.tmpdir(), 'zitadel-buf-'));
|
|
// Use a subdirectory for local generation to avoid pollution
|
|
const localGenDir = join(baseTempDir, 'local');
|
|
fs.mkdirSync(localGenDir, { recursive: true });
|
|
|
|
const templatePath = resolve(join(ROOT_DIR, 'buf.gen.yaml'));
|
|
|
|
try {
|
|
// Simple p-limit implementation to avoid adding dependency
|
|
const pLimit = (concurrency) => {
|
|
const queue = [];
|
|
let active = 0;
|
|
|
|
const next = () => {
|
|
active--;
|
|
if (queue.length > 0) {
|
|
queue.shift()();
|
|
}
|
|
};
|
|
|
|
const run = async (fn) => {
|
|
if (active >= concurrency) {
|
|
await new Promise((resolve) => queue.push(resolve));
|
|
}
|
|
active++;
|
|
try {
|
|
return await fn();
|
|
} finally {
|
|
next();
|
|
}
|
|
};
|
|
|
|
return run;
|
|
};
|
|
|
|
const limit = pLimit(2);
|
|
|
|
await Promise.all(versions.map(v => limit(async () => {
|
|
if (v.type === 'external') return; // Skip archive links
|
|
|
|
const label = v.param;
|
|
const outputPath = resolve(join(OPENAPI_DIR, label));
|
|
|
|
console.log(`\n--- Generating OpenAPI specs for ${label} ---`);
|
|
|
|
fs.rmSync(outputPath, { recursive: true, force: true });
|
|
fs.mkdirSync(outputPath, { recursive: true });
|
|
|
|
// Create a unique temp dir for this specific generation task to avoid conflicts
|
|
const taskTempDir = join(baseTempDir, label);
|
|
fs.mkdirSync(taskTempDir, { recursive: true });
|
|
|
|
// Determine buf input based on refType
|
|
let bufInput;
|
|
if (v.refType === 'local') {
|
|
// Point to local proto directory (repo root/proto)
|
|
bufInput = PROTO_DIR;
|
|
} else {
|
|
const refPart = v.refType === 'branch' ? `branch=${v.ref}` : `tag=${v.ref}`;
|
|
bufInput = `${REPO_URL}#${refPart},subdir=proto`;
|
|
}
|
|
|
|
console.log(`Using input for ${label}: ${bufInput}`);
|
|
|
|
|
|
// Use spawn (async) instead of spawnSync to avoid blocking the event loop
|
|
await new Promise(async (resolvePromise, rejectPromise) => {
|
|
// Dynamic discovery of excluded paths
|
|
const getExcludedPaths = async () => {
|
|
const patterns = ['**/v3alpha', '**/v2beta'];
|
|
const excluded = new Set();
|
|
|
|
if (v.refType === 'local') {
|
|
// For local, use glob against PROTO_DIR
|
|
// patterns are relative to PROTO_DIR, but glob needs to search inside PROTO_DIR
|
|
// The result of glob will be paths relative to PROTO_DIR if cwd is PROTO_DIR
|
|
try {
|
|
const files = await glob(patterns, { cwd: PROTO_DIR, nodir: false });
|
|
// We only want directories that contain v3alpha, or files?
|
|
// buf --exclude-path expects directories or files.
|
|
// If we found directories ending in v3alpha, usage is fine.
|
|
// glob '**/v3alpha' matches directories or files ending in v3alpha.
|
|
// Ensure we pass absolute paths for local input to avoid CWD resolution issues
|
|
files.forEach(f => excluded.add(resolve(PROTO_DIR, f)));
|
|
} catch (e) {
|
|
console.warn('Failed to glob excluded paths locally', e);
|
|
}
|
|
} else {
|
|
// For remote (tag/branch), use git ls-tree
|
|
// We need to run git ls-tree -r <ref> --name-only
|
|
// and filter for lines matching /v3alpha/
|
|
// We run this in ROOT_DIR (where .git presumably is available via parent)
|
|
// The paths returned by git ls-tree are relative to repo root.
|
|
// buf input is relative to 'proto' subdir.
|
|
// So we need to strip 'proto/' prefix.
|
|
try {
|
|
const ref = v.ref;
|
|
const gitCmd = spawnSync('git', ['ls-tree', '-r', '--name-only', ref], { cwd: ROOT_DIR, encoding: 'utf-8' });
|
|
if (gitCmd.error) throw gitCmd.error;
|
|
const files = gitCmd.stdout.split('\n');
|
|
files.forEach(f => {
|
|
if ((f.includes('v3alpha') || f.includes('v2beta')) && f.startsWith('proto/')) {
|
|
// strip 'proto/' (6 chars) and take the directory
|
|
const relPath = f.substring(6);
|
|
const dirPath = dirname(relPath);
|
|
if (dirPath.includes('v3alpha') || dirPath.includes('v2beta')) {
|
|
excluded.add(dirPath);
|
|
} else {
|
|
excluded.add(relPath);
|
|
}
|
|
}
|
|
});
|
|
} catch (e) {
|
|
console.warn(`Failed to list tree for ${v.param}`, e);
|
|
}
|
|
}
|
|
return Array.from(excluded);
|
|
};
|
|
|
|
const excludedPaths = await getExcludedPaths();
|
|
if (excludedPaths.length > 0) {
|
|
console.log(`Excluding ${excludedPaths.length} paths matching v3alpha or v2beta`);
|
|
}
|
|
|
|
import('child_process').then(({ spawn }) => {
|
|
const args = [
|
|
'@bufbuild/buf', 'generate',
|
|
bufInput,
|
|
'--template', templatePath,
|
|
'--output', outputPath
|
|
];
|
|
|
|
// Add excluded paths
|
|
for (const excludedPath of excludedPaths) {
|
|
args.push('--exclude-path', excludedPath);
|
|
}
|
|
|
|
const child = spawn('npx', args, {
|
|
cwd: taskTempDir,
|
|
stdio: 'inherit',
|
|
env: {
|
|
...process.env,
|
|
BUF_TOKEN: '851d3e2519b882d9e6da46eadec5e3ccc6a966dae0ce5e78dd285d9f912e35fd'
|
|
}
|
|
});
|
|
|
|
child.on('close', (code) => {
|
|
if (code !== 0) {
|
|
rejectPromise(new Error(`Failed to generate OpenAPI for ${label} (exit code ${code})`));
|
|
} else {
|
|
console.log(`Successfully generated OpenAPI for ${label}`);
|
|
|
|
// Post-generation cleanup: delete any missed v2beta/v3alpha files
|
|
try {
|
|
const generatedFiles = glob.sync('**/*', { cwd: outputPath, absolute: true, nodir: false });
|
|
generatedFiles.forEach(f => {
|
|
if (f.includes('v2beta') || f.includes('v3alpha')) {
|
|
fs.rmSync(f, { recursive: true, force: true });
|
|
}
|
|
});
|
|
} catch (e) {
|
|
console.warn(`Failed to clean up OpenAPI specs for ${label}`, e);
|
|
}
|
|
|
|
resolvePromise();
|
|
}
|
|
});
|
|
|
|
child.on('error', (err) => {
|
|
rejectPromise(err);
|
|
});
|
|
});
|
|
});
|
|
})));
|
|
} finally {
|
|
fs.rmSync(baseTempDir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
run().catch(err => {
|
|
console.error(err);
|
|
process.exit(1);
|
|
});
|