Skip to main content
Glama
update-server.ts3.75 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import type { MedplumClient, MedplumClientOptions, MedplumInfraConfig } from '@medplum/core'; import { spawnSync } from 'node:child_process'; import * as semver from 'semver'; import { createMedplumClient } from '../util/client'; import { getConfigFileName, readConfig, writeConfig } from '../utils'; import { getServerVersions, printConfigNotFound } from './utils'; export interface UpdateServerOptions extends MedplumClientOptions { file?: string; toVersion?: string; } /** * The AWS "update-server" command updates the Medplum server in a Medplum CloudFormation stack. * @param tag - The Medplum stack tag. * @param options - Client options */ export async function updateServerCommand(tag: string, options: UpdateServerOptions): Promise<void> { const client = await createMedplumClient(options); const config = readConfig(tag, options) as MedplumInfraConfig; if (!config) { console.log(`Configuration file ${getConfigFileName(tag)} not found`); printConfigNotFound(tag, options); throw new Error(`Config not found: ${tag}`); } const separatorIndex = config.serverImage.lastIndexOf(':'); const serverImagePrefix = config.serverImage.slice(0, separatorIndex); const initialVersion = await getCurrentVersion(client, config); let updateVersion = await nextUpdateVersion(initialVersion); while (updateVersion) { if (options.toVersion && semver.gt(updateVersion, options.toVersion)) { console.log(`Skipping update to v${updateVersion}`); break; } console.log(`Performing update to v${updateVersion}`); config.serverImage = `${serverImagePrefix}:${updateVersion}`; deployServerUpdate(tag, config); // Run data migrations await client.startAsyncRequest('/admin/super/migrate'); updateVersion = await nextUpdateVersion(updateVersion); } } async function getCurrentVersion(medplum: MedplumClient, config: MedplumInfraConfig): Promise<string> { const separatorIndex = config.serverImage.lastIndexOf(':'); let initialVersion = config.serverImage.slice(separatorIndex + 1); if (initialVersion === 'latest') { const serverInfo = await medplum.get('/healthcheck'); initialVersion = serverInfo.version as string; const sep = initialVersion.indexOf('-'); if (sep > -1) { initialVersion = initialVersion.slice(0, sep); } } return initialVersion; } async function nextUpdateVersion(currentVersion: string, targetVersion?: string): Promise<string | undefined> { // The list of server versions is sorted in descending order // The first entry is the latest version // The last entry is the oldest version // We want to find the "next" version after our current version // Filter the list to only include versions that are greater than or equal to the current minor version // Then pop the last entry from the list const allVersions = await getServerVersions(currentVersion); const latestVersion = allVersions[0]; return allVersions .filter( (v) => v === latestVersion || v === targetVersion || semver.gte(v, semver.inc(currentVersion, 'minor') as string) ) .pop(); } function deployServerUpdate(tag: string, config: MedplumInfraConfig): void { const configFile = getConfigFileName(tag); writeConfig(configFile, config); const cmd = `npx cdk deploy -c config=${configFile}${config.region !== 'us-east-1' ? ' --all' : ''}`; console.log('> ' + cmd); const deploy = spawnSync(cmd, { stdio: 'inherit' }); if (deploy.status !== 0) { throw new Error(`Deploy of ${config.serverImage} failed (exit code ${deploy.status}): ${deploy.stderr}`); } console.log(deploy.stdout); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/medplum/medplum'

If you have feedback or need assistance with the MCP directory API, please join our Discord server