Skip to content

Instantly share code, notes, and snippets.

@vitormv
Created September 12, 2025 16:59
Show Gist options
  • Select an option

  • Save vitormv/57f4310b3c47634c9f3b271fcda6d855 to your computer and use it in GitHub Desktop.

Select an option

Save vitormv/57f4310b3c47634c9f3b271fcda6d855 to your computer and use it in GitHub Desktop.
Nelly Start
#!/usr/bin/env ts-node-script
import { execSync } from "node:child_process";
import { chdir } from "node:process";
import * as path from "node:path";
import assert from "node:assert";
const red = (text: string) => `\x1b[31m${text}\x1b[0m`;
// Define your paths 🛤️
const NELLY_MONO_PATH = path.resolve(path.join(import.meta.dirname, ".."));
const NELLY_MONO_DOCKER_COMPOSE_CMD = "docker compose up -d";
// check if docker is running
// docker info > /dev/null 2>&1 && echo "Docker is running" || echo "Docker is not running"
// using the command above, if docker is not running, start it
try {
execSync("docker info", { stdio: "ignore" });
} catch (error) {
console.error(
` ${red("Docker is not running. Please start it, and try again.")}`
);
process.exit(1);
}
// Define Service type - consolidate all possible services as a union type
export type ServiceName =
| "api"
| "lib"
| "patient-portal"
| "website"
| "doctor-portal"
| "backoffice-web"
| "backoffice-api"
| "feature-flags";
export type ServiceDefinition = {
cmd: string;
workingDir: string;
dependencies?: ServiceName[];
alias?: string[];
color: string; // Color to use in concurrently output
};
// Define all services with organized structure
const serviceConfig: Record<ServiceName, ServiceDefinition> = {
lib: {
workingDir: path.join(NELLY_MONO_PATH, "packages/lib"),
cmd: "pnpx nodemon -w src -e js,ts --exec 'pnpm build'",
color: "yellow.bold",
},
"backoffice-api": {
workingDir: path.join(NELLY_MONO_PATH, "packages/backoffice-api"),
cmd: "PORT=8081 pnpm dev",
dependencies: ["lib"],
color: "#eb8334",
},
"backoffice-web": {
alias: ["bo"],
workingDir: path.join(NELLY_MONO_PATH, "packages/backoffice-web"),
cmd: "PORT=3000 pnpm dev",
dependencies: ["lib", "feature-flags", "backoffice-api"],
color: "cyan",
},
api: {
workingDir: path.join(NELLY_MONO_PATH, "packages/api"),
cmd: "pnpx wait-on --timeout 10s tcp:5432 && pnpx wait-on --timeout 10s tcp:9094 && sleep 3 && pnpm dev",
dependencies: ["lib", "feature-flags"],
color: "magenta",
},
"patient-portal": {
alias: ["papo"],
workingDir: path.join(NELLY_MONO_PATH, "packages/patient-portal"),
cmd: "PORT=3002 pnpm dev",
dependencies: ["lib", "feature-flags", "api"],
color: "blue",
},
website: {
workingDir: path.join(NELLY_MONO_PATH, "packages/website"),
cmd: "PORT=3003 pnpm dev",
dependencies: ["lib", "api"],
color: "red",
},
"doctor-portal": {
alias: ["dopo"],
workingDir: path.join(NELLY_MONO_PATH, "packages/doctor-portal"),
cmd: "PORT=3001 pnpm dev",
dependencies: ["lib", "feature-flags", "api"],
color: "#eb8334", // nelly green
},
"feature-flags": {
alias: ["ff"],
workingDir: path.join(NELLY_MONO_PATH, "packages/feature-flag-node"),
cmd: "ldcli dev-server start --base-uri https://app.eu.launchdarkly.com --dev-stream-uri https://stream.eu.launchdarkly.com --project default --source demo",
color: "blue.bold",
},
};
// Define all available services
const ALL_SERVICE_NAMES = Object.keys(serviceConfig) as ServiceName[];
// Function to start a service
const startService = (service: ServiceName): string => {
const config = serviceConfig[service];
if (!config) {
throw new Error(`Unknown service: ${service}`);
}
return `cd ${config.workingDir} && ${config.cmd}`;
};
// Function to convert alias to service name
const resolveServiceName = (input: string): ServiceName => {
// Check if input is a direct service name
if (input in serviceConfig) {
return input as ServiceName;
}
// Check if input is an alias
for (const [serviceName, config] of Object.entries(serviceConfig)) {
if (config.alias?.includes(input)) {
return serviceName as ServiceName;
}
}
throw new Error(`Unknown service or alias: ${input}`);
};
// typeguard to check if a string is a valid service or alias
const isValidServiceInput = (service: string): boolean => {
return resolveServiceName(service) !== null;
};
// Function to get unique services including dependencies
const getServices = (requestedInputs: string[]): ServiceName[] => {
const services = new Set<ServiceName>();
const invalidInputs = requestedInputs.filter(
(input) => !isValidServiceInput(input)
);
assert(
invalidInputs.length === 0,
`Invalid service(s) or alias(es) provided: ${invalidInputs.join(", ")}`
);
// Convert inputs (which might be aliases) to actual service names
const requestedServices: ServiceName[] = requestedInputs.map((input) =>
resolveServiceName(input)
);
const allDeps = requestedServices.flatMap(
(service) => serviceConfig[service].dependencies ?? []
);
for (const dep of allDeps) {
if (!services.has(dep)) {
services.add(dep);
}
}
// Add the requested services themselves
for (const service of requestedServices) {
services.add(service);
}
return Array.from(services);
};
// Set the name of your developer profile
process.env.AWS_PROFILE = "nelly-staging-services";
// Check if AWS SSO is logged in, if not login
const isAwsSsoLoggedIn = (): boolean => {
try {
execSync("aws sts get-caller-identity", { stdio: "ignore" });
return true;
} catch (error) {
return false;
}
};
// start docker cmd
execSync(NELLY_MONO_DOCKER_COMPOSE_CMD, { stdio: "ignore" });
if (!isAwsSsoLoggedIn()) {
console.log("Logging in with AWS SSO...");
execSync("aws sso login", { stdio: "inherit" });
}
// Install dependencies 📦 at start-up
chdir(NELLY_MONO_PATH);
console.log("Installing dependencies...");
execSync("pnpm install", { stdio: "inherit" });
// Get services to start
const args = process.argv.slice(2);
const serviceNames = args.length === 0 ? ALL_SERVICE_NAMES : getServices(args);
const maxNameLength = Math.max(
...serviceNames.map((service) => service.length)
);
// Prepare concurrently command with fixed colors for each service
const names = serviceNames
.map((name) => name.padStart(maxNameLength, " "))
.join(",");
const colors = serviceNames
.map((service) => serviceConfig[service].color)
.join(",");
const cmd = [
"pnpx",
"concurrently",
"--kill-others-on-fail",
`--names="${names}"`,
`--prefix-colors=${colors}`,
];
for (const service of serviceNames) {
const serviceCmd = startService(service as ServiceName);
cmd.push(`"${serviceCmd}"`);
}
// Run the command
console.log(`Starting services: ${serviceNames.join(", ")}`);
const fullCmd = cmd.join(" ");
console.log(`Running: ${fullCmd}`);
// Execute the command
execSync(fullCmd, { stdio: "inherit" });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment