core: Prep OpenAPI generators for NPM Workspaces.

This commit is contained in:
Teffen Ellis
2025-04-29 02:40:11 +02:00
parent 70d60c7ab2
commit 16019b8585
5 changed files with 150 additions and 53 deletions

View File

@ -4,7 +4,6 @@
PWD = $(shell pwd)
UID = $(shell id -u)
GID = $(shell id -g)
NPM_VERSION = $(shell python -m scripts.generate_semver)
PY_SOURCES = authentik tests scripts lifecycle .github
DOCKER_IMAGE ?= "authentik:test"
@ -116,49 +115,21 @@ gen-diff: ## (Release) generate the changelog diff between the current schema a
sed -i 's/}/}/g' diff.md
npx prettier --write diff.md
gen-clean-ts: ## Remove generated API client for Typescript
rm -rf ./${GEN_API_TS}/
rm -rf ./web/node_modules/@goauthentik/api/
gen-client-ts: ## Build and install the authentik API for Typescript into the authentik UI Application
./scripts/gen-client-ts.mjs
gen-clean-go: ## Remove generated API client for Go
rm -rf ./${GEN_API_GO}/
npm i --prefix ${GEN_API_TS}
gen-clean-py: ## Remove generated API client for Python
rm -rf ./${GEN_API_PY}/
cd ./${GEN_API_TS} && npm link
cd ./web && npm link @goauthentik/api
gen-clean: gen-clean-ts gen-clean-go gen-clean-py ## Remove generated API clients
gen-client-py: ## Build and install the authentik API for Python
./scripts/gen-client-py.mjs
gen-client-ts: gen-clean-ts ## Build and install the authentik API for Typescript into the authentik UI Application
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-generator-cli:v7.11.0 generate \
-i /local/schema.yml \
-g typescript-fetch \
-o /local/${GEN_API_TS} \
-c /local/scripts/api-ts-config.yaml \
--additional-properties=npmVersion=${NPM_VERSION} \
--git-repo-id authentik \
--git-user-id goauthentik
mkdir -p web/node_modules/@goauthentik/api
cd ./${GEN_API_TS} && npm i
\cp -rf ./${GEN_API_TS}/* web/node_modules/@goauthentik/api
gen-client-py: gen-clean-py ## Build and install the authentik API for Python
docker run \
--rm -v ${PWD}:/local \
--user ${UID}:${GID} \
docker.io/openapitools/openapi-generator-cli:v7.11.0 generate \
-i /local/schema.yml \
-g python \
-o /local/${GEN_API_PY} \
-c /local/scripts/api-py-config.yaml \
--additional-properties=packageVersion=${NPM_VERSION} \
--git-repo-id authentik \
--git-user-id goauthentik
pip install ./${GEN_API_PY}
gen-client-go: gen-clean-go ## Build and install the authentik API for Golang
gen-client-go: ## Build and install the authentik API for Golang
rm -rf ./${GEN_API_GO}/
mkdir -p ./${GEN_API_GO} ./${GEN_API_GO}/templates
wget https://raw.githubusercontent.com/goauthentik/client-go/main/config.yaml -O ./${GEN_API_GO}/config.yaml
wget https://raw.githubusercontent.com/goauthentik/client-go/main/templates/README.mustache -O ./${GEN_API_GO}/templates/README.mustache

19
scripts/gen-client-py.mjs Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env node
/**
* @file Generates the authentik API client for Python.
*/
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { generateOpenAPIClient } from "./openapi-generator.mjs";
const scriptDirectory = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(scriptDirectory, "..");
generateOpenAPIClient({
cwd: repoRoot,
outputDirectory: resolve(repoRoot, "gen-py-api"),
generatorName: "python",
config: resolve(scriptDirectory, "api-py-config.yaml"),
});

22
scripts/gen-client-ts.mjs Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env node
/**
* @file Generates the authentik API client for TypeScript.
*/
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import PackageJSON from "../package.json" with { type: "json" };
import { generateOpenAPIClient } from "./openapi-generator.mjs";
const scriptDirectory = dirname(fileURLToPath(import.meta.url));
const repoRoot = resolve(scriptDirectory, "..");
const npmVersion = [PackageJSON.version, Date.now()].join("-");
generateOpenAPIClient({
cwd: repoRoot,
outputDirectory: resolve(repoRoot, "gen-ts-api"),
generatorName: "typescript-fetch",
config: resolve(scriptDirectory, "api-ts-config.yaml"),
commandArgs: [`--additional-properties=npmVersion=${npmVersion}`],
});

View File

@ -1,15 +0,0 @@
#!/usr/bin/env python3
"""
Generates a Semantic Versioning identifier, suffixed with a timestamp.
"""
from time import time
from authentik import __version__ as package_version
"""
See: https://semver.org/#spec-item-9 (Pre-release spec)
"""
pre_release_timestamp = int(time())
print(f"{package_version}-{pre_release_timestamp}")

View File

@ -0,0 +1,100 @@
/**
* @file OpenAPI generator utilities.
*/
import { execFileSync, execSync } from "node:child_process";
import { existsSync, rmSync } from "node:fs";
import { userInfo } from "node:os";
import { join, relative, resolve } from "node:path";
const OPENAPI_CONTAINER_IMAGE = "docker.io/openapitools/openapi-generator-cli:v7.11.0";
/**
* Checks if a command exists in the PATH.
*
* @template {string} T
* @param {T} command
* @returns {T | null}
*/
function commandExists(command) {
if (execSync(`command -v ${command} || echo ''`).toString().trim()) {
return command;
}
return null;
}
/**
* Given a path relative to the current working directory,
* resolves it to a path relative to the local volume.
*
* @param {string} cwd
* @param {...string} pathSegments
*/
function resolveLocalPath(cwd, ...pathSegments) {
return resolve("/local", relative(cwd, join(...pathSegments)));
}
/**
* @typedef {object} GenerateOpenAPIClientOptions
* @property {string} cwd The working directory to run the generator in.
* @property {string} outputDirectory The path to the output directory.
* @property {string} generatorName The name of the generator.
* @property {string} config The path to the generator configuration.
* @property {string} [inputSpec] The path to the OpenAPI specification.
* @property {Array<string | string[]>} [commandArgs] Additional arguments to pass to the generator.
*/
/**
* Generates an OpenAPI client using the `openapi-generator-cli` Docker image.
*
* @param {GenerateOpenAPIClientOptions} options
* @see {@link https://openapi-generator.tech/docs/usage}
*/
export function generateOpenAPIClient({
cwd,
outputDirectory,
generatorName,
config,
inputSpec = resolve(cwd, "schema.yml"),
commandArgs = [],
}) {
if (existsSync(outputDirectory)) {
console.log(`Removing existing generated API client from ${outputDirectory}`);
rmSync(outputDirectory, { recursive: true, force: true });
}
const containerEngine = commandExists("docker") || commandExists("podman");
if (!containerEngine) {
throw new Error("Container engine not found. Is Docker or Podman available in the PATH?");
}
const { gid, uid } = userInfo();
const args = [
"run",
[`--user`, `${uid}:${gid}`],
`--rm`,
[`-v`, `${cwd}:/local`],
OPENAPI_CONTAINER_IMAGE,
"generate",
["--input-spec", resolveLocalPath(cwd, inputSpec)],
[`--generator-name`, generatorName],
["--config", resolveLocalPath(cwd, config)],
["--git-repo-id", `authentik`],
["--git-user-id", `goauthentik`],
["--output", resolveLocalPath(cwd, outputDirectory)],
...commandArgs,
];
console.debug(`Running command: ${containerEngine}`, args);
execFileSync(containerEngine, args.flat(), {
cwd,
stdio: "inherit",
});
console.log(`Generated API client to ${outputDirectory}`);
}