import StaticSiteAwsResourceGroup from "./staticsite-aws/staticsiteAws"; import FullstackAwsResourceGroup from "./fullstack-aws/fullstackAws"; import { existsSync, promises as fs } from "fs"; import path from "path"; import { z } from "zod"; import { parseYaml } from "@/yaml"; import { BaseTemplate } from "@/templates"; /** * Represents information about the dependencies resource group / framework. * * @typedef {Object} DependencyInfo * @property {string[]} components - An array of component names that the resource group depends on. * @property {string[]} packages - An array of package names that the resource group depends on. */ export type DependencyInfo = { files: string[]; packages: string[]; }; /** * An array containing all possible resource group types. * Currently, it includes only 'fullstack-aws'. */ export const AllResourceGroupTypes = [ "fullstack-aws", "staticsite-aws" ] as const; /** * Represents the type of a resource group. * * Currently, the only supported resource group type is 'fullstack-aws'. */ export type ResourceGroupType = (typeof AllResourceGroupTypes)[number]; export type BaseResourceGroupInputOptions = { resourceGroupName: string; resourceGroupType: ResourceGroupType; }; /** * Imports a resource group module based on the provided resource group type. * * @param resourceGroupType - The type of the resource group to import. * @param name - The name of the resource group. * @param directory - The directory in which the resource group is to be created. * @returns A promise that resolves to the imported resource group module. */ export function importResourceGroupType( resourceGroupType: ResourceGroupType, name: string, directory: string ): BaseTemplate | undefined { switch (resourceGroupType) { case "fullstack-aws": return new FullstackAwsResourceGroup(name, directory); case "staticsite-aws": return new StaticSiteAwsResourceGroup(name, directory); default: break; } } /** * Initializes a resourceGroup folder within the 'infrastructure' directory. * Creates the directory recursively if it does not exist. * * @param resourceGroupName - The name of the resourceGroup for which the folder is to be created. * @returns A promise that resolves when the directory has been created. */ export async function ensureResourceGroupFolder(resourceGroupName: string) { // Rename the 'projects' folder to 'resourceGroups' if it exists if (existsSync(path.join("infrastructure", "projects"))) { const projectPath = path.resolve("infrastructure", "projects"); await fs.rename( projectPath, projectPath.replace("projects", "resourceGroups") ); } await fs.mkdir( path.join("infrastructure", "resourceGroups", resourceGroupName), { recursive: true } ); } /** * Options for generating a Pulumi project YAML file. */ export interface ProjectYamlOptions { resourceGroupType: ResourceGroupType; framework: any; resourceGroupName: string; } /** * Generates a Pulumi project YAML file with the provided options. * * @param opts - The options to use when generating the project YAML file. * @returns A promise that resolves when the project YAML file has been written. */ export async function generatePulumiProjectYaml( opts: ProjectYamlOptions & Record ) { const { resourceGroupName: resourceGroupName, resourceGroupType: resourceGroupType, framework, ...resourceGroupInputs } = opts; await ensureResourceGroupFolder(opts.resourceGroupName); const yaml = ` name: ${opts.resourceGroupName} description: A pulumi project created with limo runtime: name: nodejs options: packagemanager: pnpm config: pulumi:tags: value: pulumi:template: aws-typescript limo: resourceGroupName: ${opts.resourceGroupName} resourceGroupType: ${opts.resourceGroupType}${ opts.framework ? ` framework: ${opts.framework}` : "" }${ Object.keys(resourceGroupInputs).length ? ` resourceGroupInputs:${Object.entries(resourceGroupInputs) .map( ([key, value]) => ` ${key}: ${value}` ) .join("")}` : "" } `.trimStart(); await fs.writeFile( path.join( "infrastructure", "resourceGroups", opts.resourceGroupName, "Pulumi.yaml" ), yaml ); } export async function readPulumiProjectMetadata(opts: { resourceGroupName: string; }) { const pulumiYamlPath = path.join( "infrastructure", "resourceGroups", opts.resourceGroupName, "Pulumi.yaml" ); const pulumiYaml = await fs.readFile(pulumiYamlPath, "utf-8"); // get the settings under the limo key if (!/limo:(\n\s+.*)+/g.test(pulumiYaml)) { throw new Error(`Could not find limo settings in ${pulumiYamlPath}`); } const { limo } = parseYaml(pulumiYaml) as any; const limoSchema = z.object({ resourceGroupName: z.string(), resourceGroupType: z.string(), framework: z.string().optional(), resourceGroupInputs: z.record(z.unknown()) }); return limoSchema.parse(limo); }