UNPKG

wgc

Version:

The official CLI tool to manage the GraphQL Federation Platform Cosmo

192 lines 7.64 kB
import { readFile } from 'node:fs/promises'; import { existsSync } from 'node:fs'; import { arch, platform } from 'node:os'; import path from 'node:path'; import { EnumStatusCode } from '@wundergraph/cosmo-connect/dist/common/common_pb'; import { SubgraphType } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb'; import { splitLabel } from '@wundergraph/cosmo-shared'; import { execa } from 'execa'; import { config, getBaseHeaders } from './config.js'; export function getDefaultPlatforms() { const supportedPlatforms = ['linux/amd64', 'linux/arm64', 'darwin/amd64', 'darwin/arm64', 'windows/amd64']; const defaultPlatforms = ['linux/amd64', 'linux/arm64']; const currentPlatform = platform(); const currentArch = arch(); let dockerPlatform = null; switch (currentPlatform) { case 'linux': { if (currentArch === 'x64') { dockerPlatform = 'linux/amd64'; } else if (currentArch === 'arm64') { dockerPlatform = 'linux/arm64'; } break; } case 'darwin': { if (currentArch === 'x64') { dockerPlatform = 'darwin/amd64'; } else if (currentArch === 'arm64') { dockerPlatform = 'darwin/arm64'; } break; } case 'win32': { if (currentArch === 'x64') { dockerPlatform = 'windows/amd64'; } break; } } if (dockerPlatform && supportedPlatforms.includes(dockerPlatform) && !defaultPlatforms.includes(dockerPlatform)) { defaultPlatforms.push(dockerPlatform); } return defaultPlatforms; } export const SUPPORTED_PLATFORMS = ['linux/amd64', 'linux/arm64', 'darwin/amd64', 'darwin/arm64', 'windows/amd64']; /** * Reads and validates the 5 required plugin files from a plugin directory. * Throws on missing/empty files. */ export async function readPluginFiles(pluginDir) { const schemaFile = path.join(pluginDir, 'src', 'schema.graphql'); const dockerFile = path.join(pluginDir, 'Dockerfile'); const protoSchemaFile = path.join(pluginDir, 'generated', 'service.proto'); const protoMappingFile = path.join(pluginDir, 'generated', 'mapping.json'); const protoLockFile = path.join(pluginDir, 'generated', 'service.proto.lock.json'); const requiredFiles = [schemaFile, dockerFile, protoSchemaFile, protoMappingFile, protoLockFile]; for (const f of requiredFiles) { if (!existsSync(f)) { throw new Error(`Required file does not exist: ${f}`); } } async function readNonEmpty(filePath) { const buffer = await readFile(filePath); const content = new TextDecoder().decode(buffer); if (content.trim().length === 0) { throw new Error(`File is empty: ${filePath}`); } return content; } const [schema, protoSchema, protoMapping, protoLock] = await Promise.all([ readNonEmpty(schemaFile), readNonEmpty(protoSchemaFile), readNonEmpty(protoMappingFile), readNonEmpty(protoLockFile), ]); return { schema, dockerFile, protoSchema, protoMapping, protoLock }; } /** * Core plugin publish pipeline: * 1. validateAndFetchPluginData (RPC) * 2. Docker login → buildx build+push → logout * 3. publishFederatedSubgraph (RPC) * * Returns a result object; never calls program.error() — the caller decides * how to handle errors. */ export async function publishPluginPipeline(params) { var _a, _b, _c, _d, _e, _f, _g, _h; const { client, pluginName, pluginDir, namespace, labels, platforms, files, cancelSignal, onProcess } = params; // Step 1: Validate and fetch plugin data const pluginDataResponse = await client.platform.validateAndFetchPluginData({ name: pluginName, namespace, labels: labels.map((label) => splitLabel(label)), }, { headers: getBaseHeaders(), }); if (((_a = pluginDataResponse.response) === null || _a === void 0 ? void 0 : _a.code) !== EnumStatusCode.OK) { return { error: new Error((_c = (_b = pluginDataResponse.response) === null || _b === void 0 ? void 0 : _b.details) !== null && _c !== void 0 ? _c : 'Failed to validate plugin data') }; } const { reference, newVersion, pushToken } = pluginDataResponse; const imageTag = `${config.pluginRegistryURL}/${reference}:${newVersion}`; const platformStr = platforms.join(','); // Step 2: Docker operations try { const loginProc = execa('docker', ['login', config.pluginRegistryURL, '-u', 'x', '--password-stdin'], { stdio: 'pipe', input: pushToken, ...(cancelSignal ? { cancelSignal } : {}), }); onProcess === null || onProcess === void 0 ? void 0 : onProcess(loginProc); await loginProc; const buildProc = execa('docker', [ 'buildx', 'build', '--sbom=false', '--provenance=false', '--push', '--platform', platformStr, '-f', files.dockerFile, '-t', imageTag, pluginDir, ], { stdio: 'pipe', ...(cancelSignal ? { cancelSignal } : {}), }); onProcess === null || onProcess === void 0 ? void 0 : onProcess(buildProc); await buildProc; } catch (error) { return { error: new Error(`Docker operation failed: ${error instanceof Error ? error.message : String(error)}`) }; } finally { try { const logoutProc = execa('docker', ['logout', config.pluginRegistryURL], { stdio: 'pipe' }); onProcess === null || onProcess === void 0 ? void 0 : onProcess(logoutProc); await logoutProc; } catch { // best-effort logout } } // Step 3: Publish schema const resp = await client.platform.publishFederatedSubgraph({ name: pluginName, namespace, schema: files.schema, labels: labels.map((label) => splitLabel(label)), type: SubgraphType.GRPC_PLUGIN, proto: { schema: files.protoSchema, mappings: files.protoMapping, lock: files.protoLock, platforms, version: newVersion, }, }, { headers: getBaseHeaders(), }); const result = { error: null, response: { code: (_d = resp.response) === null || _d === void 0 ? void 0 : _d.code, details: (_e = resp.response) === null || _e === void 0 ? void 0 : _e.details, hasChanged: resp.hasChanged, compositionErrors: resp.compositionErrors, deploymentErrors: resp.deploymentErrors, compositionWarnings: resp.compositionWarnings, proposalMatchMessage: resp.proposalMatchMessage, }, }; switch ((_f = resp.response) === null || _f === void 0 ? void 0 : _f.code) { case EnumStatusCode.OK: case EnumStatusCode.ERR_SUBGRAPH_COMPOSITION_FAILED: case EnumStatusCode.ERR_DEPLOYMENT_FAILED: case EnumStatusCode.ERR_SCHEMA_MISMATCH_WITH_APPROVED_PROPOSAL: { return result; } default: { return { ...result, error: new Error((_h = (_g = resp.response) === null || _g === void 0 ? void 0 : _g.details) !== null && _h !== void 0 ? _h : 'Failed to publish plugin subgraph'), }; } } } //# sourceMappingURL=plugin-publish.js.map